On Thu, Dec 08, 2022 at 12:40:00PM +0200, Tomi Valkeinen wrote: > From: Luca Ceresoli <luca@xxxxxxxxxxxxxxxx> > > 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. Besides comments given against diff between series versions, see below. ... > +static int i2c_atr_attach_client(struct i2c_adapter *adapter, > + const struct i2c_board_info *info, > + const struct i2c_client *client) > +{ > + struct i2c_atr_chan *chan = adapter->algo_data; > + struct i2c_atr *atr = chan->atr; > + struct i2c_atr_cli2alias_pair *c2a; > + u16 alias_id; > + int ret; > + > + c2a = kzalloc(sizeof(*c2a), GFP_KERNEL); > + if (!c2a) > + return -ENOMEM; > + > + ret = atr->ops->attach_client(atr, chan->chan_id, info, client, > + &alias_id); > + if (ret) > + goto err_free; > + if (alias_id == 0) { > + ret = -EINVAL; I'm wondering why attach_client can't return this error and provide a guarantee that if no error, the alias_id is never be 0? > + goto err_free; > + } > + > + c2a->client = client; > + c2a->alias = alias_id; > + list_add(&c2a->node, &chan->alias_list); > + > + return 0; > + > +err_free: > + kfree(c2a); > + return ret; > +} ... > + if (bus_handle) { > + device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle)); I believe the correct way, while above still works, is device_set_node(&chan->adap.dev, bus_handle); fwnode_handle_get(dev_fwnode(&chan->adap.dev)); But I agree that this looks a bit verbose. And... > + } else { > + struct fwnode_handle *atr_node; > + struct fwnode_handle *child; > + u32 reg; > + > + atr_node = device_get_named_child_node(dev, "i2c-atr"); > + > + fwnode_for_each_child_node(atr_node, child) { > + ret = fwnode_property_read_u32(child, "reg", ®); > + if (ret) > + continue; > + if (chan_id == reg) > + break; > + } > + > + device_set_node(&chan->adap.dev, child); ...OTOH, you set node with bumped reference here. So I leave all this to the maintainers. > + fwnode_handle_put(atr_node); > + } > + ret = i2c_add_adapter(&chan->adap); > + if (ret) { > + dev_err(dev, "failed to add atr-adapter %u (error=%d)\n", > + chan_id, ret); > + goto err_mutex_destroy; > + } > + > + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", > + chan->chan_id); > + > + ret = sysfs_create_link(&chan->adap.dev.kobj, &dev->kobj, "atr_device"); > + if (ret) > + dev_warn(dev, "can't create symlink to atr device\n"); > + ret = sysfs_create_link(&dev->kobj, &chan->adap.dev.kobj, symlink_name); > + if (ret) > + dev_warn(dev, "can't create symlink for channel %u\n", chan_id); > + > + dev_dbg(dev, "Added ATR child bus %d\n", i2c_adapter_id(&chan->adap)); > + > + atr->adapter[chan_id] = &chan->adap; > + return 0; > + > +err_mutex_destroy: Now it's a bit misleading, wouldn't be better err_put_fwnode: ? > + fwnode_handle_put(dev_fwnode(&chan->adap.dev)); > + mutex_destroy(&chan->orig_addrs_lock); > + kfree(chan); > + return ret; > +} > +EXPORT_SYMBOL_GPL(i2c_atr_add_adapter); Wondering if we may put this into namespace from day 1. ... > +/** > + * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by > + * i2c_atr_del_adapter(). > + * @atr: The I2C ATR > + * @chan_id: Index of the `adapter to be removed (0 .. max_adapters-1) > + */ > +void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) > +{ > + char symlink_name[ATR_MAX_SYMLINK_LEN]; > + > + struct i2c_adapter *adap = atr->adapter[chan_id]; > + struct i2c_atr_chan *chan = adap->algo_data; > + struct fwnode_handle *fwnode = dev_fwnode(&adap->dev); > + struct device *dev = atr->dev; > + if (!atr->adapter[chan_id]) { Isn't it the same as if (!adap) ? > + dev_err(dev, "Adapter %d does not exist\n", chan_id); > + return; > + } > + > + dev_dbg(dev, "Removing ATR child bus %d\n", i2c_adapter_id(adap)); > + > + atr->adapter[chan_id] = NULL; > + > + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", > + chan->chan_id); > + sysfs_remove_link(&dev->kobj, symlink_name); > + sysfs_remove_link(&chan->adap.dev.kobj, "atr_device"); > + > + i2c_del_adapter(adap); > + fwnode_handle_put(fwnode); > + mutex_destroy(&chan->orig_addrs_lock); > + kfree(chan->orig_addrs); > + kfree(chan); > +} ... > +struct i2c_atr { > + /* private: internal use only */ What is private? The entire structure? Then why it's defined in the include/linux/? Can't you make it opaque? > + struct i2c_adapter *parent; > + struct device *dev; > + const struct i2c_atr_ops *ops; > + > + void *priv; > + > + struct i2c_algorithm algo; > + /* lock for the I2C bus segment (see struct i2c_lock_operations) */ > + struct mutex lock; > + int max_adapters; > + > + struct i2c_adapter *adapter[]; > +}; ... > +static inline void i2c_atr_set_clientdata(struct i2c_atr *atr, void *data) > +{ > + atr->priv = data; > +} > + > +static inline void *i2c_atr_get_clientdata(struct i2c_atr *atr) > +{ > + return atr->priv; > +} The function names are misleading, because I would think this is about driver data that has been set. I would rather use name like i2c_atr_get_priv() i2c_atr_set_priv() -- With Best Regards, Andy Shevchenko