[PATCH v4 4/4] i2c: aspeed: add DMA mode transfer support
Jae Hyun Yoo
jae.hyun.yoo at linux.intel.com
Thu May 20 04:38:56 AEST 2021
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
[....]
More information about the Linux-aspeed
mailing list