xref: /linux/drivers/gpio/gpio-pcie-idio-24.c (revision 55d0969c451159cff86949b38c39171cab962069)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * GPIO driver for the ACCES PCIe-IDIO-24 family
4  * Copyright (C) 2018 William Breathitt Gray
5  *
6  * This driver supports the following ACCES devices: PCIe-IDIO-24,
7  * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
8  */
9 #include <linux/bits.h>
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/gpio/regmap.h>
13 #include <linux/irq.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/pci.h>
17 #include <linux/regmap.h>
18 #include <linux/spinlock.h>
19 #include <linux/types.h>
20 
21 /*
22  * PLX PEX8311 PCI LCS_INTCSR Interrupt Control/Status
23  *
24  * Bit: Description
25  *   0: Enable Interrupt Sources (Bit 0)
26  *   1: Enable Interrupt Sources (Bit 1)
27  *   2: Generate Internal PCI Bus Internal SERR# Interrupt
28  *   3: Mailbox Interrupt Enable
29  *   4: Power Management Interrupt Enable
30  *   5: Power Management Interrupt
31  *   6: Slave Read Local Data Parity Check Error Enable
32  *   7: Slave Read Local Data Parity Check Error Status
33  *   8: Internal PCI Wire Interrupt Enable
34  *   9: PCI Express Doorbell Interrupt Enable
35  *  10: PCI Abort Interrupt Enable
36  *  11: Local Interrupt Input Enable
37  *  12: Retry Abort Enable
38  *  13: PCI Express Doorbell Interrupt Active
39  *  14: PCI Abort Interrupt Active
40  *  15: Local Interrupt Input Active
41  *  16: Local Interrupt Output Enable
42  *  17: Local Doorbell Interrupt Enable
43  *  18: DMA Channel 0 Interrupt Enable
44  *  19: DMA Channel 1 Interrupt Enable
45  *  20: Local Doorbell Interrupt Active
46  *  21: DMA Channel 0 Interrupt Active
47  *  22: DMA Channel 1 Interrupt Active
48  *  23: Built-In Self-Test (BIST) Interrupt Active
49  *  24: Direct Master was the Bus Master during a Master or Target Abort
50  *  25: DMA Channel 0 was the Bus Master during a Master or Target Abort
51  *  26: DMA Channel 1 was the Bus Master during a Master or Target Abort
52  *  27: Target Abort after internal 256 consecutive Master Retrys
53  *  28: PCI Bus wrote data to LCS_MBOX0
54  *  29: PCI Bus wrote data to LCS_MBOX1
55  *  30: PCI Bus wrote data to LCS_MBOX2
56  *  31: PCI Bus wrote data to LCS_MBOX3
57  */
58 #define PLX_PEX8311_PCI_LCS_INTCSR  0x68
59 #define INTCSR_INTERNAL_PCI_WIRE    BIT(8)
60 #define INTCSR_LOCAL_INPUT          BIT(11)
61 #define IDIO_24_ENABLE_IRQ          (INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT)
62 
63 #define IDIO_24_OUT_BASE 0x0
64 #define IDIO_24_TTLCMOS_OUT_REG 0x3
65 #define IDIO_24_IN_BASE 0x4
66 #define IDIO_24_TTLCMOS_IN_REG 0x7
67 #define IDIO_24_COS_STATUS_BASE 0x8
68 #define IDIO_24_CONTROL_REG 0xC
69 #define IDIO_24_COS_ENABLE 0xE
70 #define IDIO_24_SOFT_RESET 0xF
71 
72 #define CONTROL_REG_OUT_MODE BIT(1)
73 
74 #define COS_ENABLE_RISING BIT(1)
75 #define COS_ENABLE_FALLING BIT(4)
76 #define COS_ENABLE_BOTH (COS_ENABLE_RISING | COS_ENABLE_FALLING)
77 
78 static const struct regmap_config pex8311_intcsr_regmap_config = {
79 	.name = "pex8311_intcsr",
80 	.reg_bits = 32,
81 	.reg_stride = 1,
82 	.reg_base = PLX_PEX8311_PCI_LCS_INTCSR,
83 	.val_bits = 32,
84 	.io_port = true,
85 };
86 
87 static const struct regmap_range idio_24_wr_ranges[] = {
88 	regmap_reg_range(0x0, 0x3), regmap_reg_range(0x8, 0xC),
89 	regmap_reg_range(0xE, 0xF),
90 };
91 static const struct regmap_range idio_24_rd_ranges[] = {
92 	regmap_reg_range(0x0, 0xC), regmap_reg_range(0xE, 0xF),
93 };
94 static const struct regmap_range idio_24_volatile_ranges[] = {
95 	regmap_reg_range(0x4, 0xB), regmap_reg_range(0xF, 0xF),
96 };
97 static const struct regmap_access_table idio_24_wr_table = {
98 	.yes_ranges = idio_24_wr_ranges,
99 	.n_yes_ranges = ARRAY_SIZE(idio_24_wr_ranges),
100 };
101 static const struct regmap_access_table idio_24_rd_table = {
102 	.yes_ranges = idio_24_rd_ranges,
103 	.n_yes_ranges = ARRAY_SIZE(idio_24_rd_ranges),
104 };
105 static const struct regmap_access_table idio_24_volatile_table = {
106 	.yes_ranges = idio_24_volatile_ranges,
107 	.n_yes_ranges = ARRAY_SIZE(idio_24_volatile_ranges),
108 };
109 
110 static const struct regmap_config idio_24_regmap_config = {
111 	.reg_bits = 8,
112 	.reg_stride = 1,
113 	.val_bits = 8,
114 	.io_port = true,
115 	.wr_table = &idio_24_wr_table,
116 	.rd_table = &idio_24_rd_table,
117 	.volatile_table = &idio_24_volatile_table,
118 	.cache_type = REGCACHE_FLAT,
119 	.use_raw_spinlock = true,
120 };
121 
122 #define IDIO_24_NGPIO_PER_REG 8
123 #define IDIO_24_REGMAP_IRQ(_id)						\
124 	[24 + _id] = {							\
125 		.reg_offset = (_id) / IDIO_24_NGPIO_PER_REG,		\
126 		.mask = BIT((_id) % IDIO_24_NGPIO_PER_REG),		\
127 		.type = { .types_supported = IRQ_TYPE_EDGE_BOTH },	\
128 	}
129 #define IDIO_24_IIN_IRQ(_id) IDIO_24_REGMAP_IRQ(_id)
130 #define IDIO_24_TTL_IRQ(_id) IDIO_24_REGMAP_IRQ(24 + _id)
131 
132 static const struct regmap_irq idio_24_regmap_irqs[] = {
133 	IDIO_24_IIN_IRQ(0), IDIO_24_IIN_IRQ(1), IDIO_24_IIN_IRQ(2), /* IIN 0-2 */
134 	IDIO_24_IIN_IRQ(3), IDIO_24_IIN_IRQ(4), IDIO_24_IIN_IRQ(5), /* IIN 3-5 */
135 	IDIO_24_IIN_IRQ(6), IDIO_24_IIN_IRQ(7), IDIO_24_IIN_IRQ(8), /* IIN 6-8 */
136 	IDIO_24_IIN_IRQ(9), IDIO_24_IIN_IRQ(10), IDIO_24_IIN_IRQ(11), /* IIN 9-11 */
137 	IDIO_24_IIN_IRQ(12), IDIO_24_IIN_IRQ(13), IDIO_24_IIN_IRQ(14), /* IIN 12-14 */
138 	IDIO_24_IIN_IRQ(15), IDIO_24_IIN_IRQ(16), IDIO_24_IIN_IRQ(17), /* IIN 15-17 */
139 	IDIO_24_IIN_IRQ(18), IDIO_24_IIN_IRQ(19), IDIO_24_IIN_IRQ(20), /* IIN 18-20 */
140 	IDIO_24_IIN_IRQ(21), IDIO_24_IIN_IRQ(22), IDIO_24_IIN_IRQ(23), /* IIN 21-23 */
141 	IDIO_24_TTL_IRQ(0), IDIO_24_TTL_IRQ(1), IDIO_24_TTL_IRQ(2), /* TTL 0-2 */
142 	IDIO_24_TTL_IRQ(3), IDIO_24_TTL_IRQ(4), IDIO_24_TTL_IRQ(5), /* TTL 3-5 */
143 	IDIO_24_TTL_IRQ(6), IDIO_24_TTL_IRQ(7), /* TTL 6-7 */
144 };
145 
146 /**
147  * struct idio_24_gpio - GPIO device private data structure
148  * @map:	regmap for the device
149  * @lock:	synchronization lock to prevent I/O race conditions
150  * @irq_type:	type configuration for IRQs
151  */
152 struct idio_24_gpio {
153 	struct regmap *map;
154 	raw_spinlock_t lock;
155 	u8 irq_type;
156 };
157 
158 static int idio_24_handle_mask_sync(const int index, const unsigned int mask_buf_def,
159 				    const unsigned int mask_buf, void *const irq_drv_data)
160 {
161 	const unsigned int type_mask = COS_ENABLE_BOTH << index;
162 	struct idio_24_gpio *const idio24gpio = irq_drv_data;
163 	u8 type;
164 	int ret;
165 
166 	raw_spin_lock(&idio24gpio->lock);
167 
168 	/* if all are masked, then disable interrupts, else set to type */
169 	type = (mask_buf == mask_buf_def) ? ~type_mask : idio24gpio->irq_type;
170 
171 	ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, type_mask, type);
172 
173 	raw_spin_unlock(&idio24gpio->lock);
174 
175 	return ret;
176 }
177 
178 static int idio_24_set_type_config(unsigned int **const buf, const unsigned int type,
179 				   const struct regmap_irq *const irq_data, const int idx,
180 				   void *const irq_drv_data)
181 {
182 	const unsigned int offset = irq_data->reg_offset;
183 	const unsigned int rising = COS_ENABLE_RISING << offset;
184 	const unsigned int falling = COS_ENABLE_FALLING << offset;
185 	const unsigned int mask = COS_ENABLE_BOTH << offset;
186 	struct idio_24_gpio *const idio24gpio = irq_drv_data;
187 	unsigned int new;
188 	unsigned int cos_enable;
189 	int ret;
190 
191 	switch (type) {
192 	case IRQ_TYPE_EDGE_RISING:
193 		new = rising;
194 		break;
195 	case IRQ_TYPE_EDGE_FALLING:
196 		new = falling;
197 		break;
198 	case IRQ_TYPE_EDGE_BOTH:
199 		new = mask;
200 		break;
201 	default:
202 		return -EINVAL;
203 	}
204 
205 	raw_spin_lock(&idio24gpio->lock);
206 
207 	/* replace old bitmap with new bitmap */
208 	idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask);
209 
210 	ret = regmap_read(idio24gpio->map, IDIO_24_COS_ENABLE, &cos_enable);
211 	if (ret)
212 		goto exit_unlock;
213 
214 	/* if COS is currently enabled then update the edge type */
215 	if (cos_enable & mask) {
216 		ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, mask,
217 					 idio24gpio->irq_type);
218 		if (ret)
219 			goto exit_unlock;
220 	}
221 
222 exit_unlock:
223 	raw_spin_unlock(&idio24gpio->lock);
224 
225 	return ret;
226 }
227 
228 static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
229 				  const unsigned int offset, unsigned int *const reg,
230 				  unsigned int *const mask)
231 {
232 	const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
233 	const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
234 	struct regmap *const map = gpio_regmap_get_drvdata(gpio);
235 	int err;
236 	unsigned int ctrl_reg;
237 
238 	switch (base) {
239 	case IDIO_24_OUT_BASE:
240 		*mask = BIT(offset % IDIO_24_NGPIO_PER_REG);
241 
242 		/* FET Outputs */
243 		if (offset < 24) {
244 			*reg = IDIO_24_OUT_BASE + out_stride;
245 			return 0;
246 		}
247 
248 		/* Isolated Inputs */
249 		if (offset < 48) {
250 			*reg = IDIO_24_IN_BASE + in_stride;
251 			return 0;
252 		}
253 
254 		err = regmap_read(map, IDIO_24_CONTROL_REG, &ctrl_reg);
255 		if (err)
256 			return err;
257 
258 		/* TTL/CMOS Outputs */
259 		if (ctrl_reg & CONTROL_REG_OUT_MODE) {
260 			*reg = IDIO_24_TTLCMOS_OUT_REG;
261 			return 0;
262 		}
263 
264 		/* TTL/CMOS Inputs */
265 		*reg = IDIO_24_TTLCMOS_IN_REG;
266 		return 0;
267 	case IDIO_24_CONTROL_REG:
268 		/* We can only set direction for TTL/CMOS lines */
269 		if (offset < 48)
270 			return -ENOTSUPP;
271 
272 		*reg = IDIO_24_CONTROL_REG;
273 		*mask = CONTROL_REG_OUT_MODE;
274 		return 0;
275 	default:
276 		/* Should never reach this path */
277 		return -EINVAL;
278 	}
279 }
280 
281 #define IDIO_24_NGPIO 56
282 static const char *idio_24_names[IDIO_24_NGPIO] = {
283 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
284 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
285 	"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
286 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
287 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
288 	"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
289 	"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
290 };
291 
292 static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
293 {
294 	struct device *const dev = &pdev->dev;
295 	struct idio_24_gpio *idio24gpio;
296 	int err;
297 	const size_t pci_plx_bar_index = 1;
298 	const size_t pci_bar_index = 2;
299 	const char *const name = pci_name(pdev);
300 	struct gpio_regmap_config gpio_config = {};
301 	void __iomem *pex8311_regs;
302 	void __iomem *idio_24_regs;
303 	struct regmap *intcsr_map;
304 	struct regmap_irq_chip *chip;
305 	struct regmap_irq_chip_data *chip_data;
306 
307 	err = pcim_enable_device(pdev);
308 	if (err) {
309 		dev_err(dev, "Failed to enable PCI device (%d)\n", err);
310 		return err;
311 	}
312 
313 	err = pcim_iomap_regions(pdev, BIT(pci_plx_bar_index) | BIT(pci_bar_index), name);
314 	if (err) {
315 		dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
316 		return err;
317 	}
318 
319 	pex8311_regs = pcim_iomap_table(pdev)[pci_plx_bar_index];
320 	idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index];
321 
322 	intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config);
323 	if (IS_ERR(intcsr_map))
324 		return dev_err_probe(dev, PTR_ERR(intcsr_map),
325 				     "Unable to initialize PEX8311 register map\n");
326 
327 	idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
328 	if (!idio24gpio)
329 		return -ENOMEM;
330 
331 	idio24gpio->map = devm_regmap_init_mmio(dev, idio_24_regs, &idio_24_regmap_config);
332 	if (IS_ERR(idio24gpio->map))
333 		return dev_err_probe(dev, PTR_ERR(idio24gpio->map),
334 				     "Unable to initialize register map\n");
335 
336 	raw_spin_lock_init(&idio24gpio->lock);
337 
338 	/* Initialize all IRQ type configuration to IRQ_TYPE_EDGE_BOTH */
339 	idio24gpio->irq_type = GENMASK(7, 0);
340 
341 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
342 	if (!chip)
343 		return -ENOMEM;
344 
345 	chip->name = name;
346 	chip->status_base = IDIO_24_COS_STATUS_BASE;
347 	chip->mask_base = IDIO_24_COS_ENABLE;
348 	chip->ack_base = IDIO_24_COS_STATUS_BASE;
349 	chip->num_regs = 4;
350 	chip->irqs = idio_24_regmap_irqs;
351 	chip->num_irqs = ARRAY_SIZE(idio_24_regmap_irqs);
352 	chip->handle_mask_sync = idio_24_handle_mask_sync;
353 	chip->set_type_config = idio_24_set_type_config;
354 	chip->irq_drv_data = idio24gpio;
355 
356 	/* Software board reset */
357 	err = regmap_write(idio24gpio->map, IDIO_24_SOFT_RESET, 0);
358 	if (err)
359 		return err;
360 	/*
361 	 * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
362 	 * input
363 	 */
364 	err = regmap_update_bits(intcsr_map, 0x0, IDIO_24_ENABLE_IRQ, IDIO_24_ENABLE_IRQ);
365 	if (err)
366 		return err;
367 
368 	err = devm_regmap_add_irq_chip(dev, idio24gpio->map, pdev->irq, 0, 0, chip, &chip_data);
369 	if (err)
370 		return dev_err_probe(dev, err, "IRQ registration failed\n");
371 
372 	gpio_config.parent = dev;
373 	gpio_config.regmap = idio24gpio->map;
374 	gpio_config.ngpio = IDIO_24_NGPIO;
375 	gpio_config.names = idio_24_names;
376 	gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
377 	gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
378 	gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(IDIO_24_CONTROL_REG);
379 	gpio_config.ngpio_per_reg = IDIO_24_NGPIO_PER_REG;
380 	gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
381 	gpio_config.reg_mask_xlate = idio_24_reg_mask_xlate;
382 	gpio_config.drvdata = idio24gpio->map;
383 
384 	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
385 }
386 
387 static const struct pci_device_id idio_24_pci_dev_id[] = {
388 	{ PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
389 	{ PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
390 	{ 0 }
391 };
392 MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
393 
394 static struct pci_driver idio_24_driver = {
395 	.name = "pcie-idio-24",
396 	.id_table = idio_24_pci_dev_id,
397 	.probe = idio_24_probe
398 };
399 
400 module_pci_driver(idio_24_driver);
401 
402 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
403 MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
404 MODULE_LICENSE("GPL v2");
405