Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754710AbdDKMPA (ORCPT ); Tue, 11 Apr 2017 08:15:00 -0400 Received: from mail-dm3nam03on0061.outbound.protection.outlook.com ([104.47.41.61]:35648 "EHLO NAM03-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753387AbdDKMOw (ORCPT ); Tue, 11 Apr 2017 08:14:52 -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 v5 2/2] i2c: mux: ltc4306: LTC4306 and LTC4305 I2C multiplexer/switch Date: Tue, 11 Apr 2017 14:16:16 +0200 Message-ID: <1491912976-8396-2-git-send-email-michael.hennerich@analog.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491912976-8396-1-git-send-email-michael.hennerich@analog.com> References: <1491912976-8396-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)(39410400002)(39860400002)(39450400003)(39850400002)(2980300002)(438002)(199003)(189002)(1720100001)(86362001)(2950100002)(50986999)(2906002)(50466002)(48376002)(76176999)(106466001)(36756003)(8936002)(47776003)(50226002)(5003940100001)(8676002)(189998001)(7636002)(2876002)(356003)(33646002)(38730400002)(305945005)(5660300001)(107886003)(77096006)(86152003)(966004)(4326008)(54906002)(6666003)(6306002)(562404015);DIR:OUT;SFP:1101;SCL:1;SRVR:BN3PR0301MB1283;H:nwd2mta1.analog.com;FPR:;SPF:Pass;MLV:sfv;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD013;1:d99cwfaHG+KUmpyOMYnvPCqUaJKM7A5jj5LEVUPBh5dPD2z71Cm4o8QDrGsQbbCzQSCNd9tyYvT35UZosT+WdD66Pc04G+2mWRV+1d2cYS4iudiIM0WUceIU8IQUPLRs5oXIMUompcricuBD76xMyelAobZNflrv0px3qFQD8nggA6XvcLwPv4lThvxYNl+Eo2I8xwjpXvaHFpksXwk6QNp9Thw4HHoYEyTNqR63mvaU9bMY6Ae/UfExPtH6u5HtI4M8uYZLtlM+cZ4ERN6xEofuG5ihwIzBKcVCw1aVU4LwVGdlgFqcyVAGEhFuuAZilciRwBmXMezodzrRAgjAWAZCEH5WIrhl4oLYUIyVERqnejKW6ntLoVYiyU3+KcpVhDUmcq17KHW6c7rsLVpOUL0kOKOtNWfR8rrQtcLQG6m2pQ/qezEEu/w/ULgXGizRz9vABQIn2COtVxhdTkefX/m7SR7qSG+wFRXQQzM4zM0dAeWMtWGGsZNo1w3jVwMrfdYMpwuLgA7b5ypCdLeWfhRihgw+za6KIzr5o21dr485qYML9RZHZI2zFlgeIcZtFBiDQ4XeU1KqgDHCup78pRWQ5w0bo61uPRgW44cLjv1DKTXR4bzmHUV2gpxStPIk X-MS-Office365-Filtering-Correlation-Id: cce02ceb-173f-4758-ca85-08d480d459e0 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001)(8251501002)(2017030254075)(201703131423075)(201703031133081)(201702281549075);SRVR:BN3PR0301MB1283; X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1283;3:HbP+60vsGil6bCZBw+WS6kosXRBZdTgnZJDT/Yqn4mPQ5yG3FSZtGnHFCi6KH8dlGP9dxhKrjyb72/L0C85AyfP+txFQPwlJaWkeCPCkT3Mn/fGPaTeds7X0o57YTfqhu+SyzlTYuZNJfgBFCHlfEl+KgRWI0omevib5KjTR3xvA1xQ+BZegLs2AW5jSdhE7Lu5JCuovttj4g6P5ICcBb0mDmOEjWWo+hWDKx/Wva2FKxANfeK3kZ9DvhbJHehMSejF0e4uPdZ94usYKd/Q5CxeUAhXNyUvDJKOsLPZz+7kVu/hdD+PZ3cIu+gGZEGnrRlR350Jhp8BB0WVU9+RXaeOYxnTDIT00Uy3oy5X8H5Wbw8Jwr+sXMSPuWdkkl8NiHoLog8g7SR12z4zqi8U9uT7bqC9LMXWP3/1jAkq6q2pWytuiEVvKN+lOgSDTiAQpBPv6LPMbgJ71oWZovz4YYDPYfMQdrAbxiw+SN2ZNezcd7fRrX5VNE1/UssaEFRM9d35QJPEz0iOryvg7pZQZnw== X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1283;25:utYbI5ZMDlaTx1UeMZMxtdgoW/PcuBZQfkXgqHgMpRmP0w9D2QQClcuNfJBQOfShN5jbEPbGcvX3Obo0dQezvLRDQSetblBOnEk7QrUr4M0vCVCqCmAEUJ+RBy135DraK3Dyw1nJElCKIMGdmcxUDU3o6lEG5hN8Sto5+6gUc9NLbMGd0Ab4Gl9D2xYpGXChbVGD8Zgisw3ri7AK6LbMLNEp587kEWZ3cJIXZO2IlYucc6pChRRAZrdgNMn+dJrM1bwjkIDSKjWE+/nKBrv2rlU46ax0jbBCVTrMNql2bpq6vCWc0HMBmNBDdKJxxE1xbK4v70mq3on/E6nx2CfzK8sNgYriU/WgV5z2gllXlsKbiRTJpCxF0UEzfjIjtjbePBvSNOI7a3I3kbtcTtE2KYxwqftRN6MHqzHo2AU1Lf23FYKCcsN1JhRPF6eLNPxK6lsDAOMCHv33afWrgOJW7w==;31:JG7A9tcWumK1zTDfORa33SfMMgFj9P8utDShOs/lsgvSnOoJNfiVv06ouUEisFhLfIIPTNh3Kk3lLbfcpe93QdjcY5AcfCj6zcKQ9Tk86Xu7vzhHSMyIztmhx0vzK8p9a39uwpBLfnXL1eYrRAbB+KtWcGywoBwLodwQEqsksc171DtG6r+MOkS6GPiCWdxc+pqRulQx3cYCrYmjjKq62bWniQqfY5tc1btPcZpM2WS2T7DQo4fptlSSbVqJBSO+0NRAKk877WzJO+Pt6Lj5uA== X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1283;20:cnL5Nou4EwzIKdwZSlJv/I76XcqURFAJkbhfP7L8jkcs9mfX4RHCOadCAH72OdQo+fTXmv5aP0pHx5I4+qDlUwcQHYFfVRtMzBRHpS56KgEYGjOT/PfyYXz2EN9Hgi8GxoBqHBzuTIXNtu4iPatsLEXHpgBlYV4ADF0PcUi4GJxsZCE/nOK08RZaxh4YKO3R1TbdcwOpm2PFb6qGdb6Guy+s6iAsgixe6rX+ynFVaeTfDYfNEhuMJXe917F43HnbqmMbzFedyIm4gRh0ylQk8dAgxvX3byJ73O0pzu5tuUJun8RP3Bb2eFA2HDweyr7X214qr0uQm9bZUxDtz8HnxMzMtUX43QcJ6E9TU0yo1caY3gJ0y3dLf6tvvfZjdERt6DLcHX8OHVbHp1KsQCczPTnBskk5kkoHsDiMCAPXaS0EMEOcuUjZCFmpHNx+Bqfuzh3gNMtiF9yhtDFvU5FHrWD5tCJGA/JiyX1hwTRWHaod0LO+tlYknLP5y+G/z5cj 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)(13024025)(13023025)(13018025)(13015025)(8121501046)(5005006)(3002001)(10201501046)(93006095)(93004095)(6055026)(6041248)(20161123564025)(20161123560025)(20161123555025)(20161123562025)(201703131423075)(201702281528075)(201703061421075)(6072148);SRVR:BN3PR0301MB1283;BCL:0;PCL:0;RULEID:;SRVR:BN3PR0301MB1283; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BN3PR0301MB1283;4:SCw6Dw9Ot3jZXSDDlb0ggc6+cZtOYgIzWgwjDf56?= =?us-ascii?Q?dyqZjV1STX2M+cEkaRTtLgDLVWz3r2zbVv34ghj5+FTePJfmkjQkGfJ55A6m?= =?us-ascii?Q?f/sN1RI4JOciJsZPWiX5B9hIWX28pUd6T5u3253VGVqb5Lo98Rl2/CTb/fPH?= =?us-ascii?Q?+hIa0mgkUQOQdRx8gCB96QkxQHsFSnGUO+tztln3LqKYBc4LWXurUMIr5SSB?= =?us-ascii?Q?Z5jdqeoM/0YHPk1RKDdwG7lZ2GcuNHJCdcTd/x+gsMyj38fWpBqHpqZU3FRL?= =?us-ascii?Q?vl3/4O1TZLsxVZs607ZMuXtyHpSRVBoEz3pIjVfu8jbLb7LMjBrnIAFQz56P?= =?us-ascii?Q?mtKD0MeZBtm5T52N0zDHYQ8vLUGCCXWt9BC0rva9HHMa8758uwwc5KD29Wsj?= =?us-ascii?Q?rnrL3uLZQCp3hRyBbSxpBmiGNUKW0QaP7W2GGn2VRo8Km2BOL8jPBylzHba8?= =?us-ascii?Q?Vlk1MjpwQAf2r/sDnIayFhsCGVlqSswPkegBPoX5yyPdwTDzhgbt2md76EJX?= =?us-ascii?Q?kbk4wvjSF37mOElP2uD8GMA4YpyYBCBPN4pQiwFxgYZXnyqLRsD7OUlNJMP6?= =?us-ascii?Q?DN3BB8Ee2jsZrdOiS01QAMBrzM/HO9WRIRMIWmHs5i+opfKfLVPGTUtPfCPF?= =?us-ascii?Q?GHAjhdojkt18oZ1RnGfyhdpm0YuSRVT1hrcEJYeojKdyP9VdjsbS+nkjzoDw?= =?us-ascii?Q?rmESFp38GYu0799cJOtfIM7SJP0lGjAkf8li4g5mAv8kETc6Q8engZ4Pe18c?= =?us-ascii?Q?WL2h0i+6hQCmDqLIk9S8/SkwdqYjXAek72PAGAJvp7mnShzU6VbYNwhoDYZT?= =?us-ascii?Q?OxaWgVHdTSI5U12prHAyK2nyUIVIqnJ/sKRRH2UQvCIp8jCnplOGYX5qJ8We?= =?us-ascii?Q?5txh88VGLxz1FYQvCAf1bAR+ORUeOWTBoUbZOwMofjHL/ACDC9pSBTbRLQUF?= =?us-ascii?Q?frZpSPvhpvjLpCQudUS0HBA5k8TkPC7rAp+DNPLmM3Jjnl0tHrmAVUHFbWx1?= =?us-ascii?Q?0rY=3D?= X-Forefront-PRVS: 0274272F87 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BN3PR0301MB1283;23:ynGwQeXmLF+XLEfloxa6+80NgTOYviFmkqRV681?= =?us-ascii?Q?5ZF1VA6L9liWAagpLcj960NN/IBYDDuIlJugFCB4lIOfmxeH2ocGUv1ZuclE?= =?us-ascii?Q?HOYa8Jdwu34YW1uCPDCx05Rp7h9CBHSGyo+CNXoHfTg/OyOhqEd+5C/Zn+WP?= =?us-ascii?Q?Ve0qcauNEoTv8dOriEj4t+tI3zwFxJ57skf28nocOE3nboL+X2czCqhSkfMV?= =?us-ascii?Q?s73fuddSC33f3gX4VkFbSxodFfLissuFuLkw4KF7RSPwCv79E3BukjE3ndvu?= =?us-ascii?Q?Rt7r0oYo+M5MryuwgAi2NG0KClqLtnIvpJu0lJRB35G4fKLgR5V9295FJewh?= =?us-ascii?Q?Wxk1G2KGg3LWSNjyCr2OCMpbo+QsTY7+91ra9ghbWWcFrc/J3Plsh+d1oC6U?= =?us-ascii?Q?ax2WzQtPKUYU2cQ5Zx3Fpjnmuoq3Jih33hePsooGGqTYpvDAHUAkWIOaX8Ve?= =?us-ascii?Q?VRQQOg5krGtmCUI1j8g931E1yEFp7I9tPVF4CRPuXqE9H0FlFKI7fOHpS2QF?= =?us-ascii?Q?hYnnSoLmi6iJUAzkd+aXjPi9nL7Kb5KpDHks0PLVtG0SBYW1CKnS5pZ/XVEp?= =?us-ascii?Q?5M48hsItc+F3bldsmQMGUdkmia1cEkJCCu8tgAhBiWKq0C07jjvqfR9nmDUy?= =?us-ascii?Q?7KRnsdL8dvFwQXkma0mdaZ6nDJP4snttk41d+RJQt8ih4yvgkmTd/qQ6Coaf?= =?us-ascii?Q?/26w2daUpQgU4e+4deH2kweYLlF0YaDd8JOgE743QgElfOwg8TXigTDOLGbi?= =?us-ascii?Q?BKXUY1MOuryts7ynojOHyNElHUqUeOR/VWYgAmtrppZ/zcINAn9NY1Xx2mPp?= =?us-ascii?Q?+EznISoFDYqYmyFqdfNRa0Yx5uqTa9l7BbJUh9kKq1t3EbjgOjKesdAHInek?= =?us-ascii?Q?3nGMLBPb/ihydx5fv+onUSnj7QQ9rVR3SuTTaH364cDmapJ+AlVzZXABDWyv?= =?us-ascii?Q?PGoGNiSh78qSSGboU1hJPBpHC+Uk0aG/YsRz3RsHitMX/emZ1j/dd/5rBG22?= =?us-ascii?Q?Vfaah/03ZMVWqJGgtXUf23MEQFKXDhLEwS2XFfrlb0SbamU60jhbUObeS2pg?= =?us-ascii?Q?H1UFK2SQ=3D?= X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1283;6:6lYeCIG47/LDNQnDKkE+ib03RL2nW1ljM54/bQyzaWmfFKNajJ787/K6XvWEhNjG/RY4oMh4NdVyFY76F8bHhoqugkerhCqlVTB/TgDZs3igTSClvJx/KqnUnlfjWziHFFQN92yfSEgyckPPPLWX4mKPvOLoHxRy5lKDuktveMIxlLcwacRXF80ZUeOGcOCiIaEl3+jwJx3i/j/8yUWF26KRtvFtK8EAItjJcY8VmcDGfkccUAQIFUK5BeZT3Lc6M49ueUnC+VXzEB86WHieSQSEzKqkipy3iMTuhhg6u++vl77pLjspBhBz+N5uwdVHVQF+OZt+V7V07oWrB22Xs+oPuV1rZgGwgn9jIUqDN1X/5pKemJ0asOlNfXHBLSgA9+afXkdFhnwqXV75j1kDI29FY35TMSUhDzkY21yM4wKODXADmwL1HRwhEuJ60kwck2y786aoGJihIiZawSOSeDYBqZwT+nl5f8sEsEqcUOM=;5:RWwIUy7FfbQhCKZpoK6q9PES4t/qlHdPPslq95wmURMqcaMGsssHJNYjTB5+4Xl2dwPbD+qNf/Hd49Y3VCPSYO/eeIWoK2Sjv/UWm/omkbNEtnfcLJNAXxfSEjgjn8b3ZHr5AnW69Z1usVa5LaERfA==;24:k02fcymYw3IDvOSNJblLulsjqkH9X/zR7Vus6XJm6mCy6S6s4VOzmWZQfAHOvDolOTqm6YxrDuKoYC4p+N/4o8ZFev32Ap/u7/FG27EEMOg= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1283;7:IdkPSI1llUmloM6wOpp22bAp5L8LRQ/EqtPSr3UmXCYSFfyLpu/eS3i/ZzP0xNb4LPL2x03BYYkNjvvIFrhvoKweli2MDDpGmkG4kYt6xHr6COwbzioAUE/6uIuvVLnzMYQqqnk7ZQ7IK1CrPYS0Of/WNBWyt7vXBe1FgbT2IUWZlK0nCISU7/XEt7GgtOsbDXq2iH6Ei2DRTZ6P90L9OeDeajNEDpLHCG62082DAIsMQt3C6BCKio4zx5hD//T0owpolIJ+Xaufybte6hNUMZjRb8TnB2/xP+Oq15Rkm98uwXX0KyXFVBNRfr5MQaL3xs/N0+iqnVJnA423CCgtDA== X-OriginatorOrg: analog.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Apr 2017 12:14:49.2600 (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: BN3PR0301MB1283 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11979 Lines: 441 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 Changes since v4: - Remove redundant includes - Use regmap cache type REGCACHE_FLAT - gpio_get restore double negates on return - Implement gpio_get_direction - Use unified device property interface - mux_alloc exact number of channels - Reword kconfig help text --- MAINTAINERS | 8 + drivers/i2c/muxes/Kconfig | 11 ++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/i2c-mux-ltc4306.c | 322 ++++++++++++++++++++++++++++++++++++ 4 files changed, 342 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..1e160fc 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 Analog Devices + 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..311b1cc --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -0,0 +1,322 @@ +/* + * 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 + +#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_FLAT, +}; + +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_get_direction(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_MODE, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(7 - offset)); +} + +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.get_direction = ltc4306_gpio_get_direction; + 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); + const struct chip_desc *chip; + struct i2c_mux_core *muxc; + struct ltc4306 *data; + struct gpio_desc *gpio; + bool idle_disc; + unsigned int val = 0; + int num, ret; + + chip = of_device_get_match_data(&client->dev); + + if (!chip) + chip = &chips[id->driver_data]; + + idle_disc = device_property_read_bool(&client->dev, + "i2c-mux-idle-disconnect"); + + muxc = i2c_mux_alloc(adap, &client->dev, + chip->nchans, sizeof(*data), + I2C_MUX_LOCKED, ltc4306_select_mux, + idle_disc ? ltc4306_deselect_mux : NULL); + if (!muxc) + return -ENOMEM; + data = i2c_mux_priv(muxc); + data->chip = chip; + + 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 (device_property_read_bool(&client->dev, + "ltc,downstream-accelerators-enable")) + val |= LTC_DOWNSTREAM_ACCL_EN; + + if (device_property_read_bool(&client->dev, + "ltc,upstream-accelerators-enable")) + val |= LTC_UPSTREAM_ACCL_EN; + + if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0) + return -ENODEV; + + ret = ltc4306_gpio_init(data); + if (ret < 0) + return ret; + + /* Now create an adapter for each channel */ + for (num = 0; num < 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