From nobody Fri Dec 19 11:15:21 2025 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (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 0BF3C1862F for ; Tue, 19 Dec 2023 12:53:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=9elements.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=9elements.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=9elements.com header.i=@9elements.com header.b="DuaD4fs8" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-336746a545fso295480f8f.0 for ; Tue, 19 Dec 2023 04:53:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1702990436; x=1703595236; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=90AFsETFiBQ92T8syUg6t+iVSilY7CpwzvHCJrI5YkQ=; b=DuaD4fs8BwACPBEPRbrWtTy4Rrtlv3FD3KiKtaiVCs+sRV65zzN7YhoX3H3swRaiwy aR69gJQ57lXvV6XdpMWvZCGijXRCuC93/ZaJsjZHUyrp9wBr1JQLPU2Q/a7TizjXV8Iz QhbN+DCjx3EIXU5MbSIg4KcqaJB4NAFMEmSBUQ+WyD942llAPmJ2InlHVD1ByR8pKP+s sE5SF710KiP7obiuRdjkB3VfPsG+JqwBO6DQJ4+o6x8GiIyc5j98RgCmh7hj1LeVduHt IEr/36+QO92pW+q1JrtKzPcNzNF9jJgO+woRgconcaIufSGLigG8zZXvX0iW41aptbk9 E93A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702990436; x=1703595236; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=90AFsETFiBQ92T8syUg6t+iVSilY7CpwzvHCJrI5YkQ=; b=X2uNY1B707AEDFa4v0h9+8Zp3Ros0EozXC37CtdtuHjPjrbwXIGcoQbV4LTURJH4cv qYisCDvu+2CYsSsdXQVPGDyerS9jetcJB9dSFNoc4dGMUkV96Sx9T9YEOnl7vPxkFMC3 g2T41fmEmQI+Ioc5/3aGXxzAec7D5j7tcZHWPUqql2szhN0f7mp0uNlNB9bF82b71aaW wnFm2tbOQCf+t6P8BDPpTkGZx0OIPiLNNYNNkhbBb0pixeTSuA7LLh94VORIsIeJE1Ft LRsYuDdxAkT4EAU6PZYMVMi/5PqWlwuqBi8fiRIjFQiCbJsjZn9bgfI4iLBnTD1G5gzh vfvA== X-Gm-Message-State: AOJu0YzECGnnxWO2dqh0pjPpihCncaxprAwQy5/Om1J7iNzHPjD4oC3I NT8xRPdq1lKfh7lCnwbITQXZA5S0ap04RlSXGMOmXA== X-Google-Smtp-Source: AGHT+IEdvQI32kDLw0uxcIXTpO/AeGkyUZW5M4jnD/fWwXY5ZR/gPwe05/DArwErdv5my8ooD3fx4g== X-Received: by 2002:a5d:5444:0:b0:336:578d:1597 with SMTP id w4-20020a5d5444000000b00336578d1597mr437849wrv.16.1702990436168; Tue, 19 Dec 2023 04:53:56 -0800 (PST) Received: from fedora.sec.9e.network (ip-037-049-067-221.um09.pools.vodafone-ip.de. [37.49.67.221]) by smtp.gmail.com with ESMTPSA id b3-20020adff903000000b003366aad3564sm4376929wrr.30.2023.12.19.04.53.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 19 Dec 2023 04:53:55 -0800 (PST) From: Patrick Rudolph To: Patrick Rudolph , Linus Walleij Cc: naresh.solanki@9elements.com, linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] pinctrl: cy8c95x0: Cache muxed registers Date: Tue, 19 Dec 2023 13:53:49 +0100 Message-ID: <20231219125350.4031370-1-patrick.rudolph@9elements.com> X-Mailer: git-send-email 2.43.0 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" Currently the port specific registers behind the PORTSEL mux aren't cached in the regmap and thus the typical setup time for a single pin on cy8c9560 is about 200msec on our system. The hotspot is the IRQ (un)masking, which causes lots of R/W operations. Introduce a separate regmap for muxed registers and helper functions to use the newly introduced regmap for muxed register access under the i2c lock. With the new cache in place the typical pin setup time is reduced to 20msec, making it about 10 times faster. As a side effect the system boot time is also reduced by 50%. Signed-off-by: Patrick Rudolph --- drivers/pinctrl/pinctrl-cy8c95x0.c | 458 ++++++++++++++++++++--------- 1 file changed, 315 insertions(+), 143 deletions(-) diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-c= y8c95x0.c index 4ccfa99ed93a..6992f522c422 100644 --- a/drivers/pinctrl/pinctrl-cy8c95x0.c +++ b/drivers/pinctrl/pinctrl-cy8c95x0.c @@ -58,6 +58,10 @@ =20 #define CY8C95X0_PIN_TO_OFFSET(x) (((x) >=3D 20) ? ((x) + 4) : (x)) =20 +#define CY8C95X0_MUX_REGMAP_TO_PORT(x) ((x) / MUXED_STRIDE) +#define CY8C95X0_MUX_REGMAP_TO_REG(x) (((x) % MUXED_STRIDE) + CY8C95X0_INT= MASK) +#define CY8C95X0_MUX_REGMAP_TO_OFFSET(x, p) ((x) - CY8C95X0_INTMASK + (p) = * MUXED_STRIDE) + static const struct i2c_device_id cy8c95x0_id[] =3D { { "cy8c9520", 20, }, { "cy8c9540", 40, }, @@ -119,12 +123,13 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_i= rq_info[] =3D { #define MAX_BANK 8 #define BANK_SZ 8 #define MAX_LINE (MAX_BANK * BANK_SZ) - +#define MUXED_STRIDE 16 #define CY8C95X0_GPIO_MASK GENMASK(7, 0) =20 /** * struct cy8c95x0_pinctrl - driver data - * @regmap: Device's regmap + * @regmap: Device's regmap. Only direct access registers. + * @muxed_regmap: Regmap for all muxed registers. * @irq_lock: IRQ bus lock * @i2c_lock: Mutex for the device internal mux register * @irq_mask: I/O bits affected by interrupts @@ -147,6 +152,7 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq= _info[] =3D { */ struct cy8c95x0_pinctrl { struct regmap *regmap; + struct regmap *muxed_regmap; struct mutex irq_lock; struct mutex i2c_lock; DECLARE_BITMAP(irq_mask, MAX_LINE); @@ -376,6 +382,54 @@ static bool cy8c95x0_precious_register(struct device *= dev, unsigned int reg) } } =20 +static bool cy8c95x0_muxed_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_INTMASK: + case CY8C95X0_PWMSEL: + case CY8C95X0_INVERT: + case CY8C95X0_DIRECTION: + case CY8C95X0_DRV_PU: + case CY8C95X0_DRV_PD: + case CY8C95X0_DRV_ODH: + case CY8C95X0_DRV_ODL: + case CY8C95X0_DRV_PP_FAST: + case CY8C95X0_DRV_PP_SLOW: + case CY8C95X0_DRV_HIZ: + return true; + default: + return false; + } +} + +static bool cy8c95x0_wc_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_DRV_PU: + case CY8C95X0_DRV_PD: + case CY8C95X0_DRV_ODH: + case CY8C95X0_DRV_ODL: + case CY8C95X0_DRV_PP_FAST: + case CY8C95X0_DRV_PP_SLOW: + case CY8C95X0_DRV_HIZ: + return true; + default: + return false; + } +} + +static bool cy8c95x0_quick_path_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_INPUT_(0) ... CY8C95X0_INPUT_(7): + case CY8C95X0_INTSTATUS_(0) ... CY8C95X0_INTSTATUS_(7): + case CY8C95X0_OUTPUT_(0) ... CY8C95X0_OUTPUT_(7): + return true; + default: + return false; + } +} + static const struct reg_default cy8c95x0_reg_defaults[] =3D { { CY8C95X0_OUTPUT_(0), GENMASK(7, 0) }, { CY8C95X0_OUTPUT_(1), GENMASK(7, 0) }, @@ -389,7 +443,89 @@ static const struct reg_default cy8c95x0_reg_defaults[= ] =3D { { CY8C95X0_PWMSEL, 0 }, }; =20 +static int +cy8c95x0_mux_reg_read(void *context, unsigned int off, unsigned int *val) +{ + struct cy8c95x0_pinctrl *chip =3D context; + u8 port =3D CY8C95X0_MUX_REGMAP_TO_PORT(off); + int ret, reg =3D CY8C95X0_MUX_REGMAP_TO_REG(off); + + mutex_lock(&chip->i2c_lock); + /* Select the correct bank */ + ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); + if (ret < 0) + goto out; + + /* + * Read the register through direct access regmap. The target range + * is marked volatile. + */ + ret =3D regmap_read(chip->regmap, reg, val); +out: + mutex_unlock(&chip->i2c_lock); + + return ret; +} + +static int +cy8c95x0_mux_reg_write(void *context, unsigned int off, unsigned int val) +{ + struct cy8c95x0_pinctrl *chip =3D context; + u8 port =3D CY8C95X0_MUX_REGMAP_TO_PORT(off); + int ret, reg =3D CY8C95X0_MUX_REGMAP_TO_REG(off); + + mutex_lock(&chip->i2c_lock); + /* Select the correct bank */ + ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); + if (ret < 0) + goto out; + + /* + * Write the register through direct access regmap. The target range + * is marked volatile. + */ + ret =3D regmap_write(chip->regmap, reg, val); +out: + mutex_unlock(&chip->i2c_lock); + + return ret; +} + +static bool cy8c95x0_mux_accessible_register(struct device *dev, unsigned = int off) +{ + struct i2c_client *i2c =3D to_i2c_client(dev); + struct cy8c95x0_pinctrl *chip =3D i2c_get_clientdata(i2c); + u8 port =3D CY8C95X0_MUX_REGMAP_TO_PORT(off); + u8 reg =3D CY8C95X0_MUX_REGMAP_TO_REG(off); + + if (port >=3D chip->nport) + return false; + + return cy8c95x0_muxed_register(reg); +} + +static struct regmap_bus cy8c95x0_regmap_bus =3D { + .reg_read =3D cy8c95x0_mux_reg_read, + .reg_write =3D cy8c95x0_mux_reg_write, +}; + +/* Regmap for muxed registers CY8C95X0_INTMASK - CY8C95X0_DRV_HIZ */ +static const struct regmap_config cy8c95x0_muxed_regmap =3D { + .name =3D "muxed", + .reg_bits =3D 8, + .val_bits =3D 8, + .cache_type =3D REGCACHE_FLAT, + .use_single_read =3D true, + .use_single_write =3D true, + .max_register =3D MUXED_STRIDE * BANK_SZ, + .num_reg_defaults_raw =3D MUXED_STRIDE * BANK_SZ, + .readable_reg =3D cy8c95x0_mux_accessible_register, + .writeable_reg =3D cy8c95x0_mux_accessible_register, +}; + +/* Direct access regmap */ static const struct regmap_config cy8c95x0_i2c_regmap =3D { + .name =3D "direct", .reg_bits =3D 8, .val_bits =3D 8, =20 @@ -405,6 +541,147 @@ static const struct regmap_config cy8c95x0_i2c_regmap= =3D { .max_register =3D CY8C95X0_COMMAND, }; =20 +static inline int cy8c95x0_regmap_update_bits_base(struct cy8c95x0_pinctrl= *chip, + unsigned int reg, + unsigned int port, + unsigned int mask, + unsigned int val, + bool *change, bool async, + bool force) +{ + struct regmap *regmap; + int ret, off, i, read_val; + + /* Caller should never modify PORTSEL directly */ + if (reg =3D=3D CY8C95X0_PORTSEL) + return -EINVAL; + + /* Registers behind the PORTSEL mux have their own regmap */ + if (cy8c95x0_muxed_register(reg)) { + regmap =3D chip->muxed_regmap; + off =3D CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port); + } else { + regmap =3D chip->regmap; + /* Quick path direct access registers honor the port argument */ + if (cy8c95x0_quick_path_register(reg)) + off =3D reg + port; + else + off =3D reg; + } + + ret =3D regmap_update_bits_base(regmap, off, mask, val, change, async, fo= rce); + if (ret < 0) + return ret; + + /* Update the cache when a WC bit is written */ + if (cy8c95x0_wc_register(reg) && (mask & val)) { + for (i =3D CY8C95X0_DRV_PU; i <=3D CY8C95X0_DRV_HIZ; i++) { + if (i =3D=3D reg) + continue; + off =3D CY8C95X0_MUX_REGMAP_TO_OFFSET(i, port); + + ret =3D regmap_read(regmap, off, &read_val); + if (ret < 0) + continue; + + if (!(read_val & mask & val)) + continue; + + regcache_cache_only(regmap, true); + regmap_update_bits(regmap, off, mask & val, 0); + regcache_cache_only(regmap, false); + } + } + + return ret; +} + +/** + * cy8c95x0_regmap_write_bits() - writes a register using the regmap cache + * @chip: The pinctrl to work on + * @reg: The register to write to. Can be direct access or muxed register. + * MUST NOT be the PORTSEL register. + * @port: The port to be used for muxed registers or quick path direct acc= ess + * registers. Otherwise unused. + * @mask: Bitmask to change + * @val: New value for bitmask + * + * This function handles the register writes to the direct access register= s and + * the muxed registers while caching all register accesses, internally han= dling + * the correct state of the PORTSEL register and protecting the access to = muxed + * registers. + * The caller must only use this function to change registers behind the P= ORTSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_write_bits(struct cy8c95x0_pinctrl *chip, unsig= ned int reg, + unsigned int port, unsigned int mask, unsigned int val) +{ + return cy8c95x0_regmap_update_bits_base(chip, reg, port, mask, val, NULL,= false, true); +} + +/** + * cy8c95x0_regmap_update_bits() - updates a register using the regmap cac= he + * @chip: The pinctrl to work on + * @reg: The register to write to. Can be direct access or muxed register. + * MUST NOT be the PORTSEL register. + * @port: The port to be used for muxed registers or quick path direct acc= ess + * registers. Otherwise unused. + * @mask: Bitmask to change + * @val: New value for bitmask + * + * This function handles the register updates to the direct access registe= rs and + * the muxed registers while caching all register accesses, internally han= dling + * the correct state of the PORTSEL register and protecting the access to = muxed + * registers. + * The caller must only use this function to change registers behind the P= ORTSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_update_bits(struct cy8c95x0_pinctrl *chip, unsi= gned int reg, + unsigned int port, unsigned int mask, unsigned int val) +{ + return cy8c95x0_regmap_update_bits_base(chip, reg, port, mask, val, NULL,= false, false); +} + +/** + * cy8c95x0_regmap_read() - reads a register using the regmap cache + * @chip: The pinctrl to work on + * @reg: The register to read from. Can be direct access or muxed register. + * @port: The port to be used for muxed registers or quick path direct acc= ess + * registers. Otherwise unused. + * @read_val: Value read from hardware or cache + * + * This function handles the register reads from the direct access registe= rs and + * the muxed registers while caching all register accesses, internally han= dling + * the correct state of the PORTSEL register and protecting the access to = muxed + * registers. + * The caller must only use this function to read registers behind the POR= TSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_read(struct cy8c95x0_pinctrl *chip, unsigned in= t reg, + unsigned int port, unsigned int *read_val) +{ + struct regmap *regmap; + int off; + + /* Registers behind the PORTSEL mux have their own regmap */ + if (cy8c95x0_muxed_register(reg)) { + regmap =3D chip->muxed_regmap; + off =3D CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port); + } else { + regmap =3D chip->regmap; + /* Quick path direct access registers honor the port argument */ + if (cy8c95x0_quick_path_register(reg)) + off =3D reg + port; + else + off =3D reg; + } + + return regmap_read(regmap, off, read_val); +} + static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, unsigned long *val, unsigned long *mask) { @@ -412,7 +689,7 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pin= ctrl *chip, int reg, DECLARE_BITMAP(tval, MAX_LINE); int write_val; int ret =3D 0; - int i, off =3D 0; + int i; u8 bits; =20 /* Add the 4 bit gap of Gport2 */ @@ -424,53 +701,22 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_p= inctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); =20 - mutex_lock(&chip->i2c_lock); for (i =3D 0; i < chip->nport; i++) { /* Skip over unused banks */ bits =3D bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) continue; =20 - switch (reg) { - /* Muxed registers */ - case CY8C95X0_INTMASK: - case CY8C95X0_PWMSEL: - case CY8C95X0_INVERT: - case CY8C95X0_DIRECTION: - case CY8C95X0_DRV_PU: - case CY8C95X0_DRV_PD: - case CY8C95X0_DRV_ODH: - case CY8C95X0_DRV_ODL: - case CY8C95X0_DRV_PP_FAST: - case CY8C95X0_DRV_PP_SLOW: - case CY8C95X0_DRV_HIZ: - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, i); - if (ret < 0) - goto out; - off =3D reg; - break; - /* Direct access registers */ - case CY8C95X0_INPUT: - case CY8C95X0_OUTPUT: - case CY8C95X0_INTSTATUS: - off =3D reg + i; - break; - default: - ret =3D -EINVAL; - goto out; - } - write_val =3D bitmap_get_value8(tval, i * BANK_SZ); =20 - ret =3D regmap_update_bits(chip->regmap, off, bits, write_val); + ret =3D cy8c95x0_regmap_update_bits(chip, reg, i, bits, write_val); if (ret < 0) goto out; } out: - mutex_unlock(&chip->i2c_lock); =20 if (ret < 0) - dev_err(chip->dev, "failed writing register %d: err %d\n", off, ret); + dev_err(chip->dev, "failed writing register %d, port %d: err %d\n", reg,= i, ret); =20 return ret; } @@ -483,7 +729,7 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinc= trl *chip, int reg, DECLARE_BITMAP(tmp, MAX_LINE); int read_val; int ret =3D 0; - int i, off =3D 0; + int i; u8 bits; =20 /* Add the 4 bit gap of Gport2 */ @@ -495,43 +741,13 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pi= nctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); =20 - mutex_lock(&chip->i2c_lock); for (i =3D 0; i < chip->nport; i++) { /* Skip over unused banks */ bits =3D bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) continue; =20 - switch (reg) { - /* Muxed registers */ - case CY8C95X0_INTMASK: - case CY8C95X0_PWMSEL: - case CY8C95X0_INVERT: - case CY8C95X0_DIRECTION: - case CY8C95X0_DRV_PU: - case CY8C95X0_DRV_PD: - case CY8C95X0_DRV_ODH: - case CY8C95X0_DRV_ODL: - case CY8C95X0_DRV_PP_FAST: - case CY8C95X0_DRV_PP_SLOW: - case CY8C95X0_DRV_HIZ: - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, i); - if (ret < 0) - goto out; - off =3D reg; - break; - /* Direct access registers */ - case CY8C95X0_INPUT: - case CY8C95X0_OUTPUT: - case CY8C95X0_INTSTATUS: - off =3D reg + i; - break; - default: - ret =3D -EINVAL; - goto out; - } - - ret =3D regmap_read(chip->regmap, off, &read_val); + ret =3D cy8c95x0_regmap_read(chip, reg, i, &read_val); if (ret < 0) goto out; =20 @@ -545,10 +761,8 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pin= ctrl *chip, int reg, bitmap_replace(val, tmp, tval, chip->shiftmask, MAX_LINE); =20 out: - mutex_unlock(&chip->i2c_lock); - if (ret < 0) - dev_err(chip->dev, "failed reading register %d: err %d\n", off, ret); + dev_err(chip->dev, "failed reading register %d, port %d: err %d\n", reg,= i, ret); =20 return ret; } @@ -563,12 +777,11 @@ static int cy8c95x0_gpio_direction_output(struct gpio= _chip *gc, { struct cy8c95x0_pinctrl *chip =3D gpiochip_get_data(gc); u8 port =3D cypress_get_port(chip, off); - u8 outreg =3D CY8C95X0_OUTPUT_(port); u8 bit =3D cypress_get_pin_mask(chip, off); int ret; =20 /* Set output level */ - ret =3D regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + ret =3D cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val = ? bit : 0); if (ret) return ret; =20 @@ -578,12 +791,12 @@ static int cy8c95x0_gpio_direction_output(struct gpio= _chip *gc, static int cy8c95x0_gpio_get_value(struct gpio_chip *gc, unsigned int off) { struct cy8c95x0_pinctrl *chip =3D gpiochip_get_data(gc); - u8 inreg =3D CY8C95X0_INPUT_(cypress_get_port(chip, off)); + u8 port =3D cypress_get_port(chip, off); u8 bit =3D cypress_get_pin_mask(chip, off); u32 reg_val; int ret; =20 - ret =3D regmap_read(chip->regmap, inreg, ®_val); + ret =3D cy8c95x0_regmap_read(chip, CY8C95X0_INPUT, port, ®_val); if (ret < 0) { /* * NOTE: @@ -601,10 +814,10 @@ static void cy8c95x0_gpio_set_value(struct gpio_chip = *gc, unsigned int off, int val) { struct cy8c95x0_pinctrl *chip =3D gpiochip_get_data(gc); - u8 outreg =3D CY8C95X0_OUTPUT_(cypress_get_port(chip, off)); + u8 port =3D cypress_get_port(chip, off); u8 bit =3D cypress_get_pin_mask(chip, off); =20 - regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val ? bit : = 0); } =20 static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int = off) @@ -615,24 +828,15 @@ static int cy8c95x0_gpio_get_direction(struct gpio_ch= ip *gc, unsigned int off) u32 reg_val; int ret; =20 - mutex_lock(&chip->i2c_lock); - - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - - ret =3D regmap_read(chip->regmap, CY8C95X0_DIRECTION, ®_val); + ret =3D cy8c95x0_regmap_read(chip, CY8C95X0_DIRECTION, port, ®_val); if (ret < 0) goto out; =20 - mutex_unlock(&chip->i2c_lock); - if (reg_val & bit) return GPIO_LINE_DIRECTION_IN; =20 return GPIO_LINE_DIRECTION_OUT; out: - mutex_unlock(&chip->i2c_lock); return ret; } =20 @@ -648,13 +852,6 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pi= nctrl *chip, u16 arg =3D 0; int ret; =20 - mutex_lock(&chip->i2c_lock); - - /* Select port */ - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - switch (param) { case PIN_CONFIG_BIAS_PULL_UP: reg =3D CY8C95X0_DRV_PU; @@ -681,7 +878,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pin= ctrl *chip, reg =3D CY8C95X0_PWMSEL; break; case PIN_CONFIG_OUTPUT: - reg =3D CY8C95X0_OUTPUT_(port); + reg =3D CY8C95X0_OUTPUT; break; case PIN_CONFIG_OUTPUT_ENABLE: reg =3D CY8C95X0_DIRECTION; @@ -709,14 +906,15 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_p= inctrl *chip, * Writing 1 to one of the drive mode registers will automatically * clear conflicting set bits in the other drive mode registers. */ - ret =3D regmap_read(chip->regmap, reg, ®_val); + ret =3D cy8c95x0_regmap_read(chip, reg, port, ®_val); + if (ret < 0) + goto out; + if (reg_val & bit) arg =3D 1; =20 *config =3D pinconf_to_config_packed(param, (u16)arg); out: - mutex_unlock(&chip->i2c_lock); - return ret; } =20 @@ -730,13 +928,6 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pi= nctrl *chip, unsigned int reg; int ret; =20 - mutex_lock(&chip->i2c_lock); - - /* Select port */ - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - switch (param) { case PIN_CONFIG_BIAS_PULL_UP: __clear_bit(off, chip->push_pull); @@ -773,10 +964,8 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pi= nctrl *chip, * Writing 1 to one of the drive mode registers will automatically * clear conflicting set bits in the other drive mode registers. */ - ret =3D regmap_write_bits(chip->regmap, reg, bit, bit); - + ret =3D cy8c95x0_regmap_write_bits(chip, reg, port, bit, bit); out: - mutex_unlock(&chip->i2c_lock); return ret; } =20 @@ -1093,14 +1282,8 @@ static int cy8c95x0_set_mode(struct cy8c95x0_pinctrl= *chip, unsigned int off, bo { u8 port =3D cypress_get_port(chip, off); u8 bit =3D cypress_get_pin_mask(chip, off); - int ret; =20 - /* Select port */ - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - return ret; - - return regmap_write_bits(chip->regmap, CY8C95X0_PWMSEL, bit, mode ? bit := 0); + return cy8c95x0_regmap_write_bits(chip, CY8C95X0_PWMSEL, port, bit, mode = ? bit : 0); } =20 static int cy8c95x0_pinmux_mode(struct cy8c95x0_pinctrl *chip, @@ -1118,24 +1301,19 @@ static int cy8c95x0_pinmux_mode(struct cy8c95x0_pin= ctrl *chip, return 0; =20 /* Set direction to output & set output to 1 so that PWM can work */ - ret =3D regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, bit); + ret =3D cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, b= it); if (ret < 0) return ret; =20 - return regmap_write_bits(chip->regmap, CY8C95X0_OUTPUT_(port), bit, bit); + return cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, bit); } =20 static int cy8c95x0_set_mux(struct pinctrl_dev *pctldev, unsigned int sele= ctor, unsigned int group) { struct cy8c95x0_pinctrl *chip =3D pinctrl_dev_get_drvdata(pctldev); - int ret; =20 - mutex_lock(&chip->i2c_lock); - ret =3D cy8c95x0_pinmux_mode(chip, selector, group); - mutex_unlock(&chip->i2c_lock); - - return ret; + return cy8c95x0_pinmux_mode(chip, selector, group); } =20 static int cy8c95x0_gpio_request_enable(struct pinctrl_dev *pctldev, @@ -1143,13 +1321,8 @@ static int cy8c95x0_gpio_request_enable(struct pinct= rl_dev *pctldev, unsigned int pin) { struct cy8c95x0_pinctrl *chip =3D pinctrl_dev_get_drvdata(pctldev); - int ret; - - mutex_lock(&chip->i2c_lock); - ret =3D cy8c95x0_set_mode(chip, pin, false); - mutex_unlock(&chip->i2c_lock); =20 - return ret; + return cy8c95x0_set_mode(chip, pin, false); } =20 static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip, @@ -1159,13 +1332,7 @@ static int cy8c95x0_pinmux_direction(struct cy8c95x0= _pinctrl *chip, u8 bit =3D cypress_get_pin_mask(chip, pin); int ret; =20 - /* Select port... */ - ret =3D regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret) - return ret; - - /* ...then direction */ - ret =3D regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, input ? = bit : 0); + ret =3D cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, i= nput ? bit : 0); if (ret) return ret; =20 @@ -1174,7 +1341,7 @@ static int cy8c95x0_pinmux_direction(struct cy8c95x0_= pinctrl *chip, * the direction register isn't sufficient in Push-Pull mode. */ if (input && test_bit(pin, chip->push_pull)) { - ret =3D regmap_write_bits(chip->regmap, CY8C95X0_DRV_HIZ, bit, bit); + ret =3D cy8c95x0_regmap_write_bits(chip, CY8C95X0_DRV_HIZ, port, bit, bi= t); if (ret) return ret; =20 @@ -1189,13 +1356,8 @@ static int cy8c95x0_gpio_set_direction(struct pinctr= l_dev *pctldev, unsigned int pin, bool input) { struct cy8c95x0_pinctrl *chip =3D pinctrl_dev_get_drvdata(pctldev); - int ret; =20 - mutex_lock(&chip->i2c_lock); - ret =3D cy8c95x0_pinmux_direction(chip, pin, input); - mutex_unlock(&chip->i2c_lock); - - return ret; + return cy8c95x0_pinmux_direction(chip, pin, input); } =20 static const struct pinmux_ops cy8c95x0_pmxops =3D { @@ -1397,12 +1559,22 @@ static int cy8c95x0_probe(struct i2c_client *client) gpiod_set_consumer_name(chip->gpio_reset, "CY8C95X0 RESET"); } =20 + /* Generic regmap for direct access registers */ chip->regmap =3D devm_regmap_init_i2c(client, &cy8c95x0_i2c_regmap); if (IS_ERR(chip->regmap)) { ret =3D PTR_ERR(chip->regmap); goto err_exit; } =20 + /* Port specific regmap behind PORTSEL mux */ + chip->muxed_regmap =3D devm_regmap_init(&client->dev, &cy8c95x0_regmap_bu= s, + chip, &cy8c95x0_muxed_regmap); + if (IS_ERR(chip->muxed_regmap)) { + ret =3D dev_err_probe(&client->dev, PTR_ERR(chip->muxed_regmap), + "Failed to register muxed regmap\n"); + goto err_exit; + } + bitmap_zero(chip->push_pull, MAX_LINE); bitmap_zero(chip->shiftmask, MAX_LINE); bitmap_set(chip->shiftmask, 0, 20); --=20 2.43.0