summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/pinctrl-cy8c95x0.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-01-17 15:55:33 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-01-17 15:55:33 -0800
commited6c23b175471d7bdecd06b5f37a0b1057c90cce (patch)
treeeef4f74156615309453e5a7b4c052adcad68a315 /drivers/pinctrl/pinctrl-cy8c95x0.c
parent5d197e97fb106c09d3d013be341e5961fd70ec8a (diff)
parent1b09c2b8f849079220a9a9ddf961582f00bdc2c4 (diff)
downloadlinux-ed6c23b175471d7bdecd06b5f37a0b1057c90cce.tar.gz
linux-ed6c23b175471d7bdecd06b5f37a0b1057c90cce.tar.bz2
linux-ed6c23b175471d7bdecd06b5f37a0b1057c90cce.zip
Merge tag 'pinctrl-v6.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl
Pull pin control updates from Linus Walleij: "For this kernel cycle I managed an immutable branch for the PEF2256 WAN framer that has some pin control portions. It already landed in your tree through the net pull request but here it is mentioned again. The most interesting is perhaps the Samsung Exynos subdrivers for the Tensor SoC used in Google Pixel 6 and the ExynosAuto subdriver for automotive. Along with the earlier merged Tesla FSD subdriver it shows some of the versatile uses of the Samsung Exynos silicon. It is also used in the latest version of Axis Communications ARTPEC chips so it is a very widely deployed SoC family. We also have the Intel Meteor Lake SoC which I think is for laptops. It's a pretty interesting chip with Xe graphics and integrated PCH. Core changes: - A new PINCTRL_GROUP_DESC() infrastructure macro is added and used in different drivers, generic group description struct group_desc is now used all over the place. New drivers: - New driver for the Texas Instruments TPS6494 Power Management IC. - New driver for the Lantic PEF2256 framer pin multiplexer. This IC has some pins that can be reconfigured in different ways. The actual driver comes on an immutable branch with the net WAN parts, the IC is some latest-and-greatest serial line funnel for e.g. wireless access points. - New subdriver for the Samsung Exynos Auto V920 pin controller, used for automotive applications. - New subdriver for the Samsung "GS101" SoC pin controller, this is the Google "Tensor" SoC used in the Google Pixel 6. - New subdriver for the Intel Meteor Point SoC pin controller. - New subdriver for the Qualcomm SM8650 top level (TLMM) and LPASS pin controllers. - New subdriver for the Qualcomm X1E80100 top level (TLMM) pin controller. - New subdriver for the Qualcomm SM4450 top level (TLMM) pin controller. - The "single" pin controller now supports the Texas Instruments J7200 SoC. Improvements: - Intel has created a new (Intel-)generic pin controller driver that is now used by all contemporary Intel platforms. - Intel is now also making use of some cleanup helpers. - Enble 910 Ohm bias in the Intel Tangier driver. - The Samsung driver now suppors irq_set_affinity() in it's IRQ chip giving support for non wake up external gpio interrupts" * tag 'pinctrl-v6.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (112 commits) pinctrl: samsung: constify iomem pointers pinctrl: cy8c95x0: Cache muxed registers dt-bindings: pinctrl: xilinx: Rename *gpio to *gpio-grp pinctrl: qcom: lpass-lpi: remove duplicated include dt-bindings: pinctrl: qcom: drop common properties and allow wakeup-parent dt-bindings: pinctrl: qcom: drop common properties dt-bindings: pinctrl: qcom,ipq5018-tlmm: use common TLMM bindings dt-bindings: pinctrl: qcom,x1e80100-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom,sm8650-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom,sm8550-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom,sdx75-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom,sa8775p-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom,qdu1000-tlmm: restrict number of interrupts dt-bindings: pinctrl: qcom: create common LPASS LPI schema pinctrl: qcom: sm4450: dd SM4450 pinctrl driver dt-bindings: pinctrl: qcom: Add SM4450 pinctrl dt-bindings: pinctrl: qcom,pmic-mpp: clean up example pinctrl: intel: Add Intel Meteor Point pin controller and GPIO support pinctrl: renesas: rzg2l: Add input enable to the Ethernet pins pinctrl: renesas: rzg2l: Add output enable support ...
Diffstat (limited to 'drivers/pinctrl/pinctrl-cy8c95x0.c')
-rw-r--r--drivers/pinctrl/pinctrl-cy8c95x0.c458
1 files changed, 315 insertions, 143 deletions
diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-cy8c95x0.c
index fe9545c630a2..67b5d160c027 100644
--- a/drivers/pinctrl/pinctrl-cy8c95x0.c
+++ b/drivers/pinctrl/pinctrl-cy8c95x0.c
@@ -58,6 +58,10 @@
#define CY8C95X0_PIN_TO_OFFSET(x) (((x) >= 20) ? ((x) + 4) : (x))
+#define CY8C95X0_MUX_REGMAP_TO_PORT(x) ((x) / MUXED_STRIDE)
+#define CY8C95X0_MUX_REGMAP_TO_REG(x) (((x) % MUXED_STRIDE) + CY8C95X0_INTMASK)
+#define CY8C95X0_MUX_REGMAP_TO_OFFSET(x, p) ((x) - CY8C95X0_INTMASK + (p) * MUXED_STRIDE)
+
static const struct i2c_device_id cy8c95x0_id[] = {
{ "cy8c9520", 20, },
{ "cy8c9540", 40, },
@@ -119,12 +123,13 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq_info[] = {
#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)
/**
* 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[] = {
*/
struct cy8c95x0_pinctrl {
struct regmap *regmap;
+ struct regmap *muxed_regmap;
struct mutex irq_lock;
struct mutex i2c_lock;
DECLARE_BITMAP(irq_mask, MAX_LINE);
@@ -379,6 +385,54 @@ static bool cy8c95x0_precious_register(struct device *dev, unsigned int reg)
}
}
+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[] = {
{ CY8C95X0_OUTPUT_(0), GENMASK(7, 0) },
{ CY8C95X0_OUTPUT_(1), GENMASK(7, 0) },
@@ -392,7 +446,89 @@ static const struct reg_default cy8c95x0_reg_defaults[] = {
{ CY8C95X0_PWMSEL, 0 },
};
+static int
+cy8c95x0_mux_reg_read(void *context, unsigned int off, unsigned int *val)
+{
+ struct cy8c95x0_pinctrl *chip = context;
+ u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off);
+ int ret, reg = CY8C95X0_MUX_REGMAP_TO_REG(off);
+
+ mutex_lock(&chip->i2c_lock);
+ /* Select the correct bank */
+ ret = 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 = 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 = context;
+ u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off);
+ int ret, reg = CY8C95X0_MUX_REGMAP_TO_REG(off);
+
+ mutex_lock(&chip->i2c_lock);
+ /* Select the correct bank */
+ ret = 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 = 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 = to_i2c_client(dev);
+ struct cy8c95x0_pinctrl *chip = i2c_get_clientdata(i2c);
+ u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off);
+ u8 reg = CY8C95X0_MUX_REGMAP_TO_REG(off);
+
+ if (port >= chip->nport)
+ return false;
+
+ return cy8c95x0_muxed_register(reg);
+}
+
+static struct regmap_bus cy8c95x0_regmap_bus = {
+ .reg_read = cy8c95x0_mux_reg_read,
+ .reg_write = cy8c95x0_mux_reg_write,
+};
+
+/* Regmap for muxed registers CY8C95X0_INTMASK - CY8C95X0_DRV_HIZ */
+static const struct regmap_config cy8c95x0_muxed_regmap = {
+ .name = "muxed",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_FLAT,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = MUXED_STRIDE * BANK_SZ,
+ .num_reg_defaults_raw = MUXED_STRIDE * BANK_SZ,
+ .readable_reg = cy8c95x0_mux_accessible_register,
+ .writeable_reg = cy8c95x0_mux_accessible_register,
+};
+
+/* Direct access regmap */
static const struct regmap_config cy8c95x0_i2c_regmap = {
+ .name = "direct",
.reg_bits = 8,
.val_bits = 8,
@@ -408,6 +544,147 @@ static const struct regmap_config cy8c95x0_i2c_regmap = {
.max_register = CY8C95X0_COMMAND,
};
+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 == CY8C95X0_PORTSEL)
+ return -EINVAL;
+
+ /* Registers behind the PORTSEL mux have their own regmap */
+ if (cy8c95x0_muxed_register(reg)) {
+ regmap = chip->muxed_regmap;
+ off = CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port);
+ } else {
+ regmap = chip->regmap;
+ /* Quick path direct access registers honor the port argument */
+ if (cy8c95x0_quick_path_register(reg))
+ off = reg + port;
+ else
+ off = reg;
+ }
+
+ ret = regmap_update_bits_base(regmap, off, mask, val, change, async, force);
+ if (ret < 0)
+ return ret;
+
+ /* Update the cache when a WC bit is written */
+ if (cy8c95x0_wc_register(reg) && (mask & val)) {
+ for (i = CY8C95X0_DRV_PU; i <= CY8C95X0_DRV_HIZ; i++) {
+ if (i == reg)
+ continue;
+ off = CY8C95X0_MUX_REGMAP_TO_OFFSET(i, port);
+
+ ret = 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 access
+ * registers. Otherwise unused.
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * This function handles the register writes to the direct access registers and
+ * the muxed registers while caching all register accesses, internally handling
+ * 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 PORTSEL mux.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int cy8c95x0_regmap_write_bits(struct cy8c95x0_pinctrl *chip, unsigned 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 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 access
+ * registers. Otherwise unused.
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * This function handles the register updates to the direct access registers and
+ * the muxed registers while caching all register accesses, internally handling
+ * 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 PORTSEL mux.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int cy8c95x0_regmap_update_bits(struct cy8c95x0_pinctrl *chip, unsigned 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 access
+ * registers. Otherwise unused.
+ * @read_val: Value read from hardware or cache
+ *
+ * This function handles the register reads from the direct access registers and
+ * the muxed registers while caching all register accesses, internally handling
+ * 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 PORTSEL mux.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int cy8c95x0_regmap_read(struct cy8c95x0_pinctrl *chip, unsigned int 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 = chip->muxed_regmap;
+ off = CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port);
+ } else {
+ regmap = chip->regmap;
+ /* Quick path direct access registers honor the port argument */
+ if (cy8c95x0_quick_path_register(reg))
+ off = reg + port;
+ else
+ off = 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)
{
@@ -415,7 +692,7 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg,
DECLARE_BITMAP(tval, MAX_LINE);
int write_val;
int ret = 0;
- int i, off = 0;
+ int i;
u8 bits;
/* Add the 4 bit gap of Gport2 */
@@ -427,53 +704,22 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg,
bitmap_shift_left(tval, tval, 4, MAX_LINE);
bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3);
- mutex_lock(&chip->i2c_lock);
for (i = 0; i < chip->nport; i++) {
/* Skip over unused banks */
bits = bitmap_get_value8(tmask, i * BANK_SZ);
if (!bits)
continue;
- 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 = regmap_write(chip->regmap, CY8C95X0_PORTSEL, i);
- if (ret < 0)
- goto out;
- off = reg;
- break;
- /* Direct access registers */
- case CY8C95X0_INPUT:
- case CY8C95X0_OUTPUT:
- case CY8C95X0_INTSTATUS:
- off = reg + i;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
write_val = bitmap_get_value8(tval, i * BANK_SZ);
- ret = regmap_update_bits(chip->regmap, off, bits, write_val);
+ ret = cy8c95x0_regmap_update_bits(chip, reg, i, bits, write_val);
if (ret < 0)
goto out;
}
out:
- mutex_unlock(&chip->i2c_lock);
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);
return ret;
}
@@ -486,7 +732,7 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg,
DECLARE_BITMAP(tmp, MAX_LINE);
int read_val;
int ret = 0;
- int i, off = 0;
+ int i;
u8 bits;
/* Add the 4 bit gap of Gport2 */
@@ -498,43 +744,13 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg,
bitmap_shift_left(tval, tval, 4, MAX_LINE);
bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3);
- mutex_lock(&chip->i2c_lock);
for (i = 0; i < chip->nport; i++) {
/* Skip over unused banks */
bits = bitmap_get_value8(tmask, i * BANK_SZ);
if (!bits)
continue;
- 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 = regmap_write(chip->regmap, CY8C95X0_PORTSEL, i);
- if (ret < 0)
- goto out;
- off = reg;
- break;
- /* Direct access registers */
- case CY8C95X0_INPUT:
- case CY8C95X0_OUTPUT:
- case CY8C95X0_INTSTATUS:
- off = reg + i;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- ret = regmap_read(chip->regmap, off, &read_val);
+ ret = cy8c95x0_regmap_read(chip, reg, i, &read_val);
if (ret < 0)
goto out;
@@ -548,10 +764,8 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg,
bitmap_replace(val, tmp, tval, chip->shiftmask, MAX_LINE);
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);
return ret;
}
@@ -566,12 +780,11 @@ static int cy8c95x0_gpio_direction_output(struct gpio_chip *gc,
{
struct cy8c95x0_pinctrl *chip = gpiochip_get_data(gc);
u8 port = cypress_get_port(chip, off);
- u8 outreg = CY8C95X0_OUTPUT_(port);
u8 bit = cypress_get_pin_mask(chip, off);
int ret;
/* Set output level */
- ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
+ ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val ? bit : 0);
if (ret)
return ret;
@@ -581,12 +794,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 = gpiochip_get_data(gc);
- u8 inreg = CY8C95X0_INPUT_(cypress_get_port(chip, off));
+ u8 port = cypress_get_port(chip, off);
u8 bit = cypress_get_pin_mask(chip, off);
u32 reg_val;
int ret;
- ret = regmap_read(chip->regmap, inreg, &reg_val);
+ ret = cy8c95x0_regmap_read(chip, CY8C95X0_INPUT, port, &reg_val);
if (ret < 0) {
/*
* NOTE:
@@ -604,10 +817,10 @@ static void cy8c95x0_gpio_set_value(struct gpio_chip *gc, unsigned int off,
int val)
{
struct cy8c95x0_pinctrl *chip = gpiochip_get_data(gc);
- u8 outreg = CY8C95X0_OUTPUT_(cypress_get_port(chip, off));
+ u8 port = cypress_get_port(chip, off);
u8 bit = cypress_get_pin_mask(chip, off);
- regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
+ cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val ? bit : 0);
}
static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
@@ -618,24 +831,15 @@ static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
u32 reg_val;
int ret;
- mutex_lock(&chip->i2c_lock);
-
- ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port);
- if (ret < 0)
- goto out;
-
- ret = regmap_read(chip->regmap, CY8C95X0_DIRECTION, &reg_val);
+ ret = cy8c95x0_regmap_read(chip, CY8C95X0_DIRECTION, port, &reg_val);
if (ret < 0)
goto out;
- mutex_unlock(&chip->i2c_lock);
-
if (reg_val & bit)
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
out:
- mutex_unlock(&chip->i2c_lock);
return ret;
}
@@ -651,13 +855,6 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip,
u16 arg = 0;
int ret;
- mutex_lock(&chip->i2c_lock);
-
- /* Select port */
- ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port);
- if (ret < 0)
- goto out;
-
switch (param) {
case PIN_CONFIG_BIAS_PULL_UP:
reg = CY8C95X0_DRV_PU;
@@ -684,7 +881,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip,
reg = CY8C95X0_PWMSEL;
break;
case PIN_CONFIG_OUTPUT:
- reg = CY8C95X0_OUTPUT_(port);
+ reg = CY8C95X0_OUTPUT;
break;
case PIN_CONFIG_OUTPUT_ENABLE:
reg = CY8C95X0_DIRECTION;
@@ -712,7 +909,10 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip,
* Writing 1 to one of the drive mode registers will automatically
* clear conflicting set bits in the other drive mode registers.
*/
- ret = regmap_read(chip->regmap, reg, &reg_val);
+ ret = cy8c95x0_regmap_read(chip, reg, port, &reg_val);
+ if (ret < 0)
+ goto out;
+
if (reg_val & bit)
arg = 1;
if (param == PIN_CONFIG_OUTPUT_ENABLE)
@@ -720,8 +920,6 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip,
*config = pinconf_to_config_packed(param, (u16)arg);
out:
- mutex_unlock(&chip->i2c_lock);
-
return ret;
}
@@ -736,13 +934,6 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip,
unsigned int reg;
int ret;
- mutex_lock(&chip->i2c_lock);
-
- /* Select port */
- ret = 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);
@@ -785,10 +976,8 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip,
* Writing 1 to one of the drive mode registers will automatically
* clear conflicting set bits in the other drive mode registers.
*/
- ret = regmap_write_bits(chip->regmap, reg, bit, bit);
-
+ ret = cy8c95x0_regmap_write_bits(chip, reg, port, bit, bit);
out:
- mutex_unlock(&chip->i2c_lock);
return ret;
}
@@ -1105,14 +1294,8 @@ static int cy8c95x0_set_mode(struct cy8c95x0_pinctrl *chip, unsigned int off, bo
{
u8 port = cypress_get_port(chip, off);
u8 bit = cypress_get_pin_mask(chip, off);
- int ret;
- /* Select port */
- ret = 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);
}
static int cy8c95x0_pinmux_mode(struct cy8c95x0_pinctrl *chip,
@@ -1130,24 +1313,19 @@ static int cy8c95x0_pinmux_mode(struct cy8c95x0_pinctrl *chip,
return 0;
/* Set direction to output & set output to 1 so that PWM can work */
- ret = regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, bit);
+ ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, bit);
if (ret < 0)
return ret;
- return regmap_write_bits(chip->regmap, CY8C95X0_OUTPUT_(port), bit, bit);
+ return cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, bit);
}
static int cy8c95x0_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,
unsigned int group)
{
struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev);
- int ret;
- mutex_lock(&chip->i2c_lock);
- ret = cy8c95x0_pinmux_mode(chip, selector, group);
- mutex_unlock(&chip->i2c_lock);
-
- return ret;
+ return cy8c95x0_pinmux_mode(chip, selector, group);
}
static int cy8c95x0_gpio_request_enable(struct pinctrl_dev *pctldev,
@@ -1155,13 +1333,8 @@ static int cy8c95x0_gpio_request_enable(struct pinctrl_dev *pctldev,
unsigned int pin)
{
struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev);
- int ret;
-
- mutex_lock(&chip->i2c_lock);
- ret = cy8c95x0_set_mode(chip, pin, false);
- mutex_unlock(&chip->i2c_lock);
- return ret;
+ return cy8c95x0_set_mode(chip, pin, false);
}
static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip,
@@ -1171,13 +1344,7 @@ static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip,
u8 bit = cypress_get_pin_mask(chip, pin);
int ret;
- /* Select port... */
- ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port);
- if (ret)
- return ret;
-
- /* ...then direction */
- ret = regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, input ? bit : 0);
+ ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, input ? bit : 0);
if (ret)
return ret;
@@ -1186,7 +1353,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 = regmap_write_bits(chip->regmap, CY8C95X0_DRV_HIZ, bit, bit);
+ ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DRV_HIZ, port, bit, bit);
if (ret)
return ret;
@@ -1201,13 +1368,8 @@ static int cy8c95x0_gpio_set_direction(struct pinctrl_dev *pctldev,
unsigned int pin, bool input)
{
struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev);
- int ret;
- mutex_lock(&chip->i2c_lock);
- ret = cy8c95x0_pinmux_direction(chip, pin, input);
- mutex_unlock(&chip->i2c_lock);
-
- return ret;
+ return cy8c95x0_pinmux_direction(chip, pin, input);
}
static const struct pinmux_ops cy8c95x0_pmxops = {
@@ -1409,12 +1571,22 @@ static int cy8c95x0_probe(struct i2c_client *client)
gpiod_set_consumer_name(chip->gpio_reset, "CY8C95X0 RESET");
}
+ /* Generic regmap for direct access registers */
chip->regmap = devm_regmap_init_i2c(client, &cy8c95x0_i2c_regmap);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
goto err_exit;
}
+ /* Port specific regmap behind PORTSEL mux */
+ chip->muxed_regmap = devm_regmap_init(&client->dev, &cy8c95x0_regmap_bus,
+ chip, &cy8c95x0_muxed_regmap);
+ if (IS_ERR(chip->muxed_regmap)) {
+ ret = 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);