Commit ae58d1e406986f31d1e88b32f5ac601506c196d8

Authored by Stephen Warren
Committed by Wolfram Sang
1 parent f8f5701bda

i2c: Add generic I2C multiplexer using pinctrl API

This is useful for SoCs whose I2C module's signals can be routed to
different sets of pins at run-time, using the pinctrl API.

                                 +-----+  +-----+
                                 | dev |  | dev |
    +------------------------+   +-----+  +-----+
    | SoC                    |      |        |
    |                   /----|------+--------+
    |   +---+   +------+     | child bus A, on first set of pins
    |   |I2C|---|Pinmux|     |
    |   +---+   +------+     | child bus B, on second set of pins
    |                   \----|------+--------+--------+
    |                        |      |        |        |
    +------------------------+  +-----+  +-----+  +-----+
                                | dev |  | dev |  | dev |
                                +-----+  +-----+  +-----+

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>

Showing 5 changed files with 426 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt
  1 +Pinctrl-based I2C Bus Mux
  2 +
  3 +This binding describes an I2C bus multiplexer that uses pin multiplexing to
  4 +route the I2C signals, and represents the pin multiplexing configuration
  5 +using the pinctrl device tree bindings.
  6 +
  7 + +-----+ +-----+
  8 + | dev | | dev |
  9 + +------------------------+ +-----+ +-----+
  10 + | SoC | | |
  11 + | /----|------+--------+
  12 + | +---+ +------+ | child bus A, on first set of pins
  13 + | |I2C|---|Pinmux| |
  14 + | +---+ +------+ | child bus B, on second set of pins
  15 + | \----|------+--------+--------+
  16 + | | | | |
  17 + +------------------------+ +-----+ +-----+ +-----+
  18 + | dev | | dev | | dev |
  19 + +-----+ +-----+ +-----+
  20 +
  21 +Required properties:
  22 +- compatible: i2c-mux-pinctrl
  23 +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
  24 + port is connected to.
  25 +
  26 +Also required are:
  27 +
  28 +* Standard pinctrl properties that specify the pin mux state for each child
  29 + bus. See ../pinctrl/pinctrl-bindings.txt.
  30 +
  31 +* Standard I2C mux properties. See mux.txt in this directory.
  32 +
  33 +* I2C child bus nodes. See mux.txt in this directory.
  34 +
  35 +For each named state defined in the pinctrl-names property, an I2C child bus
  36 +will be created. I2C child bus numbers are assigned based on the index into
  37 +the pinctrl-names property.
  38 +
  39 +The only exception is that no bus will be created for a state named "idle". If
  40 +such a state is defined, it must be the last entry in pinctrl-names. For
  41 +example:
  42 +
  43 + pinctrl-names = "ddc", "pta", "idle" -> ddc = bus 0, pta = bus 1
  44 + pinctrl-names = "ddc", "idle", "pta" -> Invalid ("idle" not last)
  45 + pinctrl-names = "idle", "ddc", "pta" -> Invalid ("idle" not last)
  46 +
  47 +Whenever an access is made to a device on a child bus, the relevant pinctrl
  48 +state will be programmed into hardware.
  49 +
  50 +If an idle state is defined, whenever an access is not being made to a device
  51 +on a child bus, the idle pinctrl state will be programmed into hardware.
  52 +
  53 +If an idle state is not defined, the most recently used pinctrl state will be
  54 +left programmed into hardware whenever no access is being made of a device on
  55 +a child bus.
  56 +
  57 +Example:
  58 +
  59 + i2cmux {
  60 + compatible = "i2c-mux-pinctrl";
  61 + #address-cells = <1>;
  62 + #size-cells = <0>;
  63 +
  64 + i2c-parent = <&i2c1>;
  65 +
  66 + pinctrl-names = "ddc", "pta", "idle";
  67 + pinctrl-0 = <&state_i2cmux_ddc>;
  68 + pinctrl-1 = <&state_i2cmux_pta>;
  69 + pinctrl-2 = <&state_i2cmux_idle>;
  70 +
  71 + i2c@0 {
  72 + reg = <0>;
  73 + #address-cells = <1>;
  74 + #size-cells = <0>;
  75 +
  76 + eeprom {
  77 + compatible = "eeprom";
  78 + reg = <0x50>;
  79 + };
  80 + };
  81 +
  82 + i2c@1 {
  83 + reg = <1>;
  84 + #address-cells = <1>;
  85 + #size-cells = <0>;
  86 +
  87 + eeprom {
  88 + compatible = "eeprom";
  89 + reg = <0x50>;
  90 + };
  91 + };
  92 + };
drivers/i2c/muxes/Kconfig
... ... @@ -37,5 +37,17 @@
37 37 This driver can also be built as a module. If so, the module
38 38 will be called i2c-mux-pca954x.
39 39  
  40 +config I2C_MUX_PINCTRL
  41 + tristate "pinctrl-based I2C multiplexer"
  42 + depends on PINCTRL
  43 + help
  44 + If you say yes to this option, support will be included for an I2C
  45 + multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing.
  46 + This is useful for SoCs whose I2C module's signals can be routed to
  47 + different sets of pins at run-time.
  48 +
  49 + This driver can also be built as a module. If so, the module will be
  50 + called pinctrl-i2cmux.
  51 +
40 52 endmenu
drivers/i2c/muxes/Makefile
... ... @@ -4,6 +4,7 @@
4 4 obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
5 5 obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
6 6 obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
  7 +obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
7 8  
8 9 ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
drivers/i2c/muxes/i2c-mux-pinctrl.c
  1 +/*
  2 + * I2C multiplexer using pinctrl API
  3 + *
  4 + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify it
  7 + * under the terms and conditions of the GNU General Public License,
  8 + * version 2, as published by the Free Software Foundation.
  9 + *
  10 + * This program is distributed in the hope it will be useful, but WITHOUT
  11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13 + * more details.
  14 + *
  15 + * You should have received a copy of the GNU General Public License
  16 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17 + */
  18 +
  19 +#include <linux/i2c.h>
  20 +#include <linux/i2c-mux.h>
  21 +#include <linux/init.h>
  22 +#include <linux/module.h>
  23 +#include <linux/of_i2c.h>
  24 +#include <linux/pinctrl/consumer.h>
  25 +#include <linux/i2c-mux-pinctrl.h>
  26 +#include <linux/platform_device.h>
  27 +#include <linux/slab.h>
  28 +
  29 +struct i2c_mux_pinctrl {
  30 + struct device *dev;
  31 + struct i2c_mux_pinctrl_platform_data *pdata;
  32 + struct pinctrl *pinctrl;
  33 + struct pinctrl_state **states;
  34 + struct pinctrl_state *state_idle;
  35 + struct i2c_adapter *parent;
  36 + struct i2c_adapter **busses;
  37 +};
  38 +
  39 +static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data,
  40 + u32 chan)
  41 +{
  42 + struct i2c_mux_pinctrl *mux = data;
  43 +
  44 + return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
  45 +}
  46 +
  47 +static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data,
  48 + u32 chan)
  49 +{
  50 + struct i2c_mux_pinctrl *mux = data;
  51 +
  52 + return pinctrl_select_state(mux->pinctrl, mux->state_idle);
  53 +}
  54 +
  55 +#ifdef CONFIG_OF
  56 +static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
  57 + struct platform_device *pdev)
  58 +{
  59 + struct device_node *np = pdev->dev.of_node;
  60 + int num_names, i, ret;
  61 + struct device_node *adapter_np;
  62 + struct i2c_adapter *adapter;
  63 +
  64 + if (!np)
  65 + return 0;
  66 +
  67 + mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
  68 + if (!mux->pdata) {
  69 + dev_err(mux->dev,
  70 + "Cannot allocate i2c_mux_pinctrl_platform_data\n");
  71 + return -ENOMEM;
  72 + }
  73 +
  74 + num_names = of_property_count_strings(np, "pinctrl-names");
  75 + if (num_names < 0) {
  76 + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
  77 + num_names);
  78 + return num_names;
  79 + }
  80 +
  81 + mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
  82 + sizeof(*mux->pdata->pinctrl_states) * num_names,
  83 + GFP_KERNEL);
  84 + if (!mux->pdata->pinctrl_states) {
  85 + dev_err(mux->dev, "Cannot allocate pinctrl_states\n");
  86 + return -ENOMEM;
  87 + }
  88 +
  89 + for (i = 0; i < num_names; i++) {
  90 + ret = of_property_read_string_index(np, "pinctrl-names", i,
  91 + &mux->pdata->pinctrl_states[mux->pdata->bus_count]);
  92 + if (ret < 0) {
  93 + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
  94 + ret);
  95 + return ret;
  96 + }
  97 + if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
  98 + "idle")) {
  99 + if (i != num_names - 1) {
  100 + dev_err(mux->dev, "idle state must be last\n");
  101 + return -EINVAL;
  102 + }
  103 + mux->pdata->pinctrl_state_idle = "idle";
  104 + } else {
  105 + mux->pdata->bus_count++;
  106 + }
  107 + }
  108 +
  109 + adapter_np = of_parse_phandle(np, "i2c-parent", 0);
  110 + if (!adapter_np) {
  111 + dev_err(mux->dev, "Cannot parse i2c-parent\n");
  112 + return -ENODEV;
  113 + }
  114 + adapter = of_find_i2c_adapter_by_node(adapter_np);
  115 + if (!adapter) {
  116 + dev_err(mux->dev, "Cannot find parent bus\n");
  117 + return -ENODEV;
  118 + }
  119 + mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
  120 + put_device(&adapter->dev);
  121 +
  122 + return 0;
  123 +}
  124 +#else
  125 +static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
  126 + struct platform_device *pdev)
  127 +{
  128 + return 0;
  129 +}
  130 +#endif
  131 +
  132 +static int __devinit i2c_mux_pinctrl_probe(struct platform_device *pdev)
  133 +{
  134 + struct i2c_mux_pinctrl *mux;
  135 + int (*deselect)(struct i2c_adapter *, void *, u32);
  136 + int i, ret;
  137 +
  138 + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
  139 + if (!mux) {
  140 + dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n");
  141 + ret = -ENOMEM;
  142 + goto err;
  143 + }
  144 + platform_set_drvdata(pdev, mux);
  145 +
  146 + mux->dev = &pdev->dev;
  147 +
  148 + mux->pdata = pdev->dev.platform_data;
  149 + if (!mux->pdata) {
  150 + ret = i2c_mux_pinctrl_parse_dt(mux, pdev);
  151 + if (ret < 0)
  152 + goto err;
  153 + }
  154 + if (!mux->pdata) {
  155 + dev_err(&pdev->dev, "Missing platform data\n");
  156 + ret = -ENODEV;
  157 + goto err;
  158 + }
  159 +
  160 + mux->states = devm_kzalloc(&pdev->dev,
  161 + sizeof(*mux->states) * mux->pdata->bus_count,
  162 + GFP_KERNEL);
  163 + if (!mux->states) {
  164 + dev_err(&pdev->dev, "Cannot allocate states\n");
  165 + ret = -ENOMEM;
  166 + goto err;
  167 + }
  168 +
  169 + mux->busses = devm_kzalloc(&pdev->dev,
  170 + sizeof(mux->busses) * mux->pdata->bus_count,
  171 + GFP_KERNEL);
  172 + if (!mux->states) {
  173 + dev_err(&pdev->dev, "Cannot allocate busses\n");
  174 + ret = -ENOMEM;
  175 + goto err;
  176 + }
  177 +
  178 + mux->pinctrl = devm_pinctrl_get(&pdev->dev);
  179 + if (IS_ERR(mux->pinctrl)) {
  180 + ret = PTR_ERR(mux->pinctrl);
  181 + dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret);
  182 + goto err;
  183 + }
  184 + for (i = 0; i < mux->pdata->bus_count; i++) {
  185 + mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
  186 + mux->pdata->pinctrl_states[i]);
  187 + if (IS_ERR(mux->states[i])) {
  188 + ret = PTR_ERR(mux->states[i]);
  189 + dev_err(&pdev->dev,
  190 + "Cannot look up pinctrl state %s: %d\n",
  191 + mux->pdata->pinctrl_states[i], ret);
  192 + goto err;
  193 + }
  194 + }
  195 + if (mux->pdata->pinctrl_state_idle) {
  196 + mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
  197 + mux->pdata->pinctrl_state_idle);
  198 + if (IS_ERR(mux->state_idle)) {
  199 + ret = PTR_ERR(mux->state_idle);
  200 + dev_err(&pdev->dev,
  201 + "Cannot look up pinctrl state %s: %d\n",
  202 + mux->pdata->pinctrl_state_idle, ret);
  203 + goto err;
  204 + }
  205 +
  206 + deselect = i2c_mux_pinctrl_deselect;
  207 + } else {
  208 + deselect = NULL;
  209 + }
  210 +
  211 + mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
  212 + if (!mux->parent) {
  213 + dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
  214 + mux->pdata->parent_bus_num);
  215 + ret = -ENODEV;
  216 + goto err;
  217 + }
  218 +
  219 + for (i = 0; i < mux->pdata->bus_count; i++) {
  220 + u32 bus = mux->pdata->base_bus_num ?
  221 + (mux->pdata->base_bus_num + i) : 0;
  222 +
  223 + mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev,
  224 + mux, bus, i,
  225 + i2c_mux_pinctrl_select,
  226 + deselect);
  227 + if (!mux->busses[i]) {
  228 + ret = -ENODEV;
  229 + dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
  230 + goto err_del_adapter;
  231 + }
  232 + }
  233 +
  234 + return 0;
  235 +
  236 +err_del_adapter:
  237 + for (; i > 0; i--)
  238 + i2c_del_mux_adapter(mux->busses[i - 1]);
  239 + i2c_put_adapter(mux->parent);
  240 +err:
  241 + return ret;
  242 +}
  243 +
  244 +static int __devexit i2c_mux_pinctrl_remove(struct platform_device *pdev)
  245 +{
  246 + struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev);
  247 + int i;
  248 +
  249 + for (i = 0; i < mux->pdata->bus_count; i++)
  250 + i2c_del_mux_adapter(mux->busses[i]);
  251 +
  252 + i2c_put_adapter(mux->parent);
  253 +
  254 + return 0;
  255 +}
  256 +
  257 +#ifdef CONFIG_OF
  258 +static const struct of_device_id i2c_mux_pinctrl_of_match[] __devinitconst = {
  259 + { .compatible = "i2c-mux-pinctrl", },
  260 + {},
  261 +};
  262 +MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
  263 +#endif
  264 +
  265 +static struct platform_driver i2c_mux_pinctrl_driver = {
  266 + .driver = {
  267 + .name = "i2c-mux-pinctrl",
  268 + .owner = THIS_MODULE,
  269 + .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
  270 + },
  271 + .probe = i2c_mux_pinctrl_probe,
  272 + .remove = __devexit_p(i2c_mux_pinctrl_remove),
  273 +};
  274 +module_platform_driver(i2c_mux_pinctrl_driver);
  275 +
  276 +MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
  277 +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
  278 +MODULE_LICENSE("GPL v2");
  279 +MODULE_ALIAS("platform:i2c-mux-pinctrl");
include/linux/i2c-mux-pinctrl.h
  1 +/*
  2 + * i2c-mux-pinctrl platform data
  3 + *
  4 + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify it
  7 + * under the terms and conditions of the GNU General Public License,
  8 + * version 2, as published by the Free Software Foundation.
  9 + *
  10 + * This program is distributed in the hope it will be useful, but WITHOUT
  11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13 + * more details.
  14 + *
  15 + * You should have received a copy of the GNU General Public License
  16 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17 + */
  18 +
  19 +#ifndef _LINUX_I2C_MUX_PINCTRL_H
  20 +#define _LINUX_I2C_MUX_PINCTRL_H
  21 +
  22 +/**
  23 + * struct i2c_mux_pinctrl_platform_data - Platform data for i2c-mux-pinctrl
  24 + * @parent_bus_num: Parent I2C bus number
  25 + * @base_bus_num: Base I2C bus number for the child busses. 0 for dynamic.
  26 + * @bus_count: Number of child busses. Also the number of elements in
  27 + * @pinctrl_states
  28 + * @pinctrl_states: The names of the pinctrl state to select for each child bus
  29 + * @pinctrl_state_idle: The pinctrl state to select when no child bus is being
  30 + * accessed. If NULL, the most recently used pinctrl state will be left
  31 + * selected.
  32 + */
  33 +struct i2c_mux_pinctrl_platform_data {
  34 + int parent_bus_num;
  35 + int base_bus_num;
  36 + int bus_count;
  37 + const char **pinctrl_states;
  38 + const char *pinctrl_state_idle;
  39 +};
  40 +
  41 +#endif