diff options
Diffstat (limited to 'drivers/i2c/muxes')
| -rw-r--r-- | drivers/i2c/muxes/Kconfig | 35 | ||||
| -rw-r--r-- | drivers/i2c/muxes/Makefile | 9 | ||||
| -rw-r--r-- | drivers/i2c/muxes/gpio-i2cmux.c | 173 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 249 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-gpio.c | 294 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca9541.c (renamed from drivers/i2c/muxes/pca9541.c) | 23 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c (renamed from drivers/i2c/muxes/pca954x.c) | 66 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pinctrl.c | 278 |
8 files changed, 889 insertions, 238 deletions
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 90b7a016389..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 @@ -15,26 +27,37 @@ 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" - depends on EXPERIMENTAL help If you say yes here you get support for the NXP PCA9541 I2C Master Selector. This driver can also be built as a module. If so, the module - will be called pca9541. + will be called 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. This driver can also be built as a module. If so, the module - will be called pca954x. + will be called i2c-mux-pca954x. + +config I2C_MUX_PINCTRL + tristate "pinctrl-based I2C multiplexer" + depends on PINCTRL + help + If you say yes to this option, support will be included for an I2C + multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing. + This is useful for SoCs whose I2C module's signals can be routed to + different sets of pins at run-time. + + This driver can also be built as a module. If so, the module will be + called pinctrl-i2cmux. endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 4640436ea61..465778b5d5d 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -1,8 +1,11 @@ # # Makefile for multiplexer I2C chip drivers. -obj-$(CONFIG_I2C_MUX_GPIO) += gpio-i2cmux.o -obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o -obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o +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 +obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c deleted file mode 100644 index e5fa695eb0f..00000000000 --- a/drivers/i2c/muxes/gpio-i2cmux.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * I2C multiplexer using GPIO API - * - * Peter Korsgaard <peter.korsgaard@barco.com> - * - * 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 <linux/i2c.h> -#include <linux/i2c-mux.h> -#include <linux/gpio-i2cmux.h> -#include <linux/platform_device.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/gpio.h> - -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, 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 <peter.korsgaard@barco.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:gpio-i2cmux"); 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 new file mode 100644 index 00000000000..d8989c823f5 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -0,0 +1,294 @@ +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard <peter.korsgaard@barco.com> + * + * 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 <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/i2c-mux-gpio.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +struct gpiomux { + struct i2c_adapter *parent; + struct i2c_adapter **adap; /* child busses */ + struct i2c_mux_gpio_platform_data data; + unsigned gpio_base; +}; + +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_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, 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 match_gpio_chip_by_label(struct gpio_chip *chip, + void *data) +{ + return !strcmp(chip->label, data); +} + +#ifdef CONFIG_OF +static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *adapter_np, *child; + struct i2c_adapter *adapter; + unsigned *values, *gpios; + int i = 0, ret; + + if (!np) + return -ENODEV; + + adapter_np = of_parse_phandle(np, "i2c-parent", 0); + if (!adapter_np) { + dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); + return -ENODEV; + } + adapter = of_find_i2c_adapter_by_node(adapter_np); + if (!adapter) { + dev_err(&pdev->dev, "Cannot find parent bus\n"); + return -EPROBE_DEFER; + } + mux->data.parent = i2c_adapter_id(adapter); + put_device(&adapter->dev); + + mux->data.n_values = of_get_child_count(np); + + values = devm_kzalloc(&pdev->dev, + sizeof(*mux->data.values) * mux->data.n_values, + GFP_KERNEL); + if (!values) { + dev_err(&pdev->dev, "Cannot allocate values array"); + return -ENOMEM; + } + + for_each_child_of_node(np, child) { + of_property_read_u32(child, "reg", values + i); + i++; + } + mux->data.values = values; + + if (of_property_read_u32(np, "idle-state", &mux->data.idle)) + mux->data.idle = I2C_MUX_GPIO_NO_IDLE; + + mux->data.n_gpios = of_gpio_named_count(np, "mux-gpios"); + if (mux->data.n_gpios < 0) { + dev_err(&pdev->dev, "Missing mux-gpios property in the DT.\n"); + return -EINVAL; + } + + gpios = devm_kzalloc(&pdev->dev, + sizeof(*mux->data.gpios) * mux->data.n_gpios, GFP_KERNEL); + if (!gpios) { + dev_err(&pdev->dev, "Cannot allocate gpios array"); + return -ENOMEM; + } + + 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; + + return 0; +} +#else +static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, + struct platform_device *pdev) +{ + return 0; +} +#endif + +static int i2c_mux_gpio_probe(struct platform_device *pdev) +{ + struct gpiomux *mux; + struct i2c_adapter *parent; + int (*deselect) (struct i2c_adapter *, void *, u32); + unsigned initial_state, gpio_base; + int i, ret; + + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); + if (!mux) { + dev_err(&pdev->dev, "Cannot allocate gpiomux structure"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, mux); + + if (!dev_get_platdata(&pdev->dev)) { + ret = i2c_mux_gpio_probe_dt(mux, pdev); + if (ret < 0) + return ret; + } 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 + * relative to its base GPIO number. Otherwise they are absolute. + */ + if (mux->data.gpio_chip) { + struct gpio_chip *gpio; + + gpio = gpiochip_find(mux->data.gpio_chip, + match_gpio_chip_by_label); + if (!gpio) + return -EPROBE_DEFER; + + gpio_base = gpio->base; + } else { + gpio_base = 0; + } + + parent = i2c_get_adapter(mux->data.parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + mux->data.parent); + return -EPROBE_DEFER; + } + + mux->parent = parent; + mux->gpio_base = gpio_base; + + mux->adap = devm_kzalloc(&pdev->dev, + sizeof(*mux->adap) * mux->data.n_values, + GFP_KERNEL); + if (!mux->adap) { + dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); + ret = -ENOMEM; + goto alloc_failed; + } + + if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { + initial_state = mux->data.idle; + deselect = i2c_mux_gpio_deselect; + } else { + initial_state = mux->data.values[0]; + deselect = NULL; + } + + for (i = 0; i < mux->data.n_gpios; i++) { + ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio"); + 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; + } + } + + for (i = 0; i < mux->data.n_values; i++) { + u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; + unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; + + mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, + mux->data.values[i], class, + 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", + mux->data.n_values, parent->name); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->adap[i - 1]); + i = mux->data.n_gpios; +err_request_gpio: + for (; i > 0; i--) + gpio_free(gpio_base + mux->data.gpios[i - 1]); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int 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->gpio_base + mux->data.gpios[i]); + + i2c_put_adapter(mux->parent); + + return 0; +} + +static const struct of_device_id i2c_mux_gpio_of_match[] = { + { .compatible = "i2c-mux-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match); + +static struct platform_driver i2c_mux_gpio_driver = { + .probe = i2c_mux_gpio_probe, + .remove = i2c_mux_gpio_remove, + .driver = { + .owner = THIS_MODULE, + .name = "i2c-mux-gpio", + .of_match_table = i2c_mux_gpio_of_match, + }, +}; + +module_platform_driver(i2c_mux_gpio_driver); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-mux-gpio"); diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index ed699c5aa79..cb772775da4 100644 --- a/drivers/i2c/muxes/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; @@ -353,7 +352,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, 0, pca9541_select_chan, pca9541_release_chan); @@ -393,19 +393,8 @@ static struct i2c_driver pca9541_driver = { .id_table = pca9541_id, }; -static int __init pca9541_init(void) -{ - return i2c_add_driver(&pca9541_driver); -} - -static void __exit pca9541_exit(void) -{ - i2c_del_driver(&pca9541_driver); -} - -module_init(pca9541_init); -module_exit(pca9541_exit); +module_i2c_driver(pca9541_driver); -MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 6f895366463..9bd4212782a 100644 --- a/drivers/i2c/muxes/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; - int num, force; + 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; @@ -216,18 +219,20 @@ static int pca954x_probe(struct i2c_client *client, /* Now create an adapter for each channel */ for (num = 0; num < chips[data->type].nchans; num++) { force = 0; /* dynamic adap number */ + class = 0; /* no class by default */ if (pdata) { - if (num < pdata->num_modes) + if (num < pdata->num_modes) { /* force static number */ force = pdata->modes[num].adap_id; - else + class = pdata->modes[num].class; + } else /* discard unconfigured channels */ break; } data->virt_adaps[num] = - i2c_add_mux_adapter(adap, client, - force, num, pca954x_select_chan, + i2c_add_mux_adapter(adap, &client->dev, client, + force, num, class, pca954x_select_chan, (pdata && pdata->modes[num].deselect_on_exit) ? pca954x_deselect_mux : NULL); @@ -250,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; } @@ -260,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; } @@ -284,18 +283,7 @@ static struct i2c_driver pca954x_driver = { .id_table = pca954x_id, }; -static int __init pca954x_init(void) -{ - return i2c_add_driver(&pca954x_driver); -} - -static void __exit pca954x_exit(void) -{ - i2c_del_driver(&pca954x_driver); -} - -module_init(pca954x_init); -module_exit(pca954x_exit); +module_i2c_driver(pca954x_driver); MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c new file mode 100644 index 00000000000..4ff0ef3e07a --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -0,0 +1,278 @@ +/* + * I2C multiplexer using pinctrl API + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.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; + struct i2c_mux_pinctrl_platform_data *pdata; + struct pinctrl *pinctrl; + struct pinctrl_state **states; + struct pinctrl_state *state_idle; + struct i2c_adapter *parent; + struct i2c_adapter **busses; +}; + +static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, + u32 chan) +{ + struct i2c_mux_pinctrl *mux = data; + + return pinctrl_select_state(mux->pinctrl, mux->states[chan]); +} + +static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, + u32 chan) +{ + struct i2c_mux_pinctrl *mux = data; + + return pinctrl_select_state(mux->pinctrl, mux->state_idle); +} + +#ifdef CONFIG_OF +static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int num_names, i, ret; + struct device_node *adapter_np; + struct i2c_adapter *adapter; + + if (!np) + return 0; + + mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); + if (!mux->pdata) { + dev_err(mux->dev, + "Cannot allocate i2c_mux_pinctrl_platform_data\n"); + return -ENOMEM; + } + + num_names = of_property_count_strings(np, "pinctrl-names"); + if (num_names < 0) { + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + num_names); + return num_names; + } + + mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, + sizeof(*mux->pdata->pinctrl_states) * num_names, + GFP_KERNEL); + if (!mux->pdata->pinctrl_states) { + dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); + return -ENOMEM; + } + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "pinctrl-names", i, + &mux->pdata->pinctrl_states[mux->pdata->bus_count]); + if (ret < 0) { + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + ret); + return ret; + } + if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], + "idle")) { + if (i != num_names - 1) { + dev_err(mux->dev, "idle state must be last\n"); + return -EINVAL; + } + mux->pdata->pinctrl_state_idle = "idle"; + } else { + mux->pdata->bus_count++; + } + } + + adapter_np = of_parse_phandle(np, "i2c-parent", 0); + if (!adapter_np) { + dev_err(mux->dev, "Cannot parse i2c-parent\n"); + return -ENODEV; + } + adapter = of_find_i2c_adapter_by_node(adapter_np); + if (!adapter) { + dev_err(mux->dev, "Cannot find parent bus\n"); + return -EPROBE_DEFER; + } + mux->pdata->parent_bus_num = i2c_adapter_id(adapter); + put_device(&adapter->dev); + + return 0; +} +#else +static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, + struct platform_device *pdev) +{ + return 0; +} +#endif + +static int i2c_mux_pinctrl_probe(struct platform_device *pdev) +{ + struct i2c_mux_pinctrl *mux; + int (*deselect)(struct i2c_adapter *, void *, u32); + int i, ret; + + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); + if (!mux) { + dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n"); + ret = -ENOMEM; + goto err; + } + platform_set_drvdata(pdev, mux); + + mux->dev = &pdev->dev; + + mux->pdata = dev_get_platdata(&pdev->dev); + if (!mux->pdata) { + ret = i2c_mux_pinctrl_parse_dt(mux, pdev); + if (ret < 0) + goto err; + } + if (!mux->pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + ret = -ENODEV; + goto err; + } + + mux->states = devm_kzalloc(&pdev->dev, + sizeof(*mux->states) * mux->pdata->bus_count, + GFP_KERNEL); + if (!mux->states) { + dev_err(&pdev->dev, "Cannot allocate states\n"); + ret = -ENOMEM; + goto err; + } + + mux->busses = devm_kzalloc(&pdev->dev, + sizeof(*mux->busses) * mux->pdata->bus_count, + GFP_KERNEL); + if (!mux->busses) { + dev_err(&pdev->dev, "Cannot allocate busses\n"); + ret = -ENOMEM; + goto err; + } + + mux->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(mux->pinctrl)) { + ret = PTR_ERR(mux->pinctrl); + dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); + goto err; + } + for (i = 0; i < mux->pdata->bus_count; i++) { + mux->states[i] = pinctrl_lookup_state(mux->pinctrl, + mux->pdata->pinctrl_states[i]); + if (IS_ERR(mux->states[i])) { + ret = PTR_ERR(mux->states[i]); + dev_err(&pdev->dev, + "Cannot look up pinctrl state %s: %d\n", + mux->pdata->pinctrl_states[i], ret); + goto err; + } + } + if (mux->pdata->pinctrl_state_idle) { + mux->state_idle = pinctrl_lookup_state(mux->pinctrl, + mux->pdata->pinctrl_state_idle); + if (IS_ERR(mux->state_idle)) { + ret = PTR_ERR(mux->state_idle); + dev_err(&pdev->dev, + "Cannot look up pinctrl state %s: %d\n", + mux->pdata->pinctrl_state_idle, ret); + goto err; + } + + deselect = i2c_mux_pinctrl_deselect; + } else { + deselect = NULL; + } + + mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num); + if (!mux->parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + mux->pdata->parent_bus_num); + ret = -EPROBE_DEFER; + goto err; + } + + for (i = 0; i < mux->pdata->bus_count; i++) { + u32 bus = mux->pdata->base_bus_num ? + (mux->pdata->base_bus_num + i) : 0; + + mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, + mux, bus, i, 0, + i2c_mux_pinctrl_select, + deselect); + if (!mux->busses[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto err_del_adapter; + } + } + + return 0; + +err_del_adapter: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->busses[i - 1]); + i2c_put_adapter(mux->parent); +err: + return ret; +} + +static int i2c_mux_pinctrl_remove(struct platform_device *pdev) +{ + struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < mux->pdata->bus_count; i++) + i2c_del_mux_adapter(mux->busses[i]); + + i2c_put_adapter(mux->parent); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_mux_pinctrl_of_match[] = { + { .compatible = "i2c-mux-pinctrl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); +#endif + +static struct platform_driver i2c_mux_pinctrl_driver = { + .driver = { + .name = "i2c-mux-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), + }, + .probe = i2c_mux_pinctrl_probe, + .remove = i2c_mux_pinctrl_remove, +}; +module_platform_driver(i2c_mux_pinctrl_driver); + +MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-mux-pinctrl"); |
