162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Multiplexed I2C bus driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> 562306a36Sopenharmony_ci * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> 662306a36Sopenharmony_ci * Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Simplifies access to complex multiplexed I2C bus topologies, by presenting 962306a36Sopenharmony_ci * each multiplexed bus segment as an additional I2C adapter. 1062306a36Sopenharmony_ci * Supports multi-level mux'ing (mux behind a mux). 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Based on: 1362306a36Sopenharmony_ci * i2c-virt.c from Kumar Gala <galak@kernel.crashing.org> 1462306a36Sopenharmony_ci * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. 1562306a36Sopenharmony_ci * i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 1862306a36Sopenharmony_ci * License version 2. This program is licensed "as is" without any 1962306a36Sopenharmony_ci * warranty of any kind, whether express or implied. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/acpi.h> 2362306a36Sopenharmony_ci#include <linux/i2c.h> 2462306a36Sopenharmony_ci#include <linux/i2c-mux.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/of.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/sysfs.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* multiplexer per channel data */ 3262306a36Sopenharmony_cistruct i2c_mux_priv { 3362306a36Sopenharmony_ci struct i2c_adapter adap; 3462306a36Sopenharmony_ci struct i2c_algorithm algo; 3562306a36Sopenharmony_ci struct i2c_mux_core *muxc; 3662306a36Sopenharmony_ci u32 chan_id; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int __i2c_mux_master_xfer(struct i2c_adapter *adap, 4062306a36Sopenharmony_ci struct i2c_msg msgs[], int num) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 4362306a36Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 4462306a36Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 4562306a36Sopenharmony_ci int ret; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Switch to the right mux port and perform the transfer. */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 5062306a36Sopenharmony_ci if (ret >= 0) 5162306a36Sopenharmony_ci ret = __i2c_transfer(parent, msgs, num); 5262306a36Sopenharmony_ci if (muxc->deselect) 5362306a36Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int i2c_mux_master_xfer(struct i2c_adapter *adap, 5962306a36Sopenharmony_ci struct i2c_msg msgs[], int num) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 6262306a36Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 6362306a36Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Switch to the right mux port and perform the transfer. */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 6962306a36Sopenharmony_ci if (ret >= 0) 7062306a36Sopenharmony_ci ret = i2c_transfer(parent, msgs, num); 7162306a36Sopenharmony_ci if (muxc->deselect) 7262306a36Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, 7862306a36Sopenharmony_ci u16 addr, unsigned short flags, 7962306a36Sopenharmony_ci char read_write, u8 command, 8062306a36Sopenharmony_ci int size, union i2c_smbus_data *data) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 8362306a36Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 8462306a36Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 8562306a36Sopenharmony_ci int ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Select the right mux port and perform the transfer. */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 9062306a36Sopenharmony_ci if (ret >= 0) 9162306a36Sopenharmony_ci ret = __i2c_smbus_xfer(parent, addr, flags, 9262306a36Sopenharmony_ci read_write, command, size, data); 9362306a36Sopenharmony_ci if (muxc->deselect) 9462306a36Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int i2c_mux_smbus_xfer(struct i2c_adapter *adap, 10062306a36Sopenharmony_ci u16 addr, unsigned short flags, 10162306a36Sopenharmony_ci char read_write, u8 command, 10262306a36Sopenharmony_ci int size, union i2c_smbus_data *data) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 10562306a36Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 10662306a36Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 10762306a36Sopenharmony_ci int ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Select the right mux port and perform the transfer. */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 11262306a36Sopenharmony_ci if (ret >= 0) 11362306a36Sopenharmony_ci ret = i2c_smbus_xfer(parent, addr, flags, 11462306a36Sopenharmony_ci read_write, command, size, data); 11562306a36Sopenharmony_ci if (muxc->deselect) 11662306a36Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Return the parent's functionality */ 12262306a36Sopenharmony_cistatic u32 i2c_mux_functionality(struct i2c_adapter *adap) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 12562306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return parent->algo->functionality(parent); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* Return all parent classes, merged */ 13162306a36Sopenharmony_cistatic unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned int class = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci do { 13662306a36Sopenharmony_ci class |= parent->class; 13762306a36Sopenharmony_ci parent = i2c_parent_is_i2c_adapter(parent); 13862306a36Sopenharmony_ci } while (parent); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return class; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 14662306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); 14962306a36Sopenharmony_ci if (!(flags & I2C_LOCK_ROOT_ADAPTER)) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci i2c_lock_bus(parent, flags); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 15762306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!rt_mutex_trylock(&parent->mux_lock)) 16062306a36Sopenharmony_ci return 0; /* mux_lock not locked, failure */ 16162306a36Sopenharmony_ci if (!(flags & I2C_LOCK_ROOT_ADAPTER)) 16262306a36Sopenharmony_ci return 1; /* we only want mux_lock, success */ 16362306a36Sopenharmony_ci if (i2c_trylock_bus(parent, flags)) 16462306a36Sopenharmony_ci return 1; /* parent locked too, success */ 16562306a36Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 16662306a36Sopenharmony_ci return 0; /* parent not locked, failure */ 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void i2c_mux_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 17262306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (flags & I2C_LOCK_ROOT_ADAPTER) 17562306a36Sopenharmony_ci i2c_unlock_bus(parent, flags); 17662306a36Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void i2c_parent_lock_bus(struct i2c_adapter *adapter, 18062306a36Sopenharmony_ci unsigned int flags) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 18362306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); 18662306a36Sopenharmony_ci i2c_lock_bus(parent, flags); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int i2c_parent_trylock_bus(struct i2c_adapter *adapter, 19062306a36Sopenharmony_ci unsigned int flags) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 19362306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!rt_mutex_trylock(&parent->mux_lock)) 19662306a36Sopenharmony_ci return 0; /* mux_lock not locked, failure */ 19762306a36Sopenharmony_ci if (i2c_trylock_bus(parent, flags)) 19862306a36Sopenharmony_ci return 1; /* parent locked too, success */ 19962306a36Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 20062306a36Sopenharmony_ci return 0; /* parent not locked, failure */ 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void i2c_parent_unlock_bus(struct i2c_adapter *adapter, 20462306a36Sopenharmony_ci unsigned int flags) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 20762306a36Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci i2c_unlock_bus(parent, flags); 21062306a36Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistruct i2c_adapter *i2c_root_adapter(struct device *dev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct device *i2c; 21662306a36Sopenharmony_ci struct i2c_adapter *i2c_root; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Walk up the device tree to find an i2c adapter, indicating 22062306a36Sopenharmony_ci * that this is an i2c client device. Check all ancestors to 22162306a36Sopenharmony_ci * handle mfd devices etc. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci for (i2c = dev; i2c; i2c = i2c->parent) { 22462306a36Sopenharmony_ci if (i2c->type == &i2c_adapter_type) 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (!i2c) 22862306a36Sopenharmony_ci return NULL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Continue up the tree to find the root i2c adapter */ 23162306a36Sopenharmony_ci i2c_root = to_i2c_adapter(i2c); 23262306a36Sopenharmony_ci while (i2c_parent_is_i2c_adapter(i2c_root)) 23362306a36Sopenharmony_ci i2c_root = i2c_parent_is_i2c_adapter(i2c_root); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return i2c_root; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_root_adapter); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistruct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, 24062306a36Sopenharmony_ci struct device *dev, int max_adapters, 24162306a36Sopenharmony_ci int sizeof_priv, u32 flags, 24262306a36Sopenharmony_ci int (*select)(struct i2c_mux_core *, u32), 24362306a36Sopenharmony_ci int (*deselect)(struct i2c_mux_core *, u32)) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct i2c_mux_core *muxc; 24662306a36Sopenharmony_ci size_t mux_size; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci mux_size = struct_size(muxc, adapter, max_adapters); 24962306a36Sopenharmony_ci muxc = devm_kzalloc(dev, size_add(mux_size, sizeof_priv), GFP_KERNEL); 25062306a36Sopenharmony_ci if (!muxc) 25162306a36Sopenharmony_ci return NULL; 25262306a36Sopenharmony_ci if (sizeof_priv) 25362306a36Sopenharmony_ci muxc->priv = &muxc->adapter[max_adapters]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci muxc->parent = parent; 25662306a36Sopenharmony_ci muxc->dev = dev; 25762306a36Sopenharmony_ci if (flags & I2C_MUX_LOCKED) 25862306a36Sopenharmony_ci muxc->mux_locked = true; 25962306a36Sopenharmony_ci if (flags & I2C_MUX_ARBITRATOR) 26062306a36Sopenharmony_ci muxc->arbitrator = true; 26162306a36Sopenharmony_ci if (flags & I2C_MUX_GATE) 26262306a36Sopenharmony_ci muxc->gate = true; 26362306a36Sopenharmony_ci muxc->select = select; 26462306a36Sopenharmony_ci muxc->deselect = deselect; 26562306a36Sopenharmony_ci muxc->max_adapters = max_adapters; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return muxc; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_alloc); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct i2c_lock_operations i2c_mux_lock_ops = { 27262306a36Sopenharmony_ci .lock_bus = i2c_mux_lock_bus, 27362306a36Sopenharmony_ci .trylock_bus = i2c_mux_trylock_bus, 27462306a36Sopenharmony_ci .unlock_bus = i2c_mux_unlock_bus, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct i2c_lock_operations i2c_parent_lock_ops = { 27862306a36Sopenharmony_ci .lock_bus = i2c_parent_lock_bus, 27962306a36Sopenharmony_ci .trylock_bus = i2c_parent_trylock_bus, 28062306a36Sopenharmony_ci .unlock_bus = i2c_parent_unlock_bus, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint i2c_mux_add_adapter(struct i2c_mux_core *muxc, 28462306a36Sopenharmony_ci u32 force_nr, u32 chan_id, 28562306a36Sopenharmony_ci unsigned int class) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 28862306a36Sopenharmony_ci struct i2c_mux_priv *priv; 28962306a36Sopenharmony_ci char symlink_name[20]; 29062306a36Sopenharmony_ci int ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (muxc->num_adapters >= muxc->max_adapters) { 29362306a36Sopenharmony_ci dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 29862306a36Sopenharmony_ci if (!priv) 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Set up private adapter data */ 30262306a36Sopenharmony_ci priv->muxc = muxc; 30362306a36Sopenharmony_ci priv->chan_id = chan_id; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Need to do algo dynamically because we don't know ahead 30662306a36Sopenharmony_ci * of time what sort of physical adapter we'll be dealing with. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci if (parent->algo->master_xfer) { 30962306a36Sopenharmony_ci if (muxc->mux_locked) 31062306a36Sopenharmony_ci priv->algo.master_xfer = i2c_mux_master_xfer; 31162306a36Sopenharmony_ci else 31262306a36Sopenharmony_ci priv->algo.master_xfer = __i2c_mux_master_xfer; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (parent->algo->master_xfer_atomic) 31562306a36Sopenharmony_ci priv->algo.master_xfer_atomic = priv->algo.master_xfer; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (parent->algo->smbus_xfer) { 31862306a36Sopenharmony_ci if (muxc->mux_locked) 31962306a36Sopenharmony_ci priv->algo.smbus_xfer = i2c_mux_smbus_xfer; 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci priv->algo.smbus_xfer = __i2c_mux_smbus_xfer; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci if (parent->algo->smbus_xfer_atomic) 32462306a36Sopenharmony_ci priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci priv->algo.functionality = i2c_mux_functionality; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Now fill out new adapter structure */ 32962306a36Sopenharmony_ci snprintf(priv->adap.name, sizeof(priv->adap.name), 33062306a36Sopenharmony_ci "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id); 33162306a36Sopenharmony_ci priv->adap.owner = THIS_MODULE; 33262306a36Sopenharmony_ci priv->adap.algo = &priv->algo; 33362306a36Sopenharmony_ci priv->adap.algo_data = priv; 33462306a36Sopenharmony_ci priv->adap.dev.parent = &parent->dev; 33562306a36Sopenharmony_ci priv->adap.retries = parent->retries; 33662306a36Sopenharmony_ci priv->adap.timeout = parent->timeout; 33762306a36Sopenharmony_ci priv->adap.quirks = parent->quirks; 33862306a36Sopenharmony_ci if (muxc->mux_locked) 33962306a36Sopenharmony_ci priv->adap.lock_ops = &i2c_mux_lock_ops; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci priv->adap.lock_ops = &i2c_parent_lock_ops; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Sanity check on class */ 34462306a36Sopenharmony_ci if (i2c_mux_parent_classes(parent) & class & ~I2C_CLASS_DEPRECATED) 34562306a36Sopenharmony_ci dev_err(&parent->dev, 34662306a36Sopenharmony_ci "Segment %d behind mux can't share classes with ancestors\n", 34762306a36Sopenharmony_ci chan_id); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci priv->adap.class = class; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * Try to populate the mux adapter's of_node, expands to 35362306a36Sopenharmony_ci * nothing if !CONFIG_OF. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci if (muxc->dev->of_node) { 35662306a36Sopenharmony_ci struct device_node *dev_node = muxc->dev->of_node; 35762306a36Sopenharmony_ci struct device_node *mux_node, *child = NULL; 35862306a36Sopenharmony_ci u32 reg; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (muxc->arbitrator) 36162306a36Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-arb"); 36262306a36Sopenharmony_ci else if (muxc->gate) 36362306a36Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-gate"); 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-mux"); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (mux_node) { 36862306a36Sopenharmony_ci /* A "reg" property indicates an old-style DT entry */ 36962306a36Sopenharmony_ci if (!of_property_read_u32(mux_node, "reg", ®)) { 37062306a36Sopenharmony_ci of_node_put(mux_node); 37162306a36Sopenharmony_ci mux_node = NULL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!mux_node) 37662306a36Sopenharmony_ci mux_node = of_node_get(dev_node); 37762306a36Sopenharmony_ci else if (muxc->arbitrator || muxc->gate) 37862306a36Sopenharmony_ci child = of_node_get(mux_node); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!child) { 38162306a36Sopenharmony_ci for_each_child_of_node(mux_node, child) { 38262306a36Sopenharmony_ci ret = of_property_read_u32(child, "reg", ®); 38362306a36Sopenharmony_ci if (ret) 38462306a36Sopenharmony_ci continue; 38562306a36Sopenharmony_ci if (chan_id == reg) 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci priv->adap.dev.of_node = child; 39162306a36Sopenharmony_ci of_node_put(mux_node); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * Associate the mux channel with an ACPI node. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci if (has_acpi_companion(muxc->dev)) 39862306a36Sopenharmony_ci acpi_preset_companion(&priv->adap.dev, 39962306a36Sopenharmony_ci ACPI_COMPANION(muxc->dev), 40062306a36Sopenharmony_ci chan_id); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (force_nr) { 40362306a36Sopenharmony_ci priv->adap.nr = force_nr; 40462306a36Sopenharmony_ci ret = i2c_add_numbered_adapter(&priv->adap); 40562306a36Sopenharmony_ci if (ret < 0) { 40662306a36Sopenharmony_ci dev_err(&parent->dev, 40762306a36Sopenharmony_ci "failed to add mux-adapter %u as bus %u (error=%d)\n", 40862306a36Sopenharmony_ci chan_id, force_nr, ret); 40962306a36Sopenharmony_ci goto err_free_priv; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci ret = i2c_add_adapter(&priv->adap); 41362306a36Sopenharmony_ci if (ret < 0) { 41462306a36Sopenharmony_ci dev_err(&parent->dev, 41562306a36Sopenharmony_ci "failed to add mux-adapter %u (error=%d)\n", 41662306a36Sopenharmony_ci chan_id, ret); 41762306a36Sopenharmony_ci goto err_free_priv; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, 42262306a36Sopenharmony_ci "mux_device"), 42362306a36Sopenharmony_ci "can't create symlink to mux device\n"); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); 42662306a36Sopenharmony_ci WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, 42762306a36Sopenharmony_ci symlink_name), 42862306a36Sopenharmony_ci "can't create symlink to channel %u\n", chan_id); 42962306a36Sopenharmony_ci dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", 43062306a36Sopenharmony_ci i2c_adapter_id(&priv->adap)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci muxc->adapter[muxc->num_adapters++] = &priv->adap; 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cierr_free_priv: 43662306a36Sopenharmony_ci kfree(priv); 43762306a36Sopenharmony_ci return ret; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_add_adapter); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_civoid i2c_mux_del_adapters(struct i2c_mux_core *muxc) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci char symlink_name[20]; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci while (muxc->num_adapters) { 44662306a36Sopenharmony_ci struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; 44762306a36Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 44862306a36Sopenharmony_ci struct device_node *np = adap->dev.of_node; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci muxc->adapter[muxc->num_adapters] = NULL; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci snprintf(symlink_name, sizeof(symlink_name), 45362306a36Sopenharmony_ci "channel-%u", priv->chan_id); 45462306a36Sopenharmony_ci sysfs_remove_link(&muxc->dev->kobj, symlink_name); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); 45762306a36Sopenharmony_ci i2c_del_adapter(adap); 45862306a36Sopenharmony_ci of_node_put(np); 45962306a36Sopenharmony_ci kfree(priv); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_del_adapters); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciMODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 46562306a36Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); 46662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 467