diff options
Diffstat (limited to 'drivers/i2c/muxes')
| -rw-r--r-- | drivers/i2c/muxes/Kconfig | 43 | ||||
| -rw-r--r-- | drivers/i2c/muxes/Makefile | 8 | ||||
| -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) | 73 | ||||
| -rw-r--r-- | drivers/i2c/muxes/i2c-mux-pinctrl.c | 278 | 
7 files changed, 903 insertions, 65 deletions
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 4d91d80bfd2..f6d313e528d 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -5,24 +5,59 @@  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 GPIOLIB +	help +	  If you say yes to this option, support will be included for a +	  GPIO based I2C multiplexer. This driver provides access to +	  I2C busses connected through a MUX, which is controlled +	  through GPIO pins. + +	  This driver can also be built as a module.  If so, the module +	  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 d743806d9b4..465778b5d5d 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -1,7 +1,11 @@  #  # Makefile for multiplexer I2C chip drivers. -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/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 54e1ce73534..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,28 +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); -	/* Read the mux register at addr to verify -	 * that the mux is in fact present. +	/* 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_read_byte(client) < 0) { +	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; @@ -215,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); @@ -249,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;  } @@ -259,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;  } @@ -283,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");  | 
