From nobody Wed Oct 8 06:47:47 2025 Received: from mail-ed1-f49.google.com (mail-ed1-f49.google.com [209.85.208.49]) (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 481EE24466E; Tue, 1 Jul 2025 10:06:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751364361; cv=none; b=mIf6jYhmAL1U9f3BdHUrWpyyF4qbGwpmXP3LrDjs5m2DOTJhOQBUjLXDpFW6fWKxoZVGjedWMxKJAlCriZrz3WvsvVQfRLe6fwxuJeU2jozxTUMpj3n8453L1xAyaTGP6xTSeyu6QSjSmeZX0MUK01tlwIW4Tx0Y/8HSkfm0wV4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751364361; c=relaxed/simple; bh=431mVV38AmwRWs9AhD2EOxQ+yeHfx1PC9TaLuE9GKSc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C+5JzTsp+zsgRWCpJdN4ttxG5GvlAZMoKMPHxjrX8ab1j+xoZuFGcGRvcnbQMGEgWWB/nCjPSXu7MnRLpYMbg1s3svJo/ZpX7lbgL+sgJDFxkaPhCUvYWGog1RQZQ+2acSIm9g+mdwbH6JOtSF0r3+tU2JlOzxL6ruRYGS0t+78= 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=TGQkKZ2y; arc=none smtp.client-ip=209.85.208.49 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="TGQkKZ2y" Received: by mail-ed1-f49.google.com with SMTP id 4fb4d7f45d1cf-60700a745e5so6403256a12.3; Tue, 01 Jul 2025 03:05:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751364358; x=1751969158; 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=jRzWTd31z5NgMVNxrwWmldTdV4yQ0MV+geV1WHPLNEg=; b=TGQkKZ2ynzxftEbjvAHO+69zGl2LJST3zJhx3T1+/CLcrfTBWwG98+NGOlbwBiFCBF LKDJwjA37PVXkCK9zkpI2nJybsPa2so98XsqyLqVzFBP1hjyj2Uz+1EvxKbKVhW7x8Kf dowz2s1zXg5FgLeHpeCPZ6joxbc3oq1+R9Ry1FGImt41Ll0DR2sKVqUx+/eHkmRpAgWk kn0KSybQnRl5S34Cz9M31pfUZX+rstrfXw3rKiZmzM3TY20s0O+9Cr7yl9+Y8mijQQgS /khkDjLlTqtCi/pjmnQ+hxkBvsVBD9aAtv9OSLaJg7XTYw7MPKUo2d1SBeI/sPFEQLmC kogg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751364358; x=1751969158; 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=jRzWTd31z5NgMVNxrwWmldTdV4yQ0MV+geV1WHPLNEg=; b=vh0Fsm+SplKStOkaG6MzF8mAXWhyPdYPnMYRYHXuhlX3TKMKUrM4oAXbhhNufLrPuE OScjsMIvaV+f9TMwxUPrBEu/kxgkzcdzbwWmahzZf+ET6ndPv8CEt58C09CxbwunRyg9 L5GS2eweDBoiFynDk/rxU2ZOTJ1cUNiUiDIxooGPETQBSYINIK1UXCnnhsOG92Rqjdmy lf6/45Qz9hmcObpdjXvZn3cfSNjKOszfxi9MEcjWVnOVwsEZ3Hw0YsQKGWeiSS5m85Wr qeJvGiXwDSGSsW9nrYHAkaOfdCnYSXdUIiCEwUFpmps20sipC3e0djnSsFyWihUtttPH Y9oA== X-Forwarded-Encrypted: i=1; AJvYcCU7h9tfmo4DUeTmgMb2Aiq83fF5ubanI2b7ql4y0/kk3MXb4o+Tay70Y9ohpQkNjJUNkVO/DoxG42CL@vger.kernel.org, AJvYcCVMYsZJp0mJVXMmiZExXCrEYusoUXUnB5jtL4YebuRr2ta8brHdF1R57941o11tj5fyK7mI/JPaoewlFCAj@vger.kernel.org X-Gm-Message-State: AOJu0YxAYsavsziVFlNeZGQqp9lY811SbZ+pEX1v/Pbh3GuTynGzfz66 p2nzM5owc7SQZ84+DdlGYs3WIyGoPx6wtz995tRJF2NiVSwuBAqrbtIdhNjuBdIMBPk= X-Gm-Gg: ASbGncsAgTwiaanQLvtM8Cha8MqebZO+/10ah6FquFeiELNGmQbksDnAU2Dlklo8BUA uKbU1cwEXP2SbTw2fDHyypUe+yLZifzWwvQPHyuSQwMEHoRzC+ekpg9rEw53iimqJYfcELryZS6 xjuOCg3SRu/eGo7QRBkvlbrOBg/90soj6OwHRxvf5KGs4uZb72okvQXe7UwC8PHeERk9r6G8fOW Ovs9W3YqYHPIVQkmc6OVMkssDN9rawDSWv/PGVE1UfwS8W0nmGwCgvEn0SNrksp2zXXub2cWkOi /pCrwgrwH3kgiloW1eoHlPFp1QkoqW/iUtluK0scKTUxDkUcdBqgsj4jziXUidb9uFoFLrh4/84 dI3p7CAp2+L6E8u/0fw== X-Google-Smtp-Source: AGHT+IFrjWljQzlYwYSLPauhXdqTs/Lh5w2oGzwdkYJIvNXAkrZsA4YpW8jXteoeqomOnHHABuVvWg== X-Received: by 2002:a17:907:9408:b0:ade:316e:bfc with SMTP id a640c23a62f3a-ae34fdbb622mr1612568666b.21.1751364358156; Tue, 01 Jul 2025 03:05:58 -0700 (PDT) Received: from maria-HP-Pavilion-Laptop.home ([178.226.54.132]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae35365a15fsm850718066b.55.2025.07.01.03.05.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 03:05:57 -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 1/2] dt-bindings: gpio: pca95xx: add TI TCA6418 Date: Tue, 1 Jul 2025 12:05:36 +0200 Message-ID: <20250701100537.91491-2-mariagarcia7293@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250701100537.91491-1-mariagarcia7293@gmail.com> References: <20250701100537.91491-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 06:47:47 2025 Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) (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 ED680273D80; Tue, 1 Jul 2025 10:06:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751364367; cv=none; b=o418g5JllvuS0QCsjjTMSQYRQoHCQUCx6ZuLyQx7SGHeV6foKWNnKdy3P0wSJlRAypYlujn/NV95YfoALA7PX6lpQ+oyBlmFTqwEAWnyVGH74wNmJ76DqkaMjF2u1T/BzqW8i0aIOPHlunPymQryyaxEWKGHIBgN4iFYXApi4j0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751364367; c=relaxed/simple; bh=ZYFvov32w/RwwkOl2V2//fwxVqEHW1aTyCc5DkqCW7A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZHCWnmYOKGXU/8fhsgYkWNpwusZUCDswMNM+20ADqlcbjoxTRON3KgaTjh0Ubhni2Z1qmTUy8Quk+E1tH+zdBOhYyQKQwGHK/FyoiIefhPSZzT2YttcM9xDAVeuqQ3dgqYMf2T+t9yn0glQBrZ4Od595ZmeRkX/E5PB8lbdBYkE= 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=ZTEBOCOy; arc=none smtp.client-ip=209.85.218.54 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="ZTEBOCOy" Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-ae0d758c3a2so895428966b.2; Tue, 01 Jul 2025 03:06:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751364363; x=1751969163; 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=1w844XWi/oRXxr4+VYqmlGsqI1yB3nb2NUKPWI+uJdA=; b=ZTEBOCOyR/OGDtR3h1GO3gDRtw5fXQrnRZlZzuE0kYhvsMGVyBbBKAqJobIpKKbS3n YwMRSZzfUxVlC2OJdm2+fcPuSCsf3/Td0LAoPzWt1jN35QkdtKamVWbFjbEEyIHqVMdW j9kG4sqGbnPsXry6+x/fr6U4DHxN4QX2RFNDM+I/tl843e08RhpSzWZneA1fv8zkawwc +veXKX1+8ytLKWjOipowkkWafkYQscz1ybaYGiL7UHgrrRI3NhUeM5SB4dnyiJHL2SJ1 XoGdWaIlMaSEPI+BMmAyMlZsodZiJ8XJFk+jpivdcx1Oyd9U6QUmw0mcmk0JFsPKWrfo UMWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751364363; x=1751969163; 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=1w844XWi/oRXxr4+VYqmlGsqI1yB3nb2NUKPWI+uJdA=; b=c03oAycIOZVt2bVpLhrOihF2/DZk822hkPHntAJrHUhJWlNwAIFVPBgfb+vJVoOK8M WwM8m8plTs+Mnj1yOxxm6Gy5i6ign/5zE/CakGpSG2HORKKD6EnrsIoz3SZgZhZWpiL+ 6G4+cQ/bSuz7bwcHpndf1XrEu3KIol7WMp8g2eHadhJ2W3v4KOn6phF3pYElfsZswX57 e6dLGdSOcZPtHPPNED/JKboAGgykTbIWx0Gg/X0w6kvkQ3lOxXwKrgV9wZj2646+XZTE u4SeKRQVNYorKHkj09pL5mhJ2MC2CL6yJT7jk6oVmVh5E4OXXFOn3PLc2UF0QOlFXdlZ Tf8Q== X-Forwarded-Encrypted: i=1; AJvYcCU73BhJrh0Q9pXvW6HSCkdjBf2L3r9nXSattjYF5dddtVibijcXyPxDAm1gyDZ1D1if9MxsQl++fCWPffEf@vger.kernel.org, AJvYcCWqeEaZ7osufG4ass0xVwfKU6X4q8SApEqMBbYoq3vmHjd1KvHB7XuUXDdl53YYSrnQAY45G3NOdGgl@vger.kernel.org X-Gm-Message-State: AOJu0Yw7m0AFqPoAsMzxQ8NT01+BhmmfpzOp1/hb9xLWPOEZ+aN1krXf YTy0+tdI43g9xIc6BCCl7xfteuxW298/9nN2GNMOz994PKRDyhf7jrpmtN4pwW8/KxA= X-Gm-Gg: ASbGncv7x3CRjOp6FdLbipiHDmO8h0G1W8X2touE6p0HMPdxi+ebzulKuaQu9GtfZ+f VR+acqpr8X9INYPPNsGR8sMiWQLtl53h2sednd/M+p6GfFKlvbn78M2zGtnO9+tp/I2jiBlVxKg hQAixnzRF3Wx3gn/apw+ZuFKlVAM4vFdhBjC7Mehcxggo/QQ1nLW80MjhOG6moi4c07Ws6H3K1d 4fDFymk09UpS/0qBA73hhyyDPlSfCEJTMaDW5/1fJi6+XStj/TUFmH3/xiXnlhEa7X3hnuHs7ov VoALbpWqJgZQXmZfRPuZH+a661yFphZNrHFcoAfYyDNjJS3fgDOS6YFkEm97dsF6nakQ1n9/pQz mcrmyHNTYVfmZyeG0bg== X-Google-Smtp-Source: AGHT+IFsF0QUebAxLKpuw2JZpI8ZroLZ21rhF3qBW9ke62FO/xxDYjP4CWNOeeX7SeX30tu/hqkdaw== X-Received: by 2002:a17:907:3e0e:b0:ad2:46b2:78b2 with SMTP id a640c23a62f3a-ae34fd892bdmr1766028466b.18.1751364362605; Tue, 01 Jul 2025 03:06:02 -0700 (PDT) Received: from maria-HP-Pavilion-Laptop.home ([178.226.54.132]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae35365a15fsm850718066b.55.2025.07.01.03.06.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 03:06:02 -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 2/2] gpio: pca953x: Add support for TI TCA6418 Date: Tue, 1 Jul 2025 12:05:37 +0200 Message-ID: <20250701100537.91491-3-mariagarcia7293@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250701100537.91491-1-mariagarcia7293@gmail.com> References: <20250701100537.91491-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 | 139 +++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 19 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e80a96f39788..7d77d43ef314 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,20 @@ 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 +380,41 @@ 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 +431,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 +457,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 +552,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 +602,12 @@ 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 +617,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 +627,11 @@ 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 +639,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 +656,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 +667,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,9 +676,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; - return GPIO_LINE_DIRECTION_OUT; } =20 @@ -1117,12 +1201,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); @@ -1154,12 +1248,18 @@ static int pca953x_probe(struct i2c_client *client) /* 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 +1425,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), }, --=20 2.43.0