// SPDX-License-Identifier: GPL-2.0 /* * I2C multiplexer * * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> * * This module supports the PCA954x and PCA984x series of I2C multiplexer/switch * chips made by NXP Semiconductors. * This includes the: * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547, * PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849. * * It's also compatible to Maxims MAX735x I2C switch chips, which are controlled * as the NXP PCA9548 and the MAX736x chips that act like the PCA9544. * * This includes the: * MAX7356, MAX7357, MAX7358, MAX7367, MAX7368 and MAX7369 * * These chips are all controlled via the I2C bus itself, and all have a * single 8-bit register. The upstream "parent" bus fans out to two, * four, or eight downstream busses or channels; which of these * are selected is determined by the chip type and register contents. A * mux can select only one sub-bus at a time; a switch can select any * combination simultaneously. * * Based on: * pca954x.c from Kumar Gala <galak@kernel.crashing.org> * Copyright (C) 2006 * * Based on: * pca954x.c from Ken Harrenstien * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) * * Based on: * i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com> * and * pca9540.c from Jean Delvare <jdelvare@suse.de>.
*/
/* * MAX7357's configuration register is writeable after POR, but * can be locked by setting the basic mode bit. MAX7358 configuration * register is locked by default and needs to be unlocked first. * The configuration register holds the following settings:
*/ #define MAX7357_CONF_INT_ENABLE BIT(0) #define MAX7357_CONF_FLUSH_OUT BIT(1) #define MAX7357_CONF_RELEASE_INT BIT(2) #define MAX7357_CONF_DISCON_SINGLE_CHAN BIT(4) #define MAX7357_CONF_PRECONNECT_TEST BIT(7)
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */ staticint pca954x_reg_write(struct i2c_adapter *adap, struct i2c_client *client, u8 val)
{ union i2c_smbus_data 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;
}
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);
data->last_chan = ret < 0 ? 0 : regval;
}
idle_state = READ_ONCE(data->idle_state); if (idle_state >= 0) /* Set the mux back to a predetermined channel */ return pca954x_select_chan(muxc, idle_state);
if (idle_state == MUX_IDLE_DISCONNECT) { /* Deselect active channel */
data->last_chan = 0; return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
ret = kstrtoint(buf, 0, &val); if (ret < 0) return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans)) return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val); /* * Set the mux into a state consistent with the new * idle_state.
*/ if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
if (device_is_compatible(&client->dev, "maxim,max7357")) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
u8 conf = MAX7357_POR_DEFAULT_CONF; /* * The interrupt signal is shared with the reset pin. Release the * interrupt after 1.6 seconds to allow using the pin as reset.
*/
conf |= MAX7357_CONF_RELEASE_INT;
if (device_property_read_bool(&client->dev, "maxim,isolate-stuck-channel"))
conf |= MAX7357_CONF_DISCON_SINGLE_CHAN; if (device_property_read_bool(&client->dev, "maxim,send-flush-out-sequence"))
conf |= MAX7357_CONF_FLUSH_OUT; if (device_property_read_bool(&client->dev, "maxim,preconnection-wiggle-test-enable"))
conf |= MAX7357_CONF_PRECONNECT_TEST;
ret = i2c_smbus_write_byte_data(client, data->last_chan, conf);
} else {
dev_warn(&client->dev, "Write byte data not supported." "Cannot enable enhanced mode features\n");
ret = i2c_smbus_write_byte(client, data->last_chan);
}
} else {
ret = i2c_smbus_write_byte(client, data->last_chan);
}
if (ret < 0)
data->last_chan = 0;
return ret;
}
staticint pca954x_get_reset(struct device *dev, struct pca954x *data)
{
data->reset_cont = devm_reset_control_get_optional_shared(dev, NULL); if (IS_ERR(data->reset_cont)) return dev_err_probe(dev, PTR_ERR(data->reset_cont), "Failed to get reset\n"); elseif (data->reset_cont) return 0;
/* * fallback to legacy reset-gpios
*/
data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(data->reset_gpio)) { return dev_err_probe(dev, PTR_ERR(data->reset_gpio), "Failed to get reset gpio");
}
data->supply = devm_regulator_get(dev, "vdd"); if (IS_ERR(data->supply)) return dev_err_probe(dev, PTR_ERR(data->supply), "Failed to request regulator\n");
ret = regulator_enable(data->supply); if (ret) return dev_err_probe(dev, ret, "Failed to enable vdd supply\n");
ret = pca954x_get_reset(dev, data); if (ret) goto fail_cleanup;
if (data->reset_cont || data->reset_gpio) {
udelay(1);
pca954x_reset_deassert(data); /* Give the chip some time to recover. */
udelay(1);
}
data->chip = device_get_match_data(dev); if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) { struct i2c_device_identity id;
ret = i2c_get_device_id(client, &id); if (ret && ret != -EOPNOTSUPP) goto fail_cleanup;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
ret = -ENODEV; goto fail_cleanup;
}
}
data->idle_state = MUX_IDLE_AS_IS; if (device_property_read_u32(dev, "idle-state", &data->idle_state)) { if (device_property_read_bool(dev, "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 a channel * or disconnected state.
*/
ret = pca954x_init(client, data); if (ret < 0) {
dev_warn(dev, "probe failed\n");
ret = -ENODEV; goto fail_cleanup;
}
ret = pca954x_irq_setup(muxc); if (ret) goto fail_cleanup;
/* Now create an adapter for each channel */ for (num = 0; num < data->chip->nchans; num++) {
ret = i2c_mux_add_adapter(muxc, 0, num); if (ret) goto fail_cleanup;
}
if (data->irq) {
ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED, "pca954x", data); if (ret) goto fail_cleanup;
}
/* * The attr probably isn't going to be needed in most cases, * so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
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.