diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch new file mode 100644 index 000000000..994c20b18 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch @@ -0,0 +1,491 @@ +From 5bf7a202eb0af455675a85a4f1bdad863fd8805d Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Tue, 18 Jun 2019 08:47:50 -0700 +Subject: [PATCH] i2c: aspeed: add DMA mode transfer support + +This commit adds DMA mode transfer support. + +Only AST2500 supports DMA mode under some limitations: +I2C is sharing the DMA H/W with UHCI host controller and MCTP +controller. Since those controllers operate with DMA mode only, I2C +has to use buffer mode or byte mode instead if one of those +controllers is enabled. Also make sure that if SD/eMMC or Port80 +snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't +use DMA mode. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../devicetree/bindings/i2c/i2c-aspeed.txt | 25 ++ + drivers/i2c/busses/i2c-aspeed.c | 253 +++++++++++++++++---- + 2 files changed, 234 insertions(+), 44 deletions(-) + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +index 0ff3539cee95..d3f4a39f7ba6 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt +@@ -22,6 +22,16 @@ Optional Properties: + - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not + specified. + - #retries : Number of retries for master transfer. ++- aspeed,dma-buf-size : size of DMA buffer (from 2 to 4095 in case of AST2500) ++ Only AST2500 supports DMA mode under some limitations: ++ I2C is sharing the DMA H/W with UHCI host controller ++ and MCTP controller. Since those controllers operate ++ with DMA mode only, I2C has to use buffer mode or byte ++ mode instead if one of those controllers is enabled. ++ Also make sure that if SD/eMMC or Port80 snoop uses ++ DMA mode instead of PIO or FIFO respectively, I2C ++ can't use DMA mode. IF both DMA and buffer modes are ++ enabled, DMA mode will be selected. + + Example: + +@@ -74,4 +84,19 @@ i2c { + interrupts = <1>; + interrupt-parent = <&i2c_ic>; + }; ++ ++ /* DMA mode transfer enabled */ ++ i2c2: i2c-bus@c0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #interrupt-cells = <1>; ++ reg = <0xc0 0x40>; ++ aspeed,dma-buf-size = <4095>; ++ compatible = "aspeed,ast2500-i2c-bus"; ++ clocks = <&syscon ASPEED_CLK_APB>; ++ resets = <&syscon ASPEED_RESET_I2C>; ++ bus-frequency = <100000>; ++ interrupts = <2>; ++ interrupt-parent = <&i2c_ic>; ++ }; + }; +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index bf72e151d11f..3c9e491cbe4e 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 */ +@@ -110,6 +114,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) +@@ -135,6 +141,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, +@@ -184,6 +198,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; +@@ -281,7 +301,11 @@ 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); +@@ -297,7 +321,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)); +@@ -314,7 +349,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, +@@ -330,7 +372,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)); +@@ -460,7 +518,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) | +@@ -483,7 +549,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; + +@@ -536,18 +613,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); +@@ -590,39 +668,52 @@ 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; + ++ if (bus->dma_buf) { ++ command |= ASPEED_I2CD_TX_DMA_ENABLE; + +- command |= ASPEED_I2CD_TX_BUFF_ENABLE; ++ memcpy(bus->dma_buf, msg->buf + bus->buf_index, len); + +- 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(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 - 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 { +@@ -639,7 +730,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); +@@ -656,7 +754,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) { +@@ -666,14 +764,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; +@@ -1260,7 +1368,51 @@ static void aspeed_i2c_set_xfer_mode(struct aspeed_i2c_bus *bus) + sram_enabled = false; + } + +- if (sram_enabled) { ++ /* ++ * Only AST2500 and AST2600 support DMA mode under some limitations: ++ * I2C is sharing the DMA H/W with UHCI host controller and MCTP ++ * controller. Since those controllers operate with DMA mode only, I2C ++ * has to use buffer mode or byte mode instead if one of those ++ * controllers is enabled. Also make sure that if SD/eMMC or Port80 ++ * snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't ++ * use DMA mode. ++ */ ++ if (sram_enabled && !IS_ENABLED(CONFIG_USB_UHCI_ASPEED) && ++ !of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2400-i2c-bus")) { ++ u32 dma_len_max = ASPEED_I2CD_DMA_LEN_MASK >> ++ ASPEED_I2CD_DMA_LEN_SHIFT; ++ ++ ret = device_property_read_u32(&pdev->dev, ++ "aspeed,dma-buf-size", ++ &bus->buf_size); ++ if (!ret && bus->buf_size > dma_len_max) ++ bus->buf_size = dma_len_max; ++ } ++ ++ if (bus->buf_size) { ++ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { ++ dev_warn(&pdev->dev, "No suitable DMA available\n"); ++ } else { ++ 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) { ++ dev_warn(&pdev->dev, ++ "Cannot allocate DMA buffer\n"); ++ dma_pool_destroy(bus->dma_pool); ++ } ++ } ++ } ++ ++ if (!bus->dma_buf && sram_enabled) { + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 1); + +@@ -1365,24 +1517,33 @@ 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; + + platform_set_drvdata(pdev, bus); + + dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n", +- bus->adap.nr, bus->buf_base ? "buffer" : "byte", irq); ++ bus->adap.nr, bus->dma_buf ? "dma" : ++ bus->buf_base ? "buffer" : "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) +@@ -1400,6 +1561,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.7.4 + |