summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-pcie-idio-24.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-pcie-idio-24.c')
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c751
1 files changed, 294 insertions, 457 deletions
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index 52f1647a46fd..80c0ba0afa67 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -3,466 +3,279 @@
* GPIO driver for the ACCES PCIe-IDIO-24 family
* Copyright (C) 2018 William Breathitt Gray
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* 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/bits.h>
#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio/driver.h>
-#include <linux/interrupt.h>
-#include <linux/irqdesc.h>
+#include <linux/err.h>
+#include <linux/gpio/regmap.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
-/**
- * struct idio_24_gpio_reg - GPIO device registers structure
- * @out0_7: Read: FET Outputs 0-7
- * Write: FET Outputs 0-7
- * @out8_15: Read: FET Outputs 8-15
- * Write: FET Outputs 8-15
- * @out16_23: Read: FET Outputs 16-23
- * Write: FET Outputs 16-23
- * @ttl_out0_7: Read: TTL/CMOS Outputs 0-7
- * Write: TTL/CMOS Outputs 0-7
- * @in0_7: Read: Isolated Inputs 0-7
- * Write: Reserved
- * @in8_15: Read: Isolated Inputs 8-15
- * Write: Reserved
- * @in16_23: Read: Isolated Inputs 16-23
- * Write: Reserved
- * @ttl_in0_7: Read: TTL/CMOS Inputs 0-7
- * Write: Reserved
- * @cos0_7: Read: COS Status Inputs 0-7
- * Write: COS Clear Inputs 0-7
- * @cos8_15: Read: COS Status Inputs 8-15
- * Write: COS Clear Inputs 8-15
- * @cos16_23: Read: COS Status Inputs 16-23
- * Write: COS Clear Inputs 16-23
- * @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7
- * Write: COS Clear TTL/CMOS 0-7
- * @ctl: Read: Control Register
- * Write: Control Register
- * @reserved: Read: Reserved
- * Write: Reserved
- * @cos_enable: Read: COS Enable
- * Write: COS Enable
- * @soft_reset: Read: IRQ Output Pin Status
- * Write: Software Board Reset
+/*
+ * PLX PEX8311 PCI LCS_INTCSR Interrupt Control/Status
+ *
+ * Bit: Description
+ * 0: Enable Interrupt Sources (Bit 0)
+ * 1: Enable Interrupt Sources (Bit 1)
+ * 2: Generate Internal PCI Bus Internal SERR# Interrupt
+ * 3: Mailbox Interrupt Enable
+ * 4: Power Management Interrupt Enable
+ * 5: Power Management Interrupt
+ * 6: Slave Read Local Data Parity Check Error Enable
+ * 7: Slave Read Local Data Parity Check Error Status
+ * 8: Internal PCI Wire Interrupt Enable
+ * 9: PCI Express Doorbell Interrupt Enable
+ * 10: PCI Abort Interrupt Enable
+ * 11: Local Interrupt Input Enable
+ * 12: Retry Abort Enable
+ * 13: PCI Express Doorbell Interrupt Active
+ * 14: PCI Abort Interrupt Active
+ * 15: Local Interrupt Input Active
+ * 16: Local Interrupt Output Enable
+ * 17: Local Doorbell Interrupt Enable
+ * 18: DMA Channel 0 Interrupt Enable
+ * 19: DMA Channel 1 Interrupt Enable
+ * 20: Local Doorbell Interrupt Active
+ * 21: DMA Channel 0 Interrupt Active
+ * 22: DMA Channel 1 Interrupt Active
+ * 23: Built-In Self-Test (BIST) Interrupt Active
+ * 24: Direct Master was the Bus Master during a Master or Target Abort
+ * 25: DMA Channel 0 was the Bus Master during a Master or Target Abort
+ * 26: DMA Channel 1 was the Bus Master during a Master or Target Abort
+ * 27: Target Abort after internal 256 consecutive Master Retrys
+ * 28: PCI Bus wrote data to LCS_MBOX0
+ * 29: PCI Bus wrote data to LCS_MBOX1
+ * 30: PCI Bus wrote data to LCS_MBOX2
+ * 31: PCI Bus wrote data to LCS_MBOX3
*/
-struct idio_24_gpio_reg {
- u8 out0_7;
- u8 out8_15;
- u8 out16_23;
- u8 ttl_out0_7;
- u8 in0_7;
- u8 in8_15;
- u8 in16_23;
- u8 ttl_in0_7;
- u8 cos0_7;
- u8 cos8_15;
- u8 cos16_23;
- u8 cos_ttl0_7;
- u8 ctl;
- u8 reserved;
- u8 cos_enable;
- u8 soft_reset;
+#define PLX_PEX8311_PCI_LCS_INTCSR 0x68
+#define INTCSR_INTERNAL_PCI_WIRE BIT(8)
+#define INTCSR_LOCAL_INPUT BIT(11)
+#define IDIO_24_ENABLE_IRQ (INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT)
+
+#define IDIO_24_OUT_BASE 0x0
+#define IDIO_24_TTLCMOS_OUT_REG 0x3
+#define IDIO_24_IN_BASE 0x4
+#define IDIO_24_TTLCMOS_IN_REG 0x7
+#define IDIO_24_COS_STATUS_BASE 0x8
+#define IDIO_24_CONTROL_REG 0xC
+#define IDIO_24_COS_ENABLE 0xE
+#define IDIO_24_SOFT_RESET 0xF
+
+#define CONTROL_REG_OUT_MODE BIT(1)
+
+#define COS_ENABLE_RISING BIT(1)
+#define COS_ENABLE_FALLING BIT(4)
+#define COS_ENABLE_BOTH (COS_ENABLE_RISING | COS_ENABLE_FALLING)
+
+static const struct regmap_config pex8311_intcsr_regmap_config = {
+ .name = "pex8311_intcsr",
+ .reg_bits = 32,
+ .reg_stride = 1,
+ .reg_base = PLX_PEX8311_PCI_LCS_INTCSR,
+ .val_bits = 32,
+ .io_port = true,
+};
+
+static const struct regmap_range idio_24_wr_ranges[] = {
+ regmap_reg_range(0x0, 0x3), regmap_reg_range(0x8, 0xC),
+ regmap_reg_range(0xE, 0xF),
+};
+static const struct regmap_range idio_24_rd_ranges[] = {
+ regmap_reg_range(0x0, 0xC), regmap_reg_range(0xE, 0xF),
+};
+static const struct regmap_range idio_24_volatile_ranges[] = {
+ regmap_reg_range(0x4, 0xB), regmap_reg_range(0xF, 0xF),
+};
+static const struct regmap_access_table idio_24_wr_table = {
+ .yes_ranges = idio_24_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(idio_24_wr_ranges),
+};
+static const struct regmap_access_table idio_24_rd_table = {
+ .yes_ranges = idio_24_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(idio_24_rd_ranges),
+};
+static const struct regmap_access_table idio_24_volatile_table = {
+ .yes_ranges = idio_24_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(idio_24_volatile_ranges),
+};
+
+static const struct regmap_config idio_24_regmap_config = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .io_port = true,
+ .wr_table = &idio_24_wr_table,
+ .rd_table = &idio_24_rd_table,
+ .volatile_table = &idio_24_volatile_table,
+ .cache_type = REGCACHE_FLAT,
+ .use_raw_spinlock = true,
+};
+
+#define IDIO_24_NGPIO_PER_REG 8
+#define IDIO_24_REGMAP_IRQ(_id) \
+ [24 + _id] = { \
+ .reg_offset = (_id) / IDIO_24_NGPIO_PER_REG, \
+ .mask = BIT((_id) % IDIO_24_NGPIO_PER_REG), \
+ .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
+ }
+#define IDIO_24_IIN_IRQ(_id) IDIO_24_REGMAP_IRQ(_id)
+#define IDIO_24_TTL_IRQ(_id) IDIO_24_REGMAP_IRQ(24 + _id)
+
+static const struct regmap_irq idio_24_regmap_irqs[] = {
+ IDIO_24_IIN_IRQ(0), IDIO_24_IIN_IRQ(1), IDIO_24_IIN_IRQ(2), /* IIN 0-2 */
+ IDIO_24_IIN_IRQ(3), IDIO_24_IIN_IRQ(4), IDIO_24_IIN_IRQ(5), /* IIN 3-5 */
+ IDIO_24_IIN_IRQ(6), IDIO_24_IIN_IRQ(7), IDIO_24_IIN_IRQ(8), /* IIN 6-8 */
+ IDIO_24_IIN_IRQ(9), IDIO_24_IIN_IRQ(10), IDIO_24_IIN_IRQ(11), /* IIN 9-11 */
+ IDIO_24_IIN_IRQ(12), IDIO_24_IIN_IRQ(13), IDIO_24_IIN_IRQ(14), /* IIN 12-14 */
+ IDIO_24_IIN_IRQ(15), IDIO_24_IIN_IRQ(16), IDIO_24_IIN_IRQ(17), /* IIN 15-17 */
+ IDIO_24_IIN_IRQ(18), IDIO_24_IIN_IRQ(19), IDIO_24_IIN_IRQ(20), /* IIN 18-20 */
+ IDIO_24_IIN_IRQ(21), IDIO_24_IIN_IRQ(22), IDIO_24_IIN_IRQ(23), /* IIN 21-23 */
+ IDIO_24_TTL_IRQ(0), IDIO_24_TTL_IRQ(1), IDIO_24_TTL_IRQ(2), /* TTL 0-2 */
+ IDIO_24_TTL_IRQ(3), IDIO_24_TTL_IRQ(4), IDIO_24_TTL_IRQ(5), /* TTL 3-5 */
+ IDIO_24_TTL_IRQ(6), IDIO_24_TTL_IRQ(7), /* TTL 6-7 */
};
/**
* struct idio_24_gpio - GPIO device private data structure
- * @chip: instance of the gpio_chip
+ * @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions
- * @reg: I/O address offset for the GPIO device registers
- * @irq_mask: I/O bits affected by interrupts
+ * @irq_type: type configuration for IRQs
*/
struct idio_24_gpio {
- struct gpio_chip chip;
+ struct regmap *map;
raw_spinlock_t lock;
- struct idio_24_gpio_reg __iomem *reg;
- unsigned long irq_mask;
+ u8 irq_type;
};
-static int idio_24_gpio_get_direction(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- const unsigned long out_mode_mask = BIT(1);
-
- /* FET Outputs */
- if (offset < 24)
- return 0;
-
- /* Isolated Inputs */
- if (offset < 48)
- return 1;
-
- /* TTL/CMOS I/O */
- /* OUT MODE = 1 when TTL/CMOS Output Mode is set */
- return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask);
-}
-
-static int idio_24_gpio_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- unsigned long flags;
- unsigned int ctl_state;
- const unsigned long out_mode_mask = BIT(1);
-
- /* TTL/CMOS I/O */
- if (offset > 47) {
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
-
- /* Clear TTL/CMOS Output Mode */
- ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
- iowrite8(ctl_state, &idio24gpio->reg->ctl);
-
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
- }
-
- return 0;
-}
-
-static int idio_24_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int idio_24_handle_mask_sync(const int index, const unsigned int mask_buf_def,
+ const unsigned int mask_buf, void *const irq_drv_data)
{
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- unsigned long flags;
- unsigned int ctl_state;
- const unsigned long out_mode_mask = BIT(1);
+ const unsigned int type_mask = COS_ENABLE_BOTH << index;
+ struct idio_24_gpio *const idio24gpio = irq_drv_data;
+ u8 type;
+ int ret;
- /* TTL/CMOS I/O */
- if (offset > 47) {
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
-
- /* Set TTL/CMOS Output Mode */
- ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
- iowrite8(ctl_state, &idio24gpio->reg->ctl);
-
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
- }
-
- chip->set(chip, offset, value);
- return 0;
-}
-
-static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
-{
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- const unsigned long offset_mask = BIT(offset % 8);
- const unsigned long out_mode_mask = BIT(1);
-
- /* FET Outputs */
- if (offset < 8)
- return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
-
- if (offset < 16)
- return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
-
- if (offset < 24)
- return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
-
- /* Isolated Inputs */
- if (offset < 32)
- return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
+ raw_spin_lock(&idio24gpio->lock);
- if (offset < 40)
- return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
+ /* if all are masked, then disable interrupts, else set to type */
+ type = (mask_buf == mask_buf_def) ? ~type_mask : idio24gpio->irq_type;
- if (offset < 48)
- return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
+ ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, type_mask, type);
- /* TTL/CMOS Outputs */
- if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
- return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
+ raw_spin_unlock(&idio24gpio->lock);
- /* TTL/CMOS Inputs */
- return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
+ return ret;
}
-static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
- unsigned long *mask, unsigned long *bits)
+static int idio_24_set_type_config(unsigned int **const buf, const unsigned int type,
+ const struct regmap_irq *const irq_data, const int idx,
+ void *const irq_drv_data)
{
- 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;
- void __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) + 1; 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) & word_mask;
+ const unsigned int offset = irq_data->reg_offset;
+ const unsigned int rising = COS_ENABLE_RISING << offset;
+ const unsigned int falling = COS_ENABLE_FALLING << offset;
+ const unsigned int mask = COS_ENABLE_BOTH << offset;
+ struct idio_24_gpio *const idio24gpio = irq_drv_data;
+ unsigned int new;
+ unsigned int cos_enable;
+ int ret;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ new = rising;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ new = falling;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ new = mask;
+ break;
+ default:
+ return -EINVAL;
}
- return 0;
-}
-
-static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- const unsigned long out_mode_mask = BIT(1);
- void __iomem *base;
- const unsigned int mask = BIT(offset % 8);
- unsigned long flags;
- unsigned int out_state;
-
- /* Isolated Inputs */
- if (offset > 23 && offset < 48)
- return;
-
- /* TTL/CMOS Inputs */
- if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
- return;
-
- /* TTL/CMOS Outputs */
- if (offset > 47)
- base = &idio24gpio->reg->ttl_out0_7;
- /* FET Outputs */
- else if (offset > 15)
- base = &idio24gpio->reg->out16_23;
- else if (offset > 7)
- base = &idio24gpio->reg->out8_15;
- else
- base = &idio24gpio->reg->out0_7;
-
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
-
- if (value)
- out_state = ioread8(base) | mask;
- else
- out_state = ioread8(base) & ~mask;
-
- iowrite8(out_state, base);
-
- 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;
- void __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(&idio24gpio->lock);
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+ /* replace old bitmap with new bitmap */
+ idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask);
- /* process output lines */
- out_state = ioread8(ports[i]) & ~gpio_mask;
- out_state |= (*bits >> bits_offset) & gpio_mask;
- iowrite8(out_state, ports[i]);
+ ret = regmap_read(idio24gpio->map, IDIO_24_COS_ENABLE, &cos_enable);
+ if (ret)
+ goto exit_unlock;
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ /* if COS is currently enabled then update the edge type */
+ if (cos_enable & mask) {
+ ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, mask,
+ idio24gpio->irq_type);
+ if (ret)
+ goto exit_unlock;
}
- /* 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)
-{
-}
-
-static void idio_24_irq_mask(struct irq_data *data)
-{
- struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- unsigned long flags;
- const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
- unsigned char new_irq_mask;
- const unsigned long bank_offset = bit_offset/8 * 8;
- unsigned char cos_enable_state;
-
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
-
- idio24gpio->irq_mask &= BIT(bit_offset);
- new_irq_mask = idio24gpio->irq_mask >> bank_offset;
-
- if (!new_irq_mask) {
- cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
-
- /* Disable Rising Edge detection */
- cos_enable_state &= ~BIT(bank_offset);
- /* Disable Falling Edge detection */
- cos_enable_state &= ~BIT(bank_offset + 4);
-
- iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
- }
+exit_unlock:
+ raw_spin_unlock(&idio24gpio->lock);
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ return ret;
}
-static void idio_24_irq_unmask(struct irq_data *data)
+static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
+ const unsigned int offset, unsigned int *const reg,
+ unsigned int *const mask)
{
- struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
- struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
- unsigned long flags;
- unsigned char prev_irq_mask;
- const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
- const unsigned long bank_offset = bit_offset/8 * 8;
- unsigned char cos_enable_state;
+ const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
+ const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
+ struct regmap *const map = gpio_regmap_get_drvdata(gpio);
+ int err;
+ unsigned int ctrl_reg;
- raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+ switch (base) {
+ case IDIO_24_OUT_BASE:
+ *mask = BIT(offset % IDIO_24_NGPIO_PER_REG);
- prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
- idio24gpio->irq_mask |= BIT(bit_offset);
+ /* FET Outputs */
+ if (offset < 24) {
+ *reg = IDIO_24_OUT_BASE + out_stride;
+ return 0;
+ }
- if (!prev_irq_mask) {
- cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
+ /* Isolated Inputs */
+ if (offset < 48) {
+ *reg = IDIO_24_IN_BASE + in_stride;
+ return 0;
+ }
- /* Enable Rising Edge detection */
- cos_enable_state |= BIT(bank_offset);
- /* Enable Falling Edge detection */
- cos_enable_state |= BIT(bank_offset + 4);
+ err = regmap_read(map, IDIO_24_CONTROL_REG, &ctrl_reg);
+ if (err)
+ return err;
- iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
- }
+ /* TTL/CMOS Outputs */
+ if (ctrl_reg & CONTROL_REG_OUT_MODE) {
+ *reg = IDIO_24_TTLCMOS_OUT_REG;
+ return 0;
+ }
- raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
-}
+ /* TTL/CMOS Inputs */
+ *reg = IDIO_24_TTLCMOS_IN_REG;
+ return 0;
+ case IDIO_24_CONTROL_REG:
+ /* We can only set direction for TTL/CMOS lines */
+ if (offset < 48)
+ return -ENOTSUPP;
-static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
-{
- /* The only valid irq types are none and both-edges */
- if (flow_type != IRQ_TYPE_NONE &&
- (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
+ *reg = IDIO_24_CONTROL_REG;
+ *mask = CONTROL_REG_OUT_MODE;
+ return 0;
+ default:
+ /* Should never reach this path */
return -EINVAL;
-
- return 0;
-}
-
-static struct irq_chip idio_24_irqchip = {
- .name = "pcie-idio-24",
- .irq_ack = idio_24_irq_ack,
- .irq_mask = idio_24_irq_mask,
- .irq_unmask = idio_24_irq_unmask,
- .irq_set_type = idio_24_irq_set_type
-};
-
-static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
-{
- struct idio_24_gpio *const idio24gpio = dev_id;
- unsigned long irq_status;
- struct gpio_chip *const chip = &idio24gpio->chip;
- unsigned long irq_mask;
- int gpio;
-
- raw_spin_lock(&idio24gpio->lock);
-
- /* Read Change-Of-State status */
- irq_status = ioread32(&idio24gpio->reg->cos0_7);
-
- raw_spin_unlock(&idio24gpio->lock);
-
- /* Make sure our device generated IRQ */
- if (!irq_status)
- return IRQ_NONE;
-
- /* Handle only unmasked IRQ */
- irq_mask = idio24gpio->irq_mask & irq_status;
-
- for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
- generic_handle_irq(irq_find_mapping(chip->irq.domain,
- gpio + 24));
-
- raw_spin_lock(&idio24gpio->lock);
-
- /* Clear Change-Of-State status */
- iowrite32(irq_status, &idio24gpio->reg->cos0_7);
-
- raw_spin_unlock(&idio24gpio->lock);
-
- return IRQ_HANDLED;
+ }
}
#define IDIO_24_NGPIO 56
@@ -481,67 +294,91 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct device *const dev = &pdev->dev;
struct idio_24_gpio *idio24gpio;
int err;
+ const size_t pci_plx_bar_index = 1;
const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev);
+ struct gpio_regmap_config gpio_config = {};
+ void __iomem *pex8311_regs;
+ void __iomem *idio_24_regs;
+ struct regmap *intcsr_map;
+ struct regmap_irq_chip *chip;
+ struct regmap_irq_chip_data *chip_data;
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to enable PCI device\n");
+
+ pex8311_regs = pcim_iomap_region(pdev, pci_plx_bar_index, "pex8311");
+ if (IS_ERR(pex8311_regs))
+ return dev_err_probe(dev, PTR_ERR(pex8311_regs), "Unable to map PEX 8311 I/O addresses\n");
+
+ idio_24_regs = pcim_iomap_region(pdev, pci_bar_index, name);
+ if (IS_ERR(idio_24_regs))
+ return dev_err_probe(dev, PTR_ERR(idio_24_regs), "Unable to map PCIe-IDIO-24 I/O addresses\n");
+
+ intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config);
+ if (IS_ERR(intcsr_map))
+ return dev_err_probe(dev, PTR_ERR(intcsr_map),
+ "Unable to initialize PEX8311 register map\n");
idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
if (!idio24gpio)
return -ENOMEM;
- err = pcim_enable_device(pdev);
- if (err) {
- dev_err(dev, "Failed to enable PCI device (%d)\n", err);
- return err;
- }
-
- err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
- if (err) {
- dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
- return err;
- }
-
- idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
-
- idio24gpio->chip.label = name;
- idio24gpio->chip.parent = dev;
- idio24gpio->chip.owner = THIS_MODULE;
- idio24gpio->chip.base = -1;
- idio24gpio->chip.ngpio = IDIO_24_NGPIO;
- idio24gpio->chip.names = idio_24_names;
- idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
- 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;
+ idio24gpio->map = devm_regmap_init_mmio(dev, idio_24_regs, &idio_24_regmap_config);
+ if (IS_ERR(idio24gpio->map))
+ return dev_err_probe(dev, PTR_ERR(idio24gpio->map),
+ "Unable to initialize register map\n");
raw_spin_lock_init(&idio24gpio->lock);
- /* Software board reset */
- iowrite8(0, &idio24gpio->reg->soft_reset);
+ /* Initialize all IRQ type configuration to IRQ_TYPE_EDGE_BOTH */
+ idio24gpio->irq_type = GENMASK(7, 0);
- err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
- if (err) {
- dev_err(dev, "GPIO registering failed (%d)\n", err);
- return err;
- }
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
- err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
- handle_edge_irq, IRQ_TYPE_NONE);
- if (err) {
- dev_err(dev, "Could not add irqchip (%d)\n", err);
- return err;
- }
+ chip->name = name;
+ chip->status_base = IDIO_24_COS_STATUS_BASE;
+ chip->mask_base = IDIO_24_COS_ENABLE;
+ chip->ack_base = IDIO_24_COS_STATUS_BASE;
+ chip->num_regs = 4;
+ chip->irqs = idio_24_regmap_irqs;
+ chip->num_irqs = ARRAY_SIZE(idio_24_regmap_irqs);
+ chip->handle_mask_sync = idio_24_handle_mask_sync;
+ chip->set_type_config = idio_24_set_type_config;
+ chip->irq_drv_data = idio24gpio;
- err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
- name, idio24gpio);
- if (err) {
- dev_err(dev, "IRQ handler registering failed (%d)\n", err);
+ /* Software board reset */
+ err = regmap_write(idio24gpio->map, IDIO_24_SOFT_RESET, 0);
+ if (err)
+ return err;
+ /*
+ * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
+ * input
+ */
+ err = regmap_update_bits(intcsr_map, 0x0, IDIO_24_ENABLE_IRQ, IDIO_24_ENABLE_IRQ);
+ if (err)
return err;
- }
- return 0;
+ err = devm_regmap_add_irq_chip(dev, idio24gpio->map, pdev->irq, 0, 0, chip, &chip_data);
+ if (err)
+ return dev_err_probe(dev, err, "IRQ registration failed\n");
+
+ gpio_config.parent = dev;
+ gpio_config.regmap = idio24gpio->map;
+ gpio_config.ngpio = IDIO_24_NGPIO;
+ gpio_config.names = idio_24_names;
+ gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
+ gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
+ gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(IDIO_24_CONTROL_REG);
+ gpio_config.ngpio_per_reg = IDIO_24_NGPIO_PER_REG;
+ gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
+ gpio_config.reg_mask_xlate = idio_24_reg_mask_xlate;
+ gpio_config.drvdata = idio24gpio->map;
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
}
static const struct pci_device_id idio_24_pci_dev_id[] = {