diff --git a/patch/driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch b/patch/driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch new file mode 100644 index 000000000..e8df99cd8 --- /dev/null +++ b/patch/driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch @@ -0,0 +1,207 @@ +From f1fb64b04bf414ab04e31ac107bb28137105c5fd Mon Sep 17 00:00:00 2001 +From: Robert Shearman +Date: Thu, 28 Feb 2019 11:43:43 +0000 +Subject: [PATCH] i2c: mux: pca954x: allow management of device idle state via + sysfs + +The behaviour, by default, to not deselect after each transfer is +unsafe when there is a device with an address that conflicts with +another device on another mux on the same parent bus, and it +may not be convenient to use devicetree to set the deselect mux, +e.g. when running on x86_64 when ACPI is used to discover most of the +device hierarchy. + +Therefore, provide the ability to set the idle state behaviour using a +new sysfs file, idle_state as a complement to the method of +instantiating the device via sysfs. The possible behaviours are +disconnect, i.e. to deselect all channels from the mux, as-is (the +default), i.e. leave the last channel selected, and set a +predetermined channel. + +The current behaviour of leaving the channel as-is after each +transaction is preserved. + +Signed-off-by: Robert Shearman +Signed-off-by: Peter Rosin +--- + .../ABI/testing/sysfs-bus-i2c-devices-pca954x | 20 +++++ + drivers/i2c/muxes/i2c-mux-pca954x.c | 85 +++++++++++++++++-- + 2 files changed, 97 insertions(+), 8 deletions(-) + create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x + +diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x +new file mode 100644 +index 000000000000..0b0de8cd0d13 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x +@@ -0,0 +1,20 @@ ++What: /sys/bus/i2c/.../idle_state ++Date: January 2019 ++KernelVersion: 5.2 ++Contact: Robert Shearman ++Description: ++ Value that exists only for mux devices that can be ++ written to control the behaviour of the multiplexer on ++ idle. Possible values: ++ -2 - disconnect on idle, i.e. deselect the last used ++ channel, which is useful when there is a device ++ with an address that conflicts with another ++ device on another mux on the same parent bus. ++ -1 - leave the mux as-is, which is the most optimal ++ setting in terms of I2C operations and is the ++ default mode. ++ 0.. - set the mux to a predetermined channel, ++ which is useful if there is one channel that is ++ used almost always, and you want to reduce the ++ latency for normal operations after rare ++ transactions on other channels +diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c +index e32fef560684..923aa3a5a3dc 100644 +--- a/drivers/i2c/muxes/i2c-mux-pca954x.c ++++ b/drivers/i2c/muxes/i2c-mux-pca954x.c +@@ -49,6 +49,7 @@ + #include + #include + #include ++#include + + #define PCA954X_MAX_NCHANS 8 + +@@ -84,7 +85,9 @@ struct pca954x { + const struct chip_desc *chip; + + u8 last_chan; /* last register value */ +- u8 deselect; ++ /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */ ++ s8 idle_state; ++ + struct i2c_client *client; + + struct irq_domain *irq; +@@ -253,15 +256,71 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) + { + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; ++ s8 idle_state; ++ ++ idle_state = READ_ONCE(data->idle_state); ++ if (idle_state >= 0) ++ /* Set the mux back to a predetermined channel */ ++ return pca954x_select_chan(muxc, idle_state); ++ ++ if (idle_state == MUX_IDLE_DISCONNECT) { ++ /* Deselect active channel */ ++ data->last_chan = 0; ++ return pca954x_reg_write(muxc->parent, client, ++ data->last_chan); ++ } + +- if (!(data->deselect & (1 << chan))) +- return 0; ++ /* otherwise leave as-is */ + +- /* Deselect active channel */ +- data->last_chan = 0; +- return pca954x_reg_write(muxc->parent, client, data->last_chan); ++ return 0; ++} ++ ++static ssize_t idle_state_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct i2c_mux_core *muxc = i2c_get_clientdata(client); ++ struct pca954x *data = i2c_mux_priv(muxc); ++ ++ return sprintf(buf, "%d\n", READ_ONCE(data->idle_state)); ++} ++ ++static ssize_t idle_state_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct i2c_mux_core *muxc = i2c_get_clientdata(client); ++ struct pca954x *data = i2c_mux_priv(muxc); ++ int val; ++ int ret; ++ ++ ret = kstrtoint(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT && ++ (val < 0 || val >= data->chip->nchans)) ++ return -EINVAL; ++ ++ i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT); ++ ++ WRITE_ONCE(data->idle_state, val); ++ /* ++ * Set the mux into a state consistent with the new ++ * idle_state. ++ */ ++ if (data->last_chan || val != MUX_IDLE_DISCONNECT) ++ ret = pca954x_deselect_mux(muxc, 0); ++ ++ i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT); ++ ++ return ret < 0 ? ret : count; + } + ++static DEVICE_ATTR_RW(idle_state); ++ + static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) + { + struct pca954x *data = dev_id; +@@ -328,8 +387,11 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc) + static void pca954x_cleanup(struct i2c_mux_core *muxc) + { + struct pca954x *data = i2c_mux_priv(muxc); ++ struct i2c_client *client = data->client; + int c, irq; + ++ device_remove_file(&client->dev, &dev_attr_idle_state); ++ + if (data->irq) { + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_find_mapping(data->irq, c); +@@ -410,9 +472,12 @@ static int pca954x_probe(struct i2c_client *client, + } + + data->last_chan = 0; /* force the first selection */ ++ data->idle_state = MUX_IDLE_AS_IS; + + idle_disconnect_dt = np && + of_property_read_bool(np, "i2c-mux-idle-disconnect"); ++ if (idle_disconnect_dt) ++ data->idle_state = MUX_IDLE_DISCONNECT; + + ret = pca954x_irq_setup(muxc); + if (ret) +@@ -420,8 +485,6 @@ static int pca954x_probe(struct i2c_client *client, + + /* Now create an adapter for each channel */ + for (num = 0; num < data->chip->nchans; num++) { +- data->deselect |= idle_disconnect_dt << num; +- + ret = i2c_mux_add_adapter(muxc, 0, num, 0); + if (ret) + goto fail_cleanup; +@@ -436,6 +499,12 @@ static int pca954x_probe(struct i2c_client *client, + goto fail_cleanup; + } + ++ /* ++ * The attr probably isn't going to be needed in most cases, ++ * so don't fail completely on error. ++ */ ++ device_create_file(dev, &dev_attr_idle_state); ++ + dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n", + num, data->chip->muxtype == pca954x_ismux + ? "mux" : "switch", client->name); +-- +2.25.1 + diff --git a/patch/driver-i2c-mux-pca954x-remove-support-for-unused-platform-d.patch b/patch/driver-i2c-mux-pca954x-remove-support-for-unused-platform-d.patch new file mode 100644 index 000000000..4be7bdcee --- /dev/null +++ b/patch/driver-i2c-mux-pca954x-remove-support-for-unused-platform-d.patch @@ -0,0 +1,72 @@ +From ddd7c492d419eaf697733ba4dbad31662d355fc4 Mon Sep 17 00:00:00 2001 + +From: Robert Shearman + +Subject: [PATCH] i2c: mux: pca954x: remove support for unused platform data + +There are no in-tree users of the pca954x platform data and the +per-channel deselect configuration complicates efforts to export the +configuration to user-space in a way that could be applied to other +muxes. Therefore, remove support for the pca954x platform data. + +Signed-off-by: Robert Shearman +Signed-off-by: Peter Rosin +--- + drivers/i2c/muxes/i2c-mux-pca954x.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c +index 24bd9275f..a0b6d0c18 100644 +--- a/drivers/i2c/muxes/i2c-mux-pca954x.c ++++ b/drivers/i2c/muxes/i2c-mux-pca954x.c +@@ -46,7 +46,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -348,14 +347,13 @@ 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 = dev_get_platdata(&client->dev); + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + bool idle_disconnect_dt; + struct gpio_desc *gpio; +- int num, force, class; + struct i2c_mux_core *muxc; + struct pca954x *data; ++ int num; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) +@@ -422,24 +420,9 @@ static int pca954x_probe(struct i2c_client *client, + + /* Now create an adapter for each channel */ + for (num = 0; num < data->chip->nchans; num++) { +- bool idle_disconnect_pd = false; +- +- force = 0; /* dynamic adap number */ +- class = 0; /* no class by default */ +- if (pdata) { +- if (num < pdata->num_modes) { +- /* force static number */ +- force = pdata->modes[num].adap_id; +- class = pdata->modes[num].class; +- } else +- /* discard unconfigured channels */ +- break; +- idle_disconnect_pd = pdata->modes[num].deselect_on_exit; +- } +- data->deselect |= (idle_disconnect_pd || +- idle_disconnect_dt) << num; ++ data->deselect |= idle_disconnect_dt << num; + +- ret = i2c_mux_add_adapter(muxc, force, num, class); ++ ret = i2c_mux_add_adapter(muxc, 0, num, 0); + if (ret) + goto fail_cleanup; + } diff --git a/patch/driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch b/patch/driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch index 9743569b9..f0c4fff72 100644 --- a/patch/driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch +++ b/patch/driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch @@ -8,10 +8,10 @@ From: wadelnn 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c -index 24bd9275f..2906afaf5 100644 +index 431791d2f..f28d93c9c 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c -@@ -196,6 +196,11 @@ static const struct i2c_device_id pca954x_id[] = { +@@ -198,6 +198,11 @@ static const struct i2c_device_id pca954x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca954x_id); @@ -23,12 +23,12 @@ index 24bd9275f..2906afaf5 100644 #ifdef CONFIG_OF static const struct of_device_id pca954x_of_match[] = { { .compatible = "nxp,pca9540", .data = &chips[pca_9540] }, -@@ -437,7 +442,7 @@ static int pca954x_probe(struct i2c_client *client, - idle_disconnect_pd = pdata->modes[num].deselect_on_exit; - } - data->deselect |= (idle_disconnect_pd || -- idle_disconnect_dt) << num; -+ idle_disconnect_dt || force_deselect_on_exit) << num; +@@ -476,7 +481,7 @@ static int pca954x_probe(struct i2c_client *client, - ret = i2c_mux_add_adapter(muxc, force, num, class); - if (ret) + idle_disconnect_dt = np && + of_property_read_bool(np, "i2c-mux-idle-disconnect"); +- if (idle_disconnect_dt) ++ if (idle_disconnect_dt || force_deselect_on_exit) + data->idle_state = MUX_IDLE_DISCONNECT; + + ret = pca954x_irq_setup(muxc); diff --git a/patch/series b/patch/series index 0b3aea266..303a2f474 100755 --- a/patch/series +++ b/patch/series @@ -12,6 +12,8 @@ driver-sff-8436-use-nvmem_device_read.patch driver-support-sff-8436-read-write-fix.patch driver-i2c-bus-intel-ismt-add-delay-param.patch driver-i2c-ocores-5-1.patch +driver-i2c-mux-pca954x-remove-support-for-unused-platform-d.patch +driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch kernel-add-kexec-reboot-string.patch driver-hwmon-max6620.patch