Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
79797b6f | 2 | /* |
79797b6f MB |
3 | * |
4 | * Copyright (C) 2010 John Crispin <blogic@phrozen.org> | |
5 | * Copyright (C) 2013-2015 Lantiq Beteiligungs-GmbH & Co.KG | |
6 | * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> | |
7 | * Copyright (C) 2017 Hauke Mehrtens <hauke@hauke-m.de> | |
8 | */ | |
9 | ||
10 | #include <linux/mfd/syscon.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/regmap.h> | |
13 | #include <linux/reset-controller.h> | |
14 | #include <linux/of_address.h> | |
15 | #include <linux/of_platform.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/property.h> | |
18 | ||
19 | #define LANTIQ_RCU_RESET_TIMEOUT 10000 | |
20 | ||
21 | struct lantiq_rcu_reset_priv { | |
22 | struct reset_controller_dev rcdev; | |
23 | struct device *dev; | |
24 | struct regmap *regmap; | |
25 | u32 reset_offset; | |
26 | u32 status_offset; | |
27 | }; | |
28 | ||
29 | static struct lantiq_rcu_reset_priv *to_lantiq_rcu_reset_priv( | |
30 | struct reset_controller_dev *rcdev) | |
31 | { | |
32 | return container_of(rcdev, struct lantiq_rcu_reset_priv, rcdev); | |
33 | } | |
34 | ||
35 | static int lantiq_rcu_reset_status(struct reset_controller_dev *rcdev, | |
36 | unsigned long id) | |
37 | { | |
38 | struct lantiq_rcu_reset_priv *priv = to_lantiq_rcu_reset_priv(rcdev); | |
39 | unsigned int status = (id >> 8) & 0x1f; | |
40 | u32 val; | |
41 | int ret; | |
42 | ||
43 | ret = regmap_read(priv->regmap, priv->status_offset, &val); | |
44 | if (ret) | |
45 | return ret; | |
46 | ||
47 | return !!(val & BIT(status)); | |
48 | } | |
49 | ||
50 | static int lantiq_rcu_reset_status_timeout(struct reset_controller_dev *rcdev, | |
51 | unsigned long id, bool assert) | |
52 | { | |
53 | int ret; | |
54 | int retry = LANTIQ_RCU_RESET_TIMEOUT; | |
55 | ||
56 | do { | |
57 | ret = lantiq_rcu_reset_status(rcdev, id); | |
58 | if (ret < 0) | |
59 | return ret; | |
60 | if (ret == assert) | |
61 | return 0; | |
62 | usleep_range(20, 40); | |
63 | } while (--retry); | |
64 | ||
65 | return -ETIMEDOUT; | |
66 | } | |
67 | ||
68 | static int lantiq_rcu_reset_update(struct reset_controller_dev *rcdev, | |
69 | unsigned long id, bool assert) | |
70 | { | |
71 | struct lantiq_rcu_reset_priv *priv = to_lantiq_rcu_reset_priv(rcdev); | |
72 | unsigned int set = id & 0x1f; | |
73 | u32 val = assert ? BIT(set) : 0; | |
74 | int ret; | |
75 | ||
76 | ret = regmap_update_bits(priv->regmap, priv->reset_offset, BIT(set), | |
77 | val); | |
78 | if (ret) { | |
79 | dev_err(priv->dev, "Failed to set reset bit %u\n", set); | |
80 | return ret; | |
81 | } | |
82 | ||
83 | ||
84 | ret = lantiq_rcu_reset_status_timeout(rcdev, id, assert); | |
85 | if (ret) | |
86 | dev_err(priv->dev, "Failed to %s bit %u\n", | |
87 | assert ? "assert" : "deassert", set); | |
88 | ||
89 | return ret; | |
90 | } | |
91 | ||
92 | static int lantiq_rcu_reset_assert(struct reset_controller_dev *rcdev, | |
93 | unsigned long id) | |
94 | { | |
95 | return lantiq_rcu_reset_update(rcdev, id, true); | |
96 | } | |
97 | ||
98 | static int lantiq_rcu_reset_deassert(struct reset_controller_dev *rcdev, | |
99 | unsigned long id) | |
100 | { | |
101 | return lantiq_rcu_reset_update(rcdev, id, false); | |
102 | } | |
103 | ||
104 | static int lantiq_rcu_reset_reset(struct reset_controller_dev *rcdev, | |
105 | unsigned long id) | |
106 | { | |
107 | int ret; | |
108 | ||
109 | ret = lantiq_rcu_reset_assert(rcdev, id); | |
110 | if (ret) | |
111 | return ret; | |
112 | ||
113 | return lantiq_rcu_reset_deassert(rcdev, id); | |
114 | } | |
115 | ||
116 | static const struct reset_control_ops lantiq_rcu_reset_ops = { | |
117 | .assert = lantiq_rcu_reset_assert, | |
118 | .deassert = lantiq_rcu_reset_deassert, | |
119 | .status = lantiq_rcu_reset_status, | |
120 | .reset = lantiq_rcu_reset_reset, | |
121 | }; | |
122 | ||
123 | static int lantiq_rcu_reset_of_parse(struct platform_device *pdev, | |
124 | struct lantiq_rcu_reset_priv *priv) | |
125 | { | |
126 | struct device *dev = &pdev->dev; | |
127 | const __be32 *offset; | |
128 | ||
129 | priv->regmap = syscon_node_to_regmap(dev->of_node->parent); | |
130 | if (IS_ERR(priv->regmap)) { | |
131 | dev_err(&pdev->dev, "Failed to lookup RCU regmap\n"); | |
132 | return PTR_ERR(priv->regmap); | |
133 | } | |
134 | ||
135 | offset = of_get_address(dev->of_node, 0, NULL, NULL); | |
136 | if (!offset) { | |
137 | dev_err(&pdev->dev, "Failed to get RCU reset offset\n"); | |
138 | return -ENOENT; | |
139 | } | |
140 | priv->reset_offset = __be32_to_cpu(*offset); | |
141 | ||
142 | offset = of_get_address(dev->of_node, 1, NULL, NULL); | |
143 | if (!offset) { | |
144 | dev_err(&pdev->dev, "Failed to get RCU status offset\n"); | |
145 | return -ENOENT; | |
146 | } | |
147 | priv->status_offset = __be32_to_cpu(*offset); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int lantiq_rcu_reset_xlate(struct reset_controller_dev *rcdev, | |
153 | const struct of_phandle_args *reset_spec) | |
154 | { | |
155 | unsigned int status, set; | |
156 | ||
157 | set = reset_spec->args[0]; | |
158 | status = reset_spec->args[1]; | |
159 | ||
160 | if (set >= rcdev->nr_resets || status >= rcdev->nr_resets) | |
161 | return -EINVAL; | |
162 | ||
163 | return (status << 8) | set; | |
164 | } | |
165 | ||
166 | static int lantiq_rcu_reset_probe(struct platform_device *pdev) | |
167 | { | |
168 | struct lantiq_rcu_reset_priv *priv; | |
169 | int err; | |
170 | ||
171 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
172 | if (!priv) | |
173 | return -ENOMEM; | |
174 | ||
175 | priv->dev = &pdev->dev; | |
176 | platform_set_drvdata(pdev, priv); | |
177 | ||
178 | err = lantiq_rcu_reset_of_parse(pdev, priv); | |
179 | if (err) | |
180 | return err; | |
181 | ||
182 | priv->rcdev.ops = &lantiq_rcu_reset_ops; | |
183 | priv->rcdev.owner = THIS_MODULE; | |
184 | priv->rcdev.of_node = pdev->dev.of_node; | |
185 | priv->rcdev.nr_resets = 32; | |
186 | priv->rcdev.of_xlate = lantiq_rcu_reset_xlate; | |
187 | priv->rcdev.of_reset_n_cells = 2; | |
188 | ||
189 | return reset_controller_register(&priv->rcdev); | |
190 | } | |
191 | ||
192 | static const struct of_device_id lantiq_rcu_reset_dt_ids[] = { | |
193 | { .compatible = "lantiq,danube-reset", }, | |
194 | { .compatible = "lantiq,xrx200-reset", }, | |
195 | { }, | |
196 | }; | |
197 | MODULE_DEVICE_TABLE(of, lantiq_rcu_reset_dt_ids); | |
198 | ||
199 | static struct platform_driver lantiq_rcu_reset_driver = { | |
200 | .probe = lantiq_rcu_reset_probe, | |
201 | .driver = { | |
202 | .name = "lantiq-reset", | |
203 | .of_match_table = lantiq_rcu_reset_dt_ids, | |
204 | }, | |
205 | }; | |
206 | module_platform_driver(lantiq_rcu_reset_driver); | |
207 | ||
208 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); | |
209 | MODULE_DESCRIPTION("Lantiq XWAY RCU Reset Controller Driver"); | |
210 | MODULE_LICENSE("GPL"); |