diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-at91.c')
-rw-r--r-- | drivers/i2c/busses/i2c-at91.c | 147 |
1 files changed, 130 insertions, 17 deletions
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index abd93f3a05a0..9e54f97a9020 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -54,6 +54,8 @@ #define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */ #define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */ #define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */ +#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */ +#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */ #define AT91_TWI_MMR 0x0004 /* Master Mode Register */ #define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */ @@ -85,6 +87,22 @@ #define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) #define AT91_TWI_ACR_DIR BIT(8) +#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */ +#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0) +#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0) +#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4) +#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4) +#define AT91_TWI_ONE_DATA 0x0 +#define AT91_TWI_TWO_DATA 0x1 +#define AT91_TWI_FOUR_DATA 0x2 + +#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */ + +#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */ +#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */ +#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */ +#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */ + #define AT91_TWI_VER 0x00fc /* Version Register */ struct at91_twi_pdata { @@ -98,7 +116,7 @@ struct at91_twi_pdata { struct at91_twi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; - struct scatterlist sg; + struct scatterlist sg[2]; struct dma_async_tx_descriptor *data_desc; enum dma_data_direction direction; bool buf_mapped; @@ -121,6 +139,7 @@ struct at91_twi_dev { struct at91_twi_pdata *pdata; bool use_dma; bool recv_len_abort; + u32 fifo_size; struct at91_twi_dma dma; }; @@ -154,6 +173,9 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev) { at91_disable_twi_interrupts(dev); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); + /* FIFO should be enabled immediately after the software reset */ + if (dev->fifo_size) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS); at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg); @@ -200,7 +222,7 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev) dma->xfer_in_progress = false; } if (dma->buf_mapped) { - dma_unmap_single(dev->dev, sg_dma_address(&dma->sg), + dma_unmap_single(dev->dev, sg_dma_address(&dma->sg[0]), dev->buf_len, dma->direction); dma->buf_mapped = false; } @@ -213,7 +235,8 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev) if (dev->buf_len <= 0) return; - at91_twi_write(dev, AT91_TWI_THR, *dev->buf); + /* 8bit write works with and without FIFO */ + writeb_relaxed(*dev->buf, dev->base + AT91_TWI_THR); /* send stop when last byte has been written */ if (--dev->buf_len == 0) @@ -229,7 +252,7 @@ static void at91_twi_write_data_dma_callback(void *data) { struct at91_twi_dev *dev = (struct at91_twi_dev *)data; - dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), dev->buf_len, DMA_TO_DEVICE); /* @@ -250,6 +273,7 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) struct dma_async_tx_descriptor *txdesc; struct at91_twi_dma *dma = &dev->dma; struct dma_chan *chan_tx = dma->chan_tx; + unsigned int sg_len = 1; if (dev->buf_len <= 0) return; @@ -265,10 +289,43 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) } dma->buf_mapped = true; at91_twi_irq_restore(dev); - sg_dma_len(&dma->sg) = dev->buf_len; - sg_dma_address(&dma->sg) = dma_addr; - txdesc = dmaengine_prep_slave_sg(chan_tx, &dma->sg, 1, DMA_MEM_TO_DEV, + if (dev->fifo_size) { + size_t part1_len, part2_len; + struct scatterlist *sg; + unsigned fifo_mr; + + sg_len = 0; + + part1_len = dev->buf_len & ~0x3; + if (part1_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part1_len; + sg_dma_address(sg) = dma_addr; + } + + part2_len = dev->buf_len & 0x3; + if (part2_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part2_len; + sg_dma_address(sg) = dma_addr + part1_len; + } + + /* + * DMA controller is triggered when at least 4 data can be + * written into the TX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_TXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } else { + sg_dma_len(&dma->sg[0]) = dev->buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + } + + txdesc = dmaengine_prep_slave_sg(chan_tx, dma->sg, sg_len, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) { dev_err(dev->dev, "dma prep slave sg failed\n"); @@ -293,7 +350,8 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) if (dev->buf_len <= 0) return; - *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff; + /* 8bit read works with and without FIFO */ + *dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR); --dev->buf_len; /* return if aborting, we only needed to read RHR to clear RXRDY*/ @@ -330,7 +388,7 @@ static void at91_twi_read_data_dma_callback(void *data) struct at91_twi_dev *dev = (struct at91_twi_dev *)data; unsigned ier = AT91_TWI_TXCOMP; - dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), dev->buf_len, DMA_FROM_DEVICE); if (!dev->pdata->has_alt_cmd) { @@ -362,10 +420,24 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev) } dma->buf_mapped = true; at91_twi_irq_restore(dev); - dma->sg.dma_address = dma_addr; - sg_dma_len(&dma->sg) = buf_len; - rxdesc = dmaengine_prep_slave_sg(chan_rx, &dma->sg, 1, DMA_DEV_TO_MEM, + if (dev->fifo_size && IS_ALIGNED(buf_len, 4)) { + unsigned fifo_mr; + + /* + * DMA controller is triggered when at least 4 data can be + * read from the RX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_RXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } + + sg_dma_len(&dma->sg[0]) = buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + + rxdesc = dmaengine_prep_slave_sg(chan_rx, dma->sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) { dev_err(dev->dev, "dma prep slave sg failed\n"); @@ -465,6 +537,21 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) reinit_completion(&dev->cmd_complete); dev->transfer_status = 0; + if (dev->fifo_size) { + unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + + /* Reset FIFO mode register */ + fifo_mr &= ~(AT91_TWI_FMR_TXRDYM_MASK | + AT91_TWI_FMR_RXRDYM_MASK); + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_ONE_DATA); + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_ONE_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + + /* Flush FIFOs */ + at91_twi_write(dev, AT91_TWI_CR, + AT91_TWI_THRCLR | AT91_TWI_RHRCLR); + } + if (!dev->buf_len) { at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK); at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); @@ -536,7 +623,8 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ret = -EIO; goto error; } - if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { dev_err(dev->dev, "tx locked\n"); ret = -EIO; goto error; @@ -555,7 +643,8 @@ error: /* first stop DMA transfer if still in progress */ at91_twi_dma_cleanup(dev); /* then flush THR/FIFO and unlock TX if locked */ - if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { dev_dbg(dev->dev, "unlock tx\n"); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_THRCLR | AT91_TWI_LOCKCLR); @@ -750,13 +839,32 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) int ret = 0; struct dma_slave_config slave_config; struct at91_twi_dma *dma = &dev->dma; + enum dma_slave_buswidth addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + /* + * The actual width of the access will be chosen in + * dmaengine_prep_slave_sg(): + * for each buffer in the scatter-gather list, if its size is aligned + * to addr_width then addr_width accesses will be performed to transfer + * the buffer. On the other hand, if the buffer size is not aligned to + * addr_width then the buffer is transferred using single byte accesses. + * Please refer to the Atmel eXtended DMA controller driver. + * When FIFOs are used, the TXRDYM threshold can always be set to + * trigger the XDMAC when at least 4 data can be written into the TX + * FIFO, even if single byte accesses are performed. + * However the RXRDYM threshold must be set to fit the access width, + * deduced from buffer length, so the XDMAC is triggered properly to + * read data from the RX FIFO. + */ + if (dev->fifo_size) + addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; memset(&slave_config, 0, sizeof(slave_config)); slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR; - slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_addr_width = addr_width; slave_config.src_maxburst = 1; slave_config.dst_addr = (dma_addr_t)phy_addr + AT91_TWI_THR; - slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_addr_width = addr_width; slave_config.dst_maxburst = 1; slave_config.device_fc = false; @@ -788,7 +896,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) goto error; } - sg_init_table(&dma->sg, 1); + sg_init_table(dma->sg, 2); dma->buf_mapped = false; dma->xfer_in_progress = false; dev->use_dma = true; @@ -874,6 +982,11 @@ static int at91_twi_probe(struct platform_device *pdev) return rc; } + if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", + &dev->fifo_size)) { + dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size); + } + rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", &bus_clk_rate); if (rc) |