From 77701a8bab89fb0f87271674e57b2ecf636fbc5f Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 4 Apr 2012 10:34:38 +0200 Subject: i2c-pnx.c: Use resources in platforms As a precondition for device tree conversion, the platforms using i2c-pnx.c are converted to using mem and irq resources instead of platform data. Signed-off-by: Roland Stigge Reviewed-by: Arnd Bergmann Signed-off-by: Wolfram Sang --- include/linux/i2c-pnx.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index a87124d4d533..6e8efb7afd7c 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -29,14 +29,9 @@ struct i2c_pnx_algo_data { struct i2c_pnx_mif mif; int last; struct clk *clk; - struct i2c_pnx_data *i2c_pnx; struct i2c_adapter adapter; -}; - -struct i2c_pnx_data { - const char *name; - u32 base; - int irq; + phys_addr_t base; + int irq; }; #endif /* __I2C_PNX_H__ */ -- cgit v1.2.3 From a092de11bb4a96ac43ede0352e09bdf7e06280e8 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Fri, 20 Apr 2012 15:34:09 +0200 Subject: i2c: pnx: add device tree support This patch adds device tree support to the pnx-i2c driver by using platform resources for memory region and irq and removing dependency on mach includes. The following platforms are affected: * PNX * LPC31xx (WIP) * LPC32xx The patch is based on a patch by Jon Smirl, working on lpc31xx integration Signed-off-by: Roland Stigge Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/pnx.txt | 36 +++++++++++++++ drivers/i2c/busses/i2c-pnx.c | 63 ++++++++++++++++++++------- include/linux/i2c-pnx.h | 1 + 3 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/pnx.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/i2c/pnx.txt b/Documentation/devicetree/bindings/i2c/pnx.txt new file mode 100644 index 000000000000..fe98ada33ee4 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/pnx.txt @@ -0,0 +1,36 @@ +* NXP PNX I2C Controller + +Required properties: + + - reg: Offset and length of the register set for the device + - compatible: should be "nxp,pnx-i2c" + - interrupts: configure one interrupt line + - #address-cells: always 1 (for i2c addresses) + - #size-cells: always 0 + - interrupt-parent: the phandle for the interrupt controller that + services interrupts for this device. + +Optional properties: + + - clock-frequency: desired I2C bus clock frequency in Hz, Default: 100000 Hz + +Examples: + + i2c1: i2c@400a0000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a0000 0x100>; + interrupt-parent = <&mic>; + interrupts = <51 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c2: i2c@400a8000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a8000 0x100>; + interrupt-parent = <&mic>; + interrupts = <50 0>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + }; diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index f69d80b8d736..99389d2eae51 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -23,10 +23,11 @@ #include #include #include +#include -#define I2C_PNX_TIMEOUT 10 /* msec */ -#define I2C_PNX_SPEED_KHZ 100 -#define I2C_PNX_REGION_SIZE 0x100 +#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ +#define I2C_PNX_SPEED_KHZ_DEFAULT 100 +#define I2C_PNX_REGION_SIZE 0x100 enum { mstatus_tdi = 0x00000001, @@ -74,8 +75,9 @@ enum { #define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ #define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ -static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_timeout(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_STS(data)) & mstatus_active)) { mdelay(1); @@ -84,8 +86,9 @@ static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) return (timeout <= 0); } -static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_reset(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { mdelay(1); @@ -97,7 +100,7 @@ static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) { struct timer_list *timer = &alg_data->mif.timer; - unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT); + unsigned long expires = msecs_to_jiffies(alg_data->timeout); if (expires <= 1) expires = 2; @@ -135,7 +138,7 @@ static int i2c_pnx_start(unsigned char slave_addr, } /* First, make sure bus is idle */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_timeout(alg_data)) { /* Somebody else is monopolizing the bus */ dev_err(&alg_data->adapter.dev, "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", @@ -228,7 +231,7 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) { /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); } @@ -326,7 +329,7 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); @@ -442,7 +445,7 @@ static void i2c_pnx_timeout(unsigned long data) ctl |= mcntrl_reset; iowrite32(ctl, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); alg_data->mif.ret = -EIO; complete(&alg_data->mif.complete); } @@ -457,18 +460,18 @@ static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) alg_data->adapter.name); iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { /* If there is data in the fifo's after transfer, * flush fifo's by reset. */ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (stat & mstatus_nai) { iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } } @@ -612,6 +615,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) struct i2c_pnx_algo_data *alg_data; unsigned long freq; struct resource *res; + u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000; alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); if (!alg_data) { @@ -626,6 +630,22 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) alg_data->adapter.algo_data = alg_data; alg_data->adapter.nr = pdev->id; + alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT; +#ifdef CONFIG_OF + alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node); + if (pdev->dev.of_node) { + of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &speed); + /* + * At this point, it is planned to add an OF timeout property. + * As soon as there is a consensus about how to call and handle + * this, sth. like the following can be put here: + * + * of_property_read_u32(pdev->dev.of_node, "timeout", + * &alg_data->timeout); + */ + } +#endif alg_data->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(alg_data->clk)) { ret = PTR_ERR(alg_data->clk); @@ -651,7 +671,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "I/O region 0x%08x for I2C already in use.\n", res->start); - ret = -ENODEV; + ret = -ENOMEM; goto out_clkget; } @@ -680,14 +700,14 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) * the deglitching filter length. */ - tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2; + tmp = (freq / speed) / 2 - 2; if (tmp > 0x3FF) tmp = 0x3FF; iowrite32(tmp, I2C_REG_CKH(alg_data)); iowrite32(tmp, I2C_REG_CKL(alg_data)); iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); - if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_reset(alg_data)) { ret = -ENODEV; goto out_clock; } @@ -710,6 +730,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) goto out_irq; } + of_i2c_register_devices(&alg_data->adapter); + dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", alg_data->adapter.name, res->start, alg_data->irq); @@ -748,10 +770,19 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id i2c_pnx_of_match[] = { + { .compatible = "nxp,pnx-i2c" }, + { }, +}; +MODULE_DEVICE_TABLE(of, i2c_pnx_of_match); +#endif + static struct platform_driver i2c_pnx_driver = { .driver = { .name = "pnx-i2c", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_pnx_of_match), }, .probe = i2c_pnx_probe, .remove = __devexit_p(i2c_pnx_remove), diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index 6e8efb7afd7c..1bc74afe7a35 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -32,6 +32,7 @@ struct i2c_pnx_algo_data { struct i2c_adapter adapter; phys_addr_t base; int irq; + u32 timeout; }; #endif /* __I2C_PNX_H__ */ -- cgit v1.2.3 From 5a3ecd5f9877b963a581ca5d4495a1a24dafc88c Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 12 Apr 2012 14:14:22 -0700 Subject: i2c: Add a struct device * parameter to i2c_add_mux_adapter() And adjust all callers. The new device parameter is used in the next patch to initialize the mux's of_node so that its children may be automatically populated. Signed-off-by: David Daney Tested-by: Lars-Peter Clausen Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-mux.c | 19 ++++++++++--------- drivers/i2c/muxes/gpio-i2cmux.c | 3 ++- drivers/i2c/muxes/pca9541.c | 3 ++- drivers/i2c/muxes/pca954x.c | 2 +- include/linux/i2c-mux.h | 3 ++- 5 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d7a4833be416..26ab31dd742b 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -31,11 +31,11 @@ struct i2c_mux_priv { struct i2c_algorithm algo; struct i2c_adapter *parent; - void *mux_dev; /* the mux chip/device */ + void *mux_priv; /* the mux chip/device */ u32 chan_id; /* the channel id */ - int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id); + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, @@ -47,11 +47,11 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_dev, priv->chan_id); + ret = priv->select(parent, priv->mux_priv, priv->chan_id); if (ret >= 0) ret = parent->algo->master_xfer(parent, msgs, num); if (priv->deselect) - priv->deselect(parent, priv->mux_dev, priv->chan_id); + priv->deselect(parent, priv->mux_priv, priv->chan_id); return ret; } @@ -67,12 +67,12 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_dev, priv->chan_id); + ret = priv->select(parent, priv->mux_priv, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); if (priv->deselect) - priv->deselect(parent, priv->mux_dev, priv->chan_id); + priv->deselect(parent, priv->mux_priv, priv->chan_id); return ret; } @@ -87,7 +87,8 @@ static u32 i2c_mux_functionality(struct i2c_adapter *adap) } struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - void *mux_dev, u32 force_nr, u32 chan_id, + struct device *mux_dev, + void *mux_priv, u32 force_nr, u32 chan_id, int (*select) (struct i2c_adapter *, void *, u32), int (*deselect) (struct i2c_adapter *, @@ -102,7 +103,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, /* Set up private adapter data */ priv->parent = parent; - priv->mux_dev = mux_dev; + priv->mux_priv = mux_priv; priv->chan_id = chan_id; priv->select = select; priv->deselect = deselect; diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c index e5fa695eb0fa..fc5c1ef9b6ec 100644 --- a/drivers/i2c/muxes/gpio-i2cmux.c +++ b/drivers/i2c/muxes/gpio-i2cmux.c @@ -105,7 +105,8 @@ static int __devinit gpiomux_probe(struct platform_device *pdev) for (i = 0; i < pdata->n_values; i++) { u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; - mux->adap[i] = i2c_add_mux_adapter(parent, mux, nr, i, + mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, + nr, i, gpiomux_select, deselect); if (!mux->adap[i]) { ret = -ENODEV; diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c index e0df9b6c66b3..8aacde1516ac 100644 --- a/drivers/i2c/muxes/pca9541.c +++ b/drivers/i2c/muxes/pca9541.c @@ -353,7 +353,8 @@ static int pca9541_probe(struct i2c_client *client, force = 0; if (pdata) force = pdata->modes[0].adap_id; - data->mux_adap = i2c_add_mux_adapter(adap, client, force, 0, + data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client, + force, 0, pca9541_select_chan, pca9541_release_chan); diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c index 0e37ef27aa12..f2dfe0d8fcce 100644 --- a/drivers/i2c/muxes/pca954x.c +++ b/drivers/i2c/muxes/pca954x.c @@ -226,7 +226,7 @@ static int pca954x_probe(struct i2c_client *client, } data->virt_adaps[num] = - i2c_add_mux_adapter(adap, client, + i2c_add_mux_adapter(adap, &client->dev, client, force, num, pca954x_select_chan, (pdata && pdata->modes[num].deselect_on_exit) ? pca954x_deselect_mux : NULL); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 747f0cde4164..c79083830014 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -34,7 +34,8 @@ * mux control. */ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - void *mux_dev, u32 force_nr, u32 chan_id, + struct device *mux_dev, + void *mux_priv, u32 force_nr, u32 chan_id, int (*select) (struct i2c_adapter *, void *mux_dev, u32 chan_id), int (*deselect) (struct i2c_adapter *, -- cgit v1.2.3 From 643dd09eb27b40ced671564edbe2640935fe37c2 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 17 Apr 2012 12:43:33 -0600 Subject: i2c: implement i2c_verify_adapter This converts a struct device * to a struct i2c_adapter * while verifying that the device really is an I2C adapter. Just like i2c_verify_client. Signed-off-by: Stephen Warren Acked-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 17 +++++++++++++++++ include/linux/i2c.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index feb7dc359186..a6ad32bc0a96 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -772,6 +772,23 @@ struct device_type i2c_adapter_type = { }; EXPORT_SYMBOL_GPL(i2c_adapter_type); +/** + * i2c_verify_adapter - return parameter as i2c_adapter or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_adapter. + */ +struct i2c_adapter *i2c_verify_adapter(struct device *dev) +{ + return (dev->type == &i2c_adapter_type) + ? to_i2c_adapter(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_adapter); + #ifdef CONFIG_I2C_COMPAT static struct class_compat *i2c_adapter_compat_class; #endif diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 195d8b3d9cfb..b66cb601435f 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -232,6 +232,7 @@ struct i2c_client { #define to_i2c_client(d) container_of(d, struct i2c_client, dev) extern struct i2c_client *i2c_verify_client(struct device *dev); +extern struct i2c_adapter *i2c_verify_adapter(struct device *dev); static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj) { -- cgit v1.2.3 From 0938643e533cc80ef0cdfdd2e260c4910d0f8bc7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 17 Apr 2012 12:43:34 -0600 Subject: of/i2c: implement of_find_i2c_adapter_by_node This finds the struct i2c_adapter * for a given device tree node. Just like of_find_i2c_device_by_node. Signed-off-by: Stephen Warren Signed-off-by: Wolfram Sang --- drivers/of/of_i2c.c | 14 ++++++++++++++ include/linux/of_i2c.h | 4 ++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 068488717f69..1e173f357674 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -94,4 +94,18 @@ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) } EXPORT_SYMBOL(of_find_i2c_device_by_node); +/* must call put_device() when done with returned i2c_adapter device */ +struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_node_match); + if (!dev) + return NULL; + + return i2c_verify_adapter(dev); +} +EXPORT_SYMBOL(of_find_i2c_adapter_by_node); + MODULE_LICENSE("GPL"); diff --git a/include/linux/of_i2c.h b/include/linux/of_i2c.h index 0efe8d465f55..1cb775f8e663 100644 --- a/include/linux/of_i2c.h +++ b/include/linux/of_i2c.h @@ -20,6 +20,10 @@ extern void of_i2c_register_devices(struct i2c_adapter *adap); /* must call put_device() when done with returned i2c_client device */ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); +/* must call put_device() when done with returned i2c_adapter device */ +extern struct i2c_adapter *of_find_i2c_adapter_by_node( + struct device_node *node); + #else static inline void of_i2c_register_devices(struct i2c_adapter *adap) { -- cgit v1.2.3 From e7065e20d9a6a8ee4a8b31ebe71d6c00a0f45354 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 28 Apr 2012 15:32:06 +0200 Subject: i2c: Rename last mux driver to standard pattern Update the MAINTAINERS entry and all other references accordingly. Based on an original patch by Wolfram Sang. Signed-off-by: Jean Delvare Acked-by: Peter Korsgaard [wsa: fixed merge conflict due to rework in i2c_add_mux_adapter()] Signed-off-by: Wolfram Sang --- Documentation/i2c/muxes/gpio-i2cmux | 65 ------------- Documentation/i2c/muxes/i2c-mux-gpio | 65 +++++++++++++ MAINTAINERS | 6 +- drivers/i2c/muxes/Kconfig | 2 +- drivers/i2c/muxes/Makefile | 2 +- drivers/i2c/muxes/gpio-i2cmux.c | 174 ----------------------------------- drivers/i2c/muxes/i2c-mux-gpio.c | 173 ++++++++++++++++++++++++++++++++++ include/linux/gpio-i2cmux.h | 38 -------- include/linux/i2c-mux-gpio.h | 38 ++++++++ 9 files changed, 281 insertions(+), 282 deletions(-) delete mode 100644 Documentation/i2c/muxes/gpio-i2cmux create mode 100644 Documentation/i2c/muxes/i2c-mux-gpio delete mode 100644 drivers/i2c/muxes/gpio-i2cmux.c create mode 100644 drivers/i2c/muxes/i2c-mux-gpio.c delete mode 100644 include/linux/gpio-i2cmux.h create mode 100644 include/linux/i2c-mux-gpio.h (limited to 'include') diff --git a/Documentation/i2c/muxes/gpio-i2cmux b/Documentation/i2c/muxes/gpio-i2cmux deleted file mode 100644 index 811cd78d4cdc..000000000000 --- a/Documentation/i2c/muxes/gpio-i2cmux +++ /dev/null @@ -1,65 +0,0 @@ -Kernel driver gpio-i2cmux - -Author: Peter Korsgaard - -Description ------------ - -gpio-i2cmux is an i2c mux driver providing access to I2C bus segments -from a master I2C bus and a hardware MUX controlled through GPIO pins. - -E.G.: - - ---------- ---------- Bus segment 1 - - - - - - | | SCL/SDA | |-------------- | | - | |------------| | - | | | | Bus segment 2 | | - | Linux | GPIO 1..N | MUX |--------------- Devices - | |------------| | | | - | | | | Bus segment M - | | | |---------------| | - ---------- ---------- - - - - - - -SCL/SDA of the master I2C bus is multiplexed to bus segment 1..M -according to the settings of the GPIO pins 1..N. - -Usage ------ - -gpio-i2cmux uses the platform bus, so you need to provide a struct -platform_device with the platform_data pointing to a struct -gpio_i2cmux_platform_data with the I2C adapter number of the master -bus, the number of bus segments to create and the GPIO pins used -to control it. See include/linux/gpio-i2cmux.h for details. - -E.G. something like this for a MUX providing 4 bus segments -controlled through 3 GPIO pins: - -#include -#include - -static const unsigned myboard_gpiomux_gpios[] = { - AT91_PIN_PC26, AT91_PIN_PC25, AT91_PIN_PC24 -}; - -static const unsigned myboard_gpiomux_values[] = { - 0, 1, 2, 3 -}; - -static struct gpio_i2cmux_platform_data myboard_i2cmux_data = { - .parent = 1, - .base_nr = 2, /* optional */ - .values = myboard_gpiomux_values, - .n_values = ARRAY_SIZE(myboard_gpiomux_values), - .gpios = myboard_gpiomux_gpios, - .n_gpios = ARRAY_SIZE(myboard_gpiomux_gpios), - .idle = 4, /* optional */ -}; - -static struct platform_device myboard_i2cmux = { - .name = "gpio-i2cmux", - .id = 0, - .dev = { - .platform_data = &myboard_i2cmux_data, - }, -}; diff --git a/Documentation/i2c/muxes/i2c-mux-gpio b/Documentation/i2c/muxes/i2c-mux-gpio new file mode 100644 index 000000000000..bd9b2299b739 --- /dev/null +++ b/Documentation/i2c/muxes/i2c-mux-gpio @@ -0,0 +1,65 @@ +Kernel driver i2c-gpio-mux + +Author: Peter Korsgaard + +Description +----------- + +i2c-gpio-mux is an i2c mux driver providing access to I2C bus segments +from a master I2C bus and a hardware MUX controlled through GPIO pins. + +E.G.: + + ---------- ---------- Bus segment 1 - - - - - + | | SCL/SDA | |-------------- | | + | |------------| | + | | | | Bus segment 2 | | + | Linux | GPIO 1..N | MUX |--------------- Devices + | |------------| | | | + | | | | Bus segment M + | | | |---------------| | + ---------- ---------- - - - - - + +SCL/SDA of the master I2C bus is multiplexed to bus segment 1..M +according to the settings of the GPIO pins 1..N. + +Usage +----- + +i2c-gpio-mux uses the platform bus, so you need to provide a struct +platform_device with the platform_data pointing to a struct +gpio_i2cmux_platform_data with the I2C adapter number of the master +bus, the number of bus segments to create and the GPIO pins used +to control it. See include/linux/i2c-gpio-mux.h for details. + +E.G. something like this for a MUX providing 4 bus segments +controlled through 3 GPIO pins: + +#include +#include + +static const unsigned myboard_gpiomux_gpios[] = { + AT91_PIN_PC26, AT91_PIN_PC25, AT91_PIN_PC24 +}; + +static const unsigned myboard_gpiomux_values[] = { + 0, 1, 2, 3 +}; + +static struct gpio_i2cmux_platform_data myboard_i2cmux_data = { + .parent = 1, + .base_nr = 2, /* optional */ + .values = myboard_gpiomux_values, + .n_values = ARRAY_SIZE(myboard_gpiomux_values), + .gpios = myboard_gpiomux_gpios, + .n_gpios = ARRAY_SIZE(myboard_gpiomux_gpios), + .idle = 4, /* optional */ +}; + +static struct platform_device myboard_i2cmux = { + .name = "i2c-gpio-mux", + .id = 0, + .dev = { + .platform_data = &myboard_i2cmux_data, + }, +}; diff --git a/MAINTAINERS b/MAINTAINERS index f0c3b4558381..4241699adea3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2937,9 +2937,9 @@ GENERIC GPIO I2C MULTIPLEXER DRIVER M: Peter Korsgaard L: linux-i2c@vger.kernel.org S: Supported -F: drivers/i2c/muxes/gpio-i2cmux.c -F: include/linux/gpio-i2cmux.h -F: Documentation/i2c/muxes/gpio-i2cmux +F: drivers/i2c/muxes/i2c-mux-gpio.c +F: include/linux/i2c-mux-gpio.h +F: Documentation/i2c/muxes/i2c-mux-gpio GENERIC HDLC (WAN) DRIVERS M: Krzysztof Halasa diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index e14a4205e5c6..beb2491db274 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -15,7 +15,7 @@ config I2C_MUX_GPIO through GPIO pins. This driver can also be built as a module. If so, the module - will be called gpio-i2cmux. + will be called i2c-mux-gpio. config I2C_MUX_PCA9541 tristate "NXP PCA9541 I2C Master Selector" diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 0868335cff11..5826249b29ca 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -1,7 +1,7 @@ # # Makefile for multiplexer I2C chip drivers. -obj-$(CONFIG_I2C_MUX_GPIO) += gpio-i2cmux.o +obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c deleted file mode 100644 index fc5c1ef9b6ec..000000000000 --- a/drivers/i2c/muxes/gpio-i2cmux.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * I2C multiplexer using GPIO API - * - * Peter Korsgaard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct gpiomux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ - struct gpio_i2cmux_platform_data data; -}; - -static void gpiomux_set(const struct gpiomux *mux, unsigned val) -{ - int i; - - for (i = 0; i < mux->data.n_gpios; i++) - gpio_set_value(mux->data.gpios[i], val & (1 << i)); -} - -static int gpiomux_select(struct i2c_adapter *adap, void *data, u32 chan) -{ - struct gpiomux *mux = data; - - gpiomux_set(mux, mux->data.values[chan]); - - return 0; -} - -static int gpiomux_deselect(struct i2c_adapter *adap, void *data, u32 chan) -{ - struct gpiomux *mux = data; - - gpiomux_set(mux, mux->data.idle); - - return 0; -} - -static int __devinit gpiomux_probe(struct platform_device *pdev) -{ - struct gpiomux *mux; - struct gpio_i2cmux_platform_data *pdata; - struct i2c_adapter *parent; - int (*deselect) (struct i2c_adapter *, void *, u32); - unsigned initial_state; - int i, ret; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); - return -ENODEV; - } - - parent = i2c_get_adapter(pdata->parent); - if (!parent) { - dev_err(&pdev->dev, "Parent adapter (%d) not found\n", - pdata->parent); - return -ENODEV; - } - - mux = kzalloc(sizeof(*mux), GFP_KERNEL); - if (!mux) { - ret = -ENOMEM; - goto alloc_failed; - } - - mux->parent = parent; - mux->data = *pdata; - mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, - GFP_KERNEL); - if (!mux->adap) { - ret = -ENOMEM; - goto alloc_failed2; - } - - if (pdata->idle != GPIO_I2CMUX_NO_IDLE) { - initial_state = pdata->idle; - deselect = gpiomux_deselect; - } else { - initial_state = pdata->values[0]; - deselect = NULL; - } - - for (i = 0; i < pdata->n_gpios; i++) { - ret = gpio_request(pdata->gpios[i], "gpio-i2cmux"); - if (ret) - goto err_request_gpio; - gpio_direction_output(pdata->gpios[i], - initial_state & (1 << i)); - } - - for (i = 0; i < pdata->n_values; i++) { - u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; - - mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, - nr, i, - gpiomux_select, deselect); - if (!mux->adap[i]) { - ret = -ENODEV; - dev_err(&pdev->dev, "Failed to add adapter %d\n", i); - goto add_adapter_failed; - } - } - - dev_info(&pdev->dev, "%d port mux on %s adapter\n", - pdata->n_values, parent->name); - - platform_set_drvdata(pdev, mux); - - return 0; - -add_adapter_failed: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->adap[i - 1]); - i = pdata->n_gpios; -err_request_gpio: - for (; i > 0; i--) - gpio_free(pdata->gpios[i - 1]); - kfree(mux->adap); -alloc_failed2: - kfree(mux); -alloc_failed: - i2c_put_adapter(parent); - - return ret; -} - -static int __devexit gpiomux_remove(struct platform_device *pdev) -{ - struct gpiomux *mux = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < mux->data.n_values; i++) - i2c_del_mux_adapter(mux->adap[i]); - - for (i = 0; i < mux->data.n_gpios; i++) - gpio_free(mux->data.gpios[i]); - - platform_set_drvdata(pdev, NULL); - i2c_put_adapter(mux->parent); - kfree(mux->adap); - kfree(mux); - - return 0; -} - -static struct platform_driver gpiomux_driver = { - .probe = gpiomux_probe, - .remove = __devexit_p(gpiomux_remove), - .driver = { - .owner = THIS_MODULE, - .name = "gpio-i2cmux", - }, -}; - -module_platform_driver(gpiomux_driver); - -MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); -MODULE_AUTHOR("Peter Korsgaard "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:gpio-i2cmux"); diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c new file mode 100644 index 000000000000..68b1f8ec3436 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -0,0 +1,173 @@ +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpiomux { + struct i2c_adapter *parent; + struct i2c_adapter **adap; /* child busses */ + struct i2c_mux_gpio_platform_data data; +}; + +static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) +{ + int i; + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_set_value(mux->data.gpios[i], val & (1 << i)); +} + +static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + i2c_mux_gpio_set(mux, mux->data.values[chan]); + + return 0; +} + +static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + i2c_mux_gpio_set(mux, mux->data.idle); + + return 0; +} + +static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) +{ + struct gpiomux *mux; + struct i2c_mux_gpio_platform_data *pdata; + struct i2c_adapter *parent; + int (*deselect) (struct i2c_adapter *, void *, u32); + unsigned initial_state; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + ret = -ENOMEM; + goto alloc_failed; + } + + mux->parent = parent; + mux->data = *pdata; + mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, + GFP_KERNEL); + if (!mux->adap) { + ret = -ENOMEM; + goto alloc_failed2; + } + + if (pdata->idle != I2C_MUX_GPIO_NO_IDLE) { + initial_state = pdata->idle; + deselect = i2c_mux_gpio_deselect; + } else { + initial_state = pdata->values[0]; + deselect = NULL; + } + + for (i = 0; i < pdata->n_gpios; i++) { + ret = gpio_request(pdata->gpios[i], "i2c-mux-gpio"); + if (ret) + goto err_request_gpio; + gpio_direction_output(pdata->gpios[i], + initial_state & (1 << i)); + } + + for (i = 0; i < pdata->n_values; i++) { + u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; + + mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, i, + i2c_mux_gpio_select, deselect); + if (!mux->adap[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto add_adapter_failed; + } + } + + dev_info(&pdev->dev, "%d port mux on %s adapter\n", + pdata->n_values, parent->name); + + platform_set_drvdata(pdev, mux); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->adap[i - 1]); + i = pdata->n_gpios; +err_request_gpio: + for (; i > 0; i--) + gpio_free(pdata->gpios[i - 1]); + kfree(mux->adap); +alloc_failed2: + kfree(mux); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int __devexit i2c_mux_gpio_remove(struct platform_device *pdev) +{ + struct gpiomux *mux = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < mux->data.n_values; i++) + i2c_del_mux_adapter(mux->adap[i]); + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_free(mux->data.gpios[i]); + + platform_set_drvdata(pdev, NULL); + i2c_put_adapter(mux->parent); + kfree(mux->adap); + kfree(mux); + + return 0; +} + +static struct platform_driver i2c_mux_gpio_driver = { + .probe = i2c_mux_gpio_probe, + .remove = __devexit_p(i2c_mux_gpio_remove), + .driver = { + .owner = THIS_MODULE, + .name = "i2c-mux-gpio", + }, +}; + +module_platform_driver(i2c_mux_gpio_driver); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-mux-gpio"); diff --git a/include/linux/gpio-i2cmux.h b/include/linux/gpio-i2cmux.h deleted file mode 100644 index 4a333bb0bd0d..000000000000 --- a/include/linux/gpio-i2cmux.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * gpio-i2cmux interface to platform code - * - * Peter Korsgaard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _LINUX_GPIO_I2CMUX_H -#define _LINUX_GPIO_I2CMUX_H - -/* MUX has no specific idle mode */ -#define GPIO_I2CMUX_NO_IDLE ((unsigned)-1) - -/** - * struct gpio_i2cmux_platform_data - Platform-dependent data for gpio-i2cmux - * @parent: Parent I2C bus adapter number - * @base_nr: Base I2C bus number to number adapters from or zero for dynamic - * @values: Array of bitmasks of GPIO settings (low/high) for each - * position - * @n_values: Number of multiplexer positions (busses to instantiate) - * @gpios: Array of GPIO numbers used to control MUX - * @n_gpios: Number of GPIOs used to control MUX - * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used - */ -struct gpio_i2cmux_platform_data { - int parent; - int base_nr; - const unsigned *values; - int n_values; - const unsigned *gpios; - int n_gpios; - unsigned idle; -}; - -#endif /* _LINUX_GPIO_I2CMUX_H */ diff --git a/include/linux/i2c-mux-gpio.h b/include/linux/i2c-mux-gpio.h new file mode 100644 index 000000000000..a36343a37ebc --- /dev/null +++ b/include/linux/i2c-mux-gpio.h @@ -0,0 +1,38 @@ +/* + * i2c-mux-gpio interface to platform code + * + * Peter Korsgaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_I2C_MUX_GPIO_H +#define _LINUX_I2C_MUX_GPIO_H + +/* MUX has no specific idle mode */ +#define I2C_MUX_GPIO_NO_IDLE ((unsigned)-1) + +/** + * struct i2c_mux_gpio_platform_data - Platform-dependent data for i2c-mux-gpio + * @parent: Parent I2C bus adapter number + * @base_nr: Base I2C bus number to number adapters from or zero for dynamic + * @values: Array of bitmasks of GPIO settings (low/high) for each + * position + * @n_values: Number of multiplexer positions (busses to instantiate) + * @gpios: Array of GPIO numbers used to control MUX + * @n_gpios: Number of GPIOs used to control MUX + * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used + */ +struct i2c_mux_gpio_platform_data { + int parent; + int base_nr; + const unsigned *values; + int n_values; + const unsigned *gpios; + int n_gpios; + unsigned idle; +}; + +#endif /* _LINUX_I2C_MUX_GPIO_H */ -- cgit v1.2.3