diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 140 |
1 files changed, 76 insertions, 64 deletions
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 24f49032ec35..019a7163572f 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c | |||
@@ -131,6 +131,12 @@ | |||
131 | #define RXBUSY (1<<2) | 131 | #define RXBUSY (1<<2) |
132 | #define TXBUSY (1<<3) | 132 | #define TXBUSY (1<<3) |
133 | 133 | ||
134 | struct s3c64xx_spi_dma_data { | ||
135 | unsigned ch; | ||
136 | enum dma_data_direction direction; | ||
137 | enum dma_ch dmach; | ||
138 | }; | ||
139 | |||
134 | /** | 140 | /** |
135 | * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. | 141 | * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. |
136 | * @clk: Pointer to the spi clock. | 142 | * @clk: Pointer to the spi clock. |
@@ -164,15 +170,13 @@ struct s3c64xx_spi_driver_data { | |||
164 | struct work_struct work; | 170 | struct work_struct work; |
165 | struct list_head queue; | 171 | struct list_head queue; |
166 | spinlock_t lock; | 172 | spinlock_t lock; |
167 | enum dma_ch rx_dmach; | ||
168 | enum dma_ch tx_dmach; | ||
169 | unsigned long sfr_start; | 173 | unsigned long sfr_start; |
170 | struct completion xfer_completion; | 174 | struct completion xfer_completion; |
171 | unsigned state; | 175 | unsigned state; |
172 | unsigned cur_mode, cur_bpw; | 176 | unsigned cur_mode, cur_bpw; |
173 | unsigned cur_speed; | 177 | unsigned cur_speed; |
174 | unsigned rx_ch; | 178 | struct s3c64xx_spi_dma_data rx_dma; |
175 | unsigned tx_ch; | 179 | struct s3c64xx_spi_dma_data tx_dma; |
176 | struct samsung_dma_ops *ops; | 180 | struct samsung_dma_ops *ops; |
177 | }; | 181 | }; |
178 | 182 | ||
@@ -229,36 +233,76 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) | |||
229 | writel(val, regs + S3C64XX_SPI_CH_CFG); | 233 | writel(val, regs + S3C64XX_SPI_CH_CFG); |
230 | } | 234 | } |
231 | 235 | ||
232 | static void s3c64xx_spi_dma_rxcb(void *data) | 236 | static void s3c64xx_spi_dmacb(void *data) |
233 | { | 237 | { |
234 | struct s3c64xx_spi_driver_data *sdd | 238 | struct s3c64xx_spi_driver_data *sdd; |
235 | = (struct s3c64xx_spi_driver_data *)data; | 239 | struct s3c64xx_spi_dma_data *dma = data; |
236 | unsigned long flags; | 240 | unsigned long flags; |
237 | 241 | ||
242 | if (dma->direction == DMA_FROM_DEVICE) | ||
243 | sdd = container_of(data, | ||
244 | struct s3c64xx_spi_driver_data, rx_dma); | ||
245 | else | ||
246 | sdd = container_of(data, | ||
247 | struct s3c64xx_spi_driver_data, tx_dma); | ||
248 | |||
238 | spin_lock_irqsave(&sdd->lock, flags); | 249 | spin_lock_irqsave(&sdd->lock, flags); |
239 | 250 | ||
240 | sdd->state &= ~RXBUSY; | 251 | if (dma->direction == DMA_FROM_DEVICE) { |
241 | /* If the other done */ | 252 | sdd->state &= ~RXBUSY; |
242 | if (!(sdd->state & TXBUSY)) | 253 | if (!(sdd->state & TXBUSY)) |
243 | complete(&sdd->xfer_completion); | 254 | complete(&sdd->xfer_completion); |
255 | } else { | ||
256 | sdd->state &= ~TXBUSY; | ||
257 | if (!(sdd->state & RXBUSY)) | ||
258 | complete(&sdd->xfer_completion); | ||
259 | } | ||
244 | 260 | ||
245 | spin_unlock_irqrestore(&sdd->lock, flags); | 261 | spin_unlock_irqrestore(&sdd->lock, flags); |
246 | } | 262 | } |
247 | 263 | ||
248 | static void s3c64xx_spi_dma_txcb(void *data) | 264 | static void prepare_dma(struct s3c64xx_spi_dma_data *dma, |
265 | unsigned len, dma_addr_t buf) | ||
249 | { | 266 | { |
250 | struct s3c64xx_spi_driver_data *sdd | 267 | struct s3c64xx_spi_driver_data *sdd; |
251 | = (struct s3c64xx_spi_driver_data *)data; | 268 | struct samsung_dma_prep_info info; |
252 | unsigned long flags; | ||
253 | 269 | ||
254 | spin_lock_irqsave(&sdd->lock, flags); | 270 | if (dma->direction == DMA_FROM_DEVICE) |
271 | sdd = container_of((void *)dma, | ||
272 | struct s3c64xx_spi_driver_data, rx_dma); | ||
273 | else | ||
274 | sdd = container_of((void *)dma, | ||
275 | struct s3c64xx_spi_driver_data, tx_dma); | ||
255 | 276 | ||
256 | sdd->state &= ~TXBUSY; | 277 | info.cap = DMA_SLAVE; |
257 | /* If the other done */ | 278 | info.len = len; |
258 | if (!(sdd->state & RXBUSY)) | 279 | info.fp = s3c64xx_spi_dmacb; |
259 | complete(&sdd->xfer_completion); | 280 | info.fp_param = dma; |
281 | info.direction = dma->direction; | ||
282 | info.buf = buf; | ||
283 | |||
284 | sdd->ops->prepare(dma->ch, &info); | ||
285 | sdd->ops->trigger(dma->ch); | ||
286 | } | ||
260 | 287 | ||
261 | spin_unlock_irqrestore(&sdd->lock, flags); | 288 | static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) |
289 | { | ||
290 | struct samsung_dma_info info; | ||
291 | |||
292 | sdd->ops = samsung_dma_get_ops(); | ||
293 | |||
294 | info.cap = DMA_SLAVE; | ||
295 | info.client = &s3c64xx_spi_dma_client; | ||
296 | info.width = sdd->cur_bpw / 8; | ||
297 | |||
298 | info.direction = sdd->rx_dma.direction; | ||
299 | info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; | ||
300 | sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &info); | ||
301 | info.direction = sdd->tx_dma.direction; | ||
302 | info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; | ||
303 | sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &info); | ||
304 | |||
305 | return 1; | ||
262 | } | 306 | } |
263 | 307 | ||
264 | static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | 308 | static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, |
@@ -268,7 +312,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | |||
268 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | 312 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
269 | void __iomem *regs = sdd->regs; | 313 | void __iomem *regs = sdd->regs; |
270 | u32 modecfg, chcfg; | 314 | u32 modecfg, chcfg; |
271 | struct samsung_dma_prep_info info; | ||
272 | 315 | ||
273 | modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); | 316 | modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); |
274 | modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); | 317 | modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); |
@@ -294,14 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | |||
294 | chcfg |= S3C64XX_SPI_CH_TXCH_ON; | 337 | chcfg |= S3C64XX_SPI_CH_TXCH_ON; |
295 | if (dma_mode) { | 338 | if (dma_mode) { |
296 | modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; | 339 | modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; |
297 | info.cap = DMA_SLAVE; | 340 | prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); |
298 | info.direction = DMA_TO_DEVICE; | ||
299 | info.buf = xfer->tx_dma; | ||
300 | info.len = xfer->len; | ||
301 | info.fp = s3c64xx_spi_dma_txcb; | ||
302 | info.fp_param = sdd; | ||
303 | sdd->ops->prepare(sdd->tx_ch, &info); | ||
304 | sdd->ops->trigger(sdd->tx_ch); | ||
305 | } else { | 341 | } else { |
306 | switch (sdd->cur_bpw) { | 342 | switch (sdd->cur_bpw) { |
307 | case 32: | 343 | case 32: |
@@ -333,14 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | |||
333 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | 369 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) |
334 | | S3C64XX_SPI_PACKET_CNT_EN, | 370 | | S3C64XX_SPI_PACKET_CNT_EN, |
335 | regs + S3C64XX_SPI_PACKET_CNT); | 371 | regs + S3C64XX_SPI_PACKET_CNT); |
336 | info.cap = DMA_SLAVE; | 372 | prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); |
337 | info.direction = DMA_FROM_DEVICE; | ||
338 | info.buf = xfer->rx_dma; | ||
339 | info.len = xfer->len; | ||
340 | info.fp = s3c64xx_spi_dma_rxcb; | ||
341 | info.fp_param = sdd; | ||
342 | sdd->ops->prepare(sdd->rx_ch, &info); | ||
343 | sdd->ops->trigger(sdd->rx_ch); | ||
344 | } | 373 | } |
345 | } | 374 | } |
346 | 375 | ||
@@ -700,10 +729,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, | |||
700 | if (use_dma) { | 729 | if (use_dma) { |
701 | if (xfer->tx_buf != NULL | 730 | if (xfer->tx_buf != NULL |
702 | && (sdd->state & TXBUSY)) | 731 | && (sdd->state & TXBUSY)) |
703 | sdd->ops->stop(sdd->tx_ch); | 732 | sdd->ops->stop(sdd->tx_dma.ch); |
704 | if (xfer->rx_buf != NULL | 733 | if (xfer->rx_buf != NULL |
705 | && (sdd->state & RXBUSY)) | 734 | && (sdd->state & RXBUSY)) |
706 | sdd->ops->stop(sdd->rx_ch); | 735 | sdd->ops->stop(sdd->rx_dma.ch); |
707 | } | 736 | } |
708 | 737 | ||
709 | goto out; | 738 | goto out; |
@@ -741,25 +770,6 @@ out: | |||
741 | msg->complete(msg->context); | 770 | msg->complete(msg->context); |
742 | } | 771 | } |
743 | 772 | ||
744 | static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) | ||
745 | { | ||
746 | |||
747 | struct samsung_dma_info info; | ||
748 | sdd->ops = samsung_dma_get_ops(); | ||
749 | |||
750 | info.cap = DMA_SLAVE; | ||
751 | info.client = &s3c64xx_spi_dma_client; | ||
752 | info.direction = DMA_FROM_DEVICE; | ||
753 | info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; | ||
754 | info.width = sdd->cur_bpw / 8; | ||
755 | sdd->rx_ch = sdd->ops->request(sdd->rx_dmach, &info); | ||
756 | info.direction = DMA_TO_DEVICE; | ||
757 | info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; | ||
758 | sdd->tx_ch = sdd->ops->request(sdd->tx_dmach, &info); | ||
759 | |||
760 | return 1; | ||
761 | } | ||
762 | |||
763 | static void s3c64xx_spi_work(struct work_struct *work) | 773 | static void s3c64xx_spi_work(struct work_struct *work) |
764 | { | 774 | { |
765 | struct s3c64xx_spi_driver_data *sdd = container_of(work, | 775 | struct s3c64xx_spi_driver_data *sdd = container_of(work, |
@@ -796,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work) | |||
796 | spin_unlock_irqrestore(&sdd->lock, flags); | 806 | spin_unlock_irqrestore(&sdd->lock, flags); |
797 | 807 | ||
798 | /* Free DMA channels */ | 808 | /* Free DMA channels */ |
799 | sdd->ops->release(sdd->rx_ch, &s3c64xx_spi_dma_client); | 809 | sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); |
800 | sdd->ops->release(sdd->tx_ch, &s3c64xx_spi_dma_client); | 810 | sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); |
801 | } | 811 | } |
802 | 812 | ||
803 | static int s3c64xx_spi_transfer(struct spi_device *spi, | 813 | static int s3c64xx_spi_transfer(struct spi_device *spi, |
@@ -1014,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
1014 | sdd->cntrlr_info = sci; | 1024 | sdd->cntrlr_info = sci; |
1015 | sdd->pdev = pdev; | 1025 | sdd->pdev = pdev; |
1016 | sdd->sfr_start = mem_res->start; | 1026 | sdd->sfr_start = mem_res->start; |
1017 | sdd->tx_dmach = dmatx_res->start; | 1027 | sdd->tx_dma.dmach = dmatx_res->start; |
1018 | sdd->rx_dmach = dmarx_res->start; | 1028 | sdd->tx_dma.direction = DMA_TO_DEVICE; |
1029 | sdd->rx_dma.dmach = dmarx_res->start; | ||
1030 | sdd->rx_dma.direction = DMA_FROM_DEVICE; | ||
1019 | 1031 | ||
1020 | sdd->cur_bpw = 8; | 1032 | sdd->cur_bpw = 8; |
1021 | 1033 | ||
@@ -1103,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
1103 | pdev->id, master->num_chipselect); | 1115 | pdev->id, master->num_chipselect); |
1104 | dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", | 1116 | dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", |
1105 | mem_res->end, mem_res->start, | 1117 | mem_res->end, mem_res->start, |
1106 | sdd->rx_dmach, sdd->tx_dmach); | 1118 | sdd->rx_dma.dmach, sdd->tx_dma.dmach); |
1107 | 1119 | ||
1108 | return 0; | 1120 | return 0; |
1109 | 1121 | ||