diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/spi/spi_s3c64xx.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/spi/spi_s3c64xx.c')
-rw-r--r-- | drivers/spi/spi_s3c64xx.c | 162 |
1 files changed, 106 insertions, 56 deletions
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index c3038da2648a..8945e201e42e 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c | |||
@@ -116,9 +116,7 @@ | |||
116 | (((i)->fifo_lvl_mask + 1))) \ | 116 | (((i)->fifo_lvl_mask + 1))) \ |
117 | ? 1 : 0) | 117 | ? 1 : 0) |
118 | 118 | ||
119 | #define S3C64XX_SPI_ST_TX_DONE(v, i) ((((v) >> (i)->rx_lvl_offset) & \ | 119 | #define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & (1 << (i)->tx_st_done)) ? 1 : 0) |
120 | (((i)->fifo_lvl_mask + 1) << 1)) \ | ||
121 | ? 1 : 0) | ||
122 | #define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask) | 120 | #define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask) |
123 | #define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask) | 121 | #define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask) |
124 | 122 | ||
@@ -261,15 +259,25 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | |||
261 | chcfg |= S3C64XX_SPI_CH_TXCH_ON; | 259 | chcfg |= S3C64XX_SPI_CH_TXCH_ON; |
262 | if (dma_mode) { | 260 | if (dma_mode) { |
263 | modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; | 261 | modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; |
264 | s3c2410_dma_config(sdd->tx_dmach, 1); | 262 | s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8); |
265 | s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, | 263 | s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, |
266 | xfer->tx_dma, xfer->len); | 264 | xfer->tx_dma, xfer->len); |
267 | s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); | 265 | s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); |
268 | } else { | 266 | } else { |
269 | unsigned char *buf = (unsigned char *) xfer->tx_buf; | 267 | switch (sdd->cur_bpw) { |
270 | int i = 0; | 268 | case 32: |
271 | while (i < xfer->len) | 269 | iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, |
272 | writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA); | 270 | xfer->tx_buf, xfer->len / 4); |
271 | break; | ||
272 | case 16: | ||
273 | iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, | ||
274 | xfer->tx_buf, xfer->len / 2); | ||
275 | break; | ||
276 | default: | ||
277 | iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, | ||
278 | xfer->tx_buf, xfer->len); | ||
279 | break; | ||
280 | } | ||
273 | } | 281 | } |
274 | } | 282 | } |
275 | 283 | ||
@@ -286,7 +294,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | |||
286 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | 294 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) |
287 | | S3C64XX_SPI_PACKET_CNT_EN, | 295 | | S3C64XX_SPI_PACKET_CNT_EN, |
288 | regs + S3C64XX_SPI_PACKET_CNT); | 296 | regs + S3C64XX_SPI_PACKET_CNT); |
289 | s3c2410_dma_config(sdd->rx_dmach, 1); | 297 | s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8); |
290 | s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, | 298 | s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, |
291 | xfer->rx_dma, xfer->len); | 299 | xfer->rx_dma, xfer->len); |
292 | s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); | 300 | s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); |
@@ -366,20 +374,26 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, | |||
366 | return -EIO; | 374 | return -EIO; |
367 | } | 375 | } |
368 | } else { | 376 | } else { |
369 | unsigned char *buf; | ||
370 | int i; | ||
371 | |||
372 | /* If it was only Tx */ | 377 | /* If it was only Tx */ |
373 | if (xfer->rx_buf == NULL) { | 378 | if (xfer->rx_buf == NULL) { |
374 | sdd->state &= ~TXBUSY; | 379 | sdd->state &= ~TXBUSY; |
375 | return 0; | 380 | return 0; |
376 | } | 381 | } |
377 | 382 | ||
378 | i = 0; | 383 | switch (sdd->cur_bpw) { |
379 | buf = xfer->rx_buf; | 384 | case 32: |
380 | while (i < xfer->len) | 385 | ioread32_rep(regs + S3C64XX_SPI_RX_DATA, |
381 | buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA); | 386 | xfer->rx_buf, xfer->len / 4); |
382 | 387 | break; | |
388 | case 16: | ||
389 | ioread16_rep(regs + S3C64XX_SPI_RX_DATA, | ||
390 | xfer->rx_buf, xfer->len / 2); | ||
391 | break; | ||
392 | default: | ||
393 | ioread8_rep(regs + S3C64XX_SPI_RX_DATA, | ||
394 | xfer->rx_buf, xfer->len); | ||
395 | break; | ||
396 | } | ||
383 | sdd->state &= ~RXBUSY; | 397 | sdd->state &= ~RXBUSY; |
384 | } | 398 | } |
385 | 399 | ||
@@ -399,13 +413,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, | |||
399 | 413 | ||
400 | static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) | 414 | static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) |
401 | { | 415 | { |
416 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
402 | void __iomem *regs = sdd->regs; | 417 | void __iomem *regs = sdd->regs; |
403 | u32 val; | 418 | u32 val; |
404 | 419 | ||
405 | /* Disable Clock */ | 420 | /* Disable Clock */ |
406 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 421 | if (sci->clk_from_cmu) { |
407 | val &= ~S3C64XX_SPI_ENCLK_ENABLE; | 422 | clk_disable(sdd->src_clk); |
408 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 423 | } else { |
424 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
425 | val &= ~S3C64XX_SPI_ENCLK_ENABLE; | ||
426 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
427 | } | ||
409 | 428 | ||
410 | /* Set Polarity and Phase */ | 429 | /* Set Polarity and Phase */ |
411 | val = readl(regs + S3C64XX_SPI_CH_CFG); | 430 | val = readl(regs + S3C64XX_SPI_CH_CFG); |
@@ -429,29 +448,39 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) | |||
429 | switch (sdd->cur_bpw) { | 448 | switch (sdd->cur_bpw) { |
430 | case 32: | 449 | case 32: |
431 | val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; | 450 | val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; |
451 | val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; | ||
432 | break; | 452 | break; |
433 | case 16: | 453 | case 16: |
434 | val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; | 454 | val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; |
455 | val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; | ||
435 | break; | 456 | break; |
436 | default: | 457 | default: |
437 | val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; | 458 | val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; |
459 | val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; | ||
438 | break; | 460 | break; |
439 | } | 461 | } |
440 | val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */ | ||
441 | 462 | ||
442 | writel(val, regs + S3C64XX_SPI_MODE_CFG); | 463 | writel(val, regs + S3C64XX_SPI_MODE_CFG); |
443 | 464 | ||
444 | /* Configure Clock */ | 465 | if (sci->clk_from_cmu) { |
445 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 466 | /* Configure Clock */ |
446 | val &= ~S3C64XX_SPI_PSR_MASK; | 467 | /* There is half-multiplier before the SPI */ |
447 | val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) | 468 | clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); |
448 | & S3C64XX_SPI_PSR_MASK); | 469 | /* Enable Clock */ |
449 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 470 | clk_enable(sdd->src_clk); |
450 | 471 | } else { | |
451 | /* Enable Clock */ | 472 | /* Configure Clock */ |
452 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 473 | val = readl(regs + S3C64XX_SPI_CLK_CFG); |
453 | val |= S3C64XX_SPI_ENCLK_ENABLE; | 474 | val &= ~S3C64XX_SPI_PSR_MASK; |
454 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 475 | val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) |
476 | & S3C64XX_SPI_PSR_MASK); | ||
477 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
478 | |||
479 | /* Enable Clock */ | ||
480 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
481 | val |= S3C64XX_SPI_ENCLK_ENABLE; | ||
482 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
483 | } | ||
455 | } | 484 | } |
456 | 485 | ||
457 | static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, | 486 | static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, |
@@ -499,6 +528,7 @@ static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id, | |||
499 | static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, | 528 | static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, |
500 | struct spi_message *msg) | 529 | struct spi_message *msg) |
501 | { | 530 | { |
531 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
502 | struct device *dev = &sdd->pdev->dev; | 532 | struct device *dev = &sdd->pdev->dev; |
503 | struct spi_transfer *xfer; | 533 | struct spi_transfer *xfer; |
504 | 534 | ||
@@ -514,6 +544,9 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, | |||
514 | /* Map until end or first fail */ | 544 | /* Map until end or first fail */ |
515 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | 545 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
516 | 546 | ||
547 | if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) | ||
548 | continue; | ||
549 | |||
517 | if (xfer->tx_buf != NULL) { | 550 | if (xfer->tx_buf != NULL) { |
518 | xfer->tx_dma = dma_map_single(dev, | 551 | xfer->tx_dma = dma_map_single(dev, |
519 | (void *)xfer->tx_buf, xfer->len, | 552 | (void *)xfer->tx_buf, xfer->len, |
@@ -545,6 +578,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, | |||
545 | static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, | 578 | static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, |
546 | struct spi_message *msg) | 579 | struct spi_message *msg) |
547 | { | 580 | { |
581 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
548 | struct device *dev = &sdd->pdev->dev; | 582 | struct device *dev = &sdd->pdev->dev; |
549 | struct spi_transfer *xfer; | 583 | struct spi_transfer *xfer; |
550 | 584 | ||
@@ -553,6 +587,9 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, | |||
553 | 587 | ||
554 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | 588 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
555 | 589 | ||
590 | if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) | ||
591 | continue; | ||
592 | |||
556 | if (xfer->rx_buf != NULL | 593 | if (xfer->rx_buf != NULL |
557 | && xfer->rx_dma != XFER_DMAADDR_INVALID) | 594 | && xfer->rx_dma != XFER_DMAADDR_INVALID) |
558 | dma_unmap_single(dev, xfer->rx_dma, | 595 | dma_unmap_single(dev, xfer->rx_dma, |
@@ -608,6 +645,14 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, | |||
608 | bpw = xfer->bits_per_word ? : spi->bits_per_word; | 645 | bpw = xfer->bits_per_word ? : spi->bits_per_word; |
609 | speed = xfer->speed_hz ? : spi->max_speed_hz; | 646 | speed = xfer->speed_hz ? : spi->max_speed_hz; |
610 | 647 | ||
648 | if (xfer->len % (bpw / 8)) { | ||
649 | dev_err(&spi->dev, | ||
650 | "Xfer length(%u) not a multiple of word size(%u)\n", | ||
651 | xfer->len, bpw / 8); | ||
652 | status = -EIO; | ||
653 | goto out; | ||
654 | } | ||
655 | |||
611 | if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { | 656 | if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { |
612 | sdd->cur_bpw = bpw; | 657 | sdd->cur_bpw = bpw; |
613 | sdd->cur_speed = speed; | 658 | sdd->cur_speed = speed; |
@@ -798,7 +843,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
798 | struct s3c64xx_spi_driver_data *sdd; | 843 | struct s3c64xx_spi_driver_data *sdd; |
799 | struct s3c64xx_spi_info *sci; | 844 | struct s3c64xx_spi_info *sci; |
800 | struct spi_message *msg; | 845 | struct spi_message *msg; |
801 | u32 psr, speed; | ||
802 | unsigned long flags; | 846 | unsigned long flags; |
803 | int err = 0; | 847 | int err = 0; |
804 | 848 | ||
@@ -841,32 +885,37 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
841 | } | 885 | } |
842 | 886 | ||
843 | /* Check if we can provide the requested rate */ | 887 | /* Check if we can provide the requested rate */ |
844 | speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */ | 888 | if (!sci->clk_from_cmu) { |
845 | 889 | u32 psr, speed; | |
846 | if (spi->max_speed_hz > speed) | 890 | |
847 | spi->max_speed_hz = speed; | 891 | /* Max possible */ |
848 | 892 | speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); | |
849 | psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; | 893 | |
850 | psr &= S3C64XX_SPI_PSR_MASK; | 894 | if (spi->max_speed_hz > speed) |
851 | if (psr == S3C64XX_SPI_PSR_MASK) | 895 | spi->max_speed_hz = speed; |
852 | psr--; | 896 | |
897 | psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; | ||
898 | psr &= S3C64XX_SPI_PSR_MASK; | ||
899 | if (psr == S3C64XX_SPI_PSR_MASK) | ||
900 | psr--; | ||
901 | |||
902 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
903 | if (spi->max_speed_hz < speed) { | ||
904 | if (psr+1 < S3C64XX_SPI_PSR_MASK) { | ||
905 | psr++; | ||
906 | } else { | ||
907 | err = -EINVAL; | ||
908 | goto setup_exit; | ||
909 | } | ||
910 | } | ||
853 | 911 | ||
854 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | 912 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); |
855 | if (spi->max_speed_hz < speed) { | 913 | if (spi->max_speed_hz >= speed) |
856 | if (psr+1 < S3C64XX_SPI_PSR_MASK) { | 914 | spi->max_speed_hz = speed; |
857 | psr++; | 915 | else |
858 | } else { | ||
859 | err = -EINVAL; | 916 | err = -EINVAL; |
860 | goto setup_exit; | ||
861 | } | ||
862 | } | 917 | } |
863 | 918 | ||
864 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
865 | if (spi->max_speed_hz >= speed) | ||
866 | spi->max_speed_hz = speed; | ||
867 | else | ||
868 | err = -EINVAL; | ||
869 | |||
870 | setup_exit: | 919 | setup_exit: |
871 | 920 | ||
872 | /* setup() returns with device de-selected */ | 921 | /* setup() returns with device de-selected */ |
@@ -888,7 +937,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) | |||
888 | /* Disable Interrupts - we use Polling if not DMA mode */ | 937 | /* Disable Interrupts - we use Polling if not DMA mode */ |
889 | writel(0, regs + S3C64XX_SPI_INT_EN); | 938 | writel(0, regs + S3C64XX_SPI_INT_EN); |
890 | 939 | ||
891 | writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, | 940 | if (!sci->clk_from_cmu) |
941 | writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, | ||
892 | regs + S3C64XX_SPI_CLK_CFG); | 942 | regs + S3C64XX_SPI_CLK_CFG); |
893 | writel(0, regs + S3C64XX_SPI_MODE_CFG); | 943 | writel(0, regs + S3C64XX_SPI_MODE_CFG); |
894 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); | 944 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); |