| .. | .. |
|---|
| 29 | 29 | #define S3C64XX_SPI_CH_CFG 0x00 |
|---|
| 30 | 30 | #define S3C64XX_SPI_CLK_CFG 0x04 |
|---|
| 31 | 31 | #define S3C64XX_SPI_MODE_CFG 0x08 |
|---|
| 32 | | -#define S3C64XX_SPI_SLAVE_SEL 0x0C |
|---|
| 32 | +#define S3C64XX_SPI_CS_REG 0x0C |
|---|
| 33 | 33 | #define S3C64XX_SPI_INT_EN 0x10 |
|---|
| 34 | 34 | #define S3C64XX_SPI_STATUS 0x14 |
|---|
| 35 | 35 | #define S3C64XX_SPI_TX_DATA 0x18 |
|---|
| .. | .. |
|---|
| 64 | 64 | #define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) |
|---|
| 65 | 65 | #define S3C64XX_SPI_MODE_4BURST (1<<0) |
|---|
| 66 | 66 | |
|---|
| 67 | | -#define S3C64XX_SPI_SLAVE_AUTO (1<<1) |
|---|
| 68 | | -#define S3C64XX_SPI_SLAVE_SIG_INACT (1<<0) |
|---|
| 69 | | -#define S3C64XX_SPI_SLAVE_NSC_CNT_2 (2<<4) |
|---|
| 67 | +#define S3C64XX_SPI_CS_NSC_CNT_2 (2<<4) |
|---|
| 68 | +#define S3C64XX_SPI_CS_AUTO (1<<1) |
|---|
| 69 | +#define S3C64XX_SPI_CS_SIG_INACT (1<<0) |
|---|
| 70 | 70 | |
|---|
| 71 | 71 | #define S3C64XX_SPI_INT_TRAILING_EN (1<<6) |
|---|
| 72 | 72 | #define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) |
|---|
| .. | .. |
|---|
| 84 | 84 | #define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) |
|---|
| 85 | 85 | |
|---|
| 86 | 86 | #define S3C64XX_SPI_PACKET_CNT_EN (1<<16) |
|---|
| 87 | +#define S3C64XX_SPI_PACKET_CNT_MASK GENMASK(15, 0) |
|---|
| 87 | 88 | |
|---|
| 88 | 89 | #define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) |
|---|
| 89 | 90 | #define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) |
|---|
| .. | .. |
|---|
| 131 | 132 | * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register. |
|---|
| 132 | 133 | * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter. |
|---|
| 133 | 134 | * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter. |
|---|
| 135 | + * @quirks: Bitmask of known quirks |
|---|
| 134 | 136 | * @high_speed: True, if the controller supports HIGH_SPEED_EN bit. |
|---|
| 135 | 137 | * @clk_from_cmu: True, if the controller does not include a clock mux and |
|---|
| 136 | 138 | * prescaler unit. |
|---|
| 139 | + * @clk_ioclk: True if clock is present on this device |
|---|
| 137 | 140 | * |
|---|
| 138 | 141 | * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but |
|---|
| 139 | 142 | * differ in some aspects such as the size of the fifo and spi bus clock |
|---|
| .. | .. |
|---|
| 155 | 158 | * @clk: Pointer to the spi clock. |
|---|
| 156 | 159 | * @src_clk: Pointer to the clock used to generate SPI signals. |
|---|
| 157 | 160 | * @ioclk: Pointer to the i/o clock between master and slave |
|---|
| 161 | + * @pdev: Pointer to device's platform device data |
|---|
| 158 | 162 | * @master: Pointer to the SPI Protocol master. |
|---|
| 159 | 163 | * @cntrlr_info: Platform specific data for the controller this driver manages. |
|---|
| 160 | 164 | * @lock: Controller specific lock. |
|---|
| 161 | 165 | * @state: Set of FLAGS to indicate status. |
|---|
| 162 | | - * @rx_dmach: Controller's DMA channel for Rx. |
|---|
| 163 | | - * @tx_dmach: Controller's DMA channel for Tx. |
|---|
| 164 | 166 | * @sfr_start: BUS address of SPI controller regs. |
|---|
| 165 | 167 | * @regs: Pointer to ioremap'ed controller registers. |
|---|
| 166 | | - * @irq: interrupt |
|---|
| 167 | 168 | * @xfer_completion: To indicate completion of xfer task. |
|---|
| 168 | 169 | * @cur_mode: Stores the active configuration of the controller. |
|---|
| 169 | 170 | * @cur_bpw: Stores the active bits per word settings. |
|---|
| 170 | | - * @cur_speed: Stores the active xfer clock speed. |
|---|
| 171 | + * @cur_speed: Current clock speed |
|---|
| 172 | + * @rx_dma: Local receive DMA data (e.g. chan and direction) |
|---|
| 173 | + * @tx_dma: Local transmit DMA data (e.g. chan and direction) |
|---|
| 174 | + * @port_conf: Local SPI port configuartion data |
|---|
| 175 | + * @port_id: Port identification number |
|---|
| 171 | 176 | */ |
|---|
| 172 | 177 | struct s3c64xx_spi_driver_data { |
|---|
| 173 | 178 | void __iomem *regs; |
|---|
| .. | .. |
|---|
| 176 | 181 | struct clk *ioclk; |
|---|
| 177 | 182 | struct platform_device *pdev; |
|---|
| 178 | 183 | struct spi_master *master; |
|---|
| 179 | | - struct s3c64xx_spi_info *cntrlr_info; |
|---|
| 184 | + struct s3c64xx_spi_info *cntrlr_info; |
|---|
| 180 | 185 | spinlock_t lock; |
|---|
| 181 | 186 | unsigned long sfr_start; |
|---|
| 182 | 187 | struct completion xfer_completion; |
|---|
| .. | .. |
|---|
| 325 | 330 | |
|---|
| 326 | 331 | if (enable) { |
|---|
| 327 | 332 | if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { |
|---|
| 328 | | - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 333 | + writel(0, sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 329 | 334 | } else { |
|---|
| 330 | | - u32 ssel = readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 335 | + u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 331 | 336 | |
|---|
| 332 | | - ssel |= (S3C64XX_SPI_SLAVE_AUTO | |
|---|
| 333 | | - S3C64XX_SPI_SLAVE_NSC_CNT_2); |
|---|
| 334 | | - writel(ssel, sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 337 | + ssel |= (S3C64XX_SPI_CS_AUTO | |
|---|
| 338 | + S3C64XX_SPI_CS_NSC_CNT_2); |
|---|
| 339 | + writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 335 | 340 | } |
|---|
| 336 | 341 | } else { |
|---|
| 337 | 342 | if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) |
|---|
| 338 | | - writel(S3C64XX_SPI_SLAVE_SIG_INACT, |
|---|
| 339 | | - sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 343 | + writel(S3C64XX_SPI_CS_SIG_INACT, |
|---|
| 344 | + sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 340 | 345 | } |
|---|
| 341 | 346 | } |
|---|
| 342 | 347 | |
|---|
| .. | .. |
|---|
| 469 | 474 | |
|---|
| 470 | 475 | /* millisecs to xfer 'len' bytes @ 'cur_speed' */ |
|---|
| 471 | 476 | ms = xfer->len * 8 * 1000 / sdd->cur_speed; |
|---|
| 472 | | - ms += 10; /* some tolerance */ |
|---|
| 477 | + ms += 30; /* some tolerance */ |
|---|
| 478 | + ms = max(ms, 100); /* minimum timeout */ |
|---|
| 473 | 479 | |
|---|
| 474 | 480 | val = msecs_to_jiffies(ms) + 10; |
|---|
| 475 | 481 | val = wait_for_completion_timeout(&sdd->xfer_completion, val); |
|---|
| .. | .. |
|---|
| 622 | 628 | ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); |
|---|
| 623 | 629 | if (ret) |
|---|
| 624 | 630 | return ret; |
|---|
| 631 | + sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2; |
|---|
| 625 | 632 | } else { |
|---|
| 626 | 633 | /* Configure Clock */ |
|---|
| 627 | 634 | val = readl(regs + S3C64XX_SPI_CLK_CFG); |
|---|
| .. | .. |
|---|
| 652 | 659 | writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); |
|---|
| 653 | 660 | |
|---|
| 654 | 661 | return 0; |
|---|
| 662 | +} |
|---|
| 663 | + |
|---|
| 664 | +static size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi) |
|---|
| 665 | +{ |
|---|
| 666 | + struct spi_controller *ctlr = spi->controller; |
|---|
| 667 | + |
|---|
| 668 | + return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX; |
|---|
| 655 | 669 | } |
|---|
| 656 | 670 | |
|---|
| 657 | 671 | static int s3c64xx_spi_transfer_one(struct spi_master *master, |
|---|
| .. | .. |
|---|
| 724 | 738 | |
|---|
| 725 | 739 | if (status) { |
|---|
| 726 | 740 | dev_err(&spi->dev, |
|---|
| 727 | | - "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", |
|---|
| 741 | + "I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d)\n", |
|---|
| 728 | 742 | xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, |
|---|
| 729 | 743 | (sdd->state & RXBUSY) ? 'f' : 'p', |
|---|
| 730 | 744 | (sdd->state & TXBUSY) ? 'f' : 'p', |
|---|
| 731 | | - xfer->len); |
|---|
| 745 | + xfer->len, use_dma ? 1 : 0, status); |
|---|
| 732 | 746 | |
|---|
| 733 | 747 | if (use_dma) { |
|---|
| 734 | | - if (xfer->tx_buf && (sdd->state & TXBUSY)) |
|---|
| 748 | + struct dma_tx_state s; |
|---|
| 749 | + |
|---|
| 750 | + if (xfer->tx_buf && (sdd->state & TXBUSY)) { |
|---|
| 751 | + dmaengine_pause(sdd->tx_dma.ch); |
|---|
| 752 | + dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s); |
|---|
| 735 | 753 | dmaengine_terminate_all(sdd->tx_dma.ch); |
|---|
| 736 | | - if (xfer->rx_buf && (sdd->state & RXBUSY)) |
|---|
| 754 | + dev_err(&spi->dev, "TX residue: %d\n", s.residue); |
|---|
| 755 | + |
|---|
| 756 | + } |
|---|
| 757 | + if (xfer->rx_buf && (sdd->state & RXBUSY)) { |
|---|
| 758 | + dmaengine_pause(sdd->rx_dma.ch); |
|---|
| 759 | + dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s); |
|---|
| 737 | 760 | dmaengine_terminate_all(sdd->rx_dma.ch); |
|---|
| 761 | + dev_err(&spi->dev, "RX residue: %d\n", s.residue); |
|---|
| 762 | + } |
|---|
| 738 | 763 | } |
|---|
| 739 | 764 | } else { |
|---|
| 740 | 765 | s3c64xx_flush_fifo(sdd); |
|---|
| .. | .. |
|---|
| 964 | 989 | sdd->cur_speed = 0; |
|---|
| 965 | 990 | |
|---|
| 966 | 991 | if (sci->no_cs) |
|---|
| 967 | | - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 992 | + writel(0, sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 968 | 993 | else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) |
|---|
| 969 | | - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); |
|---|
| 994 | + writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); |
|---|
| 970 | 995 | |
|---|
| 971 | 996 | /* Disable Interrupts - we use Polling if not DMA mode */ |
|---|
| 972 | 997 | writel(0, regs + S3C64XX_SPI_INT_EN); |
|---|
| .. | .. |
|---|
| 1118 | 1143 | master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; |
|---|
| 1119 | 1144 | master->prepare_message = s3c64xx_spi_prepare_message; |
|---|
| 1120 | 1145 | master->transfer_one = s3c64xx_spi_transfer_one; |
|---|
| 1146 | + master->max_transfer_size = s3c64xx_spi_max_transfer_size; |
|---|
| 1121 | 1147 | master->num_chipselect = sci->num_cs; |
|---|
| 1122 | 1148 | master->dma_alignment = 8; |
|---|
| 1123 | 1149 | master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | |
|---|
| .. | .. |
|---|
| 1186 | 1212 | |
|---|
| 1187 | 1213 | if (!is_polling(sdd)) { |
|---|
| 1188 | 1214 | /* Acquire DMA channels */ |
|---|
| 1189 | | - sdd->rx_dma.ch = dma_request_slave_channel_reason(&pdev->dev, |
|---|
| 1190 | | - "rx"); |
|---|
| 1215 | + sdd->rx_dma.ch = dma_request_chan(&pdev->dev, "rx"); |
|---|
| 1191 | 1216 | if (IS_ERR(sdd->rx_dma.ch)) { |
|---|
| 1192 | 1217 | dev_err(&pdev->dev, "Failed to get RX DMA channel\n"); |
|---|
| 1193 | 1218 | ret = PTR_ERR(sdd->rx_dma.ch); |
|---|
| 1194 | 1219 | goto err_disable_io_clk; |
|---|
| 1195 | 1220 | } |
|---|
| 1196 | | - sdd->tx_dma.ch = dma_request_slave_channel_reason(&pdev->dev, |
|---|
| 1197 | | - "tx"); |
|---|
| 1221 | + sdd->tx_dma.ch = dma_request_chan(&pdev->dev, "tx"); |
|---|
| 1198 | 1222 | if (IS_ERR(sdd->tx_dma.ch)) { |
|---|
| 1199 | 1223 | dev_err(&pdev->dev, "Failed to get TX DMA channel\n"); |
|---|
| 1200 | 1224 | ret = PTR_ERR(sdd->tx_dma.ch); |
|---|
| .. | .. |
|---|
| 1363 | 1387 | |
|---|
| 1364 | 1388 | s3c64xx_spi_hwinit(sdd); |
|---|
| 1365 | 1389 | |
|---|
| 1390 | + writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | |
|---|
| 1391 | + S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, |
|---|
| 1392 | + sdd->regs + S3C64XX_SPI_INT_EN); |
|---|
| 1393 | + |
|---|
| 1366 | 1394 | return 0; |
|---|
| 1367 | 1395 | |
|---|
| 1368 | 1396 | err_disable_src_clk: |
|---|
| .. | .. |
|---|
| 1406 | 1434 | .tx_st_done = 25, |
|---|
| 1407 | 1435 | .high_speed = true, |
|---|
| 1408 | 1436 | .clk_from_cmu = true, |
|---|
| 1437 | + .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, |
|---|
| 1409 | 1438 | }; |
|---|
| 1410 | 1439 | |
|---|
| 1411 | 1440 | static struct s3c64xx_spi_port_config exynos7_spi_port_config = { |
|---|