[coreboot-gerrit] New patch to review for coreboot: device: i2c: Add support for I2C bus operations

Duncan Laurie (dlaurie@chromium.org) gerrit at coreboot.org
Wed Jun 8 03:21:01 CEST 2016


Duncan Laurie (dlaurie at chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15100

-gerrit

commit 69bf8ce5eca4e94561b453e136f53bedcac88c2c
Author: Duncan Laurie <dlaurie at chromium.org>
Date:   Tue Jun 7 15:38:14 2016 -0700

    device: i2c: Add support for I2C bus operations
    
    In order to support doing bus operations on an I2C device that is
    described in the devicetree there needs to be some linkage of the
    device and the existing opaque I2C controller bus number.
    
    This is provided in a similar fashion to the existing SMBUS operations
    but modified to fit within the existing I2C infrastructure.
    
    Variants of the existing I2C helper functions are provided that will
    obtain the bus number that corresponds to this device by looking for
    the SOC-provided I2C bus operation structure to provide a function
    that will make that translation.
    
    For example an SOC using a PCI I2C controller at 0:15.0 could use:
    
    soc/intel/.../i2c.c:
      static int i2c_dev_to_bus(struct device *dev)
      {
        if (dev->path.pci.devfn == PCI_DEVFN(0x15, 0))
          return 0;
        return -1;
      }
      static struct i2c_bus_operation i2c_bus_ops = {
        .dev_to_bus = &i2c_dev_to_bus
      }
      static struct device_operations i2c_dev_ops = {
        .ops_i2c_bus = &i2c_bus_ops
        ...
      }
    
    With an I2C device on that bus at address 0x1a described in the tree:
    
    devicetree.cb:
      device pci 15.0 on # I2C0
        chip drivers/i2c/sample
          device i2c 1a.0 on end
        end
      end
    
    That driver can then do I2C transactions with the device object
    without needing to know that the SOC-specific bus number that this
    I2C device lives on is "0".
    
    For example it could read a version value from register address 0
    with a byte transaction:
    
    drivers/i2c/sample/sample.c:
      static void i2c_sample_enable(struct device *dev)
      {
        uint8_t ver;
        if (!i2c_dev_readb(dev, 0x00, &ver))
          printk(BIOS_INFO, "I2C %s version 0x02x\n", dev_path(dev), ver);
      }
    
    Change-Id: I6c41c8e0d10caabe01cc41da96382074de40e91e
    Signed-off-by: Duncan Laurie <dlaurie at chromium.org>
---
 src/device/i2c.c            | 83 +++++++++++++++++++++++++++++++++++++++++++++
 src/include/device/device.h |  2 ++
 src/include/device/i2c.h    | 18 ++++++++++
 3 files changed, 103 insertions(+)

diff --git a/src/device/i2c.c b/src/device/i2c.c
index 53bfdf0..096311f 100644
--- a/src/device/i2c.c
+++ b/src/device/i2c.c
@@ -13,7 +13,90 @@
  * GNU General Public License for more details.
  */
 
+#include <device/device.h>
 #include <device/i2c.h>
+#include <stdint.h>
+
+#if ENV_RAMSTAGE
+/* Return I2C operations for a bus */
+static inline const struct i2c_bus_operations *ops_i2c_bus(struct bus *bus)
+{
+	if (bus && bus->dev && bus->dev->ops)
+		return bus->dev->ops->ops_i2c_bus;
+	return NULL;
+}
+
+int i2c_dev_find_bus(struct device *dev)
+{
+	const struct i2c_bus_operations *ops;
+	struct bus *pbus;
+
+	if (!dev)
+		return -1;
+
+	/* Locate parent bus with I2C controller ops */
+	pbus = dev->bus;
+	while (pbus && pbus->dev && !ops_i2c_bus(pbus))
+		if (pbus->dev->bus != pbus)
+			pbus = pbus->dev->bus;
+
+	/* Check if this I2C controller ops implements dev_to_bus() */
+	ops = ops_i2c_bus(pbus);
+	if (!ops || !ops->dev_to_bus)
+		return -1;
+
+	/* Use controller ops->get_bus() to determine the bus number */
+	return ops->dev_to_bus(pbus->dev);
+}
+
+int i2c_dev_transfer(struct device *dev, struct i2c_seg *segments, int count)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_transfer(bus, segments, count);
+}
+
+int i2c_dev_readb(struct device *dev, uint8_t reg, uint8_t *data)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_readb(bus, dev->path.i2c.device, reg, data);
+}
+
+int i2c_dev_writeb(struct device *dev, uint8_t reg, uint8_t data)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_writeb(bus, dev->path.i2c.device, reg, data);
+}
+
+int i2c_dev_read_bytes(struct device *dev, uint8_t reg, uint8_t *data, int len)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_read_bytes(bus, dev->path.i2c.device, reg, data, len);
+}
+
+int i2c_dev_read_raw(struct device *dev, uint8_t *data, int len)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_read_raw(bus, dev->path.i2c.device, data, len);
+}
+
+int i2c_dev_write_raw(struct device *dev, uint8_t *data, int len)
+{
+	int bus = i2c_dev_find_bus(dev);
+	if (bus < 0)
+		return -1;
+	return i2c_write_raw(bus, dev->path.i2c.device, data, len);
+}
+#endif
 
 int i2c_read_field(unsigned bus, uint8_t chip, uint8_t reg, uint8_t *data,
 		   uint8_t mask, uint8_t shift)
diff --git a/src/include/device/device.h b/src/include/device/device.h
index 00ff3d9..95fabf4 100644
--- a/src/include/device/device.h
+++ b/src/include/device/device.h
@@ -20,6 +20,7 @@ struct device;
 typedef struct device * device_t;
 struct pci_operations;
 struct pci_bus_operations;
+struct i2c_bus_operations;
 struct smbus_bus_operations;
 struct pnp_mode_ops;
 
@@ -62,6 +63,7 @@ struct device_operations {
 	const char *(*acpi_name)(device_t dev);
 #endif
 	const struct pci_operations *ops_pci;
+	const struct i2c_bus_operations *ops_i2c_bus;
 	const struct smbus_bus_operations *ops_smbus_bus;
 	const struct pci_bus_operations * (*ops_pci_bus)(device_t dev);
 	const struct pnp_mode_ops *ops_pnp_mode;
diff --git a/src/include/device/i2c.h b/src/include/device/i2c.h
index c8c7b22..39f2a32 100644
--- a/src/include/device/i2c.h
+++ b/src/include/device/i2c.h
@@ -168,4 +168,22 @@ static inline int i2c_writeb(unsigned bus, uint8_t chip, uint8_t reg,
 	return i2c_transfer(bus, &seg, 1);
 }
 
+/* I2C bus operation for ramstage drivers */
+struct device;
+struct i2c_bus_operations {
+	/* Find the bus number for this device */
+	int (*dev_to_bus)(struct device *dev);
+};
+
+/* Return I2C bus number for provided device, -1 if not found */
+int i2c_dev_find_bus(struct device *dev);
+
+/* Variants of I2C helper functions that take a device instead of bus number */
+int i2c_dev_transfer(struct device *dev, struct i2c_seg *segments, int count);
+int i2c_dev_readb(struct device *dev, uint8_t reg, uint8_t *data);
+int i2c_dev_writeb(struct device *dev, uint8_t reg, uint8_t data);
+int i2c_dev_read_bytes(struct device *dev, uint8_t reg, uint8_t *data, int len);
+int i2c_dev_read_raw(struct device *dev, uint8_t *data, int len);
+int i2c_dev_write_raw(struct device *dev, uint8_t *data, int len);
+
 #endif	/* _DEVICE_I2C_H_ */



More information about the coreboot-gerrit mailing list