18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Multiplexed I2C bus driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> 58c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> 68c2ecf20Sopenharmony_ci * Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Simplifies access to complex multiplexed I2C bus topologies, by presenting 98c2ecf20Sopenharmony_ci * each multiplexed bus segment as an additional I2C adapter. 108c2ecf20Sopenharmony_ci * Supports multi-level mux'ing (mux behind a mux). 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Based on: 138c2ecf20Sopenharmony_ci * i2c-virt.c from Kumar Gala <galak@kernel.crashing.org> 148c2ecf20Sopenharmony_ci * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. 158c2ecf20Sopenharmony_ci * i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com> 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 188c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 198c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/acpi.h> 238c2ecf20Sopenharmony_ci#include <linux/i2c.h> 248c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* multiplexer per channel data */ 328c2ecf20Sopenharmony_cistruct i2c_mux_priv { 338c2ecf20Sopenharmony_ci struct i2c_adapter adap; 348c2ecf20Sopenharmony_ci struct i2c_algorithm algo; 358c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc; 368c2ecf20Sopenharmony_ci u32 chan_id; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int __i2c_mux_master_xfer(struct i2c_adapter *adap, 408c2ecf20Sopenharmony_ci struct i2c_msg msgs[], int num) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 438c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 448c2ecf20Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 458c2ecf20Sopenharmony_ci int ret; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* Switch to the right mux port and perform the transfer. */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 508c2ecf20Sopenharmony_ci if (ret >= 0) 518c2ecf20Sopenharmony_ci ret = __i2c_transfer(parent, msgs, num); 528c2ecf20Sopenharmony_ci if (muxc->deselect) 538c2ecf20Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int i2c_mux_master_xfer(struct i2c_adapter *adap, 598c2ecf20Sopenharmony_ci struct i2c_msg msgs[], int num) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 628c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 638c2ecf20Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Switch to the right mux port and perform the transfer. */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 698c2ecf20Sopenharmony_ci if (ret >= 0) 708c2ecf20Sopenharmony_ci ret = i2c_transfer(parent, msgs, num); 718c2ecf20Sopenharmony_ci if (muxc->deselect) 728c2ecf20Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, 788c2ecf20Sopenharmony_ci u16 addr, unsigned short flags, 798c2ecf20Sopenharmony_ci char read_write, u8 command, 808c2ecf20Sopenharmony_ci int size, union i2c_smbus_data *data) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 838c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 848c2ecf20Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Select the right mux port and perform the transfer. */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 908c2ecf20Sopenharmony_ci if (ret >= 0) 918c2ecf20Sopenharmony_ci ret = __i2c_smbus_xfer(parent, addr, flags, 928c2ecf20Sopenharmony_ci read_write, command, size, data); 938c2ecf20Sopenharmony_ci if (muxc->deselect) 948c2ecf20Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int i2c_mux_smbus_xfer(struct i2c_adapter *adap, 1008c2ecf20Sopenharmony_ci u16 addr, unsigned short flags, 1018c2ecf20Sopenharmony_ci char read_write, u8 command, 1028c2ecf20Sopenharmony_ci int size, union i2c_smbus_data *data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 1058c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc = priv->muxc; 1068c2ecf20Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Select the right mux port and perform the transfer. */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ret = muxc->select(muxc, priv->chan_id); 1128c2ecf20Sopenharmony_ci if (ret >= 0) 1138c2ecf20Sopenharmony_ci ret = i2c_smbus_xfer(parent, addr, flags, 1148c2ecf20Sopenharmony_ci read_write, command, size, data); 1158c2ecf20Sopenharmony_ci if (muxc->deselect) 1168c2ecf20Sopenharmony_ci muxc->deselect(muxc, priv->chan_id); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Return the parent's functionality */ 1228c2ecf20Sopenharmony_cistatic u32 i2c_mux_functionality(struct i2c_adapter *adap) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 1258c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return parent->algo->functionality(parent); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Return all parent classes, merged */ 1318c2ecf20Sopenharmony_cistatic unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned int class = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci do { 1368c2ecf20Sopenharmony_ci class |= parent->class; 1378c2ecf20Sopenharmony_ci parent = i2c_parent_is_i2c_adapter(parent); 1388c2ecf20Sopenharmony_ci } while (parent); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return class; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 1468c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); 1498c2ecf20Sopenharmony_ci if (!(flags & I2C_LOCK_ROOT_ADAPTER)) 1508c2ecf20Sopenharmony_ci return; 1518c2ecf20Sopenharmony_ci i2c_lock_bus(parent, flags); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 1578c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!rt_mutex_trylock(&parent->mux_lock)) 1608c2ecf20Sopenharmony_ci return 0; /* mux_lock not locked, failure */ 1618c2ecf20Sopenharmony_ci if (!(flags & I2C_LOCK_ROOT_ADAPTER)) 1628c2ecf20Sopenharmony_ci return 1; /* we only want mux_lock, success */ 1638c2ecf20Sopenharmony_ci if (i2c_trylock_bus(parent, flags)) 1648c2ecf20Sopenharmony_ci return 1; /* parent locked too, success */ 1658c2ecf20Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 1668c2ecf20Sopenharmony_ci return 0; /* parent not locked, failure */ 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void i2c_mux_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 1728c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (flags & I2C_LOCK_ROOT_ADAPTER) 1758c2ecf20Sopenharmony_ci i2c_unlock_bus(parent, flags); 1768c2ecf20Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void i2c_parent_lock_bus(struct i2c_adapter *adapter, 1808c2ecf20Sopenharmony_ci unsigned int flags) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 1838c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter)); 1868c2ecf20Sopenharmony_ci i2c_lock_bus(parent, flags); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int i2c_parent_trylock_bus(struct i2c_adapter *adapter, 1908c2ecf20Sopenharmony_ci unsigned int flags) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 1938c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!rt_mutex_trylock(&parent->mux_lock)) 1968c2ecf20Sopenharmony_ci return 0; /* mux_lock not locked, failure */ 1978c2ecf20Sopenharmony_ci if (i2c_trylock_bus(parent, flags)) 1988c2ecf20Sopenharmony_ci return 1; /* parent locked too, success */ 1998c2ecf20Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 2008c2ecf20Sopenharmony_ci return 0; /* parent not locked, failure */ 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void i2c_parent_unlock_bus(struct i2c_adapter *adapter, 2048c2ecf20Sopenharmony_ci unsigned int flags) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adapter->algo_data; 2078c2ecf20Sopenharmony_ci struct i2c_adapter *parent = priv->muxc->parent; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci i2c_unlock_bus(parent, flags); 2108c2ecf20Sopenharmony_ci rt_mutex_unlock(&parent->mux_lock); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct i2c_adapter *i2c_root_adapter(struct device *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct device *i2c; 2168c2ecf20Sopenharmony_ci struct i2c_adapter *i2c_root; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * Walk up the device tree to find an i2c adapter, indicating 2208c2ecf20Sopenharmony_ci * that this is an i2c client device. Check all ancestors to 2218c2ecf20Sopenharmony_ci * handle mfd devices etc. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci for (i2c = dev; i2c; i2c = i2c->parent) { 2248c2ecf20Sopenharmony_ci if (i2c->type == &i2c_adapter_type) 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci if (!i2c) 2288c2ecf20Sopenharmony_ci return NULL; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Continue up the tree to find the root i2c adapter */ 2318c2ecf20Sopenharmony_ci i2c_root = to_i2c_adapter(i2c); 2328c2ecf20Sopenharmony_ci while (i2c_parent_is_i2c_adapter(i2c_root)) 2338c2ecf20Sopenharmony_ci i2c_root = i2c_parent_is_i2c_adapter(i2c_root); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return i2c_root; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_root_adapter); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistruct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, 2408c2ecf20Sopenharmony_ci struct device *dev, int max_adapters, 2418c2ecf20Sopenharmony_ci int sizeof_priv, u32 flags, 2428c2ecf20Sopenharmony_ci int (*select)(struct i2c_mux_core *, u32), 2438c2ecf20Sopenharmony_ci int (*deselect)(struct i2c_mux_core *, u32)) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct i2c_mux_core *muxc; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci muxc = devm_kzalloc(dev, struct_size(muxc, adapter, max_adapters) 2488c2ecf20Sopenharmony_ci + sizeof_priv, GFP_KERNEL); 2498c2ecf20Sopenharmony_ci if (!muxc) 2508c2ecf20Sopenharmony_ci return NULL; 2518c2ecf20Sopenharmony_ci if (sizeof_priv) 2528c2ecf20Sopenharmony_ci muxc->priv = &muxc->adapter[max_adapters]; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci muxc->parent = parent; 2558c2ecf20Sopenharmony_ci muxc->dev = dev; 2568c2ecf20Sopenharmony_ci if (flags & I2C_MUX_LOCKED) 2578c2ecf20Sopenharmony_ci muxc->mux_locked = true; 2588c2ecf20Sopenharmony_ci if (flags & I2C_MUX_ARBITRATOR) 2598c2ecf20Sopenharmony_ci muxc->arbitrator = true; 2608c2ecf20Sopenharmony_ci if (flags & I2C_MUX_GATE) 2618c2ecf20Sopenharmony_ci muxc->gate = true; 2628c2ecf20Sopenharmony_ci muxc->select = select; 2638c2ecf20Sopenharmony_ci muxc->deselect = deselect; 2648c2ecf20Sopenharmony_ci muxc->max_adapters = max_adapters; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return muxc; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_alloc); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct i2c_lock_operations i2c_mux_lock_ops = { 2718c2ecf20Sopenharmony_ci .lock_bus = i2c_mux_lock_bus, 2728c2ecf20Sopenharmony_ci .trylock_bus = i2c_mux_trylock_bus, 2738c2ecf20Sopenharmony_ci .unlock_bus = i2c_mux_unlock_bus, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct i2c_lock_operations i2c_parent_lock_ops = { 2778c2ecf20Sopenharmony_ci .lock_bus = i2c_parent_lock_bus, 2788c2ecf20Sopenharmony_ci .trylock_bus = i2c_parent_trylock_bus, 2798c2ecf20Sopenharmony_ci .unlock_bus = i2c_parent_unlock_bus, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciint i2c_mux_add_adapter(struct i2c_mux_core *muxc, 2838c2ecf20Sopenharmony_ci u32 force_nr, u32 chan_id, 2848c2ecf20Sopenharmony_ci unsigned int class) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct i2c_adapter *parent = muxc->parent; 2878c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv; 2888c2ecf20Sopenharmony_ci char symlink_name[20]; 2898c2ecf20Sopenharmony_ci int ret; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (muxc->num_adapters >= muxc->max_adapters) { 2928c2ecf20Sopenharmony_ci dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); 2938c2ecf20Sopenharmony_ci return -EINVAL; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 2978c2ecf20Sopenharmony_ci if (!priv) 2988c2ecf20Sopenharmony_ci return -ENOMEM; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Set up private adapter data */ 3018c2ecf20Sopenharmony_ci priv->muxc = muxc; 3028c2ecf20Sopenharmony_ci priv->chan_id = chan_id; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Need to do algo dynamically because we don't know ahead 3058c2ecf20Sopenharmony_ci * of time what sort of physical adapter we'll be dealing with. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (parent->algo->master_xfer) { 3088c2ecf20Sopenharmony_ci if (muxc->mux_locked) 3098c2ecf20Sopenharmony_ci priv->algo.master_xfer = i2c_mux_master_xfer; 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci priv->algo.master_xfer = __i2c_mux_master_xfer; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci if (parent->algo->master_xfer_atomic) 3148c2ecf20Sopenharmony_ci priv->algo.master_xfer_atomic = priv->algo.master_xfer; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (parent->algo->smbus_xfer) { 3178c2ecf20Sopenharmony_ci if (muxc->mux_locked) 3188c2ecf20Sopenharmony_ci priv->algo.smbus_xfer = i2c_mux_smbus_xfer; 3198c2ecf20Sopenharmony_ci else 3208c2ecf20Sopenharmony_ci priv->algo.smbus_xfer = __i2c_mux_smbus_xfer; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci if (parent->algo->smbus_xfer_atomic) 3238c2ecf20Sopenharmony_ci priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci priv->algo.functionality = i2c_mux_functionality; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Now fill out new adapter structure */ 3288c2ecf20Sopenharmony_ci snprintf(priv->adap.name, sizeof(priv->adap.name), 3298c2ecf20Sopenharmony_ci "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id); 3308c2ecf20Sopenharmony_ci priv->adap.owner = THIS_MODULE; 3318c2ecf20Sopenharmony_ci priv->adap.algo = &priv->algo; 3328c2ecf20Sopenharmony_ci priv->adap.algo_data = priv; 3338c2ecf20Sopenharmony_ci priv->adap.dev.parent = &parent->dev; 3348c2ecf20Sopenharmony_ci priv->adap.retries = parent->retries; 3358c2ecf20Sopenharmony_ci priv->adap.timeout = parent->timeout; 3368c2ecf20Sopenharmony_ci priv->adap.quirks = parent->quirks; 3378c2ecf20Sopenharmony_ci if (muxc->mux_locked) 3388c2ecf20Sopenharmony_ci priv->adap.lock_ops = &i2c_mux_lock_ops; 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci priv->adap.lock_ops = &i2c_parent_lock_ops; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Sanity check on class */ 3438c2ecf20Sopenharmony_ci if (i2c_mux_parent_classes(parent) & class & ~I2C_CLASS_DEPRECATED) 3448c2ecf20Sopenharmony_ci dev_err(&parent->dev, 3458c2ecf20Sopenharmony_ci "Segment %d behind mux can't share classes with ancestors\n", 3468c2ecf20Sopenharmony_ci chan_id); 3478c2ecf20Sopenharmony_ci else 3488c2ecf20Sopenharmony_ci priv->adap.class = class; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* 3518c2ecf20Sopenharmony_ci * Try to populate the mux adapter's of_node, expands to 3528c2ecf20Sopenharmony_ci * nothing if !CONFIG_OF. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci if (muxc->dev->of_node) { 3558c2ecf20Sopenharmony_ci struct device_node *dev_node = muxc->dev->of_node; 3568c2ecf20Sopenharmony_ci struct device_node *mux_node, *child = NULL; 3578c2ecf20Sopenharmony_ci u32 reg; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (muxc->arbitrator) 3608c2ecf20Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-arb"); 3618c2ecf20Sopenharmony_ci else if (muxc->gate) 3628c2ecf20Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-gate"); 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci mux_node = of_get_child_by_name(dev_node, "i2c-mux"); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (mux_node) { 3678c2ecf20Sopenharmony_ci /* A "reg" property indicates an old-style DT entry */ 3688c2ecf20Sopenharmony_ci if (!of_property_read_u32(mux_node, "reg", ®)) { 3698c2ecf20Sopenharmony_ci of_node_put(mux_node); 3708c2ecf20Sopenharmony_ci mux_node = NULL; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!mux_node) 3758c2ecf20Sopenharmony_ci mux_node = of_node_get(dev_node); 3768c2ecf20Sopenharmony_ci else if (muxc->arbitrator || muxc->gate) 3778c2ecf20Sopenharmony_ci child = of_node_get(mux_node); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!child) { 3808c2ecf20Sopenharmony_ci for_each_child_of_node(mux_node, child) { 3818c2ecf20Sopenharmony_ci ret = of_property_read_u32(child, "reg", ®); 3828c2ecf20Sopenharmony_ci if (ret) 3838c2ecf20Sopenharmony_ci continue; 3848c2ecf20Sopenharmony_ci if (chan_id == reg) 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci priv->adap.dev.of_node = child; 3908c2ecf20Sopenharmony_ci of_node_put(mux_node); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Associate the mux channel with an ACPI node. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci if (has_acpi_companion(muxc->dev)) 3978c2ecf20Sopenharmony_ci acpi_preset_companion(&priv->adap.dev, 3988c2ecf20Sopenharmony_ci ACPI_COMPANION(muxc->dev), 3998c2ecf20Sopenharmony_ci chan_id); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (force_nr) { 4028c2ecf20Sopenharmony_ci priv->adap.nr = force_nr; 4038c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(&priv->adap); 4048c2ecf20Sopenharmony_ci if (ret < 0) { 4058c2ecf20Sopenharmony_ci dev_err(&parent->dev, 4068c2ecf20Sopenharmony_ci "failed to add mux-adapter %u as bus %u (error=%d)\n", 4078c2ecf20Sopenharmony_ci chan_id, force_nr, ret); 4088c2ecf20Sopenharmony_ci goto err_free_priv; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } else { 4118c2ecf20Sopenharmony_ci ret = i2c_add_adapter(&priv->adap); 4128c2ecf20Sopenharmony_ci if (ret < 0) { 4138c2ecf20Sopenharmony_ci dev_err(&parent->dev, 4148c2ecf20Sopenharmony_ci "failed to add mux-adapter %u (error=%d)\n", 4158c2ecf20Sopenharmony_ci chan_id, ret); 4168c2ecf20Sopenharmony_ci goto err_free_priv; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, 4218c2ecf20Sopenharmony_ci "mux_device"), 4228c2ecf20Sopenharmony_ci "can't create symlink to mux device\n"); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); 4258c2ecf20Sopenharmony_ci WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, 4268c2ecf20Sopenharmony_ci symlink_name), 4278c2ecf20Sopenharmony_ci "can't create symlink to channel %u\n", chan_id); 4288c2ecf20Sopenharmony_ci dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", 4298c2ecf20Sopenharmony_ci i2c_adapter_id(&priv->adap)); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci muxc->adapter[muxc->num_adapters++] = &priv->adap; 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cierr_free_priv: 4358c2ecf20Sopenharmony_ci kfree(priv); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_add_adapter); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_civoid i2c_mux_del_adapters(struct i2c_mux_core *muxc) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci char symlink_name[20]; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci while (muxc->num_adapters) { 4458c2ecf20Sopenharmony_ci struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; 4468c2ecf20Sopenharmony_ci struct i2c_mux_priv *priv = adap->algo_data; 4478c2ecf20Sopenharmony_ci struct device_node *np = adap->dev.of_node; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci muxc->adapter[muxc->num_adapters] = NULL; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci snprintf(symlink_name, sizeof(symlink_name), 4528c2ecf20Sopenharmony_ci "channel-%u", priv->chan_id); 4538c2ecf20Sopenharmony_ci sysfs_remove_link(&muxc->dev->kobj, symlink_name); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); 4568c2ecf20Sopenharmony_ci i2c_del_adapter(adap); 4578c2ecf20Sopenharmony_ci of_node_put(np); 4588c2ecf20Sopenharmony_ci kfree(priv); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2c_mux_del_adapters); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 4648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); 4658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 466