diff options
author | Wolfram Sang | 2020-01-29 21:59:20 +0100 |
---|---|---|
committer | Wolfram Sang | 2020-01-29 21:59:20 +0100 |
commit | 4a890148df4740a6fce25f9bd4a70d1587a8e8e8 (patch) | |
tree | 772b0d8b14b24085f1e59b256988bb419e583e95 /drivers/i2c | |
parent | 6810df46c41879eba8437ab020c7abf5d1db827e (diff) | |
parent | e65e228eb0963be32180db3039f0a1467b426aae (diff) |
Merge branch 'i2c-mux/for-next' of https://github.com/peda-r/i2c-mux into i2c/for-5.6
The main feature is the idle-state rework of the pca954x driver from Biwen Li.
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca9541.c | 29 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 69 |
2 files changed, 61 insertions, 37 deletions
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 50e1fb4aedf5..6daec8d3d331 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -16,6 +16,7 @@ * warranty of any kind, whether express or implied. */ +#include <linux/bitops.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/i2c.h> @@ -42,20 +43,20 @@ #define PCA9541_CONTROL 0x01 #define PCA9541_ISTAT 0x02 -#define PCA9541_CTL_MYBUS (1 << 0) -#define PCA9541_CTL_NMYBUS (1 << 1) -#define PCA9541_CTL_BUSON (1 << 2) -#define PCA9541_CTL_NBUSON (1 << 3) -#define PCA9541_CTL_BUSINIT (1 << 4) -#define PCA9541_CTL_TESTON (1 << 6) -#define PCA9541_CTL_NTESTON (1 << 7) - -#define PCA9541_ISTAT_INTIN (1 << 0) -#define PCA9541_ISTAT_BUSINIT (1 << 1) -#define PCA9541_ISTAT_BUSOK (1 << 2) -#define PCA9541_ISTAT_BUSLOST (1 << 3) -#define PCA9541_ISTAT_MYTEST (1 << 6) -#define PCA9541_ISTAT_NMYTEST (1 << 7) +#define PCA9541_CTL_MYBUS BIT(0) +#define PCA9541_CTL_NMYBUS BIT(1) +#define PCA9541_CTL_BUSON BIT(2) +#define PCA9541_CTL_NBUSON BIT(3) +#define PCA9541_CTL_BUSINIT BIT(4) +#define PCA9541_CTL_TESTON BIT(6) +#define PCA9541_CTL_NTESTON BIT(7) + +#define PCA9541_ISTAT_INTIN BIT(0) +#define PCA9541_ISTAT_BUSINIT BIT(1) +#define PCA9541_ISTAT_BUSOK BIT(2) +#define PCA9541_ISTAT_BUSLOST BIT(3) +#define PCA9541_ISTAT_MYTEST BIT(6) +#define PCA9541_ISTAT_NMYTEST BIT(7) #define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON) #define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 923aa3a5a3dc..a0d926ae3f86 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -86,7 +86,7 @@ struct pca954x { u8 last_chan; /* last register value */ /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */ - s8 idle_state; + s32 idle_state; struct i2c_client *client; @@ -229,20 +229,23 @@ static int pca954x_reg_write(struct i2c_adapter *adap, I2C_SMBUS_BYTE, &dummy); } +static u8 pca954x_regval(struct pca954x *data, u8 chan) +{ + /* We make switches look like muxes, not sure how to be smarter. */ + if (data->chip->muxtype == pca954x_ismux) + return chan | data->chip->enable; + else + return 1 << chan; +} + static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) { struct pca954x *data = i2c_mux_priv(muxc); struct i2c_client *client = data->client; - const struct chip_desc *chip = data->chip; u8 regval; int ret = 0; - /* we make switches look like muxes, not sure how to be smarter */ - if (chip->muxtype == pca954x_ismux) - regval = chan | chip->enable; - else - regval = 1 << chan; - + regval = pca954x_regval(data, chan); /* Only select the channel if its different from the last channel */ if (data->last_chan != regval) { ret = pca954x_reg_write(muxc->parent, client, regval); @@ -256,7 +259,7 @@ 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; + s32 idle_state; idle_state = READ_ONCE(data->idle_state); if (idle_state >= 0) @@ -402,6 +405,22 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc) i2c_mux_del_adapters(muxc); } +static int pca954x_init(struct i2c_client *client, struct pca954x *data) +{ + int ret; + + if (data->idle_state >= 0) + data->last_chan = pca954x_regval(data, data->idle_state); + else + data->last_chan = 0; /* Disconnect multiplexer */ + + ret = i2c_smbus_write_byte(client, data->last_chan); + if (ret < 0) + data->last_chan = 0; + + return ret; +} + /* * I2C init/probing/exit functions */ @@ -411,7 +430,6 @@ static int pca954x_probe(struct i2c_client *client, struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; struct device_node *np = dev->of_node; - bool idle_disconnect_dt; struct gpio_desc *gpio; struct i2c_mux_core *muxc; struct pca954x *data; @@ -462,23 +480,24 @@ static int pca954x_probe(struct i2c_client *client, } } - /* Write the mux register at addr to verify + data->idle_state = MUX_IDLE_AS_IS; + if (of_property_read_u32(np, "idle-state", &data->idle_state)) { + if (np && of_property_read_bool(np, "i2c-mux-idle-disconnect")) + data->idle_state = MUX_IDLE_DISCONNECT; + } + + /* + * Write the mux register at addr to verify * that the mux is in fact present. This also - * initializes the mux to disconnected state. + * initializes the mux to a channel + * or disconnected state. */ - if (i2c_smbus_write_byte(client, 0) < 0) { + ret = pca954x_init(client, data); + if (ret < 0) { dev_warn(dev, "probe failed\n"); return -ENODEV; } - 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) goto fail_cleanup; @@ -530,9 +549,13 @@ static int pca954x_resume(struct device *dev) 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 ret; - data->last_chan = 0; - return i2c_smbus_write_byte(client, 0); + ret = pca954x_init(client, data); + if (ret < 0) + dev_err(&client->dev, "failed to verify mux presence\n"); + + return ret; } #endif |