1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * AR71xx Reset Controller Driver |
4 | * Author: Alban Bedel |
5 | * |
6 | * Copyright (C) 2015 Alban Bedel <albeu@free.fr> |
7 | */ |
8 | |
9 | #include <linux/io.h> |
10 | #include <linux/init.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/reset-controller.h> |
14 | #include <linux/reboot.h> |
15 | |
16 | struct ath79_reset { |
17 | struct reset_controller_dev rcdev; |
18 | struct notifier_block restart_nb; |
19 | void __iomem *base; |
20 | spinlock_t lock; |
21 | }; |
22 | |
23 | #define FULL_CHIP_RESET 24 |
24 | |
25 | static int ath79_reset_update(struct reset_controller_dev *rcdev, |
26 | unsigned long id, bool assert) |
27 | { |
28 | struct ath79_reset *ath79_reset = |
29 | container_of(rcdev, struct ath79_reset, rcdev); |
30 | unsigned long flags; |
31 | u32 val; |
32 | |
33 | spin_lock_irqsave(&ath79_reset->lock, flags); |
34 | val = readl(addr: ath79_reset->base); |
35 | if (assert) |
36 | val |= BIT(id); |
37 | else |
38 | val &= ~BIT(id); |
39 | writel(val, addr: ath79_reset->base); |
40 | spin_unlock_irqrestore(lock: &ath79_reset->lock, flags); |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | static int ath79_reset_assert(struct reset_controller_dev *rcdev, |
46 | unsigned long id) |
47 | { |
48 | return ath79_reset_update(rcdev, id, assert: true); |
49 | } |
50 | |
51 | static int ath79_reset_deassert(struct reset_controller_dev *rcdev, |
52 | unsigned long id) |
53 | { |
54 | return ath79_reset_update(rcdev, id, assert: false); |
55 | } |
56 | |
57 | static int ath79_reset_status(struct reset_controller_dev *rcdev, |
58 | unsigned long id) |
59 | { |
60 | struct ath79_reset *ath79_reset = |
61 | container_of(rcdev, struct ath79_reset, rcdev); |
62 | u32 val; |
63 | |
64 | val = readl(addr: ath79_reset->base); |
65 | |
66 | return !!(val & BIT(id)); |
67 | } |
68 | |
69 | static const struct reset_control_ops ath79_reset_ops = { |
70 | .assert = ath79_reset_assert, |
71 | .deassert = ath79_reset_deassert, |
72 | .status = ath79_reset_status, |
73 | }; |
74 | |
75 | static int ath79_reset_restart_handler(struct notifier_block *nb, |
76 | unsigned long action, void *data) |
77 | { |
78 | struct ath79_reset *ath79_reset = |
79 | container_of(nb, struct ath79_reset, restart_nb); |
80 | |
81 | ath79_reset_assert(rcdev: &ath79_reset->rcdev, FULL_CHIP_RESET); |
82 | |
83 | return NOTIFY_DONE; |
84 | } |
85 | |
86 | static int ath79_reset_probe(struct platform_device *pdev) |
87 | { |
88 | struct ath79_reset *ath79_reset; |
89 | int err; |
90 | |
91 | ath79_reset = devm_kzalloc(dev: &pdev->dev, |
92 | size: sizeof(*ath79_reset), GFP_KERNEL); |
93 | if (!ath79_reset) |
94 | return -ENOMEM; |
95 | |
96 | ath79_reset->base = devm_platform_ioremap_resource(pdev, index: 0); |
97 | if (IS_ERR(ptr: ath79_reset->base)) |
98 | return PTR_ERR(ptr: ath79_reset->base); |
99 | |
100 | spin_lock_init(&ath79_reset->lock); |
101 | ath79_reset->rcdev.ops = &ath79_reset_ops; |
102 | ath79_reset->rcdev.owner = THIS_MODULE; |
103 | ath79_reset->rcdev.of_node = pdev->dev.of_node; |
104 | ath79_reset->rcdev.of_reset_n_cells = 1; |
105 | ath79_reset->rcdev.nr_resets = 32; |
106 | |
107 | err = devm_reset_controller_register(dev: &pdev->dev, rcdev: &ath79_reset->rcdev); |
108 | if (err) |
109 | return err; |
110 | |
111 | ath79_reset->restart_nb.notifier_call = ath79_reset_restart_handler; |
112 | ath79_reset->restart_nb.priority = 128; |
113 | |
114 | err = register_restart_handler(&ath79_reset->restart_nb); |
115 | if (err) |
116 | dev_warn(&pdev->dev, "Failed to register restart handler\n" ); |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static const struct of_device_id ath79_reset_dt_ids[] = { |
122 | { .compatible = "qca,ar7100-reset" , }, |
123 | { }, |
124 | }; |
125 | |
126 | static struct platform_driver ath79_reset_driver = { |
127 | .probe = ath79_reset_probe, |
128 | .driver = { |
129 | .name = "ath79-reset" , |
130 | .of_match_table = ath79_reset_dt_ids, |
131 | .suppress_bind_attrs = true, |
132 | }, |
133 | }; |
134 | builtin_platform_driver(ath79_reset_driver); |
135 | |