i2c: mux: Add dt support to i2c-mux-gpio driver
Allow the i2c-mux-gpio to be used by a device tree enabled device. The bindings are inspired by the one found in the i2c-mux-pinctrl driver. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Reviewed-by: Stephen Warren <swarren@nvidia.com> Acked-by: Peter Korsgaard <peter.korsgaard@barco.com> [wsa: fixed some whitespace] Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
This commit is contained in:
Родитель
29fb08c300
Коммит
9b7a0c40de
|
@ -0,0 +1,81 @@
|
|||
GPIO-based I2C Bus Mux
|
||||
|
||||
This binding describes an I2C bus multiplexer that uses GPIOs to
|
||||
route the I2C signals.
|
||||
|
||||
+-----+ +-----+
|
||||
| dev | | dev |
|
||||
+------------+ +-----+ +-----+
|
||||
| SoC | | |
|
||||
| | /--------+--------+
|
||||
| +------+ | +------+ child bus A, on GPIO value set to 0
|
||||
| | I2C |-|--| Mux |
|
||||
| +------+ | +--+---+ child bus B, on GPIO value set to 1
|
||||
| | | \----------+--------+--------+
|
||||
| +------+ | | | | |
|
||||
| | GPIO |-|-----+ +-----+ +-----+ +-----+
|
||||
| +------+ | | dev | | dev | | dev |
|
||||
+------------+ +-----+ +-----+ +-----+
|
||||
|
||||
Required properties:
|
||||
- compatible: i2c-mux-gpio
|
||||
- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
|
||||
port is connected to.
|
||||
- mux-gpios: list of gpios used to control the muxer
|
||||
* Standard I2C mux properties. See mux.txt in this directory.
|
||||
* I2C child bus nodes. See mux.txt in this directory.
|
||||
|
||||
Optional properties:
|
||||
- idle-state: value to set the muxer to when idle. When no value is
|
||||
given, it defaults to the last value used.
|
||||
|
||||
For each i2c child node, an I2C child bus will be created. They will
|
||||
be numbered based on their order in the device tree.
|
||||
|
||||
Whenever an access is made to a device on a child bus, the value set
|
||||
in the revelant node's reg property will be output using the list of
|
||||
GPIOs, the first in the list holding the least-significant value.
|
||||
|
||||
If an idle state is defined, using the idle-state (optional) property,
|
||||
whenever an access is not being made to a device on a child bus, the
|
||||
GPIOs will be set according to the idle value.
|
||||
|
||||
If an idle state is not defined, the most recently used value will be
|
||||
left programmed into hardware whenever no access is being made to a
|
||||
device on a child bus.
|
||||
|
||||
Example:
|
||||
i2cmux {
|
||||
compatible = "i2c-mux-gpio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mux-gpios = <&gpio1 22 0 &gpio1 23 0>;
|
||||
i2c-parent = <&i2c1>;
|
||||
|
||||
i2c@1 {
|
||||
reg = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ssd1307: oled@3c {
|
||||
compatible = "solomon,ssd1307fb-i2c";
|
||||
reg = <0x3c>;
|
||||
pwms = <&pwm 4 3000>;
|
||||
reset-gpios = <&gpio2 7 1>;
|
||||
reset-active-low;
|
||||
};
|
||||
};
|
||||
|
||||
i2c@3 {
|
||||
reg = <3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pca9555: pca9555@20 {
|
||||
compatible = "nxp,pca9555";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
reg = <0x20>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_i2c.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
struct gpiomux {
|
||||
struct i2c_adapter *parent;
|
||||
|
@ -57,29 +59,110 @@ static int __devinit match_gpio_chip_by_label(struct gpio_chip *chip,
|
|||
return !strcmp(chip->label, data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int __devinit 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;
|
||||
|
||||
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 -ENODEV;
|
||||
}
|
||||
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++)
|
||||
gpios[i] = of_get_named_gpio(np, "mux-gpios", i);
|
||||
|
||||
mux->data.gpios = gpios;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int __devinit i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpiomux *mux;
|
||||
struct i2c_mux_gpio_platform_data *pdata;
|
||||
struct i2c_adapter *parent;
|
||||
int (*deselect) (struct i2c_adapter *, void *, u32);
|
||||
unsigned initial_state, gpio_base;
|
||||
int i, ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Missing platform data\n");
|
||||
return -ENODEV;
|
||||
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 (!pdev->dev.platform_data) {
|
||||
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else
|
||||
memcpy(&mux->data, pdev->dev.platform_data, 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 (pdata->gpio_chip) {
|
||||
if (mux->data.gpio_chip) {
|
||||
struct gpio_chip *gpio;
|
||||
|
||||
gpio = gpiochip_find(pdata->gpio_chip,
|
||||
gpio = gpiochip_find(mux->data.gpio_chip,
|
||||
match_gpio_chip_by_label);
|
||||
if (!gpio)
|
||||
return -EPROBE_DEFER;
|
||||
|
@ -89,49 +172,44 @@ static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
gpio_base = 0;
|
||||
}
|
||||
|
||||
parent = i2c_get_adapter(pdata->parent);
|
||||
parent = i2c_get_adapter(mux->data.parent);
|
||||
if (!parent) {
|
||||
dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
|
||||
pdata->parent);
|
||||
mux->data.parent);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_failed;
|
||||
}
|
||||
|
||||
mux->parent = parent;
|
||||
mux->data = *pdata;
|
||||
mux->gpio_base = gpio_base;
|
||||
|
||||
mux->adap = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*mux->adap) * pdata->n_values,
|
||||
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 (pdata->idle != I2C_MUX_GPIO_NO_IDLE) {
|
||||
initial_state = pdata->idle;
|
||||
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
||||
initial_state = mux->data.idle;
|
||||
deselect = i2c_mux_gpio_deselect;
|
||||
} else {
|
||||
initial_state = pdata->values[0];
|
||||
initial_state = mux->data.values[0];
|
||||
deselect = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->n_gpios; i++) {
|
||||
ret = gpio_request(gpio_base + pdata->gpios[i], "i2c-mux-gpio");
|
||||
for (i = 0; i < mux->data.n_gpios; i++) {
|
||||
ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio");
|
||||
if (ret)
|
||||
goto err_request_gpio;
|
||||
gpio_direction_output(gpio_base + pdata->gpios[i],
|
||||
gpio_direction_output(gpio_base + mux->data.gpios[i],
|
||||
initial_state & (1 << i));
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->n_values; i++) {
|
||||
u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0;
|
||||
unsigned int class = pdata->classes ? pdata->classes[i] : 0;
|
||||
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,
|
||||
i, class,
|
||||
|
@ -144,19 +222,17 @@ static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
dev_info(&pdev->dev, "%d port mux on %s adapter\n",
|
||||
pdata->n_values, parent->name);
|
||||
|
||||
platform_set_drvdata(pdev, mux);
|
||||
mux->data.n_values, parent->name);
|
||||
|
||||
return 0;
|
||||
|
||||
add_adapter_failed:
|
||||
for (; i > 0; i--)
|
||||
i2c_del_mux_adapter(mux->adap[i - 1]);
|
||||
i = pdata->n_gpios;
|
||||
i = mux->data.n_gpios;
|
||||
err_request_gpio:
|
||||
for (; i > 0; i--)
|
||||
gpio_free(gpio_base + pdata->gpios[i - 1]);
|
||||
gpio_free(gpio_base + mux->data.gpios[i - 1]);
|
||||
alloc_failed:
|
||||
i2c_put_adapter(parent);
|
||||
|
||||
|
@ -180,12 +256,19 @@ static int __devexit i2c_mux_gpio_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_mux_gpio_of_match[] __devinitconst = {
|
||||
{ .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 = __devexit_p(i2c_mux_gpio_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "i2c-mux-gpio",
|
||||
.of_match_table = of_match_ptr(i2c_mux_gpio_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче