diff options
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/Kconfig | 1 | ||||
-rw-r--r-- | drivers/dma/mxs-dma.c | 188 |
2 files changed, 128 insertions, 61 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index ef378b5b17e4..aadeb5be9dba 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig | |||
@@ -238,6 +238,7 @@ config IMX_DMA | |||
238 | config MXS_DMA | 238 | config MXS_DMA |
239 | bool "MXS DMA support" | 239 | bool "MXS DMA support" |
240 | depends on SOC_IMX23 || SOC_IMX28 | 240 | depends on SOC_IMX23 || SOC_IMX28 |
241 | select STMP_DEVICE | ||
241 | select DMA_ENGINE | 242 | select DMA_ENGINE |
242 | help | 243 | help |
243 | Support the MXS DMA engine. This engine including APBH-DMA | 244 | Support the MXS DMA engine. This engine including APBH-DMA |
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 3db3a48d3f01..c96ab15319f2 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c | |||
@@ -22,11 +22,14 @@ | |||
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/dmaengine.h> | 23 | #include <linux/dmaengine.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/module.h> | ||
25 | #include <linux/fsl/mxs-dma.h> | 26 | #include <linux/fsl/mxs-dma.h> |
27 | #include <linux/stmp_device.h> | ||
28 | #include <linux/of.h> | ||
29 | #include <linux/of_device.h> | ||
26 | 30 | ||
27 | #include <asm/irq.h> | 31 | #include <asm/irq.h> |
28 | #include <mach/mxs.h> | 32 | #include <mach/mxs.h> |
29 | #include <mach/common.h> | ||
30 | 33 | ||
31 | #include "dmaengine.h" | 34 | #include "dmaengine.h" |
32 | 35 | ||
@@ -36,12 +39,8 @@ | |||
36 | * dma can program the controller registers of peripheral devices. | 39 | * dma can program the controller registers of peripheral devices. |
37 | */ | 40 | */ |
38 | 41 | ||
39 | #define MXS_DMA_APBH 0 | 42 | #define dma_is_apbh(mxs_dma) ((mxs_dma)->type == MXS_DMA_APBH) |
40 | #define MXS_DMA_APBX 1 | 43 | #define apbh_is_old(mxs_dma) ((mxs_dma)->dev_id == IMX23_DMA) |
41 | #define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH) | ||
42 | |||
43 | #define APBH_VERSION_LATEST 3 | ||
44 | #define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST) | ||
45 | 44 | ||
46 | #define HW_APBHX_CTRL0 0x000 | 45 | #define HW_APBHX_CTRL0 0x000 |
47 | #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) | 46 | #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) |
@@ -51,13 +50,14 @@ | |||
51 | #define HW_APBHX_CTRL2 0x020 | 50 | #define HW_APBHX_CTRL2 0x020 |
52 | #define HW_APBHX_CHANNEL_CTRL 0x030 | 51 | #define HW_APBHX_CHANNEL_CTRL 0x030 |
53 | #define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16 | 52 | #define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16 |
54 | #define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800) | 53 | /* |
55 | #define HW_APBX_VERSION 0x800 | 54 | * The offset of NXTCMDAR register is different per both dma type and version, |
56 | #define BP_APBHX_VERSION_MAJOR 24 | 55 | * while stride for each channel is all the same 0x70. |
57 | #define HW_APBHX_CHn_NXTCMDAR(n) \ | 56 | */ |
58 | (((dma_is_apbh() && apbh_is_old()) ? 0x050 : 0x110) + (n) * 0x70) | 57 | #define HW_APBHX_CHn_NXTCMDAR(d, n) \ |
59 | #define HW_APBHX_CHn_SEMA(n) \ | 58 | (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x050 : 0x110) + (n) * 0x70) |
60 | (((dma_is_apbh() && apbh_is_old()) ? 0x080 : 0x140) + (n) * 0x70) | 59 | #define HW_APBHX_CHn_SEMA(d, n) \ |
60 | (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x080 : 0x140) + (n) * 0x70) | ||
61 | 61 | ||
62 | /* | 62 | /* |
63 | * ccw bits definitions | 63 | * ccw bits definitions |
@@ -121,9 +121,19 @@ struct mxs_dma_chan { | |||
121 | #define MXS_DMA_CHANNELS 16 | 121 | #define MXS_DMA_CHANNELS 16 |
122 | #define MXS_DMA_CHANNELS_MASK 0xffff | 122 | #define MXS_DMA_CHANNELS_MASK 0xffff |
123 | 123 | ||
124 | enum mxs_dma_devtype { | ||
125 | MXS_DMA_APBH, | ||
126 | MXS_DMA_APBX, | ||
127 | }; | ||
128 | |||
129 | enum mxs_dma_id { | ||
130 | IMX23_DMA, | ||
131 | IMX28_DMA, | ||
132 | }; | ||
133 | |||
124 | struct mxs_dma_engine { | 134 | struct mxs_dma_engine { |
125 | int dev_id; | 135 | enum mxs_dma_id dev_id; |
126 | unsigned int version; | 136 | enum mxs_dma_devtype type; |
127 | void __iomem *base; | 137 | void __iomem *base; |
128 | struct clk *clk; | 138 | struct clk *clk; |
129 | struct dma_device dma_device; | 139 | struct dma_device dma_device; |
@@ -131,17 +141,86 @@ struct mxs_dma_engine { | |||
131 | struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; | 141 | struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; |
132 | }; | 142 | }; |
133 | 143 | ||
144 | struct mxs_dma_type { | ||
145 | enum mxs_dma_id id; | ||
146 | enum mxs_dma_devtype type; | ||
147 | }; | ||
148 | |||
149 | static struct mxs_dma_type mxs_dma_types[] = { | ||
150 | { | ||
151 | .id = IMX23_DMA, | ||
152 | .type = MXS_DMA_APBH, | ||
153 | }, { | ||
154 | .id = IMX23_DMA, | ||
155 | .type = MXS_DMA_APBX, | ||
156 | }, { | ||
157 | .id = IMX28_DMA, | ||
158 | .type = MXS_DMA_APBH, | ||
159 | }, { | ||
160 | .id = IMX28_DMA, | ||
161 | .type = MXS_DMA_APBX, | ||
162 | } | ||
163 | }; | ||
164 | |||
165 | static struct platform_device_id mxs_dma_ids[] = { | ||
166 | { | ||
167 | .name = "imx23-dma-apbh", | ||
168 | .driver_data = (kernel_ulong_t) &mxs_dma_types[0], | ||
169 | }, { | ||
170 | .name = "imx23-dma-apbx", | ||
171 | .driver_data = (kernel_ulong_t) &mxs_dma_types[1], | ||
172 | }, { | ||
173 | .name = "imx28-dma-apbh", | ||
174 | .driver_data = (kernel_ulong_t) &mxs_dma_types[2], | ||
175 | }, { | ||
176 | .name = "imx28-dma-apbx", | ||
177 | .driver_data = (kernel_ulong_t) &mxs_dma_types[3], | ||
178 | }, { | ||
179 | /* end of list */ | ||
180 | } | ||
181 | }; | ||
182 | |||
183 | static const struct of_device_id mxs_dma_dt_ids[] = { | ||
184 | { .compatible = "fsl,imx23-dma-apbh", .data = &mxs_dma_ids[0], }, | ||
185 | { .compatible = "fsl,imx23-dma-apbx", .data = &mxs_dma_ids[1], }, | ||
186 | { .compatible = "fsl,imx28-dma-apbh", .data = &mxs_dma_ids[2], }, | ||
187 | { .compatible = "fsl,imx28-dma-apbx", .data = &mxs_dma_ids[3], }, | ||
188 | { /* sentinel */ } | ||
189 | }; | ||
190 | MODULE_DEVICE_TABLE(of, mxs_dma_dt_ids); | ||
191 | |||
192 | static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) | ||
193 | { | ||
194 | return container_of(chan, struct mxs_dma_chan, chan); | ||
195 | } | ||
196 | |||
197 | int mxs_dma_is_apbh(struct dma_chan *chan) | ||
198 | { | ||
199 | struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); | ||
200 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | ||
201 | |||
202 | return dma_is_apbh(mxs_dma); | ||
203 | } | ||
204 | |||
205 | int mxs_dma_is_apbx(struct dma_chan *chan) | ||
206 | { | ||
207 | struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); | ||
208 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | ||
209 | |||
210 | return !dma_is_apbh(mxs_dma); | ||
211 | } | ||
212 | |||
134 | static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) | 213 | static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) |
135 | { | 214 | { |
136 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | 215 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; |
137 | int chan_id = mxs_chan->chan.chan_id; | 216 | int chan_id = mxs_chan->chan.chan_id; |
138 | 217 | ||
139 | if (dma_is_apbh() && apbh_is_old()) | 218 | if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) |
140 | writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL), | 219 | writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL), |
141 | mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); | 220 | mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); |
142 | else | 221 | else |
143 | writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL), | 222 | writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL), |
144 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR); | 223 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET); |
145 | } | 224 | } |
146 | 225 | ||
147 | static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) | 226 | static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) |
@@ -151,10 +230,10 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) | |||
151 | 230 | ||
152 | /* set cmd_addr up */ | 231 | /* set cmd_addr up */ |
153 | writel(mxs_chan->ccw_phys, | 232 | writel(mxs_chan->ccw_phys, |
154 | mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); | 233 | mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(mxs_dma, chan_id)); |
155 | 234 | ||
156 | /* write 1 to SEMA to kick off the channel */ | 235 | /* write 1 to SEMA to kick off the channel */ |
157 | writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id)); | 236 | writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(mxs_dma, chan_id)); |
158 | } | 237 | } |
159 | 238 | ||
160 | static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) | 239 | static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) |
@@ -168,12 +247,12 @@ static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan) | |||
168 | int chan_id = mxs_chan->chan.chan_id; | 247 | int chan_id = mxs_chan->chan.chan_id; |
169 | 248 | ||
170 | /* freeze the channel */ | 249 | /* freeze the channel */ |
171 | if (dma_is_apbh() && apbh_is_old()) | 250 | if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) |
172 | writel(1 << chan_id, | 251 | writel(1 << chan_id, |
173 | mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); | 252 | mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); |
174 | else | 253 | else |
175 | writel(1 << chan_id, | 254 | writel(1 << chan_id, |
176 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR); | 255 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET); |
177 | 256 | ||
178 | mxs_chan->status = DMA_PAUSED; | 257 | mxs_chan->status = DMA_PAUSED; |
179 | } | 258 | } |
@@ -184,21 +263,16 @@ static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan) | |||
184 | int chan_id = mxs_chan->chan.chan_id; | 263 | int chan_id = mxs_chan->chan.chan_id; |
185 | 264 | ||
186 | /* unfreeze the channel */ | 265 | /* unfreeze the channel */ |
187 | if (dma_is_apbh() && apbh_is_old()) | 266 | if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) |
188 | writel(1 << chan_id, | 267 | writel(1 << chan_id, |
189 | mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR); | 268 | mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR); |
190 | else | 269 | else |
191 | writel(1 << chan_id, | 270 | writel(1 << chan_id, |
192 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_CLR_ADDR); | 271 | mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_CLR); |
193 | 272 | ||
194 | mxs_chan->status = DMA_IN_PROGRESS; | 273 | mxs_chan->status = DMA_IN_PROGRESS; |
195 | } | 274 | } |
196 | 275 | ||
197 | static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) | ||
198 | { | ||
199 | return container_of(chan, struct mxs_dma_chan, chan); | ||
200 | } | ||
201 | |||
202 | static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) | 276 | static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) |
203 | { | 277 | { |
204 | return dma_cookie_assign(tx); | 278 | return dma_cookie_assign(tx); |
@@ -220,11 +294,11 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) | |||
220 | /* completion status */ | 294 | /* completion status */ |
221 | stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1); | 295 | stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1); |
222 | stat1 &= MXS_DMA_CHANNELS_MASK; | 296 | stat1 &= MXS_DMA_CHANNELS_MASK; |
223 | writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR); | 297 | writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR); |
224 | 298 | ||
225 | /* error status */ | 299 | /* error status */ |
226 | stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2); | 300 | stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2); |
227 | writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR); | 301 | writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR); |
228 | 302 | ||
229 | /* | 303 | /* |
230 | * When both completion and error of termination bits set at the | 304 | * When both completion and error of termination bits set at the |
@@ -567,27 +641,21 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) | |||
567 | if (ret) | 641 | if (ret) |
568 | return ret; | 642 | return ret; |
569 | 643 | ||
570 | ret = mxs_reset_block(mxs_dma->base); | 644 | ret = stmp_reset_block(mxs_dma->base); |
571 | if (ret) | 645 | if (ret) |
572 | goto err_out; | 646 | goto err_out; |
573 | 647 | ||
574 | /* only major version matters */ | ||
575 | mxs_dma->version = readl(mxs_dma->base + | ||
576 | ((mxs_dma->dev_id == MXS_DMA_APBX) ? | ||
577 | HW_APBX_VERSION : HW_APBH_VERSION)) >> | ||
578 | BP_APBHX_VERSION_MAJOR; | ||
579 | |||
580 | /* enable apbh burst */ | 648 | /* enable apbh burst */ |
581 | if (dma_is_apbh()) { | 649 | if (dma_is_apbh(mxs_dma)) { |
582 | writel(BM_APBH_CTRL0_APB_BURST_EN, | 650 | writel(BM_APBH_CTRL0_APB_BURST_EN, |
583 | mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); | 651 | mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); |
584 | writel(BM_APBH_CTRL0_APB_BURST8_EN, | 652 | writel(BM_APBH_CTRL0_APB_BURST8_EN, |
585 | mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); | 653 | mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); |
586 | } | 654 | } |
587 | 655 | ||
588 | /* enable irq for all the channels */ | 656 | /* enable irq for all the channels */ |
589 | writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS, | 657 | writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS, |
590 | mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR); | 658 | mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET); |
591 | 659 | ||
592 | err_out: | 660 | err_out: |
593 | clk_disable_unprepare(mxs_dma->clk); | 661 | clk_disable_unprepare(mxs_dma->clk); |
@@ -596,8 +664,9 @@ err_out: | |||
596 | 664 | ||
597 | static int __init mxs_dma_probe(struct platform_device *pdev) | 665 | static int __init mxs_dma_probe(struct platform_device *pdev) |
598 | { | 666 | { |
599 | const struct platform_device_id *id_entry = | 667 | const struct platform_device_id *id_entry; |
600 | platform_get_device_id(pdev); | 668 | const struct of_device_id *of_id; |
669 | const struct mxs_dma_type *dma_type; | ||
601 | struct mxs_dma_engine *mxs_dma; | 670 | struct mxs_dma_engine *mxs_dma; |
602 | struct resource *iores; | 671 | struct resource *iores; |
603 | int ret, i; | 672 | int ret, i; |
@@ -606,7 +675,15 @@ static int __init mxs_dma_probe(struct platform_device *pdev) | |||
606 | if (!mxs_dma) | 675 | if (!mxs_dma) |
607 | return -ENOMEM; | 676 | return -ENOMEM; |
608 | 677 | ||
609 | mxs_dma->dev_id = id_entry->driver_data; | 678 | of_id = of_match_device(mxs_dma_dt_ids, &pdev->dev); |
679 | if (of_id) | ||
680 | id_entry = of_id->data; | ||
681 | else | ||
682 | id_entry = platform_get_device_id(pdev); | ||
683 | |||
684 | dma_type = (struct mxs_dma_type *)id_entry->driver_data; | ||
685 | mxs_dma->type = dma_type->type; | ||
686 | mxs_dma->dev_id = dma_type->id; | ||
610 | 687 | ||
611 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 688 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
612 | 689 | ||
@@ -689,23 +766,12 @@ err_request_region: | |||
689 | return ret; | 766 | return ret; |
690 | } | 767 | } |
691 | 768 | ||
692 | static struct platform_device_id mxs_dma_type[] = { | ||
693 | { | ||
694 | .name = "mxs-dma-apbh", | ||
695 | .driver_data = MXS_DMA_APBH, | ||
696 | }, { | ||
697 | .name = "mxs-dma-apbx", | ||
698 | .driver_data = MXS_DMA_APBX, | ||
699 | }, { | ||
700 | /* end of list */ | ||
701 | } | ||
702 | }; | ||
703 | |||
704 | static struct platform_driver mxs_dma_driver = { | 769 | static struct platform_driver mxs_dma_driver = { |
705 | .driver = { | 770 | .driver = { |
706 | .name = "mxs-dma", | 771 | .name = "mxs-dma", |
772 | .of_match_table = mxs_dma_dt_ids, | ||
707 | }, | 773 | }, |
708 | .id_table = mxs_dma_type, | 774 | .id_table = mxs_dma_ids, |
709 | }; | 775 | }; |
710 | 776 | ||
711 | static int __init mxs_dma_module_init(void) | 777 | static int __init mxs_dma_module_init(void) |