Commit 4a890148 authored by Wolfram Sang's avatar Wolfram Sang
Browse files

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.
parents 6810df46 e65e228e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ Required Properties:
Optional Properties:

  - reset-gpios: Reference to the GPIO connected to the reset input.
  - idle-state: if present, overrides i2c-mux-idle-disconnect,
    Please refer to Documentation/devicetree/bindings/mux/mux-controller.txt
  - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
    children in idle state. This is necessary for example, if there are several
    multiplexers on the bus and the devices behind them use same I2C addresses.
+15 −14
Original line number Diff line number Diff line
@@ -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)
+46 −23
Original line number Diff line number Diff line
@@ -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