150a5ba87SWolfram Sang /* 250a5ba87SWolfram Sang * Pinctrl based I2C DeMultiplexer 350a5ba87SWolfram Sang * 450a5ba87SWolfram Sang * Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> 550a5ba87SWolfram Sang * Copyright (C) 2015-16 by Renesas Electronics Corporation 650a5ba87SWolfram Sang * 750a5ba87SWolfram Sang * This program is free software; you can redistribute it and/or modify it 850a5ba87SWolfram Sang * under the terms of the GNU General Public License as published by the 950a5ba87SWolfram Sang * Free Software Foundation; version 2 of the License. 1050a5ba87SWolfram Sang * 1150a5ba87SWolfram Sang * See the bindings doc for DTS setup and the sysfs doc for usage information. 1250a5ba87SWolfram Sang * (look for filenames containing 'i2c-demux-pinctrl' in Documentation/) 1350a5ba87SWolfram Sang */ 1450a5ba87SWolfram Sang 1550a5ba87SWolfram Sang #include <linux/i2c.h> 1650a5ba87SWolfram Sang #include <linux/init.h> 1750a5ba87SWolfram Sang #include <linux/module.h> 1850a5ba87SWolfram Sang #include <linux/of.h> 1950a5ba87SWolfram Sang #include <linux/pinctrl/consumer.h> 2050a5ba87SWolfram Sang #include <linux/platform_device.h> 2150a5ba87SWolfram Sang #include <linux/slab.h> 2250a5ba87SWolfram Sang #include <linux/sysfs.h> 2350a5ba87SWolfram Sang 2450a5ba87SWolfram Sang struct i2c_demux_pinctrl_chan { 2550a5ba87SWolfram Sang struct device_node *parent_np; 2650a5ba87SWolfram Sang struct i2c_adapter *parent_adap; 2750a5ba87SWolfram Sang struct of_changeset chgset; 2850a5ba87SWolfram Sang }; 2950a5ba87SWolfram Sang 3050a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv { 3150a5ba87SWolfram Sang int cur_chan; 3250a5ba87SWolfram Sang int num_chan; 3350a5ba87SWolfram Sang struct device *dev; 3450a5ba87SWolfram Sang const char *bus_name; 3550a5ba87SWolfram Sang struct i2c_adapter cur_adap; 3650a5ba87SWolfram Sang struct i2c_algorithm algo; 3750a5ba87SWolfram Sang struct i2c_demux_pinctrl_chan chan[]; 3850a5ba87SWolfram Sang }; 3950a5ba87SWolfram Sang 4050a5ba87SWolfram Sang static struct property status_okay = { .name = "status", .length = 3, .value = "ok" }; 4150a5ba87SWolfram Sang 4250a5ba87SWolfram Sang static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) 4350a5ba87SWolfram Sang { 4450a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv = adap->algo_data; 4550a5ba87SWolfram Sang struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap; 4650a5ba87SWolfram Sang 4750a5ba87SWolfram Sang return __i2c_transfer(parent, msgs, num); 4850a5ba87SWolfram Sang } 4950a5ba87SWolfram Sang 5050a5ba87SWolfram Sang static u32 i2c_demux_functionality(struct i2c_adapter *adap) 5150a5ba87SWolfram Sang { 5250a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv = adap->algo_data; 5350a5ba87SWolfram Sang struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap; 5450a5ba87SWolfram Sang 5550a5ba87SWolfram Sang return parent->algo->functionality(parent); 5650a5ba87SWolfram Sang } 5750a5ba87SWolfram Sang 5850a5ba87SWolfram Sang static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan) 5950a5ba87SWolfram Sang { 6050a5ba87SWolfram Sang struct i2c_adapter *adap; 6150a5ba87SWolfram Sang struct pinctrl *p; 6250a5ba87SWolfram Sang int ret; 6350a5ba87SWolfram Sang 6450a5ba87SWolfram Sang ret = of_changeset_apply(&priv->chan[new_chan].chgset); 6550a5ba87SWolfram Sang if (ret) 6650a5ba87SWolfram Sang goto err; 6750a5ba87SWolfram Sang 6850a5ba87SWolfram Sang adap = of_find_i2c_adapter_by_node(priv->chan[new_chan].parent_np); 6950a5ba87SWolfram Sang if (!adap) { 7050a5ba87SWolfram Sang ret = -ENODEV; 71ce8cb803SWolfram Sang goto err_with_revert; 7250a5ba87SWolfram Sang } 7350a5ba87SWolfram Sang 7450a5ba87SWolfram Sang p = devm_pinctrl_get_select(adap->dev.parent, priv->bus_name); 7550a5ba87SWolfram Sang if (IS_ERR(p)) { 7650a5ba87SWolfram Sang ret = PTR_ERR(p); 7750a5ba87SWolfram Sang goto err_with_put; 7850a5ba87SWolfram Sang } 7950a5ba87SWolfram Sang 8050a5ba87SWolfram Sang priv->chan[new_chan].parent_adap = adap; 8150a5ba87SWolfram Sang priv->cur_chan = new_chan; 8250a5ba87SWolfram Sang 8350a5ba87SWolfram Sang /* Now fill out current adapter structure. cur_chan must be up to date */ 8450a5ba87SWolfram Sang priv->algo.master_xfer = i2c_demux_master_xfer; 8550a5ba87SWolfram Sang priv->algo.functionality = i2c_demux_functionality; 8650a5ba87SWolfram Sang 8750a5ba87SWolfram Sang snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name), 8850a5ba87SWolfram Sang "i2c-demux (master i2c-%d)", i2c_adapter_id(adap)); 8950a5ba87SWolfram Sang priv->cur_adap.owner = THIS_MODULE; 9050a5ba87SWolfram Sang priv->cur_adap.algo = &priv->algo; 9150a5ba87SWolfram Sang priv->cur_adap.algo_data = priv; 9250a5ba87SWolfram Sang priv->cur_adap.dev.parent = priv->dev; 9350a5ba87SWolfram Sang priv->cur_adap.class = adap->class; 9450a5ba87SWolfram Sang priv->cur_adap.retries = adap->retries; 9550a5ba87SWolfram Sang priv->cur_adap.timeout = adap->timeout; 9650a5ba87SWolfram Sang priv->cur_adap.quirks = adap->quirks; 9750a5ba87SWolfram Sang priv->cur_adap.dev.of_node = priv->dev->of_node; 9850a5ba87SWolfram Sang ret = i2c_add_adapter(&priv->cur_adap); 9950a5ba87SWolfram Sang if (ret < 0) 10050a5ba87SWolfram Sang goto err_with_put; 10150a5ba87SWolfram Sang 10250a5ba87SWolfram Sang return 0; 10350a5ba87SWolfram Sang 10450a5ba87SWolfram Sang err_with_put: 10550a5ba87SWolfram Sang i2c_put_adapter(adap); 106ce8cb803SWolfram Sang err_with_revert: 107ce8cb803SWolfram Sang of_changeset_revert(&priv->chan[new_chan].chgset); 10850a5ba87SWolfram Sang err: 10950a5ba87SWolfram Sang dev_err(priv->dev, "failed to setup demux-adapter %d (%d)\n", new_chan, ret); 11050a5ba87SWolfram Sang return ret; 11150a5ba87SWolfram Sang } 11250a5ba87SWolfram Sang 11350a5ba87SWolfram Sang static int i2c_demux_deactivate_master(struct i2c_demux_pinctrl_priv *priv) 11450a5ba87SWolfram Sang { 11550a5ba87SWolfram Sang int ret, cur = priv->cur_chan; 11650a5ba87SWolfram Sang 11750a5ba87SWolfram Sang if (cur < 0) 11850a5ba87SWolfram Sang return 0; 11950a5ba87SWolfram Sang 12050a5ba87SWolfram Sang i2c_del_adapter(&priv->cur_adap); 12150a5ba87SWolfram Sang i2c_put_adapter(priv->chan[cur].parent_adap); 12250a5ba87SWolfram Sang 12350a5ba87SWolfram Sang ret = of_changeset_revert(&priv->chan[cur].chgset); 12450a5ba87SWolfram Sang 12550a5ba87SWolfram Sang priv->chan[cur].parent_adap = NULL; 12650a5ba87SWolfram Sang priv->cur_chan = -EINVAL; 12750a5ba87SWolfram Sang 12850a5ba87SWolfram Sang return ret; 12950a5ba87SWolfram Sang } 13050a5ba87SWolfram Sang 13150a5ba87SWolfram Sang static int i2c_demux_change_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan) 13250a5ba87SWolfram Sang { 13350a5ba87SWolfram Sang int ret; 13450a5ba87SWolfram Sang 13550a5ba87SWolfram Sang if (new_chan == priv->cur_chan) 13650a5ba87SWolfram Sang return 0; 13750a5ba87SWolfram Sang 13850a5ba87SWolfram Sang ret = i2c_demux_deactivate_master(priv); 13950a5ba87SWolfram Sang if (ret) 14050a5ba87SWolfram Sang return ret; 14150a5ba87SWolfram Sang 14250a5ba87SWolfram Sang return i2c_demux_activate_master(priv, new_chan); 14350a5ba87SWolfram Sang } 14450a5ba87SWolfram Sang 145c0c508a4SBen Hutchings static ssize_t available_masters_show(struct device *dev, 146c0c508a4SBen Hutchings struct device_attribute *attr, 14750a5ba87SWolfram Sang char *buf) 14850a5ba87SWolfram Sang { 14950a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); 15050a5ba87SWolfram Sang int count = 0, i; 15150a5ba87SWolfram Sang 15250a5ba87SWolfram Sang for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++) 153c0c508a4SBen Hutchings count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%s%c", 154c0c508a4SBen Hutchings i, priv->chan[i].parent_np->full_name, 155c0c508a4SBen Hutchings i == priv->num_chan - 1 ? '\n' : ' '); 15650a5ba87SWolfram Sang 15750a5ba87SWolfram Sang return count; 15850a5ba87SWolfram Sang } 159c0c508a4SBen Hutchings static DEVICE_ATTR_RO(available_masters); 16050a5ba87SWolfram Sang 161c0c508a4SBen Hutchings static ssize_t current_master_show(struct device *dev, 162c0c508a4SBen Hutchings struct device_attribute *attr, 163c0c508a4SBen Hutchings char *buf) 164c0c508a4SBen Hutchings { 165c0c508a4SBen Hutchings struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); 166c0c508a4SBen Hutchings 167c0c508a4SBen Hutchings return sprintf(buf, "%d\n", priv->cur_chan); 168c0c508a4SBen Hutchings } 169c0c508a4SBen Hutchings 170c0c508a4SBen Hutchings static ssize_t current_master_store(struct device *dev, 171c0c508a4SBen Hutchings struct device_attribute *attr, 17250a5ba87SWolfram Sang const char *buf, size_t count) 17350a5ba87SWolfram Sang { 17450a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev); 17550a5ba87SWolfram Sang unsigned int val; 17650a5ba87SWolfram Sang int ret; 17750a5ba87SWolfram Sang 17850a5ba87SWolfram Sang ret = kstrtouint(buf, 0, &val); 17950a5ba87SWolfram Sang if (ret < 0) 18050a5ba87SWolfram Sang return ret; 18150a5ba87SWolfram Sang 18250a5ba87SWolfram Sang if (val >= priv->num_chan) 18350a5ba87SWolfram Sang return -EINVAL; 18450a5ba87SWolfram Sang 18550a5ba87SWolfram Sang ret = i2c_demux_change_master(priv, val); 18650a5ba87SWolfram Sang 18750a5ba87SWolfram Sang return ret < 0 ? ret : count; 18850a5ba87SWolfram Sang } 189c0c508a4SBen Hutchings static DEVICE_ATTR_RW(current_master); 19050a5ba87SWolfram Sang 19150a5ba87SWolfram Sang static int i2c_demux_pinctrl_probe(struct platform_device *pdev) 19250a5ba87SWolfram Sang { 19350a5ba87SWolfram Sang struct device_node *np = pdev->dev.of_node; 19450a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv; 19550a5ba87SWolfram Sang int num_chan, i, j, err; 19650a5ba87SWolfram Sang 19750a5ba87SWolfram Sang num_chan = of_count_phandle_with_args(np, "i2c-parent", NULL); 19850a5ba87SWolfram Sang if (num_chan < 2) { 19950a5ba87SWolfram Sang dev_err(&pdev->dev, "Need at least two I2C masters to switch\n"); 20050a5ba87SWolfram Sang return -EINVAL; 20150a5ba87SWolfram Sang } 20250a5ba87SWolfram Sang 20350a5ba87SWolfram Sang priv = devm_kzalloc(&pdev->dev, sizeof(*priv) 20450a5ba87SWolfram Sang + num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL); 20550a5ba87SWolfram Sang if (!priv) 20650a5ba87SWolfram Sang return -ENOMEM; 20750a5ba87SWolfram Sang 20850a5ba87SWolfram Sang err = of_property_read_string(np, "i2c-bus-name", &priv->bus_name); 20950a5ba87SWolfram Sang if (err) 21050a5ba87SWolfram Sang return err; 21150a5ba87SWolfram Sang 21250a5ba87SWolfram Sang for (i = 0; i < num_chan; i++) { 21350a5ba87SWolfram Sang struct device_node *adap_np; 21450a5ba87SWolfram Sang 21550a5ba87SWolfram Sang adap_np = of_parse_phandle(np, "i2c-parent", i); 21650a5ba87SWolfram Sang if (!adap_np) { 21750a5ba87SWolfram Sang dev_err(&pdev->dev, "can't get phandle for parent %d\n", i); 21850a5ba87SWolfram Sang err = -ENOENT; 21950a5ba87SWolfram Sang goto err_rollback; 22050a5ba87SWolfram Sang } 22150a5ba87SWolfram Sang priv->chan[i].parent_np = adap_np; 22250a5ba87SWolfram Sang 22350a5ba87SWolfram Sang of_changeset_init(&priv->chan[i].chgset); 22450a5ba87SWolfram Sang of_changeset_update_property(&priv->chan[i].chgset, adap_np, &status_okay); 22550a5ba87SWolfram Sang } 22650a5ba87SWolfram Sang 22750a5ba87SWolfram Sang priv->num_chan = num_chan; 22850a5ba87SWolfram Sang priv->dev = &pdev->dev; 22950a5ba87SWolfram Sang 23050a5ba87SWolfram Sang platform_set_drvdata(pdev, priv); 23150a5ba87SWolfram Sang 23250a5ba87SWolfram Sang /* switch to first parent as active master */ 23350a5ba87SWolfram Sang i2c_demux_activate_master(priv, 0); 23450a5ba87SWolfram Sang 235c0c508a4SBen Hutchings err = device_create_file(&pdev->dev, &dev_attr_available_masters); 23650a5ba87SWolfram Sang if (err) 23750a5ba87SWolfram Sang goto err_rollback; 23850a5ba87SWolfram Sang 239c0c508a4SBen Hutchings err = device_create_file(&pdev->dev, &dev_attr_current_master); 240c0c508a4SBen Hutchings if (err) 241c0c508a4SBen Hutchings goto err_rollback_available; 242c0c508a4SBen Hutchings 24350a5ba87SWolfram Sang return 0; 24450a5ba87SWolfram Sang 245c0c508a4SBen Hutchings err_rollback_available: 246c0c508a4SBen Hutchings device_remove_file(&pdev->dev, &dev_attr_available_masters); 24750a5ba87SWolfram Sang err_rollback: 24850a5ba87SWolfram Sang for (j = 0; j < i; j++) { 24950a5ba87SWolfram Sang of_node_put(priv->chan[j].parent_np); 25050a5ba87SWolfram Sang of_changeset_destroy(&priv->chan[j].chgset); 25150a5ba87SWolfram Sang } 25250a5ba87SWolfram Sang 25350a5ba87SWolfram Sang return err; 25450a5ba87SWolfram Sang } 25550a5ba87SWolfram Sang 25650a5ba87SWolfram Sang static int i2c_demux_pinctrl_remove(struct platform_device *pdev) 25750a5ba87SWolfram Sang { 25850a5ba87SWolfram Sang struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev); 25950a5ba87SWolfram Sang int i; 26050a5ba87SWolfram Sang 261c0c508a4SBen Hutchings device_remove_file(&pdev->dev, &dev_attr_current_master); 262c0c508a4SBen Hutchings device_remove_file(&pdev->dev, &dev_attr_available_masters); 26350a5ba87SWolfram Sang 26450a5ba87SWolfram Sang i2c_demux_deactivate_master(priv); 26550a5ba87SWolfram Sang 26650a5ba87SWolfram Sang for (i = 0; i < priv->num_chan; i++) { 26750a5ba87SWolfram Sang of_node_put(priv->chan[i].parent_np); 26850a5ba87SWolfram Sang of_changeset_destroy(&priv->chan[i].chgset); 26950a5ba87SWolfram Sang } 27050a5ba87SWolfram Sang 27150a5ba87SWolfram Sang return 0; 27250a5ba87SWolfram Sang } 27350a5ba87SWolfram Sang 27450a5ba87SWolfram Sang static const struct of_device_id i2c_demux_pinctrl_of_match[] = { 27550a5ba87SWolfram Sang { .compatible = "i2c-demux-pinctrl", }, 27650a5ba87SWolfram Sang {}, 27750a5ba87SWolfram Sang }; 27850a5ba87SWolfram Sang MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match); 27950a5ba87SWolfram Sang 28050a5ba87SWolfram Sang static struct platform_driver i2c_demux_pinctrl_driver = { 28150a5ba87SWolfram Sang .driver = { 28250a5ba87SWolfram Sang .name = "i2c-demux-pinctrl", 28350a5ba87SWolfram Sang .of_match_table = i2c_demux_pinctrl_of_match, 28450a5ba87SWolfram Sang }, 28550a5ba87SWolfram Sang .probe = i2c_demux_pinctrl_probe, 28650a5ba87SWolfram Sang .remove = i2c_demux_pinctrl_remove, 28750a5ba87SWolfram Sang }; 28850a5ba87SWolfram Sang module_platform_driver(i2c_demux_pinctrl_driver); 28950a5ba87SWolfram Sang 29050a5ba87SWolfram Sang MODULE_DESCRIPTION("pinctrl-based I2C demux driver"); 29150a5ba87SWolfram Sang MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); 29250a5ba87SWolfram Sang MODULE_LICENSE("GPL v2"); 29350a5ba87SWolfram Sang MODULE_ALIAS("platform:i2c-demux-pinctrl"); 294