From nobody Wed Oct 8 00:25:02 2025 Received: from mail-ed1-f45.google.com (mail-ed1-f45.google.com [209.85.208.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5493D2BE63B; Thu, 3 Jul 2025 20:58:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751576289; cv=none; b=FVuoi60k+0XUC4wE1kLo0nrRMX/oBU16DH9mYzF87AXB70S7E0T0sx5+rhrHaoDEBnjpLlcqZUdarM42ii9qIY2JVc1pzddHNsJb+AC5s9O/HS9AAJH+oKsa6FcRGJDG40bjrNinODS7ACce+p02+wxaR7AU4wLvVpOcisOVQH0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751576289; c=relaxed/simple; bh=/nuqxa/ILnXCe22BXynLdnDgeySQEaO9Y992jK3M8pU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oZIm26FTO9kWrCOTpVkHmcKZMrAETd8gw0DJ+4ubQgGmiQ097fhiJPE/F935Dd2xDP6WQ91OG0zD1p9HehfWFNECAjOqGClcu8zsVhvwWTPK9n7ARGlQjmN5OZ49Uopm1RhBmS2jXdbM7upWxMxzrMX9WAJtAZXoH/jp4As+OwA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fmSGe9EH; arc=none smtp.client-ip=209.85.208.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fmSGe9EH" Received: by mail-ed1-f45.google.com with SMTP id 4fb4d7f45d1cf-60cc11b34f6so2326078a12.0; Thu, 03 Jul 2025 13:58:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751576286; x=1752181086; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZR6cvLWrkJQnQk+tQxKGEbYe0TZ4qvAI7uF+63w+eJs=; b=fmSGe9EH/kJw6F5wJJS5DeLBZ9LM0fKr5AudOLR/jDhf3Mwmr0FPMNQz08Rdkv5au0 LKAB6kqf7SmRbyn4glVN4d5vmF4ytbJfspWvaYghIzIwHH59YS5Yuss819FLiuIlLX0R Oy429EIqYC6QjnJkk+sIvd4rEBRKbZNjCKo7slxLwq9tn22q81ka/ZUpp6db8IZMfy6i uz6gNuW9MKTAtCV466J3Q6zM5ThdtU0AYrZDv9qdMb12TKnU7+N5nb5SInlNwTPYrcqz YloIPXVwwnLKsQk+nR7IIn4NgNrCAzuqouw9h2uwRKKHwsfFDfz6WHkzffY5c+9CzXGH +7/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751576286; x=1752181086; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZR6cvLWrkJQnQk+tQxKGEbYe0TZ4qvAI7uF+63w+eJs=; b=ICOlv51r6KCRu2qKKhbk2jz7fkJ0q/00I16gpfYOQ2spUPAJe6y+3Iyq0IWqOyCtWt WwgoePY0jfr/r6DAvASRdKdC11VCHjvCd2pebFgREYUkP2SEMVomzhhAXX2t0jcnvLAU Uwgv/xdVjWdHTvxPWGnRGF2TDFcoL3qt8EtL93iDoCIw1MQzC2SasIBJENId3Z5A2ia5 ZJVvhgYcJHcDz72VZnOTOypJzHdp/KFXEvliKyCqH8ckpaBwR6V67GX4bM4x8Vo4iLr/ 0JHNgLkylr1AtZvihoDbc24v3Vowwoq6WsPV7cwb/onFgmHvj4eu7/Vjs8yhXgtvxC/o 2Exg== X-Forwarded-Encrypted: i=1; AJvYcCUq4iKSRpsK8Dl+E1HnQQBPUZerh7TadbuGot03VPQXQz7LMCtfxQKty19TrY6E2RhrT0CakrZte7jMaHPV@vger.kernel.org, AJvYcCWmuE5QV7u9X5qH6i7DYBEksqdTHNM6sQG9bCoPDrCpob6ujIDMg+T5LsN3le8CL7Qy9d15/6U4VV/+@vger.kernel.org X-Gm-Message-State: AOJu0YzIfp6L2uGskdJbldQ6RQrORVoErYvSw1bKjdx5koCXi3SLK1gG 21GpKDZClJd2ROw2Y8O9jUaofHpW7iMnc4v4R85OQsQAxfb+ybfA4SZNpA9a9RiRruU= X-Gm-Gg: ASbGncso0awZDDz/PdvScSoXkTjo3P7WvQqWR/77A9Sa/hIx4aBjLnuV3BHXjaELbzd hMIm6Zfpy5bcg4kHAZDAv3nHCa+MlKOgEZ6Y+0vx60Z1LnKw2vzOxOzgg7sKOqqTMW6WWRZuDnw Q3BTPQUXvSd9aAWB9nKvgcNG4gVl5EiUV0+DqdB0613ZW/YCigDEADSo/l/9VYkBNczJKKah/dG QVvN4TVZcFNKbNjpYYD0ULNsdIPDh7y8PEw7tkO6TrYS4Y04pn/hh26rtNU9aU/G1zFr+CeHlUk wzDhBnuPpwpXbWTtK+SFZ7KUrJ06nB08pNEnKkvTYHl+J3+d/Cm/khmND9RtaPfS7YWbfOb5IiD +0xyvPN69XsDQIeFHf1xLuls2ptHa X-Google-Smtp-Source: AGHT+IEl6VZoJLD7n2Y7R1MQ2jPxtbh9Uw6Vzg4iLjrmCZBv1J4pJf0zKMtjIgjvQrDXbSUdPeshyg== X-Received: by 2002:a17:906:3144:b0:ae0:ace9:67eb with SMTP id a640c23a62f3a-ae3f82d6384mr48970566b.14.1751576285353; Thu, 03 Jul 2025 13:58:05 -0700 (PDT) Received: from maria-HP-Pavilion-Laptop.home ([178.226.54.132]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae3f6ac5feasm38750366b.107.2025.07.03.13.58.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 03 Jul 2025 13:58:04 -0700 (PDT) From: Maria Garcia To: linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Linus Walleij , Bartosz Golaszewski , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maria Garcia , Maria Garcia , Conor Dooley Subject: [PATCH v4 1/2] dt-bindings: gpio: pca95xx: add TI TCA6418 Date: Thu, 3 Jul 2025 22:57:39 +0200 Message-ID: <20250703205740.45385-2-mariagarcia7293@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250703205740.45385-1-mariagarcia7293@gmail.com> References: <20250703205740.45385-1-mariagarcia7293@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The TCA6418E is a 18-channel I2C I/O expander with integrated ESD protection. Signed-off-by: Maria Garcia Acked-by: Conor Dooley --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Doc= umentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 4d3f52f8d1b8..12134c737ad8 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -68,6 +68,7 @@ properties: - ti,pca9536 - ti,tca6408 - ti,tca6416 + - ti,tca6418 - ti,tca6424 - ti,tca9535 - ti,tca9538 --=20 2.43.0 From nobody Wed Oct 8 00:25:02 2025 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 31D4C2BEC21; Thu, 3 Jul 2025 20:58:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751576294; cv=none; b=VBR3SHV8w1Z5RjHoCuY8DEsJCreKmTTtB4Z8naQdu5FDvRnLcc1fsGvWaaHcjr/y7IE7/xZmu+DP5EgKDFNIOmfq8hoce+w88o9BLpz+iRm+j6MJb+fY2/cx17F2lPzkmw2XHcH60JIVb4TIzZiSuDPTwEefw+bqb3nsYlHZt+g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751576294; c=relaxed/simple; bh=ljqK7ZSinwdj3wGb/XbvTD/aeZolJSDY9gLpfwvON0Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KpeRUX5jQhueP3a1ijQbJ2atuABjLpAqcAc8QvpLrKRUreHncajvQ9b4g1qEKeW30yTv8JrMprYc87IwURthN1PW1yN7CUmGt933LQ9UtvWciee3ou8x+d8dSiZLLpRzRxXKfwumbC+oIS4miyK3gz5nBI3TdT+LmHFKKu2w2m0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Fzi/B22+; arc=none smtp.client-ip=209.85.218.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Fzi/B22+" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-ae0a0cd709bso298826966b.0; Thu, 03 Jul 2025 13:58:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751576290; x=1752181090; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RBl8dQczHAXEhEM0gV05tmYGqc2dxbZmQ+ZuwlIemCU=; b=Fzi/B22+DR3NE2F1XskrNjUY6xp0kErfyYbVgHSovu4dE+0e5Wp/bDMdv0MHWXC6Cc Rs1VwWtZm1W0+qOs/cqMwPgvMOwU+1bY9KzbYrrJXsmqTTbaA25q1YMXD0drCTV0MngX bkLjOFL3rVJCMZvpbag92yVKX/AxdNQZjIQ3ZGcQUwttvFtx15Xkfxkwdo02omOSMaQY IhWTb8nr1SxHbDge1AZs5tSXHLWH1i6wX9r1QaAY3383XN23Rybf3a4Wwrb9hh36aPtb XMbCoxrL6zVJjvIGeYp6GnAUwGkAIgLPBw0R/54/p1dm8LNCluTZ30CBa0Oa6Tl36K41 rWRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751576290; x=1752181090; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RBl8dQczHAXEhEM0gV05tmYGqc2dxbZmQ+ZuwlIemCU=; b=sokj0emX+LMZaD8fP2azYU+G37I5bCwWavY+DUhOhGA36/qVj6v11RISQ/QmD87ZfR g1h5GJL3rOjL3dE0JSMNtkTmeCLrIpQJazKOza/s7YHAsNZUjDVEmdavp7LCh9Z0OSgD rKMJRVZfOyN3rAFPFkUy9IT/4eL+M2B9XbyfYxnwwkI07qthccMg2xTeFrvQminwU/Z5 YbRF/Ct57N5EoGop1zKhkjIRv1QDm2OfQlarUNA7/r4XvXKvzP1kn+ywqDDkV4qaRqiX 9yUUpM0cA5cW9GYcBfRyyXLfqaKmgN8+4xp5DiZ22rWhRJ/FNUlfFrqYZBiOWUSPIQhR gnDA== X-Forwarded-Encrypted: i=1; AJvYcCUC5TE/t+H6fr0zF8Gsv2OgLPNTNw8UPiPfJWXc7wvxCOiEkYLgF3egq9ImWCykm5d1GpShEEf8wWd0@vger.kernel.org, AJvYcCWVKd+y25no3nhLtWXhImQn7k/kfRF/UFqjiJbSwF5oXq/kbVh84XYcuu96RqgAdSmkxkRuBDhpCsOYsXxj@vger.kernel.org X-Gm-Message-State: AOJu0YyhWVOhTcJO6xqkcxySj1dKj/Mx3XHFuYS2tMp814R0d0QCJw2p x6NEp8vciao4p7r9sKDf2d+g76KPgbsN/i+KDcI7bth9Ey13ZIjqZIXqUk0pwuJaOvg= X-Gm-Gg: ASbGncvjLUbwuKAHCOUCVfBGcWtrpSh29VLBdSaasW+I3X7btlVJctf6euobVdXMdDX IuTnsZw+QEN/8aNOUrv9+jHzePRaVYJcDRwlen8JEoUCTNyKsiCJW3imNBMuSdpKI9PDId9CM4O 3/BAaU5FfwDgVmox13jrYIw8Ewr9cFawHZ+46WKdktan4RLIgSqlVxe6zMly32aJMj+fMtWXtSX ytA0PUGdZYsfsk5tq5KWUCnrEMhH1GeRGBt4m3Hq1jcxxrCH0At/MbMp/7AgrNlYASyjpTZZv9y 9MMQ/4jNLNBWxvs5yN5W0x3BjIuQodQ2GkiXbWGXfvneFcv6zte6SpvRAZZml7+Kzmvmbazt19Q YL8tD2q/nRfkJeKT41H5kTw3zZoeE X-Google-Smtp-Source: AGHT+IHH8akoCM0HxEWjNOLH+Y13IPfIkT1lnFFch0nvpFBf3R9eFnP7PvP3T7bTb4ZkkYDOjqkbTw== X-Received: by 2002:a17:906:c14e:b0:ae3:d0fe:a35e with SMTP id a640c23a62f3a-ae3f9c06608mr39671266b.11.1751576289790; Thu, 03 Jul 2025 13:58:09 -0700 (PDT) Received: from maria-HP-Pavilion-Laptop.home ([178.226.54.132]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae3f6ac5feasm38750366b.107.2025.07.03.13.58.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 03 Jul 2025 13:58:09 -0700 (PDT) From: Maria Garcia To: linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Linus Walleij , Bartosz Golaszewski , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Maria Garcia , Maria Garcia Subject: [PATCH v4 2/2] gpio: pca953x: Add support for TI TCA6418 Date: Thu, 3 Jul 2025 22:57:40 +0200 Message-ID: <20250703205740.45385-3-mariagarcia7293@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250703205740.45385-1-mariagarcia7293@gmail.com> References: <20250703205740.45385-1-mariagarcia7293@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The TI TCA6418 is a 18-channel I2C I/O expander. It is slightly different to other models from the same family, such as TCA6416, but has enough in common with them to make it work with just a few tweaks, which are explained in the code's documentation. Signed-off-by: Maria Garcia --- drivers/gpio/gpio-pca953x.c | 153 +++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e80a96f39788..5d40bf9b941d 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -38,6 +38,10 @@ #define PCA953X_INVERT 0x02 #define PCA953X_DIRECTION 0x03 =20 +#define TCA6418_INPUT 0x14 +#define TCA6418_OUTPUT 0x17 +#define TCA6418_DIRECTION 0x23 + #define REG_ADDR_MASK GENMASK(5, 0) #define REG_ADDR_EXT BIT(6) #define REG_ADDR_AI BIT(7) @@ -76,7 +80,8 @@ #define PCA953X_TYPE BIT(12) #define PCA957X_TYPE BIT(13) #define PCAL653X_TYPE BIT(14) -#define PCA_TYPE_MASK GENMASK(15, 12) +#define TCA6418_TYPE BIT(16) +#define PCA_TYPE_MASK GENMASK(16, 12) =20 #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) =20 @@ -115,6 +120,7 @@ static const struct i2c_device_id pca953x_id[] =3D { { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, @@ -204,6 +210,13 @@ static const struct pca953x_reg_config pca957x_regs = =3D { .invert =3D PCA957X_INVRT, }; =20 +static const struct pca953x_reg_config tca6418_regs =3D { + .direction =3D TCA6418_DIRECTION, + .output =3D TCA6418_OUTPUT, + .input =3D TCA6418_INPUT, + .invert =3D 0xFF, /* Does not apply */ +}; + struct pca953x_chip { unsigned gpio_start; struct mutex i2c_lock; @@ -237,6 +250,22 @@ static int pca953x_bank_shift(struct pca953x_chip *chi= p) return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); } =20 +/* + * Helper function to get the correct bit mask for a given offset and chip= type. + * The TCA6418's input, output, and direction banks have a peculiar bit or= der: + * the first byte uses reversed bit order, while the second byte uses stan= dard order. + */ +static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned = int offset) +{ + unsigned int bit_pos_in_bank =3D offset % BANK_SZ; + int msb =3D BANK_SZ - 1; + + if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D TCA6418_TYPE && offset <=3D m= sb) + return BIT(msb - bit_pos_in_bank); + + return BIT(bit_pos_in_bank); +} + #define PCA953x_BANK_INPUT BIT(0) #define PCA953x_BANK_OUTPUT BIT(1) #define PCA953x_BANK_POLARITY BIT(2) @@ -353,18 +382,43 @@ static bool pcal6534_check_register(struct pca953x_ch= ip *chip, unsigned int reg, return true; } =20 +/* TCA6418 breaks the PCA953x register order rule */ +static bool tca6418_check_register(struct pca953x_chip *chip, unsigned int= reg, + u32 access_type_mask) +{ + /* Valid Input Registers - BIT(0) for readable access */ + if (reg >=3D TCA6418_INPUT && reg < (TCA6418_INPUT + NBANK(chip))) + return (access_type_mask & BIT(0)); + + /* Valid Output Registers - BIT(1) for writeable access */ + if (reg >=3D TCA6418_OUTPUT && reg < (TCA6418_OUTPUT + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + /* Valid Direction Registers - BIT(2) for volatile access */ + if (reg >=3D TCA6418_DIRECTION && reg < (TCA6418_DIRECTION + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + return false; +} + static bool pca953x_readable_register(struct device *dev, unsigned int reg) { struct pca953x_chip *chip =3D dev_get_drvdata(dev); u32 bank; =20 - if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank =3D PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(0) to indicate read access */ + return tca6418_check_register(chip, reg, BIT(0)); + default: bank =3D PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } =20 if (chip->driver_data & PCA_PCAL) { @@ -381,12 +435,18 @@ static bool pca953x_writeable_register(struct device = *dev, unsigned int reg) struct pca953x_chip *chip =3D dev_get_drvdata(dev); u32 bank; =20 - if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank =3D PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(1) for write access */ + return tca6418_check_register(chip, reg, BIT(1)); + default: bank =3D PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } =20 if (chip->driver_data & PCA_PCAL) @@ -401,10 +461,17 @@ static bool pca953x_volatile_register(struct device *= dev, unsigned int reg) struct pca953x_chip *chip =3D dev_get_drvdata(dev); u32 bank; =20 - if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D PCA957X_TYPE) + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank =3D PCA957x_BANK_INPUT; - else + break; + case TCA6418_TYPE: + /* BIT(2) for volatile access */ + return tca6418_check_register(chip, reg, BIT(2)); + default: bank =3D PCA953x_BANK_INPUT; + break; + } =20 if (chip->driver_data & PCA_PCAL) bank |=3D PCAL9xxx_BANK_IRQ_STAT; @@ -489,6 +556,16 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *ch= ip, int reg, int off) return pinctrl + addr + (off / BANK_SZ); } =20 +static u8 tca6418_recalc_addr(struct pca953x_chip *chip, int reg_base, int= offset) +{ + /* + * reg_base will be TCA6418_INPUT, TCA6418_OUTPUT, or TCA6418_DIRECTION + * offset is the global GPIO line offset (0-17) + * BANK_SZ is 8 for TCA6418 (8 bits per register bank) + */ + return reg_base + (offset / BANK_SZ); +} + static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned= long *val) { u8 regaddr =3D chip->recalc_addr(chip, reg, 0); @@ -529,10 +606,13 @@ static int pca953x_gpio_direction_input(struct gpio_c= hip *gc, unsigned off) { struct pca953x_chip *chip =3D gpiochip_get_data(gc); u8 dirreg =3D chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit =3D BIT(off % BANK_SZ); + u8 bit =3D pca953x_get_bit_mask(chip, off); =20 guard(mutex)(&chip->i2c_lock); =20 + if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D TCA6418_TYPE) + return regmap_write_bits(chip->regmap, dirreg, bit, 0); + return regmap_write_bits(chip->regmap, dirreg, bit, bit); } =20 @@ -542,7 +622,7 @@ static int pca953x_gpio_direction_output(struct gpio_ch= ip *gc, struct pca953x_chip *chip =3D gpiochip_get_data(gc); u8 dirreg =3D chip->recalc_addr(chip, chip->regs->direction, off); u8 outreg =3D chip->recalc_addr(chip, chip->regs->output, off); - u8 bit =3D BIT(off % BANK_SZ); + u8 bit =3D pca953x_get_bit_mask(chip, off); int ret; =20 guard(mutex)(&chip->i2c_lock); @@ -552,7 +632,13 @@ static int pca953x_gpio_direction_output(struct gpio_c= hip *gc, if (ret) return ret; =20 - /* then direction */ + /* + * then direction + * (in/out logic is inverted on TCA6418) + */ + if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D TCA6418_TYPE) + return regmap_write_bits(chip->regmap, dirreg, bit, bit); + return regmap_write_bits(chip->regmap, dirreg, bit, 0); } =20 @@ -560,7 +646,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc,= unsigned off) { struct pca953x_chip *chip =3D gpiochip_get_data(gc); u8 inreg =3D chip->recalc_addr(chip, chip->regs->input, off); - u8 bit =3D BIT(off % BANK_SZ); + u8 bit =3D pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; =20 @@ -577,7 +663,7 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc,= unsigned int off, { struct pca953x_chip *chip =3D gpiochip_get_data(gc); u8 outreg =3D chip->recalc_addr(chip, chip->regs->output, off); - u8 bit =3D BIT(off % BANK_SZ); + u8 bit =3D pca953x_get_bit_mask(chip, off); =20 guard(mutex)(&chip->i2c_lock); =20 @@ -588,7 +674,7 @@ static int pca953x_gpio_get_direction(struct gpio_chip = *gc, unsigned off) { struct pca953x_chip *chip =3D gpiochip_get_data(gc); u8 dirreg =3D chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit =3D BIT(off % BANK_SZ); + u8 bit =3D pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; =20 @@ -597,7 +683,14 @@ static int pca953x_gpio_get_direction(struct gpio_chip= *gc, unsigned off) if (ret < 0) return ret; =20 - if (reg_val & bit) + /* (in/out logic is inverted on TCA6418) */ + if (reg_val & bit) { + if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D TCA6418_TYPE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; + } + if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D TCA6418_TYPE) return GPIO_LINE_DIRECTION_IN; =20 return GPIO_LINE_DIRECTION_OUT; @@ -1117,12 +1210,22 @@ static int pca953x_probe(struct i2c_client *client) regmap_config =3D &pca953x_i2c_regmap; } =20 - if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D PCAL653X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCAL653X_TYPE: chip->recalc_addr =3D pcal6534_recalc_addr; chip->check_reg =3D pcal6534_check_register; - } else { + break; + case TCA6418_TYPE: + chip->recalc_addr =3D tca6418_recalc_addr; + /* + * We don't assign chip->check_reg =3D tca6418_check_register directly h= ere. + * Instead, the wrappers handle the dispatch based on PCA_CHIP_TYPE. + */ + break; + default: chip->recalc_addr =3D pca953x_recalc_addr; chip->check_reg =3D pca953x_check_register; + break; } =20 chip->regmap =3D devm_regmap_init_i2c(client, regmap_config); @@ -1151,15 +1254,22 @@ static int pca953x_probe(struct i2c_client *client) lockdep_set_subclass(&chip->i2c_lock, i2c_adapter_depth(client->adapter)); =20 - /* initialize cached registers from their original values. + /* + * initialize cached registers from their original values. * we can't share this chip with another i2c master. */ - if (PCA_CHIP_TYPE(chip->driver_data) =3D=3D PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: chip->regs =3D &pca957x_regs; ret =3D device_pca957x_init(chip); - } else { + break; + case TCA6418_TYPE: + chip->regs =3D &tca6418_regs; + break; + default: chip->regs =3D &pca953x_regs; ret =3D device_pca95xx_init(chip); + break; } if (ret) return ret; @@ -1325,6 +1435,7 @@ static const struct of_device_id pca953x_dt_ids[] =3D= { { .compatible =3D "ti,pca9536", .data =3D OF_953X( 4, 0), }, { .compatible =3D "ti,tca6408", .data =3D OF_953X( 8, PCA_INT), }, { .compatible =3D "ti,tca6416", .data =3D OF_953X(16, PCA_INT), }, + { .compatible =3D "ti,tca6418", .data =3D (void *)(18 | TCA6418_TYPE | PC= A_INT), }, { .compatible =3D "ti,tca6424", .data =3D OF_953X(24, PCA_INT), }, { .compatible =3D "ti,tca9535", .data =3D OF_953X(16, PCA_INT), }, { .compatible =3D "ti,tca9538", .data =3D OF_953X( 8, PCA_INT), }, @@ -1355,7 +1466,9 @@ static int __init pca953x_init(void) { return i2c_add_driver(&pca953x_driver); } -/* register after i2c postcore initcall and before + +/* + * register after i2c postcore initcall and before * subsys initcalls that may rely on these GPIOs */ subsys_initcall(pca953x_init); --=20 2.43.0