diff options
Diffstat (limited to 'drivers/i2c/muxes')
| -rw-r--r-- | drivers/i2c/muxes/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/i2c/muxes/Makefile | 2 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 249 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-gpio.c | 52 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca9541.c | 5 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 41 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pinctrl.c | 9 |
7 files changed, 323 insertions, 52 deletions
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index a0edd985421..f6d313e528d 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -5,9 +5,21 @@ menu "Multiplexer I2C Chip support" depends on I2C_MUX +config I2C_ARB_GPIO_CHALLENGE + tristate "GPIO-based I2C arbitration" + depends on GPIOLIB && OF + help + If you say yes to this option, support will be included for an + I2C multimaster arbitration scheme using GPIOs and a challenge & + response mechanism where masters have to claim the bus by asserting + a GPIO. + + This driver can also be built as a module. If so, the module + will be called i2c-arb-gpio-challenge. + config I2C_MUX_GPIO tristate "GPIO-based I2C multiplexer" - depends on GENERIC_GPIO + depends on GPIOLIB help If you say yes to this option, support will be included for a GPIO based I2C multiplexer. This driver provides access to @@ -19,7 +31,6 @@ config I2C_MUX_GPIO config I2C_MUX_PCA9541 tristate "NXP PCA9541 I2C Master Selector" - depends on EXPERIMENTAL help If you say yes here you get support for the NXP PCA9541 I2C Master Selector. @@ -29,7 +40,7 @@ config I2C_MUX_PCA9541 config I2C_MUX_PCA954x tristate "Philips PCA954x I2C Mux/switches" - depends on EXPERIMENTAL + depends on GPIOLIB help If you say yes here you get support for the Philips PCA954x I2C mux/switch devices. diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 76da8692aff..465778b5d5d 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -1,6 +1,8 @@ # # Makefile for multiplexer I2C chip drivers. +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.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/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c new file mode 100644 index 00000000000..69afffa8f42 --- /dev/null +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -0,0 +1,249 @@ +/* + * GPIO-based I2C Arbitration Using a Challenge & Response Mechanism + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. See the + * GNU General Public License for more details. + * + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + + +/** + * struct i2c_arbitrator_data - Driver data for I2C arbitrator + * + * @parent: Parent adapter + * @child: Child bus + * @our_gpio: GPIO we'll use to claim. + * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == + * this then consider it released. + * @their_gpio: GPIO that the other side will use to claim. + * @their_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == + * this then consider it released. + * @slew_delay_us: microseconds to wait for a GPIO to go high. + * @wait_retry_us: we'll attempt another claim after this many microseconds. + * @wait_free_us: we'll give up after this many microseconds. + */ + +struct i2c_arbitrator_data { + struct i2c_adapter *parent; + struct i2c_adapter *child; + int our_gpio; + int our_gpio_release; + int their_gpio; + int their_gpio_release; + unsigned int slew_delay_us; + unsigned int wait_retry_us; + unsigned int wait_free_us; +}; + + +/** + * i2c_arbitrator_select - claim the I2C bus + * + * Use the GPIO-based signalling protocol; return -EBUSY if we fail. + */ +static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + const struct i2c_arbitrator_data *arb = data; + unsigned long stop_retry, stop_time; + + /* Start a round of trying to claim the bus */ + stop_time = jiffies + usecs_to_jiffies(arb->wait_free_us) + 1; + do { + /* Indicate that we want to claim the bus */ + gpio_set_value(arb->our_gpio, !arb->our_gpio_release); + udelay(arb->slew_delay_us); + + /* Wait for the other master to release it */ + stop_retry = jiffies + usecs_to_jiffies(arb->wait_retry_us) + 1; + while (time_before(jiffies, stop_retry)) { + int gpio_val = !!gpio_get_value(arb->their_gpio); + + if (gpio_val == arb->their_gpio_release) { + /* We got it, so return */ + return 0; + } + + usleep_range(50, 200); + } + + /* It didn't release, so give up, wait, and try again */ + gpio_set_value(arb->our_gpio, arb->our_gpio_release); + + usleep_range(arb->wait_retry_us, arb->wait_retry_us * 2); + } while (time_before(jiffies, stop_time)); + + /* Give up, release our claim */ + gpio_set_value(arb->our_gpio, arb->our_gpio_release); + udelay(arb->slew_delay_us); + dev_err(&adap->dev, "Could not claim bus, timeout\n"); + return -EBUSY; +} + +/** + * i2c_arbitrator_deselect - release the I2C bus + * + * Release the I2C bus using the GPIO-based signalling protocol. + */ +static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data, + u32 chan) +{ + const struct i2c_arbitrator_data *arb = data; + + /* Release the bus and wait for the other master to notice */ + gpio_set_value(arb->our_gpio, arb->our_gpio_release); + udelay(arb->slew_delay_us); + + return 0; +} + +static int i2c_arbitrator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_arbitrator_data *arb; + enum of_gpio_flags gpio_flags; + unsigned long out_init; + int ret; + + /* We only support probing from device tree; no platform_data */ + if (!np) { + dev_err(dev, "Cannot find device tree node\n"); + return -ENODEV; + } + if (dev_get_platdata(dev)) { + dev_err(dev, "Platform data is not supported\n"); + return -EINVAL; + } + + arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); + if (!arb) { + dev_err(dev, "Cannot allocate i2c_arbitrator_data\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, arb); + + /* Request GPIOs */ + ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags); + if (!gpio_is_valid(ret)) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Error getting our-claim-gpio\n"); + return ret; + } + arb->our_gpio = ret; + arb->our_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW); + out_init = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = devm_gpio_request_one(dev, arb->our_gpio, out_init, + "our-claim-gpio"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Error requesting our-claim-gpio\n"); + return ret; + } + + ret = of_get_named_gpio_flags(np, "their-claim-gpios", 0, &gpio_flags); + if (!gpio_is_valid(ret)) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Error getting their-claim-gpio\n"); + return ret; + } + arb->their_gpio = ret; + arb->their_gpio_release = !!(gpio_flags & OF_GPIO_ACTIVE_LOW); + ret = devm_gpio_request_one(dev, arb->their_gpio, GPIOF_IN, + "their-claim-gpio"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Error requesting their-claim-gpio\n"); + return ret; + } + + /* At the moment we only support a single two master (us + 1 other) */ + if (gpio_is_valid(of_get_named_gpio(np, "their-claim-gpios", 1))) { + dev_err(dev, "Only one other master is supported\n"); + return -EINVAL; + } + + /* Arbitration parameters */ + if (of_property_read_u32(np, "slew-delay-us", &arb->slew_delay_us)) + arb->slew_delay_us = 10; + if (of_property_read_u32(np, "wait-retry-us", &arb->wait_retry_us)) + arb->wait_retry_us = 3000; + if (of_property_read_u32(np, "wait-free-us", &arb->wait_free_us)) + arb->wait_free_us = 50000; + + /* Find our parent */ + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return -EINVAL; + } + arb->parent = of_find_i2c_adapter_by_node(parent_np); + if (!arb->parent) { + dev_err(dev, "Cannot find parent bus\n"); + return -EPROBE_DEFER; + } + + /* Actually add the mux adapter */ + arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0, + i2c_arbitrator_select, + i2c_arbitrator_deselect); + if (!arb->child) { + dev_err(dev, "Failed to add adapter\n"); + ret = -ENODEV; + i2c_put_adapter(arb->parent); + } + + return ret; +} + +static int i2c_arbitrator_remove(struct platform_device *pdev) +{ + struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev); + + i2c_del_mux_adapter(arb->child); + i2c_put_adapter(arb->parent); + + return 0; +} + +static const struct of_device_id i2c_arbitrator_of_match[] = { + { .compatible = "i2c-arb-gpio-challenge", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_arbitrator_of_match); + +static struct platform_driver i2c_arbitrator_driver = { + .probe = i2c_arbitrator_probe, + .remove = i2c_arbitrator_remove, + .driver = { + .owner = THIS_MODULE, + .name = "i2c-arb-gpio-challenge", + .of_match_table = i2c_arbitrator_of_match, + }, +}; + +module_platform_driver(i2c_arbitrator_driver); + +MODULE_DESCRIPTION("GPIO-based I2C Arbitration"); +MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-arb-gpio-challenge"); diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 9f50ef04a4b..d8989c823f5 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -12,11 +12,9 @@ #include <linux/i2c-mux.h> #include <linux/i2c-mux-gpio.h> #include <linux/platform_device.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/gpio.h> -#include <linux/of_i2c.h> #include <linux/of_gpio.h> struct gpiomux { @@ -31,15 +29,15 @@ 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->gpio_base + mux->data.gpios[i], - val & (1 << i)); + gpio_set_value_cansleep(mux->gpio_base + 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]); + i2c_mux_gpio_set(mux, chan); return 0; } @@ -67,7 +65,7 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, struct device_node *adapter_np, *child; struct i2c_adapter *adapter; unsigned *values, *gpios; - int i = 0; + int i = 0, ret; if (!np) return -ENODEV; @@ -80,7 +78,7 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, adapter = of_find_i2c_adapter_by_node(adapter_np); if (!adapter) { dev_err(&pdev->dev, "Cannot find parent bus\n"); - return -ENODEV; + return -EPROBE_DEFER; } mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -117,8 +115,12 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, return -ENOMEM; } - for (i = 0; i < mux->data.n_gpios; i++) - gpios[i] = of_get_named_gpio(np, "mux-gpios", i); + for (i = 0; i < mux->data.n_gpios; i++) { + ret = of_get_named_gpio(np, "mux-gpios", i); + if (ret < 0) + return ret; + gpios[i] = ret; + } mux->data.gpios = gpios; @@ -148,12 +150,14 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mux); - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { ret = i2c_mux_gpio_probe_dt(mux, pdev); if (ret < 0) return ret; - } else - memcpy(&mux->data, pdev->dev.platform_data, sizeof(mux->data)); + } else { + memcpy(&mux->data, dev_get_platdata(&pdev->dev), + sizeof(mux->data)); + } /* * If a GPIO chip name is provided, the GPIO pin numbers provided are @@ -176,7 +180,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) if (!parent) { dev_err(&pdev->dev, "Parent adapter (%d) not found\n", mux->data.parent); - return -ENODEV; + return -EPROBE_DEFER; } mux->parent = parent; @@ -201,10 +205,21 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) for (i = 0; i < mux->data.n_gpios; i++) { ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio"); - if (ret) + if (ret) { + dev_err(&pdev->dev, "Failed to request GPIO %d\n", + mux->data.gpios[i]); + goto err_request_gpio; + } + + ret = gpio_direction_output(gpio_base + mux->data.gpios[i], + initial_state & (1 << i)); + if (ret) { + dev_err(&pdev->dev, + "Failed to set direction of GPIO %d to output\n", + mux->data.gpios[i]); + i++; /* gpio_request above succeeded, so must free */ goto err_request_gpio; - gpio_direction_output(gpio_base + mux->data.gpios[i], - initial_state & (1 << i)); + } } for (i = 0; i < mux->data.n_values; i++) { @@ -212,7 +227,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, - i, class, + mux->data.values[i], class, i2c_mux_gpio_select, deselect); if (!mux->adap[i]) { ret = -ENODEV; @@ -250,7 +265,6 @@ static int i2c_mux_gpio_remove(struct platform_device *pdev) for (i = 0; i < mux->data.n_gpios; i++) gpio_free(mux->gpio_base + mux->data.gpios[i]); - platform_set_drvdata(pdev, NULL); i2c_put_adapter(mux->parent); return 0; @@ -268,7 +282,7 @@ static struct platform_driver i2c_mux_gpio_driver = { .driver = { .owner = THIS_MODULE, .name = "i2c-mux-gpio", - .of_match_table = of_match_ptr(i2c_mux_gpio_of_match), + .of_match_table = i2c_mux_gpio_of_match, }, }; diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index f3b8f9a6a89..cb772775da4 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -3,7 +3,7 @@ * * Copyright (c) 2010 Ericsson AB. * - * Author: Guenter Roeck <guenter.roeck@ericsson.com> + * Author: Guenter Roeck <linux@roeck-us.net> * * Derived from: * pca954x.c @@ -17,7 +17,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/jiffies.h> #include <linux/delay.h> #include <linux/slab.h> @@ -324,7 +323,7 @@ static int pca9541_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adap = client->adapter; - struct pca954x_platform_data *pdata = client->dev.platform_data; + struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); struct pca9541 *data; int force; int ret = -ENODEV; diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 8e4387235b6..9bd4212782a 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -28,21 +28,20 @@ * Based on: * i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com> * and - * pca9540.c from Jean Delvare <khali@linux-fr.org>. + * pca9540.c from Jean Delvare <jdelvare@suse.de>. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> #include <linux/device.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> - #include <linux/i2c/pca954x.h> +#include <linux/module.h> +#include <linux/slab.h> #define PCA954X_MAX_NCHANS 8 @@ -185,29 +184,33 @@ static int pca954x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); - struct pca954x_platform_data *pdata = client->dev.platform_data; + struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); + struct gpio_desc *gpio; int num, force, class; struct pca954x *data; - int ret = -ENODEV; + int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) - goto err; + return -ENODEV; - data = kzalloc(sizeof(struct pca954x), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err; - } + data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); + /* Get the mux out of reset if a reset GPIO is specified. */ + gpio = devm_gpiod_get(&client->dev, "reset"); + if (!IS_ERR(gpio)) + gpiod_direction_output(gpio, 0); + /* Write the mux register at addr to verify * that the mux is in fact present. This also * initializes the mux to disconnected state. */ if (i2c_smbus_write_byte(client, 0) < 0) { dev_warn(&client->dev, "probe failed\n"); - goto exit_free; + return -ENODEV; } data->type = id->driver_data; @@ -252,9 +255,6 @@ static int pca954x_probe(struct i2c_client *client, virt_reg_failed: for (num--; num >= 0; num--) i2c_del_mux_adapter(data->virt_adaps[num]); -exit_free: - kfree(data); -err: return ret; } @@ -262,17 +262,14 @@ static int pca954x_remove(struct i2c_client *client) { struct pca954x *data = i2c_get_clientdata(client); const struct chip_desc *chip = &chips[data->type]; - int i, err; + int i; for (i = 0; i < chip->nchans; ++i) if (data->virt_adaps[i]) { - err = i2c_del_mux_adapter(data->virt_adaps[i]); - if (err) - return err; + i2c_del_mux_adapter(data->virt_adaps[i]); data->virt_adaps[i] = NULL; } - kfree(data); return 0; } diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index a43c0ce5e3d..4ff0ef3e07a 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -18,13 +18,12 @@ #include <linux/i2c.h> #include <linux/i2c-mux.h> -#include <linux/init.h> #include <linux/module.h> -#include <linux/of_i2c.h> #include <linux/pinctrl/consumer.h> #include <linux/i2c-mux-pinctrl.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/of.h> struct i2c_mux_pinctrl { struct device *dev; @@ -114,7 +113,7 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, adapter = of_find_i2c_adapter_by_node(adapter_np); if (!adapter) { dev_err(mux->dev, "Cannot find parent bus\n"); - return -ENODEV; + return -EPROBE_DEFER; } mux->pdata->parent_bus_num = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -145,7 +144,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) mux->dev = &pdev->dev; - mux->pdata = pdev->dev.platform_data; + mux->pdata = dev_get_platdata(&pdev->dev); if (!mux->pdata) { ret = i2c_mux_pinctrl_parse_dt(mux, pdev); if (ret < 0) @@ -212,7 +211,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) if (!mux->parent) { dev_err(&pdev->dev, "Parent adapter (%d) not found\n", mux->pdata->parent_bus_num); - ret = -ENODEV; + ret = -EPROBE_DEFER; goto err; } |
