Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * GPIO driver for the ACCES PCI-IDIO-16
4 * Copyright (C) 2017 William Breathitt Gray
5 */
6#include <linux/bits.h>
7#include <linux/device.h>
8#include <linux/errno.h>
9#include <linux/gpio/driver.h>
10#include <linux/interrupt.h>
11#include <linux/irqdesc.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/pci.h>
15#include <linux/spinlock.h>
16#include <linux/types.h>
17
18#include "gpio-idio-16.h"
19
20/**
21 * struct idio_16_gpio - GPIO device private data structure
22 * @chip: instance of the gpio_chip
23 * @lock: synchronization lock to prevent I/O race conditions
24 * @reg: I/O address offset for the GPIO device registers
25 * @state: ACCES IDIO-16 device state
26 * @irq_mask: I/O bits affected by interrupts
27 */
28struct idio_16_gpio {
29 struct gpio_chip chip;
30 raw_spinlock_t lock;
31 struct idio_16 __iomem *reg;
32 struct idio_16_state state;
33 unsigned long irq_mask;
34};
35
36static int idio_16_gpio_get_direction(struct gpio_chip *chip,
37 unsigned int offset)
38{
39 if (idio_16_get_direction(offset))
40 return GPIO_LINE_DIRECTION_IN;
41
42 return GPIO_LINE_DIRECTION_OUT;
43}
44
45static int idio_16_gpio_direction_input(struct gpio_chip *chip,
46 unsigned int offset)
47{
48 return 0;
49}
50
51static int idio_16_gpio_direction_output(struct gpio_chip *chip,
52 unsigned int offset, int value)
53{
54 chip->set(chip, offset, value);
55 return 0;
56}
57
58static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
59{
60 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
61
62 return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
63}
64
65static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
66 unsigned long *mask, unsigned long *bits)
67{
68 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
69
70 idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
71 return 0;
72}
73
74static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
75 int value)
76{
77 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
78
79 idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
80}
81
82static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
83 unsigned long *mask, unsigned long *bits)
84{
85 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
86
87 idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
88}
89
90static void idio_16_irq_ack(struct irq_data *data)
91{
92}
93
94static void idio_16_irq_mask(struct irq_data *data)
95{
96 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
97 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
98 const unsigned long mask = BIT(irqd_to_hwirq(data));
99 unsigned long flags;
100
101 idio16gpio->irq_mask &= ~mask;
102
103 if (!idio16gpio->irq_mask) {
104 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
105
106 iowrite8(0, &idio16gpio->reg->irq_ctl);
107
108 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
109 }
110}
111
112static void idio_16_irq_unmask(struct irq_data *data)
113{
114 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
115 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
116 const unsigned long mask = BIT(irqd_to_hwirq(data));
117 const unsigned long prev_irq_mask = idio16gpio->irq_mask;
118 unsigned long flags;
119
120 idio16gpio->irq_mask |= mask;
121
122 if (!prev_irq_mask) {
123 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
124
125 ioread8(&idio16gpio->reg->irq_ctl);
126
127 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
128 }
129}
130
131static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
132{
133 /* The only valid irq types are none and both-edges */
134 if (flow_type != IRQ_TYPE_NONE &&
135 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
136 return -EINVAL;
137
138 return 0;
139}
140
141static struct irq_chip idio_16_irqchip = {
142 .name = "pci-idio-16",
143 .irq_ack = idio_16_irq_ack,
144 .irq_mask = idio_16_irq_mask,
145 .irq_unmask = idio_16_irq_unmask,
146 .irq_set_type = idio_16_irq_set_type
147};
148
149static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
150{
151 struct idio_16_gpio *const idio16gpio = dev_id;
152 unsigned int irq_status;
153 struct gpio_chip *const chip = &idio16gpio->chip;
154 int gpio;
155
156 raw_spin_lock(&idio16gpio->lock);
157
158 irq_status = ioread8(&idio16gpio->reg->irq_status);
159
160 raw_spin_unlock(&idio16gpio->lock);
161
162 /* Make sure our device generated IRQ */
163 if (!(irq_status & 0x3) || !(irq_status & 0x4))
164 return IRQ_NONE;
165
166 for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
167 generic_handle_domain_irq(chip->irq.domain, gpio);
168
169 raw_spin_lock(&idio16gpio->lock);
170
171 /* Clear interrupt */
172 iowrite8(0, &idio16gpio->reg->in0_7);
173
174 raw_spin_unlock(&idio16gpio->lock);
175
176 return IRQ_HANDLED;
177}
178
179#define IDIO_16_NGPIO 32
180static const char *idio_16_names[IDIO_16_NGPIO] = {
181 "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
182 "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
183 "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
184 "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
185};
186
187static int idio_16_irq_init_hw(struct gpio_chip *gc)
188{
189 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
190
191 /* Disable IRQ by default and clear any pending interrupt */
192 iowrite8(0, &idio16gpio->reg->irq_ctl);
193 iowrite8(0, &idio16gpio->reg->in0_7);
194
195 return 0;
196}
197
198static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
199{
200 struct device *const dev = &pdev->dev;
201 struct idio_16_gpio *idio16gpio;
202 int err;
203 const size_t pci_bar_index = 2;
204 const char *const name = pci_name(pdev);
205 struct gpio_irq_chip *girq;
206
207 idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
208 if (!idio16gpio)
209 return -ENOMEM;
210
211 err = pcim_enable_device(pdev);
212 if (err) {
213 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
214 return err;
215 }
216
217 err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
218 if (err) {
219 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
220 return err;
221 }
222
223 idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
224
225 /* Deactivate input filters */
226 iowrite8(0, &idio16gpio->reg->filter_ctl);
227
228 idio16gpio->chip.label = name;
229 idio16gpio->chip.parent = dev;
230 idio16gpio->chip.owner = THIS_MODULE;
231 idio16gpio->chip.base = -1;
232 idio16gpio->chip.ngpio = IDIO_16_NGPIO;
233 idio16gpio->chip.names = idio_16_names;
234 idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
235 idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
236 idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
237 idio16gpio->chip.get = idio_16_gpio_get;
238 idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
239 idio16gpio->chip.set = idio_16_gpio_set;
240 idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
241
242 idio_16_state_init(&idio16gpio->state);
243
244 girq = &idio16gpio->chip.irq;
245 girq->chip = &idio_16_irqchip;
246 /* This will let us handle the parent IRQ in the driver */
247 girq->parent_handler = NULL;
248 girq->num_parents = 0;
249 girq->parents = NULL;
250 girq->default_type = IRQ_TYPE_NONE;
251 girq->handler = handle_edge_irq;
252 girq->init_hw = idio_16_irq_init_hw;
253
254 raw_spin_lock_init(&idio16gpio->lock);
255
256 err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
257 if (err) {
258 dev_err(dev, "GPIO registering failed (%d)\n", err);
259 return err;
260 }
261
262 err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
263 name, idio16gpio);
264 if (err) {
265 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
266 return err;
267 }
268
269 return 0;
270}
271
272static const struct pci_device_id idio_16_pci_dev_id[] = {
273 { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
274};
275MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
276
277static struct pci_driver idio_16_driver = {
278 .name = "pci-idio-16",
279 .id_table = idio_16_pci_dev_id,
280 .probe = idio_16_probe
281};
282
283module_pci_driver(idio_16_driver);
284
285MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
286MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
287MODULE_LICENSE("GPL v2");
288MODULE_IMPORT_NS(GPIO_IDIO_16);
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * GPIO driver for the ACCES PCI-IDIO-16
4 * Copyright (C) 2017 William Breathitt Gray
5 */
6#include <linux/bits.h>
7#include <linux/device.h>
8#include <linux/err.h>
9#include <linux/irq.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/pci.h>
13#include <linux/regmap.h>
14#include <linux/types.h>
15
16#include "gpio-idio-16.h"
17
18static const struct regmap_range idio_16_wr_ranges[] = {
19 regmap_reg_range(0x0, 0x2), regmap_reg_range(0x3, 0x4),
20};
21static const struct regmap_range idio_16_rd_ranges[] = {
22 regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x6),
23};
24static const struct regmap_range idio_16_precious_ranges[] = {
25 regmap_reg_range(0x2, 0x2),
26};
27static const struct regmap_access_table idio_16_wr_table = {
28 .yes_ranges = idio_16_wr_ranges,
29 .n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges),
30};
31static const struct regmap_access_table idio_16_rd_table = {
32 .yes_ranges = idio_16_rd_ranges,
33 .n_yes_ranges = ARRAY_SIZE(idio_16_rd_ranges),
34};
35static const struct regmap_access_table idio_16_precious_table = {
36 .yes_ranges = idio_16_precious_ranges,
37 .n_yes_ranges = ARRAY_SIZE(idio_16_precious_ranges),
38};
39static const struct regmap_config idio_16_regmap_config = {
40 .reg_bits = 8,
41 .reg_stride = 1,
42 .val_bits = 8,
43 .io_port = true,
44 .wr_table = &idio_16_wr_table,
45 .rd_table = &idio_16_rd_table,
46 .volatile_table = &idio_16_rd_table,
47 .precious_table = &idio_16_precious_table,
48 .cache_type = REGCACHE_FLAT,
49 .use_raw_spinlock = true,
50};
51
52/* Only input lines (GPIO 16-31) support interrupts */
53#define IDIO_16_REGMAP_IRQ(_id) \
54 [16 + _id] = { \
55 .mask = BIT(2), \
56 .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
57 }
58
59static const struct regmap_irq idio_16_regmap_irqs[] = {
60 IDIO_16_REGMAP_IRQ(0), IDIO_16_REGMAP_IRQ(1), IDIO_16_REGMAP_IRQ(2), /* 0-2 */
61 IDIO_16_REGMAP_IRQ(3), IDIO_16_REGMAP_IRQ(4), IDIO_16_REGMAP_IRQ(5), /* 3-5 */
62 IDIO_16_REGMAP_IRQ(6), IDIO_16_REGMAP_IRQ(7), IDIO_16_REGMAP_IRQ(8), /* 6-8 */
63 IDIO_16_REGMAP_IRQ(9), IDIO_16_REGMAP_IRQ(10), IDIO_16_REGMAP_IRQ(11), /* 9-11 */
64 IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */
65 IDIO_16_REGMAP_IRQ(15), /* 15 */
66};
67
68static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
69{
70 struct device *const dev = &pdev->dev;
71 int err;
72 const size_t pci_bar_index = 2;
73 struct idio_16_regmap_config config = {};
74 void __iomem *regs;
75 struct regmap *map;
76
77 err = pcim_enable_device(pdev);
78 if (err)
79 return dev_err_probe(dev, err, "Failed to enable PCI device\n");
80
81 regs = pcim_iomap_region(pdev, pci_bar_index, pci_name(pdev));
82 if (IS_ERR(regs))
83 return dev_err_probe(dev, PTR_ERR(regs), "Unable to map PCI I/O addresses\n");
84
85 map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config);
86 if (IS_ERR(map))
87 return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n");
88
89 config.parent = dev;
90 config.map = map;
91 config.regmap_irqs = idio_16_regmap_irqs;
92 config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs);
93 config.irq = pdev->irq;
94 config.filters = true;
95
96 return devm_idio_16_regmap_register(dev, &config);
97}
98
99static const struct pci_device_id idio_16_pci_dev_id[] = {
100 { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
101};
102MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
103
104static struct pci_driver idio_16_driver = {
105 .name = "pci-idio-16",
106 .id_table = idio_16_pci_dev_id,
107 .probe = idio_16_probe
108};
109
110module_pci_driver(idio_16_driver);
111
112MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
113MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
114MODULE_LICENSE("GPL v2");
115MODULE_IMPORT_NS("GPIO_IDIO_16");