1*ae58d1e4SStephen Warren /* 2*ae58d1e4SStephen Warren * I2C multiplexer using pinctrl API 3*ae58d1e4SStephen Warren * 4*ae58d1e4SStephen Warren * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 5*ae58d1e4SStephen Warren * 6*ae58d1e4SStephen Warren * This program is free software; you can redistribute it and/or modify it 7*ae58d1e4SStephen Warren * under the terms and conditions of the GNU General Public License, 8*ae58d1e4SStephen Warren * version 2, as published by the Free Software Foundation. 9*ae58d1e4SStephen Warren * 10*ae58d1e4SStephen Warren * This program is distributed in the hope it will be useful, but WITHOUT 11*ae58d1e4SStephen Warren * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12*ae58d1e4SStephen Warren * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13*ae58d1e4SStephen Warren * more details. 14*ae58d1e4SStephen Warren * 15*ae58d1e4SStephen Warren * You should have received a copy of the GNU General Public License 16*ae58d1e4SStephen Warren * along with this program. If not, see <http://www.gnu.org/licenses/>. 17*ae58d1e4SStephen Warren */ 18*ae58d1e4SStephen Warren 19*ae58d1e4SStephen Warren #include <linux/i2c.h> 20*ae58d1e4SStephen Warren #include <linux/i2c-mux.h> 21*ae58d1e4SStephen Warren #include <linux/init.h> 22*ae58d1e4SStephen Warren #include <linux/module.h> 23*ae58d1e4SStephen Warren #include <linux/of_i2c.h> 24*ae58d1e4SStephen Warren #include <linux/pinctrl/consumer.h> 25*ae58d1e4SStephen Warren #include <linux/i2c-mux-pinctrl.h> 26*ae58d1e4SStephen Warren #include <linux/platform_device.h> 27*ae58d1e4SStephen Warren #include <linux/slab.h> 28*ae58d1e4SStephen Warren 29*ae58d1e4SStephen Warren struct i2c_mux_pinctrl { 30*ae58d1e4SStephen Warren struct device *dev; 31*ae58d1e4SStephen Warren struct i2c_mux_pinctrl_platform_data *pdata; 32*ae58d1e4SStephen Warren struct pinctrl *pinctrl; 33*ae58d1e4SStephen Warren struct pinctrl_state **states; 34*ae58d1e4SStephen Warren struct pinctrl_state *state_idle; 35*ae58d1e4SStephen Warren struct i2c_adapter *parent; 36*ae58d1e4SStephen Warren struct i2c_adapter **busses; 37*ae58d1e4SStephen Warren }; 38*ae58d1e4SStephen Warren 39*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, 40*ae58d1e4SStephen Warren u32 chan) 41*ae58d1e4SStephen Warren { 42*ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux = data; 43*ae58d1e4SStephen Warren 44*ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->states[chan]); 45*ae58d1e4SStephen Warren } 46*ae58d1e4SStephen Warren 47*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, 48*ae58d1e4SStephen Warren u32 chan) 49*ae58d1e4SStephen Warren { 50*ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux = data; 51*ae58d1e4SStephen Warren 52*ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->state_idle); 53*ae58d1e4SStephen Warren } 54*ae58d1e4SStephen Warren 55*ae58d1e4SStephen Warren #ifdef CONFIG_OF 56*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, 57*ae58d1e4SStephen Warren struct platform_device *pdev) 58*ae58d1e4SStephen Warren { 59*ae58d1e4SStephen Warren struct device_node *np = pdev->dev.of_node; 60*ae58d1e4SStephen Warren int num_names, i, ret; 61*ae58d1e4SStephen Warren struct device_node *adapter_np; 62*ae58d1e4SStephen Warren struct i2c_adapter *adapter; 63*ae58d1e4SStephen Warren 64*ae58d1e4SStephen Warren if (!np) 65*ae58d1e4SStephen Warren return 0; 66*ae58d1e4SStephen Warren 67*ae58d1e4SStephen Warren mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); 68*ae58d1e4SStephen Warren if (!mux->pdata) { 69*ae58d1e4SStephen Warren dev_err(mux->dev, 70*ae58d1e4SStephen Warren "Cannot allocate i2c_mux_pinctrl_platform_data\n"); 71*ae58d1e4SStephen Warren return -ENOMEM; 72*ae58d1e4SStephen Warren } 73*ae58d1e4SStephen Warren 74*ae58d1e4SStephen Warren num_names = of_property_count_strings(np, "pinctrl-names"); 75*ae58d1e4SStephen Warren if (num_names < 0) { 76*ae58d1e4SStephen Warren dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", 77*ae58d1e4SStephen Warren num_names); 78*ae58d1e4SStephen Warren return num_names; 79*ae58d1e4SStephen Warren } 80*ae58d1e4SStephen Warren 81*ae58d1e4SStephen Warren mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, 82*ae58d1e4SStephen Warren sizeof(*mux->pdata->pinctrl_states) * num_names, 83*ae58d1e4SStephen Warren GFP_KERNEL); 84*ae58d1e4SStephen Warren if (!mux->pdata->pinctrl_states) { 85*ae58d1e4SStephen Warren dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); 86*ae58d1e4SStephen Warren return -ENOMEM; 87*ae58d1e4SStephen Warren } 88*ae58d1e4SStephen Warren 89*ae58d1e4SStephen Warren for (i = 0; i < num_names; i++) { 90*ae58d1e4SStephen Warren ret = of_property_read_string_index(np, "pinctrl-names", i, 91*ae58d1e4SStephen Warren &mux->pdata->pinctrl_states[mux->pdata->bus_count]); 92*ae58d1e4SStephen Warren if (ret < 0) { 93*ae58d1e4SStephen Warren dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", 94*ae58d1e4SStephen Warren ret); 95*ae58d1e4SStephen Warren return ret; 96*ae58d1e4SStephen Warren } 97*ae58d1e4SStephen Warren if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], 98*ae58d1e4SStephen Warren "idle")) { 99*ae58d1e4SStephen Warren if (i != num_names - 1) { 100*ae58d1e4SStephen Warren dev_err(mux->dev, "idle state must be last\n"); 101*ae58d1e4SStephen Warren return -EINVAL; 102*ae58d1e4SStephen Warren } 103*ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle = "idle"; 104*ae58d1e4SStephen Warren } else { 105*ae58d1e4SStephen Warren mux->pdata->bus_count++; 106*ae58d1e4SStephen Warren } 107*ae58d1e4SStephen Warren } 108*ae58d1e4SStephen Warren 109*ae58d1e4SStephen Warren adapter_np = of_parse_phandle(np, "i2c-parent", 0); 110*ae58d1e4SStephen Warren if (!adapter_np) { 111*ae58d1e4SStephen Warren dev_err(mux->dev, "Cannot parse i2c-parent\n"); 112*ae58d1e4SStephen Warren return -ENODEV; 113*ae58d1e4SStephen Warren } 114*ae58d1e4SStephen Warren adapter = of_find_i2c_adapter_by_node(adapter_np); 115*ae58d1e4SStephen Warren if (!adapter) { 116*ae58d1e4SStephen Warren dev_err(mux->dev, "Cannot find parent bus\n"); 117*ae58d1e4SStephen Warren return -ENODEV; 118*ae58d1e4SStephen Warren } 119*ae58d1e4SStephen Warren mux->pdata->parent_bus_num = i2c_adapter_id(adapter); 120*ae58d1e4SStephen Warren put_device(&adapter->dev); 121*ae58d1e4SStephen Warren 122*ae58d1e4SStephen Warren return 0; 123*ae58d1e4SStephen Warren } 124*ae58d1e4SStephen Warren #else 125*ae58d1e4SStephen Warren static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, 126*ae58d1e4SStephen Warren struct platform_device *pdev) 127*ae58d1e4SStephen Warren { 128*ae58d1e4SStephen Warren return 0; 129*ae58d1e4SStephen Warren } 130*ae58d1e4SStephen Warren #endif 131*ae58d1e4SStephen Warren 132*ae58d1e4SStephen Warren static int __devinit i2c_mux_pinctrl_probe(struct platform_device *pdev) 133*ae58d1e4SStephen Warren { 134*ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux; 135*ae58d1e4SStephen Warren int (*deselect)(struct i2c_adapter *, void *, u32); 136*ae58d1e4SStephen Warren int i, ret; 137*ae58d1e4SStephen Warren 138*ae58d1e4SStephen Warren mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); 139*ae58d1e4SStephen Warren if (!mux) { 140*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n"); 141*ae58d1e4SStephen Warren ret = -ENOMEM; 142*ae58d1e4SStephen Warren goto err; 143*ae58d1e4SStephen Warren } 144*ae58d1e4SStephen Warren platform_set_drvdata(pdev, mux); 145*ae58d1e4SStephen Warren 146*ae58d1e4SStephen Warren mux->dev = &pdev->dev; 147*ae58d1e4SStephen Warren 148*ae58d1e4SStephen Warren mux->pdata = pdev->dev.platform_data; 149*ae58d1e4SStephen Warren if (!mux->pdata) { 150*ae58d1e4SStephen Warren ret = i2c_mux_pinctrl_parse_dt(mux, pdev); 151*ae58d1e4SStephen Warren if (ret < 0) 152*ae58d1e4SStephen Warren goto err; 153*ae58d1e4SStephen Warren } 154*ae58d1e4SStephen Warren if (!mux->pdata) { 155*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Missing platform data\n"); 156*ae58d1e4SStephen Warren ret = -ENODEV; 157*ae58d1e4SStephen Warren goto err; 158*ae58d1e4SStephen Warren } 159*ae58d1e4SStephen Warren 160*ae58d1e4SStephen Warren mux->states = devm_kzalloc(&pdev->dev, 161*ae58d1e4SStephen Warren sizeof(*mux->states) * mux->pdata->bus_count, 162*ae58d1e4SStephen Warren GFP_KERNEL); 163*ae58d1e4SStephen Warren if (!mux->states) { 164*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot allocate states\n"); 165*ae58d1e4SStephen Warren ret = -ENOMEM; 166*ae58d1e4SStephen Warren goto err; 167*ae58d1e4SStephen Warren } 168*ae58d1e4SStephen Warren 169*ae58d1e4SStephen Warren mux->busses = devm_kzalloc(&pdev->dev, 170*ae58d1e4SStephen Warren sizeof(mux->busses) * mux->pdata->bus_count, 171*ae58d1e4SStephen Warren GFP_KERNEL); 172*ae58d1e4SStephen Warren if (!mux->states) { 173*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot allocate busses\n"); 174*ae58d1e4SStephen Warren ret = -ENOMEM; 175*ae58d1e4SStephen Warren goto err; 176*ae58d1e4SStephen Warren } 177*ae58d1e4SStephen Warren 178*ae58d1e4SStephen Warren mux->pinctrl = devm_pinctrl_get(&pdev->dev); 179*ae58d1e4SStephen Warren if (IS_ERR(mux->pinctrl)) { 180*ae58d1e4SStephen Warren ret = PTR_ERR(mux->pinctrl); 181*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); 182*ae58d1e4SStephen Warren goto err; 183*ae58d1e4SStephen Warren } 184*ae58d1e4SStephen Warren for (i = 0; i < mux->pdata->bus_count; i++) { 185*ae58d1e4SStephen Warren mux->states[i] = pinctrl_lookup_state(mux->pinctrl, 186*ae58d1e4SStephen Warren mux->pdata->pinctrl_states[i]); 187*ae58d1e4SStephen Warren if (IS_ERR(mux->states[i])) { 188*ae58d1e4SStephen Warren ret = PTR_ERR(mux->states[i]); 189*ae58d1e4SStephen Warren dev_err(&pdev->dev, 190*ae58d1e4SStephen Warren "Cannot look up pinctrl state %s: %d\n", 191*ae58d1e4SStephen Warren mux->pdata->pinctrl_states[i], ret); 192*ae58d1e4SStephen Warren goto err; 193*ae58d1e4SStephen Warren } 194*ae58d1e4SStephen Warren } 195*ae58d1e4SStephen Warren if (mux->pdata->pinctrl_state_idle) { 196*ae58d1e4SStephen Warren mux->state_idle = pinctrl_lookup_state(mux->pinctrl, 197*ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle); 198*ae58d1e4SStephen Warren if (IS_ERR(mux->state_idle)) { 199*ae58d1e4SStephen Warren ret = PTR_ERR(mux->state_idle); 200*ae58d1e4SStephen Warren dev_err(&pdev->dev, 201*ae58d1e4SStephen Warren "Cannot look up pinctrl state %s: %d\n", 202*ae58d1e4SStephen Warren mux->pdata->pinctrl_state_idle, ret); 203*ae58d1e4SStephen Warren goto err; 204*ae58d1e4SStephen Warren } 205*ae58d1e4SStephen Warren 206*ae58d1e4SStephen Warren deselect = i2c_mux_pinctrl_deselect; 207*ae58d1e4SStephen Warren } else { 208*ae58d1e4SStephen Warren deselect = NULL; 209*ae58d1e4SStephen Warren } 210*ae58d1e4SStephen Warren 211*ae58d1e4SStephen Warren mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num); 212*ae58d1e4SStephen Warren if (!mux->parent) { 213*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Parent adapter (%d) not found\n", 214*ae58d1e4SStephen Warren mux->pdata->parent_bus_num); 215*ae58d1e4SStephen Warren ret = -ENODEV; 216*ae58d1e4SStephen Warren goto err; 217*ae58d1e4SStephen Warren } 218*ae58d1e4SStephen Warren 219*ae58d1e4SStephen Warren for (i = 0; i < mux->pdata->bus_count; i++) { 220*ae58d1e4SStephen Warren u32 bus = mux->pdata->base_bus_num ? 221*ae58d1e4SStephen Warren (mux->pdata->base_bus_num + i) : 0; 222*ae58d1e4SStephen Warren 223*ae58d1e4SStephen Warren mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, 224*ae58d1e4SStephen Warren mux, bus, i, 225*ae58d1e4SStephen Warren i2c_mux_pinctrl_select, 226*ae58d1e4SStephen Warren deselect); 227*ae58d1e4SStephen Warren if (!mux->busses[i]) { 228*ae58d1e4SStephen Warren ret = -ENODEV; 229*ae58d1e4SStephen Warren dev_err(&pdev->dev, "Failed to add adapter %d\n", i); 230*ae58d1e4SStephen Warren goto err_del_adapter; 231*ae58d1e4SStephen Warren } 232*ae58d1e4SStephen Warren } 233*ae58d1e4SStephen Warren 234*ae58d1e4SStephen Warren return 0; 235*ae58d1e4SStephen Warren 236*ae58d1e4SStephen Warren err_del_adapter: 237*ae58d1e4SStephen Warren for (; i > 0; i--) 238*ae58d1e4SStephen Warren i2c_del_mux_adapter(mux->busses[i - 1]); 239*ae58d1e4SStephen Warren i2c_put_adapter(mux->parent); 240*ae58d1e4SStephen Warren err: 241*ae58d1e4SStephen Warren return ret; 242*ae58d1e4SStephen Warren } 243*ae58d1e4SStephen Warren 244*ae58d1e4SStephen Warren static int __devexit i2c_mux_pinctrl_remove(struct platform_device *pdev) 245*ae58d1e4SStephen Warren { 246*ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev); 247*ae58d1e4SStephen Warren int i; 248*ae58d1e4SStephen Warren 249*ae58d1e4SStephen Warren for (i = 0; i < mux->pdata->bus_count; i++) 250*ae58d1e4SStephen Warren i2c_del_mux_adapter(mux->busses[i]); 251*ae58d1e4SStephen Warren 252*ae58d1e4SStephen Warren i2c_put_adapter(mux->parent); 253*ae58d1e4SStephen Warren 254*ae58d1e4SStephen Warren return 0; 255*ae58d1e4SStephen Warren } 256*ae58d1e4SStephen Warren 257*ae58d1e4SStephen Warren #ifdef CONFIG_OF 258*ae58d1e4SStephen Warren static const struct of_device_id i2c_mux_pinctrl_of_match[] __devinitconst = { 259*ae58d1e4SStephen Warren { .compatible = "i2c-mux-pinctrl", }, 260*ae58d1e4SStephen Warren {}, 261*ae58d1e4SStephen Warren }; 262*ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); 263*ae58d1e4SStephen Warren #endif 264*ae58d1e4SStephen Warren 265*ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = { 266*ae58d1e4SStephen Warren .driver = { 267*ae58d1e4SStephen Warren .name = "i2c-mux-pinctrl", 268*ae58d1e4SStephen Warren .owner = THIS_MODULE, 269*ae58d1e4SStephen Warren .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), 270*ae58d1e4SStephen Warren }, 271*ae58d1e4SStephen Warren .probe = i2c_mux_pinctrl_probe, 272*ae58d1e4SStephen Warren .remove = __devexit_p(i2c_mux_pinctrl_remove), 273*ae58d1e4SStephen Warren }; 274*ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver); 275*ae58d1e4SStephen Warren 276*ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); 277*ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 278*ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2"); 279*ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl"); 280