1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * GPIO driver for the ACCES 104-DIO-48E series |
4 | * Copyright (C) 2016 William Breathitt Gray |
5 | * |
6 | * This driver supports the following ACCES devices: 104-DIO-48E and |
7 | * 104-DIO-24E. |
8 | */ |
9 | #include <linux/bits.h> |
10 | #include <linux/device.h> |
11 | #include <linux/err.h> |
12 | #include <linux/i8254.h> |
13 | #include <linux/ioport.h> |
14 | #include <linux/irq.h> |
15 | #include <linux/isa.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/moduleparam.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/spinlock.h> |
21 | #include <linux/types.h> |
22 | |
23 | #include "gpio-i8255.h" |
24 | |
25 | MODULE_IMPORT_NS(I8255); |
26 | |
27 | #define DIO48E_EXTENT 16 |
28 | #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT) |
29 | |
30 | static unsigned int base[MAX_NUM_DIO48E]; |
31 | static unsigned int num_dio48e; |
32 | module_param_hw_array(base, uint, ioport, &num_dio48e, 0); |
33 | MODULE_PARM_DESC(base, "ACCES 104-DIO-48E base addresses" ); |
34 | |
35 | static unsigned int irq[MAX_NUM_DIO48E]; |
36 | static unsigned int num_irq; |
37 | module_param_hw_array(irq, uint, irq, &num_irq, 0); |
38 | MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers" ); |
39 | |
40 | #define DIO48E_ENABLE_INTERRUPT 0xB |
41 | #define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT |
42 | #define DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING 0xD |
43 | #define DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING |
44 | #define DIO48E_CLEAR_INTERRUPT 0xF |
45 | |
46 | #define DIO48E_NUM_PPI 2 |
47 | |
48 | static const struct regmap_range dio48e_wr_ranges[] = { |
49 | regmap_reg_range(0x0, 0x9), regmap_reg_range(0xB, 0xB), |
50 | regmap_reg_range(0xD, 0xD), regmap_reg_range(0xF, 0xF), |
51 | }; |
52 | static const struct regmap_range dio48e_rd_ranges[] = { |
53 | regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x6), |
54 | regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD), |
55 | regmap_reg_range(0xF, 0xF), |
56 | }; |
57 | static const struct regmap_range dio48e_volatile_ranges[] = { |
58 | i8255_volatile_regmap_range(0x0), i8255_volatile_regmap_range(0x4), |
59 | regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD), |
60 | regmap_reg_range(0xF, 0xF), |
61 | }; |
62 | static const struct regmap_range dio48e_precious_ranges[] = { |
63 | regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD), |
64 | regmap_reg_range(0xF, 0xF), |
65 | }; |
66 | static const struct regmap_access_table dio48e_wr_table = { |
67 | .yes_ranges = dio48e_wr_ranges, |
68 | .n_yes_ranges = ARRAY_SIZE(dio48e_wr_ranges), |
69 | }; |
70 | static const struct regmap_access_table dio48e_rd_table = { |
71 | .yes_ranges = dio48e_rd_ranges, |
72 | .n_yes_ranges = ARRAY_SIZE(dio48e_rd_ranges), |
73 | }; |
74 | static const struct regmap_access_table dio48e_volatile_table = { |
75 | .yes_ranges = dio48e_volatile_ranges, |
76 | .n_yes_ranges = ARRAY_SIZE(dio48e_volatile_ranges), |
77 | }; |
78 | static const struct regmap_access_table dio48e_precious_table = { |
79 | .yes_ranges = dio48e_precious_ranges, |
80 | .n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges), |
81 | }; |
82 | |
83 | static const struct regmap_range pit_wr_ranges[] = { |
84 | regmap_reg_range(0x0, 0x3), |
85 | }; |
86 | static const struct regmap_range pit_rd_ranges[] = { |
87 | regmap_reg_range(0x0, 0x2), |
88 | }; |
89 | static const struct regmap_access_table pit_wr_table = { |
90 | .yes_ranges = pit_wr_ranges, |
91 | .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges), |
92 | }; |
93 | static const struct regmap_access_table pit_rd_table = { |
94 | .yes_ranges = pit_rd_ranges, |
95 | .n_yes_ranges = ARRAY_SIZE(pit_rd_ranges), |
96 | }; |
97 | |
98 | /* only bit 3 on each respective Port C supports interrupts */ |
99 | #define DIO48E_REGMAP_IRQ(_ppi) \ |
100 | [19 + (_ppi) * 24] = { \ |
101 | .mask = BIT(_ppi), \ |
102 | .type = { .types_supported = IRQ_TYPE_EDGE_RISING }, \ |
103 | } |
104 | |
105 | static const struct regmap_irq dio48e_regmap_irqs[] = { |
106 | DIO48E_REGMAP_IRQ(0), DIO48E_REGMAP_IRQ(1), |
107 | }; |
108 | |
109 | /** |
110 | * struct dio48e_gpio - GPIO device private data structure |
111 | * @lock: synchronization lock to prevent I/O race conditions |
112 | * @map: Regmap for the device |
113 | * @regs: virtual mapping for device registers |
114 | * @flags: IRQ flags saved during locking |
115 | * @irq_mask: Current IRQ mask state on the device |
116 | */ |
117 | struct dio48e_gpio { |
118 | raw_spinlock_t lock; |
119 | struct regmap *map; |
120 | void __iomem *regs; |
121 | unsigned long flags; |
122 | unsigned int irq_mask; |
123 | }; |
124 | |
125 | static void dio48e_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock) |
126 | { |
127 | struct dio48e_gpio *const dio48egpio = lock_arg; |
128 | unsigned long flags; |
129 | |
130 | raw_spin_lock_irqsave(&dio48egpio->lock, flags); |
131 | dio48egpio->flags = flags; |
132 | } |
133 | |
134 | static void dio48e_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock) |
135 | { |
136 | struct dio48e_gpio *const dio48egpio = lock_arg; |
137 | |
138 | raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags); |
139 | } |
140 | |
141 | static void pit_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock) |
142 | { |
143 | struct dio48e_gpio *const dio48egpio = lock_arg; |
144 | unsigned long flags; |
145 | |
146 | raw_spin_lock_irqsave(&dio48egpio->lock, flags); |
147 | dio48egpio->flags = flags; |
148 | |
149 | iowrite8(0x00, dio48egpio->regs + DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING); |
150 | } |
151 | |
152 | static void pit_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock) |
153 | { |
154 | struct dio48e_gpio *const dio48egpio = lock_arg; |
155 | |
156 | ioread8(dio48egpio->regs + DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING); |
157 | |
158 | raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags); |
159 | } |
160 | |
161 | static int dio48e_handle_mask_sync(const int index, |
162 | const unsigned int mask_buf_def, |
163 | const unsigned int mask_buf, |
164 | void *const irq_drv_data) |
165 | { |
166 | struct dio48e_gpio *const dio48egpio = irq_drv_data; |
167 | const unsigned int prev_mask = dio48egpio->irq_mask; |
168 | int err; |
169 | unsigned int val; |
170 | |
171 | /* exit early if no change since the previous mask */ |
172 | if (mask_buf == prev_mask) |
173 | return 0; |
174 | |
175 | /* remember the current mask for the next mask sync */ |
176 | dio48egpio->irq_mask = mask_buf; |
177 | |
178 | /* if all previously masked, enable interrupts when unmasking */ |
179 | if (prev_mask == mask_buf_def) { |
180 | err = regmap_write(map: dio48egpio->map, DIO48E_CLEAR_INTERRUPT, val: 0x00); |
181 | if (err) |
182 | return err; |
183 | return regmap_write(map: dio48egpio->map, DIO48E_ENABLE_INTERRUPT, val: 0x00); |
184 | } |
185 | |
186 | /* if all are currently masked, disable interrupts */ |
187 | if (mask_buf == mask_buf_def) |
188 | return regmap_read(map: dio48egpio->map, DIO48E_DISABLE_INTERRUPT, val: &val); |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | #define DIO48E_NGPIO 48 |
194 | static const char *dio48e_names[DIO48E_NGPIO] = { |
195 | "PPI Group 0 Port A 0" , "PPI Group 0 Port A 1" , "PPI Group 0 Port A 2" , |
196 | "PPI Group 0 Port A 3" , "PPI Group 0 Port A 4" , "PPI Group 0 Port A 5" , |
197 | "PPI Group 0 Port A 6" , "PPI Group 0 Port A 7" , "PPI Group 0 Port B 0" , |
198 | "PPI Group 0 Port B 1" , "PPI Group 0 Port B 2" , "PPI Group 0 Port B 3" , |
199 | "PPI Group 0 Port B 4" , "PPI Group 0 Port B 5" , "PPI Group 0 Port B 6" , |
200 | "PPI Group 0 Port B 7" , "PPI Group 0 Port C 0" , "PPI Group 0 Port C 1" , |
201 | "PPI Group 0 Port C 2" , "PPI Group 0 Port C 3" , "PPI Group 0 Port C 4" , |
202 | "PPI Group 0 Port C 5" , "PPI Group 0 Port C 6" , "PPI Group 0 Port C 7" , |
203 | "PPI Group 1 Port A 0" , "PPI Group 1 Port A 1" , "PPI Group 1 Port A 2" , |
204 | "PPI Group 1 Port A 3" , "PPI Group 1 Port A 4" , "PPI Group 1 Port A 5" , |
205 | "PPI Group 1 Port A 6" , "PPI Group 1 Port A 7" , "PPI Group 1 Port B 0" , |
206 | "PPI Group 1 Port B 1" , "PPI Group 1 Port B 2" , "PPI Group 1 Port B 3" , |
207 | "PPI Group 1 Port B 4" , "PPI Group 1 Port B 5" , "PPI Group 1 Port B 6" , |
208 | "PPI Group 1 Port B 7" , "PPI Group 1 Port C 0" , "PPI Group 1 Port C 1" , |
209 | "PPI Group 1 Port C 2" , "PPI Group 1 Port C 3" , "PPI Group 1 Port C 4" , |
210 | "PPI Group 1 Port C 5" , "PPI Group 1 Port C 6" , "PPI Group 1 Port C 7" |
211 | }; |
212 | |
213 | static int dio48e_irq_init_hw(struct regmap *const map) |
214 | { |
215 | unsigned int val; |
216 | |
217 | /* Disable IRQ by default */ |
218 | return regmap_read(map, DIO48E_DISABLE_INTERRUPT, val: &val); |
219 | } |
220 | |
221 | static int dio48e_probe(struct device *dev, unsigned int id) |
222 | { |
223 | const char *const name = dev_name(dev); |
224 | struct i8255_regmap_config config = {}; |
225 | void __iomem *regs; |
226 | struct regmap *map; |
227 | struct regmap_config dio48e_regmap_config; |
228 | struct regmap_config pit_regmap_config; |
229 | struct i8254_regmap_config pit_config; |
230 | int err; |
231 | struct regmap_irq_chip *chip; |
232 | struct dio48e_gpio *dio48egpio; |
233 | struct regmap_irq_chip_data *chip_data; |
234 | |
235 | if (!devm_request_region(dev, base[id], DIO48E_EXTENT, name)) { |
236 | dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n" , |
237 | base[id], base[id] + DIO48E_EXTENT); |
238 | return -EBUSY; |
239 | } |
240 | |
241 | dio48egpio = devm_kzalloc(dev, size: sizeof(*dio48egpio), GFP_KERNEL); |
242 | if (!dio48egpio) |
243 | return -ENOMEM; |
244 | |
245 | regs = devm_ioport_map(dev, port: base[id], DIO48E_EXTENT); |
246 | if (!regs) |
247 | return -ENOMEM; |
248 | |
249 | dio48egpio->regs = regs; |
250 | |
251 | raw_spin_lock_init(&dio48egpio->lock); |
252 | |
253 | dio48e_regmap_config = (struct regmap_config) { |
254 | .reg_bits = 8, |
255 | .reg_stride = 1, |
256 | .val_bits = 8, |
257 | .lock = dio48e_regmap_lock, |
258 | .unlock = dio48e_regmap_unlock, |
259 | .lock_arg = dio48egpio, |
260 | .io_port = true, |
261 | .wr_table = &dio48e_wr_table, |
262 | .rd_table = &dio48e_rd_table, |
263 | .volatile_table = &dio48e_volatile_table, |
264 | .precious_table = &dio48e_precious_table, |
265 | .cache_type = REGCACHE_FLAT, |
266 | }; |
267 | |
268 | map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config); |
269 | if (IS_ERR(ptr: map)) |
270 | return dev_err_probe(dev, err: PTR_ERR(ptr: map), |
271 | fmt: "Unable to initialize register map\n" ); |
272 | |
273 | dio48egpio->map = map; |
274 | |
275 | pit_regmap_config = (struct regmap_config) { |
276 | .name = "i8254" , |
277 | .reg_bits = 8, |
278 | .reg_stride = 1, |
279 | .val_bits = 8, |
280 | .lock = pit_regmap_lock, |
281 | .unlock = pit_regmap_unlock, |
282 | .lock_arg = dio48egpio, |
283 | .io_port = true, |
284 | .wr_table = &pit_wr_table, |
285 | .rd_table = &pit_rd_table, |
286 | }; |
287 | |
288 | pit_config.map = devm_regmap_init_mmio(dev, regs, &pit_regmap_config); |
289 | if (IS_ERR(ptr: pit_config.map)) |
290 | return dev_err_probe(dev, err: PTR_ERR(ptr: pit_config.map), |
291 | fmt: "Unable to initialize i8254 register map\n" ); |
292 | |
293 | chip = devm_kzalloc(dev, size: sizeof(*chip), GFP_KERNEL); |
294 | if (!chip) |
295 | return -ENOMEM; |
296 | |
297 | chip->name = name; |
298 | chip->mask_base = DIO48E_ENABLE_INTERRUPT; |
299 | chip->ack_base = DIO48E_CLEAR_INTERRUPT; |
300 | chip->no_status = true; |
301 | chip->num_regs = 1; |
302 | chip->irqs = dio48e_regmap_irqs; |
303 | chip->num_irqs = ARRAY_SIZE(dio48e_regmap_irqs); |
304 | chip->handle_mask_sync = dio48e_handle_mask_sync; |
305 | chip->irq_drv_data = dio48egpio; |
306 | |
307 | /* Initialize to prevent spurious interrupts before we're ready */ |
308 | err = dio48e_irq_init_hw(map); |
309 | if (err) |
310 | return err; |
311 | |
312 | err = devm_regmap_add_irq_chip(dev, map, irq: irq[id], irq_flags: 0, irq_base: 0, chip, data: &chip_data); |
313 | if (err) |
314 | return dev_err_probe(dev, err, fmt: "IRQ registration failed\n" ); |
315 | |
316 | pit_config.parent = dev; |
317 | |
318 | err = devm_i8254_regmap_register(dev, config: &pit_config); |
319 | if (err) |
320 | return err; |
321 | |
322 | config.parent = dev; |
323 | config.map = map; |
324 | config.num_ppi = DIO48E_NUM_PPI; |
325 | config.names = dio48e_names; |
326 | config.domain = regmap_irq_get_domain(data: chip_data); |
327 | |
328 | return devm_i8255_regmap_register(dev, config: &config); |
329 | } |
330 | |
331 | static struct isa_driver dio48e_driver = { |
332 | .probe = dio48e_probe, |
333 | .driver = { |
334 | .name = "104-dio-48e" |
335 | }, |
336 | }; |
337 | module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq); |
338 | |
339 | MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>" ); |
340 | MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver" ); |
341 | MODULE_LICENSE("GPL v2" ); |
342 | MODULE_IMPORT_NS(I8254); |
343 | |