Re: [PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-22 Thread Wolfram Sang

> >> @@ -326,6 +463,12 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data 
> >> *drv_data)
> >>drv_data->reg_base + drv_data->reg_offsets.control);
> >>break;
> >>  
> >> +  case MV64XXX_I2C_ACTION_OFFLOAD_SEND_START:
> >> +  if (mv64xxx_i2c_offload_msg(drv_data) <= 0)
> > 
> > needs to be adjusted when using -EINVAL above. I'd prefer the error case
> > in the else branch, though. Easier to read.
> > 
> 
> OK, but in this case ...
> 
> >> +  break;
> >> +  else
> >> +  drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
> >> +  /* FALLTHRU */
> 
> ... the fall through here is less readable. But it is a matter of
> taste, I will change this.

Ah, I see. Well, try both and decide.

Thanks!



signature.asc
Description: Digital signature


Re: [PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-22 Thread Gregory CLEMENT
On 21/08/2013 23:01, Wolfram Sang wrote:
> Hi,
> 
> here is the review. BTW have you tested with and without the offload
> engine?

yes with eeprog.

> 
>> +static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
>> +{
>> +unsigned long data_reg_hi = 0;
>> +unsigned long data_reg_lo = 0;
>> +unsigned long ctrl_reg;
>> +unsigned int i;
>> +struct i2c_msg *msg = drv_data->msgs;
>> +
>> +drv_data->msg = msg;
>> +drv_data->byte_posn = 0;
>> +drv_data->bytes_left = msg->len;
>> +drv_data->aborting = 0;
>> +drv_data->rc = 0;
>> +/* Only regular transactions can be offloaded */
>> +if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0)
>> +return 1;
> 
> -EINVAL?
> 

OK

>> +
>> +/* Only 1-8 byte transfers can be offloaded */
>> +if (msg->len < 1 || msg->len > 8)
>> +return 1;
> 
> ditto
> 

OK

>> +
>> +/* Build transaction */
>> +ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE |
>> +   (msg->addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT);
>> +
>> +if ((msg->flags & I2C_M_TEN) != 0)
>> +ctrl_reg |=  MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT;
>> +
>> +if ((msg->flags & I2C_M_RD) == 0) {
>> +for (i = 0; i < 4 && i < msg->len; i++)
>> +data_reg_lo = data_reg_lo |
>> +(msg->buf[i] << ((i & 0x3) * 8));
>> +
>> +for (i = 4; i < 8 && i < msg->len; i++)
>> +data_reg_hi = data_reg_hi |
>> +(msg->buf[i] << ((i & 0x3) * 8));
> 
> What about:
> 
>   local_buf[8] = { 0 };
> 
>   memcpy(local_buf, msg->buf, msg->len);
>   cpu_to_be32(...)
> 
> ? A lot less lines and be32 macros are likely more efficient. Copy loop
> probably, too.
> 

OK


>> +
>> +ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR |
>> +(msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT;
>> +} else {
>> +ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD |
>> +(msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT;
>> +}
>> +
>> +/* Execute transaction */
>> +writel_relaxed(data_reg_lo,
>> +drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO);
>> +writel_relaxed(data_reg_hi,
>> +drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI);
> 
> Do you need to write the 0 in case of I2C_M_RD?
> 

Not sure I will check it

>> +writel(ctrl_reg, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL);
>> +
>> +return 0;
>> +}
>> +
>> +static void
>> +mv64xxx_i2c_update_offload_data(struct i2c_msg *msg, unsigned long 
>> data_reg_hi,
>> +unsigned long data_reg_lo)
>> +{
>> +int i;
>> +
>> +if ((msg->flags & I2C_M_RD) != 0) {
> 
> != 0 is superfluous
> 

OK

>> +for (i = 0; i < 4 && i < msg->len; i++) {
>> +msg->buf[i] = data_reg_lo & 0xFF;
>> +data_reg_lo >>= 8;
>> +}
>> +
>> +for (i = 4; i < 8 && i < msg->len; i++) {
>> +msg->buf[i] = data_reg_hi & 0xFF;
>> +data_reg_hi >>= 8;
>> +}
>> +}
> 
> Same idea as above?
> 

OK I will do it here also as in both cases the number of lines will be
shorter, but for small amount of data I am not sure it will be faster.

>> @@ -298,21 +420,36 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 
>> status)
>>  static void
>>  mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>>  {
>> +unsigned long data_reg_hi = 0;
>> +unsigned long data_reg_lo = 0;
>> +
>>  switch(drv_data->action) {
>> +case MV64XXX_I2C_ACTION_OFFLOAD_RESTART:
>> +data_reg_lo = readl(drv_data->reg_base +
>> +MV64XXX_I2C_REG_RX_DATA_LO);
>> +data_reg_hi = readl(drv_data->reg_base +
>> +MV64XXX_I2C_REG_RX_DATA_HI);
> 
> Initializing data_reg_* is the same for both calls to
> update_offload_data, so it could be moved into the function.
> Probably not needed when using the local_buf idea.
> 

I will move this part in the update_offload_data function.

>> @@ -326,6 +463,12 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>>  drv_data->reg_base + drv_data->reg_offsets.control);
>>  break;
>>  
>> +case MV64XXX_I2C_ACTION_OFFLOAD_SEND_START:
>> +if (mv64xxx_i2c_offload_msg(drv_data) <= 0)
> 
> needs to be adjusted when using -EINVAL above. I'd prefer the error case
> in the else branch, though. Easier to read.
> 

OK, but in this case ...

>> +break;
>> +else
>> +drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
>> +/* FALLTHRU */

... the fall through here is less readable. But it is a matter of
taste, I will change this.

>>  case MV64XXX_I2C_ACTION_SEND_START:
>>  writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
>> 

Re: [PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-21 Thread Wolfram Sang
Hi,

here is the review. BTW have you tested with and without the offload
engine?

> +static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
> +{
> + unsigned long data_reg_hi = 0;
> + unsigned long data_reg_lo = 0;
> + unsigned long ctrl_reg;
> + unsigned int i;
> + struct i2c_msg *msg = drv_data->msgs;
> +
> + drv_data->msg = msg;
> + drv_data->byte_posn = 0;
> + drv_data->bytes_left = msg->len;
> + drv_data->aborting = 0;
> + drv_data->rc = 0;
> + /* Only regular transactions can be offloaded */
> + if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0)
> + return 1;

-EINVAL?

> +
> + /* Only 1-8 byte transfers can be offloaded */
> + if (msg->len < 1 || msg->len > 8)
> + return 1;

ditto

> +
> + /* Build transaction */
> + ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE |
> +(msg->addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT);
> +
> + if ((msg->flags & I2C_M_TEN) != 0)
> + ctrl_reg |=  MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT;
> +
> + if ((msg->flags & I2C_M_RD) == 0) {
> + for (i = 0; i < 4 && i < msg->len; i++)
> + data_reg_lo = data_reg_lo |
> + (msg->buf[i] << ((i & 0x3) * 8));
> +
> + for (i = 4; i < 8 && i < msg->len; i++)
> + data_reg_hi = data_reg_hi |
> + (msg->buf[i] << ((i & 0x3) * 8));

What about:

local_buf[8] = { 0 };

memcpy(local_buf, msg->buf, msg->len);
cpu_to_be32(...)

? A lot less lines and be32 macros are likely more efficient. Copy loop
probably, too.

> +
> + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR |
> + (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT;
> + } else {
> + ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD |
> + (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT;
> + }
> +
> + /* Execute transaction */
> + writel_relaxed(data_reg_lo,
> + drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_LO);
> + writel_relaxed(data_reg_hi,
> + drv_data->reg_base + MV64XXX_I2C_REG_TX_DATA_HI);

Do you need to write the 0 in case of I2C_M_RD?

> + writel(ctrl_reg, drv_data->reg_base + MV64XXX_I2C_REG_BRIDGE_CONTROL);
> +
> + return 0;
> +}
> +
> +static void
> +mv64xxx_i2c_update_offload_data(struct i2c_msg *msg, unsigned long 
> data_reg_hi,
> + unsigned long data_reg_lo)
> +{
> + int i;
> +
> + if ((msg->flags & I2C_M_RD) != 0) {

!= 0 is superfluous

> + for (i = 0; i < 4 && i < msg->len; i++) {
> + msg->buf[i] = data_reg_lo & 0xFF;
> + data_reg_lo >>= 8;
> + }
> +
> + for (i = 4; i < 8 && i < msg->len; i++) {
> + msg->buf[i] = data_reg_hi & 0xFF;
> + data_reg_hi >>= 8;
> + }
> + }

Same idea as above?

> @@ -298,21 +420,36 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 
> status)
>  static void
>  mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>  {
> + unsigned long data_reg_hi = 0;
> + unsigned long data_reg_lo = 0;
> +
>   switch(drv_data->action) {
> + case MV64XXX_I2C_ACTION_OFFLOAD_RESTART:
> + data_reg_lo = readl(drv_data->reg_base +
> + MV64XXX_I2C_REG_RX_DATA_LO);
> + data_reg_hi = readl(drv_data->reg_base +
> + MV64XXX_I2C_REG_RX_DATA_HI);

Initializing data_reg_* is the same for both calls to
update_offload_data, so it could be moved into the function.
Probably not needed when using the local_buf idea.

> @@ -326,6 +463,12 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
>   drv_data->reg_base + drv_data->reg_offsets.control);
>   break;
>  
> + case MV64XXX_I2C_ACTION_OFFLOAD_SEND_START:
> + if (mv64xxx_i2c_offload_msg(drv_data) <= 0)

needs to be adjusted when using -EINVAL above. I'd prefer the error case
in the else branch, though. Easier to read.

> + break;
> + else
> + drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
> + /* FALLTHRU */
>   case MV64XXX_I2C_ACTION_SEND_START:
>   writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
>   drv_data->reg_base + drv_data->reg_offsets.control);

> @@ -601,6 +779,13 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
>  
>   memcpy(&drv_data->reg_offsets, device->data, 
> sizeof(drv_data->reg_offsets));
>  
> + /*
> +  * For controllers embedded in new SoCs activate the
> +  * Transaction Generator support.
> +  */
> + if (of_device_is_compatible(np, "marvell,mv78230-i2c"))
> + drv_data->offload_enabled = true;

For now OK, if there are more users, someone will need t

Re: [PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-20 Thread Gregory CLEMENT
On 15/08/2013 14:13, Wolfram Sang wrote:
> 
>> +if ((msg->flags & I2C_M_RD) == 0) { +   for (i = 0; i < 4 && i 
>> < msg->len; i++) +   data_reg_lo = data_reg_lo | +   
>> (msg->buf[i] << ((i & 0x3) * 8)); + +   
>> for (i = 4; i < 8 && i <
>> msg->len; i++) + data_reg_hi = data_reg_hi | +   
>> (msg->buf[i] << ((i & 0x3) * 8));
> 
> Same comment as in the last version: What about be32_to_cpu and friends 
> instead of the loops (here and later)?
> 

We can't use be32_to_cpu and friends, because one of the stop condition of this
loop is the length of the msg.

For reading from msg->buf we could consider to mask the irrelevant part.But for
writing to msg->buf it would imply to write outside of the allocated buffer!

Do you agree to not change anything on this patch?

Thanks,


-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-15 Thread Wolfram Sang

> + if ((msg->flags & I2C_M_RD) == 0) {
> + for (i = 0; i < 4 && i < msg->len; i++)
> + data_reg_lo = data_reg_lo |
> + (msg->buf[i] << ((i & 0x3) * 8));
> +
> + for (i = 4; i < 8 && i < msg->len; i++)
> + data_reg_hi = data_reg_hi |
> + (msg->buf[i] << ((i & 0x3) * 8));

Same comment as in the last version: What about be32_to_cpu and friends
instead of the loops (here and later)?



signature.asc
Description: Digital signature


[PATCH v5 1/3] i2c-mv64xxx: Add I2C Transaction Generator support

2013-08-09 Thread Gregory CLEMENT
The I2C Transaction Generator offloads CPU from managing I2C
transfer step by step.

This feature is currently only available on Armada XP, so usage of
this mechanism is activated through device tree.

Based on the work of Piotr Ziecik and rewrote to use the new way of
handling multiples i2c messages.

Signed-off-by: Piotr Ziecik 
Signed-off-by: Gregory CLEMENT 
---
 drivers/i2c/busses/i2c-mv64xxx.c | 208 ---
 1 file changed, 197 insertions(+), 11 deletions(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index b1f42bf..406d9c6 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -55,6 +55,32 @@
 #defineMV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK0xe8
 #defineMV64XXX_I2C_STATUS_NO_STATUS0xf8
 
+/* Register defines (I2C bridge) */
+#defineMV64XXX_I2C_REG_TX_DATA_LO  0xc0
+#defineMV64XXX_I2C_REG_TX_DATA_HI  0xc4
+#defineMV64XXX_I2C_REG_RX_DATA_LO  0xc8
+#defineMV64XXX_I2C_REG_RX_DATA_HI  0xcc
+#defineMV64XXX_I2C_REG_BRIDGE_CONTROL  0xd0
+#defineMV64XXX_I2C_REG_BRIDGE_STATUS   0xd4
+#defineMV64XXX_I2C_REG_BRIDGE_INTR_CAUSE   0xd8
+#defineMV64XXX_I2C_REG_BRIDGE_INTR_MASK0xdC
+#defineMV64XXX_I2C_REG_BRIDGE_TIMING   0xe0
+
+/* Bridge Control values */
+#defineMV64XXX_I2C_BRIDGE_CONTROL_WR   0x0001
+#defineMV64XXX_I2C_BRIDGE_CONTROL_RD   0x0002
+#defineMV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT   2
+#defineMV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT 0x1000
+#defineMV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT13
+#defineMV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT16
+#defineMV64XXX_I2C_BRIDGE_CONTROL_ENABLE   0x0008
+
+/* Bridge Status values */
+#defineMV64XXX_I2C_BRIDGE_STATUS_ERROR 0x0001
+#defineMV64XXX_I2C_STATUS_OFFLOAD_ERROR0xf001
+#defineMV64XXX_I2C_STATUS_OFFLOAD_OK   0xf000
+
+
 /* Driver states */
 enum {
MV64XXX_I2C_STATE_INVALID,
@@ -71,14 +97,17 @@ enum {
 enum {
MV64XXX_I2C_ACTION_INVALID,
MV64XXX_I2C_ACTION_CONTINUE,
+   MV64XXX_I2C_ACTION_OFFLOAD_SEND_START,
MV64XXX_I2C_ACTION_SEND_START,
MV64XXX_I2C_ACTION_SEND_RESTART,
+   MV64XXX_I2C_ACTION_OFFLOAD_RESTART,
MV64XXX_I2C_ACTION_SEND_ADDR_1,
MV64XXX_I2C_ACTION_SEND_ADDR_2,
MV64XXX_I2C_ACTION_SEND_DATA,
MV64XXX_I2C_ACTION_RCV_DATA,
MV64XXX_I2C_ACTION_RCV_DATA_STOP,
MV64XXX_I2C_ACTION_SEND_STOP,
+   MV64XXX_I2C_ACTION_OFFLOAD_SEND_STOP,
 };
 
 struct mv64xxx_i2c_regs {
@@ -117,6 +146,7 @@ struct mv64xxx_i2c_data {
spinlock_t  lock;
struct i2c_msg  *msg;
struct i2c_adapter  adapter;
+   booloffload_enabled;
 };
 
 static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
@@ -165,6 +195,79 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data 
*drv_data,
}
 }
 
+static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
+{
+   unsigned long data_reg_hi = 0;
+   unsigned long data_reg_lo = 0;
+   unsigned long ctrl_reg;
+   unsigned int i;
+   struct i2c_msg *msg = drv_data->msgs;
+
+   drv_data->msg = msg;
+   drv_data->byte_posn = 0;
+   drv_data->bytes_left = msg->len;
+   drv_data->aborting = 0;
+   drv_data->rc = 0;
+   /* Only regular transactions can be offloaded */
+   if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0)
+   return 1;
+
+   /* Only 1-8 byte transfers can be offloaded */
+   if (msg->len < 1 || msg->len > 8)
+   return 1;
+
+   /* Build transaction */
+   ctrl_reg = MV64XXX_I2C_BRIDGE_CONTROL_ENABLE |
+  (msg->addr << MV64XXX_I2C_BRIDGE_CONTROL_ADDR_SHIFT);
+
+   if ((msg->flags & I2C_M_TEN) != 0)
+   ctrl_reg |=  MV64XXX_I2C_BRIDGE_CONTROL_ADDR_EXT;
+
+   if ((msg->flags & I2C_M_RD) == 0) {
+   for (i = 0; i < 4 && i < msg->len; i++)
+   data_reg_lo = data_reg_lo |
+   (msg->buf[i] << ((i & 0x3) * 8));
+
+   for (i = 4; i < 8 && i < msg->len; i++)
+   data_reg_hi = data_reg_hi |
+   (msg->buf[i] << ((i & 0x3) * 8));
+
+   ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_WR |
+   (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_TX_SIZE_SHIFT;
+   } else {
+   ctrl_reg |= MV64XXX_I2C_BRIDGE_CONTROL_RD |
+   (msg->len - 1) << MV64XXX_I2C_BRIDGE_CONTROL_RX_SIZE_SHIFT;