Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756283AbdCXICY (ORCPT ); Fri, 24 Mar 2017 04:02:24 -0400 Received: from mail-eopbgr40106.outbound.protection.outlook.com ([40.107.4.106]:19264 "EHLO EUR03-DB5-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752310AbdCXICN (ORCPT ); Fri, 24 Mar 2017 04:02:13 -0400 Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=none action=none header.from=axentia.se; Subject: Re: [PATCH] i2c/muxes/i2c-mux-ltc4306: LTC4306 and LTC4305 I2C multiplexer/switch To: , , , , References: <1490278978-28357-1-git-send-email-michael.hennerich@analog.com> CC: , , , From: Peter Rosin Organization: Axentia Technologies AB Message-ID: <5e4b224d-4dba-e183-4eda-d68f57faff7e@axentia.se> Date: Fri, 24 Mar 2017 09:01:08 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0 MIME-Version: 1.0 In-Reply-To: <1490278978-28357-1-git-send-email-michael.hennerich@analog.com> Content-Type: text/plain; charset="windows-1252" Content-Transfer-Encoding: 7bit X-Originating-IP: [81.224.171.159] X-ClientProxiedBy: DB6PR0601CA0034.eurprd06.prod.outlook.com (10.169.209.20) To AM5PR0202MB2546.eurprd02.prod.outlook.com (10.173.89.7) X-MS-Office365-Filtering-Correlation-Id: 20c1174c-f9a6-4eb7-9692-08d4728bf0be X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001);SRVR:AM5PR0202MB2546; X-Microsoft-Exchange-Diagnostics: 1;AM5PR0202MB2546;3:b+6ZabJnHr3f31P2eUkgxF7Big7OG1zKBRYHGFJYBIAyf3whaGF4L6BM09yYD16UqXB6fLN+FlX8hOqolfqIduXOzpL6fO4d5aO4GEa2ypEKifLLsIamMK4MVTYl32sKBdsjDd7qtJNcwTVbcg8WMMbk82CEFAPM90BAZlR/GlFV6yc8U1PnPHqzufda79GycV9bbjxujfc/5/Uoxz10FPwJbnuCTlzHanPwoGpuLhb3PuZTNgoc5m04DkyYirYoxuSXA2/9UB0xdyzU49PP4g==;25:vCqEGrezhnHgSPzxBojb058PO0XqBdZhF64t1OisTezdhCK+hrPDEmA+QM7BlhbB1fIJ5Qal8D7B1XyHZPUOB8c0zYVQ7kuvVNrxmk4sFq2I7zr6siObxTfPMWTZM8hI9Ct0lTeqY49u90eky4CPD7Bv2iTcl2GjdJIMo4A4UdTYdSds6aaMjVNjTs+7ZEEj1uJ4RWndJhJRWioSP024xEk4PoRfIrt1NNE1ouRVyj9Emos4fixBOtzPKQji365FmGW4eSqZhTmRQ1Sw1XUekwRW3jyKCTFlOGhM0o85A4ficsjflz4Qh8/6WMuLMRiQdnSgLzRepLger9kZWNuOJ1VLep0BMzbNyx8VPXm/IVywRRdfAzOMKdFLDRrixE3clFz9QxF/eDM1fRdQRqdnm+acfecY+wFW1ThpTwXIahb4llvAZSSYetdZG/x2OwbgdHLuvqg48XYXSQDjDIK5Dg== X-Microsoft-Exchange-Diagnostics: 1;AM5PR0202MB2546;31:BPIjEOeBFb3Nd63eVU4cUm364sN6n5DlJ4cEjy4K5Y3l/A2RFj/HWttiHk2xcR7feDgilsexmv/UlpCwq385lD/gsJhJ+5v5IbRU5Xc0JLSJST1DXCFltMIEm35WidEpgsHQCM2H3kjiQnY6V2j5+9i3MVHUR3whA9jZ2DJNcNWeu+QqN5qQRiMzHka2ADlXGyWhVnf0B6RPEsIn4dxrW1c0fjoxRZWcHVe0Ig5qdfA9+/LEgF+qw/64SWfRrUXic+0IhJl12R4mHRKTqn+gm8vV/AohLq+xR474s2f+fYc= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(9452136761055)(232431446821674)(21532816269658)(170811661138872); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040375)(601004)(2401047)(5005006)(8121501046)(3002001)(10201501046)(6041248)(20161123558025)(20161123560025)(2016111802025)(20161123562025)(20161123564025)(20161123555025)(6072148)(6043046);SRVR:AM5PR0202MB2546;BCL:0;PCL:0;RULEID:;SRVR:AM5PR0202MB2546; X-Microsoft-Exchange-Diagnostics: 1;AM5PR0202MB2546;4:QhCDUegg2GmXXgBIKPYF+aqez19v1JxhJ0/sRuZWOTSs8y57FfccMJSEnVVvG7qDjUC4obTO27WJ1QhS4OzVAHTYsd0ymz3bB5NCfZ0nU0B0SkIMizuzsqt0Axp6q2BzjmnPOJKaNJm71bNkJRz3o3H/h4iQUNdRerb5mr/c6YExmLpnlmvHLF2t6enH6nDTkrfA/hG4tmrT5REmgYA7Qc+O1eP+loN6C8aKlGtn1pjuy6Re8kAbdo44eNa3kS9V2W1Y6atLpezOK/8vjRXkxhkzVNf3UElhrF2pOiKsF6ASmI5wfmYu3hve4SPvYj+DxW6f6urrT0QDsCjpxC5QGBVnr9/FYeCVbd5ZEEDvMlKpTDwZJ+T3Bkqjq9fgxSKRxBQMUpuGXdoWki10gLcmfmHYT6Pgwwd1I8Tz9URBVI6TAyYxdAlPaKHL2vPKApDi+/d4MwQCcg1aLiCuTaLAzLealhbNiWR2xNAtxIpPKcSL6v7InRpacZ/UaMVTN+J/o/Hg4PRVqVy5t6xMnI8A3IsWgDflE1WtqQeXLFfs0EHVGVlpQXFz8YBxrH34kQd3SFSKqJqk+5UFDds+rq/gClpb/gNZHJmHVSrsFSKcxoi9Q9sWOZviSkasMyR37j52u3evCkzC/niKJCLcv6qYMBz6poyRW/0y01UsCETQ/ktxV6NADkYa50UZ4NxHOHAkeUAbmeEdFek3oZJlkDRa1Up5bTOUssXMGDuVjc0ZtcbDj5Wb4pkJ6Oy5Ly5QTlhoW0QUgh3+cUWqoR2ofQi+aumakkVYEJ4qBTnhckjaRes= X-Forefront-PRVS: 0256C18696 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6009001)(6049001)(39830400002)(39410400002)(39450400003)(377424004)(37524003)(24454002)(38730400002)(230783001)(8666007)(3846002)(6116002)(83506001)(229853002)(23746002)(1720100001)(7736002)(66066001)(4326008)(966004)(5660300001)(53936002)(36756003)(117156001)(230700001)(6486002)(8676002)(65956001)(65806001)(54356999)(50466002)(50986999)(53546009)(64126003)(47776003)(6666003)(74482002)(77096006)(90366009)(76176999)(65826007)(33646002)(6246003)(31696002)(575784001)(86362001)(305945005)(2950100002)(3260700006)(25786009)(189998001)(6306002)(42186005)(562404015)(42262002);DIR:OUT;SFP:1102;SCL:1;SRVR:AM5PR0202MB2546;H:[192.168.0.125];FPR:;SPF:None;MLV:sfv;LANG:en; X-Microsoft-Exchange-Diagnostics: =?Windows-1252?Q?1;AM5PR0202MB2546;23:EiOLJtzFSa0t8sTqn1Aiq8q5HqIEmwEoA/5?= =?Windows-1252?Q?L7PVWYAP1Sxm6jgk4D06Q9ShJ8CCsoiZpmvPU02ILBSYMidV8laXvNOG?= =?Windows-1252?Q?1Ok8rviGAU+jtZtZh0zMbBEiOZwBok7K89y8b08Uc0mSzX3AQEjnNg7S?= =?Windows-1252?Q?5aEw0BTecW5Q6lBNZCs+yf1NPRNsDv78YaTULKOY5TidOoVWxZh4l1FR?= =?Windows-1252?Q?KcNwJl6TCQp2I8Nt7BMqt+Aj3ugWX9XUHbpl5VArOrOiR+70btT0C+No?= =?Windows-1252?Q?je6x5dWouZUfV63UtIhZ66lBqa70shc/PwlkwpFJ2HEKOKMZotX8kGFX?= =?Windows-1252?Q?Y+Cu6eGBwtNywViFC4UXJfiY9QHH/eQhM7QPs8Z/aJb3V2V2bCgRHfNy?= =?Windows-1252?Q?2xUogNbfG4ZOX6N+m6laVRRBmS35Mb0xrsohtN/gYN/CxOEomIZ9ymB5?= =?Windows-1252?Q?OiCpPYXv1igeyGr7kgyr0CxyIRV6P052vZqYboTVRUotDUQcVFexRxz+?= =?Windows-1252?Q?y/ujOrSrI0Ek2mFtp7Hxh61VK7z7bwRNWgJaaqqND/1Qk0lIH0zA/YqT?= =?Windows-1252?Q?5QyFo01zu4IU5iigvOxYlYe9+xQN8yvfayMwg+5BfdtYGTA7qcRgBRYK?= =?Windows-1252?Q?C6rvrvZ1gdi01sl2aNTda+QG2eGVqCoY21dFTbpLjlq2i6va1FwBWEUv?= =?Windows-1252?Q?tLpiKRB/ll7gZQrFIBPp1XepNSX8iu3tZlJsmW44eLiUQxT9eSQHs0Fc?= =?Windows-1252?Q?39LfbTz9vW1G1McYRYLWY/I4QCH9woN4EALpW8f49GPa8PXCtjZlFLeJ?= =?Windows-1252?Q?ndRVjSeXyJDuAzTKEEirL+T8bYztGIW4zZbzzmtIkZogy+v4gokE/HNs?= =?Windows-1252?Q?5/xAsDWdWIigcKj1In6CWan/Rgtk6AxcJrnN77G13Q8M+l3lM0JSnl+F?= =?Windows-1252?Q?fpwlZDHH7ezYEG+PpsUcJdvQRY7JOCwJ0JQIXlmU8ng9JDi5dmsCYYul?= =?Windows-1252?Q?l5D0gy561ZOYDKZmpUTKv/s0MAR2azoO6YkAI2CG1DNnHSZhwi6sqBiK?= =?Windows-1252?Q?WfHNN52VEKeHbMGVzRzfDG4UM4JxLWUamf2QbRBRtov4UAmLDyG9trwv?= =?Windows-1252?Q?4ulWX7MyB/r2sHdfqnSJ7M7G6aHlf0dgppOON/0yAEPZKOZhEsTnwtCt?= =?Windows-1252?Q?vCN9dDuUHY1qMINjLpKuSRBBuDACGs0H9NjvxuWW3HrXzsiUR6ua04Hs?= =?Windows-1252?Q?oAzKGotfA4cfRiOEtrVxy3Ax4Wyj5CStZzFWwP2rNmFkdrBipu885h4H?= =?Windows-1252?Q?o8KvA9R2FBRt72fpfMyLkDBdaTzJDzshtjPuhzH6s7ol6hbpTp3WQQJb?= =?Windows-1252?Q?YeUxzTb56lad6li5oBBgwnzkVNgK/j5fAzD1TQSVUtnv8HbrR2y6qTKb?= =?Windows-1252?Q?lzCvRhEmwAqqhTXindalkxT1AL28jYgnjUex1udVc3NSSwjV9luSHZJy?= =?Windows-1252?Q?4uAXQt8Q=3D?= X-Microsoft-Exchange-Diagnostics: 1;AM5PR0202MB2546;6:JLLBIngXPEk4Byw/J8EIo7vZirgJaCXYzKmLT3JTPg4Z4xevk16vxRJ6Erz5O2q+PrDbizfCe0q009qm5Jah+1McJuQ6zimlJam6T5vG07PVgPFQJt3kTjw+sqHsAmHJZB/KqseyttmudTDpWosR3f0R3r/37uiSrGVywLXgRcBJU6Pjs+SgRpWdeTyWNYuHeEWnmC2c5ffMT5y1thWjMTJGZZr66aiBMXv53H15yruMOk4QimgOTXPZkchC4SFhQxKySY2ABQ2Mm+PDSr0qXOtOJjR4osIBdH++XiYJtTFnBGwPPjQ2laPaCOW4FbObmyYPFgAI7AMpGtbGx8xUtUEsASS5p4ON3WCN/ypMsDB6hG60ELzJeXxLrpZlXn9r/KLYY3ZcAJcjqrSJz6QmVw==;5:kStzQtGfR7agggxTQsnIxC1A+jL8DpUCAzSl1gdcSIn57RJgL3rGmwGff9owE7QwqkBoQAsPLDoBnguu9fkl5rqDhKYmu1K8NnO/iTNPOgYZsxU8NvxE29KF3qdPv6rz+o+zbRWJiy9m+mwEx5Jx3g==;24:HrsjDa1gB5Q5hgcQ7MeeD+2RJcaUEmHfqsH6AQsxVM6sloE47tHQVQ9CWBsP6dHviXlfP+xIz5BOusDsfYIED9hwLaViHl9dIroirbT0L88= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;AM5PR0202MB2546;7:CQt2zp91RIJKoWk6WmmJYhC+6pMhx0rH1vW9klewMI51knrV+JmHevXDB1oxYw4WZDvholyGbEtgZuN4ujUUG4j/xrIz1gk432QuvXHCAl1AtRqFLlpDT5yZDAtaitNFr1MjrfwrAJxGpEy76E4nnws1RKD3s0Dd6aNjJiS2jGmTWj7vDJkvqcWAVoOG85zPojY86FAaqHxEONMUYarm41dfgyVz6OePR8ejk8LKz0NMyVmkPHJpjBKhlAa5AmnhowMt1S4h3tWnMYkfh9h2Lwfr1xQ8wZUdM9dqqlm2+GzFXo+BIQxBIEZDcLZPr9FFqtiUAohcVN1E9RM2Oyi0Jg== X-OriginatorOrg: axentia.se X-MS-Exchange-CrossTenant-OriginalArrivalTime: 24 Mar 2017 08:01:11.9615 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0202MB2546 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 17310 Lines: 583 On 2017-03-23 15:22, michael.hennerich@analog.com wrote: > From: Michael Hennerich > > This patch adds support for the Analog Devices / Linear Technology > LTC4306 and LTC4305 4/2 Channel I2C Bus Multiplexer/Switches. > The LTC4306 optionally provides two general purpose input/output pins > (GPIOs) that can be configured as logic inputs, opendrain outputs or > push-pull outputs via the generic GPIOLIB framework. Hmmm, I'm not sure if it is ok to implement a gpio provider outside of drivers/gpio like this? Should it be done with MFD, or is that just adding needless complexity? I also wonder if you have thought about implementing support for alerts? And do you need recovery if things get stuck? I see timeout bits and failure to connect bits in the register map... > > Signed-off-by: Michael Hennerich > --- > .../devicetree/bindings/i2c/i2c-mux-ltc4306.txt | 61 ++++ > MAINTAINERS | 8 + > drivers/i2c/muxes/Kconfig | 10 + > drivers/i2c/muxes/Makefile | 1 + > drivers/i2c/muxes/i2c-mux-ltc4306.c | 365 +++++++++++++++++++++ > 5 files changed, 445 insertions(+) > create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt > create mode 100644 drivers/i2c/muxes/i2c-mux-ltc4306.c > > diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt > new file mode 100644 > index 0000000..1e98c6b > --- /dev/null > +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt > @@ -0,0 +1,61 @@ > +* Linear Technology / Analog Devices I2C bus switch > + > +Required Properties: > + > + - compatible: Must contain one of the following. > + "lltc,ltc4305", "lltc,ltc4306" > + - reg: The I2C address of the device. > + > + The following required properties are defined externally: > + > + - Standard I2C mux properties. See i2c-mux.txt in this directory. > + - I2C child bus nodes. See i2c-mux.txt in this directory. > + > +Optional Properties: > + > + - enable-gpios: Reference to the GPIO connected to the enable input. > + - i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all > + children in idle state. This is necessary for example, if there are several > + multiplexers on the bus and the devices behind them use same I2C addresses. > + - gpio-controller: Marks the device node as a GPIO Controller. > + - #gpio-cells: Should be two. The first cell is the pin number and > + the second cell is used to specify flags. > + See ../gpio/gpio.txt for more information. > + - ltc,downstream-accelerators-enable: Enables the rise time accelerators > + on the downstream port. > + - ltc,upstream-accelerators-enable: Enables the rise time accelerators > + on the upstream port. > + > +Example: > + > + ltc4306: i2c-mux@4a { > + compatible = "lltc,ltc4306"; > + #address-cells = <1>; > + #size-cells = <0>; > + reg = <0x4a>; > + > + gpio-controller; > + #gpio-cells = <2>; > + > + i2c@0 { > + #address-cells = <1>; > + #size-cells = <0>; > + reg = <0>; > + > + eeprom@50 { > + compatible = "at,24c02"; > + reg = <0x50>; > + }; > + }; > + > + i2c@1 { > + #address-cells = <1>; > + #size-cells = <0>; > + reg = <1>; > + > + eeprom@50 { > + compatible = "at,24c02"; > + reg = <0x50>; > + }; > + }; > + }; > diff --git a/MAINTAINERS b/MAINTAINERS > index c776906..9a27a19 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -7698,6 +7698,14 @@ S: Maintained > F: Documentation/hwmon/ltc4261 > F: drivers/hwmon/ltc4261.c > > +LTC4306 I2C MULTIPLEXER DRIVER > +M: Michael Hennerich > +W: http://ez.analog.com/community/linux-device-drivers > +L: linux-i2c@vger.kernel.org > +S: Supported > +F: drivers/i2c/muxes/i2c-mux-ltc4306.c > +F: Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt > + > LTP (Linux Test Project) > M: Mike Frysinger > M: Cyril Hrubis > diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig > index 10b3d17..f501b3b 100644 > --- a/drivers/i2c/muxes/Kconfig > +++ b/drivers/i2c/muxes/Kconfig > @@ -30,6 +30,16 @@ 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_LTC4306 > + tristate "LTC LTC4306/5 I2C multiplexer" > + select GPIOLIB > + help > + If you say yes here you get support for the LTC LTC4306 or LTC4305 > + I2C mux/switch devices. > + > + This driver can also be built as a module. If so, the module > + will be called i2c-mux-ltc4306. > + > config I2C_MUX_PCA9541 > tristate "NXP PCA9541 I2C Master Selector" > help > diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile > index 9948fa4..a452d09 100644 > --- a/drivers/i2c/muxes/Makefile > +++ b/drivers/i2c/muxes/Makefile > @@ -7,6 +7,7 @@ obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o > > obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o > obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o > +obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o Keep the list sorted, please. > 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-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c > new file mode 100644 > index 0000000..f0fd4d1 > --- /dev/null > +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c > @@ -0,0 +1,365 @@ > +/* > + * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch > + * > + * Copyright (C) 2017 Analog Devices Inc. > + * > + * Licensed under the GPL-2. > + * > + * Based on: i2c-mux-pca954x.c > + * > + * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include Sort the includes, please. > + > +#define LTC4306_MAX_NCHANS 4 Why not a define for LTC4305_MAX_NCHANS as well? > + > +#define LTC_REG_STATUS 0x0 > +#define LTC_REG_CONFIG 0x1 > +#define LTC_REG_MODE 0x2 > +#define LTC_REG_SWITCH 0x3 > + > +#define LTC_DOWNSTREAM_ACCL_EN BIT(6) > +#define LTC_UPSTREAM_ACCL_EN BIT(7) > + > +#define LTC_GPIO_ALL_INPUT 0xC0 > + > +enum ltc_type { > + ltc_4305, > + ltc_4306, > +}; > + > +struct chip_desc { > + u8 nchans; > + u8 num_gpios; > +}; > + > +struct ltc4306 { > + struct i2c_client *client; > + struct gpio_chip gpiochip; > + const struct chip_desc *chip; > + > + u8 deselect; > + u8 regs[LTC_REG_SWITCH + 1]; Feels like a generic register cache, can you use regmap? > +}; > + > +/* Provide specs for the PCA954x types we know about */ > +static const struct chip_desc chips[] = { > + [ltc_4305] = { > + .nchans = 2, > + }, > + [ltc_4306] = { > + .nchans = LTC4306_MAX_NCHANS, > + .num_gpios = 2, > + }, > +}; > + > +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset) > +{ > + struct ltc4306 *data = gpiochip_get_data(chip); > + int ret = 0; > + > + if (gpiochip_line_is_open_drain(chip, offset) || > + (data->regs[LTC_REG_MODE] & BIT(7 - offset))) { Alignment should match open parenthesis. > + /* Open Drain or Input */ > + ret = i2c_smbus_read_byte_data(data->client, LTC_REG_CONFIG); > + if (ret < 0) > + return ret; > + > + return !!(ret & BIT(1 - offset)); > + } else { > + return !!(data->regs[LTC_REG_CONFIG] & BIT(5 - offset)); > + } > +} > + > +static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset, > + int value) > +{ > + struct ltc4306 *data = gpiochip_get_data(chip); > + > + if (value) > + data->regs[LTC_REG_CONFIG] |= BIT(5 - offset); > + else > + data->regs[LTC_REG_CONFIG] &= ~BIT(5 - offset); > + > + Please don't use multiple blank lines. > + i2c_smbus_write_byte_data(data->client, LTC_REG_CONFIG, > + data->regs[LTC_REG_CONFIG]); Alignment should match open parenthesis. > +} > + > +static int ltc4306_gpio_direction_input(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct ltc4306 *data = gpiochip_get_data(chip); > + > + data->regs[LTC_REG_MODE] |= BIT(7 - offset); > + > + return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE, > + data->regs[LTC_REG_MODE]); > +} > + > +static int ltc4306_gpio_direction_output(struct gpio_chip *chip, > + unsigned int offset, int value) > +{ > + struct ltc4306 *data = gpiochip_get_data(chip); > + > + ltc4306_gpio_set(chip, offset, value); > + data->regs[LTC_REG_MODE] &= ~BIT(7 - offset); > + > + return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE, > + data->regs[LTC_REG_MODE]); > +} > + > +static int ltc4306_gpio_set_config(struct gpio_chip *chip, > + unsigned int offset, unsigned long config) > +{ > + struct ltc4306 *data = gpiochip_get_data(chip); > + > + switch (pinconf_to_config_param(config)) { > + case PIN_CONFIG_DRIVE_OPEN_DRAIN: > + data->regs[LTC_REG_MODE] &= ~BIT(4 - offset); > + break; > + case PIN_CONFIG_DRIVE_PUSH_PULL: > + data->regs[LTC_REG_MODE] |= BIT(4 - offset); > + break; > + default: > + return -ENOTSUPP; > + } > + > + return i2c_smbus_write_byte_data(data->client, LTC_REG_MODE, > + data->regs[LTC_REG_MODE]); > +} > + > +static int ltc4306_gpio_init(struct ltc4306 *data) > +{ > + if (!data->chip->num_gpios) > + return 0; > + > + data->gpiochip.label = dev_name(&data->client->dev); > + data->gpiochip.base = -1; > + data->gpiochip.ngpio = data->chip->num_gpios; > + data->gpiochip.parent = &data->client->dev; > + data->gpiochip.can_sleep = true; > + data->gpiochip.direction_input = ltc4306_gpio_direction_input; > + data->gpiochip.direction_output = ltc4306_gpio_direction_output; > + data->gpiochip.get = ltc4306_gpio_get; > + data->gpiochip.set = ltc4306_gpio_set; > + data->gpiochip.set_config = ltc4306_gpio_set_config; > + data->gpiochip.owner = THIS_MODULE; > + > + /* gpiolib assumes all GPIOs default input */ > + data->regs[LTC_REG_MODE] |= LTC_GPIO_ALL_INPUT; > + i2c_smbus_write_byte_data(data->client, LTC_REG_MODE, > + data->regs[LTC_REG_MODE]); Alignment should match open parenthesis. > + > + return devm_gpiochip_add_data(&data->client->dev, > + &data->gpiochip, data); > +} > + > +/* > + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() > + * as they will try to lock the adapter a second time. > + */ > +static int ltc4306_reg_write(struct i2c_adapter *adap, > + struct i2c_client *client, u8 reg, u8 val) > +{ > + int ret; > + > + if (adap->algo->master_xfer) { > + struct i2c_msg msg; > + char buf[2]; > + > + msg.addr = client->addr; > + msg.flags = 0; > + msg.len = 2; > + buf[0] = reg; > + buf[1] = val; > + msg.buf = buf; > + ret = __i2c_transfer(adap, &msg, 1); > + } else { > + union i2c_smbus_data data; > + > + data.byte = val; > + ret = adap->algo->smbus_xfer(adap, client->addr, > + client->flags, > + I2C_SMBUS_WRITE, > + reg, > + I2C_SMBUS_BYTE_DATA, &data); > + } > + > + return ret; > +} You have selected to go parent-locked, but you can get rid of the above workaround if you go mux-locked. See Documentation/i2c/i2c-topology for how these differ. Overall, since you do have the GPIO possibility, I think you are closing some possibilities by going parent-locked. Those GPIOs will more easily get locked out if you build a complex tree involving these muxes, when the mux is parent-locked. But on the other hand, mux-locked doesn't work everywhere either, the person who needs mux-locked can add that option if you don't want to... > +static int ltc4306_select_chan(struct i2c_mux_core *muxc, u32 chan) > +{ > + struct ltc4306 *data = i2c_mux_priv(muxc); > + struct i2c_client *client = data->client; > + u8 regval; > + int ret = 0; > + > + regval = BIT(7 - chan); > + > + /* Only select the channel if its different from the last channel */ > + if (data->regs[LTC_REG_SWITCH] != regval) { > + ret = ltc4306_reg_write(muxc->parent, client, > + LTC_REG_SWITCH, regval); > + data->regs[LTC_REG_SWITCH] = ret < 0 ? 0 : regval; > + } > + > + return ret; > +} > + > +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) > +{ > + struct ltc4306 *data = i2c_mux_priv(muxc); > + struct i2c_client *client = data->client; > + > + if (!(data->deselect & BIT(chan))) > + return 0; > + > + /* Deselect active channel */ > + data->regs[LTC_REG_SWITCH] = 0; > + > + return ltc4306_reg_write(muxc->parent, client, > + LTC_REG_SWITCH, data->regs[LTC_REG_SWITCH]); > +} > + > +static const struct i2c_device_id ltc4306_id[] = { > + { "ltc4305", ltc_4305 }, > + { "ltc4306", ltc_4306 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, ltc4306_id); > + > +static const struct of_device_id ltc4306_of_match[] = { > + { .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] }, > + { .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, ltc4306_of_match); > + > +static int ltc4306_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); > + struct device_node *of_node = client->dev.of_node; > + bool idle_disconnect_dt = false; > + struct gpio_desc *gpio; > + struct i2c_mux_core *muxc; > + struct ltc4306 *data; > + const struct of_device_id *match; > + int num, ret; > + > + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) > + return -ENODEV; > + > + muxc = i2c_mux_alloc(adap, &client->dev, > + LTC4306_MAX_NCHANS, sizeof(*data), 0, > + ltc4306_select_chan, ltc4306_deselect_mux); Where's the symmetry in ..._chan for select and ..._mux for deselect? > + if (!muxc) > + return -ENOMEM; > + data = i2c_mux_priv(muxc); > + > + i2c_set_clientdata(client, muxc); > + data->client = client; > + > + /* Enable the mux if a enable GPIO is specified. */ s/ a / an / > + gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); > + if (IS_ERR(gpio)) > + return PTR_ERR(gpio); Should you not use this pin later as well? In suspend/resume if you add handling for that? > + > + /* Write the mux register at addr to verify > + * that the mux is in fact present. This also > + * initializes the mux to disconnected state. > + */ Multi-line comments should start with a /* on its own line. > + if (i2c_smbus_write_byte_data(client, LTC_REG_SWITCH, 0) < 0) { > + dev_warn(&client->dev, "probe failed\n"); > + return -ENODEV; > + } > + > + match = of_match_device(of_match_ptr(ltc4306_of_match), &client->dev); Just call of_device_get_match_data() directly. > + if (match) > + data->chip = of_device_get_match_data(&client->dev); > + else > + data->chip = &chips[id->driver_data]; > + > + if (of_node) { > + idle_disconnect_dt = > + of_property_read_bool(of_node, > + "i2c-mux-idle-disconnect"); > + if (of_property_read_bool(of_node, > + "ltc,downstream-accelerators-enable")) > + data->regs[LTC_REG_CONFIG] |= LTC_DOWNSTREAM_ACCL_EN; > + > + if (of_property_read_bool(of_node, > + "ltc,upstream-accelerators-enable")) > + data->regs[LTC_REG_CONFIG] |= LTC_UPSTREAM_ACCL_EN; > + > + if (i2c_smbus_write_byte_data(client, LTC_REG_CONFIG, > + data->regs[LTC_REG_CONFIG]) < 0) { > + dev_warn(&client->dev, "probe failed\n"); > + return -ENODEV; > + } > + } > + > + ret = ltc4306_gpio_init(data); > + if (ret < 0) > + return ret; > + > + /* Now create an adapter for each channel */ > + for (num = 0; num < data->chip->nchans; num++) { > + Blank lines aren't necessary after an open brace. > + data->deselect |= idle_disconnect_dt << num; Either *all* relevant bits are 1 or *all* relevant bits are 0. You can get rid of this variable and invoke i2c_mux_alloc with NULL instead of ltc4306_deselect_mux when not configured to disconnect the mux. You have to delay the mux allocation to later in probe, but I don't think it will be too hairy to accomplish that? Cheers, peda > + > + ret = i2c_mux_add_adapter(muxc, 0, num, 0); > + if (ret) { > + dev_err(&client->dev, > + "failed to register multiplexed adapter %d\n", > + num); > + goto virt_reg_failed; > + } > + } > + > + dev_info(&client->dev, > + "registered %d multiplexed busses for I2C switch %s\n", > + num, client->name); > + > + return 0; > + > +virt_reg_failed: > + i2c_mux_del_adapters(muxc); > + return ret; > +} > + > +static int ltc4306_remove(struct i2c_client *client) > +{ > + struct i2c_mux_core *muxc = i2c_get_clientdata(client); > + > + i2c_mux_del_adapters(muxc); > + return 0; > +} > + > +static struct i2c_driver ltc4306_driver = { > + .driver = { > + .name = "ltc4306", > + .of_match_table = of_match_ptr(ltc4306_of_match), > + }, > + .probe = ltc4306_probe, > + .remove = ltc4306_remove, > + .id_table = ltc4306_id, > +}; > + > +module_i2c_driver(ltc4306_driver); > + > +MODULE_AUTHOR("Michael Hennerich "); > +MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver"); > +MODULE_LICENSE("GPL v2"); >