Message ID | 20210224191720.7724-5-jae.hyun.yoo@linux.intel.com |
---|---|
State | New |
Headers | show |
Series | i2c: aspeed: Add buffer and DMA modes support | expand |
On Wed, Feb 24, 2021 at 11:04 AM Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> wrote: > > This commit adds DMA mode transfer support. > > Only AST2500 and later versions support DMA mode. > > AST2500 has these restrictions: > - If one of these controllers is enabled > * UHCI host controller > * MCTP controller > I2C has to use buffer mode or byte mode instead > since these controllers run only in DMA mode and > I2C is sharing the same DMA H/W with them. > - If one of these controllers uses DMA mode, I2C > can't use DMA mode > * SD/eMMC > * Port80 snoop > > Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> > --- > Changes since v3: > - None > > Changes since v2: > - Refined SoC family dependent xfer mode configuration functions. > > Changes since v1: > - Updated commit message and comments. > - Refined using abstract functions. > > drivers/i2c/busses/i2c-aspeed.c | 265 ++++++++++++++++++++++++++------ > 1 file changed, 216 insertions(+), 49 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c > index ffc52937df26..3e3bb014b027 100644 > --- a/drivers/i2c/busses/i2c-aspeed.c > +++ b/drivers/i2c/busses/i2c-aspeed.c > @@ -10,6 +10,8 @@ > #include <linux/bitfield.h> > #include <linux/clk.h> > #include <linux/completion.h> > +#include <linux/dma-mapping.h> > +#include <linux/dmapool.h> > #include <linux/err.h> > #include <linux/errno.h> > #include <linux/i2c.h> > @@ -47,6 +49,8 @@ > #define ASPEED_I2C_DEV_ADDR_REG 0x18 > #define ASPEED_I2C_BUF_CTRL_REG 0x1c > #define ASPEED_I2C_BYTE_BUF_REG 0x20 > +#define ASPEED_I2C_DMA_ADDR_REG 0x24 > +#define ASPEED_I2C_DMA_LEN_REG 0x28 > > /* Device Register Definition */ > /* 0x00 : I2CD Function Control Register */ > @@ -111,6 +115,8 @@ > #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) > > /* Command Bit */ > +#define ASPEED_I2CD_RX_DMA_ENABLE BIT(9) > +#define ASPEED_I2CD_TX_DMA_ENABLE BIT(8) > #define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7) > #define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6) > #define ASPEED_I2CD_M_STOP_CMD BIT(5) > @@ -136,6 +142,14 @@ > #define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8) > #define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0) > > +/* 0x24 : I2CD DMA Mode Buffer Address Register */ > +#define ASPEED_I2CD_DMA_ADDR_MASK GENMASK(31, 2) > +#define ASPEED_I2CD_DMA_ALIGN 4 > + > +/* 0x28 : I2CD DMA Transfer Length Register */ > +#define ASPEED_I2CD_DMA_LEN_SHIFT 0 > +#define ASPEED_I2CD_DMA_LEN_MASK GENMASK(11, 0) > + > enum aspeed_i2c_master_state { > ASPEED_I2C_MASTER_INACTIVE, > ASPEED_I2C_MASTER_PENDING, > @@ -161,6 +175,7 @@ struct aspeed_i2c_config { > u32 (*get_clk_reg_val)(struct device *dev, u32 divisor); > int (*enable_sram)(void); > int (*set_buf_xfer_mode)(struct device *dev); > + int (*set_dma_xfer_mode)(struct device *dev); > }; > > struct aspeed_i2c_bus { > @@ -190,6 +205,12 @@ struct aspeed_i2c_bus { > void __iomem *buf_base; > u8 buf_offset; > u8 buf_page; > + /* DMA mode */ > + struct dma_pool *dma_pool; > + dma_addr_t dma_handle; > + u8 *dma_buf; > + size_t dma_len; > + /* Buffer/DMA mode */ > size_t buf_size; > #if IS_ENABLED(CONFIG_I2C_SLAVE) > struct i2c_client *slave; > @@ -272,9 +293,13 @@ static inline void > aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, > u8 *value) > { > - if (bus->buf_base && > + if (bus->dma_buf && > bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && > !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) > + *value = bus->dma_buf[0]; > + else if (bus->buf_base && > + bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && > + !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) > *value = readb(bus->buf_base); > else > *value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; > @@ -288,7 +313,18 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, > > if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && > irq_status & ASPEED_I2CD_INTR_RX_DONE) { > - if (bus->buf_base) { > + if (bus->dma_buf) { > + len = bus->buf_size - > + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, > + readl(bus->base + > + ASPEED_I2C_DMA_LEN_REG)); > + for (i = 0; i < len; i++) { > + *value = bus->dma_buf[i]; > + i2c_slave_event(bus->slave, > + I2C_SLAVE_WRITE_RECEIVED, > + value); > + } > + } else if (bus->buf_base) { > len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, > readl(bus->base + > ASPEED_I2C_BUF_CTRL_REG)); > @@ -305,7 +341,14 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, > static inline void > aspeed_i2c_slave_handle_write_requested(struct aspeed_i2c_bus *bus, u8 *value) > { > - if (bus->buf_base) { > + if (bus->dma_buf) { > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + writel(ASPEED_I2CD_RX_DMA_ENABLE, > + bus->base + ASPEED_I2C_CMD_REG); > + } else if (bus->buf_base) { > writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, > bus->buf_size - 1) | > FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, > @@ -321,7 +364,23 @@ aspeed_i2c_slave_handle_write_received(struct aspeed_i2c_bus *bus, u8 *value) > { > int i, len; > > - if (bus->buf_base) { > + if (bus->dma_buf) { > + len = bus->buf_size - > + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, > + readl(bus->base + > + ASPEED_I2C_DMA_LEN_REG)); > + for (i = 1; i < len; i++) { > + *value = bus->dma_buf[i]; > + i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, > + value); > + } > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + writel(ASPEED_I2CD_RX_DMA_ENABLE, > + bus->base + ASPEED_I2C_CMD_REG); > + } else if (bus->buf_base) { > len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, > readl(bus->base + > ASPEED_I2C_BUF_CTRL_REG)); > @@ -451,7 +510,15 @@ aspeed_i2c_prepare_rx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) > command |= ASPEED_I2CD_M_S_RX_CMD_LAST; > } > > - if (bus->buf_base) { > + if (bus->dma_buf) { > + command |= ASPEED_I2CD_RX_DMA_ENABLE; > + > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + bus->dma_len = len; > + } else { > command |= ASPEED_I2CD_RX_BUFF_ENABLE; > > writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, len - 1) | > @@ -474,7 +541,18 @@ aspeed_i2c_prepare_tx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) > else > len = msg->len + 1; > > - if (bus->buf_base) { > + if (bus->dma_buf) { > + command |= ASPEED_I2CD_TX_DMA_ENABLE; > + > + bus->dma_buf[0] = slave_addr; > + memcpy(bus->dma_buf + 1, msg->buf, len); > + > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + bus->dma_len = len; > + } else { > u8 wbuf[4]; > int i; > > @@ -527,18 +605,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) > if (msg->flags & I2C_M_RD) { > command |= ASPEED_I2CD_M_RX_CMD; > if (!(msg->flags & I2C_M_RECV_LEN)) { > - if (msg->len && bus->buf_base) > + if (msg->len && (bus->dma_buf || bus->buf_base)) > command |= aspeed_i2c_prepare_rx_buf(bus, msg); > > /* Need to let the hardware know to NACK after RX. */ > if (msg->len <= 1) > command |= ASPEED_I2CD_M_S_RX_CMD_LAST; > } > - } else if (msg->len && bus->buf_base) { > + } else if (msg->len && (bus->dma_buf || bus->buf_base)) { > command |= aspeed_i2c_prepare_tx_buf(bus, msg); > } > > - if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE)) > + if (!(command & (ASPEED_I2CD_TX_BUFF_ENABLE | > + ASPEED_I2CD_TX_DMA_ENABLE))) > writel(i2c_8bit_addr_from_msg(msg), > bus->base + ASPEED_I2C_BYTE_BUF_REG); > writel(command, bus->base + ASPEED_I2C_CMD_REG); > @@ -581,42 +660,55 @@ aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, > { > u32 command = 0; > > - if (bus->buf_base) { > - u8 wbuf[4]; > + if (bus->dma_buf || bus->buf_base) { > int len; > - int i; > > if (msg->len - bus->buf_index > bus->buf_size) > len = bus->buf_size; > else > len = msg->len - bus->buf_index; > > - command |= ASPEED_I2CD_TX_BUFF_ENABLE; > + if (bus->dma_buf) { > + command |= ASPEED_I2CD_TX_DMA_ENABLE; > > - if (msg->len - bus->buf_index > bus->buf_size) > - len = bus->buf_size; > - else > - len = msg->len - bus->buf_index; > + memcpy(bus->dma_buf, msg->buf + bus->buf_index, len); > > - /* > - * Looks bad here again but use dword writings to avoid data > - * corruption of byte writing on remapped I2C SRAM. > - */ > - for (i = 0; i < len; i++) { > - wbuf[i % 4] = msg->buf[bus->buf_index + i]; > - if (i % 4 == 3) > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + bus->dma_len = len; > + } else { > + u8 wbuf[4]; > + int i; > + > + command |= ASPEED_I2CD_TX_BUFF_ENABLE; > + > + if (msg->len - bus->buf_index > bus->buf_size) > + len = bus->buf_size; > + else > + len = msg->len - bus->buf_index; > + > + /* > + * Looks bad here again but use dword writings to avoid > + * data corruption of byte writing on remapped I2C SRAM. > + */ > + for (i = 0; i < len; i++) { > + wbuf[i % 4] = msg->buf[bus->buf_index + i]; > + if (i % 4 == 3) > + writel(*(u32 *)wbuf, > + bus->buf_base + i - 3); > + } > + if (--i % 4 != 3) > writel(*(u32 *)wbuf, > - bus->buf_base + i - 3); > - } > - if (--i % 4 != 3) > - writel(*(u32 *)wbuf, > - bus->buf_base + i - (i % 4)); > + bus->buf_base + i - (i % 4)); > > - writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, > - len - 1) | > - FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, > - bus->buf_offset), > - bus->base + ASPEED_I2C_BUF_CTRL_REG); > + writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, > + len - 1) | > + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, > + bus->buf_offset), > + bus->base + ASPEED_I2C_BUF_CTRL_REG); > + } > > bus->buf_index += len; > } else { Some of these functions are getting really complex and most of the logic for the different modes is in different if-else blocks. Could you look into splitting this into separate functions based on which mode is being used? Otherwise, this patch looks good. > @@ -633,7 +725,14 @@ aspeed_i2c_master_handle_rx(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) > u8 recv_byte; > int len; > > - if (bus->buf_base) { > + if (bus->dma_buf) { > + len = bus->dma_len - > + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, > + readl(bus->base + ASPEED_I2C_DMA_LEN_REG)); > + > + memcpy(msg->buf + bus->buf_index, bus->dma_buf, len); > + bus->buf_index += len; > + } else if (bus->buf_base) { > len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, > readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); > memcpy_fromio(msg->buf + bus->buf_index, bus->buf_base, len); > @@ -650,7 +749,7 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, > { > u32 command = 0; > > - if (bus->buf_base) { > + if (bus->dma_buf || bus->buf_base) { > int len; > > if (msg->len - bus->buf_index > bus->buf_size) { > @@ -660,14 +759,24 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, > command |= ASPEED_I2CD_M_S_RX_CMD_LAST; > } > > - command |= ASPEED_I2CD_RX_BUFF_ENABLE; > + if (bus->dma_buf) { > + command |= ASPEED_I2CD_RX_DMA_ENABLE; > > - writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, > - len - 1) | > - FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | > - FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, > - bus->buf_offset), > - bus->base + ASPEED_I2C_BUF_CTRL_REG); > + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, > + bus->base + ASPEED_I2C_DMA_ADDR_REG); > + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), > + bus->base + ASPEED_I2C_DMA_LEN_REG); > + bus->dma_len = len; > + } else { > + command |= ASPEED_I2CD_RX_BUFF_ENABLE; > + > + writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, > + len - 1) | > + FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | > + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, > + bus->buf_offset), > + bus->base + ASPEED_I2C_BUF_CTRL_REG); > + } > } else { > if (bus->buf_index + 1 == msg->len) > command |= ASPEED_I2CD_M_S_RX_CMD_LAST; > @@ -1287,22 +1396,63 @@ static int aspeed_i2c_25xx_set_buf_xfer_mode(struct device *dev) > return bus->buf_size ? 0 : -EINVAL; > } > > +static int aspeed_i2c_24xx_set_dma_xfer_mode(struct device *dev) > +{ > + /* AST24xx doesn't support DMA mode */ > + > + return -EBADR; > +} > + > +static int aspeed_i2c_25xx_set_dma_xfer_mode(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev); > + int ret; > + > + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); > + if (!ret) { > + bus->buf_size = ASPEED_I2CD_DMA_LEN_MASK >> > + ASPEED_I2CD_DMA_LEN_SHIFT; > + bus->dma_pool = dma_pool_create("i2c-aspeed", > + &pdev->dev, > + bus->buf_size, > + ASPEED_I2CD_DMA_ALIGN, > + 0); > + if (bus->dma_pool) > + bus->dma_buf = dma_pool_alloc(bus->dma_pool, > + GFP_KERNEL, > + &bus->dma_handle); > + > + if (!bus->dma_buf) { > + ret = -ENOMEM; > + bus->buf_size = 0; > + dev_dbg(&pdev->dev, "Cannot allocate DMA buffer\n"); > + dma_pool_destroy(bus->dma_pool); > + } > + } > + > + return ret; > +} > + > static const struct aspeed_i2c_config ast24xx_config = { > .get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val, > .enable_sram = aspeed_i2c_24xx_enable_sram, > .set_buf_xfer_mode = aspeed_i2c_24xx_set_buf_xfer_mode, > + .set_dma_xfer_mode = aspeed_i2c_24xx_set_dma_xfer_mode, > }; > > static const struct aspeed_i2c_config ast25xx_config = { > .get_clk_reg_val = aspeed_i2c_25xx_get_clk_reg_val, > .enable_sram = aspeed_i2c_25xx_enable_sram, > .set_buf_xfer_mode = aspeed_i2c_25xx_set_buf_xfer_mode, > + .set_dma_xfer_mode = aspeed_i2c_25xx_set_dma_xfer_mode, > }; > > static const struct aspeed_i2c_config ast26xx_config = { > .get_clk_reg_val = aspeed_i2c_25xx_get_clk_reg_val, > .enable_sram = aspeed_i2c_24xx_enable_sram, > .set_buf_xfer_mode = aspeed_i2c_25xx_set_buf_xfer_mode, > + .set_dma_xfer_mode = aspeed_i2c_25xx_set_dma_xfer_mode, > }; > > static const struct of_device_id aspeed_i2c_bus_of_table[] = { > @@ -1324,8 +1474,12 @@ static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) > return; > > ret = bus->config->enable_sram(); > - if (!ret && !strncasecmp(mode, "buf", 3)) > - ret = bus->config->set_buf_xfer_mode(bus->dev); > + if (!ret) { > + if (!strncasecmp(mode, "buf", 3)) > + ret = bus->config->set_buf_xfer_mode(bus->dev); > + else if (!strncasecmp(mode, "dma", 3)) > + ret = bus->config->set_dma_xfer_mode(bus->dev); > + } > > if (ret) > dev_dbg(&pdev->dev, "Use default (byte) xfer mode\n"); > @@ -1400,22 +1554,31 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) > */ > ret = aspeed_i2c_init(bus, pdev); > if (ret < 0) > - return ret; > + goto out_free_dma_buf; > > irq = irq_of_parse_and_map(pdev->dev.of_node, 0); > ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, > 0, dev_name(&pdev->dev), bus); > if (ret < 0) > - return ret; > + goto out_free_dma_buf; > > ret = i2c_add_adapter(&bus->adap); > if (ret < 0) > - return ret; > + goto out_free_dma_buf; > > dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", > - bus->adap.nr, bus->buf_base ? "buf" : "byte", irq); > + bus->adap.nr, bus->dma_buf ? "dma" : > + bus->buf_base ? "buf" : "byte", > + irq); > > return 0; > + > +out_free_dma_buf: > + if (bus->dma_buf) > + dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); > + dma_pool_destroy(bus->dma_pool); > + > + return ret; > } > > static int aspeed_i2c_remove_bus(struct platform_device *pdev) > @@ -1433,6 +1596,10 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev) > > reset_control_assert(bus->rst); > > + if (bus->dma_buf) > + dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); > + dma_pool_destroy(bus->dma_pool); > + > i2c_del_adapter(&bus->adap); > > return 0; > -- > 2.17.1 >
Hi Brendan, On 4/14/2021 8:08 AM, Jae Hyun Yoo wrote: [....] >>> @@ -581,42 +660,55 @@ aspeed_i2c_master_handle_tx_first(struct >>> aspeed_i2c_bus *bus, >>> { >>> u32 command = 0; >>> >>> - if (bus->buf_base) { >>> - u8 wbuf[4]; >>> + if (bus->dma_buf || bus->buf_base) { >>> int len; >>> - int i; >>> >>> if (msg->len - bus->buf_index > bus->buf_size) >>> len = bus->buf_size; >>> else >>> len = msg->len - bus->buf_index; >>> >>> - command |= ASPEED_I2CD_TX_BUFF_ENABLE; >>> + if (bus->dma_buf) { >>> + command |= ASPEED_I2CD_TX_DMA_ENABLE; >>> >>> - if (msg->len - bus->buf_index > bus->buf_size) >>> - len = bus->buf_size; >>> - else >>> - len = msg->len - bus->buf_index; >>> + memcpy(bus->dma_buf, msg->buf + >>> bus->buf_index, len); >>> >>> - /* >>> - * Looks bad here again but use dword writings to >>> avoid data >>> - * corruption of byte writing on remapped I2C SRAM. >>> - */ >>> - for (i = 0; i < len; i++) { >>> - wbuf[i % 4] = msg->buf[bus->buf_index + i]; >>> - if (i % 4 == 3) >>> + writel(bus->dma_handle & >>> ASPEED_I2CD_DMA_ADDR_MASK, >>> + bus->base + ASPEED_I2C_DMA_ADDR_REG); >>> + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, >>> len), >>> + bus->base + ASPEED_I2C_DMA_LEN_REG); >>> + bus->dma_len = len; >>> + } else { >>> + u8 wbuf[4]; >>> + int i; >>> + >>> + command |= ASPEED_I2CD_TX_BUFF_ENABLE; >>> + >>> + if (msg->len - bus->buf_index > bus->buf_size) >>> + len = bus->buf_size; >>> + else >>> + len = msg->len - bus->buf_index; >>> + >>> + /* >>> + * Looks bad here again but use dword >>> writings to avoid >>> + * data corruption of byte writing on >>> remapped I2C SRAM. >>> + */ >>> + for (i = 0; i < len; i++) { >>> + wbuf[i % 4] = msg->buf[bus->buf_index >>> + i]; >>> + if (i % 4 == 3) >>> + writel(*(u32 *)wbuf, >>> + bus->buf_base + i - 3); >>> + } >>> + if (--i % 4 != 3) >>> writel(*(u32 *)wbuf, >>> - bus->buf_base + i - 3); >>> - } >>> - if (--i % 4 != 3) >>> - writel(*(u32 *)wbuf, >>> - bus->buf_base + i - (i % 4)); >>> + bus->buf_base + i - (i % 4)); >>> >>> - writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, >>> - len - 1) | >>> - FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, >>> - bus->buf_offset), >>> - bus->base + ASPEED_I2C_BUF_CTRL_REG); >>> + writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, >>> + len - 1) | >>> + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, >>> + bus->buf_offset), >>> + bus->base + ASPEED_I2C_BUF_CTRL_REG); >>> + } >>> >>> bus->buf_index += len; >>> } else { >> >> Some of these functions are getting really complex and most of the >> logic for the different modes is in different if-else blocks. Could >> you look into splitting this into separate functions based on which >> mode is being used? >> >> Otherwise, this patch looks good. > > I already splitted some big chunk of mode dependant logics to address > your comment on v1. Could you please check again the patched result of > this function? It's not much complex and it'd be better keep as is for > consistency in other changes in this patch. I think, splitting it again > would be not good for readability of the code flow. > > Thanks, > Jae > This is the patched result of this function: static inline u32 aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) { u32 command = 0; if (bus->dma_buf || bus->buf_base) { int len; if (msg->len - bus->buf_index > bus->buf_size) len = bus->buf_size; else len = msg->len - bus->buf_index; if (bus->dma_buf) { command |= ASPEED_I2CD_TX_DMA_ENABLE; memcpy(bus->dma_buf, msg->buf + bus->buf_index, len); writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, bus->base + ASPEED_I2C_DMA_ADDR_REG); writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), bus->base + ASPEED_I2C_DMA_LEN_REG); bus->dma_len = len; } else { u8 wbuf[4]; int i; command |= ASPEED_I2CD_TX_BUFF_ENABLE; if (msg->len - bus->buf_index > bus->buf_size) len = bus->buf_size; else len = msg->len - bus->buf_index; for (i = 0; i < len; i++) { wbuf[i % 4] = msg->buf[bus->buf_index + i]; if (i % 4 == 3) writel(*(u32 *)wbuf, bus->buf_base + i - 3); } if (--i % 4 != 3) writel(*(u32 *)wbuf, bus->buf_base + i - (i % 4)); writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, len - 1) | FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, bus->buf_offset), bus->base + ASPEED_I2C_BUF_CTRL_REG); } bus->buf_index += len; } else { writel(msg->buf[bus->buf_index++], bus->base + ASPEED_I2C_BYTE_BUF_REG); } return command; } Do you still think that it should be split into separate functions per each transfer mode? Thanks, Jae [....]
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index ffc52937df26..3e3bb014b027 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -10,6 +10,8 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/i2c.h> @@ -47,6 +49,8 @@ #define ASPEED_I2C_DEV_ADDR_REG 0x18 #define ASPEED_I2C_BUF_CTRL_REG 0x1c #define ASPEED_I2C_BYTE_BUF_REG 0x20 +#define ASPEED_I2C_DMA_ADDR_REG 0x24 +#define ASPEED_I2C_DMA_LEN_REG 0x28 /* Device Register Definition */ /* 0x00 : I2CD Function Control Register */ @@ -111,6 +115,8 @@ #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11) /* Command Bit */ +#define ASPEED_I2CD_RX_DMA_ENABLE BIT(9) +#define ASPEED_I2CD_TX_DMA_ENABLE BIT(8) #define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7) #define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6) #define ASPEED_I2CD_M_STOP_CMD BIT(5) @@ -136,6 +142,14 @@ #define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8) #define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0) +/* 0x24 : I2CD DMA Mode Buffer Address Register */ +#define ASPEED_I2CD_DMA_ADDR_MASK GENMASK(31, 2) +#define ASPEED_I2CD_DMA_ALIGN 4 + +/* 0x28 : I2CD DMA Transfer Length Register */ +#define ASPEED_I2CD_DMA_LEN_SHIFT 0 +#define ASPEED_I2CD_DMA_LEN_MASK GENMASK(11, 0) + enum aspeed_i2c_master_state { ASPEED_I2C_MASTER_INACTIVE, ASPEED_I2C_MASTER_PENDING, @@ -161,6 +175,7 @@ struct aspeed_i2c_config { u32 (*get_clk_reg_val)(struct device *dev, u32 divisor); int (*enable_sram)(void); int (*set_buf_xfer_mode)(struct device *dev); + int (*set_dma_xfer_mode)(struct device *dev); }; struct aspeed_i2c_bus { @@ -190,6 +205,12 @@ struct aspeed_i2c_bus { void __iomem *buf_base; u8 buf_offset; u8 buf_page; + /* DMA mode */ + struct dma_pool *dma_pool; + dma_addr_t dma_handle; + u8 *dma_buf; + size_t dma_len; + /* Buffer/DMA mode */ size_t buf_size; #if IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *slave; @@ -272,9 +293,13 @@ static inline void aspeed_i2c_slave_handle_rx_done(struct aspeed_i2c_bus *bus, u32 irq_status, u8 *value) { - if (bus->buf_base && + if (bus->dma_buf && bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) + *value = bus->dma_buf[0]; + else if (bus->buf_base && + bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && + !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) *value = readb(bus->buf_base); else *value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; @@ -288,7 +313,18 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && irq_status & ASPEED_I2CD_INTR_RX_DONE) { - if (bus->buf_base) { + if (bus->dma_buf) { + len = bus->buf_size - + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, + readl(bus->base + + ASPEED_I2C_DMA_LEN_REG)); + for (i = 0; i < len; i++) { + *value = bus->dma_buf[i]; + i2c_slave_event(bus->slave, + I2C_SLAVE_WRITE_RECEIVED, + value); + } + } else if (bus->buf_base) { len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); @@ -305,7 +341,14 @@ aspeed_i2c_slave_handle_normal_stop(struct aspeed_i2c_bus *bus, u32 irq_status, static inline void aspeed_i2c_slave_handle_write_requested(struct aspeed_i2c_bus *bus, u8 *value) { - if (bus->buf_base) { + if (bus->dma_buf) { + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), + bus->base + ASPEED_I2C_DMA_LEN_REG); + writel(ASPEED_I2CD_RX_DMA_ENABLE, + bus->base + ASPEED_I2C_CMD_REG); + } else if (bus->buf_base) { writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, bus->buf_size - 1) | FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, @@ -321,7 +364,23 @@ aspeed_i2c_slave_handle_write_received(struct aspeed_i2c_bus *bus, u8 *value) { int i, len; - if (bus->buf_base) { + if (bus->dma_buf) { + len = bus->buf_size - + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, + readl(bus->base + + ASPEED_I2C_DMA_LEN_REG)); + for (i = 1; i < len; i++) { + *value = bus->dma_buf[i]; + i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, + value); + } + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, bus->buf_size), + bus->base + ASPEED_I2C_DMA_LEN_REG); + writel(ASPEED_I2CD_RX_DMA_ENABLE, + bus->base + ASPEED_I2C_CMD_REG); + } else if (bus->buf_base) { len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); @@ -451,7 +510,15 @@ aspeed_i2c_prepare_rx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) command |= ASPEED_I2CD_M_S_RX_CMD_LAST; } - if (bus->buf_base) { + if (bus->dma_buf) { + command |= ASPEED_I2CD_RX_DMA_ENABLE; + + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), + bus->base + ASPEED_I2C_DMA_LEN_REG); + bus->dma_len = len; + } else { command |= ASPEED_I2CD_RX_BUFF_ENABLE; writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, len - 1) | @@ -474,7 +541,18 @@ aspeed_i2c_prepare_tx_buf(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) else len = msg->len + 1; - if (bus->buf_base) { + if (bus->dma_buf) { + command |= ASPEED_I2CD_TX_DMA_ENABLE; + + bus->dma_buf[0] = slave_addr; + memcpy(bus->dma_buf + 1, msg->buf, len); + + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), + bus->base + ASPEED_I2C_DMA_LEN_REG); + bus->dma_len = len; + } else { u8 wbuf[4]; int i; @@ -527,18 +605,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) if (msg->flags & I2C_M_RD) { command |= ASPEED_I2CD_M_RX_CMD; if (!(msg->flags & I2C_M_RECV_LEN)) { - if (msg->len && bus->buf_base) + if (msg->len && (bus->dma_buf || bus->buf_base)) command |= aspeed_i2c_prepare_rx_buf(bus, msg); /* Need to let the hardware know to NACK after RX. */ if (msg->len <= 1) command |= ASPEED_I2CD_M_S_RX_CMD_LAST; } - } else if (msg->len && bus->buf_base) { + } else if (msg->len && (bus->dma_buf || bus->buf_base)) { command |= aspeed_i2c_prepare_tx_buf(bus, msg); } - if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE)) + if (!(command & (ASPEED_I2CD_TX_BUFF_ENABLE | + ASPEED_I2CD_TX_DMA_ENABLE))) writel(i2c_8bit_addr_from_msg(msg), bus->base + ASPEED_I2C_BYTE_BUF_REG); writel(command, bus->base + ASPEED_I2C_CMD_REG); @@ -581,42 +660,55 @@ aspeed_i2c_master_handle_tx_first(struct aspeed_i2c_bus *bus, { u32 command = 0; - if (bus->buf_base) { - u8 wbuf[4]; + if (bus->dma_buf || bus->buf_base) { int len; - int i; if (msg->len - bus->buf_index > bus->buf_size) len = bus->buf_size; else len = msg->len - bus->buf_index; - command |= ASPEED_I2CD_TX_BUFF_ENABLE; + if (bus->dma_buf) { + command |= ASPEED_I2CD_TX_DMA_ENABLE; - if (msg->len - bus->buf_index > bus->buf_size) - len = bus->buf_size; - else - len = msg->len - bus->buf_index; + memcpy(bus->dma_buf, msg->buf + bus->buf_index, len); - /* - * Looks bad here again but use dword writings to avoid data - * corruption of byte writing on remapped I2C SRAM. - */ - for (i = 0; i < len; i++) { - wbuf[i % 4] = msg->buf[bus->buf_index + i]; - if (i % 4 == 3) + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), + bus->base + ASPEED_I2C_DMA_LEN_REG); + bus->dma_len = len; + } else { + u8 wbuf[4]; + int i; + + command |= ASPEED_I2CD_TX_BUFF_ENABLE; + + if (msg->len - bus->buf_index > bus->buf_size) + len = bus->buf_size; + else + len = msg->len - bus->buf_index; + + /* + * Looks bad here again but use dword writings to avoid + * data corruption of byte writing on remapped I2C SRAM. + */ + for (i = 0; i < len; i++) { + wbuf[i % 4] = msg->buf[bus->buf_index + i]; + if (i % 4 == 3) + writel(*(u32 *)wbuf, + bus->buf_base + i - 3); + } + if (--i % 4 != 3) writel(*(u32 *)wbuf, - bus->buf_base + i - 3); - } - if (--i % 4 != 3) - writel(*(u32 *)wbuf, - bus->buf_base + i - (i % 4)); + bus->buf_base + i - (i % 4)); - writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, - len - 1) | - FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, - bus->buf_offset), - bus->base + ASPEED_I2C_BUF_CTRL_REG); + writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, + len - 1) | + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, + bus->buf_offset), + bus->base + ASPEED_I2C_BUF_CTRL_REG); + } bus->buf_index += len; } else { @@ -633,7 +725,14 @@ aspeed_i2c_master_handle_rx(struct aspeed_i2c_bus *bus, struct i2c_msg *msg) u8 recv_byte; int len; - if (bus->buf_base) { + if (bus->dma_buf) { + len = bus->dma_len - + FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK, + readl(bus->base + ASPEED_I2C_DMA_LEN_REG)); + + memcpy(msg->buf + bus->buf_index, bus->dma_buf, len); + bus->buf_index += len; + } else if (bus->buf_base) { len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)); memcpy_fromio(msg->buf + bus->buf_index, bus->buf_base, len); @@ -650,7 +749,7 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, { u32 command = 0; - if (bus->buf_base) { + if (bus->dma_buf || bus->buf_base) { int len; if (msg->len - bus->buf_index > bus->buf_size) { @@ -660,14 +759,24 @@ aspeed_i2c_master_handle_rx_next(struct aspeed_i2c_bus *bus, command |= ASPEED_I2CD_M_S_RX_CMD_LAST; } - command |= ASPEED_I2CD_RX_BUFF_ENABLE; + if (bus->dma_buf) { + command |= ASPEED_I2CD_RX_DMA_ENABLE; - writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, - len - 1) | - FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | - FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, - bus->buf_offset), - bus->base + ASPEED_I2C_BUF_CTRL_REG); + writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK, + bus->base + ASPEED_I2C_DMA_ADDR_REG); + writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK, len), + bus->base + ASPEED_I2C_DMA_LEN_REG); + bus->dma_len = len; + } else { + command |= ASPEED_I2CD_RX_BUFF_ENABLE; + + writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK, + len - 1) | + FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK, 0) | + FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK, + bus->buf_offset), + bus->base + ASPEED_I2C_BUF_CTRL_REG); + } } else { if (bus->buf_index + 1 == msg->len) command |= ASPEED_I2CD_M_S_RX_CMD_LAST; @@ -1287,22 +1396,63 @@ static int aspeed_i2c_25xx_set_buf_xfer_mode(struct device *dev) return bus->buf_size ? 0 : -EINVAL; } +static int aspeed_i2c_24xx_set_dma_xfer_mode(struct device *dev) +{ + /* AST24xx doesn't support DMA mode */ + + return -EBADR; +} + +static int aspeed_i2c_25xx_set_dma_xfer_mode(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev); + int ret; + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (!ret) { + bus->buf_size = ASPEED_I2CD_DMA_LEN_MASK >> + ASPEED_I2CD_DMA_LEN_SHIFT; + bus->dma_pool = dma_pool_create("i2c-aspeed", + &pdev->dev, + bus->buf_size, + ASPEED_I2CD_DMA_ALIGN, + 0); + if (bus->dma_pool) + bus->dma_buf = dma_pool_alloc(bus->dma_pool, + GFP_KERNEL, + &bus->dma_handle); + + if (!bus->dma_buf) { + ret = -ENOMEM; + bus->buf_size = 0; + dev_dbg(&pdev->dev, "Cannot allocate DMA buffer\n"); + dma_pool_destroy(bus->dma_pool); + } + } + + return ret; +} + static const struct aspeed_i2c_config ast24xx_config = { .get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val, .enable_sram = aspeed_i2c_24xx_enable_sram, .set_buf_xfer_mode = aspeed_i2c_24xx_set_buf_xfer_mode, + .set_dma_xfer_mode = aspeed_i2c_24xx_set_dma_xfer_mode, }; static const struct aspeed_i2c_config ast25xx_config = { .get_clk_reg_val = aspeed_i2c_25xx_get_clk_reg_val, .enable_sram = aspeed_i2c_25xx_enable_sram, .set_buf_xfer_mode = aspeed_i2c_25xx_set_buf_xfer_mode, + .set_dma_xfer_mode = aspeed_i2c_25xx_set_dma_xfer_mode, }; static const struct aspeed_i2c_config ast26xx_config = { .get_clk_reg_val = aspeed_i2c_25xx_get_clk_reg_val, .enable_sram = aspeed_i2c_24xx_enable_sram, .set_buf_xfer_mode = aspeed_i2c_25xx_set_buf_xfer_mode, + .set_dma_xfer_mode = aspeed_i2c_25xx_set_dma_xfer_mode, }; static const struct of_device_id aspeed_i2c_bus_of_table[] = { @@ -1324,8 +1474,12 @@ static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) return; ret = bus->config->enable_sram(); - if (!ret && !strncasecmp(mode, "buf", 3)) - ret = bus->config->set_buf_xfer_mode(bus->dev); + if (!ret) { + if (!strncasecmp(mode, "buf", 3)) + ret = bus->config->set_buf_xfer_mode(bus->dev); + else if (!strncasecmp(mode, "dma", 3)) + ret = bus->config->set_dma_xfer_mode(bus->dev); + } if (ret) dev_dbg(&pdev->dev, "Use default (byte) xfer mode\n"); @@ -1400,22 +1554,31 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) */ ret = aspeed_i2c_init(bus, pdev); if (ret < 0) - return ret; + goto out_free_dma_buf; irq = irq_of_parse_and_map(pdev->dev.of_node, 0); ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq, 0, dev_name(&pdev->dev), bus); if (ret < 0) - return ret; + goto out_free_dma_buf; ret = i2c_add_adapter(&bus->adap); if (ret < 0) - return ret; + goto out_free_dma_buf; dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", - bus->adap.nr, bus->buf_base ? "buf" : "byte", irq); + bus->adap.nr, bus->dma_buf ? "dma" : + bus->buf_base ? "buf" : "byte", + irq); return 0; + +out_free_dma_buf: + if (bus->dma_buf) + dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); + dma_pool_destroy(bus->dma_pool); + + return ret; } static int aspeed_i2c_remove_bus(struct platform_device *pdev) @@ -1433,6 +1596,10 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev) reset_control_assert(bus->rst); + if (bus->dma_buf) + dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle); + dma_pool_destroy(bus->dma_pool); + i2c_del_adapter(&bus->adap); return 0;
This commit adds DMA mode transfer support. Only AST2500 and later versions support DMA mode. AST2500 has these restrictions: - If one of these controllers is enabled * UHCI host controller * MCTP controller I2C has to use buffer mode or byte mode instead since these controllers run only in DMA mode and I2C is sharing the same DMA H/W with them. - If one of these controllers uses DMA mode, I2C can't use DMA mode * SD/eMMC * Port80 snoop Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> --- Changes since v3: - None Changes since v2: - Refined SoC family dependent xfer mode configuration functions. Changes since v1: - Updated commit message and comments. - Refined using abstract functions. drivers/i2c/busses/i2c-aspeed.c | 265 ++++++++++++++++++++++++++------ 1 file changed, 216 insertions(+), 49 deletions(-)