2018-03-22 13:06:02

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 0/7] Implement get_multiple for ACCES and PC/104 drivers

Changes in v4:
- Remove STX104 patch as it has been picked up through the IIO tree
- Utilize GENMASK for 104-IDIO-16 patch to avoid long literal bitmasks
- Fix off-by-one errors for port_mask initializations
- Add end comma to ports arrays initializations for consistency

This patchset implements get_multiple callbacks for the PC104 GPIO
drivers as well as the PCI-IDIO-16 and PCIe-IDIO-24 GPIO drivers. These
devices all acquire the multiple input lines with a single read, so
utilizing the get_multiple callback can provide improvement for those
users who regularly access multiple input lines.

While developing this patchset I noticed many of these devices make use
of Intel 8255 compatible interfaces for their I/O. I may write a generic
8255 GPIO driver in the future to reduce some of the redundant code I
see pop among the drivers for these devices.

William Breathitt Gray (7):
gpio: 104-idio-16: Implement get_multiple callback
gpio: pci-idio-16: Implement get_multiple callback
gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks
gpio: 104-dio-48e: Implement get_multiple callback
gpio: 104-idi-48: Implement get_multiple callback
gpio: gpio-mm: Implement get_multiple callback
gpio: ws16c48: Implement get_multiple callback

drivers/gpio/gpio-104-dio-48e.c | 47 ++++++++++++++++
drivers/gpio/gpio-104-idi-48.c | 47 ++++++++++++++++
drivers/gpio/gpio-104-idio-16.c | 15 +++++
drivers/gpio/gpio-gpio-mm.c | 47 ++++++++++++++++
drivers/gpio/gpio-pci-idio-16.c | 50 +++++++++++++++++
drivers/gpio/gpio-pcie-idio-24.c | 117 +++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpio-ws16c48.c | 47 ++++++++++++++++
7 files changed, 370 insertions(+)

--
2.16.2



2018-03-22 13:02:54

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 6/7] gpio: gpio-mm: Implement get_multiple callback

The Diamond Systems GPIO-MM series of devices contain two 82C55A
devices, which each feature three 8-bit ports of I/O. Since eight input
lines are acquired on a single port input read, the GPIO-MM GPIO driver
may improve multiple input reads by utilizing a get_multiple callback.
This patch implements the gpiomm_gpio_get_multiple function which serves
as the respective get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-gpio-mm.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index 11ade5b288f8..d496cc56c2a2 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -14,6 +14,7 @@
* This driver supports the following Diamond Systems devices: GPIO-MM and
* GPIO-MM-12.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -171,6 +172,51 @@ static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(port_state & mask);
}

+static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(gpiommgpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -268,6 +314,7 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
gpiommgpio->chip.get = gpiomm_gpio_get;
+ gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
gpiommgpio->chip.set = gpiomm_gpio_set;
gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
gpiommgpio->base = base[id];
--
2.16.2


2018-03-22 13:03:33

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 7/7] gpio: ws16c48: Implement get_multiple callback

The WinSystems WS16C48 device provides 48 lines of digital I/O accessed
via six 8-bit ports. Since eight input lines are acquired on a single
port input read, the WS16C48 GPIO driver may improve multiple input
reads by utilizing a get_multiple callback. This patch implements the
ws16c48_gpio_get_multiple function which serves as the respective
get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-ws16c48.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index 746648244bf3..c7028eb0b8e1 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -129,6 +130,51 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(port_state & mask);
}

+static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned int gpio_reg_size = 8;
+ size_t i;
+ const size_t num_ports = chip->ngpio / gpio_reg_size;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < num_ports; i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(ws16c48gpio->base + i);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
@@ -383,6 +429,7 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
ws16c48gpio->chip.get = ws16c48_gpio_get;
+ ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
ws16c48gpio->chip.set = ws16c48_gpio_set;
ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
ws16c48gpio->base = base[id];
--
2.16.2


2018-03-22 13:05:26

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 4/7] gpio: 104-dio-48e: Implement get_multiple callback

The ACCES I/O 104-DIO-48E series of devices contain two Programmable
Peripheral Interface (PPI) chips of type 82C55, which each feature three
8-bit ports of I/O. Since eight input lines are acquired on a single
port input read, the 104-DIO-48E GPIO driver may improve multiple input
reads by utilizing a get_multiple callback. This patch implements the
dio48e_gpio_get_multiple function which serves as the respective
get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-104-dio-48e.c | 47 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index bab3b94c5cbc..31e22c93e844 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -14,6 +14,7 @@
* This driver supports the following ACCES devices: 104-DIO-48E and
* 104-DIO-24E.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -182,6 +183,51 @@ static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(port_state & mask);
}

+static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(dio48egpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
@@ -384,6 +430,7 @@ static int dio48e_probe(struct device *dev, unsigned int id)
dio48egpio->chip.direction_input = dio48e_gpio_direction_input;
dio48egpio->chip.direction_output = dio48e_gpio_direction_output;
dio48egpio->chip.get = dio48e_gpio_get;
+ dio48egpio->chip.get_multiple = dio48e_gpio_get_multiple;
dio48egpio->chip.set = dio48e_gpio_set;
dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple;
dio48egpio->base = base[id];
--
2.16.2


2018-03-22 13:05:54

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 2/7] gpio: pci-idio-16: Implement get_multiple callback

The ACCES I/O PCI-IDIO-16 series of devices provides 16
optically-isolated digital inputs accessed via two 8-bit ports. Since
eight input lines are acquired on a single port input read, the
PCI-IDIO-16 GPIO driver may improve multiple input reads by utilizing a
get_multiple callback. This patch implements the
idio_16_gpio_get_multiple function which serves as the respective
get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-pci-idio-16.c | 50 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)

diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 57d1b7fbf07b..1948724d8c36 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -103,6 +104,54 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
}

+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);
+ size_t i;
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+ u8 __iomem ports[] = {
+ idio16gpio->reg->out0_7, idio16gpio->reg->out8_15,
+ idio16gpio->reg->in0_7, idio16gpio->reg->in8_15,
+ };
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = ioread8(ports + i);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -299,6 +348,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
+ 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;

--
2.16.2


2018-03-22 13:05:55

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 3/7] gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks

The ACCES I/O PCIe-IDIO-24 series of devices provides 24
optically-isolated digital I/O accessed via six 8-bit ports. Since eight
input lines are acquired on a single port input read -- and similarly
eight output lines are set on a single port output write -- the
PCIe-IDIO-24 GPIO driver may improve multiple I/O reads/writes by
utilizing a get_multiple/set_multiple callbacks. This patch implements
the idio_24_gpio_get_multiple function which serves as the respective
get_multiple callback, and implements the idio_24_gpio_set_multiple
function which serves as the respective set_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-pcie-idio-24.c | 117 +++++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)

diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index f666e2e69074..835607ecf658 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -15,6 +15,7 @@
* This driver supports the following ACCES devices: PCIe-IDIO-24,
* PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -193,6 +194,61 @@ static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
}

+static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+ size_t i;
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+ u8 __iomem ports[] = {
+ idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+ idio24gpio->reg->out16_23, idio24gpio->reg->in0_7,
+ idio24gpio->reg->in8_15, idio24gpio->reg->in16_23,
+ };
+ const unsigned long out_mode_mask = BIT(1);
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port (port 6 is TTL GPIO) */
+ if (i < 6)
+ port_state = ioread8(ports + i);
+ else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
+ port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
+ else
+ port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -234,6 +290,65 @@ static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
}

+static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+ size_t i;
+ unsigned long bits_offset;
+ unsigned long gpio_mask;
+ const unsigned int gpio_reg_size = 8;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+ unsigned long flags;
+ unsigned int out_state;
+ u8 __iomem ports[] = {
+ idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+ idio24gpio->reg->out16_23
+ };
+ const unsigned long out_mode_mask = BIT(1);
+ const unsigned int ttl_offset = 48;
+ const size_t ttl_i = BIT_WORD(ttl_offset);
+ const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
+ const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
+ const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
+
+ /* set bits are processed a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* check if any set bits for current port */
+ gpio_mask = (*mask >> bits_offset) & port_mask;
+ if (!gpio_mask) {
+ /* no set bits for this port so move on to next port */
+ continue;
+ }
+
+ raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+ /* process output lines */
+ out_state = ioread8(ports + i) & ~gpio_mask;
+ out_state |= (*bits >> bits_offset) & gpio_mask;
+ iowrite8(out_state, ports + i);
+
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ }
+
+ /* check if setting TTL lines and if they are in output mode */
+ if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
+ return;
+
+ /* handle TTL output */
+ raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+ /* process output lines */
+ out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
+ out_state |= ttl_bits;
+ iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
+
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+}
+
static void idio_24_irq_ack(struct irq_data *data)
{
}
@@ -397,7 +512,9 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
idio24gpio->chip.get = idio_24_gpio_get;
+ idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
idio24gpio->chip.set = idio_24_gpio_set;
+ idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;

raw_spin_lock_init(&idio24gpio->lock);

--
2.16.2


2018-03-22 13:06:08

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 5/7] gpio: 104-idi-48: Implement get_multiple callback

The ACCES I/O 104-IDI-48 series of devices provides 48
optically-isolated inputs accessed via six 8-bit ports. Since eight
input lines are acquired on a single port input read, the 104-IDI-48
GPIO driver may improve multiple input reads by utilizing a get_multiple
callback. This patch implements the idi_48_gpio_get_multiple function
which serves as the respective get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-104-idi-48.c | 47 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)

diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index add859d59766..f35632609379 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -14,6 +14,7 @@
* This driver supports the following ACCES devices: 104-IDI-48A,
* 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -88,6 +89,51 @@ static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset)
return 0;
}

+static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(idi48gpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idi_48_irq_ack(struct irq_data *data)
{
}
@@ -256,6 +302,7 @@ static int idi_48_probe(struct device *dev, unsigned int id)
idi48gpio->chip.get_direction = idi_48_gpio_get_direction;
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
idi48gpio->chip.get = idi_48_gpio_get;
+ idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple;
idi48gpio->base = base[id];

raw_spin_lock_init(&idi48gpio->lock);
--
2.16.2


2018-03-22 13:06:33

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v4 1/7] gpio: 104-idio-16: Implement get_multiple callback

The ACCES I/O 104-IDIO-16 series of devices provides 16
optically-isolated digital inputs accessed via two 8-bit ports. Since
eight input lines are acquired on a single port input read, the
104-IDIO-16 GPIO driver may improve multiple input reads by utilizing a
get_multiple callback. This patch implements the
idio_16_gpio_get_multiple function which serves as the respective
get_multiple callback.

Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/gpio-104-idio-16.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 2f16638a0589..96700308c1e3 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -90,6 +90,20 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(inb(idio16gpio->base + 5) & (mask>>8));
}

+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);
+
+ *bits = 0;
+ if (*mask & GENMASK(23, 16))
+ *bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
+ if (*mask & GENMASK(31, 24))
+ *bits |= (unsigned long)inb(idio16gpio->base + 5) << 24;
+
+ return 0;
+}
+
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
@@ -244,6 +258,7 @@ static int idio_16_probe(struct device *dev, unsigned int id)
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
+ 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->base = base[id];
--
2.16.2


2018-03-26 08:11:57

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 1/7] gpio: 104-idio-16: Implement get_multiple callback

On Thu, Mar 22, 2018 at 1:59 PM, William Breathitt Gray
<[email protected]> wrote:

> The ACCES I/O 104-IDIO-16 series of devices provides 16
> optically-isolated digital inputs accessed via two 8-bit ports. Since
> eight input lines are acquired on a single port input read, the
> 104-IDIO-16 GPIO driver may improve multiple input reads by utilizing a
> get_multiple callback. This patch implements the
> idio_16_gpio_get_multiple function which serves as the respective
> get_multiple callback.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Patch applied.

Yours,
Linus Walleij

2018-03-26 08:25:41

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 2/7] gpio: pci-idio-16: Implement get_multiple callback

On Thu, Mar 22, 2018 at 1:59 PM, William Breathitt Gray
<[email protected]> wrote:

> The ACCES I/O PCI-IDIO-16 series of devices provides 16
> optically-isolated digital inputs accessed via two 8-bit ports. Since
> eight input lines are acquired on a single port input read, the
> PCI-IDIO-16 GPIO driver may improve multiple input reads by utilizing a
> get_multiple callback. This patch implements the
> idio_16_gpio_get_multiple function which serves as the respective
> get_multiple callback.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Patch applied.

Yours,
Linus Walleij

2018-03-26 08:28:35

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 5/7] gpio: 104-idi-48: Implement get_multiple callback

On Thu, Mar 22, 2018 at 2:00 PM, William Breathitt Gray
<[email protected]> wrote:

> The ACCES I/O 104-IDI-48 series of devices provides 48
> optically-isolated inputs accessed via six 8-bit ports. Since eight
> input lines are acquired on a single port input read, the 104-IDI-48
> GPIO driver may improve multiple input reads by utilizing a get_multiple
> callback. This patch implements the idi_48_gpio_get_multiple function
> which serves as the respective get_multiple callback.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Patch applied.

Yours,
Linus Walleij

2018-03-26 08:29:23

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 6/7] gpio: gpio-mm: Implement get_multiple callback

On Thu, Mar 22, 2018 at 2:00 PM, William Breathitt Gray
<[email protected]> wrote:

> The Diamond Systems GPIO-MM series of devices contain two 82C55A
> devices, which each feature three 8-bit ports of I/O. Since eight input
> lines are acquired on a single port input read, the GPIO-MM GPIO driver
> may improve multiple input reads by utilizing a get_multiple callback.
> This patch implements the gpiomm_gpio_get_multiple function which serves
> as the respective get_multiple callback.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Patch applied.

Yours,
Linus Walleij

2018-03-26 08:29:52

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 3/7] gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks

On Thu, Mar 22, 2018 at 2:00 PM, William Breathitt Gray
<[email protected]> wrote:

> The ACCES I/O PCIe-IDIO-24 series of devices provides 24
> optically-isolated digital I/O accessed via six 8-bit ports. Since eight
> input lines are acquired on a single port input read -- and similarly
> eight output lines are set on a single port output write -- the
> PCIe-IDIO-24 GPIO driver may improve multiple I/O reads/writes by
> utilizing a get_multiple/set_multiple callbacks. This patch implements
> the idio_24_gpio_get_multiple function which serves as the respective
> get_multiple callback, and implements the idio_24_gpio_set_multiple
> function which serves as the respective set_multiple callback.
>
> Signed-off-by: William Breathitt Gray <[email protected]>

Patch applied.

Yours,
Linus Walleij

2018-03-26 08:33:08

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] Implement get_multiple for ACCES and PC/104 drivers

On Thu, Mar 22, 2018 at 1:59 PM, William Breathitt Gray
<[email protected]> wrote:

> Changes in v4:

I applied the v4 patch set so we get some rotation in linux-next before
the merge window.

If reviewers still find smallish issues they can certainly be fixed in follow-up
patches.

> This patchset implements get_multiple callbacks for the PC104 GPIO
> drivers as well as the PCI-IDIO-16 and PCIe-IDIO-24 GPIO drivers. These
> devices all acquire the multiple input lines with a single read, so
> utilizing the get_multiple callback can provide improvement for those
> users who regularly access multiple input lines.

Did you use the userspace chardev for some interesting experiments
with your set-ups?

I tend to think of your applications as pretty interesting for industry
peers, and a good proof-of-concept for the userspace API.

> While developing this patchset I noticed many of these devices make use
> of Intel 8255 compatible interfaces for their I/O. I may write a generic
> 8255 GPIO driver in the future to reduce some of the redundant code I
> see pop among the drivers for these devices.

Oh!
This guy?
https://en.wikipedia.org/wiki/Intel_8255

Yeah that makes all kind of sense. We are living with the legacy
of the first PCs every day when it comes own to EISA & friends...

Yours,
Linus Walleij

2018-03-26 13:29:04

by William Breathitt Gray

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] Implement get_multiple for ACCES and PC/104 drivers

On Mon, Mar 26, 2018 at 10:31:41AM +0200, Linus Walleij wrote:
>On Thu, Mar 22, 2018 at 1:59 PM, William Breathitt Gray
><[email protected]> wrote:
>
>> Changes in v4:
>
>I applied the v4 patch set so we get some rotation in linux-next before
>the merge window.
>
>If reviewers still find smallish issues they can certainly be fixed in follow-up
>patches.
>
>> This patchset implements get_multiple callbacks for the PC104 GPIO
>> drivers as well as the PCI-IDIO-16 and PCIe-IDIO-24 GPIO drivers. These
>> devices all acquire the multiple input lines with a single read, so
>> utilizing the get_multiple callback can provide improvement for those
>> users who regularly access multiple input lines.
>
>Did you use the userspace chardev for some interesting experiments
>with your set-ups?
>
>I tend to think of your applications as pretty interesting for industry
>peers, and a good proof-of-concept for the userspace API.

I use these PC104 drivers in several semi-realtime critical
applications, so latency has always been a big concern for me. The
switch from sysfs file I/O to chardev ioctl had a great improvement in
the reduction of latency, which I was very happy to utilize.

Since the next big point of latency in my systems have been port I/O,
taking advantage of the get_multiple/set_multiple functionality is my
next goal in optimization. I have a new system coming out soon that uses
the 104-IDI-48 for input -- it'll be interesting to see the performance
improvements of acquiring the entire input port at a time, rather than
every GPIO line individually.

>
>> While developing this patchset I noticed many of these devices make use
>> of Intel 8255 compatible interfaces for their I/O. I may write a generic
>> 8255 GPIO driver in the future to reduce some of the redundant code I
>> see pop among the drivers for these devices.
>
>Oh!
>This guy?
>https://en.wikipedia.org/wiki/Intel_8255
>
>Yeah that makes all kind of sense. We are living with the legacy
>of the first PCs every day when it comes own to EISA & friends...

That's the one! I see this interface a lot in the GPIO devices I handle.
It's very popular, which I believe is a testament to the popularity of
the IBM PC.

Nowadays, most devices I encounter implement this interface in an FPGA,
but I do occasionally come across the actual Intel 82C55 chip, such as
on the 104-DIO-48E; it's fascinating to see this physical chip still
popping up on circuit boards from time to time.

I would like to make a generic driver for this interface that will allow
it to be used both in the port I/O and memory I/O drivers out there.
Since I don't want to introduce too many changes in a single patchset
I've postponed it for now to get get_multiple support merged in for
these drivers, but I'll hopefully have something like a gpio-i8255
driver submitted soon enough.

William Breathitt Gray

>
>Yours,
>Linus Walleij