[U-Boot] [PATCH v2 08/17] dm: i2c: Add a uclass for I2C

Masahiro Yamada yamada.m at jp.panasonic.com
Wed Nov 19 09:56:34 CET 2014


Hi Simon,



On Tue, 11 Nov 2014 10:46:24 -0700
Simon Glass <sjg at chromium.org> wrote:

> The uclass implements the same operations as the current I2C framework but
> makes some changes to make it fit driver model better:
> 
> - Remove the chip address from API calls
> - Remove the address length from API calls
> - Remove concept of 'current' I2C bus
> - Drop all existing init functions
> 
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
> 
> Changes in v2:
> - Fix cihp typo
> - Implement generic I2C devices to allow 'i2c probe' on unknown devices
> - Return the probed device from i2c_probe()
> - Set the bus speed after the bus is probed
> - Add some debugging for generic I2C device binding
> - Add a 'deblock' method to recover an I2C bus stuck in mid-transaction
> - Add a helper function to find a chip on a particular bus number
[snip]
> +
> +int i2c_read(struct udevice *dev, uint addr, uint8_t *buffer, int len)
> +{
> +	struct dm_i2c_chip *chip = dev_get_parentdata(dev);
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +
> +	if (!ops->read)
> +		return -ENOSYS;
> +
> +	return ops->read(bus, chip->chip_addr, addr, chip->addr_len, buffer,
> +			 len);
> +}
> +
> +int i2c_write(struct udevice *dev, uint addr, const uint8_t *buffer, int len)
> +{
> +	struct dm_i2c_chip *chip = dev_get_parentdata(dev);
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +
> +	if (!ops->write)
> +		return -ENOSYS;
> +
> +	return ops->write(bus, chip->chip_addr, addr, chip->addr_len, buffer,
> +			  len);
> +}

This seems inconsistent with "struct dm_i2c_ops".
In this function, the address length(chip->addr_len) is passed to the forth argument of ops->write().

You should compare it with "struct dm_i2c_ops" in include/i2c.h
It says that the third argument is alen.



BTW, address_offset within the chip and data are treated in the same way in I2C bus.
Should we pass them separately to each driver?

I mean, can we put the offset address and data in the buffer?





> +
> +int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp)
> +{
> +	struct udevice *dev;
> +
> +	debug("%s: Searching bus '%s' for address %02x: ", __func__,
> +	      bus->name, chip_addr);
> +	for (device_find_first_child(bus, &dev); dev;
> +			device_find_next_child(&dev)) {
> +		struct dm_i2c_chip store;
> +		struct dm_i2c_chip *chip = dev_get_parentdata(dev);
> +		int ret;
> +
> +		if (!chip) {
> +			chip = &store;
> +			i2c_chip_ofdata_to_platdata(gd->fdt_blob,
> +						    dev->of_offset, chip);
> +		}
> +		if (chip->chip_addr == chip_addr) {
> +			ret = device_probe(dev);
> +			debug("found, ret=%d\n", ret);
> +			if (ret)
> +				return ret;
> +			*devp = dev;
> +			return 0;
> +		}


If "chip" is "NULL", i2c_chip_ofdata_to_platdata() is called to create
struct dm_i2c_chip, but it is not thrown away soon.  It is not efficient.

If we use device_probe_child() instead of device_probe(), I think we can
re-use it.

I mean,

                if (chip->chip_addr == chip_addr) {
                        ret = device_probe_child(dev, chip);


Perhaps, we can remove sandbox_i2c_child_pre_probe().





> +	}
> +	debug("not found\n");
> +	return i2c_bind_driver(bus, chip_addr, devp);
> +}



If no chip-device is found at the specified chip_addr,
the last line calls i2c_bind_driver().  Why?

The i2c_bind_driver() tries to create a "generic" chip.
What is this "generic" chip?

Besides, i2c_bind_driver() tries to probe the created generic chip,
but it always fails in i2c_chip_ofdata_to_platdata()
because the generic chip does not have "reg" property

I could not understand at all what this code wants to do.






> +}
> +
> +int i2c_probe(struct udevice *bus, uint chip_addr, struct udevice **devp)
> +{
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +	int ret;
> +
> +	*devp = NULL;
> +	if (!ops->probe)
> +		return -ENODEV;
> +
> +	/* First probe that chip */
> +	ret = ops->probe(bus, chip_addr);
> +	debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name,
> +	      chip_addr, ret);
> +	if (ret)
> +		return ret;


Is the "ops->probe" responsible to probe child devices?
(At least, sandbox_i2c probes its children)



> +	/* The chip was found, see if we have a driver, and probe it */
> +	ret = i2c_get_chip(bus, chip_addr, devp);
> +	debug("%s:  i2c_get_chip: ret=%d\n", __func__, ret);
> +	if (!ret || ret != -ENODEV)
> +		return ret;
> +
> +	return i2c_bind_driver(bus, chip_addr, devp);
> +}


If so, why do we need to call i2c_get_chip() and probe it again?






> +int i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
> +{
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +	struct dm_i2c_bus *i2c = bus->uclass_priv;
> +	int ret;
> +
> +	if (ops->set_bus_speed) {
> +		ret = ops->set_bus_speed(bus, speed);
> +		if (ret)
> +			return ret;
> +	}
> +	i2c->speed_hz = speed;
> +
> +	return 0;
> +}
> +
> +/*
> + * i2c_get_bus_speed:
> + *
> + *  Returns speed of selected I2C bus in Hz
> + */
> +int i2c_get_bus_speed(struct udevice *bus)
> +{
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +	struct dm_i2c_bus *i2c = bus->uclass_priv;
> +
> +	if (!ops->set_bus_speed)
> +		return i2c->speed_hz;
> +
> +	return ops->get_bus_speed(bus);
> +}
> +
> +int i2c_set_addr_len(struct udevice *dev, uint addr_len)
> +{
> +	struct udevice *bus = dev->parent;
> +	struct dm_i2c_chip *chip = dev_get_parentdata(dev);
> +	struct dm_i2c_ops *ops = i2c_get_ops(bus);
> +	int ret;
> +
> +	if (addr_len > 3)
> +		return -EINVAL;
> +	if (ops->set_addr_len) {
> +		ret = ops->set_addr_len(dev, addr_len);
> +		if (ret)
> +			return ret;
> +	}
> +	chip->addr_len = addr_len;
> +
> +	return 0;
> +}


I am not 100% sure, but "addr_len" is a user-configurable parameter?

I think each device has its own fixed address size.












> +UCLASS_DRIVER(i2c_generic) = {
> +	.id		= UCLASS_I2C_GENERIC,
> +	.name		= "i2c_generic",
> +};
> +
> +U_BOOT_DRIVER(i2c_generic_drv) = {
> +	.name		= "i2c_generic_drv",
> +	.id		= UCLASS_I2C_GENERIC,
> +};


Could you explain how "i2c_generic" is used.



> +
> +/**
> + * struct dm_i2c_ops - driver operations for I2C uclass
> + *
> + * Drivers should support these operations unless otherwise noted. These
> + * operations are intended to be used by uclass code, not directly from
> + * other code.
> + */
> +struct dm_i2c_ops {
> +	/**
> +	 * read() - read from a chip
> +	 *
> +	 * @bus:	Bus to read from
> +	 * @chip_addr:	Chip address to read from
> +	 * @alen:	Length of chip address in bytes
> +	 * @offset:	Offset within chip to start reading
> +	 * @buffer:	Place to put data
> +	 * @len:	Number of bytes to read
> +	 */
> +	int (*read)(struct udevice *bus, uint chip_addr, uint alen,
> +		    uint offset, uint8_t *buffer, int len);
> +
> +	/**
> +	 * write() - write bytes to a chip
> +	 *
> +	 * @dev:	Device to write to
> +	 * @chip_addr:	Chip address to read from
> +	 * @alen:	Length of chip address in bytes
> +	 * @offset:	Offset within chip to start writing
> +	 * @buffer:	Buffer containing data to write
> +	 * @len:	Number of bytes to write
> +	 *
> +	 * @return 0 on success, -ve on failure
> +	 */
> +	int (*write)(struct udevice *bus, uint chip_addr, uint alen,
> +		     uint offset, const uint8_t *buffer, int len);


See. The third argument is address length.



Best Regards
Masahiro Yamada



More information about the U-Boot mailing list