linux/drivers/i2c/muxes/i2c-mux-gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * I2C multiplexer using GPIO API
   4 *
   5 * Peter Korsgaard <peter.korsgaard@barco.com>
   6 */
   7
   8#include <linux/i2c.h>
   9#include <linux/i2c-mux.h>
  10#include <linux/platform_data/i2c-mux-gpio.h>
  11#include <linux/platform_device.h>
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/bits.h>
  15#include <linux/gpio/consumer.h>
  16/* FIXME: stop poking around inside gpiolib */
  17#include "../../gpio/gpiolib.h"
  18
  19struct gpiomux {
  20        struct i2c_mux_gpio_platform_data data;
  21        int ngpios;
  22        struct gpio_desc **gpios;
  23};
  24
  25static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
  26{
  27        DECLARE_BITMAP(values, BITS_PER_TYPE(val));
  28
  29        values[0] = val;
  30
  31        gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values);
  32}
  33
  34static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
  35{
  36        struct gpiomux *mux = i2c_mux_priv(muxc);
  37
  38        i2c_mux_gpio_set(mux, chan);
  39
  40        return 0;
  41}
  42
  43static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
  44{
  45        struct gpiomux *mux = i2c_mux_priv(muxc);
  46
  47        i2c_mux_gpio_set(mux, mux->data.idle);
  48
  49        return 0;
  50}
  51
  52#ifdef CONFIG_OF
  53static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
  54                                        struct platform_device *pdev)
  55{
  56        struct device_node *np = pdev->dev.of_node;
  57        struct device_node *adapter_np, *child;
  58        struct i2c_adapter *adapter;
  59        unsigned *values;
  60        int i = 0;
  61
  62        if (!np)
  63                return -ENODEV;
  64
  65        adapter_np = of_parse_phandle(np, "i2c-parent", 0);
  66        if (!adapter_np) {
  67                dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
  68                return -ENODEV;
  69        }
  70        adapter = of_find_i2c_adapter_by_node(adapter_np);
  71        of_node_put(adapter_np);
  72        if (!adapter)
  73                return -EPROBE_DEFER;
  74
  75        mux->data.parent = i2c_adapter_id(adapter);
  76        put_device(&adapter->dev);
  77
  78        mux->data.n_values = of_get_child_count(np);
  79
  80        values = devm_kcalloc(&pdev->dev,
  81                              mux->data.n_values, sizeof(*mux->data.values),
  82                              GFP_KERNEL);
  83        if (!values) {
  84                dev_err(&pdev->dev, "Cannot allocate values array");
  85                return -ENOMEM;
  86        }
  87
  88        for_each_child_of_node(np, child) {
  89                of_property_read_u32(child, "reg", values + i);
  90                i++;
  91        }
  92        mux->data.values = values;
  93
  94        if (of_property_read_u32(np, "idle-state", &mux->data.idle))
  95                mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
  96
  97        return 0;
  98}
  99#else
 100static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
 101                                        struct platform_device *pdev)
 102{
 103        return 0;
 104}
 105#endif
 106
 107static int i2c_mux_gpio_probe(struct platform_device *pdev)
 108{
 109        struct i2c_mux_core *muxc;
 110        struct gpiomux *mux;
 111        struct i2c_adapter *parent;
 112        struct i2c_adapter *root;
 113        unsigned initial_state;
 114        int i, ngpios, ret;
 115
 116        mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
 117        if (!mux)
 118                return -ENOMEM;
 119
 120        if (!dev_get_platdata(&pdev->dev)) {
 121                ret = i2c_mux_gpio_probe_dt(mux, pdev);
 122                if (ret < 0)
 123                        return ret;
 124        } else {
 125                memcpy(&mux->data, dev_get_platdata(&pdev->dev),
 126                        sizeof(mux->data));
 127        }
 128
 129        ngpios = gpiod_count(&pdev->dev, "mux");
 130        if (ngpios <= 0) {
 131                dev_err(&pdev->dev, "no valid gpios provided\n");
 132                return ngpios ?: -EINVAL;
 133        }
 134        mux->ngpios = ngpios;
 135
 136        parent = i2c_get_adapter(mux->data.parent);
 137        if (!parent)
 138                return -EPROBE_DEFER;
 139
 140        muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
 141                             ngpios * sizeof(*mux->gpios), 0,
 142                             i2c_mux_gpio_select, NULL);
 143        if (!muxc) {
 144                ret = -ENOMEM;
 145                goto alloc_failed;
 146        }
 147        mux->gpios = muxc->priv;
 148        muxc->priv = mux;
 149
 150        platform_set_drvdata(pdev, muxc);
 151
 152        root = i2c_root_adapter(&parent->dev);
 153
 154        muxc->mux_locked = true;
 155
 156        if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
 157                initial_state = mux->data.idle;
 158                muxc->deselect = i2c_mux_gpio_deselect;
 159        } else {
 160                initial_state = mux->data.values[0];
 161        }
 162
 163        for (i = 0; i < ngpios; i++) {
 164                struct device *gpio_dev;
 165                struct gpio_desc *gpiod;
 166                enum gpiod_flags flag;
 167
 168                if (initial_state & BIT(i))
 169                        flag = GPIOD_OUT_HIGH;
 170                else
 171                        flag = GPIOD_OUT_LOW;
 172                gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag);
 173                if (IS_ERR(gpiod)) {
 174                        ret = PTR_ERR(gpiod);
 175                        goto alloc_failed;
 176                }
 177
 178                mux->gpios[i] = gpiod;
 179
 180                if (!muxc->mux_locked)
 181                        continue;
 182
 183                /* FIXME: find a proper way to access the GPIO device */
 184                gpio_dev = &gpiod->gdev->dev;
 185                muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
 186        }
 187
 188        if (muxc->mux_locked)
 189                dev_info(&pdev->dev, "mux-locked i2c mux\n");
 190
 191        for (i = 0; i < mux->data.n_values; i++) {
 192                u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
 193                unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
 194
 195                ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
 196                if (ret)
 197                        goto add_adapter_failed;
 198        }
 199
 200        dev_info(&pdev->dev, "%d port mux on %s adapter\n",
 201                 mux->data.n_values, parent->name);
 202
 203        return 0;
 204
 205add_adapter_failed:
 206        i2c_mux_del_adapters(muxc);
 207alloc_failed:
 208        i2c_put_adapter(parent);
 209
 210        return ret;
 211}
 212
 213static int i2c_mux_gpio_remove(struct platform_device *pdev)
 214{
 215        struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
 216
 217        i2c_mux_del_adapters(muxc);
 218        i2c_put_adapter(muxc->parent);
 219
 220        return 0;
 221}
 222
 223static const struct of_device_id i2c_mux_gpio_of_match[] = {
 224        { .compatible = "i2c-mux-gpio", },
 225        {},
 226};
 227MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match);
 228
 229static struct platform_driver i2c_mux_gpio_driver = {
 230        .probe  = i2c_mux_gpio_probe,
 231        .remove = i2c_mux_gpio_remove,
 232        .driver = {
 233                .name   = "i2c-mux-gpio",
 234                .of_match_table = i2c_mux_gpio_of_match,
 235        },
 236};
 237
 238module_platform_driver(i2c_mux_gpio_driver);
 239
 240MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver");
 241MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>");
 242MODULE_LICENSE("GPL");
 243MODULE_ALIAS("platform:i2c-mux-gpio");
 244