Skip to content

Commit d65435e

Browse files
committed
backport i2c mux pca954x allow management of device idle state
driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch Signed-off-by: Guohan Lu <[email protected]>
1 parent 8eee9d8 commit d65435e

4 files changed

+291
-10
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
From f1fb64b04bf414ab04e31ac107bb28137105c5fd Mon Sep 17 00:00:00 2001
2+
From: Robert Shearman <[email protected]>
3+
Date: Thu, 28 Feb 2019 11:43:43 +0000
4+
Subject: [PATCH] i2c: mux: pca954x: allow management of device idle state via
5+
sysfs
6+
7+
The behaviour, by default, to not deselect after each transfer is
8+
unsafe when there is a device with an address that conflicts with
9+
another device on another mux on the same parent bus, and it
10+
may not be convenient to use devicetree to set the deselect mux,
11+
e.g. when running on x86_64 when ACPI is used to discover most of the
12+
device hierarchy.
13+
14+
Therefore, provide the ability to set the idle state behaviour using a
15+
new sysfs file, idle_state as a complement to the method of
16+
instantiating the device via sysfs. The possible behaviours are
17+
disconnect, i.e. to deselect all channels from the mux, as-is (the
18+
default), i.e. leave the last channel selected, and set a
19+
predetermined channel.
20+
21+
The current behaviour of leaving the channel as-is after each
22+
transaction is preserved.
23+
24+
Signed-off-by: Robert Shearman <[email protected]>
25+
Signed-off-by: Peter Rosin <[email protected]>
26+
---
27+
.../ABI/testing/sysfs-bus-i2c-devices-pca954x | 20 +++++
28+
drivers/i2c/muxes/i2c-mux-pca954x.c | 85 +++++++++++++++++--
29+
2 files changed, 97 insertions(+), 8 deletions(-)
30+
create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x
31+
32+
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x
33+
new file mode 100644
34+
index 000000000000..0b0de8cd0d13
35+
--- /dev/null
36+
+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x
37+
@@ -0,0 +1,20 @@
38+
+What: /sys/bus/i2c/.../idle_state
39+
+Date: January 2019
40+
+KernelVersion: 5.2
41+
+Contact: Robert Shearman <[email protected]>
42+
+Description:
43+
+ Value that exists only for mux devices that can be
44+
+ written to control the behaviour of the multiplexer on
45+
+ idle. Possible values:
46+
+ -2 - disconnect on idle, i.e. deselect the last used
47+
+ channel, which is useful when there is a device
48+
+ with an address that conflicts with another
49+
+ device on another mux on the same parent bus.
50+
+ -1 - leave the mux as-is, which is the most optimal
51+
+ setting in terms of I2C operations and is the
52+
+ default mode.
53+
+ 0..<nchans> - set the mux to a predetermined channel,
54+
+ which is useful if there is one channel that is
55+
+ used almost always, and you want to reduce the
56+
+ latency for normal operations after rare
57+
+ transactions on other channels
58+
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
59+
index e32fef560684..923aa3a5a3dc 100644
60+
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
61+
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
62+
@@ -49,6 +49,7 @@
63+
#include <linux/pm.h>
64+
#include <linux/slab.h>
65+
#include <linux/spinlock.h>
66+
+#include <dt-bindings/mux/mux.h>
67+
68+
#define PCA954X_MAX_NCHANS 8
69+
70+
@@ -84,7 +85,9 @@ struct pca954x {
71+
const struct chip_desc *chip;
72+
73+
u8 last_chan; /* last register value */
74+
- u8 deselect;
75+
+ /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
76+
+ s8 idle_state;
77+
+
78+
struct i2c_client *client;
79+
80+
struct irq_domain *irq;
81+
@@ -253,15 +256,71 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
82+
{
83+
struct pca954x *data = i2c_mux_priv(muxc);
84+
struct i2c_client *client = data->client;
85+
+ s8 idle_state;
86+
+
87+
+ idle_state = READ_ONCE(data->idle_state);
88+
+ if (idle_state >= 0)
89+
+ /* Set the mux back to a predetermined channel */
90+
+ return pca954x_select_chan(muxc, idle_state);
91+
+
92+
+ if (idle_state == MUX_IDLE_DISCONNECT) {
93+
+ /* Deselect active channel */
94+
+ data->last_chan = 0;
95+
+ return pca954x_reg_write(muxc->parent, client,
96+
+ data->last_chan);
97+
+ }
98+
99+
- if (!(data->deselect & (1 << chan)))
100+
- return 0;
101+
+ /* otherwise leave as-is */
102+
103+
- /* Deselect active channel */
104+
- data->last_chan = 0;
105+
- return pca954x_reg_write(muxc->parent, client, data->last_chan);
106+
+ return 0;
107+
+}
108+
+
109+
+static ssize_t idle_state_show(struct device *dev,
110+
+ struct device_attribute *attr,
111+
+ char *buf)
112+
+{
113+
+ struct i2c_client *client = to_i2c_client(dev);
114+
+ struct i2c_mux_core *muxc = i2c_get_clientdata(client);
115+
+ struct pca954x *data = i2c_mux_priv(muxc);
116+
+
117+
+ return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
118+
+}
119+
+
120+
+static ssize_t idle_state_store(struct device *dev,
121+
+ struct device_attribute *attr,
122+
+ const char *buf, size_t count)
123+
+{
124+
+ struct i2c_client *client = to_i2c_client(dev);
125+
+ struct i2c_mux_core *muxc = i2c_get_clientdata(client);
126+
+ struct pca954x *data = i2c_mux_priv(muxc);
127+
+ int val;
128+
+ int ret;
129+
+
130+
+ ret = kstrtoint(buf, 0, &val);
131+
+ if (ret < 0)
132+
+ return ret;
133+
+
134+
+ if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
135+
+ (val < 0 || val >= data->chip->nchans))
136+
+ return -EINVAL;
137+
+
138+
+ i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
139+
+
140+
+ WRITE_ONCE(data->idle_state, val);
141+
+ /*
142+
+ * Set the mux into a state consistent with the new
143+
+ * idle_state.
144+
+ */
145+
+ if (data->last_chan || val != MUX_IDLE_DISCONNECT)
146+
+ ret = pca954x_deselect_mux(muxc, 0);
147+
+
148+
+ i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
149+
+
150+
+ return ret < 0 ? ret : count;
151+
}
152+
153+
+static DEVICE_ATTR_RW(idle_state);
154+
+
155+
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
156+
{
157+
struct pca954x *data = dev_id;
158+
@@ -328,8 +387,11 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
159+
static void pca954x_cleanup(struct i2c_mux_core *muxc)
160+
{
161+
struct pca954x *data = i2c_mux_priv(muxc);
162+
+ struct i2c_client *client = data->client;
163+
int c, irq;
164+
165+
+ device_remove_file(&client->dev, &dev_attr_idle_state);
166+
+
167+
if (data->irq) {
168+
for (c = 0; c < data->chip->nchans; c++) {
169+
irq = irq_find_mapping(data->irq, c);
170+
@@ -410,9 +472,12 @@ static int pca954x_probe(struct i2c_client *client,
171+
}
172+
173+
data->last_chan = 0; /* force the first selection */
174+
+ data->idle_state = MUX_IDLE_AS_IS;
175+
176+
idle_disconnect_dt = np &&
177+
of_property_read_bool(np, "i2c-mux-idle-disconnect");
178+
+ if (idle_disconnect_dt)
179+
+ data->idle_state = MUX_IDLE_DISCONNECT;
180+
181+
ret = pca954x_irq_setup(muxc);
182+
if (ret)
183+
@@ -420,8 +485,6 @@ static int pca954x_probe(struct i2c_client *client,
184+
185+
/* Now create an adapter for each channel */
186+
for (num = 0; num < data->chip->nchans; num++) {
187+
- data->deselect |= idle_disconnect_dt << num;
188+
-
189+
ret = i2c_mux_add_adapter(muxc, 0, num, 0);
190+
if (ret)
191+
goto fail_cleanup;
192+
@@ -436,6 +499,12 @@ static int pca954x_probe(struct i2c_client *client,
193+
goto fail_cleanup;
194+
}
195+
196+
+ /*
197+
+ * The attr probably isn't going to be needed in most cases,
198+
+ * so don't fail completely on error.
199+
+ */
200+
+ device_create_file(dev, &dev_attr_idle_state);
201+
+
202+
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
203+
num, data->chip->muxtype == pca954x_ismux
204+
? "mux" : "switch", client->name);
205+
--
206+
2.25.1
207+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
From ddd7c492d419eaf697733ba4dbad31662d355fc4 Mon Sep 17 00:00:00 2001
2+
3+
From: Robert Shearman <[email protected]>
4+
5+
Subject: [PATCH] i2c: mux: pca954x: remove support for unused platform data
6+
7+
There are no in-tree users of the pca954x platform data and the
8+
per-channel deselect configuration complicates efforts to export the
9+
configuration to user-space in a way that could be applied to other
10+
muxes. Therefore, remove support for the pca954x platform data.
11+
12+
Signed-off-by: Robert Shearman <[email protected]>
13+
Signed-off-by: Peter Rosin <[email protected]>
14+
---
15+
drivers/i2c/muxes/i2c-mux-pca954x.c | 23 +++--------------------
16+
1 file changed, 3 insertions(+), 20 deletions(-)
17+
18+
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
19+
index 24bd9275f..a0b6d0c18 100644
20+
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
21+
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
22+
@@ -46,7 +46,6 @@
23+
#include <linux/of.h>
24+
#include <linux/of_device.h>
25+
#include <linux/of_irq.h>
26+
-#include <linux/platform_data/pca954x.h>
27+
#include <linux/pm.h>
28+
#include <linux/slab.h>
29+
#include <linux/spinlock.h>
30+
@@ -348,14 +347,13 @@ static int pca954x_probe(struct i2c_client *client,
31+
const struct i2c_device_id *id)
32+
{
33+
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
34+
- struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
35+
struct device *dev = &client->dev;
36+
struct device_node *np = dev->of_node;
37+
bool idle_disconnect_dt;
38+
struct gpio_desc *gpio;
39+
- int num, force, class;
40+
struct i2c_mux_core *muxc;
41+
struct pca954x *data;
42+
+ int num;
43+
int ret;
44+
45+
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
46+
@@ -422,24 +420,9 @@ static int pca954x_probe(struct i2c_client *client,
47+
48+
/* Now create an adapter for each channel */
49+
for (num = 0; num < data->chip->nchans; num++) {
50+
- bool idle_disconnect_pd = false;
51+
-
52+
- force = 0; /* dynamic adap number */
53+
- class = 0; /* no class by default */
54+
- if (pdata) {
55+
- if (num < pdata->num_modes) {
56+
- /* force static number */
57+
- force = pdata->modes[num].adap_id;
58+
- class = pdata->modes[num].class;
59+
- } else
60+
- /* discard unconfigured channels */
61+
- break;
62+
- idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
63+
- }
64+
- data->deselect |= (idle_disconnect_pd ||
65+
- idle_disconnect_dt) << num;
66+
+ data->deselect |= idle_disconnect_dt << num;
67+
68+
- ret = i2c_mux_add_adapter(muxc, force, num, class);
69+
+ ret = i2c_mux_add_adapter(muxc, 0, num, 0);
70+
if (ret)
71+
goto fail_cleanup;
72+
}

patch/driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ From: wadelnn <[email protected]>
88
1 file changed, 6 insertions(+), 1 deletion(-)
99

1010
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
11-
index 24bd9275f..2906afaf5 100644
11+
index 431791d2f..f28d93c9c 100644
1212
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
1313
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
14-
@@ -196,6 +196,11 @@ static const struct i2c_device_id pca954x_id[] = {
14+
@@ -198,6 +198,11 @@ static const struct i2c_device_id pca954x_id[] = {
1515
};
1616
MODULE_DEVICE_TABLE(i2c, pca954x_id);
1717

@@ -23,12 +23,12 @@ index 24bd9275f..2906afaf5 100644
2323
#ifdef CONFIG_OF
2424
static const struct of_device_id pca954x_of_match[] = {
2525
{ .compatible = "nxp,pca9540", .data = &chips[pca_9540] },
26-
@@ -437,7 +442,7 @@ static int pca954x_probe(struct i2c_client *client,
27-
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
28-
}
29-
data->deselect |= (idle_disconnect_pd ||
30-
- idle_disconnect_dt) << num;
31-
+ idle_disconnect_dt || force_deselect_on_exit) << num;
26+
@@ -476,7 +481,7 @@ static int pca954x_probe(struct i2c_client *client,
3227

33-
ret = i2c_mux_add_adapter(muxc, force, num, class);
34-
if (ret)
28+
idle_disconnect_dt = np &&
29+
of_property_read_bool(np, "i2c-mux-idle-disconnect");
30+
- if (idle_disconnect_dt)
31+
+ if (idle_disconnect_dt || force_deselect_on_exit)
32+
data->idle_state = MUX_IDLE_DISCONNECT;
33+
34+
ret = pca954x_irq_setup(muxc);

patch/series

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ driver-sff-8436-use-nvmem_device_read.patch
1515
driver-support-sff-8436-read-write-fix.patch
1616
driver-i2c-bus-intel-ismt-add-delay-param.patch
1717
driver-i2c-ocores-5-1.patch
18+
driver-i2c-mux-pca954x-remove-support-for-unused-platform-d.patch
19+
driver-i2c-mux-pca954x-allow-management-of-device-idle-stat.patch
1820
driver-pca954x-i2c-mux-force-deselect-on-exit-flag.patch
1921
kernel-add-kexec-reboot-string.patch
2022
driver-hwmon-max6620.patch

0 commit comments

Comments
 (0)