aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoachim Eastwood <manabian@gmail.com>2015-05-05 18:10:26 -0400
committerPhilipp Zabel <p.zabel@pengutronix.de>2015-08-03 07:13:51 -0400
commitc392b65ba853f653cff3d1c7de2138bd6906d536 (patch)
tree2687763e9aaa39eb778af21ed28279d65c8233f6
parenta518db45c2580fd726f6578539482596f46a8219 (diff)
reset: add driver for lpc18xx rgu
Add reset driver for the Reset Generation Unit (RGU) found on NXP LPC18xx and LPC43xx devies. This reset controller features up to 64 reset lines connected to different blocks and peripheral in the SoC. Most reset lines on the controller are self clearing except for those dealing with the Cortex-M0 cores on LPC43xx devices. This driver also registers a restart handler that can be used to reset the entire device. Signed-off-by: Joachim Eastwood <manabian@gmail.com> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-lpc18xx.c258
2 files changed, 259 insertions, 0 deletions
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 157d421f755b..1d41feeb2dce 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,4 +1,5 @@
1obj-$(CONFIG_RESET_CONTROLLER) += core.o 1obj-$(CONFIG_RESET_CONTROLLER) += core.o
2obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
2obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o 3obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
3obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o 4obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
4obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o 5obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c
new file mode 100644
index 000000000000..70922e9ac27f
--- /dev/null
+++ b/drivers/reset/reset-lpc18xx.c
@@ -0,0 +1,258 @@
1/*
2 * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU).
3 *
4 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 */
11
12#include <linux/clk.h>
13#include <linux/delay.h>
14#include <linux/err.h>
15#include <linux/io.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/reboot.h>
20#include <linux/reset-controller.h>
21#include <linux/spinlock.h>
22
23/* LPC18xx RGU registers */
24#define LPC18XX_RGU_CTRL0 0x100
25#define LPC18XX_RGU_CTRL1 0x104
26#define LPC18XX_RGU_ACTIVE_STATUS0 0x150
27#define LPC18XX_RGU_ACTIVE_STATUS1 0x154
28
29#define LPC18XX_RGU_RESETS_PER_REG 32
30
31/* Internal reset outputs */
32#define LPC18XX_RGU_CORE_RST 0
33#define LPC43XX_RGU_M0SUB_RST 12
34#define LPC43XX_RGU_M0APP_RST 56
35
36struct lpc18xx_rgu_data {
37 struct reset_controller_dev rcdev;
38 struct clk *clk_delay;
39 struct clk *clk_reg;
40 void __iomem *base;
41 spinlock_t lock;
42 u32 delay_us;
43};
44
45#define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev)
46
47static void __iomem *rgu_base;
48
49static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode,
50 void *cmd)
51{
52 writel(BIT(LPC18XX_RGU_CORE_RST), rgu_base + LPC18XX_RGU_CTRL0);
53 mdelay(2000);
54
55 pr_emerg("%s: unable to restart system\n", __func__);
56
57 return NOTIFY_DONE;
58}
59
60static struct notifier_block lpc18xx_rgu_restart_nb = {
61 .notifier_call = lpc18xx_rgu_restart,
62 .priority = 192,
63};
64
65/*
66 * The LPC18xx RGU has mostly self-deasserting resets except for the
67 * two reset lines going to the internal Cortex-M0 cores.
68 *
69 * To prevent the M0 core resets from accidentally getting deasserted
70 * status register must be check and bits in control register set to
71 * preserve the state.
72 */
73static int lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev,
74 unsigned long id, bool set)
75{
76 struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
77 u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0;
78 u32 ctrl_offset = LPC18XX_RGU_CTRL0;
79 unsigned long flags;
80 u32 stat, rst_bit;
81
82 stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
83 ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
84 rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
85
86 spin_lock_irqsave(&rc->lock, flags);
87 stat = ~readl(rc->base + stat_offset);
88 if (set)
89 writel(stat | rst_bit, rc->base + ctrl_offset);
90 else
91 writel(stat & ~rst_bit, rc->base + ctrl_offset);
92 spin_unlock_irqrestore(&rc->lock, flags);
93
94 return 0;
95}
96
97static int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev,
98 unsigned long id)
99{
100 return lpc18xx_rgu_setclear_reset(rcdev, id, true);
101}
102
103static int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev,
104 unsigned long id)
105{
106 return lpc18xx_rgu_setclear_reset(rcdev, id, false);
107}
108
109/* Only M0 cores require explicit reset deassert */
110static int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev,
111 unsigned long id)
112{
113 struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
114
115 lpc18xx_rgu_assert(rcdev, id);
116 udelay(rc->delay_us);
117
118 switch (id) {
119 case LPC43XX_RGU_M0SUB_RST:
120 case LPC43XX_RGU_M0APP_RST:
121 lpc18xx_rgu_setclear_reset(rcdev, id, false);
122 }
123
124 return 0;
125}
126
127static int lpc18xx_rgu_status(struct reset_controller_dev *rcdev,
128 unsigned long id)
129{
130 struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
131 u32 bit, offset = LPC18XX_RGU_ACTIVE_STATUS0;
132
133 offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
134 bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
135
136 return !(readl(rc->base + offset) & bit);
137}
138
139static struct reset_control_ops lpc18xx_rgu_ops = {
140 .reset = lpc18xx_rgu_reset,
141 .assert = lpc18xx_rgu_assert,
142 .deassert = lpc18xx_rgu_deassert,
143 .status = lpc18xx_rgu_status,
144};
145
146static int lpc18xx_rgu_probe(struct platform_device *pdev)
147{
148 struct lpc18xx_rgu_data *rc;
149 struct resource *res;
150 u32 fcclk, firc;
151 int ret;
152
153 rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL);
154 if (!rc)
155 return -ENOMEM;
156
157 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
158 rc->base = devm_ioremap_resource(&pdev->dev, res);
159 if (IS_ERR(rc->base))
160 return PTR_ERR(rc->base);
161
162 rc->clk_reg = devm_clk_get(&pdev->dev, "reg");
163 if (IS_ERR(rc->clk_reg)) {
164 dev_err(&pdev->dev, "reg clock not found\n");
165 return PTR_ERR(rc->clk_reg);
166 }
167
168 rc->clk_delay = devm_clk_get(&pdev->dev, "delay");
169 if (IS_ERR(rc->clk_delay)) {
170 dev_err(&pdev->dev, "delay clock not found\n");
171 return PTR_ERR(rc->clk_delay);
172 }
173
174 ret = clk_prepare_enable(rc->clk_reg);
175 if (ret) {
176 dev_err(&pdev->dev, "unable to enable reg clock\n");
177 return ret;
178 }
179
180 ret = clk_prepare_enable(rc->clk_delay);
181 if (ret) {
182 dev_err(&pdev->dev, "unable to enable delay clock\n");
183 goto dis_clk_reg;
184 }
185
186 fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC;
187 firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC;
188 if (fcclk == 0 || firc == 0)
189 rc->delay_us = 2;
190 else
191 rc->delay_us = DIV_ROUND_UP(fcclk, firc * firc);
192
193 spin_lock_init(&rc->lock);
194
195 rc->rcdev.owner = THIS_MODULE;
196 rc->rcdev.nr_resets = 64;
197 rc->rcdev.ops = &lpc18xx_rgu_ops;
198 rc->rcdev.of_node = pdev->dev.of_node;
199
200 platform_set_drvdata(pdev, rc);
201
202 ret = reset_controller_register(&rc->rcdev);
203 if (ret) {
204 dev_err(&pdev->dev, "unable to register device\n");
205 goto dis_clks;
206 }
207
208 rgu_base = rc->base;
209 ret = register_restart_handler(&lpc18xx_rgu_restart_nb);
210 if (ret)
211 dev_warn(&pdev->dev, "failed to register restart handler\n");
212
213 return 0;
214
215dis_clks:
216 clk_disable_unprepare(rc->clk_delay);
217dis_clk_reg:
218 clk_disable_unprepare(rc->clk_reg);
219
220 return ret;
221}
222
223static int lpc18xx_rgu_remove(struct platform_device *pdev)
224{
225 struct lpc18xx_rgu_data *rc = platform_get_drvdata(pdev);
226 int ret;
227
228 ret = unregister_restart_handler(&lpc18xx_rgu_restart_nb);
229 if (ret)
230 dev_warn(&pdev->dev, "failed to unregister restart handler\n");
231
232 reset_controller_unregister(&rc->rcdev);
233
234 clk_disable_unprepare(rc->clk_delay);
235 clk_disable_unprepare(rc->clk_reg);
236
237 return 0;
238}
239
240static const struct of_device_id lpc18xx_rgu_match[] = {
241 { .compatible = "nxp,lpc1850-rgu" },
242 { }
243};
244MODULE_DEVICE_TABLE(of, lpc18xx_rgu_match);
245
246static struct platform_driver lpc18xx_rgu_driver = {
247 .probe = lpc18xx_rgu_probe,
248 .remove = lpc18xx_rgu_remove,
249 .driver = {
250 .name = "lpc18xx-reset",
251 .of_match_table = lpc18xx_rgu_match,
252 },
253};
254module_platform_driver(lpc18xx_rgu_driver);
255
256MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
257MODULE_DESCRIPTION("Reset driver for LPC18xx/43xx RGU");
258MODULE_LICENSE("GPL v2");