Migrate PCIe-IDIO-24 GPIO driver to the regmap API
Subsystems (5)
cover Migrate PCIe-IDIO-24 GPIO driver to the regmap API
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 the device regmap and irq_drv_data as a parameters for the struct regmap_irq_chip set_type_config() is included. This is needed by idio_24_set_type_config() in order to update the type configuration on the device as well as irq_drv_data for idio_24_handle_mask_sync().
A patch moving the struct gpio_regmap declaration to linux/gpio/regmap.h is also included. This is needed by idio_24_reg_mask_xlate() in order to determine the current offset's direction by using gpio->regmap in regmap_read(). One point to consider is whether an alternative solution is better of passing regmap in the reg_mask_xlate() parameter list; this would avoid the need to include <linux/gpio/driver.h> in order to resolve an incomplete type warning for struct gpio_chip due to the move.
William Breathitt Gray (3):
regmap: Pass regmap and irq_drv_data as parameters for
set_type_config()
gpio: gpio-regmap: Expose struct gpio_regmap in linux/gpio/regmap.h
gpio: pcie-idio-24: Migrate to the regmap API
drivers/base/regmap/regmap-irq.c | 13 +- drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-pcie-idio-24.c | 697 ++++++++++++------------------- drivers/gpio/gpio-regmap.c | 20 - include/linux/gpio/regmap.h | 23 +- include/linux/regmap.h | 12 +- 6 files changed, 303 insertions(+), 465 deletions(-)
base-commit: 4827aae061337251bb91801b316157a78b845ec7
Signature · 1 line
2.39.2
PATCH 1/3 regmap: Pass regmap and irq_drv_data as parameters for set_type_config()
Allow struct regmap_irq_chip set_type_config() callbacks to access the device regmap and irq_drv_data by passing them as parameters.
--- drivers/base/regmap/regmap-irq.c | 13 +++++++++---- include/linux/regmap.h | 12 ++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-)
drivers/base/regmap/regmap-irq.c +9 -4
@@ -333,8 +333,9 @@ 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); + ret = d->chip->set_type_config (map, d->config_buf, type, + irq_data, reg, + d->chip->irq_drv_data);if (ret)return ret; }
@@ -650,18 +651,22 @@ EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);/** * regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback. +* @map: The regmap for the device. * @buf: Buffer containing configuration register values, this is a 2D array of * `num_config_bases` rows, each of `num_config_regs` elements. * @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) +int regmap_irq_set_type_config_simple (struct regmap *map,unsigned int **buf, +unsigned int type, +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 +8 -4
@@ -1648,8 +1648,10 @@ struct regmap_irq_chip { unsigned int mask_buf, void *irq_drv_data);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); +int (*set_type_config)(struct regmap *map,unsigned int **buf, +unsigned int type, +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;
@@ -1657,8 +1659,10 @@ 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); +int regmap_irq_set_type_config_simple (struct regmap *map,unsigned int **buf, +unsigned int type, +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
PATCH 2/3 gpio: gpio-regmap: Expose struct gpio_regmap in linux/gpio/regmap.h
In reply to William Breathitt GrayA struct gpio_regmap is passed as a parameter for reg_mask_xlate(), but for callbacks to access its members the declaration must be exposed. Move the struct gpio_regmap declaration from drivers/gpio/gpio-regmap.c to include/linux/gpio/regmap.h so callbacks can properly interact with struct gpio_regmap members.
--- drivers/gpio/gpio-regmap.c | 20 -------------------- include/linux/gpio/regmap.h | 23 ++++++++++++++++++++++- 2 files changed, 22 insertions(+), 21 deletions(-)
drivers/gpio/gpio-regmap.c +0 -20
@@ -11,26 +11,6 @@#include <linux/module.h> #include <linux/regmap.h> -struct gpio_regmap { -struct device *parent; -struct regmap *regmap; -struct gpio_chip gpio_chip; - -int reg_stride; -int ngpio_per_reg; -unsigned int reg_dat_base; -unsigned int reg_set_base; -unsigned int reg_clr_base; -unsigned int reg_dir_in_base; -unsigned int reg_dir_out_base; - -int (*reg_mask_xlate)(struct gpio_regmap *gpio,unsigned int base, -unsigned int offset,unsigned int *reg, -unsigned int *mask); - -void *driver_data; -}; -static unsigned int gpio_regmap_addr (unsigned int addr) {if (addr ==GPIO_REGMAP_ADDR_ZERO )
include/linux/gpio/regmap.h +22 -1
@@ -3,15 +3,36 @@#ifndef _LINUX_GPIO_REGMAP_H#define _LINUX_GPIO_REGMAP_H +#include <linux/gpio/driver.h> +struct device ;struct fwnode_handle ; -struct gpio_regmap ;struct irq_domain ;struct regmap ;#define GPIO_REGMAP_ADDR_ZERO ((unsigned int)(-1))#define GPIO_REGMAP_ADDR (addr) ((addr) ? : GPIO_REGMAP_ADDR_ZERO) +struct gpio_regmap { +struct device *parent; +struct regmap *regmap; +struct gpio_chip gpio_chip; + +int reg_stride; +int ngpio_per_reg; +unsigned int reg_dat_base; +unsigned int reg_set_base; +unsigned int reg_clr_base; +unsigned int reg_dir_in_base; +unsigned int reg_dir_out_base; + +int (*reg_mask_xlate)(struct gpio_regmap *gpio,unsigned int base, +unsigned int offset,unsigned int *reg, +unsigned int *mask); + +void *driver_data; +}; +/** * struct gpio_regmap_config - Description of a generic regmap gpio_chip. * @parent: The parent device
Signature · 1 line
2.39.2
Re: [PATCH 2/3] gpio: gpio-regmap: Expose struct gpio_regmap in linux/gpio/regmap.h
Hi,
Am 2023-02-28 02:53, schrieb William Breathitt Gray:
Quoted 2 lines
A struct
gpio_regmapis passed as a parameter forreg_mask_xlate(), but for callbacks to access its members the declaration must be exposed.
That parameter is only an opaque one to call any gpio_regmap_*().
Quoted 3 lines
Move the struct
gpio_regmapdeclaration fromdrivers/gpio/gpio-regmap.ctoinclude/linux/gpio/regmap.hso callbacks can properly interact with structgpio_regmapmembers.
That struct should be kept private. It seems you only need the regmap. Either introduce a gpio_regmap_get_regmap() or add the regmap to a private struct and use gpio_regmap_get_drvdata().
-michael
Re: [PATCH 2/3] gpio: gpio-regmap: Expose struct gpio_regmap in linux/gpio/regmap.h
Quoted 12 lines · Michael Walle
Hi,
Am 2023-02-28 02:53, schrieb William Breathitt Gray:
Quoted 2 lines
A struct
gpio_regmapis passed as a parameter forreg_mask_xlate(), but for callbacks to access its members the declaration must be exposed.That parameter is only an opaque one to call any gpio_regmap_*().
Quoted 3 lines
Move the struct
gpio_regmapdeclaration fromdrivers/gpio/gpio-regmap.ctoinclude/linux/gpio/regmap.hso callbacks can properly interact with structgpio_regmapmembers.
Quoted 5 lines
That struct should be kept private. It seems you only need the regmap. Either introduce a
gpio_regmap_get_regmap()or add the regmap to a private struct and usegpio_regmap_get_drvdata().-michael
Ah, I'll drop this patch then and use gpio_regmap_get_drvdata() in v2 instead to get access to the regmap.
William Breathitt Gray
PATCH 3/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.
--- drivers/gpio/Kconfig | 3 + drivers/gpio/gpio-pcie-idio-24.c | 697 ++++++++++++------------------- 2 files changed, 264 insertions(+), 436 deletions(-)
drivers/gpio/Kconfig +3 -0
@@ -1578,7 +1578,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 +261 -436
767 lines not on this page · raw message
Signature · 1 line
2.39.2
kernel test robot
build botRe: [PATCH 3/3] gpio: pcie-idio-24: Migrate to the regmap API
I love your patch! Perhaps something to improve:
Hi William,
I love your patch! Perhaps something to improve:
[auto build test WARNING on 4827aae061337251bb91801b316157a78b845ec7]
url: https://github.com/intel-lab-lkp/linux/commits/William-Breathitt-Gray/regmap-Pass-regmap-and-irq_drv_data-as-parameters-for-set_type_config/20230301-030010 base: 4827aae061337251bb91801b316157a78b845ec7 patch link: https://lore.kernel.org/r/39f4c4b7083b2a50973e0ac5b4b1db5040fcda53.1677547393.git.william.gray%40linaro.org patch subject: [PATCH 3/3] gpio: pcie-idio-24: Migrate to the regmap API config: x86_64-randconfig-a001-20230227 (https://download.01.org/0day-ci/archive/20230301/[email protected]/config) compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/6f59a8e427da386890ca2eaccab41064a250ebea
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review William-Breathitt-Gray/regmap-Pass-regmap-and-irq_drv_data-as-parameters-for-set_type_config/20230301-030010 git checkout 6f59a8e427da386890ca2eaccab41064a250ebea
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/gpio/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/gpio/gpio-pcie-idio-24.c:353:7: warning: variable 'irq_type' is uninitialized when used here [-Wuninitialized]
if (!irq_type)
^~~~~~~~
drivers/gpio/gpio-pcie-idio-24.c:319:23: note: initialize the variable 'irq_type' to silence this warning
unsigned int irq_type;
^
= 01 warning generated.
vim +/irq_type +353 drivers/gpio/gpio-pcie-idio-24.c
305
306 static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
307 {
308 struct device *const dev = &pdev->dev;
309 int err;
310 const size_t pci_plx_bar_index = 1;
311 const size_t pci_bar_index = 2;
312 const char *const name = pci_name(pdev);
313 struct gpio_regmap_config gpio_config = {};
314 void __iomem *pex8311_intcsr;
315 void __iomem *idio_24_regs;
316 struct regmap *pex8311_intcsr_map;
317 struct regmap *idio_24_map;
318 struct regmap_irq_chip *chip;
319 unsigned int irq_type;
320 struct regmap_irq_chip_data *chip_data;
321
322 err = pcim_enable_device(pdev);
323 if (err) {
324 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
325 return err;
326 }
327
328 err = pcim_iomap_regions(pdev, BIT(pci_plx_bar_index) | BIT(pci_bar_index), name);
329 if (err) {
330 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
331 return err;
332 }
333
334 pex8311_intcsr = pcim_iomap_table(pdev)[pci_plx_bar_index] + PLX_PEX8311_PCI_LCS_INTCSR;
335 idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index];
336
337 pex8311_intcsr_map = devm_regmap_init_mmio(dev, pex8311_intcsr,
338 &pex8311_intcsr_regmap_config);
339 if (IS_ERR(pex8311_intcsr_map))
340 return dev_err_probe(dev, PTR_ERR(pex8311_intcsr_map),
341 "Unable to initialize PEX8311 register map\n");
342 idio_24_map = devm_regmap_init_mmio(dev, idio_24_regs,
343 &idio_24_regmap_config);
344 if (IS_ERR(idio_24_map))
345 return dev_err_probe(dev, PTR_ERR(idio_24_map),
346 "Unable to initialize register map\n");
347
348 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
349 if (!chip)
350 return -ENOMEM;
351
352 chip->irq_drv_data = devm_kzalloc(dev, sizeof(irq_type), GFP_KERNEL);
> 353 if (!irq_type)
354 return -ENOMEM;
355
356 chip->name = name;
357 chip->status_base = IDIO_24_STATUS_BASE;
358 chip->mask_base = IDIO_24_COS_ENABLE;
359 chip->ack_base = IDIO_24_ACK_BASE;
360 chip->num_regs = 4;
361 chip->irqs = idio_24_regmap_irqs;
362 chip->num_irqs = ARRAY_SIZE(idio_24_regmap_irqs);
363 chip->handle_mask_sync = idio_24_handle_mask_sync;
364 chip->set_type_config = idio_24_set_type_config;
365
366 /* Software board reset */
367 err = regmap_write(idio_24_map, IDIO_24_SOFT_RESET, 0);
368 if (err)
369 return err;
370 /*
371 * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
372 * input
373 */
374 err = regmap_update_bits(pex8311_intcsr_map, 0x0, IDIO_24_ENABLE_IRQ,
375 IDIO_24_ENABLE_IRQ);
376 if (err)
377 return err;
378
379 err = devm_regmap_add_irq_chip(dev, idio_24_map, pdev->irq, 0, 0, chip,
380 &chip_data);
381 if (err)
382 return dev_err_probe(dev, err, "IRQ registration failed\n");
383
384 gpio_config.parent = dev;
385 gpio_config.regmap = idio_24_map;
386 gpio_config.ngpio = IDIO_24_NGPIO;
387 gpio_config.names = idio_24_names;
388 gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_24_DAT_BASE);
389 gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_24_DAT_BASE);
390 gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(IDIO_24_CONTROL_REG);
391 gpio_config.ngpio_per_reg = IDIO_24_NGPIO_PER_REG;
392 gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
393 gpio_config.reg_mask_xlate = idio_24_reg_mask_xlate;
394
395 return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
396 }
397
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests
Mark Brown
maintainerQuoted 4 lines · William Breathitt Gray
A patch to pass the device regmap and
irq_drv_dataas a parameters for the structregmap_irq_chipset_type_config()is included. This is needed byidio_24_set_type_config()in order to update the type configuration on the device as well asirq_drv_dataforidio_24_handle_mask_sync().
The values from the config buffer are supposed to be written out in regmap_irq_sync_unlock() - why is something custom needed here?
Quoted 9 lines · Mark Brown
Quoted 4 lines · William Breathitt Gray
A patch to pass the device regmap and
irq_drv_dataas a parameters for the structregmap_irq_chipset_type_config()is included. This is needed byidio_24_set_type_config()in order to update the type configuration on the device as well asirq_drv_dataforidio_24_handle_mask_sync().The values from the config buffer are supposed to be written out in
regmap_irq_sync_unlock()- why is something custom needed here?
The PCIe-IDIO-24 "COS Enable" serves a dual purpose of interrupt enabling/disabling as well as configuring the interrupt types. Since this register is used for masking, config buffer would clobber the register if we use it in this particular case. Instead, we ignore the config buffer and configure the type directly for the device (handling the case where interrupts are masked and shouldn't be enabled).
William Breathitt Gray
Mark Brown
maintainerQuoted 11 lines · William Breathitt Gray
Quoted 2 lines · Mark Brown
The values from the config buffer are supposed to be written out in
regmap_irq_sync_unlock()- why is something custom needed here?The PCIe-IDIO-24 "COS Enable" serves a dual purpose of interrupt enabling/disabling as well as configuring the interrupt types. Since this register is used for masking, config buffer would clobber the register if we use it in this particular case. Instead, we ignore the config buffer and configure the type directly for the device (handling the case where interrupts are masked and shouldn't be enabled).
Could you be more concrete about what's going on here please? In what way does this "COS Enable" serve these dual functions and why do they clobber each other?
Quoted 13 lines · Mark Brown
Quoted 11 lines · William Breathitt Gray
Quoted 2 lines · Mark Brown
The values from the config buffer are supposed to be written out in
regmap_irq_sync_unlock()- why is something custom needed here?The PCIe-IDIO-24 "COS Enable" serves a dual purpose of interrupt enabling/disabling as well as configuring the interrupt types. Since this register is used for masking, config buffer would clobber the register if we use it in this particular case. Instead, we ignore the config buffer and configure the type directly for the device (handling the case where interrupts are masked and shouldn't be enabled).
Quoted 3 lines
Could you be more concrete about what's going on here please? In what way does this "COS Enable" serve these dual functions and why do they clobber each other?
An explanation of the device registers is provided in [PATCH 3/3]; here's the relevant portion:
The 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.
So in order to mask lines, the respective bits need to be set to 0. However, if we use the regmap-irq config buffer to set the type, this mask will be cloberred and the disabled lines become enabled. To prevent the clobber, we can save the type configuration to irq_drv_data for use later in handle_mask_sync() and then update the type in this COS Enable register only when the lines are unmasked.
William Breathitt Gray