1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Turris Mox module configuration bus driver |
4 | * |
5 | * Copyright (C) 2019 Marek BehĂșn <kabel@kernel.org> |
6 | */ |
7 | |
8 | #include <dt-bindings/bus/moxtet.h> |
9 | #include <linux/bitops.h> |
10 | #include <linux/debugfs.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/module.h> |
13 | #include <linux/moxtet.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/of_device.h> |
16 | #include <linux/of_irq.h> |
17 | #include <linux/spi/spi.h> |
18 | |
19 | /* |
20 | * @name: module name for sysfs |
21 | * @hwirq_base: base index for IRQ for this module (-1 if no IRQs) |
22 | * @nirqs: how many interrupts does the shift register provide |
23 | * @desc: module description for kernel log |
24 | */ |
25 | static const struct { |
26 | const char *name; |
27 | int hwirq_base; |
28 | int nirqs; |
29 | const char *desc; |
30 | } mox_module_table[] = { |
31 | /* do not change order of this array! */ |
32 | { NULL, 0, 0, NULL }, |
33 | { "sfp" , -1, 0, "MOX D (SFP cage)" }, |
34 | { "pci" , MOXTET_IRQ_PCI, 1, "MOX B (Mini-PCIe)" }, |
35 | { "topaz" , MOXTET_IRQ_TOPAZ, 1, "MOX C (4 port switch)" }, |
36 | { "peridot" , MOXTET_IRQ_PERIDOT(0), 1, "MOX E (8 port switch)" }, |
37 | { "usb3" , MOXTET_IRQ_USB3, 2, "MOX F (USB 3.0)" }, |
38 | { "pci-bridge" , -1, 0, "MOX G (Mini-PCIe bridge)" }, |
39 | }; |
40 | |
41 | static inline bool mox_module_known(unsigned int id) |
42 | { |
43 | return id >= TURRIS_MOX_MODULE_FIRST && id <= TURRIS_MOX_MODULE_LAST; |
44 | } |
45 | |
46 | static inline const char *mox_module_name(unsigned int id) |
47 | { |
48 | if (mox_module_known(id)) |
49 | return mox_module_table[id].name; |
50 | else |
51 | return "unknown" ; |
52 | } |
53 | |
54 | #define DEF_MODULE_ATTR(name, fmt, ...) \ |
55 | static ssize_t \ |
56 | module_##name##_show(struct device *dev, struct device_attribute *a, \ |
57 | char *buf) \ |
58 | { \ |
59 | struct moxtet_device *mdev = to_moxtet_device(dev); \ |
60 | return sprintf(buf, (fmt), __VA_ARGS__); \ |
61 | } \ |
62 | static DEVICE_ATTR_RO(module_##name) |
63 | |
64 | DEF_MODULE_ATTR(id, "0x%x\n" , mdev->id); |
65 | DEF_MODULE_ATTR(name, "%s\n" , mox_module_name(mdev->id)); |
66 | DEF_MODULE_ATTR(description, "%s\n" , |
67 | mox_module_known(mdev->id) ? mox_module_table[mdev->id].desc |
68 | : "" ); |
69 | |
70 | static struct attribute *moxtet_dev_attrs[] = { |
71 | &dev_attr_module_id.attr, |
72 | &dev_attr_module_name.attr, |
73 | &dev_attr_module_description.attr, |
74 | NULL, |
75 | }; |
76 | |
77 | static const struct attribute_group moxtet_dev_group = { |
78 | .attrs = moxtet_dev_attrs, |
79 | }; |
80 | |
81 | static const struct attribute_group *moxtet_dev_groups[] = { |
82 | &moxtet_dev_group, |
83 | NULL, |
84 | }; |
85 | |
86 | static int moxtet_match(struct device *dev, struct device_driver *drv) |
87 | { |
88 | struct moxtet_device *mdev = to_moxtet_device(dev); |
89 | struct moxtet_driver *tdrv = to_moxtet_driver(drv); |
90 | const enum turris_mox_module_id *t; |
91 | |
92 | if (of_driver_match_device(dev, drv)) |
93 | return 1; |
94 | |
95 | if (!tdrv->id_table) |
96 | return 0; |
97 | |
98 | for (t = tdrv->id_table; *t; ++t) |
99 | if (*t == mdev->id) |
100 | return 1; |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static const struct bus_type moxtet_bus_type = { |
106 | .name = "moxtet" , |
107 | .dev_groups = moxtet_dev_groups, |
108 | .match = moxtet_match, |
109 | }; |
110 | |
111 | int __moxtet_register_driver(struct module *owner, |
112 | struct moxtet_driver *mdrv) |
113 | { |
114 | mdrv->driver.owner = owner; |
115 | mdrv->driver.bus = &moxtet_bus_type; |
116 | return driver_register(drv: &mdrv->driver); |
117 | } |
118 | EXPORT_SYMBOL_GPL(__moxtet_register_driver); |
119 | |
120 | static int moxtet_dev_check(struct device *dev, void *data) |
121 | { |
122 | struct moxtet_device *mdev = to_moxtet_device(dev); |
123 | struct moxtet_device *new_dev = data; |
124 | |
125 | if (mdev->moxtet == new_dev->moxtet && mdev->id == new_dev->id && |
126 | mdev->idx == new_dev->idx) |
127 | return -EBUSY; |
128 | return 0; |
129 | } |
130 | |
131 | static void moxtet_dev_release(struct device *dev) |
132 | { |
133 | struct moxtet_device *mdev = to_moxtet_device(dev); |
134 | |
135 | put_device(dev: mdev->moxtet->dev); |
136 | kfree(objp: mdev); |
137 | } |
138 | |
139 | static struct moxtet_device * |
140 | moxtet_alloc_device(struct moxtet *moxtet) |
141 | { |
142 | struct moxtet_device *dev; |
143 | |
144 | if (!get_device(dev: moxtet->dev)) |
145 | return NULL; |
146 | |
147 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
148 | if (!dev) { |
149 | put_device(dev: moxtet->dev); |
150 | return NULL; |
151 | } |
152 | |
153 | dev->moxtet = moxtet; |
154 | dev->dev.parent = moxtet->dev; |
155 | dev->dev.bus = &moxtet_bus_type; |
156 | dev->dev.release = moxtet_dev_release; |
157 | |
158 | device_initialize(dev: &dev->dev); |
159 | |
160 | return dev; |
161 | } |
162 | |
163 | static int moxtet_add_device(struct moxtet_device *dev) |
164 | { |
165 | static DEFINE_MUTEX(add_mutex); |
166 | int ret; |
167 | |
168 | if (dev->idx >= TURRIS_MOX_MAX_MODULES || dev->id > 0xf) |
169 | return -EINVAL; |
170 | |
171 | dev_set_name(dev: &dev->dev, name: "moxtet-%s.%u" , mox_module_name(id: dev->id), |
172 | dev->idx); |
173 | |
174 | mutex_lock(&add_mutex); |
175 | |
176 | ret = bus_for_each_dev(bus: &moxtet_bus_type, NULL, data: dev, |
177 | fn: moxtet_dev_check); |
178 | if (ret) |
179 | goto done; |
180 | |
181 | ret = device_add(dev: &dev->dev); |
182 | if (ret < 0) |
183 | dev_err(dev->moxtet->dev, "can't add %s, status %d\n" , |
184 | dev_name(dev->moxtet->dev), ret); |
185 | |
186 | done: |
187 | mutex_unlock(lock: &add_mutex); |
188 | return ret; |
189 | } |
190 | |
191 | static int __unregister(struct device *dev, void *null) |
192 | { |
193 | if (dev->of_node) { |
194 | of_node_clear_flag(n: dev->of_node, OF_POPULATED); |
195 | of_node_put(node: dev->of_node); |
196 | } |
197 | |
198 | device_unregister(dev); |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | static struct moxtet_device * |
204 | of_register_moxtet_device(struct moxtet *moxtet, struct device_node *nc) |
205 | { |
206 | struct moxtet_device *dev; |
207 | u32 val; |
208 | int ret; |
209 | |
210 | dev = moxtet_alloc_device(moxtet); |
211 | if (!dev) { |
212 | dev_err(moxtet->dev, |
213 | "Moxtet device alloc error for %pOF\n" , nc); |
214 | return ERR_PTR(error: -ENOMEM); |
215 | } |
216 | |
217 | ret = of_property_read_u32(np: nc, propname: "reg" , out_value: &val); |
218 | if (ret) { |
219 | dev_err(moxtet->dev, "%pOF has no valid 'reg' property (%d)\n" , |
220 | nc, ret); |
221 | goto err_put; |
222 | } |
223 | |
224 | dev->idx = val; |
225 | |
226 | if (dev->idx >= TURRIS_MOX_MAX_MODULES) { |
227 | dev_err(moxtet->dev, "%pOF Moxtet address 0x%x out of range\n" , |
228 | nc, dev->idx); |
229 | ret = -EINVAL; |
230 | goto err_put; |
231 | } |
232 | |
233 | dev->id = moxtet->modules[dev->idx]; |
234 | |
235 | if (!dev->id) { |
236 | dev_err(moxtet->dev, "%pOF Moxtet address 0x%x is empty\n" , nc, |
237 | dev->idx); |
238 | ret = -ENODEV; |
239 | goto err_put; |
240 | } |
241 | |
242 | of_node_get(node: nc); |
243 | dev->dev.of_node = nc; |
244 | |
245 | ret = moxtet_add_device(dev); |
246 | if (ret) { |
247 | dev_err(moxtet->dev, |
248 | "Moxtet device register error for %pOF\n" , nc); |
249 | of_node_put(node: nc); |
250 | goto err_put; |
251 | } |
252 | |
253 | return dev; |
254 | |
255 | err_put: |
256 | put_device(dev: &dev->dev); |
257 | return ERR_PTR(error: ret); |
258 | } |
259 | |
260 | static void of_register_moxtet_devices(struct moxtet *moxtet) |
261 | { |
262 | struct moxtet_device *dev; |
263 | struct device_node *nc; |
264 | |
265 | if (!moxtet->dev->of_node) |
266 | return; |
267 | |
268 | for_each_available_child_of_node(moxtet->dev->of_node, nc) { |
269 | if (of_node_test_and_set_flag(n: nc, OF_POPULATED)) |
270 | continue; |
271 | dev = of_register_moxtet_device(moxtet, nc); |
272 | if (IS_ERR(ptr: dev)) { |
273 | dev_warn(moxtet->dev, |
274 | "Failed to create Moxtet device for %pOF\n" , |
275 | nc); |
276 | of_node_clear_flag(n: nc, OF_POPULATED); |
277 | } |
278 | } |
279 | } |
280 | |
281 | static void |
282 | moxtet_register_devices_from_topology(struct moxtet *moxtet) |
283 | { |
284 | struct moxtet_device *dev; |
285 | int i, ret; |
286 | |
287 | for (i = 0; i < moxtet->count; ++i) { |
288 | dev = moxtet_alloc_device(moxtet); |
289 | if (!dev) { |
290 | dev_err(moxtet->dev, "Moxtet device %u alloc error\n" , |
291 | i); |
292 | continue; |
293 | } |
294 | |
295 | dev->idx = i; |
296 | dev->id = moxtet->modules[i]; |
297 | |
298 | ret = moxtet_add_device(dev); |
299 | if (ret && ret != -EBUSY) { |
300 | put_device(dev: &dev->dev); |
301 | dev_err(moxtet->dev, |
302 | "Moxtet device %u register error: %i\n" , i, |
303 | ret); |
304 | } |
305 | } |
306 | } |
307 | |
308 | /* |
309 | * @nsame: how many modules with same id are already in moxtet->modules |
310 | */ |
311 | static int moxtet_set_irq(struct moxtet *moxtet, int idx, int id, int nsame) |
312 | { |
313 | int i, first; |
314 | struct moxtet_irqpos *pos; |
315 | |
316 | first = mox_module_table[id].hwirq_base + |
317 | nsame * mox_module_table[id].nirqs; |
318 | |
319 | if (first + mox_module_table[id].nirqs > MOXTET_NIRQS) |
320 | return -EINVAL; |
321 | |
322 | for (i = 0; i < mox_module_table[id].nirqs; ++i) { |
323 | pos = &moxtet->irq.position[first + i]; |
324 | pos->idx = idx; |
325 | pos->bit = i; |
326 | moxtet->irq.exists |= BIT(first + i); |
327 | } |
328 | |
329 | return 0; |
330 | } |
331 | |
332 | static int moxtet_find_topology(struct moxtet *moxtet) |
333 | { |
334 | u8 buf[TURRIS_MOX_MAX_MODULES]; |
335 | int cnts[TURRIS_MOX_MODULE_LAST]; |
336 | int i, ret; |
337 | |
338 | memset(cnts, 0, sizeof(cnts)); |
339 | |
340 | ret = spi_read(spi: to_spi_device(dev: moxtet->dev), buf, TURRIS_MOX_MAX_MODULES); |
341 | if (ret < 0) |
342 | return ret; |
343 | |
344 | if (buf[0] == TURRIS_MOX_CPU_ID_EMMC) { |
345 | dev_info(moxtet->dev, "Found MOX A (eMMC CPU) module\n" ); |
346 | } else if (buf[0] == TURRIS_MOX_CPU_ID_SD) { |
347 | dev_info(moxtet->dev, "Found MOX A (CPU) module\n" ); |
348 | } else { |
349 | dev_err(moxtet->dev, "Invalid Turris MOX A CPU module 0x%02x\n" , |
350 | buf[0]); |
351 | return -ENODEV; |
352 | } |
353 | |
354 | moxtet->count = 0; |
355 | |
356 | for (i = 1; i < TURRIS_MOX_MAX_MODULES; ++i) { |
357 | int id; |
358 | |
359 | if (buf[i] == 0xff) |
360 | break; |
361 | |
362 | id = buf[i] & 0xf; |
363 | |
364 | moxtet->modules[i-1] = id; |
365 | ++moxtet->count; |
366 | |
367 | if (mox_module_known(id)) { |
368 | dev_info(moxtet->dev, "Found %s module\n" , |
369 | mox_module_table[id].desc); |
370 | |
371 | if (moxtet_set_irq(moxtet, idx: i-1, id, nsame: cnts[id]++) < 0) |
372 | dev_err(moxtet->dev, |
373 | " Cannot set IRQ for module %s\n" , |
374 | mox_module_table[id].desc); |
375 | } else { |
376 | dev_warn(moxtet->dev, |
377 | "Unknown Moxtet module found (ID 0x%02x)\n" , |
378 | id); |
379 | } |
380 | } |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static int moxtet_spi_read(struct moxtet *moxtet, u8 *buf) |
386 | { |
387 | struct spi_transfer xfer = { |
388 | .rx_buf = buf, |
389 | .tx_buf = moxtet->tx, |
390 | .len = moxtet->count + 1 |
391 | }; |
392 | int ret; |
393 | |
394 | mutex_lock(&moxtet->lock); |
395 | |
396 | ret = spi_sync_transfer(spi: to_spi_device(dev: moxtet->dev), xfers: &xfer, num_xfers: 1); |
397 | |
398 | mutex_unlock(lock: &moxtet->lock); |
399 | |
400 | return ret; |
401 | } |
402 | |
403 | int moxtet_device_read(struct device *dev) |
404 | { |
405 | struct moxtet_device *mdev = to_moxtet_device(dev); |
406 | struct moxtet *moxtet = mdev->moxtet; |
407 | u8 buf[TURRIS_MOX_MAX_MODULES]; |
408 | int ret; |
409 | |
410 | if (mdev->idx >= moxtet->count) |
411 | return -EINVAL; |
412 | |
413 | ret = moxtet_spi_read(moxtet, buf); |
414 | if (ret < 0) |
415 | return ret; |
416 | |
417 | return buf[mdev->idx + 1] >> 4; |
418 | } |
419 | EXPORT_SYMBOL_GPL(moxtet_device_read); |
420 | |
421 | int moxtet_device_write(struct device *dev, u8 val) |
422 | { |
423 | struct moxtet_device *mdev = to_moxtet_device(dev); |
424 | struct moxtet *moxtet = mdev->moxtet; |
425 | int ret; |
426 | |
427 | if (mdev->idx >= moxtet->count) |
428 | return -EINVAL; |
429 | |
430 | mutex_lock(&moxtet->lock); |
431 | |
432 | moxtet->tx[moxtet->count - mdev->idx] = val; |
433 | |
434 | ret = spi_write(spi: to_spi_device(dev: moxtet->dev), buf: moxtet->tx, |
435 | len: moxtet->count + 1); |
436 | |
437 | mutex_unlock(lock: &moxtet->lock); |
438 | |
439 | return ret; |
440 | } |
441 | EXPORT_SYMBOL_GPL(moxtet_device_write); |
442 | |
443 | int moxtet_device_written(struct device *dev) |
444 | { |
445 | struct moxtet_device *mdev = to_moxtet_device(dev); |
446 | struct moxtet *moxtet = mdev->moxtet; |
447 | |
448 | if (mdev->idx >= moxtet->count) |
449 | return -EINVAL; |
450 | |
451 | return moxtet->tx[moxtet->count - mdev->idx]; |
452 | } |
453 | EXPORT_SYMBOL_GPL(moxtet_device_written); |
454 | |
455 | #ifdef CONFIG_DEBUG_FS |
456 | static int moxtet_debug_open(struct inode *inode, struct file *file) |
457 | { |
458 | file->private_data = inode->i_private; |
459 | |
460 | return nonseekable_open(inode, filp: file); |
461 | } |
462 | |
463 | static ssize_t input_read(struct file *file, char __user *buf, size_t len, |
464 | loff_t *ppos) |
465 | { |
466 | struct moxtet *moxtet = file->private_data; |
467 | u8 bin[TURRIS_MOX_MAX_MODULES]; |
468 | u8 hex[sizeof(bin) * 2 + 1]; |
469 | int ret, n; |
470 | |
471 | ret = moxtet_spi_read(moxtet, buf: bin); |
472 | if (ret < 0) |
473 | return ret; |
474 | |
475 | n = moxtet->count + 1; |
476 | bin2hex(dst: hex, src: bin, count: n); |
477 | |
478 | hex[2*n] = '\n'; |
479 | |
480 | return simple_read_from_buffer(to: buf, count: len, ppos, from: hex, available: 2*n + 1); |
481 | } |
482 | |
483 | static const struct file_operations input_fops = { |
484 | .owner = THIS_MODULE, |
485 | .open = moxtet_debug_open, |
486 | .read = input_read, |
487 | .llseek = no_llseek, |
488 | }; |
489 | |
490 | static ssize_t output_read(struct file *file, char __user *buf, size_t len, |
491 | loff_t *ppos) |
492 | { |
493 | struct moxtet *moxtet = file->private_data; |
494 | u8 hex[TURRIS_MOX_MAX_MODULES * 2 + 1]; |
495 | u8 *p = hex; |
496 | int i; |
497 | |
498 | mutex_lock(&moxtet->lock); |
499 | |
500 | for (i = 0; i < moxtet->count; ++i) |
501 | p = hex_byte_pack(buf: p, byte: moxtet->tx[moxtet->count - i]); |
502 | |
503 | mutex_unlock(lock: &moxtet->lock); |
504 | |
505 | *p++ = '\n'; |
506 | |
507 | return simple_read_from_buffer(to: buf, count: len, ppos, from: hex, available: p - hex); |
508 | } |
509 | |
510 | static ssize_t output_write(struct file *file, const char __user *buf, |
511 | size_t len, loff_t *ppos) |
512 | { |
513 | struct moxtet *moxtet = file->private_data; |
514 | u8 bin[TURRIS_MOX_MAX_MODULES]; |
515 | u8 hex[sizeof(bin) * 2 + 1]; |
516 | ssize_t res; |
517 | loff_t dummy = 0; |
518 | int err, i; |
519 | |
520 | if (len > 2 * moxtet->count + 1 || len < 2 * moxtet->count) |
521 | return -EINVAL; |
522 | |
523 | res = simple_write_to_buffer(to: hex, available: sizeof(hex), ppos: &dummy, from: buf, count: len); |
524 | if (res < 0) |
525 | return res; |
526 | |
527 | if (len % 2 == 1 && hex[len - 1] != '\n') |
528 | return -EINVAL; |
529 | |
530 | err = hex2bin(dst: bin, src: hex, count: moxtet->count); |
531 | if (err < 0) |
532 | return -EINVAL; |
533 | |
534 | mutex_lock(&moxtet->lock); |
535 | |
536 | for (i = 0; i < moxtet->count; ++i) |
537 | moxtet->tx[moxtet->count - i] = bin[i]; |
538 | |
539 | err = spi_write(spi: to_spi_device(dev: moxtet->dev), buf: moxtet->tx, |
540 | len: moxtet->count + 1); |
541 | |
542 | mutex_unlock(lock: &moxtet->lock); |
543 | |
544 | return err < 0 ? err : len; |
545 | } |
546 | |
547 | static const struct file_operations output_fops = { |
548 | .owner = THIS_MODULE, |
549 | .open = moxtet_debug_open, |
550 | .read = output_read, |
551 | .write = output_write, |
552 | .llseek = no_llseek, |
553 | }; |
554 | |
555 | static int moxtet_register_debugfs(struct moxtet *moxtet) |
556 | { |
557 | struct dentry *root, *entry; |
558 | |
559 | root = debugfs_create_dir(name: "moxtet" , NULL); |
560 | |
561 | if (IS_ERR(ptr: root)) |
562 | return PTR_ERR(ptr: root); |
563 | |
564 | entry = debugfs_create_file_unsafe(name: "input" , mode: 0444, parent: root, data: moxtet, |
565 | fops: &input_fops); |
566 | if (IS_ERR(ptr: entry)) |
567 | goto err_remove; |
568 | |
569 | entry = debugfs_create_file_unsafe(name: "output" , mode: 0644, parent: root, data: moxtet, |
570 | fops: &output_fops); |
571 | if (IS_ERR(ptr: entry)) |
572 | goto err_remove; |
573 | |
574 | moxtet->debugfs_root = root; |
575 | |
576 | return 0; |
577 | err_remove: |
578 | debugfs_remove_recursive(dentry: root); |
579 | return PTR_ERR(ptr: entry); |
580 | } |
581 | |
582 | static void moxtet_unregister_debugfs(struct moxtet *moxtet) |
583 | { |
584 | debugfs_remove_recursive(dentry: moxtet->debugfs_root); |
585 | } |
586 | #else |
587 | static inline int moxtet_register_debugfs(struct moxtet *moxtet) |
588 | { |
589 | return 0; |
590 | } |
591 | |
592 | static inline void moxtet_unregister_debugfs(struct moxtet *moxtet) |
593 | { |
594 | } |
595 | #endif |
596 | |
597 | static int moxtet_irq_domain_map(struct irq_domain *d, unsigned int irq, |
598 | irq_hw_number_t hw) |
599 | { |
600 | struct moxtet *moxtet = d->host_data; |
601 | |
602 | if (hw >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(hw))) { |
603 | dev_err(moxtet->dev, "Invalid hw irq number\n" ); |
604 | return -EINVAL; |
605 | } |
606 | |
607 | irq_set_chip_data(irq, data: d->host_data); |
608 | irq_set_chip_and_handler(irq, chip: &moxtet->irq.chip, handle: handle_level_irq); |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | static int moxtet_irq_domain_xlate(struct irq_domain *d, |
614 | struct device_node *ctrlr, |
615 | const u32 *intspec, unsigned int intsize, |
616 | unsigned long *out_hwirq, |
617 | unsigned int *out_type) |
618 | { |
619 | struct moxtet *moxtet = d->host_data; |
620 | int irq; |
621 | |
622 | if (WARN_ON(intsize < 1)) |
623 | return -EINVAL; |
624 | |
625 | irq = intspec[0]; |
626 | |
627 | if (irq >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(irq))) |
628 | return -EINVAL; |
629 | |
630 | *out_hwirq = irq; |
631 | *out_type = IRQ_TYPE_NONE; |
632 | return 0; |
633 | } |
634 | |
635 | static const struct irq_domain_ops moxtet_irq_domain = { |
636 | .map = moxtet_irq_domain_map, |
637 | .xlate = moxtet_irq_domain_xlate, |
638 | }; |
639 | |
640 | static void moxtet_irq_mask(struct irq_data *d) |
641 | { |
642 | struct moxtet *moxtet = irq_data_get_irq_chip_data(d); |
643 | |
644 | moxtet->irq.masked |= BIT(d->hwirq); |
645 | } |
646 | |
647 | static void moxtet_irq_unmask(struct irq_data *d) |
648 | { |
649 | struct moxtet *moxtet = irq_data_get_irq_chip_data(d); |
650 | |
651 | moxtet->irq.masked &= ~BIT(d->hwirq); |
652 | } |
653 | |
654 | static void moxtet_irq_print_chip(struct irq_data *d, struct seq_file *p) |
655 | { |
656 | struct moxtet *moxtet = irq_data_get_irq_chip_data(d); |
657 | struct moxtet_irqpos *pos = &moxtet->irq.position[d->hwirq]; |
658 | int id; |
659 | |
660 | id = moxtet->modules[pos->idx]; |
661 | |
662 | seq_printf(m: p, fmt: " moxtet-%s.%i#%i" , mox_module_name(id), pos->idx, |
663 | pos->bit); |
664 | } |
665 | |
666 | static const struct irq_chip moxtet_irq_chip = { |
667 | .name = "moxtet" , |
668 | .irq_mask = moxtet_irq_mask, |
669 | .irq_unmask = moxtet_irq_unmask, |
670 | .irq_print_chip = moxtet_irq_print_chip, |
671 | }; |
672 | |
673 | static int moxtet_irq_read(struct moxtet *moxtet, unsigned long *map) |
674 | { |
675 | struct moxtet_irqpos *pos = moxtet->irq.position; |
676 | u8 buf[TURRIS_MOX_MAX_MODULES]; |
677 | int i, ret; |
678 | |
679 | ret = moxtet_spi_read(moxtet, buf); |
680 | if (ret < 0) |
681 | return ret; |
682 | |
683 | *map = 0; |
684 | |
685 | for_each_set_bit(i, &moxtet->irq.exists, MOXTET_NIRQS) { |
686 | if (!(buf[pos[i].idx + 1] & BIT(4 + pos[i].bit))) |
687 | set_bit(nr: i, addr: map); |
688 | } |
689 | |
690 | return 0; |
691 | } |
692 | |
693 | static irqreturn_t moxtet_irq_thread_fn(int irq, void *data) |
694 | { |
695 | struct moxtet *moxtet = data; |
696 | unsigned long set; |
697 | int nhandled = 0, i, sub_irq, ret; |
698 | |
699 | ret = moxtet_irq_read(moxtet, map: &set); |
700 | if (ret < 0) |
701 | goto out; |
702 | |
703 | set &= ~moxtet->irq.masked; |
704 | |
705 | do { |
706 | for_each_set_bit(i, &set, MOXTET_NIRQS) { |
707 | sub_irq = irq_find_mapping(domain: moxtet->irq.domain, hwirq: i); |
708 | handle_nested_irq(irq: sub_irq); |
709 | dev_dbg(moxtet->dev, "%i irq\n" , i); |
710 | ++nhandled; |
711 | } |
712 | |
713 | ret = moxtet_irq_read(moxtet, map: &set); |
714 | if (ret < 0) |
715 | goto out; |
716 | |
717 | set &= ~moxtet->irq.masked; |
718 | } while (set); |
719 | |
720 | out: |
721 | return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); |
722 | } |
723 | |
724 | static void moxtet_irq_free(struct moxtet *moxtet) |
725 | { |
726 | int i, irq; |
727 | |
728 | for (i = 0; i < MOXTET_NIRQS; ++i) { |
729 | if (moxtet->irq.exists & BIT(i)) { |
730 | irq = irq_find_mapping(domain: moxtet->irq.domain, hwirq: i); |
731 | irq_dispose_mapping(virq: irq); |
732 | } |
733 | } |
734 | |
735 | irq_domain_remove(host: moxtet->irq.domain); |
736 | } |
737 | |
738 | static int moxtet_irq_setup(struct moxtet *moxtet) |
739 | { |
740 | int i, ret; |
741 | |
742 | moxtet->irq.domain = irq_domain_add_simple(of_node: moxtet->dev->of_node, |
743 | MOXTET_NIRQS, first_irq: 0, |
744 | ops: &moxtet_irq_domain, host_data: moxtet); |
745 | if (moxtet->irq.domain == NULL) { |
746 | dev_err(moxtet->dev, "Could not add IRQ domain\n" ); |
747 | return -ENOMEM; |
748 | } |
749 | |
750 | for (i = 0; i < MOXTET_NIRQS; ++i) |
751 | if (moxtet->irq.exists & BIT(i)) |
752 | irq_create_mapping(host: moxtet->irq.domain, hwirq: i); |
753 | |
754 | moxtet->irq.chip = moxtet_irq_chip; |
755 | moxtet->irq.masked = ~0; |
756 | |
757 | ret = request_threaded_irq(irq: moxtet->dev_irq, NULL, thread_fn: moxtet_irq_thread_fn, |
758 | IRQF_SHARED | IRQF_ONESHOT, name: "moxtet" , dev: moxtet); |
759 | if (ret < 0) |
760 | goto err_free; |
761 | |
762 | return 0; |
763 | |
764 | err_free: |
765 | moxtet_irq_free(moxtet); |
766 | return ret; |
767 | } |
768 | |
769 | static int moxtet_probe(struct spi_device *spi) |
770 | { |
771 | struct moxtet *moxtet; |
772 | int ret; |
773 | |
774 | ret = spi_setup(spi); |
775 | if (ret < 0) |
776 | return ret; |
777 | |
778 | moxtet = devm_kzalloc(dev: &spi->dev, size: sizeof(struct moxtet), |
779 | GFP_KERNEL); |
780 | if (!moxtet) |
781 | return -ENOMEM; |
782 | |
783 | moxtet->dev = &spi->dev; |
784 | spi_set_drvdata(spi, data: moxtet); |
785 | |
786 | mutex_init(&moxtet->lock); |
787 | |
788 | moxtet->dev_irq = of_irq_get(dev: moxtet->dev->of_node, index: 0); |
789 | if (moxtet->dev_irq == -EPROBE_DEFER) |
790 | return -EPROBE_DEFER; |
791 | |
792 | if (moxtet->dev_irq <= 0) { |
793 | dev_err(moxtet->dev, "No IRQ resource found\n" ); |
794 | return -ENXIO; |
795 | } |
796 | |
797 | ret = moxtet_find_topology(moxtet); |
798 | if (ret < 0) |
799 | return ret; |
800 | |
801 | if (moxtet->irq.exists) { |
802 | ret = moxtet_irq_setup(moxtet); |
803 | if (ret < 0) |
804 | return ret; |
805 | } |
806 | |
807 | of_register_moxtet_devices(moxtet); |
808 | moxtet_register_devices_from_topology(moxtet); |
809 | |
810 | ret = moxtet_register_debugfs(moxtet); |
811 | if (ret < 0) |
812 | dev_warn(moxtet->dev, "Failed creating debugfs entries: %i\n" , |
813 | ret); |
814 | |
815 | return 0; |
816 | } |
817 | |
818 | static void moxtet_remove(struct spi_device *spi) |
819 | { |
820 | struct moxtet *moxtet = spi_get_drvdata(spi); |
821 | |
822 | free_irq(moxtet->dev_irq, moxtet); |
823 | |
824 | moxtet_irq_free(moxtet); |
825 | |
826 | moxtet_unregister_debugfs(moxtet); |
827 | |
828 | device_for_each_child(dev: moxtet->dev, NULL, fn: __unregister); |
829 | |
830 | mutex_destroy(lock: &moxtet->lock); |
831 | } |
832 | |
833 | static const struct spi_device_id moxtet_spi_ids[] = { |
834 | { "moxtet" }, |
835 | { }, |
836 | }; |
837 | MODULE_DEVICE_TABLE(spi, moxtet_spi_ids); |
838 | |
839 | static const struct of_device_id moxtet_dt_ids[] = { |
840 | { .compatible = "cznic,moxtet" }, |
841 | {}, |
842 | }; |
843 | MODULE_DEVICE_TABLE(of, moxtet_dt_ids); |
844 | |
845 | static struct spi_driver moxtet_spi_driver = { |
846 | .driver = { |
847 | .name = "moxtet" , |
848 | .of_match_table = moxtet_dt_ids, |
849 | }, |
850 | .id_table = moxtet_spi_ids, |
851 | .probe = moxtet_probe, |
852 | .remove = moxtet_remove, |
853 | }; |
854 | |
855 | static int __init moxtet_init(void) |
856 | { |
857 | int ret; |
858 | |
859 | ret = bus_register(bus: &moxtet_bus_type); |
860 | if (ret < 0) { |
861 | pr_err("moxtet bus registration failed: %d\n" , ret); |
862 | goto error; |
863 | } |
864 | |
865 | ret = spi_register_driver(&moxtet_spi_driver); |
866 | if (ret < 0) { |
867 | pr_err("moxtet spi driver registration failed: %d\n" , ret); |
868 | goto error_bus; |
869 | } |
870 | |
871 | return 0; |
872 | |
873 | error_bus: |
874 | bus_unregister(bus: &moxtet_bus_type); |
875 | error: |
876 | return ret; |
877 | } |
878 | postcore_initcall_sync(moxtet_init); |
879 | |
880 | static void __exit moxtet_exit(void) |
881 | { |
882 | spi_unregister_driver(sdrv: &moxtet_spi_driver); |
883 | bus_unregister(bus: &moxtet_bus_type); |
884 | } |
885 | module_exit(moxtet_exit); |
886 | |
887 | MODULE_AUTHOR("Marek Behun <kabel@kernel.org>" ); |
888 | MODULE_DESCRIPTION("CZ.NIC's Turris Mox module configuration bus" ); |
889 | MODULE_LICENSE("GPL v2" ); |
890 | |