This change enables the Linux driver to access i2c devices via
the Tilera hypervisor's virtualized API.
Note that the arch/tile/include/hv/ headers are "upstream" headers
that likely benefit less from LKML review.
Signed-off-by: Chris Metcalf <[email protected]>
---
arch/tile/include/hv/drv_i2cm_intf.h | 68 +++++++
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-tile.c | 324 ++++++++++++++++++++++++++++++++++
4 files changed, 403 insertions(+), 0 deletions(-)
create mode 100644 arch/tile/include/hv/drv_i2cm_intf.h
create mode 100644 drivers/i2c/busses/i2c-tile.c
diff --git a/arch/tile/include/hv/drv_i2cm_intf.h b/arch/tile/include/hv/drv_i2cm_intf.h
new file mode 100644
index 0000000..6584a72
--- /dev/null
+++ b/arch/tile/include/hv/drv_i2cm_intf.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ */
+
+/**
+ * @file drv_i2cm_intf.h
+ * Interface definitions for the I2CM driver.
+ *
+ * The I2C Master interface driver provides a manner for supervisors to
+ * access the I2C devices on the I2C bus.
+ *
+ * When the supervisor issues an I2C data transaction, it stores the i2c
+ * device slave address and the data offset within the device in the offset
+ * of the HV I2C device handle. The low half-word contains the slave address
+ * while the data offset is stored in byte 2 and 3. For the write access,
+ * the first 1 or 2 bytes of the write data contain the device data address
+ * if the data offset field of the HV device handle offset is 0; otherwise,
+ * the write data are puse data payload. For the read access, it is always
+ * preceded by a dummy write access which should contain an either 1-byte or
+ * 2-byte device data address while the read message holds no addressing
+ * information.
+ */
+
+#ifndef _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
+#define _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
+
+/** Maximum size of an HV I2C transfer. */
+#define HV_I2CM_CHUNK_SIZE 128
+
+/** Length of the i2c device name. */
+#define I2C_DEV_NAME_SIZE 20
+
+/** I2C device descriptor, to be exported to the client OS. */
+typedef struct
+{
+ char name[I2C_DEV_NAME_SIZE]; /**< Device name, e.g. "24c512". */
+ uint32_t addr; /**< I2C device slave address */
+}
+tile_i2c_desc_t;
+
+/** Get the number of i2c devices. Read-only, takes a 4-byte value. */
+#define I2C_GET_NUM_DEVS_OFF 0xF0000000
+
+/** Get i2c device info. Read-only, takes an array of tile_i2c_desc_t's. */
+#define I2C_GET_DEV_INFO_OFF 0xF0000004
+
+/** This structure is used to encode the I2C device slave address
+ * and the chip data offset and is passed to the HV in the offset
+ * of the i2cm HV device file.
+ */
+typedef struct
+{
+ uint32_t addr:8; /**< I2C device slave address */
+ uint32_t data_offset:24; /**< I2C device data offset */
+}
+tile_i2c_addr_desc_t;
+
+#endif /* _SYS_HV_INCLUDE_DRV_I2CM_INTF_H */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 3a6321c..5d8d8ab 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -607,6 +607,16 @@ config I2C_STU300
This driver can also be built as a module. If so, the module
will be called i2c-stu300.
+config I2C_TILE
+ tristate "Tilera I2C hypervisor interface"
+ depends on TILE
+ help
+ This supports the Tilera hypervisor interface for
+ communicating with I2C devices attached to the chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-tile.
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 84cb16a..0a739eb 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
+obj-$(CONFIG_I2C_TILE) += i2c-tile.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
diff --git a/drivers/i2c/busses/i2c-tile.c b/drivers/i2c/busses/i2c-tile.c
new file mode 100644
index 0000000..b4e8988
--- /dev/null
+++ b/drivers/i2c/busses/i2c-tile.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2010 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * Tilera-specific I2C driver.
+ *
+ * This source code is derived from the following driver:
+ *
+ * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ * Copyright (C) 2004 Rick Bronson
+ * Converted to 2.6 by Andrew Victor <[email protected]>
+ *
+ * Borrowed heavily from original work by:
+ * Copyright (C) 2000 Philip Edelbrock <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <hv/hypervisor.h>
+#include <hv/drv_i2cm_intf.h>
+
+#define DRV_NAME "i2c-tile"
+
+static char i2cm_device[16] = "i2cm/0";
+
+/* Handle for hypervisor device. */
+static int i2cm_hv_devhdl;
+
+/*
+ * The I2C platform device.
+ */
+static struct platform_device *i2c_platform_device;
+
+static int xfer_msg(struct i2c_adapter *adap, struct i2c_msg *pmsg)
+{
+ int retval = 0;
+ int data_offset = 0;
+ int addr = pmsg->addr;
+ int length = pmsg->len;
+ char *buf = pmsg->buf;
+
+ /* HV uses 8-bit slave addresses. */
+ addr <<= 1;
+
+ while (length) {
+ int hv_retval;
+ tile_i2c_addr_desc_t hv_offset = {
+ .addr = addr,
+ .data_offset = data_offset,
+ };
+
+ int bytes_this_pass = length;
+ if (bytes_this_pass > HV_I2CM_CHUNK_SIZE)
+ bytes_this_pass = HV_I2CM_CHUNK_SIZE;
+
+ if (pmsg->flags & I2C_M_RD)
+ hv_retval = hv_dev_pread(i2cm_hv_devhdl, 0,
+ (HV_VirtAddr) buf,
+ bytes_this_pass,
+ *(int *) &hv_offset);
+ else
+ hv_retval = hv_dev_pwrite(i2cm_hv_devhdl, 0,
+ (HV_VirtAddr) buf,
+ bytes_this_pass,
+ *(int *) &hv_offset);
+ if (hv_retval < 0) {
+ pr_err(DRV_NAME ": %s failed, error %d\n",
+ (pmsg->flags & I2C_M_RD) ? "hv_dev_pread" :
+ "hv_dev_pwrite", hv_retval);
+ if (hv_retval == HV_ENODEV)
+ retval = -ENODEV;
+ else
+ retval = -EIO;
+ break;
+ }
+
+ buf += hv_retval;
+ data_offset += hv_retval;
+ length -= hv_retval;
+ }
+
+ return retval;
+}
+
+/*
+ * Generic I2C master transfer routine.
+ */
+static int tile_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+ int num)
+{
+ int ret, i;
+
+ /* We don't support ten bit chip address. */
+ if (pmsg->flags & I2C_M_TEN)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ if (pmsg->len && pmsg->buf) {
+
+ ret = xfer_msg(adap, pmsg);
+ if (ret)
+ return ret;
+
+ pmsg++;
+ } else
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+/*
+ * Return list of supported functionality.
+ */
+static u32 tile_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm tile_i2c_algorithm = {
+ .master_xfer = tile_i2c_xfer,
+ .functionality = tile_i2c_functionality,
+};
+
+/*
+ * This routine is called to register all I2C devices that are connected to
+ * the I2C bus. This should be done at arch_initcall time, before declaring
+ * the I2C adapter. This function does the following:
+ *
+ * 1. Retrieve the I2C device list from the HV which selectively grants the
+ * access permission of individual I2C devices, and build an array of struct
+ * i2c_board_info.
+ * 2. Statically declare these I2C devices by calling
+ * i2c_register_board_info().
+ */
+static int __init tile_i2c_dev_init(void)
+{
+ int ret;
+ int i;
+ int i2c_devs = 0;
+ struct i2c_board_info *tile_i2c_devices;
+ int i2c_desc_size;
+ tile_i2c_desc_t *tile_i2c_desc;
+
+ /* Open the HV i2cm device. */
+ i2cm_hv_devhdl = hv_dev_open((HV_VirtAddr)i2cm_device, 0);
+ if (i2cm_hv_devhdl < 0) {
+ switch (i2cm_hv_devhdl) {
+ case HV_ENODEV:
+ return -ENODEV;
+ default:
+ return (ssize_t)i2cm_hv_devhdl;
+ }
+ }
+
+ ret = hv_dev_pread(i2cm_hv_devhdl, 0, (HV_VirtAddr)&i2c_devs,
+ sizeof(i2c_devs), I2C_GET_NUM_DEVS_OFF);
+ if (ret <= 0) {
+ pr_err(DRV_NAME ": hv_dev_pread(I2C_GET_NUM_DEVS_OFF)"
+ " failed, error %d\n", ret);
+ return -EIO;
+ }
+
+ if (i2c_devs == 0)
+ return 0;
+
+ pr_info(DRV_NAME ": detected %d I2C devices.\n", i2c_devs);
+
+ i2c_desc_size = i2c_devs * sizeof(tile_i2c_desc_t);
+ tile_i2c_desc = kzalloc(i2c_desc_size, GFP_KERNEL);
+ if (!tile_i2c_desc)
+ return -ENOMEM;
+
+ ret = hv_dev_pread(i2cm_hv_devhdl, 0, (HV_VirtAddr)tile_i2c_desc,
+ i2c_desc_size, I2C_GET_DEV_INFO_OFF);
+ if (ret <= 0) {
+ pr_err(DRV_NAME ": hv_dev_pread(I2C_GET_DEV_INFO_OFF)"
+ " failed, error %d\n", ret);
+ return -EIO;
+ }
+
+ i2c_desc_size = i2c_devs * sizeof(struct i2c_board_info);
+ tile_i2c_devices = kzalloc(i2c_desc_size, GFP_KERNEL);
+ if (!tile_i2c_devices)
+ return -ENOMEM;
+
+ for (i = 0; i < i2c_devs; i++) {
+ strncpy(tile_i2c_devices[i].type, tile_i2c_desc[i].name,
+ I2C_NAME_SIZE);
+ /* HV uses 8-bit slave addresses, convert to 7bit for Linux. */
+ tile_i2c_devices[i].addr = tile_i2c_desc[i].addr >> 1;
+ }
+
+ ret = i2c_register_board_info(0, tile_i2c_devices, i2c_devs);
+
+ kfree(tile_i2c_desc);
+ kfree(tile_i2c_devices);
+
+ return ret;
+}
+arch_initcall(tile_i2c_dev_init);
+
+/*
+ * I2C adapter probe routine which registers the I2C adapter with the I2C core.
+ */
+static int __devinit tile_i2c_probe(struct platform_device *dev)
+{
+ struct i2c_adapter *adapter;
+ int ret;
+
+ adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (adapter == NULL) {
+ ret = -ENOMEM;
+ goto malloc_err;
+ }
+
+ adapter->owner = THIS_MODULE;
+
+ /*
+ * If "dev->id" is negative we consider it as zero.
+ * The reason to do so is to avoid sysfs names that only make
+ * sense when there are multiple adapters.
+ */
+ adapter->nr = dev->id != -1 ? dev->id : 0;
+ snprintf(adapter->name, sizeof(adapter->name), "tile_i2c-i2c.%u",
+ adapter->nr);
+
+ adapter->algo = &tile_i2c_algorithm;
+ adapter->class = I2C_CLASS_HWMON;
+ adapter->dev.parent = &dev->dev;
+
+ ret = i2c_add_numbered_adapter(adapter);
+ if (ret < 0) {
+ pr_info(DRV_NAME ": %s registration failed\n",
+ adapter->name);
+ goto add_adapter_err;
+ }
+
+ platform_set_drvdata(dev, adapter);
+
+ return 0;
+
+add_adapter_err:
+ kfree(adapter);
+malloc_err:
+ return ret;
+}
+
+/*
+ * I2C adapter cleanup routine.
+ */
+static int __devexit tile_i2c_remove(struct platform_device *dev)
+{
+ struct i2c_adapter *adapter = platform_get_drvdata(dev);
+ int rc;
+
+ rc = i2c_del_adapter(adapter);
+ platform_set_drvdata(dev, NULL);
+
+ kfree(adapter);
+
+ return rc;
+}
+
+static struct platform_driver tile_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tile_i2c_probe,
+ .remove = __devexit_p(tile_i2c_remove),
+};
+
+/*
+ * Driver init routine.
+ */
+static int __init tile_i2c_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&tile_i2c_driver);
+ if (err)
+ return err;
+
+ i2c_platform_device =
+ platform_device_register_simple(DRV_NAME, -1, NULL, 0);
+ if (IS_ERR(i2c_platform_device)) {
+ err = PTR_ERR(i2c_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&tile_i2c_driver);
+ return err;
+}
+
+/*
+ * Driver cleanup routine.
+ */
+static void __exit tile_i2c_exit(void)
+{
+ platform_driver_unregister(&tile_i2c_driver);
+}
+
+module_init(tile_i2c_init);
+module_exit(tile_i2c_exit);
--
1.6.5.2
On Tue, 2 Nov 2010 13:05:34 -0400 Chris Metcalf wrote:
> This change enables the Linux driver to access i2c devices via
> the Tilera hypervisor's virtualized API.
>
> Note that the arch/tile/include/hv/ headers are "upstream" headers
> that likely benefit less from LKML review.
>
> Signed-off-by: Chris Metcalf <[email protected]>
> ---
> arch/tile/include/hv/drv_i2cm_intf.h | 68 +++++++
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-tile.c | 324 ++++++++++++++++++++++++++++++++++
> 4 files changed, 403 insertions(+), 0 deletions(-)
> create mode 100644 arch/tile/include/hv/drv_i2cm_intf.h
> create mode 100644 drivers/i2c/busses/i2c-tile.c
>
> diff --git a/arch/tile/include/hv/drv_i2cm_intf.h b/arch/tile/include/hv/drv_i2cm_intf.h
> new file mode 100644
> index 0000000..6584a72
> --- /dev/null
> +++ b/arch/tile/include/hv/drv_i2cm_intf.h
> @@ -0,0 +1,68 @@
> +/*
> + * Copyright 2010 Tilera Corporation. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation, version 2.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for
> + * more details.
> + */
> +
> +/**
In kernel source code, "/**" is used to begin kernel-doc notation blocks.
Please don't use it for non-kernel-doc comments.
(multiple times...)
> + * @file drv_i2cm_intf.h
> + * Interface definitions for the I2CM driver.
> + *
> + * The I2C Master interface driver provides a manner for supervisors to
> + * access the I2C devices on the I2C bus.
> + *
> + * When the supervisor issues an I2C data transaction, it stores the i2c
> + * device slave address and the data offset within the device in the offset
> + * of the HV I2C device handle. The low half-word contains the slave address
> + * while the data offset is stored in byte 2 and 3. For the write access,
> + * the first 1 or 2 bytes of the write data contain the device data address
> + * if the data offset field of the HV device handle offset is 0; otherwise,
> + * the write data are puse data payload. For the read access, it is always
> + * preceded by a dummy write access which should contain an either 1-byte or
> + * 2-byte device data address while the read message holds no addressing
> + * information.
> + */
> +
> +#ifndef _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
> +#define _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
> +
> +/** Maximum size of an HV I2C transfer. */
> +#define HV_I2CM_CHUNK_SIZE 128
> +
> +/** Length of the i2c device name. */
> +#define I2C_DEV_NAME_SIZE 20
> +
> +/** I2C device descriptor, to be exported to the client OS. */
> +typedef struct
> +{
> + char name[I2C_DEV_NAME_SIZE]; /**< Device name, e.g. "24c512". */
> + uint32_t addr; /**< I2C device slave address */
> +}
> +tile_i2c_desc_t;
+ } tile_i2c_desc_t;
(i.e., join those 2 lines)
> +
> +/** Get the number of i2c devices. Read-only, takes a 4-byte value. */
> +#define I2C_GET_NUM_DEVS_OFF 0xF0000000
> +
> +/** Get i2c device info. Read-only, takes an array of tile_i2c_desc_t's. */
> +#define I2C_GET_DEV_INFO_OFF 0xF0000004
> +
> +/** This structure is used to encode the I2C device slave address
> + * and the chip data offset and is passed to the HV in the offset
> + * of the i2cm HV device file.
> + */
> +typedef struct
> +{
> + uint32_t addr:8; /**< I2C device slave address */
> + uint32_t data_offset:24; /**< I2C device data offset */
> +}
> +tile_i2c_addr_desc_t;
ditto. (assuming that typedefs are even accepted)
> +
> +#endif /* _SYS_HV_INCLUDE_DRV_I2CM_INTF_H */
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
On Tue, Nov 02, 2010 at 01:05:34PM -0400, Chris Metcalf wrote:
> This change enables the Linux driver to access i2c devices via
> the Tilera hypervisor's virtualized API.
>
> Note that the arch/tile/include/hv/ headers are "upstream" headers
> that likely benefit less from LKML review.
>
> Signed-off-by: Chris Metcalf <[email protected]>
> ---
> arch/tile/include/hv/drv_i2cm_intf.h | 68 +++++++
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-tile.c | 324 ++++++++++++++++++++++++++++++++++
> 4 files changed, 403 insertions(+), 0 deletions(-)
> create mode 100644 arch/tile/include/hv/drv_i2cm_intf.h
> create mode 100644 drivers/i2c/busses/i2c-tile.c
>
> diff --git a/arch/tile/include/hv/drv_i2cm_intf.h b/arch/tile/include/hv/drv_i2cm_intf.h
> new file mode 100644
> index 0000000..6584a72
> --- /dev/null
> +++ b/arch/tile/include/hv/drv_i2cm_intf.h
> @@ -0,0 +1,68 @@
> +/*
> + * Copyright 2010 Tilera Corporation. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation, version 2.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for
> + * more details.
> + */
> +
> +/**
> + * @file drv_i2cm_intf.h
> + * Interface definitions for the I2CM driver.
> + *
> + * The I2C Master interface driver provides a manner for supervisors to
> + * access the I2C devices on the I2C bus.
> + *
> + * When the supervisor issues an I2C data transaction, it stores the i2c
> + * device slave address and the data offset within the device in the offset
> + * of the HV I2C device handle. The low half-word contains the slave address
> + * while the data offset is stored in byte 2 and 3. For the write access,
> + * the first 1 or 2 bytes of the write data contain the device data address
> + * if the data offset field of the HV device handle offset is 0; otherwise,
> + * the write data are puse data payload. For the read access, it is always
> + * preceded by a dummy write access which should contain an either 1-byte or
> + * 2-byte device data address while the read message holds no addressing
> + * information.
> + */
> +
> +#ifndef _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
> +#define _SYS_HV_INCLUDE_DRV_I2CM_INTF_H
> +
> +/** Maximum size of an HV I2C transfer. */
> +#define HV_I2CM_CHUNK_SIZE 128
> +
> +/** Length of the i2c device name. */
> +#define I2C_DEV_NAME_SIZE 20
> +
> +/** I2C device descriptor, to be exported to the client OS. */
> +typedef struct
> +{
> + char name[I2C_DEV_NAME_SIZE]; /**< Device name, e.g. "24c512". */
> + uint32_t addr; /**< I2C device slave address */
> +}
> +tile_i2c_desc_t;
I'd rather you'd not typedef these things and just used named structures.
> +#define I2C_GET_DEV_INFO_OFF 0xF0000004
> +
> +/** This structure is used to encode the I2C device slave address
> + * and the chip data offset and is passed to the HV in the offset
> + * of the i2cm HV device file.
> + */
> +typedef struct
> +{
> + uint32_t addr:8; /**< I2C device slave address */
> + uint32_t data_offset:24; /**< I2C device data offset */
> +}
> +tile_i2c_addr_desc_t;
You'll be better off just having this as a uint32 and assembling it
in code, gcc isn't guaranteed to pack these as you'd think it should.
Go for something like
ADDR_DESC(addr, data) (((data) << 8) | (addr))
> +#endif /* _SYS_HV_INCLUDE_DRV_I2CM_INTF_H */
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 3a6321c..5d8d8ab 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -607,6 +607,16 @@ config I2C_STU300
> This driver can also be built as a module. If so, the module
> will be called i2c-stu300.
>
> +config I2C_TILE
> + tristate "Tilera I2C hypervisor interface"
> + depends on TILE
> + help
> + This supports the Tilera hypervisor interface for
> + communicating with I2C devices attached to the chip.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-tile.
> +
> config I2C_VERSATILE
> tristate "ARM Versatile/Realview I2C bus support"
> depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 84cb16a..0a739eb 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -58,6 +58,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
> obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
> obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
> obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
> +obj-$(CONFIG_I2C_TILE) += i2c-tile.o
> obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> diff --git a/drivers/i2c/busses/i2c-tile.c b/drivers/i2c/busses/i2c-tile.c
> new file mode 100644
> index 0000000..b4e8988
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-tile.c
> @@ -0,0 +1,324 @@
> +/*
> + * Copyright 2010 Tilera Corporation. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation, version 2.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for
> + * more details.
> + *
> + * Tilera-specific I2C driver.
> + *
> + * This source code is derived from the following driver:
> + *
> + * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
> + *
> + * Copyright (C) 2004 Rick Bronson
> + * Converted to 2.6 by Andrew Victor <[email protected]>
> + *
> + * Borrowed heavily from original work by:
> + * Copyright (C) 2000 Philip Edelbrock <[email protected]>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/uaccess.h>
> +#include <hv/hypervisor.h>
> +#include <hv/drv_i2cm_intf.h>
> +
> +#define DRV_NAME "i2c-tile"
> +
> +static char i2cm_device[16] = "i2cm/0";
> +
> +/* Handle for hypervisor device. */
> +static int i2cm_hv_devhdl;
> +
> +/*
> + * The I2C platform device.
> + */
> +static struct platform_device *i2c_platform_device;
> +
> +static int xfer_msg(struct i2c_adapter *adap, struct i2c_msg *pmsg)
> +{
> + int retval = 0;
> + int data_offset = 0;
> + int addr = pmsg->addr;
> + int length = pmsg->len;
> + char *buf = pmsg->buf;
> +
> + /* HV uses 8-bit slave addresses. */
> + addr <<= 1;
> +
> + while (length) {
> + int hv_retval;
> + tile_i2c_addr_desc_t hv_offset = {
> + .addr = addr,
> + .data_offset = data_offset,
> + }
hmm, you could have init-ed the addr outside of the while loop
and just set the data-offset each time around the loop.
Also, see notes on just making this a single unsigned int.
could you could even ignore the offset and just increment the buffer
pointer?
> +
> + int bytes_this_pass = length;
> + if (bytes_this_pass > HV_I2CM_CHUNK_SIZE)
> + bytes_this_pass = HV_I2CM_CHUNK_SIZE;
> +
> + if (pmsg->flags & I2C_M_RD)
> + hv_retval = hv_dev_pread(i2cm_hv_devhdl, 0,
> + (HV_VirtAddr) buf,
> + bytes_this_pass,
> + *(int *) &hv_offset);
please, just create hv_offset in a uint32 and just pass it in.
> + else
> + hv_retval = hv_dev_pwrite(i2cm_hv_devhdl, 0,
> + (HV_VirtAddr) buf,
> + bytes_this_pass,
> + *(int *) &hv_offset);
really, is HV_VirtAddr not compatible with 'void *'?
> + if (hv_retval < 0) {
> + pr_err(DRV_NAME ": %s failed, error %d\n",
> + (pmsg->flags & I2C_M_RD) ? "hv_dev_pread" :
> + "hv_dev_pwrite", hv_retval);
> + if (hv_retval == HV_ENODEV)
> + retval = -ENODEV;
> + else
> + retval = -EIO;
> + break;
see (a) comments about retval conversion, and (b), can you really lose a
device in the middle of a transfer?
note, what happens if you run i2c-detect, do you get lots of console output?
> + }
> +
> + buf += hv_retval;
> + data_offset += hv_retval;
> + length -= hv_retval;
> + }
> +
> + return retval;
> +}
> +
> +/*
> + * Generic I2C master transfer routine.
> + */
> +static int tile_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
> + int num)
> +{
> + int ret, i;
> +
> + /* We don't support ten bit chip address. */
> + if (pmsg->flags & I2C_M_TEN)
> + return -EINVAL;
check this for each message.
> + for (i = 0; i < num; i++) {
> + if (pmsg->len && pmsg->buf) {
pmsg->len == 0 would still mean sending an address to the other end and
getting an ack from it.
> + ret = xfer_msg(adap, pmsg);
> + if (ret)
> + return ret;
> +
> + pmsg++;
> + } else
> + return -EINVAL;
> + }
> +
> + return i;
> +}
> +/*
> + * Return list of supported functionality.
> + */
> +static u32 tile_i2c_functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C;
> +}
> +
> +static const struct i2c_algorithm tile_i2c_algorithm = {
> + .master_xfer = tile_i2c_xfer,
> + .functionality = tile_i2c_functionality,
> +};
> +
> +/*
> + * This routine is called to register all I2C devices that are connected to
> + * the I2C bus. This should be done at arch_initcall time, before declaring
> + * the I2C adapter. This function does the following:
> + *
> + * 1. Retrieve the I2C device list from the HV which selectively grants the
> + * access permission of individual I2C devices, and build an array of struct
> + * i2c_board_info.
> + * 2. Statically declare these I2C devices by calling
> + * i2c_register_board_info().
> + */
> +static int __init tile_i2c_dev_init(void)
> +{
> + int ret;
> + int i;
> + int i2c_devs = 0;
> + struct i2c_board_info *tile_i2c_devices;
> + int i2c_desc_size;
> + tile_i2c_desc_t *tile_i2c_desc;
i'd like to see this with the larger items ordered towards the top of the
function, but that's just my preference.
> + /* Open the HV i2cm device. */
> + i2cm_hv_devhdl = hv_dev_open((HV_VirtAddr)i2cm_device, 0);
yeurk, why not either have it sa a HV_VirtAddr in the first place or
make the api take a 'char *' instead.
> + if (i2cm_hv_devhdl < 0) {
> + switch (i2cm_hv_devhdl) {
> + case HV_ENODEV:
> + return -ENODEV;
returning -ENODEV means there's no device here, not that something went
wrong when we knew a device was meant to be here. You'll lose the error
in the upper layer.
> + default:
> + return (ssize_t)i2cm_hv_devhdl;
not an ssize_t return.
maybe the hv should either just return a proper error or have a conversion
function.
> + }
> + }
> +
> + ret = hv_dev_pread(i2cm_hv_devhdl, 0, (HV_VirtAddr)&i2c_devs,
> + sizeof(i2c_devs), I2C_GET_NUM_DEVS_OFF);
not liking this hv_dev api at-all. do we really need to keep doing these
cats to HV_VirtAddr around here? it could end up masking problems later on.
> + if (ret <= 0) {
> + pr_err(DRV_NAME ": hv_dev_pread(I2C_GET_NUM_DEVS_OFF)"
> + " failed, error %d\n", ret);
> + return -EIO;
> + }
> +
> + if (i2c_devs == 0)
> + return 0;
> +
> + pr_info(DRV_NAME ": detected %d I2C devices.\n", i2c_devs);
> +
> + i2c_desc_size = i2c_devs * sizeof(tile_i2c_desc_t);
> + tile_i2c_desc = kzalloc(i2c_desc_size, GFP_KERNEL);
> + if (!tile_i2c_desc)
> + return -ENOMEM;
no error print here?
> + ret = hv_dev_pread(i2cm_hv_devhdl, 0, (HV_VirtAddr)tile_i2c_desc,
> + i2c_desc_size, I2C_GET_DEV_INFO_OFF);
> + if (ret <= 0) {
> + pr_err(DRV_NAME ": hv_dev_pread(I2C_GET_DEV_INFO_OFF)"
> + " failed, error %d\n", ret);
> + return -EIO;
> + }
> +
> + i2c_desc_size = i2c_devs * sizeof(struct i2c_board_info);
> + tile_i2c_devices = kzalloc(i2c_desc_size, GFP_KERNEL);
> + if (!tile_i2c_devices)
> + return -ENOMEM;
> +
> + for (i = 0; i < i2c_devs; i++) {
> + strncpy(tile_i2c_devices[i].type, tile_i2c_desc[i].name,
> + I2C_NAME_SIZE);
> + /* HV uses 8-bit slave addresses, convert to 7bit for Linux. */
> + tile_i2c_devices[i].addr = tile_i2c_desc[i].addr >> 1;
> + }
> +
> + ret = i2c_register_board_info(0, tile_i2c_devices, i2c_devs);
are you sure you're registered as bus 0? what happens if there's >1 bus?
> + kfree(tile_i2c_desc);
> + kfree(tile_i2c_devices);
why not do this stuff in the platform device probe in case you're handling
multiple instances?
> +
> + return ret;
> +}
> +arch_initcall(tile_i2c_dev_init);
> +
> +/*
> + * I2C adapter probe routine which registers the I2C adapter with the I2C core.
> + */
> +static int __devinit tile_i2c_probe(struct platform_device *dev)
> +{
> + struct i2c_adapter *adapter;
> + int ret;
> +
> + adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
> + if (adapter == NULL) {
> + ret = -ENOMEM;
> + goto malloc_err;
no error print?
> + }
> +
> + adapter->owner = THIS_MODULE;
> +
> + /*
> + * If "dev->id" is negative we consider it as zero.
> + * The reason to do so is to avoid sysfs names that only make
> + * sense when there are multiple adapters.
> + */
> + adapter->nr = dev->id != -1 ? dev->id : 0;
> + snprintf(adapter->name, sizeof(adapter->name), "tile_i2c-i2c.%u",
> + adapter->nr);
you could just use the dev_name() of the platform device here.
> + adapter->algo = &tile_i2c_algorithm;
> + adapter->class = I2C_CLASS_HWMON;
> + adapter->dev.parent = &dev->dev;
> +
> + ret = i2c_add_numbered_adapter(adapter);
> + if (ret < 0) {
> + pr_info(DRV_NAME ": %s registration failed\n",
> + adapter->name);
> + goto add_adapter_err;
dev_err() would have been better.
> + }
> +
> + platform_set_drvdata(dev, adapter);
> +
> + return 0;
> +
> +add_adapter_err:
> + kfree(adapter);
> +malloc_err:
> + return ret;
> +}
> +
> +/*
> + * I2C adapter cleanup routine.
> + */
> +static int __devexit tile_i2c_remove(struct platform_device *dev)
> +{
> + struct i2c_adapter *adapter = platform_get_drvdata(dev);
> + int rc;
> +
> + rc = i2c_del_adapter(adapter);
> + platform_set_drvdata(dev, NULL);
> +
> + kfree(adapter);
> +
> + return rc;
> +}
> +
> +static struct platform_driver tile_i2c_driver = {
> + .driver = {
> + .name = DRV_NAME,
> + .owner = THIS_MODULE,
> + },
> + .probe = tile_i2c_probe,
> + .remove = __devexit_p(tile_i2c_remove),
> +};
> +
> +/*
> + * Driver init routine.
> + */
> +static int __init tile_i2c_init(void)
> +{
> + int err;
> +
> + err = platform_driver_register(&tile_i2c_driver);
> + if (err)
> + return err;
> +
> + i2c_platform_device =
> + platform_device_register_simple(DRV_NAME, -1, NULL, 0);
> + if (IS_ERR(i2c_platform_device)) {
> + err = PTR_ERR(i2c_platform_device);
> + goto unreg_platform_driver;
you probably wanted to print an error here.
ps, why keep it around if you don't then use it again? why not have the
dev as a local pointer.
> + }
> +
> + return 0;
> +
> +unreg_platform_driver:
> + platform_driver_unregister(&tile_i2c_driver);
> + return err;
> +}
> +
> +/*
> + * Driver cleanup routine.
> + */
> +static void __exit tile_i2c_exit(void)
> +{
> + platform_driver_unregister(&tile_i2c_driver);
> +}
> +
> +module_init(tile_i2c_init);
> +module_exit(tile_i2c_exit);
> --
> 1.6.5.2
>
--
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
On 11/2/2010 1:19 PM, Randy Dunlap wrote:
> On Tue, 2 Nov 2010 13:05:34 -0400 Chris Metcalf wrote:
>> This change enables the Linux driver to access i2c devices via
>> the Tilera hypervisor's virtualized API.
>>
>> Note that the arch/tile/include/hv/ headers are "upstream" headers
>> that likely benefit less from LKML review.
>>
>> [...]
>> +++ b/arch/tile/include/hv/drv_i2cm_intf.h
>> [...]
>> +/**
> In kernel source code, "/**" is used to begin kernel-doc notation blocks.
> Please don't use it for non-kernel-doc comments.
>
> (multiple times...)
These headers are essentially "upstream" for the Linux port; they are owned
by the Tilera hypervisor. The "/**" comments in those files are Doxygen
comments used to generate standalone API documentation for the hypervisor.
The same answer applies to your comment later in the file where you pointed
out some brace-positioning issues and the use of typedefs, since the coding
style for those headers is different the Linux coding style. (Note also
that these headers are using two-space indents rather than tab indents.)
To avoid stylistic clash, we could have released the hypervisor headers as
a separate tarball (somewhere) and required that the kernel build take an
environment variable or some such to locate them, but it seemed simpler to
incorporate them into the Tilera Linux code so that the kernel can build in
a reasonably self-contained way. The hypervisor header model is that the
client can use any internally consistent set of hypervisor headers and just
register the version number associated with that set of headers when
starting up.
--
Chris Metcalf, Tilera Corp.
http://www.tilera.com
On 11/2/2010 1:36 PM, Ben Dooks wrote:
> On Tue, Nov 02, 2010 at 01:05:34PM -0400, Chris Metcalf wrote:
>> This change enables the Linux driver to access i2c devices via
>> the Tilera hypervisor's virtualized API.
Ben, thanks for your detailed review of the code!
>> Note that the arch/tile/include/hv/ headers are "upstream" headers
>> that likely benefit less from LKML review.
As you may have seen in my reply to Randy, the "hv/" headers are from our
"upstream", so while I'm happy to improve on them in some ways, I'm
unlikely to make wholesale stylistic changes. See the other files in
arch/tile/include/hv/ for the stylistic similarity of the new
drv_i2cm_intf.h header. But let me reply to your specific concerns.
>> +typedef struct
>> +{
>> + uint32_t addr:8; /**< I2C device slave address */
>> + uint32_t data_offset:24; /**< I2C device data offset */
>> +}
>> +tile_i2c_addr_desc_t;
> You'll be better off just having this as a uint32 and assembling it
> in code, gcc isn't guaranteed to pack these as you'd think it should.
>
> Go for something like
>
> ADDR_DESC(addr, data) (((data) << 8) | (addr))
We have a lot of use of bitfields in our low-level architecture headers
(that describe things like SPR bitfields, I/O memory accesses for TILE-Gx,
etc.), so it's actually a good fit stylistically for the hypervisor code,
which is close to the hardware. Since we wrote the tile gcc backend, and
will likely be maintaining it for a good long while to come, we are pretty
confident that gcc will pack things the way we intend. (And the i2c-tile
code isn't relevant to any other architecture than tile, of course.)
That said... that cast is pretty heinous. We have another idiom that we
use frequently in the hypervisor layer, and should use here as well:
union
{
struct
{
uint32_t addr:8; /**< I2C device slave address */
uint32_t data_offset:24; /**< I2C device data offset */
};
uint32_t word; /**< To pass to the hypervisor */
}
This allows you to manipulate the fields as we do now, and then just pass
"hv_offset.word" to the hypervisor.
> could you could even ignore the offset and just increment the buffer
> pointer?
Is that guaranteed to work in all cases? I would think you'd want the
hv_offset offset to match the buffer offset.
> really, is HV_VirtAddr not compatible with 'void *'?
We've had conversations about this internally (and even have a bugzilla
filed internally). The type is "uint32_t" for TILEPro ("uint64_t" for
TILE-Gx). The original concern was that we wanted to be able to use types
like this not just natively, but also when doing various kind of
cross-compilation work. In particular, if hardware structures hold virtual
addresses, we don't want to use "void *" when it may be a 32-bit tile chip
being simulated by 64-bit simulator userspace, etc. In addition, we use
this type in the hypervisor, where it connotes a "client" (i.e. Linux)
address that shouldn't be directly dereferenced, much like Linux "__user".
However, I've argued that function declarations for hypervisor calls as
seen by Linux might as well be "void *", and we just haven't yet made time
to do this. When we do, we'll come through and bomb these uses away.
>> + if (hv_retval < 0) {
>> + pr_err(DRV_NAME ": %s failed, error %d\n",
>> + (pmsg->flags & I2C_M_RD) ? "hv_dev_pread" :
>> + "hv_dev_pwrite", hv_retval);
>> + if (hv_retval == HV_ENODEV)
>> + retval = -ENODEV;
>> + else
>> + retval = -EIO;
>> + break;
> see (a) comments about retval conversion
Which comment was that? I don't see it in this email.
> and (b), can you really lose a device in the middle of a transfer?
Probably not. I think the intent was just to capture all possible return
values from the hypervisor.
> note, what happens if you run i2c-detect, do you get lots of console output?
I'll try that. Obviously it would be annoying if we did. :-)
>> + for (i = 0; i < num; i++) {
>> + if (pmsg->len && pmsg->buf) {
> pmsg->len == 0 would still mean sending an address to the other end and
> getting an ack from it.
Do you mean we should force a zero-length read or write operation in this case?
>> + /* Open the HV i2cm device. */
>> + i2cm_hv_devhdl = hv_dev_open((HV_VirtAddr)i2cm_device, 0);
> yeurk, why not either have it sa a HV_VirtAddr in the first place or
> make the api take a 'char *' instead.
See above. (The actual i2cm_device is a char array, so it can't be
anything but a pointer type here.)
>> + if (i2cm_hv_devhdl < 0) {
>> + switch (i2cm_hv_devhdl) {
>> + case HV_ENODEV:
>> + return -ENODEV;
> returning -ENODEV means there's no device here, not that something went
> wrong when we knew a device was meant to be here. You'll lose the error
> in the upper layer.
The failure mode here is not that an I2C device is missing, but that the
hypervisor is not configured with support for its i2cm driver. In fact
HV_ENODEV ("no i2cm driver configured in hypervisor") merits a printk()
here, and in practice no other error will be returned. I'll fix this area
of code, and just return -EINVAL if things aren't good, which is more normal.
>> + tile_i2c_desc = kzalloc(i2c_desc_size, GFP_KERNEL);
>> + if (!tile_i2c_desc)
>> + return -ENOMEM;
> no error print here?
If kmalloc() and friends return NULL, you'll get a kernel message about it
anyway (unless you set GFP_NOWARN). The slab allocator will always try to
get memory from the page allocator, which will report "page allocation
failure" and dump a backtrace.
> + ret = i2c_register_board_info(0, tile_i2c_devices, i2c_devs);
> are you sure you're registered as bus 0? what happens if there's >1 bus?
I think our hardware doesn't support that, but I'll check.
>> + adapter->nr = dev->id != -1 ? dev->id : 0;
>> + snprintf(adapter->name, sizeof(adapter->name), "tile_i2c-i2c.%u",
>> + adapter->nr);
> you could just use the dev_name() of the platform device here.
Just to be clear, you mean something like this?
snprintf(adapter->name, sizeof(adapter->name), "%s.%u",
dev_name(&dev->dev), adapter->nr);
Thanks again. If I didn't reply to one of your comments, I just made the
change as suggested.
--
Chris Metcalf, Tilera Corp.
http://www.tilera.com