/* * I2C multiplexer driver for PCA9541 bus master selector * * Copyright (c) 2010 Ericsson AB. * * Author: Guenter Roeck <linux@roeck-us.net> * * Derived from: * pca954x.c * * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied.
*/
/* * The PCA9541 is a bus master selector. It supports two I2C masters connected * to a single slave bus. * * Before each bus transaction, a master has to acquire bus ownership. After the * transaction is complete, bus ownership has to be released. This fits well * into the I2C multiplexer framework, which provides select and release * functions for this purpose. For this reason, this driver is modeled as * single-channel I2C bus multiplexer. * * This driver assumes that the two bus masters are controlled by two different * hosts. If a single host controls both masters, platform code has to ensure * that only one of the masters is instantiated at any given time.
*/
/* * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() * as they will try to lock the adapter a second time.
*/ staticint pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
{ struct i2c_adapter *adap = client->adapter; union i2c_smbus_data data = { .byte = val };
/* * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer() * as they will try to lock adapter a second time.
*/ staticint pca9541_reg_read(struct i2c_client *client, u8 command)
{ struct i2c_adapter *adap = client->adapter; union i2c_smbus_data data; int ret;
ret = __i2c_smbus_xfer(adap, client->addr, client->flags,
I2C_SMBUS_READ, command,
I2C_SMBUS_BYTE_DATA, &data);
return ret ?: data.byte;
}
/* * Arbitration management functions
*/
/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ staticvoid pca9541_release_bus(struct i2c_client *client)
{ int reg;
/* * Arbitration is defined as a two-step process. A bus master can only activate * the slave bus if it owns it; otherwise it has to request ownership first. * This multi-step process ensures that access contention is resolved * gracefully. * * Bus Ownership Other master Action * state requested access * ---------------------------------------------------- * off - yes wait for arbitration timeout or * for other master to drop request * off no no take ownership * off yes no turn on bus * on yes - done * on no - wait for arbitration timeout or * for other master to release bus * * The main contention point occurs if the slave bus is off and both masters * request ownership at the same time. In this case, one master will turn on * the slave bus, believing that it owns it. The other master will request * bus ownership. Result is that the bus is turned on, and master which did * _not_ own the slave bus before ends up owning it.
*/
/* * Channel arbitration * * Return values: * <0: error * 0 : bus not acquired * 1 : bus acquired
*/ staticint pca9541_arbitrate(struct i2c_client *client)
{ struct i2c_mux_core *muxc = i2c_get_clientdata(client); struct pca9541 *data = i2c_mux_priv(muxc); int reg;
reg = pca9541_reg_read(client, PCA9541_CONTROL); if (reg < 0) return reg;
if (busoff(reg)) { int istat; /* * Bus is off. Request ownership or turn it on unless * other master requested ownership.
*/
istat = pca9541_reg_read(client, PCA9541_ISTAT); if (!(istat & PCA9541_ISTAT_NMYTEST)
|| time_is_before_eq_jiffies(data->arb_timeout)) { /* * Other master did not request ownership, * or arbitration timeout expired. Take the bus.
*/
pca9541_reg_write(client,
PCA9541_CONTROL,
pca9541_control[reg & 0x0f]
| PCA9541_CTL_NTESTON);
data->select_timeout = SELECT_DELAY_SHORT;
} else { /* * Other master requested ownership. * Set extra long timeout to give it time to acquire it.
*/
data->select_timeout = SELECT_DELAY_LONG * 2;
}
} elseif (mybus(reg)) { /* * Bus is on, and we own it. We are done with acquisition. * Reset NTESTON and BUSINIT, then return success.
*/ if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT))
pca9541_reg_write(client,
PCA9541_CONTROL,
reg & ~(PCA9541_CTL_NTESTON
| PCA9541_CTL_BUSINIT)); return 1;
} else { /* * Other master owns the bus. * If arbitration timeout has expired, force ownership. * Otherwise request it.
*/
data->select_timeout = SELECT_DELAY_LONG; if (time_is_before_eq_jiffies(data->arb_timeout)) { /* Time is up, take the bus and reset it. */
pca9541_reg_write(client,
PCA9541_CONTROL,
pca9541_control[reg & 0x0f]
| PCA9541_CTL_BUSINIT
| PCA9541_CTL_NTESTON);
} else { /* Request bus ownership if needed */ if (!(reg & PCA9541_CTL_NTESTON))
pca9541_reg_write(client,
PCA9541_CONTROL,
reg | PCA9541_CTL_NTESTON);
}
} return 0;
}
staticint pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
{ struct pca9541 *data = i2c_mux_priv(muxc); struct i2c_client *client = data->client; int ret; unsignedlong timeout = jiffies + ARB2_TIMEOUT; /* give up after this time */
data->arb_timeout = jiffies + ARB_TIMEOUT; /* force bus ownership after this time */
do {
ret = pca9541_arbitrate(client); if (ret) return ret < 0 ? ret : 0;
if (data->select_timeout == SELECT_DELAY_SHORT)
udelay(data->select_timeout); else
msleep(data->select_timeout / 1000);
} while (time_is_after_eq_jiffies(timeout));
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV;
/* * I2C accesses are unprotected here. * We have to lock the I2C segment before releasing the bus.
*/
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
pca9541_release_bus(client);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.