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