Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933283AbdDENH2 (ORCPT ); Wed, 5 Apr 2017 09:07:28 -0400 Received: from mail-sn1nam01on0061.outbound.protection.outlook.com ([104.47.32.61]:45664 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932904AbdDENGb (ORCPT ); Wed, 5 Apr 2017 09:06:31 -0400 Authentication-Results: spf=pass (sender IP is 137.71.25.55) smtp.mailfrom=analog.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=bestguesspass action=none header.from=analog.com; From: To: , , , , CC: , , , , "Michael Hennerich" Subject: [PATCH v4 2/2] i2c: mux: ltc4306: LTC4306 and LTC4305 I2C multiplexer/switch Date: Wed, 5 Apr 2017 15:07:51 +0200 Message-ID: <1491397671-14675-2-git-send-email-michael.hennerich@analog.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491397671-14675-1-git-send-email-michael.hennerich@analog.com> References: <1491397671-14675-1-git-send-email-michael.hennerich@analog.com> MIME-Version: 1.0 Content-Type: text/plain X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:137.71.25.55;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(6009001)(39400400002)(39840400002)(39850400002)(39450400003)(39860400002)(39410400002)(2980300002)(438002)(199003)(189002)(6306002)(47776003)(966004)(54906002)(86152003)(305945005)(48376002)(8676002)(50226002)(8936002)(77096006)(7636002)(50986999)(6666003)(76176999)(2950100002)(4326008)(356003)(5003940100001)(50466002)(107886003)(189998001)(33646002)(36756003)(2876002)(38730400002)(1720100001)(86362001)(562404015);DIR:OUT;SFP:1101;SCL:1;SRVR:BY2PR0301MB1974;H:nwd2mta1.analog.com;FPR:;SPF:Pass;MLV:sfv;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BN1BFFO11FD006;1:Soo2eOQMS/s26j0vq9pEw8M7sbpsTXi4tpdlNoaPHWcb2KNKCeBjC6mlWH5E79U0zmG3zdxk1cyTPGdIAGF2MzRb2qpoPVzV3cCvB2Q3znKNhuxJxD3mm11qDLyBrnfUskW1p5mlSSaVx8mPPP0wBYzdoWD5L7O1HzaF//3DVXYVmEHCNAXadek8pu//R4IZQIWtWnWksmFJK9i0jNy7E75lemG6lCFaICBbsWTzFAZmWXyL5khBBpIPi/lppeNZa7fAIp4t1hqfEetI+fnQ6tDdzaz3h0+yZjXxjAh73I5hYV1Rg/J9fP471inWQzxqnP3PqhsK9X+LOnaD0j+q3+bVxUqGpMeVOL1xLz2ZMhUnyBH+fNGdWVN22wAsgzA8yBgSXMBefTzrRiXaGD/m1HZPXgLI1TZMqqjbSBXpkDELZebhupeMhkzH+bzwhB+LH1KbWiJrIiyJybytYwGNaXCBY66ePd2Nb7IGulRHS1Aj+dsu5O2lUhGyf9YykIkOUl410381EDnj60LJxfp1w0jPP14vYogd5q6yq+k7p+en2HIkKu/+vLn4wuR4jk9togvlzoKOMQ/RJrZ1nuXQJL0v01PdwWfOx+zgruRkDrb36mpdKIhzCUYstorgxirp X-MS-Office365-Filtering-Correlation-Id: 429e8d7d-d520-484e-3a4d-08d47c249269 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001)(8251501002)(2017030254075)(201703131423075)(201703031133081);SRVR:BY2PR0301MB1974; X-Microsoft-Exchange-Diagnostics: 1;BY2PR0301MB1974;3:chAkuJuVMxZxYgH5XaiSc9TLnJmqfgToLasaOHtjRSUzmU8qSUPFYydTnWIgEtN1j0V/2V+dizVDPM4V6SnQQufIHR3oWKI86nqMxIW4GZxqVjJj6cJfNnQ7GxfKMugSkmPaVdkNKL0Yu0qvVQrhaRL5fqsalKt3M2ThUTCCoErz8+bsgYF9Nf8fkCj07XzyouKrUcSa5lhvbDdM4mkRUipT4kZNgzjIUpEot2/29MgrPjdyjt3deOMtlnt7PWorgdFc1LzJIaI/wBjkGEmK/tSxE/p/rboY6OWc+VndTPGin1IyetyaAGe0e3vkeFR9/wO8SYvNx2cloFwJ+ThDqHToHcHFHZ9whNwP3+PT5Cp12oVGXviofObLmt+flxNIJcdh3SsElfo45ElQmTi+JRMJgJC6fnOPugyERrHeJ42d2yaUPMEkBDOe5EB859u5V25oOVD7C+0VHJA3vzirm5QOmQyoAJgJ+TMl8uUyRTdTyykUu3tNNb265iP2Cv1s X-Microsoft-Exchange-Diagnostics: 1;BY2PR0301MB1974;25:uKmpOqKfIjh2aR78aKUx5kWdfsqmDa0f/JErJp9nOPQC37YELnt44bJqdAvSgb4hMVvoukt3sBAZYh9nc0klAMt20lLUh8r747/nzvNwRF9b3aLiKVKNmGeTxOsOG1zVXmNlnCyKrzpNF8eu1qEF8+PQ0h/a9QMpV2vXum+nZbDgQU2SlQmbdgcwCvxZCyJ7TVjxsl4NdVZDGYts+GdAKbuq8Umvc+D1ARwls/n2g/hFkLJ6S3avHqfDKYa1bxOtSb2nQI6H6nJ9BoxyfHil9EhoY2qBkBNW1qPJQjE69G+Nkf/JV19wb7mUoSkRLH000yaK8lVRtNUsQxy+HUWE3nRYFxRst0TineEbtjbfSz2sCxAS1mdVXsNyP1CICQZmGw/Lq2YO/94yv+ggYH+eWxWXzTwLUy/52hKYGGoG2DOQi5ogd4g2/tjMwBb3zGZ1qdW+V1DV233hBxbnjrjcBg==;31:Gl7YwtFKPg059XDhEnIVjID5eRLVUM4WfuVuRl/anUxeo/AlhFVzlOnRGA87cF9/sOHxCNrfcu1LZ1tE6VZNhs2IxW4EKNeY9y5tDg3+3RI1LgHE+QBIdbJowl5tS38JJL5Dla08Ski5pYdlGChR3d/MGk2ijyWKIhsPo11PhGr+0l5kqVQDd6peWSZOW5d0EmePfG0wZUe/9Ot1SRhfWC4OE86CTuLhlK2JTgv4tx/2wONFuB+KR2RO57KPUxFswLKhDCc0L5xsjyz3XjVvHw== X-Microsoft-Exchange-Diagnostics: 1;BY2PR0301MB1974;20:k7I5p2U8ijOF8iQtGv2PgV9t4EkkNGdgHks0Il4m9JZzP3IgzMSYdDhrs0NFMnWkmwKxTr5IQja1uGEvaqgDv+gvIxKtux9w7PFeaX8r70ZrRyStw3T0pWzv+uVXCLSGL/PB/MY2NPEI+iF/4Va1V2HLt295E05Ipk0rvDpx77u3joA0n5siLbfl+YFXr1MjGQe9OGWM4HmGqCb0hM3nGDv77Rh3Gv9r0//BGJHRy3pEOh4UV7PH01Qpv++0SosVJ7PoEewcC8JeXVdGBkflvSTwoByr7rE1CoaESSuh5xxTeyTfR0Pg2pf7B5CQAXlU4RM1A17v5fTLM5Jev+9n4ifwDCTgGxRrQw4eubvQLv1O9ajRn2GFwgv5iUicb7bdgzaH06OBnVwbIXF3sik7U/qBHnN+2CXQxoTdZLl12zmFewiA0EtNuFfwPuhGH/7k0cUFbeLJe0lUuDXUxT17yghA2v8ctFcf1vq1zOZGZxH7DYBEeXbDDnF1lHM0a0Gp X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(9452136761055)(232431446821674)(211171220733660)(170811661138872); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040450)(601004)(2401047)(13017025)(13018025)(13024025)(13023025)(13015025)(8121501046)(5005006)(10201501046)(93006095)(93004095)(3002001)(6055026)(6041248)(201703131423075)(201702281528075)(201703061421075)(20161123564025)(20161123560025)(20161123555025)(20161123562025)(6072148);SRVR:BY2PR0301MB1974;BCL:0;PCL:0;RULEID:;SRVR:BY2PR0301MB1974; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BY2PR0301MB1974;4:NkJuI5RhcRXobjBhMUdHuRitWzUxZx/aQTk/gfoK?= =?us-ascii?Q?vvn+6WbzoY2aTEXVCwDSoeNn3tWfyCxpN5PE0bHgqau4zIxXG6PzdS4MY23J?= =?us-ascii?Q?lKbaHLOMiaJHuVNsHcbTkYHhdc4S33hVht7RC+yI2EHB7683NRz+A4ILWPI2?= =?us-ascii?Q?189qF6wb/hG0aOElEauYgvcgAMDXezHeXhRupyN3IQaxDBPrCUECxG6cdRNN?= =?us-ascii?Q?6aCjIgiU2gPkYRhYXl/3UaoZDypwwa0ycDtsJ4rD7agOp0qzkSquHqLsLkaB?= =?us-ascii?Q?6PZ3uLq/URdhxv1je3IU0UiH9eLyEwqG1GnG9+1k+AWI/c3BAycB2b5saEzh?= =?us-ascii?Q?x8v20FYA+zEsRePuhF/N/Z4MylhbP/2XhehTnHptcC3kb1OXgaN+aoSuYVeD?= =?us-ascii?Q?/Xu4A7qa6OaZSaIj2YtjvL4f+Syzs1u7y/68LkEf8j9yQBDT2XhbJ1taLG1l?= =?us-ascii?Q?LbRU/5L67BvELUFZnA/gD9teMYN1QaFV2FAzsjyA91KCo0ZfNFHBNgvkrtx1?= =?us-ascii?Q?PKOhO/XX6WbN1EWCFpBgAvpawRm7DATTQ+PbNm9OGaE9wDeDpd1eQMWLBYSS?= =?us-ascii?Q?0AMQmGsT7merNEbZGG3iTfh/kI+0KtvB9OcyV73R1KaoYMYcOe7WUhEgit3K?= =?us-ascii?Q?aLE0i3/lvNmdhUnIRdSW5azDTSOYsi/Q1ttZh9iZ6jg7fUgCd6WrKEV7RfLK?= =?us-ascii?Q?c5uSJoUXWzeqhxrLZyAyPksxZqq1BgTSlSrOswn66oHTTnmL7DyHt11f4Qqy?= =?us-ascii?Q?wI/H+j+tYaDnJ7cnCKtKqTmkvWa5eCp70236BIXODbhT3NcNti8iBFuvhJMZ?= =?us-ascii?Q?BfZi1+0nlRJoW9WjWy7st2iW47HsYQrhn5bXnPJwjjr4XqZCj9Us8Foi/l8h?= =?us-ascii?Q?jafbRm/kjAam3Wv/uKiM7USJKzE+iDBfii7Bp5d3Hx3amaLwscbLTfGqFuV+?= =?us-ascii?Q?iKf78Bf9Br+k8MfHp4pTlbRRcBU7Fe3VhtCoKsxbLXvkDdJ2Dv5aOatXYkA5?= =?us-ascii?Q?avU=3D?= X-Forefront-PRVS: 0268246AE7 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BY2PR0301MB1974;23:aYGPaaBqz7RkbHxmZrUBvMO1VDT8Ffn2Lm1jpvw?= =?us-ascii?Q?zsWE7CdAf/BE5O2JcNei8UrODuM6UBx5mkPcAvVThVpVsI0hX2KSz2SH+1Yz?= =?us-ascii?Q?qJ5qqQ4ObxVBlntOxSjCW5WgKGTu/7GxIge5V3HonBUqg0HIPnsVwGJFpUEn?= =?us-ascii?Q?osYFcgBnT40K1RP3Wceg6SINHt8gVCq7eprb3Ro5VEx8htNWvB3PD2Cckf6i?= =?us-ascii?Q?wa64vP55RV4gf6IyAXjnPhHVU7N0elMLgAKRPWrUykxVEmubfgB0GjaLW6+Y?= =?us-ascii?Q?SIaQavBbQZU98vemh4A2WWNEoObo/cWRwm2/OrOBrewokLxKy5fi3HNbmKRI?= =?us-ascii?Q?mjE9AlbMiE/sdl6KvAjyEA4Jmz9rKHw3v9rDDCrKIknY3JZu5IQpqbUDbdFq?= =?us-ascii?Q?ogdcJ3sx2fgL8js1TcJ9plgWVXN3t5a34ElfkzzLkxh/IgMIrf+rpjthu7oN?= =?us-ascii?Q?E+Yg6aq7iXUJea65m8CrMU7YEOJD9llKYpmxBIs8mUFzIejFlkfk2OdACDhC?= =?us-ascii?Q?4rAEMngGsLKfV14SWcUxmpZa+DhR+ouWECjap0hY0K4vSRHOEj5w5G61qWCF?= =?us-ascii?Q?0nZKT7IMxQFGr8PTfkDYsi3QkOD7W/V2hv2DqZbo9wvdFDUp6LTVq1c8rbxF?= =?us-ascii?Q?rz6uc+pw7q9QiiIfxTOd8Mghlct3Ls7fBYPCDwFphzf9fORBv7TBuzHCTdNd?= =?us-ascii?Q?93zOUwqM35GLijZhV9hQZnppSlo7ls/gG3y/LbpRMbzv+aZn/XMncbX+QH1m?= =?us-ascii?Q?mN+Y+I364OIKyruNmI4P7KSR/tKjxWJ0MZp3akHwN5dMigIdrVhw63K9H5h3?= =?us-ascii?Q?xbbA9H0PVn6o4bI6sbJe23BXW/qattz7AsEUvpwVBbqjgaLjKuDcQvWx5Go5?= =?us-ascii?Q?TchTv7tbYYkeP9OKf7Yiuap0Z7W/+54S62T2ZFISSzeVMg7FQgbEhdMp2FAW?= =?us-ascii?Q?UYLXRSiEy3TkdwyLWLU4bDtTPe/4Ve37lffwGmf/BX19AVYqHk703w50ctmr?= =?us-ascii?Q?FAMB766otTNNogG7t6QOTw19Q?= X-Microsoft-Exchange-Diagnostics: 1;BY2PR0301MB1974;6:OFMGR+yl8sH6bsMFI1Mh9tdCJXTdCtnLOwAmxC0rgAyK+M79cH0YsLfi4hxedF/LzTFmDoGJ1r2mf6D8zatW9b3MimqBU9/LIne/i0db7TeKusmhT2GCfW8N/StKE/zo/eNnrib4rEutHSgUVhsC8F+5CTzql25j1IrVWLD4xYLpSJoFyszON6w5Q+dtqWXQNbgr1uZ2D7VUwJE0Kibt7g32TLhp4vjITSdUC19LTQJ6zIHJF20sOu6IySRiYV788KSiUFA68uE5JCkQo7X6FHnFN59itK3+lsyR7uox+9lFp4GWqyTeTlqQjiUXYzYO/nh4ONjq7cFVSk3bF7UwtCR5KZMuhNJ2HEnGb1aaaMMxGjzNIazhi9Hwn5aKANkBbeAiK7X7/zF4yLd0pifZGSViDACuQwPxa2d7Sdbe66w=;5:9N8DX8DeJ5DhRC5rY6JC3YgbC59LpBM+O1HiYPsTq6Vz685Qztn4DK1wBmA44hwERz48bKHOhu9OgPgS/ITroyBlZCU18vOsKZg3F4kJfS9cE9j66a+HB5+140lbKmL4mNQWkipaDnNan2GyRX/9lA==;24:VQwB/uB1nqYUbaHCzZZAv3eWn6Re6EhKaqjc/3DIdEOW/GVqBdj5OrHU4aS+0RyNUG+C+mUfo26XFPFHehtFSg5S5up7gzCNSu208EnImMQ= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;BY2PR0301MB1974;7:X8os+YDEza+P7ZiAysEiWZKnGyNUpt0076L8aSHfRKMlI19FrVzrsypklUtLZPuRv2tNDqcRkOcf1fouhzo+ppZcG8YVwHRFrItmN2BKfSmMWicjfZwvShBgDuMjsQK6gJgP1Fgy7SyTJ2upvd6ADu1nMBl4bosCWeJgN8zTurDy200kZCIRl+9+noXlHSQfC8JvG97S2virBfmnBPJZQfVA3cSIk5K2pjuadWmECBdg+K2XqWSZhVhJJ2LHg7RVaMud/E0DMehSVQx3VZltYvnpLN5iHEpDM5LyrjbObyxk4Wibxz6Lg32bJRI9l8H4a2PXQ06hsxH+2wOrj4EOYw== X-OriginatorOrg: analog.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Apr 2017 13:06:28.1559 (UTC) X-MS-Exchange-CrossTenant-Id: eaa689b4-8f87-40e0-9c6f-7228de4d754a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=eaa689b4-8f87-40e0-9c6f-7228de4d754a;Ip=[137.71.25.55];Helo=[nwd2mta1.analog.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2PR0301MB1974 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11388 Lines: 419 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. Signed-off-by: Michael Hennerich --- Changes since v1: - Sort makefile entries - Sort driver includes - Use proper defines - Miscellaneous coding style fixups - Rename mux select callback - Revise i2c-mux-idle-disconnect handling - Add ENABLE GPIO handling on error and device removal. - Remove surplus of_match_device call. Changes since v2: - Stop double error reporting (i2c_mux_add_adapter) - Change subject - Split dt bindings to separate patch Changes since v3: - Change subject and add spaces - Convert to I2C_MUX_LOCKED - Convert to regmap - Remove local register cache - Restore previous ENABLE GPIO handling - Initially pulse ENABLE low - Eliminate i2c client struct in driver state structure - Simplify error return path - Misc minor cleanups --- MAINTAINERS | 8 + drivers/i2c/muxes/Kconfig | 11 ++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/i2c-mux-ltc4306.c | 310 ++++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 drivers/i2c/muxes/i2c-mux-ltc4306.c 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..41153b4 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -30,6 +30,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_LTC4306 + tristate "LTC LTC4306/5 I2C multiplexer" + select GPIOLIB + select REGMAP_I2C + 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..ff7618c 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o +obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.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..7d34434 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -0,0 +1,310 @@ +/* + * 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 +#include + +#define LTC4305_MAX_NCHANS 2 +#define LTC4306_MAX_NCHANS 4 + +#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 +#define LTC_SWITCH_MASK 0xF0 + +enum ltc_type { + ltc_4305, + ltc_4306, +}; + +struct chip_desc { + u8 nchans; + u8 num_gpios; +}; + +struct ltc4306 { + struct regmap *regmap; + struct gpio_chip gpiochip; + const struct chip_desc *chip; +}; + +static const struct chip_desc chips[] = { + [ltc_4305] = { + .nchans = LTC4305_MAX_NCHANS, + }, + [ltc_4306] = { + .nchans = LTC4306_MAX_NCHANS, + .num_gpios = 2, + }, +}; + +static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (reg == LTC_REG_CONFIG) ? true : false; +} + +static const struct regmap_config ltc4306_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC_REG_SWITCH, + .volatile_reg = ltc4306_is_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, LTC_REG_CONFIG, &val); + if (ret < 0) + return ret; + + return (val & BIT(1 - offset)); +} + +static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset), + value ? BIT(5 - offset) : 0); +} + +static int ltc4306_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), BIT(7 - offset)); +} + +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); + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(7 - offset), 0); +} + +static int ltc4306_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, unsigned long config) +{ + struct ltc4306 *data = gpiochip_get_data(chip); + unsigned int val; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + val = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + val = BIT(4 - offset); + break; + default: + return -ENOTSUPP; + } + + return regmap_update_bits(data->regmap, LTC_REG_MODE, + BIT(4 - offset), val); +} + +static int ltc4306_gpio_init(struct ltc4306 *data) +{ + struct device *dev = regmap_get_device(data->regmap); + + if (!data->chip->num_gpios) + return 0; + + data->gpiochip.label = dev_name(dev); + data->gpiochip.base = -1; + data->gpiochip.ngpio = data->chip->num_gpios; + data->gpiochip.parent = 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 */ + regmap_write(data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT); + + return devm_gpiochip_add_data(dev, &data->gpiochip, data); +} + +static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, BIT(7 - chan)); +} + +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan) +{ + struct ltc4306 *data = i2c_mux_priv(muxc); + + return regmap_update_bits(data->regmap, LTC_REG_SWITCH, + LTC_SWITCH_MASK, 0); +} + +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; + struct i2c_mux_core *muxc; + struct ltc4306 *data; + struct gpio_desc *gpio; + bool idle_disc = false; + int num, ret; + + if (of_node) + idle_disc = of_property_read_bool(of_node, + "i2c-mux-idle-disconnect"); + + muxc = i2c_mux_alloc(adap, &client->dev, + LTC4306_MAX_NCHANS, sizeof(*data), + I2C_MUX_LOCKED, ltc4306_select_mux, + idle_disc ? ltc4306_deselect_mux : NULL); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + + i2c_set_clientdata(client, muxc); + + data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* Reset and enable the mux if an enable GPIO is specified. */ + gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + udelay(1); + gpiod_set_value(gpio, 1); + } + + /* + * Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if (regmap_write(data->regmap, LTC_REG_SWITCH, 0) < 0) { + dev_warn(&client->dev, "probe failed\n"); + return -ENODEV; + } + + if (of_node) { + unsigned int val = 0; + + data->chip = of_device_get_match_data(&client->dev); + + if (of_property_read_bool(of_node, + "ltc,downstream-accelerators-enable")) + val |= LTC_DOWNSTREAM_ACCL_EN; + + if (of_property_read_bool(of_node, + "ltc,upstream-accelerators-enable")) + val |= LTC_UPSTREAM_ACCL_EN; + + if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0) + return -ENODEV; + } else { + data->chip = &chips[id->driver_data]; + } + + 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++) { + ret = i2c_mux_add_adapter(muxc, 0, num, 0); + if (ret) { + i2c_mux_del_adapters(muxc); + return ret; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C switch %s\n", + num, client->name); + + return 0; +} + +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"); -- 2.7.4