Changes in v2:
- Utilize bitmap_zero macro to clear bits array
- Refactor bitwise operations and loop structure for clarity
- Implement set_multiple callback for PCIe-IDIO-16 driver
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 (8):
iio: stx104: Implement get_multiple callback
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 | 46 +++++++++++++++
drivers/gpio/gpio-104-idi-48.c | 46 +++++++++++++++
drivers/gpio/gpio-104-idio-16.c | 15 +++++
drivers/gpio/gpio-gpio-mm.c | 46 +++++++++++++++
drivers/gpio/gpio-pci-idio-16.c | 50 +++++++++++++++++
drivers/gpio/gpio-pcie-idio-24.c | 118 +++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpio-ws16c48.c | 46 +++++++++++++++
drivers/iio/adc/stx104.c | 11 ++++
8 files changed, 378 insertions(+)
--
2.16.2
The Apex Embedded Systems STX104 series of devices provides 4 TTL
compatible lines of inputs accessed via a single 4-bit port. Since four
input lines are acquired on a single port input read, the STX104 GPIO
driver may improve multiple input reads by utilizing a get_multiple
callback. This patch implements the stx104_gpio_get_multiple function
which serves as the respective get_multiple callback.
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/adc/stx104.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c
index 17b021f33180..0662ca199eb0 100644
--- a/drivers/iio/adc/stx104.c
+++ b/drivers/iio/adc/stx104.c
@@ -233,6 +233,16 @@ static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(inb(stx104gpio->base) & BIT(offset));
}
+static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
+
+ *bits = inb(stx104gpio->base);
+
+ return 0;
+}
+
static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -342,6 +352,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
stx104gpio->chip.direction_input = stx104_gpio_direction_input;
stx104gpio->chip.direction_output = stx104_gpio_direction_output;
stx104gpio->chip.get = stx104_gpio_get;
+ stx104gpio->chip.get_multiple = stx104_gpio_get_multiple;
stx104gpio->chip.set = stx104_gpio_set;
stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
stx104gpio->base = base[id] + 3;
--
2.16.2
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..5de5819e5156 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 & 0xFF0000)
+ *bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
+ if (*mask & 0xFF000000)
+ *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
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..ef43a37ccaff 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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+ unsigned long port_state;
+ const 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
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 | 118 +++++++++++++++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index f666e2e69074..8d2f4745a13c 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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+ unsigned long port_state;
+ const 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,66 @@ 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 gpio_mask;
+ unsigned long flags;
+ unsigned int out_state;
+ const 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 +513,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 = idio_24_gpio_set_multiple;
raw_spin_lock_init(&idio24gpio->lock);
--
2.16.2
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 | 46 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index bab3b94c5cbc..537b436c6e7f 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -182,6 +182,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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 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 +429,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
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 | 46 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index add859d59766..1beb6b0591ee 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -88,6 +88,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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 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(idi48egpio->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 +301,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
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 | 46 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index 746648244bf3..9f2e7d0b48f9 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -129,6 +129,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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 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 +428,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
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 | 46 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index 11ade5b288f8..d9be9ebe41cf 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -171,6 +171,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 mask_word;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 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 +313,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
Hi William,
I love your patch! Yet something to improve:
[auto build test ERROR on v4.16-rc4]
[also build test ERROR on next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Implement-get_multiple-for-ACCES-and-PC-104-drivers/20180317-224135
config: x86_64-randconfig-x016-201810 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/gpio/gpio-104-idi-48.c: In function 'idi_48_gpio_get_multiple':
>> drivers/gpio/gpio-104-idi-48.c:120:3: error: 'word_mask' undeclared (first use in this function); did you mean 'port_mask'?
word_mask = mask[word_index] & (port_mask << word_offset);
^~~~~~~~~
port_mask
drivers/gpio/gpio-104-idi-48.c:120:3: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/gpio/gpio-104-idi-48.c:127:20: error: 'idi48egpio' undeclared (first use in this function); did you mean 'idi48gpio'?
port_state = inb(idi48egpio->base + ports[i]);
^~~~~~~~~~
idi48gpio
drivers/gpio/gpio-104-idi-48.c:101:16: warning: unused variable 'mask_word' [-Wunused-variable]
unsigned long mask_word;
^~~~~~~~~
drivers/gpio/gpio-104-idi-48.c:94:28: warning: unused variable 'idi48gpio' [-Wunused-variable]
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
^~~~~~~~~
vim +120 drivers/gpio/gpio-104-idi-48.c
90
91 static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
92 unsigned long *bits)
93 {
94 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
95 size_t i;
96 const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
97 const unsigned int gpio_reg_size = 8;
98 unsigned int bits_offset;
99 size_t word_index;
100 unsigned int word_offset;
101 unsigned long mask_word;
102 const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
103 unsigned long port_state;
104
105 /* clear bits array to a clean slate */
106 bitmap_zero(bits, chip->ngpio);
107
108 /* get bits are evaluated a gpio port register at a time */
109 for (i = 0; i < ARRAY_SIZE(ports); i++) {
110 /* gpio offset in bits array */
111 bits_offset = i * gpio_reg_size;
112
113 /* word index for bits array */
114 word_index = BIT_WORD(bits_offset);
115
116 /* gpio offset within current word of bits array */
117 word_offset = bits_offset % BITS_PER_LONG;
118
119 /* mask of get bits for current gpio within current word */
> 120 word_mask = mask[word_index] & (port_mask << word_offset);
121 if (!word_mask) {
122 /* no get bits in this port so skip to next one */
123 continue;
124 }
125
126 /* read bits from current gpio port */
> 127 port_state = inb(idi48egpio->base + ports[i]);
128
129 /* store acquired bits at respective bits array offset */
130 bits[word_index] |= port_state << word_offset;
131 }
132
133 return 0;
134 }
135
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi William,
I love your patch! Yet something to improve:
[auto build test ERROR on v4.16-rc4]
[also build test ERROR on next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Implement-get_multiple-for-ACCES-and-PC-104-drivers/20180317-224135
config: i386-randconfig-a0-201810 (attached as .config)
compiler: gcc-4.9 (Debian 4.9.4-2) 4.9.4
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
drivers/gpio/gpio-104-dio-48e.c: In function 'dio48e_gpio_get_multiple':
>> drivers/gpio/gpio-104-dio-48e.c:214:3: error: 'word_mask' undeclared (first use in this function)
word_mask = mask[word_index] & (port_mask << word_offset);
^
drivers/gpio/gpio-104-dio-48e.c:214:3: note: each undeclared identifier is reported only once for each function it appears in
drivers/gpio/gpio-104-dio-48e.c:195:16: warning: unused variable 'mask_word' [-Wunused-variable]
unsigned long mask_word;
^
vim +/word_mask +214 drivers/gpio/gpio-104-dio-48e.c
184
185 static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
186 unsigned long *bits)
187 {
188 struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
189 size_t i;
190 const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
191 const unsigned int gpio_reg_size = 8;
192 unsigned int bits_offset;
193 size_t word_index;
194 unsigned int word_offset;
195 unsigned long mask_word;
196 const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
197 unsigned long port_state;
198
199 /* clear bits array to a clean slate */
200 bitmap_zero(bits, chip->ngpio);
201
202 /* get bits are evaluated a gpio port register at a time */
203 for (i = 0; i < ARRAY_SIZE(ports); i++) {
204 /* gpio offset in bits array */
205 bits_offset = i * gpio_reg_size;
206
207 /* word index for bits array */
208 word_index = BIT_WORD(bits_offset);
209
210 /* gpio offset within current word of bits array */
211 word_offset = bits_offset % BITS_PER_LONG;
212
213 /* mask of get bits for current gpio within current word */
> 214 word_mask = mask[word_index] & (port_mask << word_offset);
215 if (!word_mask) {
216 /* no get bits in this port so skip to next one */
217 continue;
218 }
219
220 /* read bits from current gpio port */
221 port_state = inb(dio48egpio->base + ports[i]);
222
223 /* store acquired bits at respective bits array offset */
224 bits[word_index] |= port_state << word_offset;
225 }
226
227 return 0;
228 }
229
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi William,
I love your patch! Perhaps something to improve:
[auto build test WARNING on v4.16-rc4]
[also build test WARNING on next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Implement-get_multiple-for-ACCES-and-PC-104-drivers/20180317-224135
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
drivers/gpio/gpio-pci-idio-16.c:139:17: sparse: undefined identifier 'word_mask'
drivers/gpio/gpio-pci-idio-16.c:140:22: sparse: undefined identifier 'word_mask'
>> drivers/gpio/gpio-pci-idio-16.c:120:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pci-idio-16.c:120:52: sparse: dereference of noderef expression
drivers/gpio/gpio-pci-idio-16.c:121:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pci-idio-16.c:121:51: sparse: dereference of noderef expression
>> drivers/gpio/gpio-pci-idio-16.c:139:17: sparse: generating address of non-lvalue (3)
drivers/gpio/gpio-pci-idio-16.c: In function 'idio_16_gpio_get_multiple':
drivers/gpio/gpio-pci-idio-16.c:139:3: error: 'word_mask' undeclared (first use in this function); did you mean 'port_mask'?
word_mask = mask[word_index] & (port_mask << word_offset);
^~~~~~~~~
port_mask
drivers/gpio/gpio-pci-idio-16.c:139:3: note: each undeclared identifier is reported only once for each function it appears in
drivers/gpio/gpio-pci-idio-16.c:146:24: warning: passing argument 1 of 'ioread8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
port_state = ioread8(ports + i);
^~~~~
In file included from arch/x86/include/asm/io.h:223:0,
from arch/x86/include/asm/realmode.h:15,
from arch/x86/include/asm/acpi.h:33,
from arch/x86/include/asm/fixmap.h:19,
from arch/x86/include/asm/apic.h:10,
from arch/x86/include/asm/smp.h:13,
from arch/x86/include/asm/mmzone_64.h:11,
from arch/x86/include/asm/mmzone.h:5,
from include/linux/mmzone.h:912,
from include/linux/gfp.h:6,
from include/linux/idr.h:16,
from include/linux/kernfs.h:14,
from include/linux/sysfs.h:16,
from include/linux/kobject.h:20,
from include/linux/device.h:16,
from drivers/gpio/gpio-pci-idio-16.c:16:
include/asm-generic/iomap.h:29:21: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
extern unsigned int ioread8(void __iomem *);
^~~~~~~
drivers/gpio/gpio-pci-idio-16.c:116:16: warning: unused variable 'mask_word' [-Wunused-variable]
unsigned long mask_word;
^~~~~~~~~
vim +120 drivers/gpio/gpio-pci-idio-16.c
106
107 static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
108 unsigned long *mask, unsigned long *bits)
109 {
110 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
111 size_t i;
112 const unsigned int gpio_reg_size = 8;
113 unsigned int bits_offset;
114 size_t word_index;
115 unsigned int word_offset;
116 unsigned long mask_word;
117 const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
118 unsigned long port_state;
119 const u8 __iomem ports[] = {
> 120 idio16gpio->reg->out0_7, idio16gpio->reg->out8_15,
121 idio16gpio->reg->in0_7, idio16gpio->reg->in8_15
122 };
123
124 /* clear bits array to a clean slate */
125 bitmap_zero(bits, chip->ngpio);
126
127 /* get bits are evaluated a gpio port register at a time */
128 for (i = 0; i < ARRAY_SIZE(ports); i++) {
129 /* gpio offset in bits array */
130 bits_offset = i * gpio_reg_size;
131
132 /* word index for bits array */
133 word_index = BIT_WORD(bits_offset);
134
135 /* gpio offset within current word of bits array */
136 word_offset = bits_offset % BITS_PER_LONG;
137
138 /* mask of get bits for current gpio within current word */
> 139 word_mask = mask[word_index] & (port_mask << word_offset);
140 if (!word_mask) {
141 /* no get bits in this port so skip to next one */
142 continue;
143 }
144
145 /* read bits from current gpio port */
146 port_state = ioread8(ports + i);
147
148 /* store acquired bits at respective bits array offset */
149 bits[word_index] |= port_state << word_offset;
150 }
151
152 return 0;
153 }
154
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi William,
I love your patch! Perhaps something to improve:
[auto build test WARNING on v4.16-rc4]
[also build test WARNING on next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Implement-get_multiple-for-ACCES-and-PC-104-drivers/20180317-224135
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
drivers/gpio/gpio-pcie-idio-24.c:231:17: sparse: undefined identifier 'word_mask'
drivers/gpio/gpio-pcie-idio-24.c:232:22: sparse: undefined identifier 'word_mask'
>> drivers/gpio/gpio-pcie-idio-24.c:331:43: sparse: incorrect type in argument 1 (different modifiers) @@ expected void [noderef] <asn:2>*<noident> @@ got unsigned char cvoid [noderef] <asn:2>*<noident> @@
drivers/gpio/gpio-pcie-idio-24.c:333:43: sparse: incorrect type in argument 2 (different modifiers) @@ expected void [noderef] <asn:2>*<noident> @@ got unsigned char cvoid [noderef] <asn:2>*<noident> @@
>> drivers/gpio/gpio-pcie-idio-24.c:518:30: sparse: incorrect type in assignment (incompatible argument 2 (different base types)) @@ expected void ( *set )( ... ) @@ got void ( *set )( ... ) @@
>> drivers/gpio/gpio-pcie-idio-24.c:210:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:210:52: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:211:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:211:54: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:212:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:212:52: sparse: dereference of noderef expression
>> drivers/gpio/gpio-pcie-idio-24.c:231:17: sparse: generating address of non-lvalue (3)
drivers/gpio/gpio-pcie-idio-24.c:306:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:306:52: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c:307:27: sparse: dereference of noderef expression
drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_gpio_get_multiple':
drivers/gpio/gpio-pcie-idio-24.c:231:3: error: 'word_mask' undeclared (first use in this function); did you mean 'port_mask'?
word_mask = mask[word_index] & (port_mask << word_offset);
^~~~~~~~~
port_mask
drivers/gpio/gpio-pcie-idio-24.c:231:3: note: each undeclared identifier is reported only once for each function it appears in
drivers/gpio/gpio-pcie-idio-24.c:239:25: warning: passing argument 1 of 'ioread8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
port_state = ioread8(ports + i);
^~~~~
In file included from arch/x86/include/asm/io.h:223:0,
from arch/x86/include/asm/realmode.h:15,
from arch/x86/include/asm/acpi.h:33,
from arch/x86/include/asm/fixmap.h:19,
from arch/x86/include/asm/apic.h:10,
from arch/x86/include/asm/smp.h:13,
from arch/x86/include/asm/mmzone_64.h:11,
from arch/x86/include/asm/mmzone.h:5,
from include/linux/mmzone.h:912,
from include/linux/gfp.h:6,
from include/linux/idr.h:16,
from include/linux/kernfs.h:14,
from include/linux/sysfs.h:16,
from include/linux/kobject.h:20,
from include/linux/device.h:16,
from drivers/gpio/gpio-pcie-idio-24.c:20:
include/asm-generic/iomap.h:29:21: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
extern unsigned int ioread8(void __iomem *);
^~~~~~~
drivers/gpio/gpio-pcie-idio-24.c:206:16: warning: unused variable 'mask_word' [-Wunused-variable]
unsigned long mask_word;
^~~~~~~~~
drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_gpio_set_multiple':
drivers/gpio/gpio-pcie-idio-24.c:302:16: error: redeclaration of 'gpio_mask' with no linkage
unsigned long gpio_mask;
^~~~~~~~~
drivers/gpio/gpio-pcie-idio-24.c:299:16: note: previous declaration of 'gpio_mask' was here
unsigned long gpio_mask;
^~~~~~~~~
drivers/gpio/gpio-pcie-idio-24.c:331:23: warning: passing argument 1 of 'ioread8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
out_state = ioread8(ports + i) & ~gpio_mask;
^~~~~
In file included from arch/x86/include/asm/io.h:223:0,
from arch/x86/include/asm/realmode.h:15,
from arch/x86/include/asm/acpi.h:33,
from arch/x86/include/asm/fixmap.h:19,
from arch/x86/include/asm/apic.h:10,
from arch/x86/include/asm/smp.h:13,
from arch/x86/include/asm/mmzone_64.h:11,
from arch/x86/include/asm/mmzone.h:5,
from include/linux/mmzone.h:912,
from include/linux/gfp.h:6,
from include/linux/idr.h:16,
from include/linux/kernfs.h:14,
from include/linux/sysfs.h:16,
from include/linux/kobject.h:20,
from include/linux/device.h:16,
from drivers/gpio/gpio-pcie-idio-24.c:20:
include/asm-generic/iomap.h:29:21: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
extern unsigned int ioread8(void __iomem *);
^~~~~~~
drivers/gpio/gpio-pcie-idio-24.c:333:23: warning: passing argument 2 of 'iowrite8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
iowrite8(out_state, ports + i);
^~~~~
In file included from arch/x86/include/asm/io.h:223:0,
from arch/x86/include/asm/realmode.h:15,
from arch/x86/include/asm/acpi.h:33,
from arch/x86/include/asm/fixmap.h:19,
from arch/x86/include/asm/apic.h:10,
from arch/x86/include/asm/smp.h:13,
from arch/x86/include/asm/mmzone_64.h:11,
from arch/x86/include/asm/mmzone.h:5,
from include/linux/mmzone.h:912,
from include/linux/gfp.h:6,
from include/linux/idr.h:16,
from include/linux/kernfs.h:14,
from include/linux/sysfs.h:16,
from include/linux/kobject.h:20,
from include/linux/device.h:16,
from drivers/gpio/gpio-pcie-idio-24.c:20:
include/asm-generic/iomap.h:39:13: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
extern void iowrite8(u8, void __iomem *);
^~~~~~~~
drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_probe':
drivers/gpio/gpio-pcie-idio-24.c:518:23: error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
idio24gpio->chip.set = idio_24_gpio_set_multiple;
^
cc1: some warnings being treated as errors
vim +331 drivers/gpio/gpio-pcie-idio-24.c
196
197 static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
198 unsigned long *mask, unsigned long *bits)
199 {
200 struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
201 size_t i;
202 const unsigned int gpio_reg_size = 8;
203 unsigned int bits_offset;
204 size_t word_index;
205 unsigned int word_offset;
206 unsigned long mask_word;
207 const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
208 unsigned long port_state;
209 const u8 __iomem ports[] = {
> 210 idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
211 idio24gpio->reg->out16_23, idio24gpio->reg->in0_7,
212 idio24gpio->reg->in8_15, idio24gpio->reg->in16_23
213 };
214 const unsigned long out_mode_mask = BIT(1);
215
216 /* clear bits array to a clean slate */
217 bitmap_zero(bits, chip->ngpio);
218
219 /* get bits are evaluated a gpio port register at a time */
220 for (i = 0; i < ARRAY_SIZE(ports); i++) {
221 /* gpio offset in bits array */
222 bits_offset = i * gpio_reg_size;
223
224 /* word index for bits array */
225 word_index = BIT_WORD(bits_offset);
226
227 /* gpio offset within current word of bits array */
228 word_offset = bits_offset % BITS_PER_LONG;
229
230 /* mask of get bits for current gpio within current word */
> 231 word_mask = mask[word_index] & (port_mask << word_offset);
232 if (!word_mask) {
233 /* no get bits in this port so skip to next one */
234 continue;
235 }
236
237 /* read bits from current gpio port (port 6 is TTL GPIO) */
238 if (i < 6)
239 port_state = ioread8(ports + i);
240 else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
241 port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
242 else
243 port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
244
245 /* store acquired bits at respective bits array offset */
246 bits[word_index] |= port_state << word_offset;
247 }
248
249 return 0;
250 }
251
252 static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
253 int value)
254 {
255 struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
256 const unsigned long out_mode_mask = BIT(1);
257 void __iomem *base;
258 const unsigned int mask = BIT(offset % 8);
259 unsigned long flags;
260 unsigned int out_state;
261
262 /* Isolated Inputs */
263 if (offset > 23 && offset < 48)
264 return;
265
266 /* TTL/CMOS Inputs */
267 if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
268 return;
269
270 /* TTL/CMOS Outputs */
271 if (offset > 47)
272 base = &idio24gpio->reg->ttl_out0_7;
273 /* FET Outputs */
274 else if (offset > 15)
275 base = &idio24gpio->reg->out16_23;
276 else if (offset > 7)
277 base = &idio24gpio->reg->out8_15;
278 else
279 base = &idio24gpio->reg->out0_7;
280
281 raw_spin_lock_irqsave(&idio24gpio->lock, flags);
282
283 if (value)
284 out_state = ioread8(base) | mask;
285 else
286 out_state = ioread8(base) & ~mask;
287
288 iowrite8(out_state, base);
289
290 raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
291 }
292
293 static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
294 unsigned long *mask, unsigned long *bits)
295 {
296 struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
297 size_t i;
298 unsigned long bits_offset;
299 unsigned long gpio_mask;
300 const unsigned int gpio_reg_size = 8;
301 const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
302 unsigned long gpio_mask;
303 unsigned long flags;
304 unsigned int out_state;
305 const u8 __iomem ports[] = {
306 idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
307 idio24gpio->reg->out16_23
308 };
309 const unsigned long out_mode_mask = BIT(1);
310 const unsigned int ttl_offset = 48;
311 const size_t ttl_i = BIT_WORD(ttl_offset);
312 const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
313 const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
314 const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
315
316 /* set bits are processed a gpio port register at a time */
317 for (i = 0; i < ARRAY_SIZE(ports); i++) {
318 /* gpio offset in bits array */
319 bits_offset = i * gpio_reg_size;
320
321 /* check if any set bits for current port */
322 gpio_mask = (*mask >> bits_offset) & port_mask;
323 if (!gpio_mask) {
324 /* no set bits for this port so move on to next port */
325 continue;
326 }
327
328 raw_spin_lock_irqsave(&idio24gpio->lock, flags);
329
330 /* process output lines */
> 331 out_state = ioread8(ports + i) & ~gpio_mask;
332 out_state |= (*bits >> bits_offset) & gpio_mask;
333 iowrite8(out_state, ports + i);
334
335 raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
336 }
337
338 /* check if setting TTL lines and if they are in output mode */
339 if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
340 return;
341
342 /* handle TTL output */
343 raw_spin_lock_irqsave(&idio24gpio->lock, flags);
344
345 /* process output lines */
346 out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
347 out_state |= ttl_bits;
348 iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
349
350 raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
351 }
352
353 static void idio_24_irq_ack(struct irq_data *data)
354 {
355 }
356
357 static void idio_24_irq_mask(struct irq_data *data)
358 {
359 struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
360 struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
361 unsigned long flags;
362 const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
363 unsigned char new_irq_mask;
364 const unsigned long bank_offset = bit_offset/8 * 8;
365 unsigned char cos_enable_state;
366
367 raw_spin_lock_irqsave(&idio24gpio->lock, flags);
368
369 idio24gpio->irq_mask &= BIT(bit_offset);
370 new_irq_mask = idio24gpio->irq_mask >> bank_offset;
371
372 if (!new_irq_mask) {
373 cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
374
375 /* Disable Rising Edge detection */
376 cos_enable_state &= ~BIT(bank_offset);
377 /* Disable Falling Edge detection */
378 cos_enable_state &= ~BIT(bank_offset + 4);
379
380 iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
381 }
382
383 raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
384 }
385
386 static void idio_24_irq_unmask(struct irq_data *data)
387 {
388 struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
389 struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
390 unsigned long flags;
391 unsigned char prev_irq_mask;
392 const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
393 const unsigned long bank_offset = bit_offset/8 * 8;
394 unsigned char cos_enable_state;
395
396 raw_spin_lock_irqsave(&idio24gpio->lock, flags);
397
398 prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
399 idio24gpio->irq_mask |= BIT(bit_offset);
400
401 if (!prev_irq_mask) {
402 cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
403
404 /* Enable Rising Edge detection */
405 cos_enable_state |= BIT(bank_offset);
406 /* Enable Falling Edge detection */
407 cos_enable_state |= BIT(bank_offset + 4);
408
409 iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
410 }
411
412 raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
413 }
414
415 static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
416 {
417 /* The only valid irq types are none and both-edges */
418 if (flow_type != IRQ_TYPE_NONE &&
419 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
420 return -EINVAL;
421
422 return 0;
423 }
424
425 static struct irq_chip idio_24_irqchip = {
426 .name = "pcie-idio-24",
427 .irq_ack = idio_24_irq_ack,
428 .irq_mask = idio_24_irq_mask,
429 .irq_unmask = idio_24_irq_unmask,
430 .irq_set_type = idio_24_irq_set_type
431 };
432
433 static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
434 {
435 struct idio_24_gpio *const idio24gpio = dev_id;
436 unsigned long irq_status;
437 struct gpio_chip *const chip = &idio24gpio->chip;
438 unsigned long irq_mask;
439 int gpio;
440
441 raw_spin_lock(&idio24gpio->lock);
442
443 /* Read Change-Of-State status */
444 irq_status = ioread32(&idio24gpio->reg->cos0_7);
445
446 raw_spin_unlock(&idio24gpio->lock);
447
448 /* Make sure our device generated IRQ */
449 if (!irq_status)
450 return IRQ_NONE;
451
452 /* Handle only unmasked IRQ */
453 irq_mask = idio24gpio->irq_mask & irq_status;
454
455 for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
456 generic_handle_irq(irq_find_mapping(chip->irq.domain,
457 gpio + 24));
458
459 raw_spin_lock(&idio24gpio->lock);
460
461 /* Clear Change-Of-State status */
462 iowrite32(irq_status, &idio24gpio->reg->cos0_7);
463
464 raw_spin_unlock(&idio24gpio->lock);
465
466 return IRQ_HANDLED;
467 }
468
469 #define IDIO_24_NGPIO 56
470 static const char *idio_24_names[IDIO_24_NGPIO] = {
471 "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
472 "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
473 "OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
474 "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
475 "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
476 "IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
477 "TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
478 };
479
480 static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
481 {
482 struct device *const dev = &pdev->dev;
483 struct idio_24_gpio *idio24gpio;
484 int err;
485 const size_t pci_bar_index = 2;
486 const char *const name = pci_name(pdev);
487
488 idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
489 if (!idio24gpio)
490 return -ENOMEM;
491
492 err = pcim_enable_device(pdev);
493 if (err) {
494 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
495 return err;
496 }
497
498 err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
499 if (err) {
500 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
501 return err;
502 }
503
504 idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
505
506 idio24gpio->chip.label = name;
507 idio24gpio->chip.parent = dev;
508 idio24gpio->chip.owner = THIS_MODULE;
509 idio24gpio->chip.base = -1;
510 idio24gpio->chip.ngpio = IDIO_24_NGPIO;
511 idio24gpio->chip.names = idio_24_names;
512 idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
513 idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
514 idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
515 idio24gpio->chip.get = idio_24_gpio_get;
516 idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
517 idio24gpio->chip.set = idio_24_gpio_set;
> 518 idio24gpio->chip.set = idio_24_gpio_set_multiple;
519
520 raw_spin_lock_init(&idio24gpio->lock);
521
522 /* Software board reset */
523 iowrite8(0, &idio24gpio->reg->soft_reset);
524
525 err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
526 if (err) {
527 dev_err(dev, "GPIO registering failed (%d)\n", err);
528 return err;
529 }
530
531 err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
532 handle_edge_irq, IRQ_TYPE_NONE);
533 if (err) {
534 dev_err(dev, "Could not add irqchip (%d)\n", err);
535 return err;
536 }
537
538 err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
539 name, idio24gpio);
540 if (err) {
541 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
542 return err;
543 }
544
545 return 0;
546 }
547
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation