xref: /linux/drivers/gpio/gpio-104-idi-48.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * GPIO driver for the ACCES 104-IDI-48 family
4  * Copyright (C) 2015 William Breathitt Gray
5  *
6  * This driver supports the following ACCES devices: 104-IDI-48A,
7  * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
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/interrupt.h>
14 #include <linux/ioport.h>
15 #include <linux/irq.h>
16 #include <linux/isa.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/moduleparam.h>
20 #include <linux/regmap.h>
21 #include <linux/types.h>
22 
23 #define IDI_48_EXTENT 8
24 #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
25 
26 static unsigned int base[MAX_NUM_IDI_48];
27 static unsigned int num_idi_48;
28 module_param_hw_array(base, uint, ioport, &num_idi_48, 0);
29 MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
30 
31 static unsigned int irq[MAX_NUM_IDI_48];
32 static unsigned int num_irq;
33 module_param_hw_array(irq, uint, irq, &num_irq, 0);
34 MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
35 
36 #define IDI48_IRQ_STATUS 0x7
37 #define IDI48_IRQ_ENABLE IDI48_IRQ_STATUS
38 
39 static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
40 				 unsigned int offset, unsigned int *reg,
41 				 unsigned int *mask)
42 {
43 	const unsigned int line = offset % 8;
44 	const unsigned int stride = offset / 8;
45 	const unsigned int port = (stride / 3) * 4;
46 	const unsigned int port_stride = stride % 3;
47 
48 	*reg = base + port + port_stride;
49 	*mask = BIT(line);
50 
51 	return 0;
52 }
53 
54 static const struct regmap_range idi_48_wr_ranges[] = {
55 	regmap_reg_range(0x0, 0x6),
56 };
57 static const struct regmap_range idi_48_rd_ranges[] = {
58 	regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x7),
59 };
60 static const struct regmap_range idi_48_precious_ranges[] = {
61 	regmap_reg_range(0x7, 0x7),
62 };
63 static const struct regmap_access_table idi_48_wr_table = {
64 	.no_ranges = idi_48_wr_ranges,
65 	.n_no_ranges = ARRAY_SIZE(idi_48_wr_ranges),
66 };
67 static const struct regmap_access_table idi_48_rd_table = {
68 	.yes_ranges = idi_48_rd_ranges,
69 	.n_yes_ranges = ARRAY_SIZE(idi_48_rd_ranges),
70 };
71 static const struct regmap_access_table idi_48_precious_table = {
72 	.yes_ranges = idi_48_precious_ranges,
73 	.n_yes_ranges = ARRAY_SIZE(idi_48_precious_ranges),
74 };
75 static const struct regmap_config idi48_regmap_config = {
76 	.reg_bits = 8,
77 	.reg_stride = 1,
78 	.val_bits = 8,
79 	.io_port = true,
80 	.max_register = 0x6,
81 	.wr_table = &idi_48_wr_table,
82 	.rd_table = &idi_48_rd_table,
83 	.precious_table = &idi_48_precious_table,
84 	.use_raw_spinlock = true,
85 };
86 
87 #define IDI48_NGPIO 48
88 
89 #define IDI48_REGMAP_IRQ(_id)						\
90 	[_id] = {							\
91 		.mask = BIT((_id) / 8),					\
92 		.type = { .types_supported = IRQ_TYPE_EDGE_BOTH },	\
93 	}
94 
95 static const struct regmap_irq idi48_regmap_irqs[IDI48_NGPIO] = {
96 	IDI48_REGMAP_IRQ(0), IDI48_REGMAP_IRQ(1), IDI48_REGMAP_IRQ(2), /* 0-2 */
97 	IDI48_REGMAP_IRQ(3), IDI48_REGMAP_IRQ(4), IDI48_REGMAP_IRQ(5), /* 3-5 */
98 	IDI48_REGMAP_IRQ(6), IDI48_REGMAP_IRQ(7), IDI48_REGMAP_IRQ(8), /* 6-8 */
99 	IDI48_REGMAP_IRQ(9), IDI48_REGMAP_IRQ(10), IDI48_REGMAP_IRQ(11), /* 9-11 */
100 	IDI48_REGMAP_IRQ(12), IDI48_REGMAP_IRQ(13), IDI48_REGMAP_IRQ(14), /* 12-14 */
101 	IDI48_REGMAP_IRQ(15), IDI48_REGMAP_IRQ(16), IDI48_REGMAP_IRQ(17), /* 15-17 */
102 	IDI48_REGMAP_IRQ(18), IDI48_REGMAP_IRQ(19), IDI48_REGMAP_IRQ(20), /* 18-20 */
103 	IDI48_REGMAP_IRQ(21), IDI48_REGMAP_IRQ(22), IDI48_REGMAP_IRQ(23), /* 21-23 */
104 	IDI48_REGMAP_IRQ(24), IDI48_REGMAP_IRQ(25), IDI48_REGMAP_IRQ(26), /* 24-26 */
105 	IDI48_REGMAP_IRQ(27), IDI48_REGMAP_IRQ(28), IDI48_REGMAP_IRQ(29), /* 27-29 */
106 	IDI48_REGMAP_IRQ(30), IDI48_REGMAP_IRQ(31), IDI48_REGMAP_IRQ(32), /* 30-32 */
107 	IDI48_REGMAP_IRQ(33), IDI48_REGMAP_IRQ(34), IDI48_REGMAP_IRQ(35), /* 33-35 */
108 	IDI48_REGMAP_IRQ(36), IDI48_REGMAP_IRQ(37), IDI48_REGMAP_IRQ(38), /* 36-38 */
109 	IDI48_REGMAP_IRQ(39), IDI48_REGMAP_IRQ(40), IDI48_REGMAP_IRQ(41), /* 39-41 */
110 	IDI48_REGMAP_IRQ(42), IDI48_REGMAP_IRQ(43), IDI48_REGMAP_IRQ(44), /* 42-44 */
111 	IDI48_REGMAP_IRQ(45), IDI48_REGMAP_IRQ(46), IDI48_REGMAP_IRQ(47), /* 45-47 */
112 };
113 
114 static const char *idi48_names[IDI48_NGPIO] = {
115 	"Bit 0 A", "Bit 1 A", "Bit 2 A", "Bit 3 A", "Bit 4 A", "Bit 5 A",
116 	"Bit 6 A", "Bit 7 A", "Bit 8 A", "Bit 9 A", "Bit 10 A", "Bit 11 A",
117 	"Bit 12 A", "Bit 13 A", "Bit 14 A", "Bit 15 A",	"Bit 16 A", "Bit 17 A",
118 	"Bit 18 A", "Bit 19 A", "Bit 20 A", "Bit 21 A", "Bit 22 A", "Bit 23 A",
119 	"Bit 0 B", "Bit 1 B", "Bit 2 B", "Bit 3 B", "Bit 4 B", "Bit 5 B",
120 	"Bit 6 B", "Bit 7 B", "Bit 8 B", "Bit 9 B", "Bit 10 B", "Bit 11 B",
121 	"Bit 12 B", "Bit 13 B", "Bit 14 B", "Bit 15 B",	"Bit 16 B", "Bit 17 B",
122 	"Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
123 };
124 
125 static int idi_48_probe(struct device *dev, unsigned int id)
126 {
127 	const char *const name = dev_name(dev);
128 	struct gpio_regmap_config config = {};
129 	void __iomem *regs;
130 	struct regmap *map;
131 	struct regmap_irq_chip *chip;
132 	struct regmap_irq_chip_data *chip_data;
133 	int err;
134 
135 	if (!devm_request_region(dev, base[id], IDI_48_EXTENT, name)) {
136 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
137 			base[id], base[id] + IDI_48_EXTENT);
138 		return -EBUSY;
139 	}
140 
141 	regs = devm_ioport_map(dev, base[id], IDI_48_EXTENT);
142 	if (!regs)
143 		return -ENOMEM;
144 
145 	map = devm_regmap_init_mmio(dev, regs, &idi48_regmap_config);
146 	if (IS_ERR(map))
147 		return dev_err_probe(dev, PTR_ERR(map),
148 				     "Unable to initialize register map\n");
149 
150 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
151 	if (!chip)
152 		return -ENOMEM;
153 
154 	chip->name = name;
155 	chip->status_base = IDI48_IRQ_STATUS;
156 	chip->unmask_base = IDI48_IRQ_ENABLE;
157 	chip->clear_on_unmask = true;
158 	chip->num_regs = 1;
159 	chip->irqs = idi48_regmap_irqs;
160 	chip->num_irqs = ARRAY_SIZE(idi48_regmap_irqs);
161 
162 	err = devm_regmap_add_irq_chip(dev, map, irq[id], IRQF_SHARED, 0, chip,
163 				       &chip_data);
164 	if (err)
165 		return dev_err_probe(dev, err, "IRQ registration failed\n");
166 
167 	config.parent = dev;
168 	config.regmap = map;
169 	config.ngpio = IDI48_NGPIO;
170 	config.names = idi48_names;
171 	config.reg_dat_base = GPIO_REGMAP_ADDR(0x0);
172 	config.ngpio_per_reg = 8;
173 	config.reg_mask_xlate = idi_48_reg_mask_xlate;
174 	config.irq_domain = regmap_irq_get_domain(chip_data);
175 
176 	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
177 }
178 
179 static struct isa_driver idi_48_driver = {
180 	.probe = idi_48_probe,
181 	.driver = {
182 		.name = "104-idi-48"
183 	},
184 };
185 module_isa_driver_with_irq(idi_48_driver, num_idi_48, num_irq);
186 
187 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
188 MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
189 MODULE_LICENSE("GPL v2");
190