Migrate the PCIe-IDIO-24 and WS16C48 GPIO drivers to the regmap API
Subsystems (5)
v5 cover Migrate the PCIe-IDIO-24 and WS16C48 GPIO drivers to the regmap API
Changes in v5:
- Refactor for map parameter removal from handle_mask_sync()
- Cleanups and line wrappings to 100 characters rather than 80
- Adjust to change mutex/spinlock_t type locks to raw_spin_lock_t type
- Remove pex8311_intcsr table configurations as superfluous
- Adjust to set pex8311_intcsr_regmap_config reg_base to PLX_PEX8311_PCI_LCS_INTCSR
- Rename PAGE_FIELD_PAGE_* defines to POL_PAGE, ENAB_PAGE, and INT_ID_PAGE
Changes in v4:
- Allocate idio24gpio before using it in idio_24_probe()
Changes in v3:
- Drop map from set_type_config() parameter list; regmap can be passed by irq_drv_data instead
- Adjust idio_24_set_type_config() for parameter list
- Add mutex to prevent clobbering the COS_ENABLE register when masking IRQ and setting their type configuration
Changes in v2:
- Simplify PCIe-IDIO-24 register offset defines to remove superfluous arithmetic
- Check for NULL pointer after chip->irq_drv_data allocation
- Set gpio_regmap drvdata and use gpio_regmap_get_drvdata() to get the regmap in idio_24_reg_map_xlate()
The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
A patch to pass irq_drv_data as a parameter for struct regmap_irq_chip set_type_config() is included. This is needed by the idio_24_set_type_config() and ws16c48_set_type_config() callbacks in order to update the type configuration on their respective devices.
This patchset depends on the "Drop map from handle_mask_sync() parameters" patchset [0].
[0] https://lore.kernel.org/all/[email protected]/
William Breathitt Gray (3):
regmap: Pass irq_drv_data as a parameter for set_type_config()
gpio: pcie-idio-24: Migrate to the regmap API
gpio: ws16c48: Migrate to the regmap API
drivers/base/regmap/regmap-irq.c | 6 +- drivers/gpio/Kconfig | 6 + drivers/gpio/gpio-pcie-idio-24.c | 677 +++++++++++-------------------- drivers/gpio/gpio-ws16c48.c | 552 +++++++++---------------- include/linux/regmap.h | 5 +- 5 files changed, 445 insertions(+), 801 deletions(-)
base-commit: 2093bcd872321a5301470978231b23cc121e3476
prerequisite-patch-id: cd19046150b7cff1be4ac7152198777aa960a3df
prerequisite-patch-id: bd3e3830d9ce4f3876a77483364d7190b7fdffa7
Signature · 1 line
2.39.2
PATCH v5 1/3 regmap: Pass irq_drv_data as a parameter for set_type_config()
Allow the struct regmap_irq_chip set_type_config() callback to access irq_drv_data by passing it as a parameter.
---
Changes in v5:
- Wrap lines to 100 characters rather than 80
Changes in v4: none
Changes in v3:
- Drop map from set_type_config() parameter list; regmap can be passed by irq_drv_data instead
Changes in v2: none
drivers/base/regmap/regmap-irq.c | 6 ++++-- include/linux/regmap.h | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-)
drivers/base/regmap/regmap-irq.c +4 -2
@@ -329,7 +329,7 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)if (d->chip->set_type_config) { ret = d->chip->set_type_config (d->config_buf, type, - irq_data, reg); + irq_data, reg, d->chip->irq_drv_data);if (ret)return ret; }
@@ -653,13 +653,15 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);* @type: The requested IRQ type. * @irq_data: The IRQ being configured. * @idx: Index of the irq's config registers within each array `buf[i]` +* @irq_drv_data: Driver specific IRQ data * * This is a &struct regmap_irq_chip->set_type_config callback suitable for * chips with one config register. Register values are updated according to * the &struct regmap_irq_type data associated with an IRQ. */ int regmap_irq_set_type_config_simple (unsigned int **buf,unsigned int type, -const struct regmap_irq *irq_data,int idx) +const struct regmap_irq *irq_data,int idx, +void *irq_drv_data) {const struct regmap_irq_type *t = &irq_data->type;
include/linux/regmap.h +3 -2
@@ -1651,7 +1651,7 @@ struct regmap_irq_chip {int (*set_type_virt)(unsigned int **buf,unsigned int type,unsigned long hwirq,int reg);int (*set_type_config)(unsigned int **buf,unsigned int type, -const struct regmap_irq *irq_data,int idx); +const struct regmap_irq *irq_data,int idx,void *irq_drv_data);unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,unsigned int base,int index);void *irq_drv_data;
@@ -1660,7 +1660,8 @@ struct regmap_irq_chip {unsigned int regmap_irq_get_irq_reg_linear (struct regmap_irq_chip_data *data,unsigned int base,int index);int regmap_irq_set_type_config_simple (unsigned int **buf,unsigned int type, -const struct regmap_irq *irq_data,int idx); +const struct regmap_irq *irq_data,int idx, +void *irq_drv_data); int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int irq_base, const struct regmap_irq_chip *chip,
Signature · 1 line
2.39.2
Mark Brown
maintainerRe: [PATCH v5 1/3] regmap: Pass irq_drv_data as a parameter for set_type_config()
Quoted 2 lines · William Breathitt Gray
Changes in v5:
- Wrap lines to 100 characters rather than 80
It's good to keep things under 80 where we can.
include/linux/regmap.h struct regmap_irq_chip { · line 1657
Quoted 6 lines
include/linux/regmap.h +1
-1int (*set_type_config)(unsigned int **buf,unsigned int type, -const struct regmap_irq *irq_data,int idx); +const struct regmap_irq *irq_data,int idx,void *irq_drv_data);unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,unsigned int base,int index);void *irq_drv_data;
There's no benefit from overflowing here and...
include/linux/regmap.h struct regmap_irq_chip { · line 1664
Quoted 7 lines
include/linux/regmap.h +2
-1@@ -1660,7 +1660,8 @@ struct regmap_irq_chip {unsigned int regmap_irq_get_irq_reg_linear (struct regmap_irq_chip_data *data,unsigned int base,int index);int regmap_irq_set_type_config_simple (unsigned int **buf,unsigned int type, -const struct regmap_irq *irq_data,int idx); +const struct regmap_irq *irq_data,int idx, +void *irq_drv_data);
...it's not even consistent.
PATCH v5 2/3 gpio: pcie-idio-24: Migrate to the regmap API
In reply to William Breathitt GrayThe regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
For the PCIe-IDIO-24 series of devices, the following BARs are available:
BAR[0]: memory mapped PEX8311
BAR[1]: I/O mapped PEX8311
BAR[2]: I/O mapped card registersThere are 24 FET Output lines, 24 Isolated Input lines, and 8 TTL/CMOS lines (which may be configured for either output or input). The GPIO lines are exposed by the following card registers:
Base +0x0-0x2 (Read/Write): FET Outputs
Base +0xB (Read/Write): TTL/CMOS
Base +0x4-0x6 (Read): Isolated Inputs
Base +0x7 (Read): TTL/CMOSIn order for the device to support interrupts, the PLX PEX8311 internal PCI wire interrupt and local interrupt input must first be enabled.
The following card registers for Change-Of-State may be used:
Base +0x8-0xA (Read): COS Status Inputs
Base +0x8-0xA (Write): COS Clear Inputs
Base +0xB (Read): COS Status TTL/CMOS
Base +0xB (Write): COS Clear TTL/CMOS
Base +0xE (Read/Write): COS EnableThe COS Enable register is used to enable/disable interrupts and configure the interrupt levels; each bit maps to a group of eight inputs as described below:
Bit 0: IRQ EN Rising Edge IN0-7
Bit 1: IRQ EN Rising Edge IN8-15
Bit 2: IRQ EN Rising Edge IN16-23
Bit 3: IRQ EN Rising Edge TTL0-7
Bit 4: IRQ EN Falling Edge IN0-7
Bit 5: IRQ EN Falling Edge IN8-15
Bit 6: IRQ EN Falling Edge IN16-23
Bit 7: IRQ EN Falling Edge TTL0-7
An interrupt is asserted when a change-of-state matching the interrupt level configuration respective for a particular group of eight inputs with enabled COS is detected.
The COS Status registers may be read to determine which inputs have changed; if interrupts were enabled, an IRQ will be generated for the set bits in these registers. Writing the value read from the COS Status register back to the respective COS Clear register will clear just those interrupts.
---
Changes in v5:
- Refactor for map parameter removal from handle_mask_sync()
- Cleanups and line wrappings to 100 characters rather than 80
- Adjust to change mutex lock to raw_spin_lock_t
- Remove pex8311_intcsr table configurations as superfluous
- Adjust to set pex8311_intcsr_regmap_config reg_base to PLX_PEX8311_PCI_LCS_INTCSR
Changes in v4:
- Allocate idio24gpio before using it in idio_24_probe()
Changes in v3:
- Adjust idio_24_set_type_config() for parameter list
- Add mutex to prevent clobbering the COS_ENABLE register when masking IRQ and setting their type configuration
Changes in v2:
- Simplify PCIe-IDIO-24 register offset defines to remove superfluous arithmetic
- Check for NULL pointer after chip->irq_drv_data allocation
- Set gpio_regmap drvdata and use gpio_regmap_get_drvdata() to get the regmap in idio_24_reg_map_xlate()
drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-pcie-idio-24.c | 677 +++++++++++-------------------- 2 files changed, 250 insertions(+), 430 deletions(-)
drivers/gpio/Kconfig +3 -0
@@ -1587,7 +1587,10 @@ config GPIO_PCI_IDIO_16 config GPIO_PCIE_IDIO_24 tristate "ACCES PCIe-IDIO-24 GPIO support" + select REGMAP_IRQ + select REGMAP_MMIO select GPIOLIB_IRQCHIP + select GPIO_REGMAP help Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24, PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated
drivers/gpio/gpio-pcie-idio-24.c +247 -430
753 lines not on this page · raw message
Signature · 1 line
2.39.2
Re: [PATCH v5 2/3] gpio: pcie-idio-24: Migrate to the regmap API
Quoted 53 lines · William Breathitt Gray
The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
For the PCIe-IDIO-24 series of devices, the following BARs are available:
BAR[0]: memory mapped PEX8311 BAR[1]: I/O mapped PEX8311 BAR[2]: I/O mapped card registersThere are 24 FET Output lines, 24 Isolated Input lines, and 8 TTL/CMOS lines (which may be configured for either output or input). The GPIO lines are exposed by the following card registers:
Base +0x0-0x2 (Read/Write): FET Outputs Base +0xB (Read/Write): TTL/CMOS Base +0x4-0x6 (Read): Isolated Inputs Base +0x7 (Read): TTL/CMOSIn order for the device to support interrupts, the PLX PEX8311 internal PCI wire interrupt and local interrupt input must first be enabled.
The following card registers for Change-Of-State may be used:
Base +0x8-0xA (Read): COS Status Inputs Base +0x8-0xA (Write): COS Clear Inputs Base +0xB (Read): COS Status TTL/CMOS Base +0xB (Write): COS Clear TTL/CMOS Base +0xE (Read/Write): COS EnableThe COS Enable register is used to enable/disable interrupts and configure the interrupt levels; each bit maps to a group of eight inputs as described below:
Bit 0: IRQ EN Rising Edge IN0-7
Bit 1: IRQ EN Rising Edge IN8-15
Bit 2: IRQ EN Rising Edge IN16-23
Bit 3: IRQ EN Rising Edge TTL0-7
Bit 4: IRQ EN Falling Edge IN0-7
Bit 5: IRQ EN Falling Edge IN8-15
Bit 6: IRQ EN Falling Edge IN16-23
Bit 7: IRQ EN Falling Edge TTL0-7An interrupt is asserted when a change-of-state matching the interrupt level configuration respective for a particular group of eight inputs with enabled COS is detected.
The COS Status registers may be read to determine which inputs have changed; if interrupts were enabled, an IRQ will be generated for the set bits in these registers. Writing the value read from the COS Status register back to the respective COS Clear register will clear just those interrupts.
...
Quoted 3 lines
Cc Arnaud de Turckheim <[email protected]>Cc John Hentges <[email protected]>Cc Jay Dolan <[email protected]>
You can use --cc parameter to the git format-patch.
...
drivers/gpio/gpio-pcie-idio-24.c line 86
Quoted 9 lines
drivers/gpio/gpio-pcie-idio-24.c +9
-0+static const struct regmap_config pex8311_intcsr_regmap_config = { + .name ="pex8311_intcsr" , + .reg_bits =32 , + .reg_stride =1 , + .reg_base =PLX_PEX8311_PCI_LCS_INTCSR , + .val_bits =32 , + .io_port = true, + .max_register =0x0 , +};
Do you need regmap lock? If so, what for?
...
drivers/gpio/gpio-pcie-idio-24.c line 121
Quoted 11 lines
drivers/gpio/gpio-pcie-idio-24.c +11
-0+static const struct regmap_config idio_24_regmap_config = { + .reg_bits =8 , + .reg_stride =1 , + .val_bits =8 , + .io_port = true, + .max_register =0xF , + .wr_table = &idio_24_wr_table, + .rd_table = &idio_24_rd_table, + .volatile_table = &idio_24_volatile_table, + .cache_type =REGCACHE_FLAT , +};
Ditto.
...
Quoted 34 lines
drivers/gpio/gpio-pcie-idio-24.c +29
-0+static int idio_24_set_type_config (unsigned int **const buf,const unsigned int type, +const struct regmap_irq *const irq_data,const int idx, +void *const irq_drv_data) { +const unsigned int offset = irq_data->reg_offset; +const unsigned int rising =COS_ENABLE_RISING << offset; +const unsigned int falling =COS_ENABLE_FALLING << offset; +const unsigned int mask =COS_ENABLE_BOTH << offset; +struct idio_24_gpio *const idio24gpio = irq_drv_data; +unsigned int new; +unsigned int cos_enable; +int ret; + +switch (type) { +case IRQ_TYPE_EDGE_RISING : + new = rising; +break ; +case IRQ_TYPE_EDGE_FALLING : + new = falling; +break ; +case IRQ_TYPE_EDGE_BOTH : + new = mask; +break ; +default : +return -EINVAL ; } +raw_spin_lock (&idio24gpio->lock); +/* replace old bitmap with new bitmap */ + idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask); + ret =regmap_read (idio24gpio->map,IDIO_24_COS_ENABLE , &cos_enable); +if (ret)
drivers/gpio/gpio-pcie-idio-24.c line 222
Quoted 10 lines
drivers/gpio/gpio-pcie-idio-24.c +7
-0+ goto exit_early; +/* if COS is currently enabled then update the edge type */ +if (cos_enable & mask) { + ret =regmap_update_bits (idio24gpio->map,IDIO_24_COS_ENABLE , mask, + idio24gpio->irq_type); + goto exit_early; } +exit_early:
More descriptive is to call it
exit_unlock:
The rule of thumb is to explain what _will_ happen, if we goto $LABEL.
Quoted 4 lines
drivers/gpio/gpio-pcie-idio-24.c +2
-0+raw_spin_unlock (&idio24gpio->lock); +return ret; }
Signature · 2 lines
With Best Regards, Andy Shevchenko
PATCH v5 3/3 gpio: ws16c48: Migrate to the regmap API
In reply to William Breathitt GrayThe regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
The WinSystems WS16C48 provides the following registers:
Offset 0x0-0x5: Port 0-5 I/O
Offset 0x6: Int_Pending
Offset 0x7: Page/Lock
Offset 0x8-0xA (Page 1): Pol_0-Pol_2
Offset 0x8-0xA (Page 2): Enab_0-Enab_2
Offset 0x8-0xA (Page 3): Int_ID0-Int_ID2Port 0-5 I/O provides access to 48 lines of digital I/O across six registers, each bit position corresponding to the respective line. Writing a 1 to a respective bit position causes that output pin to sink current, while writing a 0 to the same bit position causes that output pin to go to a high-impedance state and allows it to be used an input. Reads on a port report the inverted state (0 = high, 1 = low) of an I/O pin when used in input mode. Interrupts are supported on Port 0-2.
Int_Pending is a read-only register that reports the combined state of the INT_ID0 through INT_ID2 registers; an interrupt pending is indicated when any of the low three bits are set.
The Page/Lock register provides the following bits:
Bit 0-5: Port 0-5 I/O Lock
Bit 6-7: Page 0-3 SelectionFor Bits 0-5, writing a 1 to a respective bit position locks the output state of the corresponding I/O port. Writing the page number to Bits 6-7 selects that respective register page for use.
Pol_0-Pol_2 are accessible when Page 1 is selected. Writing a 1 to a respective bit position selects the rising edge detection interrupts for that input line, while writing a 0 to the same bit position selects the falling edge detection interrupts.
Enab_0-Enab_2 are accessible when Page 2 is selected. Writing a 1 to a respective bit position enables interrupts for that input line, while writing a 0 to that same bit position clears and disables interrupts for that input line.
Int_ID0-Int_ID2 are accessible when Page 3 is selected. A respective bit when read as a 1 indicates that an edge of the polarity set in the corresponding polarity register was detected for the corresponding input line. Writing any value to this register clears all pending interrupts for the register.
---
Changes in v5:
- Refactor for map parameter removal from handle_mask_sync()
- Cleanups and lines to 100 characters rather than 80
- Rename PAGE_FIELD_PAGE_* defines to POL_PAGE, ENAB_PAGE, and INT_ID_PAGE
- Adjust to change spinlock_t lock to raw_spin_lock
Changes in v4: none
drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-ws16c48.c | 552 ++++++++++++------------------------ 2 files changed, 188 insertions(+), 367 deletions(-)
drivers/gpio/Kconfig +3 -0
@@ -983,7 +983,10 @@ config GPIO_WINBOND config GPIO_WS16C48 tristate "WinSystems WS16C48 GPIO support" select ISA_BUS_API + select REGMAP_IRQ + select REGMAP_MMIO select GPIOLIB_IRQCHIP + select GPIO_REGMAP help Enables GPIO support for the WinSystems WS16C48. The base port addresses for the devices may be configured via the base module
drivers/gpio/gpio-ws16c48.c +185 -367
655 lines not on this page · raw message
Signature · 1 line
2.39.2
Re: [PATCH v5 3/3] gpio: ws16c48: Migrate to the regmap API
Quoted 49 lines · William Breathitt Gray
The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
The WinSystems WS16C48 provides the following registers:
Offset 0x0-0x5: Port 0-5 I/O Offset 0x6: Int_Pending Offset 0x7: Page/Lock Offset 0x8-0xA (Page 1): Pol_0-Pol_2 Offset 0x8-0xA (Page 2): Enab_0-Enab_2 Offset 0x8-0xA (Page 3): Int_ID0-Int_ID2Port 0-5 I/O provides access to 48 lines of digital I/O across six registers, each bit position corresponding to the respective line. Writing a 1 to a respective bit position causes that output pin to sink current, while writing a 0 to the same bit position causes that output pin to go to a high-impedance state and allows it to be used an input. Reads on a port report the inverted state (0 = high, 1 = low) of an I/O pin when used in input mode. Interrupts are supported on Port 0-2.
Int_Pendingis a read-only register that reports the combined state of theINT_ID0throughINT_ID2registers; an interrupt pending is indicated when any of the low three bits are set.The Page/Lock register provides the following bits:
Bit 0-5: Port 0-5 I/O Lock Bit 6-7: Page 0-3 SelectionFor Bits 0-5, writing a 1 to a respective bit position locks the output state of the corresponding I/O port. Writing the page number to Bits 6-7 selects that respective register page for use.
Pol_0-Pol_2 are accessible when Page 1 is selected. Writing a 1 to a respective bit position selects the rising edge detection interrupts for that input line, while writing a 0 to the same bit position selects the falling edge detection interrupts.
Enab_0-Enab_2 are accessible when Page 2 is selected. Writing a 1 to a respective bit position enables interrupts for that input line, while writing a 0 to that same bit position clears and disables interrupts for that input line.
Int_ID0-Int_ID2 are accessible when Page 3 is selected. A respective bit when read as a 1 indicates that an edge of the polarity set in the corresponding polarity register was detected for the corresponding input line. Writing any value to this register clears all pending interrupts for the register.
...
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 76
Quoted 11 lines
drivers/gpio/gpio-ws16c48.c +11
-0+static const struct regmap_config ws16c48_regmap_config = { + .reg_bits =8 , + .reg_stride =1 , + .val_bits =8 , + .io_port = true, + .max_register =0xA , + .wr_table = &ws16c48_wr_table, + .rd_table = &ws16c48_rd_table, + .volatile_table = &ws16c48_volatile_table, + .cache_type =REGCACHE_FLAT , +};
Do we need regmap lock?
...
Quoted 12 lines
/**
* structws16c48_gpio- GPIO device private data structure
- * @chip: instance of thegpio_chip
- * @io_state: bit I/O state (whether bit is set to input or output)
- * @out_state: output bits state
+ * @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
- * @flow_mask: IRQ flow type mask for the respective I/O bits
- * @reg: I/O address offset for the device registers
*/
structws16c48_gpio{
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 111
Quoted 9 lines
drivers/gpio/gpio-ws16c48.c +2
-6-struct gpio_chip chip; -unsigned char io_state[6 ]; -unsigned char out_state[6 ]; +struct regmap *map;raw_spinlock_t lock; -unsigned long irq_mask; -unsigned long flow_mask; -struct ws16c48_reg __iomem *reg; +u8 irq_mask[WS16C48_NUM_IRQS /WS16C48_NGPIO_PER_REG ];
Looking at this (and also thinking about the previous patch) perhaps this should be declared as
DECLARE_BITMAP(...);
and corresponding bit ops to be used?
Quoted 1 line
};
...
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 122
Quoted 9 lines
drivers/gpio/gpio-ws16c48.c +4
-0+static int ws16c48_handle_pre_irq (void *const irq_drv_data) { +struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; +/* Lock to prevent Page/Lock register change while we handle IRQ */ +raw_spin_lock (&ws16c48gpio->lock);return 0 ; }
Hmm... Don't we have irq bus lock and unlock callbacks for this?
...
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 131
Quoted 8 lines
drivers/gpio/gpio-ws16c48.c +3
-0+static int ws16c48_handle_post_irq (void *const irq_drv_data) { +struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; +raw_spin_unlock (&ws16c48gpio->lock);return 0 ; }
Ditto.
Also shouldn't you annotate them for sparse so it won't complain about unbalanced locks?
...
Quoted 1 line
+exit_early:
exit_unlock() ?
Quoted 3 lines
drivers/gpio/gpio-ws16c48.c +1
-0raw_spin_unlock_irqrestore (&ws16c48gpio->lock, flags); +return ret;
...
drivers/gpio/gpio-ws16c48.c static const char *ws16c48_names[WS16C48_NGPIO] = { · line 232
Quoted 1 line
drivers/gpio/gpio-ws16c48.c +1
-0+ err =regmap_write (map,WS16C48_ENAB ,0x00 );
WS16C48_ENAB + 0
(for the sake of symmetry with the below)?
Quoted 8 lines
drivers/gpio/gpio-ws16c48.c +8
-0+if (err) +return err; + err =regmap_write (map,WS16C48_ENAB +1 ,0x00 ); +if (err) +return err; + err =regmap_write (map,WS16C48_ENAB +2 ,0x00 ); +if (err) +return err;
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v5 3/3] gpio: ws16c48: Migrate to the regmap API
Quoted 53 lines · Andy Shevchenko
Quoted 49 lines · William Breathitt Gray
The regmap API supports IO port accessors so we can take advantage of regmap abstractions rather than handling access to the device registers directly in the driver.
The WinSystems WS16C48 provides the following registers:
Offset 0x0-0x5: Port 0-5 I/O Offset 0x6: Int_Pending Offset 0x7: Page/Lock Offset 0x8-0xA (Page 1): Pol_0-Pol_2 Offset 0x8-0xA (Page 2): Enab_0-Enab_2 Offset 0x8-0xA (Page 3): Int_ID0-Int_ID2Port 0-5 I/O provides access to 48 lines of digital I/O across six registers, each bit position corresponding to the respective line. Writing a 1 to a respective bit position causes that output pin to sink current, while writing a 0 to the same bit position causes that output pin to go to a high-impedance state and allows it to be used an input. Reads on a port report the inverted state (0 = high, 1 = low) of an I/O pin when used in input mode. Interrupts are supported on Port 0-2.
Int_Pendingis a read-only register that reports the combined state of theINT_ID0throughINT_ID2registers; an interrupt pending is indicated when any of the low three bits are set.The Page/Lock register provides the following bits:
Bit 0-5: Port 0-5 I/O Lock Bit 6-7: Page 0-3 SelectionFor Bits 0-5, writing a 1 to a respective bit position locks the output state of the corresponding I/O port. Writing the page number to Bits 6-7 selects that respective register page for use.
Pol_0-Pol_2 are accessible when Page 1 is selected. Writing a 1 to a respective bit position selects the rising edge detection interrupts for that input line, while writing a 0 to the same bit position selects the falling edge detection interrupts.
Enab_0-Enab_2 are accessible when Page 2 is selected. Writing a 1 to a respective bit position enables interrupts for that input line, while writing a 0 to that same bit position clears and disables interrupts for that input line.
Int_ID0-Int_ID2 are accessible when Page 3 is selected. A respective bit when read as a 1 indicates that an edge of the polarity set in the corresponding polarity register was detected for the corresponding input line. Writing any value to this register clears all pending interrupts for the register.
...
Quoted 13 lines
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 76
Quoted 11 lines
drivers/gpio/gpio-ws16c48.c +11
-0+static const struct regmap_config ws16c48_regmap_config = { + .reg_bits =8 , + .reg_stride =1 , + .val_bits =8 , + .io_port = true, + .max_register =0xA , + .wr_table = &ws16c48_wr_table, + .rd_table = &ws16c48_rd_table, + .volatile_table = &ws16c48_volatile_table, + .cache_type =REGCACHE_FLAT , +};Do we need regmap lock?
We make regmap calls within an interrupt context in this driver so I think we need to disable the default regmap mutex locking behavior; I believe the current raw_spin_lock locking in this driver is enough to protect access.
Quoted 25 lines
Quoted 12 lines
/**
* structws16c48_gpio- GPIO device private data structure
- * @chip: instance of thegpio_chip
- * @io_state: bit I/O state (whether bit is set to input or output)
- * @out_state: output bits state
+ * @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
- * @flow_mask: IRQ flow type mask for the respective I/O bits
- * @reg: I/O address offset for the device registers
*/
structws16c48_gpio{drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 111
Quoted 9 lines
drivers/gpio/gpio-ws16c48.c +2
-6-struct gpio_chip chip; -unsigned char io_state[6 ]; -unsigned char out_state[6 ]; +struct regmap *map;raw_spinlock_t lock; -unsigned long irq_mask; -unsigned long flow_mask; -struct ws16c48_reg __iomem *reg; +u8 irq_mask[WS16C48_NUM_IRQS /WS16C48_NGPIO_PER_REG ];Looking at this (and also thinking about the previous patch) perhaps this should be declared as
Quoted 3 lines
DECLARE_BITMAP(...);and corresponding bit ops to be used?
The irq_mask array is just used to store the previous mask_buf state for comparision against the next time in the ws16c48_handle_mask_sync(); we're just checking if mask_buf has changed so we don't needless write out to hardware.
I think declaring as BITMAP would obscure the intention of this array, as well as increase the amount of code in ws16c48_handle_mask_sync() because mask_buf is not declared as a bitmap and would need to be converted for comparision against a bitmap irq_mask. So I believe we should keep irq_mask as an array of u8 for now.
What might be worth discussing is whether the regmap_irq should internally utilize BITMAP rather than passing around array elements and indices. The regmap_irq buffer arrays (such as mask_buf) appear to operate essentially as makeshift bitmaps, so I suspect they could benefit from the Linux bitmap API.
Quoted 11 lines
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 122
Quoted 9 lines
drivers/gpio/gpio-ws16c48.c +4
-0+static int ws16c48_handle_pre_irq (void *const irq_drv_data) { +struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; +/* Lock to prevent Page/Lock register change while we handle IRQ */ +raw_spin_lock (&ws16c48gpio->lock);return 0 ; }Hmm... Don't we have irq bus lock and unlock callbacks for this?
The WS16C48 shares the same address space for its interrupt polarity, IRQ masking, and IRQ status/ACK registers; a page register is utilize in order to multiplex between the three register pages.
Because the same address space is shared between these different functions, we use the ws16c48gpio->lock to coordinate access between them to prevent the registers from being clobbered. For example, interrupt polarity is set in irq_set_type(), while IRQ masking and ACKing occurs in irq_bus_sync_unlock(), and IRQ status reading happens in regmap_irq_thread().
The lock acquired in ws16c48_handle_pre_irq() is for just this last function of checking the IRQ status. We can't hold it through irq_bus_lock() because we have to handle IRQ masking and ACKing in irq_bus_sync_unlock().
Quoted 6 lines
Quoted 6 lines
drivers/gpio/gpio-ws16c48.c +3
-0+static int ws16c48_handle_post_irq (void *const irq_drv_data) { +struct ws16c48_gpio *const ws16c48gpio = irq_drv_data; +raw_spin_unlock (&ws16c48gpio->lock);
Quoted 7 lines
Quoted 2 lines
return 0;
}Ditto.
Also shouldn't you annotate them for sparse so it won't complain about unbalanced locks?
Yes, I'll annotate them with __acquires and __releases respectively.
William Breathitt Gray
Mark Brown
maintainerRe: [PATCH v5 3/3] gpio: ws16c48: Migrate to the regmap API
Quoted 17 lines · William Breathitt Gray
Quoted 15 lines · Andy Shevchenko
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 76
Quoted 11 lines · William Breathitt Gray
drivers/gpio/gpio-ws16c48.c +11
-0+static const struct regmap_config ws16c48_regmap_config = { + .reg_bits =8 , + .reg_stride =1 , + .val_bits =8 , + .io_port = true, + .max_register =0xA , + .wr_table = &ws16c48_wr_table, + .rd_table = &ws16c48_rd_table, + .volatile_table = &ws16c48_volatile_table, + .cache_type =REGCACHE_FLAT , +};Do we need regmap lock?
Quoted 4 lines
We make regmap calls within an interrupt context in this driver so I think we need to disable the default regmap mutex locking behavior; I believe the current
raw_spin_locklocking in this driver is enough to protect access.
The above doesn't configure the regmap locking so you'll have a spinlock by default (MMIO being a fast bus).
Re: [PATCH v5 3/3] gpio: ws16c48: Migrate to the regmap API
Quoted 18 lines · Mark Brown
Quoted 16 lines · William Breathitt Gray
Quoted 15 lines · Andy Shevchenko
drivers/gpio/gpio-ws16c48.c static unsigned int num_irq; · line 76
Quoted 11 lines · William Breathitt Gray
drivers/gpio/gpio-ws16c48.c +11
-0+static const struct regmap_config ws16c48_regmap_config = { + .reg_bits =8 , + .reg_stride =1 , + .val_bits =8 , + .io_port = true, + .max_register =0xA , + .wr_table = &ws16c48_wr_table, + .rd_table = &ws16c48_rd_table, + .volatile_table = &ws16c48_volatile_table, + .cache_type =REGCACHE_FLAT , +};Do we need regmap lock?
Quoted 7 lines
Quoted 4 lines
We make regmap calls within an interrupt context in this driver so I think we need to disable the default regmap mutex locking behavior; I believe the current
raw_spin_locklocking in this driver is enough to protect access.The above doesn't configure the regmap locking so you'll have a spinlock by default (MMIO being a fast bus).
You're right, it'll be a spinlock in this case and not mutex. Unfortunately, we'll still need to change that to avoid deadlocks on -rt kernels [0].
William Breathitt Gray
[0] https://lore.kernel.org/all/1466065537-82027-1-git-send-email-mika.westerberg@linux.intel.com/
Mark Brown
maintainerRe: [PATCH v5 3/3] gpio: ws16c48: Migrate to the regmap API
Quoted 8 lines · William Breathitt Gray
Quoted 2 lines · Mark Brown
The above doesn't configure the regmap locking so you'll have a spinlock by default (MMIO being a fast bus).
You're right, it'll be a spinlock in this case and not mutex. Unfortunately, we'll still need to change that to avoid deadlocks on -rt kernels [0].
My point is that you haven't actually done that.