Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <robert.shearman@att.com>
Signed-off-by: Peter Rosin <peda@axentia.se>

authored by

Robert Shearman and committed by
Peter Rosin
f1fb64b0 590085f0

+97 -8
+20
Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x
··· 1 + What: /sys/bus/i2c/.../idle_state 2 + Date: January 2019 3 + KernelVersion: 5.2 4 + Contact: Robert Shearman <robert.shearman@att.com> 5 + Description: 6 + Value that exists only for mux devices that can be 7 + written to control the behaviour of the multiplexer on 8 + idle. Possible values: 9 + -2 - disconnect on idle, i.e. deselect the last used 10 + channel, which is useful when there is a device 11 + with an address that conflicts with another 12 + device on another mux on the same parent bus. 13 + -1 - leave the mux as-is, which is the most optimal 14 + setting in terms of I2C operations and is the 15 + default mode. 16 + 0..<nchans> - set the mux to a predetermined channel, 17 + which is useful if there is one channel that is 18 + used almost always, and you want to reduce the 19 + latency for normal operations after rare 20 + transactions on other channels
+77 -8
drivers/i2c/muxes/i2c-mux-pca954x.c
··· 49 49 #include <linux/pm.h> 50 50 #include <linux/slab.h> 51 51 #include <linux/spinlock.h> 52 + #include <dt-bindings/mux/mux.h> 52 53 53 54 #define PCA954X_MAX_NCHANS 8 54 55 ··· 85 84 const struct chip_desc *chip; 86 85 87 86 u8 last_chan; /* last register value */ 88 - u8 deselect; 87 + /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */ 88 + s8 idle_state; 89 + 89 90 struct i2c_client *client; 90 91 91 92 struct irq_domain *irq; ··· 256 253 { 257 254 struct pca954x *data = i2c_mux_priv(muxc); 258 255 struct i2c_client *client = data->client; 256 + s8 idle_state; 259 257 260 - if (!(data->deselect & (1 << chan))) 261 - return 0; 258 + idle_state = READ_ONCE(data->idle_state); 259 + if (idle_state >= 0) 260 + /* Set the mux back to a predetermined channel */ 261 + return pca954x_select_chan(muxc, idle_state); 262 262 263 - /* Deselect active channel */ 264 - data->last_chan = 0; 265 - return pca954x_reg_write(muxc->parent, client, data->last_chan); 263 + if (idle_state == MUX_IDLE_DISCONNECT) { 264 + /* Deselect active channel */ 265 + data->last_chan = 0; 266 + return pca954x_reg_write(muxc->parent, client, 267 + data->last_chan); 268 + } 269 + 270 + /* otherwise leave as-is */ 271 + 272 + return 0; 266 273 } 274 + 275 + static ssize_t idle_state_show(struct device *dev, 276 + struct device_attribute *attr, 277 + char *buf) 278 + { 279 + struct i2c_client *client = to_i2c_client(dev); 280 + struct i2c_mux_core *muxc = i2c_get_clientdata(client); 281 + struct pca954x *data = i2c_mux_priv(muxc); 282 + 283 + return sprintf(buf, "%d\n", READ_ONCE(data->idle_state)); 284 + } 285 + 286 + static ssize_t idle_state_store(struct device *dev, 287 + struct device_attribute *attr, 288 + const char *buf, size_t count) 289 + { 290 + struct i2c_client *client = to_i2c_client(dev); 291 + struct i2c_mux_core *muxc = i2c_get_clientdata(client); 292 + struct pca954x *data = i2c_mux_priv(muxc); 293 + int val; 294 + int ret; 295 + 296 + ret = kstrtoint(buf, 0, &val); 297 + if (ret < 0) 298 + return ret; 299 + 300 + if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT && 301 + (val < 0 || val >= data->chip->nchans)) 302 + return -EINVAL; 303 + 304 + i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT); 305 + 306 + WRITE_ONCE(data->idle_state, val); 307 + /* 308 + * Set the mux into a state consistent with the new 309 + * idle_state. 310 + */ 311 + if (data->last_chan || val != MUX_IDLE_DISCONNECT) 312 + ret = pca954x_deselect_mux(muxc, 0); 313 + 314 + i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT); 315 + 316 + return ret < 0 ? ret : count; 317 + } 318 + 319 + static DEVICE_ATTR_RW(idle_state); 267 320 268 321 static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) 269 322 { ··· 387 328 static void pca954x_cleanup(struct i2c_mux_core *muxc) 388 329 { 389 330 struct pca954x *data = i2c_mux_priv(muxc); 331 + struct i2c_client *client = data->client; 390 332 int c, irq; 333 + 334 + device_remove_file(&client->dev, &dev_attr_idle_state); 391 335 392 336 if (data->irq) { 393 337 for (c = 0; c < data->chip->nchans; c++) { ··· 472 410 } 473 411 474 412 data->last_chan = 0; /* force the first selection */ 413 + data->idle_state = MUX_IDLE_AS_IS; 475 414 476 415 idle_disconnect_dt = np && 477 416 of_property_read_bool(np, "i2c-mux-idle-disconnect"); 417 + if (idle_disconnect_dt) 418 + data->idle_state = MUX_IDLE_DISCONNECT; 478 419 479 420 ret = pca954x_irq_setup(muxc); 480 421 if (ret) ··· 485 420 486 421 /* Now create an adapter for each channel */ 487 422 for (num = 0; num < data->chip->nchans; num++) { 488 - data->deselect |= idle_disconnect_dt << num; 489 - 490 423 ret = i2c_mux_add_adapter(muxc, 0, num, 0); 491 424 if (ret) 492 425 goto fail_cleanup; ··· 498 435 if (ret) 499 436 goto fail_cleanup; 500 437 } 438 + 439 + /* 440 + * The attr probably isn't going to be needed in most cases, 441 + * so don't fail completely on error. 442 + */ 443 + device_create_file(dev, &dev_attr_idle_state); 501 444 502 445 dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n", 503 446 num, data->chip->muxtype == pca954x_ismux