On Tue, Oct 8, 2019 at 2:10 PM Jae Hyun Yoo <jae.hyun.yoo@xxxxxxxxxxxxxxx> wrote: > > Hi Brendan, > > On 10/8/2019 1:12 PM, Brendan Higgins wrote: > > On Mon, Oct 07, 2019 at 04:13:12PM -0700, Jae Hyun Yoo wrote: > >> Byte mode currently this driver uses makes lots of interrupt call > > > > nit: Drop "Byte mode". > > 'Byte mode' is one of modes which is described in the datasheet. > > Would it be better if I change it like below? > "This driver uses byte mode that makes lots of interrupt call ..." Yeah, I think that would probably be clearer. > >> which isn't good for performance and it makes the driver very > >> timing sensitive. To improve performance of the driver, this commit > >> adds buffer mode transfer support which uses I2C SRAM buffer > >> instead of using a single byte buffer. > > > > nit: Please use imperative mood. > > I used imperative mood in commit title. The commit message is okay as it > is. Hey, that's just what I have been told in the past. I don't actually feel strongly about it though. If no one else cares, then it is fine. > >> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@xxxxxxxxxxxxxxx> > >> Tested-by: Tao Ren <taoren@xxxxxx> > >> --- > >> drivers/i2c/busses/i2c-aspeed.c | 297 ++++++++++++++++++++++++++++---- > >> 1 file changed, 263 insertions(+), 34 deletions(-) > >> > >> diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c > >> index 40f6cf98d32e..37d1a7fa2f87 100644 > >> --- a/drivers/i2c/busses/i2c-aspeed.c > >> +++ b/drivers/i2c/busses/i2c-aspeed.c [...] > >> @@ -238,6 +260,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) > >> { > >> u32 command, irq_handled = 0; > >> struct i2c_client *slave = bus->slave; > >> + int i, len; > >> u8 value; > >> > >> if (!slave) > >> @@ -260,7 +283,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) > >> > >> /* Slave was sent something. */ > >> if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { > >> - value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; > >> + if (bus->buf_base && > >> + bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && > >> + !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP)) > > > > I think checking for the buf_base all over the place makes this really > > complicated and hard to read. > > > > It might be better to just split this out and have separate handlers > > based on what mode the driver is running in. > > I think you're saying about splitting this irq handler out to: > aspeed_i2c_slave_byte_mode_irq() > aspeed_i2c_slave_buffer_mode_irq() > aspeed_i2c_slave_dma_mode_irq() > > Yes, I can do like that but it will bring us two bad things: > 1. It makes big chunks of duplicate code because most of interrupt > handling logic is the same. > 2. If we are going to change something in irq routine, we need to > touch all irq routines if the change is commonly used. > > I think, the way this patch uses is better. I think there are other alternatives. For example, I think you could abstract over the buffer reading mechanism here. We might have a method on aspeed_i2c_bus called handle_rx_done() or something like that which could get called here. I just really don't want to grow the McCabe's complexity of this function much more, it is really too high as it is. Nevertheless, I am open to other suggestions on how to improve this function. > >> + value = readb(bus->buf_base); > >> + else > >> + value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; > >> /* Handle address frame. */ > >> if (bus->slave_state == ASPEED_I2C_SLAVE_START) { > >> if (value & 0x1) > >> @@ -275,6 +303,20 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) > >> > >> /* Slave was asked to stop. */ > >> if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { > >> + if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED && > >> + irq_status & ASPEED_I2CD_INTR_RX_DONE) { > >> + if (bus->buf_base) { > >> + len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK, > >> + readl(bus->base + > >> + ASPEED_I2C_BUF_CTRL_REG)); > > > > It looks like you have a lot of improvements in here unrelated to adding > > support for buffer mode. > > > > I really appreciate the improvements, but it makes it harder to > > understand what buffer features you are adding vs. what > > improvments/modernizations you are making. > > > > Can you split this commit up? > > No, this isn't an improvement. This code will not be executed if > transfer mode is byte mode. This is added because data handling pattern > is different in buffer mode so the collected data in buffer mode should > be sent when it recieves RX_DONE. Oh sorry about that, I saw the switch to the devm_platform_ioremap_resource below and saw all the FIELD_{GET|PREP} and assumed that some of them were improvements. If devm_platform_ioremap_resource is the only one, that's fine. Actually, would you mind (in a separate commit), update the existing usages to FIELD_{GET|PREP}? It's kind of jarring going back and forth between them. > >> + for (i = 0; i < len; i++) { > >> + value = readb(bus->buf_base + i); > >> + i2c_slave_event(slave, > >> + I2C_SLAVE_WRITE_RECEIVED, > >> + &value); > >> + } > >> + } > >> + } > >> irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; > >> bus->slave_state = ASPEED_I2C_SLAVE_STOP; > >> } [....] > >> @@ -990,6 +1180,45 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) > >> bus->get_clk_reg_val = (u32 (*)(struct device *, u32)) > >> match->data; > >> > >> + /* > >> + * Enable I2C SRAM in case of AST2500. > >> + * SRAM is enabled by default in AST2400 and AST2600. > >> + */ > > > > This probe function is already pretty complicated as it is. Can we move > > this to a helper function (especially since it only applies to the > > 25xx)? > > Okay, that would be better. I'll add this transfer mode setting logic > as a helper function. > > >> + if (of_device_is_compatible(pdev->dev.of_node, > >> + "aspeed,ast2500-i2c-bus")) { > >> + struct regmap *gr_regmap = syscon_regmap_lookup_by_compatible("aspeed,ast2500-i2c-gr"); > > > > So this memory is global, right? It is shared by all the busses? > > Yes, this is global register area which can be shared by all busses. > > > If I am reading this right, then I think we need to protect so that only > > one bus is accessing this memory at a time. > > It will not be accessed at run time but only at probing time. Since we > don't use multi-threaded probing, we don't need to protect it. What if this is loaded as a module? Also, it seems as though turning on SRAM should only happen once. Is this correct? > >> + if (IS_ERR(gr_regmap)) > >> + ret = PTR_ERR(gr_regmap); > >> + else > >> + ret = regmap_update_bits(gr_regmap, > >> + ASPEED_I2CG_GLOBAL_CTRL_REG, > >> + ASPEED_I2CG_SRAM_BUFFER_EN, > >> + ASPEED_I2CG_SRAM_BUFFER_EN); > >> + > >> + if (ret) > >> + sram_enabled = false; > >> + } > >> + > >> + if (sram_enabled) { > >> + struct resource *res = platform_get_resource(pdev, > >> + IORESOURCE_MEM, 1); > >> + > >> + if (res && resource_size(res) >= 2) > >> + bus->buf_base = devm_ioremap_resource(&pdev->dev, res); > >> + > >> + if (!IS_ERR_OR_NULL(bus->buf_base)) { > >> + bus->buf_size = resource_size(res); > >> + if (of_device_is_compatible(pdev->dev.of_node, > >> + "aspeed,ast2400-i2c-bus")) { > >> + bus->buf_page = ((res->start >> 8) & > >> + GENMASK(3, 0)) - 8; > >> + bus->buf_offset = (res->start >> 2) & > >> + ASPEED_I2CD_BUF_OFFSET_MASK; > >> + } > >> + } > >> + } [...] Cheers