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 <[email protected]> --- drivers/i2c/Makefile | 1 + drivers/i2c/i2c-uclass.c | 177 +++++++++++++++++++++++++++++++ include/config_fallbacks.h | 6 ++ include/dm/uclass-id.h | 1 + include/i2c.h | 252 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 437 insertions(+) create mode 100644 drivers/i2c/i2c-uclass.c diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 416ea4f..2ee468d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o obj-$(CONFIG_DW_I2C) += designware_i2c.o diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c new file mode 100644 index 0000000..6bdce8c --- /dev/null +++ b/drivers/i2c/i2c-uclass.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <dm/device-internal.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +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); +} + +int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +{ + struct udevice *dev; + + 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); + if (ret) + return ret; + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int i2c_probe(struct udevice *bus, uint chip) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct udevice *dev; + int ret; + + if (!ops->probe) + return -ENODEV; + + /* First probe that chip */ + ret = ops->probe(bus, chip); + if (ret) + return ret; + + /* The cihp was found, see if we have a driver, and probe it */ + return i2c_get_chip(bus, chip, &dev); + + /* No driver. TODO create a dummy one */ +} + +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; +} + +int i2c_chip_ofdata_to_platdata(const void *blob, int node, + struct dm_i2c_chip *chip) +{ + chip->addr_len = 1; /* default */ + chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1); + if (chip->chip_addr == -1) { + debug("%s: I2C Node '%s' has no 'reg' property\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + return 0; +} + +static int i2c_post_probe(struct udevice *dev) +{ + struct dm_i2c_bus *i2c = dev->uclass_priv; + + i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 100000); + + return 0; +} + +int i2c_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .post_bind = i2c_post_bind, + .post_probe = i2c_post_probe, +}; diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h index 76818f6..c75f42d 100644 --- a/include/config_fallbacks.h +++ b/include/config_fallbacks.h @@ -91,4 +91,10 @@ #undef CONFIG_IMAGE_FORMAT_LEGACY #endif +#ifdef CONFIG_DM_I2C +# ifdef CONFIG_SYS_I2C +# error "Cannot define CONFIG_SYS_I2C when CONFIG_DM_I2C is used" +# endif +#endif + #endif /* __CONFIG_FALLBACKS_H */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index e3e9296..9611a13 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -28,6 +28,7 @@ enum uclass_id { UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_CROS_EC, /* Chrome OS EC */ + UCLASS_I2C, /* I2C bus */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/i2c.h b/include/i2c.h index 1b4078e..85b914d 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -18,6 +18,255 @@ #define _I2C_H_ /* + * For now there are essentially two parts to this file - driver model + * here at the top, and the older code below (with CONFIG_SYS_I2C being + * most recent). The plan is to migrate everything to driver model. + * The driver model structures and API are separate as they are different + * enough as to be incompatible for compilation purposes. + */ + +#ifdef CONFIG_DM_I2C + +/** + * struct dm_i2c_chip - information about an i2c chip + * + * An I2C chip is a device on the I2C bus. It sits at a particular address + * and normally supports 7-bit or 10-bit addressing. + * + * To obtain this structure, use dev_get_parentdata(dev) where dev is the + * chip to examine. + * + * @chip_addr: Chip address on bus + * @addr_len: Address length in bytes (normally 1 for 7-bit address) + * @emul: Emulator for this chip address (only used for emulation) + */ +struct dm_i2c_chip { + uint chip_addr; + uint addr_len; +#ifdef CONFIG_SANDBOX + struct udevice *emul; +#endif +}; + +/** + * struct dm_i2c_bus- information about an i2c bus + * + * An I2C bus contains 0 or more chips on it, each at its own address. The + * bus can operate at different speeds (measured in Hz, typically 100KHz + * or 400KHz). + * + * To obtain this structure, use bus->uclass_priv where bus is the I2C + * bus udevice. + * + * @speed_hz: Bus speed in hertz (typically 100000) + */ +struct dm_i2c_bus { + int speed_hz; +}; + +/** + * i2c_read() - read bytes from an I2C chip + * + * To obtain an I2C device (called a 'chip') given the I2C bus address you + * can use i2c_get_chip(). To obtain a bus by bus number use + * uclass_get_device_by_seq(UCLASS_I2C, <bus number>). + * + * To set the address length of a devce use i2c_set_addr_len(). It + * defaults to 1. + * + * @dev: Chip to read from + * @offset: Offset within chip to start reading + * @buffer: Place to put data + * @len: Number of bytes to read + * + * @return 0 on success, -ve on failure + */ +int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, + int len); + +/** + * i2c_write() - write bytes to an I2C chip + * + * See notes for i2c_read() above. + * + * @dev: Chip to write to + * @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 i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, + int len); + +/** + * i2c_probe() - probe a particular chip address + * + * This can be useful to check for the existence of a chip on the bus. + * It is typically implemented by writing the chip address to the bus + * and checking that the chip replies with an ACK. + * + * @bus: Bus to probe + * @chip_addr: 7-bit address to probe (10-bit and others are not supported) + * @return 0 if a chip was found at that address, -ve if not + */ +int i2c_probe(struct udevice *bus, uint chip_addr); + +/** + * i2c_set_bus_speed() - set the speed of a bus + * + * @bus: Bus to adjust + * @speed: Requested speed in Hz + * @return 0 if OK, -EINVAL for invalid values + */ +int i2c_set_bus_speed(struct udevice *bus, unsigned int speed); + +/** + * i2c_get_bus_speed() - get the speed of a bus + * + * @bus: Bus to check + * @return speed of selected I2C bus in Hz, -ve on error + */ +int i2c_get_bus_speed(struct udevice *bus); + +/** + * i2c_set_addr_len() - set the address length of a chip + * + * Typically addresses are 7 bits (so @addr_len should be 1 which is the + * default). For 10-bit addresses use a value of 2. Some drivers and + * chips may support 0 and 3 also. + * + * @dev: Chip to adjust + * @addr_len: New address length value (typically 1 or 2) + * @return 0 if OK, -EINVAL if value is unsupported, other -ve value on error + */ +int i2c_set_addr_len(struct udevice *dev, uint addr_len); + +/** + * 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); + + /** + * probe() - probe a particular chip address (recommended) + * + * This function is optional but should be implemented since it is + * an expected feature of the I2C subsystem. + * + * @bus: Bus to probe + * @chip_addr: 7-bit address to probe + * @return 0 if a chip was found at that address, -ve if not + */ + int (*probe)(struct udevice *bus, uint chip_addr); + + /** + * set_bus_speed() - set the speed of a bus (optional) + * + * The bus speed value will be updated by the uclass if this function + * does not return an error. + * + * @bus: Bus to adjust + * @speed: Requested speed in Hz + * @return 0 if OK, -INVAL for invalid values + */ + int (*set_bus_speed)(struct udevice *bus, unsigned int speed); + + /** + * get_bus_speed() - get the speed of a bus (optional) + * + * Normally this can be provided by the uclass, but if you want your + * driver to check the bus speed by looking at the hardware, you can + * implement that here. + * + * @bus: Bus to check + * @return speed of selected I2C bus in Hz, -ve on error + */ + int (*get_bus_speed)(struct udevice *bus); + + /** + * set_addr_len() - set the address length of a chip (optional) + * + * This is generally implemented by the uclass, but drivers can + * check the value to ensure that unsupported options are not used. + * If provided, this method will always be called when the address + * length changes from the default of 1. + * + * @dev: Chip to adjust + * @addr_len: New address length value (typically 1 or 2) + * @return 0 if OK, -INVAL if value is unsupported + */ + int (*set_addr_len)(struct udevice *dev, uint addr_len); +}; + +#define i2c_get_ops(dev) ((struct dm_i2c_ops *)(dev)->driver->ops) + +/** + * i2c_get_chip() - get a device to use to access a chip on a bus + * + * This returns the device for the given chip address. The device can then + * be used with calls to i2c_read(), i2c_write(), i2c_probe(), etc. + * + * TODO([email protected]): This function should permit a device to be + * created 'on the fly' for any address. + * + * @bus: Bus to examine + * @chip_addr: Chip address for the new device + * @devp: Returns pointer to new device if found or -ENODEV if not + * found + * + */ +int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp); + +/** + * i2c_chip_ofdata_to_platdata() - Decode standard I2C platform data + * + * This decodes the chip address from a device tree node and puts it into + * its dm_i2c_chip structure. This should be called in your driver's + * ofdata_to_platdata() method. + * + * @blob: Device tree blob + * @node: Node offset to read from + * @spi: Place to put the decoded information + */ +int i2c_chip_ofdata_to_platdata(const void *blob, int node, + struct dm_i2c_chip *chip); + +#endif + +#ifndef CONFIG_DM_I2C + +/* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * The implementation MUST NOT use static or global variables if the @@ -451,4 +700,7 @@ int i2c_get_bus_num_fdt(int node); * @return 0 if port was reset, -1 if not found */ int i2c_reset_port_fdt(const void *blob, int node); + +#endif /* !CONFIG_DM_I2C */ + #endif /* _I2C_H_ */ -- 2.1.0.rc2.206.gedb03e5 _______________________________________________ U-Boot mailing list [email protected] http://lists.denx.de/mailman/listinfo/u-boot

