2019-06-04 11:11:33 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2011-01-11 00:11:23 +03:00
|
|
|
/*
|
|
|
|
* I2C multiplexer using GPIO API
|
|
|
|
*
|
|
|
|
* Peter Korsgaard <peter.korsgaard@barco.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/i2c-mux.h>
|
2018-04-19 23:00:08 +03:00
|
|
|
#include <linux/platform_data/i2c-mux-gpio.h>
|
2011-01-11 00:11:23 +03:00
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
2019-06-18 13:58:33 +03:00
|
|
|
#include <linux/bits.h>
|
|
|
|
#include <linux/gpio/consumer.h>
|
|
|
|
/* FIXME: stop poking around inside gpiolib */
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
#include "../../gpio/gpiolib.h"
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
struct gpiomux {
|
2012-04-28 17:32:06 +04:00
|
|
|
struct i2c_mux_gpio_platform_data data;
|
2019-06-18 13:58:33 +03:00
|
|
|
int ngpios;
|
2016-11-24 21:19:28 +03:00
|
|
|
struct gpio_desc **gpios;
|
2011-01-11 00:11:23 +03:00
|
|
|
};
|
|
|
|
|
2012-04-28 17:32:06 +04:00
|
|
|
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
2011-01-11 00:11:23 +03:00
|
|
|
{
|
2018-09-06 00:50:05 +03:00
|
|
|
DECLARE_BITMAP(values, BITS_PER_TYPE(val));
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2018-09-06 00:50:05 +03:00
|
|
|
values[0] = val;
|
2016-11-24 21:19:28 +03:00
|
|
|
|
2019-06-18 13:58:33 +03:00
|
|
|
gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values);
|
2011-01-11 00:11:23 +03:00
|
|
|
}
|
|
|
|
|
2016-04-20 09:39:05 +03:00
|
|
|
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
2011-01-11 00:11:23 +03:00
|
|
|
{
|
2016-04-20 09:39:05 +03:00
|
|
|
struct gpiomux *mux = i2c_mux_priv(muxc);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2013-10-11 14:09:57 +04:00
|
|
|
i2c_mux_gpio_set(mux, chan);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-20 09:39:05 +03:00
|
|
|
static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
2011-01-11 00:11:23 +03:00
|
|
|
{
|
2016-04-20 09:39:05 +03:00
|
|
|
struct gpiomux *mux = i2c_mux_priv(muxc);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2012-04-28 17:32:06 +04:00
|
|
|
i2c_mux_gpio_set(mux, mux->data.idle);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
#ifdef CONFIG_ACPI
|
|
|
|
|
|
|
|
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
|
|
|
struct fwnode_handle *fwdev,
|
|
|
|
unsigned int *adr)
|
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
{
|
2020-11-19 02:40:25 +03:00
|
|
|
unsigned long long adr64;
|
|
|
|
acpi_status status;
|
|
|
|
|
|
|
|
status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
|
|
|
|
METHOD_NAME__ADR,
|
|
|
|
NULL, &adr64);
|
|
|
|
|
|
|
|
if (!ACPI_SUCCESS(status)) {
|
|
|
|
dev_err(dev, "Cannot get address\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*adr = adr64;
|
|
|
|
if (*adr != adr64) {
|
|
|
|
dev_err(dev, "Address out of range\n");
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
|
|
|
struct fwnode_handle *fwdev,
|
|
|
|
unsigned int *adr)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-10-25 20:23:53 +04:00
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
#endif
|
2012-10-25 20:23:53 +04:00
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
|
|
|
struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct device_node *np = dev->of_node;
|
|
|
|
struct device_node *adapter_np;
|
|
|
|
struct i2c_adapter *adapter = NULL;
|
|
|
|
struct fwnode_handle *child;
|
|
|
|
unsigned *values;
|
|
|
|
int rc, i = 0;
|
|
|
|
|
|
|
|
if (is_of_node(dev->fwnode)) {
|
|
|
|
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);
|
|
|
|
of_node_put(adapter_np);
|
|
|
|
|
|
|
|
} else if (is_acpi_node(dev->fwnode)) {
|
|
|
|
/*
|
|
|
|
* In ACPI land the mux should be a direct child of the i2c
|
|
|
|
* bus it muxes.
|
|
|
|
*/
|
|
|
|
acpi_handle dev_handle = ACPI_HANDLE(dev->parent);
|
|
|
|
|
|
|
|
adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
|
2012-10-25 20:23:53 +04:00
|
|
|
}
|
2020-11-19 02:40:25 +03:00
|
|
|
|
2015-03-30 16:03:38 +03:00
|
|
|
if (!adapter)
|
2013-10-09 13:50:45 +04:00
|
|
|
return -EPROBE_DEFER;
|
2015-03-30 16:03:38 +03:00
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
mux->data.parent = i2c_adapter_id(adapter);
|
|
|
|
put_device(&adapter->dev);
|
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
mux->data.n_values = device_get_child_node_count(dev);
|
2020-11-19 02:40:24 +03:00
|
|
|
values = devm_kcalloc(dev,
|
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
|
|
|
mux->data.n_values, sizeof(*mux->data.values),
|
2012-10-25 20:23:53 +04:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!values) {
|
2020-11-19 02:40:24 +03:00
|
|
|
dev_err(dev, "Cannot allocate values array");
|
2012-10-25 20:23:53 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
device_for_each_child_node(dev, child) {
|
|
|
|
if (is_of_node(child)) {
|
|
|
|
fwnode_property_read_u32(child, "reg", values + i);
|
|
|
|
|
|
|
|
} else if (is_acpi_node(child)) {
|
|
|
|
rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
mux->data.values = values;
|
|
|
|
|
2020-11-19 02:40:25 +03:00
|
|
|
if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
|
2012-10-25 20:23:53 +04:00
|
|
|
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-28 00:59:38 +04:00
|
|
|
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
2011-01-11 00:11:23 +03:00
|
|
|
{
|
2016-04-20 09:39:05 +03:00
|
|
|
struct i2c_mux_core *muxc;
|
2011-01-11 00:11:23 +03:00
|
|
|
struct gpiomux *mux;
|
|
|
|
struct i2c_adapter *parent;
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
struct i2c_adapter *root;
|
2019-06-18 13:58:33 +03:00
|
|
|
unsigned initial_state;
|
|
|
|
int i, ngpios, ret;
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
2016-04-20 09:39:05 +03:00
|
|
|
if (!mux)
|
2012-10-25 20:23:53 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-07-30 11:59:33 +04:00
|
|
|
if (!dev_get_platdata(&pdev->dev)) {
|
2020-11-19 02:40:25 +03:00
|
|
|
ret = i2c_mux_gpio_probe_fw(mux, pdev);
|
2012-10-25 20:23:53 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2013-07-30 11:59:33 +04:00
|
|
|
} else {
|
|
|
|
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
|
|
|
sizeof(mux->data));
|
|
|
|
}
|
2012-10-25 20:23:53 +04:00
|
|
|
|
2019-06-18 13:58:33 +03:00
|
|
|
ngpios = gpiod_count(&pdev->dev, "mux");
|
|
|
|
if (ngpios <= 0) {
|
|
|
|
dev_err(&pdev->dev, "no valid gpios provided\n");
|
|
|
|
return ngpios ?: -EINVAL;
|
2012-10-06 00:23:54 +04:00
|
|
|
}
|
2019-06-18 13:58:33 +03:00
|
|
|
mux->ngpios = ngpios;
|
2012-10-06 00:23:54 +04:00
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
parent = i2c_get_adapter(mux->data.parent);
|
2015-03-30 16:03:38 +03:00
|
|
|
if (!parent)
|
2013-10-09 13:50:45 +04:00
|
|
|
return -EPROBE_DEFER;
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2016-11-24 21:19:28 +03:00
|
|
|
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
2019-06-18 13:58:33 +03:00
|
|
|
ngpios * sizeof(*mux->gpios), 0,
|
2016-04-20 09:39:05 +03:00
|
|
|
i2c_mux_gpio_select, NULL);
|
|
|
|
if (!muxc) {
|
2011-01-11 00:11:23 +03:00
|
|
|
ret = -ENOMEM;
|
2012-10-06 00:23:53 +04:00
|
|
|
goto alloc_failed;
|
2011-01-11 00:11:23 +03:00
|
|
|
}
|
2016-11-24 21:19:28 +03:00
|
|
|
mux->gpios = muxc->priv;
|
2016-04-20 09:39:05 +03:00
|
|
|
muxc->priv = mux;
|
|
|
|
|
|
|
|
platform_set_drvdata(pdev, muxc);
|
|
|
|
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
root = i2c_root_adapter(&parent->dev);
|
|
|
|
|
|
|
|
muxc->mux_locked = true;
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
|
|
|
initial_state = mux->data.idle;
|
2016-04-20 09:39:05 +03:00
|
|
|
muxc->deselect = i2c_mux_gpio_deselect;
|
2011-01-11 00:11:23 +03:00
|
|
|
} else {
|
2012-10-25 20:23:53 +04:00
|
|
|
initial_state = mux->data.values[0];
|
2011-01-11 00:11:23 +03:00
|
|
|
}
|
|
|
|
|
2019-06-18 13:58:33 +03:00
|
|
|
for (i = 0; i < ngpios; i++) {
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
struct device *gpio_dev;
|
2019-06-18 13:58:33 +03:00
|
|
|
struct gpio_desc *gpiod;
|
|
|
|
enum gpiod_flags flag;
|
|
|
|
|
|
|
|
if (initial_state & BIT(i))
|
|
|
|
flag = GPIOD_OUT_HIGH;
|
|
|
|
else
|
|
|
|
flag = GPIOD_OUT_LOW;
|
|
|
|
gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag);
|
|
|
|
if (IS_ERR(gpiod)) {
|
|
|
|
ret = PTR_ERR(gpiod);
|
|
|
|
goto alloc_failed;
|
2013-03-07 02:35:53 +04:00
|
|
|
}
|
|
|
|
|
2019-06-18 13:58:33 +03:00
|
|
|
mux->gpios[i] = gpiod;
|
2016-11-24 21:19:28 +03:00
|
|
|
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
if (!muxc->mux_locked)
|
|
|
|
continue;
|
|
|
|
|
2019-06-18 13:58:33 +03:00
|
|
|
/* FIXME: find a proper way to access the GPIO device */
|
|
|
|
gpio_dev = &gpiod->gdev->dev;
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
|
2011-01-11 00:11:23 +03:00
|
|
|
}
|
|
|
|
|
i2c: mux: relax locking of the top i2c adapter during mux-locked muxing
With a i2c topology like the following
GPIO ---| ------ BAT1
| v /
I2C -----+----------+---- MUX
| \
EEPROM ------ BAT2
there is a locking problem with the GPIO controller since it is a client
on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1)
will lock the whole i2c bus prior to attempting to switch the mux to the
correct i2c segment. In the above case, the GPIO device is an I/O expander
with an i2c interface, and since the GPIO subsystem knows nothing (and
rightfully so) about the lockless needs of the i2c mux code, this results
in a deadlock when the GPIO driver issues i2c transfers to modify the
mux.
So, observing that while it is needed to have the i2c bus locked during the
actual MUX update in order to avoid random garbage on the slave side, it
is not strictly a must to have it locked over the whole sequence of a full
select-transfer-deselect mux client operation. The mux itself needs to be
locked, so transfers to clients behind the mux are serialized, and the mux
needs to be stable during all i2c traffic (otherwise individual mux slave
segments might see garbage, or worse).
Introduce this new locking concept as "mux-locked" muxes, and call the
pre-existing mux locking scheme "parent-locked".
Modify the i2c mux locking so that muxes that are "mux-locked" locks only
the muxes on the parent adapter instead of the whole i2c bus when there is
a transfer to the slave side of the mux. This lock serializes transfers to
the slave side of the muxes on the parent adapter.
Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved
gpio/pinctrl devices have a parent that is an i2c adapter in the same
adapter tree that is muxed, and request a "mux-locked mux" if that is the
case.
Modify the select-transfer-deselect code for "mux-locked" muxes so
that each of the select-transfer-deselect ops locks the mux parent
adapter individually.
Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2016-05-04 23:15:29 +03:00
|
|
|
if (muxc->mux_locked)
|
|
|
|
dev_info(&pdev->dev, "mux-locked i2c mux\n");
|
|
|
|
|
2012-10-25 20:23:53 +04:00
|
|
|
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;
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2016-04-20 09:39:05 +03:00
|
|
|
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
|
2017-04-03 11:14:11 +03:00
|
|
|
if (ret)
|
2011-01-11 00:11:23 +03:00
|
|
|
goto add_adapter_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_info(&pdev->dev, "%d port mux on %s adapter\n",
|
2012-10-25 20:23:53 +04:00
|
|
|
mux->data.n_values, parent->name);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
add_adapter_failed:
|
2016-04-20 09:39:05 +03:00
|
|
|
i2c_mux_del_adapters(muxc);
|
2011-01-11 00:11:23 +03:00
|
|
|
alloc_failed:
|
|
|
|
i2c_put_adapter(parent);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-28 00:59:38 +04:00
|
|
|
static int i2c_mux_gpio_remove(struct platform_device *pdev)
|
2011-01-11 00:11:23 +03:00
|
|
|
{
|
2016-04-20 09:39:05 +03:00
|
|
|
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
2016-04-20 09:39:05 +03:00
|
|
|
i2c_mux_del_adapters(muxc);
|
|
|
|
i2c_put_adapter(muxc->parent);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-28 00:59:38 +04:00
|
|
|
static const struct of_device_id i2c_mux_gpio_of_match[] = {
|
2012-10-25 20:23:53 +04:00
|
|
|
{ .compatible = "i2c-mux-gpio", },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match);
|
|
|
|
|
2012-04-28 17:32:06 +04:00
|
|
|
static struct platform_driver i2c_mux_gpio_driver = {
|
|
|
|
.probe = i2c_mux_gpio_probe,
|
2012-11-28 00:59:38 +04:00
|
|
|
.remove = i2c_mux_gpio_remove,
|
2011-01-11 00:11:23 +03:00
|
|
|
.driver = {
|
2012-04-28 17:32:06 +04:00
|
|
|
.name = "i2c-mux-gpio",
|
2013-09-30 07:34:25 +04:00
|
|
|
.of_match_table = i2c_mux_gpio_of_match,
|
2011-01-11 00:11:23 +03:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2012-04-28 17:32:06 +04:00
|
|
|
module_platform_driver(i2c_mux_gpio_driver);
|
2011-01-11 00:11:23 +03:00
|
|
|
|
|
|
|
MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver");
|
|
|
|
MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|
2012-04-28 17:32:06 +04:00
|
|
|
MODULE_ALIAS("platform:i2c-mux-gpio");
|