Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757455AbbFQQoH (ORCPT ); Wed, 17 Jun 2015 12:44:07 -0400 Received: from mail-bn1bn0105.outbound.protection.outlook.com ([157.56.110.105]:29107 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756864AbbFQQoC (ORCPT ); Wed, 17 Jun 2015 12:44:02 -0400 Authentication-Results: barco.com; dkim=none (message not signed) header.d=none; Message-ID: <5581A3CA.4050805@freescale.com> Date: Wed, 17 Jun 2015 09:43:54 -0700 From: York Sun User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Thunderbird/31.7.0 MIME-Version: 1.0 To: Alexander Sverdlin , CC: , , Peter Korsgaard Subject: Re: [PATCH] driver/i2c/mux: Add register based mux i2c-mux-reg References: <1434475692-4611-1-git-send-email-yorksun@freescale.com> <55818C3C.8000704@nokia.com> In-Reply-To: <55818C3C.8000704@nokia.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-Originating-IP: [192.88.168.49] X-ClientProxiedBy: SN1PR12CA0021.namprd12.prod.outlook.com (25.162.96.159) To BY2PR03MB158.namprd03.prod.outlook.com (10.242.36.15) X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB158;2:ARQ1hmNKnE6AAa9DT6qw8QhNkWPTKn61izSXp0W+GaL1vCWZjxsqvXRXH1Z6wUiv;2:wXsIQiSJT5PlSQ9UqLeSGXLKdrsHgmbFsS+EpBcxmMj6QNcOW6CuI3D9iV8qoDLBBtPdC/7tiFwBNEiV8SUpoIn7/bMm93lcvLchZTW/S/Tp2TJJCoqRuwJNTWCwifyTtKWZqFyRqceY7rA1RGW8gA==;6:7ShLaVyQdGr3YUBcm0zJ8gAd+l4V8mmmTVEKYO+S3zFl/zidYT89+EiyOB2eRGXS7wFz12XGU1o8OYGofuFwtUnA8vkBmZe0Jfe05IuOddExexRrXbEnkYmZAYKwlFj1iOK3RiU+VYHUECQmZMZN7OqCL+EiOy7hC+uL0bQf3QlFqTHB20z0t7epkKGJZMyTQS6jtXpyhFm117PXT79h5oP12KHTBE9lRTJjThw2idYcBDp5Va5Za9D6GCDhzQuDg88jjrF6ui3TlujeYs9Tc3yUBp6U0b7iTF2D7VoovLO9D27OJKwdiLQgMWyuE5vWA+FuDhU4Dj9vqex6s/D5WQy6Os43bTdYpY3qIVkhdN1XRglizhno/PaysWz8ka8P2fj1vqWcYqVng0NnaWKgwoE2pTxGp601svN7+1SZC3a3yLGMch7N86NNlvsjRm4zFO3QJOkhuqD23nDAuhchuvaM+StAvw0683MEoS/ebK3ajE2VwDZuWeWWcD9kaaluAdaXdvRquyjra2G0kVQhTNyX+ICNM2PpbP0v+5TMRB1TErR4kYFVIvcJBb3RiLT5/N6g+q+lXFRE2zo2ezRlTw2KvxzOhqSxuG112ZodwKQ= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BY2PR03MB158; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(520003)(3002001);SRVR:BY2PR03MB158;BCL:0;PCL:0;RULEID:;SRVR:BY2PR03MB158; X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB158;3:5TBY2SGtVeMQlyKe6WjtSh6+uyuErEfTswk7Z/bxxGfPJA10l3zFct/LvEps98NidK0uQkGKL/XTZsUHdfDK/0Ml9oVgvMrEOB+rZyhHTbHsZMYVG2moF5MIIWR2WSOlnL3oXzbWQPlMwleRHgW27+CZRG3TKRLufSNPu76d+MK3APZDjshBblFnH3PjttvR1FSC3yG1jKzGjc6ry/U0fUETpHoe8q7N08kr3ZLDhwc3rGpBpe/e17OKsx5+QdJSpDXPbBsLz/n9VHCFhXNFwDvsYr4dmyOW44PWpFDWv6HxXiPoeP16L/hf+cLddhd0 X-Forefront-PRVS: 0610D16BBE X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(6009001)(6049001)(479174004)(377454003)(51704005)(24454002)(23676002)(122386002)(40100003)(86362001)(87976001)(77096005)(33656002)(189998001)(64126003)(5001960100002)(62966003)(83506001)(42186005)(77156002)(5001920100001)(19580395003)(80316001)(19580405001)(92566002)(46102003)(2950100001)(76176999)(87266999)(50986999)(65816999)(230783001)(59896002)(5001770100001)(66066001)(47776003)(65956001)(65806001)(54356999)(36756003)(50466002);DIR:OUT;SFP:1102;SCL:1;SRVR:BY2PR03MB158;H:[10.214.81.216];FPR:;SPF:None;MLV:sfv;LANG:en; X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtCWTJQUjAzTUIxNTg7OTp1QlZZTXd5V1hHVCtaZUNZc3hSaC84Q1p3bXdE?= =?utf-8?B?cGpQU3B0ZVZQY09BTkIwOUFDVVNwNVhlWEpueXo5UVgxSHh4VHpEeWdIVE9O?= =?utf-8?B?ZjJKNUk2azJTa0gyNHBHYzc2azdSYnZDYngyUWRrQ1NrWlIwTGtaK0YzSTFT?= =?utf-8?B?dkFSRDFEYk94Z2JQUzZKY1ZMdmlxdkU0NjFpcElibzZlM2FFOGhVdjBVS2Z5?= =?utf-8?B?Vm5tU2V6Ry85bkNjTWNqY3UyMlRaME9SOTdVaG5RZm9IRjhVQjd1Wll5VkFD?= =?utf-8?B?bFBWRmd6c2toQ0RDdFZnRGxRZitIeHM5ZEZBVWxzL0dmU3RMOVcvMjhkb3hE?= =?utf-8?B?NjB0Wm01MkZOM0gxNkpSbXZvRThEeEdDeTRKZWhiZWhQUWdsN1grSmJ1TGxp?= =?utf-8?B?cjNlOUFFdjVDdU91TTd0R1VsNUovZ25nNE8xanNsSkxzL01Va1VtMDVjY0Nk?= =?utf-8?B?c3VCbGRNN2J4QzVQSnliUm5xUGs3aVJYWER5TjRIbDJNRmY0aGpxMzJyNFZF?= =?utf-8?B?UTE2ODFvd25VM1RYekh4L1FEdElNN1pVNVZKN0NJOWwzZG5XNTQwbkdlazdT?= =?utf-8?B?UWc0UlBaTWVNL1BpaVhxNGdUMnNISjBFMzg2a1ZIR2dTZGpFcDl0Y1hqbUlU?= =?utf-8?B?NE9RWUZSd1k4MG4xRVpCS0VpUXEzSVVyRmYweldaMEd4bVJkT2RpeDNCbUNO?= =?utf-8?B?VTBNb0ZJWWtIaVI4Ti9aVjVpR2UvdG9Dc3ZoSFIyTDM0Q2RsdHRvVTRNbERW?= =?utf-8?B?dDVhVVN0RVh4U29mOFZJcGdJeW9NMWp1aGczbGlWTnJEbkY1UmEyYzVzOUx3?= =?utf-8?B?OWZoSXliZEY1d1FFblMyeElXZzloOEQwNFdBZ2ZUbmpJKzlOaTBUM29xNUZ4?= =?utf-8?B?NlBFZHl0Q1N0YXFycGlJZGc4TzRDS21xc202NFVnU01MOFFhSWtFRVpETVow?= =?utf-8?B?dDRJeWQzS2ZZeVlBelkzdFdWdDk3WHZKWS9TRnFXN1V3SW5BaDFVRDBrejJ6?= =?utf-8?B?dUhNcFVNTDBVMmY5NW1qMjRkRlVQNG9semVCTzJnbkJPZ09NUHFLYWc1dmlJ?= =?utf-8?B?TEtLait4Z05wTGpBSHlRN1hiSHRlZ3ZuQVVobVZDZ2lFNnZZMVFqVUV3KzhF?= =?utf-8?B?OGdHQlU1RWRoZkpxbkJSWlFua3luNkpsT2luSnJJclhodm5Uc1ExL25rTHFt?= =?utf-8?B?UnI4dEVjQjJiVnJwc2JQT0VlREQzT1h6WnJsOGpxbzIxejFLaG1FVm1wazQv?= =?utf-8?B?L3liYmxMbVRleTZhUmlrTUVsMG1UNS9XMlUwbUNHeXp5bXI5UHJIa09QbWJG?= =?utf-8?B?YmN5c05XbzZ3Um9qZngzVjlhcnNUd2dQMm93RGh1eFBCVy9LMjhuRjYxNVh5?= =?utf-8?B?cW9mRWQ5VFBJY3MvVTVEQ1FObHd3U0IxdVVpbGZUYUMzS0NEVzhRSElrTGxH?= =?utf-8?Q?+Qm9u53PdTXkwS+wObP/r9iq/?= X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB158;3:yonjg5g9AFNHAklsupJUcKgcrHFqqVKGFh0OKKIVOtdehbgSO7MKsS9Ulca+NecqhPXW30Gk7UrfC/J1TYRQ/l5feeIbS/iVSUHX6mf3oERfEUdbNGZ0pMP+8jY+3sc5Q3Jp2JZGQ0tvJ/WcBYNFXg==;10:dVEAfb40fomPiYwZ4iwIWLvs8iIWhBbGGo3rrLiLXPzhIBqD+XssveSR+4VDjyCDYVLzrgPCILDYDw6zUfTWznouyAYpPw+Z9N8UYTGiXKY=;6:aJ158k3fjepYB9yTuvUC5Pjf+HM6FZND7co2eDPhkGPXYhPYO3tbBA3OE81ygdDn X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jun 2015 16:43:58.3395 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2PR03MB158 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15326 Lines: 485 On 06/17/2015 08:03 AM, Alexander Sverdlin wrote: > Hi! > > On 16/06/15 19:28, ext York Sun wrote: >> Based on i2c-mux-gpio driver, similarly the register based mux >> switch from one bus to another by setting a single register. >> The register can be on PCIe bus, local bus, or any memory-mapped >> address. >> >> Signed-off-by: York Sun >> CC: Wolfram Sang >> CC: Peter Korsgaard >> --- >> .../devicetree/bindings/i2c/i2c-mux-reg.txt | 69 ++++++ >> drivers/i2c/muxes/Kconfig | 11 + >> drivers/i2c/muxes/Makefile | 1 + >> drivers/i2c/muxes/i2c-mux-reg.c | 239 ++++++++++++++++++++ >> drivers/i2c/muxes/i2c-mux-reg.h | 38 ++++ >> 5 files changed, 358 insertions(+) >> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt >> create mode 100644 drivers/i2c/muxes/i2c-mux-reg.c >> create mode 100644 drivers/i2c/muxes/i2c-mux-reg.h >> >> diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt >> new file mode 100644 >> index 0000000..ad7cc4f >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt >> @@ -0,0 +1,69 @@ >> +Register-based I2C Bus Mux >> + >> +This binding describes an I2C bus multiplexer that uses a single regsiter > ^^^^^^^^ Nice catch. > >> +to route the I2C signals. >> + >> +Required properties: >> +- compatible: i2c-mux-reg >> +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side >> + port is connected to. >> +* Standard I2C mux properties. See mux.txt in this directory. >> +* I2C child bus nodes. See mux.txt in this directory. >> + >> +Optional properties: >> +- reg: this pair of specifies the register to control the mux. >> + The depends on its parent node. It can be any memory-mapped >> + address. If omitted, the resource of this device will be used. >> +- idle-state: value to set the muxer to when idle. When no value is >> + given, it defaults to the last value used. >> + >> +For each i2c child node, an I2C child bus will be created. They will >> +be numbered based on their order in the device tree. >> + >> +Whenever an access is made to a device on a child bus, the value set >> +in the revelant node's reg property will be output to the register. >> + >> +If an idle state is defined, using the idle-state (optional) property, >> +whenever an access is not being made to a device on a child bus, the >> +register will be set according to the idle value. >> + >> +If an idle state is not defined, the most recently used value will be >> +left programmed into the register. >> + >> +Example of a mux on PCIe card, the host is a powerpc SoC (big endian): >> + >> + i2c-mux { >> + /* the depends on the address translation >> + * of the parent device. If omitted, device resource >> + * will be used instead. >> + */ >> + reg = <0x6028 0x4>; >> + compatible = "i2c-mux-reg"; >> + #address-cells = <1>; >> + #size-cells = <0>; >> + i2c-parent = <&i2c1>; >> + i2c@0 { >> + reg = <0>; >> + #address-cells = <1>; >> + #size-cells = <0>; >> + >> + si5338: clock-generator@70 { >> + compatible = "silabs,si5338"; >> + reg = <0x70>; >> + /* other stuff */ >> + }; >> + }; >> + >> + i2c@1 { >> + /* data is in little endian on pcie bus */ >> + reg = <0x01000000>; >> + #address-cells = <1>; >> + #size-cells = <0>; >> + >> + si5338: clock-generator@70 { >> + compatible = "silabs,si5338"; >> + reg = <0x70>; >> + /* other stuff */ >> + }; >> + }; >> + }; >> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig >> index f6d313e..77c1257 100644 >> --- a/drivers/i2c/muxes/Kconfig >> +++ b/drivers/i2c/muxes/Kconfig >> @@ -29,6 +29,17 @@ config I2C_MUX_GPIO >> This driver can also be built as a module. If so, the module >> will be called i2c-mux-gpio. >> >> +config I2C_MUX_REG >> + tristate "Register-based I2C multiplexer" >> + help >> + If you say yes to this option, support will be included for a >> + register based I2C multiplexer. This driver provides access to >> + I2C busses connected through a MUX, which is controlled >> + by a sinple register. >> + >> + This driver can also be built as a module. If so, the module >> + will be called i2c-mux-reg. >> + >> config I2C_MUX_PCA9541 >> tristate "NXP PCA9541 I2C Master Selector" >> help >> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile >> index 465778b..bc517bb 100644 >> --- a/drivers/i2c/muxes/Makefile >> +++ b/drivers/i2c/muxes/Makefile >> @@ -4,6 +4,7 @@ >> obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o >> >> obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o >> +obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o >> obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o >> obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o >> obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o >> diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c >> new file mode 100644 >> index 0000000..03ce858 >> --- /dev/null >> +++ b/drivers/i2c/muxes/i2c-mux-reg.c >> @@ -0,0 +1,239 @@ >> +/* >> + * I2C multiplexer using a single register >> + * >> + * Copyright 2015 Freescale Semiconductor >> + * York Sun >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 as >> + * published by the Free Software Foundation. >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include "i2c-mux-reg.h" >> + >> +struct regmux { >> + struct i2c_adapter *parent; >> + struct i2c_adapter **adap; /* child busses */ >> + struct i2c_mux_reg_platform_data data; >> +}; >> + >> +static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan) >> +{ >> + if (!mux->data.reg || chan < 0 || chan > mux->data.n_values) >> + return -EINVAL; >> + >> + *mux->data.reg = mux->data.values[chan]; > > Oh, I believe, this will not work. This will not work for PCI, because it's > a "posted" bus, it will not work on certain high-performance SoC like Octeon, > where writes to the memory are buffered... Finally your I2C transfer can > be performed before this write will be completed (if at all), because there > is no synchronization here. You probably want to use some iowrite*(), but > maybe also accomplish it with readback. There are ARM architectures where > writes to memory mapped HW modules are only ordered inside one module, but > not ordered between the modules, etc... Without volatile a good compiler > will optimize this statement away completely as it produces no "side effect". Agree I should have used iowrite. > >> + >> + return 0; >> +} >> + >> +static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data, >> + unsigned int chan) >> +{ >> + struct regmux *mux = data; >> + >> + return i2c_mux_reg_set(mux, chan); >> +} >> + >> +static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, >> + unsigned int chan) >> +{ >> + struct regmux *mux = data; >> + >> + return i2c_mux_reg_set(mux, mux->data.idle); >> +} >> + >> +#ifdef CONFIG_OF >> +static int i2c_mux_reg_probe_dt(struct regmux *mux, >> + struct platform_device *pdev) >> +{ >> + struct device_node *np = pdev->dev.of_node; >> + struct device_node *adapter_np, *child; >> + struct i2c_adapter *adapter; >> + struct resource res; >> + unsigned *values; >> + int i = 0; >> + >> + if (!np) >> + return -ENODEV; >> + >> + adapter_np = of_parse_phandle(np, "i2c-parent", 0); >> + if (!adapter_np) { >> + dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); >> + return -ENODEV; >> + } >> + adapter = of_find_i2c_adapter_by_node(adapter_np); >> + if (!adapter) { >> + dev_err(&pdev->dev, "Cannot find parent bus\n"); >> + return -EPROBE_DEFER; >> + } >> + mux->parent = adapter; >> + mux->data.parent = i2c_adapter_id(adapter); >> + put_device(&adapter->dev); >> + >> + mux->data.n_values = of_get_child_count(np); >> + >> + values = devm_kzalloc(&pdev->dev, >> + sizeof(*mux->data.values) * mux->data.n_values, >> + GFP_KERNEL); >> + if (!values) { >> + dev_err(&pdev->dev, "Cannot allocate values array"); >> + return -ENOMEM; >> + } >> + >> + for_each_child_of_node(np, child) { >> + of_property_read_u32(child, "reg", values + i); >> + i++; >> + } >> + mux->data.values = values; >> + >> + if (of_property_read_u32(np, "idle-state", &mux->data.idle)) >> + mux->data.idle = I2C_MUX_REG_NO_IDLE; >> + >> + /* map address from "reg" if exists */ >> + if (!of_address_to_resource(np, 0, &res)) { >> + mux->data.reg = devm_ioremap_resource(&pdev->dev, &res); >> + if (IS_ERR(mux->data.reg)) >> + return PTR_ERR(mux->data.reg); >> + } >> + >> + return 0; >> +} >> +#else >> +static int i2c_mux_reg_probe_dt(struct gpiomux *mux, >> + struct platform_device *pdev) >> +{ >> + return 0; >> +} >> +#endif >> + >> +static int i2c_mux_reg_probe(struct platform_device *pdev) >> +{ >> + struct regmux *mux; >> + struct i2c_adapter *parent; >> + struct resource *res; >> + int (*deselect)(struct i2c_adapter *, void *, u32); >> + unsigned int initial_state, class; >> + int i, ret, nr; >> + >> + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); >> + if (!mux) >> + return -ENOMEM; >> + >> + platform_set_drvdata(pdev, mux); >> + >> + if (dev_get_platdata(&pdev->dev)) { >> + memcpy(&mux->data, dev_get_platdata(&pdev->dev), >> + sizeof(mux->data)); >> + >> + parent = i2c_get_adapter(mux->data.parent); >> + if (!parent) { >> + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", >> + mux->data.parent); >> + return -EPROBE_DEFER; >> + } >> + mux->parent = parent; >> + i2c_put_adapter(parent); > > You should only give up this reference in remove() function of your driver. > >> + } else { >> + ret = i2c_mux_reg_probe_dt(mux, pdev); >> + if (ret < 0) { >> + dev_err(&pdev->dev, "Cannot locate device tree"); >> + return ret; >> + } >> + } >> + >> + if (!mux->data.reg) { >> + dev_info(&pdev->dev, >> + "Register not set, using platform resource\n"); >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + mux->data.reg = devm_ioremap_resource(&pdev->dev, res); >> + if (IS_ERR(mux->data.reg)) >> + return PTR_ERR(mux->data.reg); >> + } >> + >> + mux->adap = devm_kzalloc(&pdev->dev, >> + sizeof(*mux->adap) * mux->data.n_values, >> + GFP_KERNEL); >> + if (!mux->adap) { >> + dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); >> + return -ENOMEM; >> + } >> + >> + if (mux->data.idle != I2C_MUX_REG_NO_IDLE) { >> + initial_state = mux->data.idle; >> + deselect = i2c_mux_reg_deselect; >> + } else { >> + initial_state = mux->data.values[0]; >> + deselect = NULL; >> + } >> + >> + for (i = 0; i < mux->data.n_values; i++) { >> + nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; >> + class = mux->data.classes ? mux->data.classes[i] : 0; >> + >> + mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux, >> + nr, mux->data.values[i], >> + class, i2c_mux_reg_select, >> + deselect); >> + if (!mux->adap[i]) { >> + ret = -ENODEV; >> + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); >> + goto add_adapter_failed; >> + } >> + } >> + >> + dev_info(&pdev->dev, "%d port mux on %s adapter\n", >> + mux->data.n_values, mux->parent->name); > > This could be dev_dbg() also, IMHO, as this information has very little value. Sure. > >> + >> + return 0; >> + >> +add_adapter_failed: >> + for (; i > 0; i--) >> + i2c_del_mux_adapter(mux->adap[i - 1]); >> + >> + return ret; >> +} >> + >> +static int i2c_mux_reg_remove(struct platform_device *pdev) >> +{ >> + struct regmux *mux = platform_get_drvdata(pdev); >> + int i; >> + >> + for (i = 0; i < mux->data.n_values; i++) >> + i2c_del_mux_adapter(mux->adap[i]); >> + >> + i2c_put_adapter(mux->parent); >> + >> + dev_info(&pdev->dev, "Removed\n"); > > I would say, only dev_dbg() is allowed here, otherwise it's a noise. > >> + >> + return 0; >> +} >> + >> +static const struct of_device_id i2c_mux_reg_of_match[] = { >> + { .compatible = "i2c-mux-reg", }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match); >> + >> +static struct platform_driver i2c_mux_reg_driver = { >> + .probe = i2c_mux_reg_probe, >> + .remove = i2c_mux_reg_remove, >> + .driver = { >> + .owner = THIS_MODULE, >> + .name = "i2c-mux-reg", >> + }, >> +}; >> + >> +module_platform_driver(i2c_mux_reg_driver); >> + >> +MODULE_DESCRIPTION("Register-based I2C multiplexer driver"); >> +MODULE_AUTHOR("York Sun "); >> +MODULE_LICENSE("GPL"); >> +MODULE_ALIAS("platform:i2c-mux-reg"); >> diff --git a/drivers/i2c/muxes/i2c-mux-reg.h b/drivers/i2c/muxes/i2c-mux-reg.h >> new file mode 100644 >> index 0000000..e213988 >> --- /dev/null >> +++ b/drivers/i2c/muxes/i2c-mux-reg.h >> @@ -0,0 +1,38 @@ >> +/* >> + * I2C multiplexer using a single register >> + * >> + * Copyright 2015 Freescale Semiconductor >> + * York Sun >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 as >> + * published by the Free Software Foundation. >> + */ >> + >> +#ifndef __LINUX_I2C_MUX_REG_H >> +#define __LINUX_I2C_MUX_REG_H >> + >> +/* MUX has no specific idel mode */ > ^^^^ > >> +#define I2C_MUX_REG_NO_IDLE ((unsigned)-1) > > What if the idle state is exactly "all ones"? Quite important corner case actually... Good point. If all values are considered possible, I guess the solution will be adding another "valid" variable to vouch for this one. > >> + >> +/** >> + * struct i2c_mux_reg_platform_data - Platform-dependent data for i2c-mux-reg >> + * @parent: Parent I2C bus adapter number >> + * @base_nr: Base I2C bus number to number adapters from or zero for dynamic >> + * @values: Array of value for each channel >> + * @n_values: Number of multiplexer channels >> + * @classes: Optional I2C auto-detection classes >> + * @idle: Value to write to mux when idle >> + * @reg: Virtual address of the register to switch channel >> + */ >> +struct i2c_mux_reg_platform_data { >> + int parent; >> + int base_nr; >> + const unsigned int *values; >> + int n_values; >> + const unsigned int *classes; >> + unsigned int idle; >> + unsigned int *reg; > > Yeah, this is really bad idea. You maybe want something like > __iomem "cookie" here instead of this bare pointer. Let me try. > >> +}; >> + >> +#endif /* __LINUX_I2C_MUX_REG_H */ >> > > Other than the mentioned above, this is a useful code. > Thanks for the encouragement. I will send an update soon. York -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/