Re: [RFCv3 2/6] i2c: add I2C Address Translator (ATR) support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Feb 7, 2022 at 7:55 PM Luca Ceresoli <luca@xxxxxxxxxxxxxxxx> wrote:
>
> An ATR is a device that looks similar to an i2c-mux: it has an I2C
> slave "upstream" port and N master "downstream" ports, and forwards
> transactions from upstream to the appropriate downstream port. But is
> is different in that the forwarded transaction has a different slave
> address. The address used on the upstream bus is called the "alias"
> and is (potentially) different from the physical slave address of the
> downstream chip.
>
> Add a helper file (just like i2c-mux.c for a mux or switch) to allow
> implementing ATR features in a device driver. The helper takes care or
> adapter creation/destruction and translates addresses at each transaction.

Why I2C mux driver can't be updated to support this feature?

...

>  RFCv1 was implemented inside i2c-mux.c and added yet more complexity
>  there. RFCv2 creates a new file on its own, i2c-atr.c. Since many ATR
>  features are not in a MUX and vice versa, the overlapping is low. This was
>  almost a complete rewrite, but for the records here are the main
>  differences from the old implementation:

While this is from a code perspective, maybe i2c mux and this one can
still share some parts?

...

> +config I2C_ATR
> +       tristate "I2C Address Translator (ATR) support"
> +       help
> +         Enable support for I2C Address Translator (ATR) chips.
> +
> +         An ATR allows accessing multiple I2C busses from a single
> +         physical bus via address translation instead of bus selection as
> +         i2c-muxes do.

What would be the module name?

...

> +/**

Is this a kernel doc formatted documentation?
Haven't you got a warning?

> + * I2C Address Translator
> + *
> + * Copyright (c) 2019 Luca Ceresoli <luca@xxxxxxxxxxxxxxxx>

2019,2022?

> + *
> + * An I2C Address Translator (ATR) is a device with an I2C slave parent
> + * ("upstream") port and N I2C master child ("downstream") ports, and
> + * forwards transactions from upstream to the appropriate downstream port
> + * with a modified slave address. The address used on the parent bus is
> + * called the "alias" and is (potentially) different from the physical
> + * slave address of the child bus. Address translation is done by the
> + * hardware.
> + *
> + * An ATR looks similar to an i2c-mux except:
> + * - the address on the parent and child busses can be different
> + * - there is normally no need to select the child port; the alias used on
> + *   the parent bus implies it
> + *
> + * The ATR functionality can be provided by a chip with many other
> + * features. This file provides a helper to implement an ATR within your
> + * driver.
> + *
> + * The ATR creates a new I2C "child" adapter on each child bus. Adding
> + * devices on the child bus ends up in invoking the driver code to select
> + * an available alias. Maintaining an appropriate pool of available aliases
> + * and picking one for each new device is up to the driver implementer. The
> + * ATR maintains an table of currently assigned alias and uses it to modify
> + * all I2C transactions directed to devices on the child buses.
> + *
> + * A typical example follows.
> + *
> + * Topology:
> + *
> + *                       Slave X @ 0x10
> + *               .-----.   |
> + *   .-----.     |     |---+---- B
> + *   | CPU |--A--| ATR |
> + *   `-----'     |     |---+---- C
> + *               `-----'   |
> + *                       Slave Y @ 0x10
> + *
> + * Alias table:
> + *
> + *   Client  Alias
> + *   -------------
> + *      X    0x20
> + *      Y    0x30
> + *
> + * Transaction:
> + *
> + *  - Slave X driver sends a transaction (on adapter B), slave address 0x10
> + *  - ATR driver rewrites messages with address 0x20, forwards to adapter A
> + *  - Physical I2C transaction on bus A, slave address 0x20
> + *  - ATR chip propagates transaction on bus B with address translated to 0x10
> + *  - Slave X chip replies on bus B
> + *  - ATR chip forwards reply on bus A
> + *  - ATR driver rewrites messages with address 0x10
> + *  - Slave X driver gets back the msgs[], with reply and address 0x10
> + *
> + * Usage:
> + *
> + *  1. In your driver (typically in the probe function) add an ATR by
> + *     calling i2c_atr_new() passing your attach/detach callbacks
> + *  2. When the attach callback is called pick an appropriate alias,
> + *     configure it in your chip and return the chosen alias in the
> + *     alias_id parameter
> + *  3. When the detach callback is called, deconfigure the alias from
> + *     your chip and put it back in the pool for later usage
> + *
> + * Originally based on i2c-mux.c
> + */

Shouldn't this comment be somewhere under Documentation/ ?

...

> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>


> +static int i2c_atr_map_msgs(struct i2c_atr_chan *chan,
> +                           struct i2c_msg msgs[], int num)

foo[] makes not much sense in the function parameter. *foo is what
will be used and it's explicit.

Can this be located on one line (similar question to make compact the
rest of the function declarations)?

> +

Redundant blank line.

...

> +       /* Ensure we have enough room to save the original addresses */
> +       if (unlikely(chan->orig_addrs_size < num)) {

> +               void *new_buf = kmalloc(num * sizeof(chan->orig_addrs[0]),
> +                                       GFP_KERNEL);

Use kmalloc_array()

> +               if (new_buf == NULL)
> +                       return -ENOMEM;
> +
> +               kfree(chan->orig_addrs);

Hmm... is it a reimplementation of krealloc_array()?

> +               chan->orig_addrs = new_buf;
> +               chan->orig_addrs_size = num;
> +       }

...

> +               if (c2a) {
> +                       msgs[i].addr = c2a->alias;
> +               } else {
> +                       dev_err(atr->dev, "client 0x%02x not mapped!\n",
> +                               msgs[i].addr);
> +                       return -ENXIO;
> +               }

'else' would be redundant if you switch to the traditional pattern,
i.e. check for errors first.

...

> +/*
> + * Restore all message address aliases with the original addresses.
> + *
> + * This function is internal for use in i2c_atr_master_xfer().
> + *
> + * @see i2c_atr_map_msgs()
> + */

Too sparse formatting of the comment. Can you make it compact?

...

> +       int ret = 0;

Unneeded assignment.

> +       /* Switch to the right atr port */
> +       if (atr->ops->select) {
> +               ret = atr->ops->select(atr, chan->chan_id);
> +               if (ret < 0)
> +                       goto out;
> +       }
> +
> +       /* Translate addresses */
> +       mutex_lock(&chan->orig_addrs_lock);
> +       ret = i2c_atr_map_msgs(chan, msgs, num);
> +       if (ret < 0) {

> +               mutex_unlock(&chan->orig_addrs_lock);
> +               goto out;

goto out_unlock_deselect;

> +       }
> +
> +       /* Perform the transfer */
> +       ret = i2c_transfer(parent, msgs, num);
> +
> +       /* Restore addresses */
> +       i2c_atr_unmap_msgs(chan, msgs, num);

out_unlock_deselct:

> +       mutex_unlock(&chan->orig_addrs_lock);

> +out:

out_deselect:

> +       if (atr->ops->deselect)
> +               atr->ops->deselect(atr, chan->chan_id);
> +
> +       return ret;
> +}

...

> +       int err = 0;

Be consistent with ret vs. err across the functions.

> +       if (atr->ops->select)
> +               err = atr->ops->select(atr, chan->chan_id);

> +       if (!err)

Perhaps

       int ret;

       ret = 0;
       if (atr->ops->select)
               ret = atr->ops->select(atr, chan->chan_id);
       if (ret)
               goto out_deselect;


> +               err = i2c_smbus_xfer(parent, c2a->alias, flags,
> +                                    read_write, command, size, data);

out_deselect:

> +       if (atr->ops->deselect)
> +               atr->ops->deselect(atr, chan->chan_id);
> +
> +       return err;
> +}

...

> +       int err = 0;

Same as above: naming, useless assignment.

...

> +       c2a = kzalloc(sizeof(struct i2c_atr_cli2alias_pair), GFP_KERNEL);

sizeof(*c2a)

> +       if (!c2a) {
> +               err = -ENOMEM;
> +               goto err_alloc;

Useless label, return directly.

> +       }

...

> +       c2a = i2c_atr_find_mapping_by_client(&chan->alias_list, client);
> +       if (c2a != NULL) {

if (c2a)

> +               list_del(&c2a->node);
> +               kfree(c2a);
> +       }

...

> +       char symlink_name[20];

Why 20? Do we have a predefined constant for that?


> +       if (dev->of_node) {

This check can be dropped, also please use device property and fwnode
APIs. No good of having OF-centric generic modules nowadays.

> +               struct device_node *atr_node;
> +               struct device_node *child;
> +               u32 reg;
> +
> +               atr_node = of_get_child_by_name(dev->of_node, "i2c-atr");

atr_node = device_get_named_child_node(...);

fwnode_for_each_child_node() {
}

> +               for_each_child_of_node(atr_node, child) {
> +                       err = of_property_read_u32(child, "reg", &reg);
> +                       if (err)
> +                               continue;
> +                       if (chan_id == reg)
> +                               break;
> +               }
> +
> +               chan->adap.dev.of_node = child;
> +               of_node_put(atr_node);
> +       }

On the second thought can you utilize the parser from I2C mux?

...

> +       WARN(sysfs_create_link(&chan->adap.dev.kobj, &dev->kobj, "atr_device"),
> +            "can't create symlink to atr device\n");
> +       snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
> +       WARN(sysfs_create_link(&dev->kobj, &chan->adap.dev.kobj, symlink_name),
> +            "can't create symlink for channel %u\n", chan_id);

Doesn't sysfs already has a warning when it's really needed?

...

> +       if (atr->adapter[chan_id] == NULL) {
> +               dev_err(dev, "Adapter %d does not exist\n", chan_id);

Noisy message. On freeing we usually don't issue such when we try to
free already freeed resource.

> +               return;
> +       }

...

> +       atr = devm_kzalloc(dev, sizeof(*atr)
> +                           + max_adapters * sizeof(atr->adapter[0]),
> +                           GFP_KERNEL);

Check overflow.h and use respective macro here.

> +       if (!atr)
> +               return ERR_PTR(-ENOMEM);

...

> +/**

It's not a kernel doc.

> + * drivers/i2c/i2c-atr.h -- I2C Address Translator

Please, no names of the files inside the files.

> + * Copyright (c) 2019 Luca Ceresoli <luca@xxxxxxxxxxxxxxxx>

2019,2022 ?

> + * Based on i2c-mux.h
> + */

...

> +#ifdef __KERNEL__

Why?

...

> +#include <linux/i2c.h>
> +#include <linux/mutex.h>

Missed types.h

Missed struct device;

-- 
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux