In a similar vein as the Intel 8255 interface library module [0], the
ACCES IDIO-16 GPIO library module is introduced to consolidate much of
the shared code between the existing 104-IDIO-16 and PCI-IDIO-16 GPIO
drivers.
The idio-16 module exposes consumer library functions to facilitate
communication with devices within the ACCES IDIO-16 family such as the
104-IDIO-16 and the PCI-IDIO-16.
A CONFIG_GPIO_IDIO_16 Kconfig option is introduced by this patch.
Modules wanting access to these idio-16 library functions should select
this Kconfig option and import the IDIO_16 symbol namespace.
[0] https://lore.kernel.org/all/d1a24895f2ea67f689c24c34a20ddb43cd7246af.1658324498.git.william.gray@linaro.org/
William Breathitt Gray (3):
gpio: idio-16: Introduce the ACCES IDIO-16 GPIO library module
gpio: 104-idio-16: Utilize the idio-16 GPIO library
gpio: pci-idio-16: Utilize the idio-16 GPIO library
MAINTAINERS | 6 ++
drivers/gpio/Kconfig | 11 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-104-idio-16.c | 91 +++++-------------
drivers/gpio/gpio-idio-16.c | 159 ++++++++++++++++++++++++++++++++
drivers/gpio/gpio-idio-16.h | 70 ++++++++++++++
drivers/gpio/gpio-pci-idio-16.c | 119 +++---------------------
7 files changed, 281 insertions(+), 176 deletions(-)
create mode 100644 drivers/gpio/gpio-idio-16.c
create mode 100644 drivers/gpio/gpio-idio-16.h
base-commit: 3af20d2723be5f70e1ce818504a4c093a81b21f5
--
2.37.2
The ACCES PCI-IDIO-16 device is part of the ACCES IDIO-16 family, so the
idio-16 GPIO library module is selected and utilized to consolidate
code.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-pci-idio-16.c | 119 ++++----------------------------
2 files changed, 14 insertions(+), 106 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 48846ee476e2..8b90bff7b198 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1585,6 +1585,7 @@ config GPIO_PCH
config GPIO_PCI_IDIO_16
tristate "ACCES PCI-IDIO-16 GPIO support"
select GPIOLIB_IRQCHIP
+ select GPIO_IDIO_16
help
Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is
generated when any of the inputs change state (low to high or high to
diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 71a13a394050..41f9c447ebf9 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -3,8 +3,7 @@
* GPIO driver for the ACCES PCI-IDIO-16
* Copyright (C) 2017 William Breathitt Gray
*/
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
+#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
@@ -16,51 +15,28 @@
#include <linux/spinlock.h>
#include <linux/types.h>
-/**
- * struct idio_16_gpio_reg - GPIO device registers structure
- * @out0_7: Read: FET Drive Outputs 0-7
- * Write: FET Drive Outputs 0-7
- * @in0_7: Read: Isolated Inputs 0-7
- * Write: Clear Interrupt
- * @irq_ctl: Read: Enable IRQ
- * Write: Disable IRQ
- * @filter_ctl: Read: Activate Input Filters 0-15
- * Write: Deactivate Input Filters 0-15
- * @out8_15: Read: FET Drive Outputs 8-15
- * Write: FET Drive Outputs 8-15
- * @in8_15: Read: Isolated Inputs 8-15
- * Write: Unused
- * @irq_status: Read: Interrupt status
- * Write: Unused
- */
-struct idio_16_gpio_reg {
- u8 out0_7;
- u8 in0_7;
- u8 irq_ctl;
- u8 filter_ctl;
- u8 out8_15;
- u8 in8_15;
- u8 irq_status;
-};
+#include "gpio-idio-16.h"
/**
* struct idio_16_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
* @lock: synchronization lock to prevent I/O race conditions
* @reg: I/O address offset for the GPIO device registers
+ * @state: ACCES IDIO-16 device state
* @irq_mask: I/O bits affected by interrupts
*/
struct idio_16_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
- struct idio_16_gpio_reg __iomem *reg;
+ struct idio_16 __iomem *reg;
+ struct idio_16_state state;
unsigned long irq_mask;
};
static int idio_16_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
- if (offset > 15)
+ if (idio_16_get_direction(offset))
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
@@ -82,43 +58,16 @@ static int idio_16_gpio_direction_output(struct gpio_chip *chip,
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- unsigned long mask = BIT(offset);
-
- if (offset < 8)
- return !!(ioread8(&idio16gpio->reg->out0_7) & mask);
-
- if (offset < 16)
- return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8));
-
- if (offset < 24)
- return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16));
- return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
+ return idio_16_get(idio16gpio->reg, offset);
}
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- unsigned long offset;
- unsigned long gpio_mask;
- void __iomem *ports[] = {
- &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
- &idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15,
- };
- void __iomem *port_addr;
- unsigned long port_state;
-
- /* clear bits array to a clean slate */
- bitmap_zero(bits, chip->ngpio);
-
- for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
- port_addr = ports[offset / 8];
- port_state = ioread8(port_addr) & gpio_mask;
-
- bitmap_set_value8(bits, port_state, offset);
- }
+ idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
return 0;
}
@@ -126,61 +75,16 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- unsigned int mask = BIT(offset);
- void __iomem *base;
- unsigned long flags;
- unsigned int out_state;
-
- if (offset > 15)
- return;
-
- if (offset > 7) {
- mask >>= 8;
- base = &idio16gpio->reg->out8_15;
- } else
- base = &idio16gpio->reg->out0_7;
-
- raw_spin_lock_irqsave(&idio16gpio->lock, flags);
- if (value)
- out_state = ioread8(base) | mask;
- else
- out_state = ioread8(base) & ~mask;
-
- iowrite8(out_state, base);
-
- raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
+ idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
}
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- unsigned long offset;
- unsigned long gpio_mask;
- void __iomem *ports[] = {
- &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
- };
- size_t index;
- void __iomem *port_addr;
- unsigned long bitmask;
- unsigned long flags;
- unsigned long out_state;
- for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
- index = offset / 8;
- port_addr = ports[index];
-
- bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
-
- raw_spin_lock_irqsave(&idio16gpio->lock, flags);
-
- out_state = ioread8(port_addr) & ~gpio_mask;
- out_state |= bitmask;
- iowrite8(out_state, port_addr);
-
- raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
- }
+ idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
}
static void idio_16_irq_ack(struct irq_data *data)
@@ -335,6 +239,8 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
+ idio_16_state_init(&idio16gpio->state);
+
girq = &idio16gpio->chip.irq;
girq->chip = &idio_16_irqchip;
/* This will let us handle the parent IRQ in the driver */
@@ -379,3 +285,4 @@ module_pci_driver(idio_16_driver);
MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IDIO_16);
--
2.37.2
Exposes consumer library functions to facilitate communication with
devices within the ACCES IDIO-16 family such as the 104-IDIO-16 and
the PCI-IDIO-16.
A CONFIG_GPIO_IDIO_16 Kconfig option is introduced by this patch.
Modules wanting access to these idio-16 library functions should select
this Kconfig option and import the IDIO_16 symbol namespace.
Signed-off-by: William Breathitt Gray <[email protected]>
---
MAINTAINERS | 6 ++
drivers/gpio/Kconfig | 9 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-idio-16.c | 159 ++++++++++++++++++++++++++++++++++++
drivers/gpio/gpio-idio-16.h | 70 ++++++++++++++++
5 files changed, 245 insertions(+)
create mode 100644 drivers/gpio/gpio-idio-16.c
create mode 100644 drivers/gpio/gpio-idio-16.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 8a5012ba6ff9..a143d4bc8547 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -312,6 +312,12 @@ L: [email protected]
S: Maintained
F: drivers/counter/104-quad-8.c
+ACCES IDIO-16 GPIO LIBRARY
+M: William Breathitt Gray <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/gpio/gpio-idio-16.c
+
ACCES PCI-IDIO-16 GPIO DRIVER
M: William Breathitt Gray <[email protected]>
L: [email protected]
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ed9e71d6713e..551351e11365 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -109,6 +109,15 @@ config GPIO_REGMAP
config GPIO_MAX730X
tristate
+config GPIO_IDIO_16
+ tristate
+ help
+ Enables support for the idio-16 library functions. The idio-16 library
+ provides functions to facilitate communication with devices within the
+ ACCES IDIO-16 family such as the 104-IDIO-16 and the PCI-IDIO-16.
+
+ If built as a module its name will be gpio-idio-16.
+
menu "Memory mapped GPIO drivers"
depends on HAS_IOMEM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b67e29d348cf..15abf88c167b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
+obj-$(CONFIG_GPIO_IDIO_16) += gpio-idio-16.o
obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o
obj-$(CONFIG_GPIO_IMX_SCU) += gpio-imx-scu.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c
new file mode 100644
index 000000000000..e244032bac35
--- /dev/null
+++ b/drivers/gpio/gpio-idio-16.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO library for the ACCES IDIO-16 family
+ * Copyright (C) 2022 William Breathitt Gray
+ */
+#include <linux/bitmap.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "gpio-idio-16.h"
+
+/**
+ * idio_16_get - get signal value at signal offset
+ * @reg: ACCES IDIO-16 device registers
+ * @offset: offset of signal to get
+ *
+ * Returns the signal value (0=low, 1=high) for the signal at @offset.
+ */
+int idio_16_get(struct idio_16 __iomem *const reg, const unsigned long offset)
+{
+ const unsigned long mask = BIT(offset);
+
+ if (offset < 8)
+ return !!(ioread8(®->out0_7) & mask);
+
+ if (offset < 16)
+ return !!(ioread8(®->out8_15) & (mask >> 8));
+
+ if (offset < 24)
+ return !!(ioread8(®->in0_7) & (mask >> 16));
+
+ return !!(ioread8(®->in8_15) & (mask >> 24));
+}
+EXPORT_SYMBOL_NS_GPL(idio_16_get, IDIO_16);
+
+/**
+ * idio_16_get_multiple - get multiple signal values at multiple signal offsets
+ * @reg: ACCES IDIO-16 device registers
+ * @state: ACCES IDIO-16 device state
+ * @mask: mask of signals to get
+ * @bits: bitmap to store signal values
+ *
+ * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask.
+ */
+void idio_16_get_multiple(struct idio_16 __iomem *const reg,
+ struct idio_16_state *const state,
+ const unsigned long *const mask,
+ unsigned long *const bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (*mask & GENMASK(7, 0))
+ bitmap_set_value8(bits, ioread8(®->out0_7), 0);
+ if (*mask & GENMASK(15, 8))
+ bitmap_set_value8(bits, ioread8(®->out8_15), 8);
+ if (*mask & GENMASK(23, 16))
+ bitmap_set_value8(bits, ioread8(®->in0_7), 16);
+ if (*mask & GENMASK(31, 24))
+ bitmap_set_value8(bits, ioread8(®->in8_15), 24);
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+EXPORT_SYMBOL_NS_GPL(idio_16_get_multiple, IDIO_16);
+
+/**
+ * idio_16_set - set signal value at signal offset
+ * @reg: ACCES IDIO-16 device registers
+ * @state: ACCES IDIO-16 device state
+ * @offset: offset of signal to set
+ * @value: value of signal to set
+ *
+ * Assigns output @value for the signal at @offset.
+ */
+void idio_16_set(struct idio_16 __iomem *const reg,
+ struct idio_16_state *const state, const unsigned long offset,
+ const unsigned long value)
+{
+ unsigned long flags;
+
+ /* offsets greater than 15 are input-only signals */
+ if (offset > 15)
+ return;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (value)
+ set_bit(offset, state->out_state);
+ else
+ clear_bit(offset, state->out_state);
+
+ if (offset < 8)
+ iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7);
+ else
+ iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15);
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+EXPORT_SYMBOL_NS_GPL(idio_16_set, IDIO_16);
+
+/**
+ * idio_16_set_multiple - set signal values at multiple signal offsets
+ * @reg: ACCES IDIO-16 device registers
+ * @state: ACCES IDIO-16 device state
+ * @mask: mask of signals to set
+ * @bits: bitmap of signal output values
+ *
+ * Assigns output values defined by @bits for the signals defined by @mask.
+ */
+void idio_16_set_multiple(struct idio_16 __iomem *const reg,
+ struct idio_16_state *const state,
+ const unsigned long *const mask,
+ const unsigned long *const bits)
+{
+ unsigned long flags;
+ unsigned long offset;
+ unsigned long port_mask;
+ unsigned long value;
+ unsigned long out_state;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ for_each_set_clump8(offset, port_mask, mask, IDIO_16_NOUT) {
+ value = bitmap_get_value8(bits, offset);
+ out_state = bitmap_get_value8(state->out_state, offset);
+
+ out_state = (out_state & ~port_mask) | (value & port_mask);
+ bitmap_set_value8(state->out_state, out_state, offset);
+
+ if (offset < 8)
+ iowrite8(out_state, ®->out0_7);
+ else
+ iowrite8(out_state, ®->out8_15);
+ }
+
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+EXPORT_SYMBOL_NS_GPL(idio_16_set_multiple, IDIO_16);
+
+/**
+ * idio_16_state_init - initialize idio_16_state structure
+ * @state: ACCES IDIO-16 device state
+ *
+ * Initializes the ACCES IDIO-16 device @state for use in idio-16 library
+ * functions.
+ */
+void idio_16_state_init(struct idio_16_state *const state)
+{
+ spin_lock_init(&state->lock);
+}
+EXPORT_SYMBOL_NS_GPL(idio_16_state_init, IDIO_16);
+
+MODULE_AUTHOR("William Breathitt Gray");
+MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-idio-16.h b/drivers/gpio/gpio-idio-16.h
new file mode 100644
index 000000000000..ce1fa0639243
--- /dev/null
+++ b/drivers/gpio/gpio-idio-16.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2022 William Breathitt Gray */
+#ifndef _IDIO_16_H_
+#define _IDIO_16_H_
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/**
+ * struct idio_16 - IDIO-16 registers structure
+ * @out0_7: Read: FET Drive Outputs 0-7
+ * Write: FET Drive Outputs 0-7
+ * @in0_7: Read: Isolated Inputs 0-7
+ * Write: Clear Interrupt
+ * @irq_ctl: Read: Enable IRQ
+ * Write: Disable IRQ
+ * @filter_ctl: Read: Activate Input Filters 0-15
+ * Write: Deactivate Input Filters 0-15
+ * @out8_15: Read: FET Drive Outputs 8-15
+ * Write: FET Drive Outputs 8-15
+ * @in8_15: Read: Isolated Inputs 8-15
+ * Write: Unused
+ * @irq_status: Read: Interrupt status
+ * Write: Unused
+ */
+struct idio_16 {
+ u8 out0_7;
+ u8 in0_7;
+ u8 irq_ctl;
+ u8 filter_ctl;
+ u8 out8_15;
+ u8 in8_15;
+ u8 irq_status;
+};
+
+#define IDIO_16_NOUT 16
+
+/**
+ * struct idio_16_state - IDIO-16 state structure
+ * @lock: synchronization lock for accessing device state
+ * @out_state: output signals state
+ */
+struct idio_16_state {
+ spinlock_t lock;
+ DECLARE_BITMAP(out_state, IDIO_16_NOUT);
+};
+
+/**
+ * idio_16_get_direction - get the I/O direction for a signal offset
+ * @offset: offset of signal to get direction
+ *
+ * Returns the signal direction (0=output, 1=input) for the signal at @offset.
+ */
+static inline int idio_16_get_direction(const unsigned long offset)
+{
+ return (offset < IDIO_16_NOUT) ? 0 : 1;
+}
+
+int idio_16_get(struct idio_16 __iomem *reg, unsigned long offset);
+void idio_16_get_multiple(struct idio_16 __iomem *reg,
+ struct idio_16_state *state,
+ const unsigned long *mask, unsigned long *bits);
+void idio_16_set(struct idio_16 __iomem *reg, struct idio_16_state *state,
+ unsigned long offset, unsigned long value);
+void idio_16_set_multiple(struct idio_16 __iomem *reg,
+ struct idio_16_state *state,
+ const unsigned long *mask, const unsigned long *bits);
+void idio_16_state_init(struct idio_16_state *state);
+
+#endif /* _IDIO_16_H_ */
--
2.37.2
The ACCES 104-IDIO-16 device is part of the ACCES IDIO-16 family, so the
idio-16 GPIO library module is selected and utilized to consolidate
code.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-104-idio-16.c | 91 ++++++++-------------------------
2 files changed, 22 insertions(+), 70 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 551351e11365..48846ee476e2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -866,6 +866,7 @@ config GPIO_104_IDIO_16
depends on PC104
select ISA_BUS_API
select GPIOLIB_IRQCHIP
+ select GPIO_IDIO_16
help
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, 104-IDO-8). The
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 65a5f581d981..75b5f019676f 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -6,7 +6,7 @@
* This driver supports the following ACCES devices: 104-IDIO-16,
* 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
*/
-#include <linux/bits.h>
+#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
@@ -21,6 +21,8 @@
#include <linux/spinlock.h>
#include <linux/types.h>
+#include "gpio-idio-16.h"
+
#define IDIO_16_EXTENT 8
#define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
@@ -33,49 +35,26 @@ static unsigned int irq[MAX_NUM_IDIO_16];
module_param_hw_array(irq, uint, irq, NULL, 0);
MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
-/**
- * struct idio_16_reg - device registers structure
- * @out0_7: Read: N/A
- * Write: FET Drive Outputs 0-7
- * @in0_7: Read: Isolated Inputs 0-7
- * Write: Clear Interrupt
- * @irq_ctl: Read: Enable IRQ
- * Write: Disable IRQ
- * @unused: N/A
- * @out8_15: Read: N/A
- * Write: FET Drive Outputs 8-15
- * @in8_15: Read: Isolated Inputs 8-15
- * Write: N/A
- */
-struct idio_16_reg {
- u8 out0_7;
- u8 in0_7;
- u8 irq_ctl;
- u8 unused;
- u8 out8_15;
- u8 in8_15;
-};
-
/**
* struct idio_16_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
* @reg: I/O address offset for the device registers
- * @out_state: output bits state
+ * @state: ACCES IDIO-16 device state
*/
struct idio_16_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
unsigned long irq_mask;
- struct idio_16_reg __iomem *reg;
- unsigned int out_state;
+ struct idio_16 __iomem *reg;
+ struct idio_16_state state;
};
static int idio_16_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
- if (offset > 15)
+ if (idio_16_get_direction(offset))
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
@@ -97,15 +76,12 @@ static int idio_16_gpio_direction_output(struct gpio_chip *chip,
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- const unsigned int mask = BIT(offset-16);
- if (offset < 16)
+ /* Reading output signals is not supported */
+ if (offset < IDIO_16_NOUT)
return -EINVAL;
- if (offset < 24)
- return !!(ioread8(&idio16gpio->reg->in0_7) & mask);
-
- return !!(ioread8(&idio16gpio->reg->in8_15) & (mask>>8));
+ return idio_16_get(idio16gpio->reg, offset);
}
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
@@ -113,12 +89,11 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- *bits = 0;
- if (*mask & GENMASK(23, 16))
- *bits |= (unsigned long)ioread8(&idio16gpio->reg->in0_7) << 16;
- if (*mask & GENMASK(31, 24))
- *bits |= (unsigned long)ioread8(&idio16gpio->reg->in8_15) << 24;
+ /* Reading output signals is not supported */
+ if (*mask & GENMASK(IDIO_16_NOUT - 1, 0))
+ return -EINVAL;
+ idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
return 0;
}
@@ -126,44 +101,16 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- const unsigned int mask = BIT(offset);
- unsigned long flags;
-
- if (offset > 15)
- return;
- raw_spin_lock_irqsave(&idio16gpio->lock, flags);
-
- if (value)
- idio16gpio->out_state |= mask;
- else
- idio16gpio->out_state &= ~mask;
-
- if (offset > 7)
- iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15);
- else
- iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7);
-
- raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
+ idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
}
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
- unsigned long flags;
- raw_spin_lock_irqsave(&idio16gpio->lock, flags);
-
- idio16gpio->out_state &= ~*mask;
- idio16gpio->out_state |= *mask & *bits;
-
- if (*mask & 0xFF)
- iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7);
- if ((*mask >> 8) & 0xFF)
- iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15);
-
- raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
+ idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
}
static void idio_16_irq_ack(struct irq_data *data)
@@ -296,7 +243,10 @@ static int idio_16_probe(struct device *dev, unsigned int id)
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
- idio16gpio->out_state = 0xFFFF;
+
+ /* FET off states are represented by bit values of "1" */
+ bitmap_fill(idio16gpio->state.out_state, IDIO_16_NOUT);
+ idio_16_state_init(&idio16gpio->state);
girq = &idio16gpio->chip.irq;
girq->chip = &idio_16_irqchip;
@@ -338,3 +288,4 @@ module_isa_driver(idio_16_driver, num_idio_16);
MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IDIO_16);
--
2.37.2
On Tue, Sep 13, 2022 at 07:19:03PM +0300, Andy Shevchenko wrote:
> On Sun, Sep 11, 2022 at 04:34:39PM -0400, William Breathitt Gray wrote:
> > The ACCES 104-IDIO-16 device is part of the ACCES IDIO-16 family, so the
> > idio-16 GPIO library module is selected and utilized to consolidate
> > code.
>
> > + /* Reading output signals is not supported */
> > + if (offset < IDIO_16_NOUT)
> > return -EINVAL;
>
> I see it's in the original code, but isn't it possible to cache and return
> cached value in such cases?
>
> --
> With Best Regards,
> Andy Shevchenko
I think you're right, we're already caching the outputs for the sake of
the signal set calls, so we can just return those respective output
values for the signal get calls. It should also save us some cycles by
avoiding the read8() calls for the outputs in the PCI-IDIO-16 case.
William Breathitt Gray
On Sun, Sep 11, 2022 at 04:34:38PM -0400, William Breathitt Gray wrote:
> Exposes consumer library functions to facilitate communication with
> devices within the ACCES IDIO-16 family such as the 104-IDIO-16 and
> the PCI-IDIO-16.
>
> A CONFIG_GPIO_IDIO_16 Kconfig option is introduced by this patch.
> Modules wanting access to these idio-16 library functions should select
> this Kconfig option and import the IDIO_16 symbol namespace.
...
> +int idio_16_get(struct idio_16 __iomem *const reg, const unsigned long offset)
> +{
> + const unsigned long mask = BIT(offset);
> +
> + if (offset < 8)
> + return !!(ioread8(®->out0_7) & mask);
> +
> + if (offset < 16)
> + return !!(ioread8(®->out8_15) & (mask >> 8));
> +
> + if (offset < 24)
> + return !!(ioread8(®->in0_7) & (mask >> 16));
> +
> + return !!(ioread8(®->in8_15) & (mask >> 24));
For the sake of robustness, since it's a library, I would do
if (offset < 32)
...
return -EINVAL;
> +}
> +EXPORT_SYMBOL_NS_GPL(idio_16_get, IDIO_16);
If there is not expected to be more than a single namespace, you may define it
just once for all via
#define DEFAULT_SYMBOL_NAMESPACE IDIO_16
And honestly, I would add also GPIO prefix to it, GPIO_IDIO_16.
...
> + if (*mask & GENMASK(7, 0))
> + bitmap_set_value8(bits, ioread8(®->out0_7), 0);
> + if (*mask & GENMASK(15, 8))
> + bitmap_set_value8(bits, ioread8(®->out8_15), 8);
> + if (*mask & GENMASK(23, 16))
> + bitmap_set_value8(bits, ioread8(®->in0_7), 16);
> + if (*mask & GENMASK(31, 24))
> + bitmap_set_value8(bits, ioread8(®->in8_15), 24);
So, the addresses of the ports are not expected to be continuous?
...
> +void idio_16_set(struct idio_16 __iomem *const reg,
> + struct idio_16_state *const state, const unsigned long offset,
> + const unsigned long value)
> +{
> + unsigned long flags;
> +
> + /* offsets greater than 15 are input-only signals */
> + if (offset > 15)
For the sake of consistency:
if (offset >= 16)
> + return;
> +
> + spin_lock_irqsave(&state->lock, flags);
> + if (value)
> + set_bit(offset, state->out_state);
> + else
> + clear_bit(offset, state->out_state);
assign_bit()
But I'm wondering why do you need the atomic bitops under the lock?
> + if (offset < 8)
> + iowrite8(bitmap_get_value8(state->out_state, 0), ®->out0_7);
> + else
> + iowrite8(bitmap_get_value8(state->out_state, 8), ®->out8_15);
> +
> + spin_unlock_irqrestore(&state->lock, flags);
> +}
...
> + for_each_set_clump8(offset, port_mask, mask, IDIO_16_NOUT) {
> + value = bitmap_get_value8(bits, offset);
> + out_state = bitmap_get_value8(state->out_state, offset);
> +
> + out_state = (out_state & ~port_mask) | (value & port_mask);
> + bitmap_set_value8(state->out_state, out_state, offset);
This looks like bitmap_replace(). It might require to redesign the flow a bit.
> + if (offset < 8)
> + iowrite8(out_state, ®->out0_7);
> + else
> + iowrite8(out_state, ®->out8_15);
> + }
...
> +static inline int idio_16_get_direction(const unsigned long offset)
> +{
> + return (offset < IDIO_16_NOUT) ? 0 : 1;
return (offset >= IDIO_16_NOUT) ? 1 : 0;
?
> +}
--
With Best Regards,
Andy Shevchenko
On Sun, Sep 11, 2022 at 04:34:39PM -0400, William Breathitt Gray wrote:
> The ACCES 104-IDIO-16 device is part of the ACCES IDIO-16 family, so the
> idio-16 GPIO library module is selected and utilized to consolidate
> code.
> + /* Reading output signals is not supported */
> + if (offset < IDIO_16_NOUT)
> return -EINVAL;
I see it's in the original code, but isn't it possible to cache and return
cached value in such cases?
--
With Best Regards,
Andy Shevchenko
On Tue, Sep 13, 2022 at 07:16:23PM +0300, Andy Shevchenko wrote:
> On Sun, Sep 11, 2022 at 04:34:38PM -0400, William Breathitt Gray wrote:
> > + if (*mask & GENMASK(7, 0))
> > + bitmap_set_value8(bits, ioread8(®->out0_7), 0);
> > + if (*mask & GENMASK(15, 8))
> > + bitmap_set_value8(bits, ioread8(®->out8_15), 8);
> > + if (*mask & GENMASK(23, 16))
> > + bitmap_set_value8(bits, ioread8(®->in0_7), 16);
> > + if (*mask & GENMASK(31, 24))
> > + bitmap_set_value8(bits, ioread8(®->in8_15), 24);
>
> So, the addresses of the ports are not expected to be continuous?
No, unfortunately the IDIO-16 devices allocate the FET outputs to byte
offsets 0 and 4 while the isolated inputs are allocated to byte offsets
1 and 5. I don't know the design reason for the split but that's the
reason I'm reading these addresses by byte rather than by word.
> > + return;
> > +
> > + spin_lock_irqsave(&state->lock, flags);
>
> > + if (value)
> > + set_bit(offset, state->out_state);
> > + else
> > + clear_bit(offset, state->out_state);
>
> assign_bit()
>
> But I'm wondering why do you need the atomic bitops under the lock?
I don't think atomic bitops are necessary in this case because of the
lock as you pointedly out, but I felt using these made the intention of
the code clearer. Is there a non-atomic version of assign_bit(), or do
you recommend I use bitwise operations directly here instead?
> > +static inline int idio_16_get_direction(const unsigned long offset)
> > +{
> > + return (offset < IDIO_16_NOUT) ? 0 : 1;
>
> return (offset >= IDIO_16_NOUT) ? 1 : 0;
>
> ?
I have no particular preference in this case, so I can switch this to
the >= version for consistency with the rest of the code.
Thanks,
William Breathitt Gray
Thu, Sep 15, 2022 at 11:46:13AM -0400, William Breathitt Gray kirjoitti:
> On Tue, Sep 13, 2022 at 07:16:23PM +0300, Andy Shevchenko wrote:
> > On Sun, Sep 11, 2022 at 04:34:38PM -0400, William Breathitt Gray wrote:
...
> > > + if (value)
> > > + set_bit(offset, state->out_state);
> > > + else
> > > + clear_bit(offset, state->out_state);
> >
> > assign_bit()
> >
> > But I'm wondering why do you need the atomic bitops under the lock?
>
> I don't think atomic bitops are necessary in this case because of the
> lock as you pointedly out, but I felt using these made the intention of
> the code clearer. Is there a non-atomic version of assign_bit(), or do
> you recommend I use bitwise operations directly here instead?
__assign_bit()
Hint: All __ prefixed bitops (for a single bit operation!) are considered
non-atomic. There are exceptions when no __-variant of op is present, but
it not the case here AFAICS.
--
With Best Regards,
Andy Shevchenko