Blame view

drivers/power/reset/at91-reset.c 6.85 KB
ecfe64d8c   Maxime Ripard   power: reset: Add...
1
  /*
bc312cbdf   Nicolas Ferre   power: reset: at9...
2
   * Atmel AT91 SAM9 & SAMA5 SoCs reset code
ecfe64d8c   Maxime Ripard   power: reset: Add...
3
4
5
6
7
8
9
10
11
12
   *
   * Copyright (C) 2007 Atmel Corporation.
   * Copyright (C) BitBox Ltd 2010
   * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
   * Copyright (C) 2014 Free Electrons
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2.  This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
2b2c6148f   Alexandre Belloni   power/reset: at91...
13
  #include <linux/clk.h>
ecfe64d8c   Maxime Ripard   power: reset: Add...
14
15
16
17
18
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/of_address.h>
  #include <linux/platform_device.h>
  #include <linux/reboot.h>
f0a0a58e6   Alexandre Belloni   ARM: at91: move s...
19
20
  #include <soc/at91/at91sam9_ddrsdr.h>
  #include <soc/at91/at91sam9_sdramc.h>
ecfe64d8c   Maxime Ripard   power: reset: Add...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  
  #define AT91_RSTC_CR	0x00		/* Reset Controller Control Register */
  #define AT91_RSTC_PROCRST	BIT(0)		/* Processor Reset */
  #define AT91_RSTC_PERRST	BIT(2)		/* Peripheral Reset */
  #define AT91_RSTC_EXTRST	BIT(3)		/* External Reset */
  #define AT91_RSTC_KEY		(0xa5 << 24)	/* KEY Password */
  
  #define AT91_RSTC_SR	0x04		/* Reset Controller Status Register */
  #define AT91_RSTC_URSTS		BIT(0)		/* User Reset Status */
  #define AT91_RSTC_RSTTYP	GENMASK(10, 8)	/* Reset Type */
  #define AT91_RSTC_NRSTL		BIT(16)		/* NRST Pin Level */
  #define AT91_RSTC_SRCMP		BIT(17)		/* Software Reset Command in Progress */
  
  #define AT91_RSTC_MR	0x08		/* Reset Controller Mode Register */
  #define AT91_RSTC_URSTEN	BIT(0)		/* User Reset Enable */
  #define AT91_RSTC_URSTIEN	BIT(4)		/* User Reset Interrupt Enable */
  #define AT91_RSTC_ERSTL		GENMASK(11, 8)	/* External Reset Length */
  
  enum reset_type {
  	RESET_TYPE_GENERAL	= 0,
  	RESET_TYPE_WAKEUP	= 1,
  	RESET_TYPE_WATCHDOG	= 2,
  	RESET_TYPE_SOFTWARE	= 3,
  	RESET_TYPE_USER		= 4,
  };
  
  static void __iomem *at91_ramc_base[2], *at91_rstc_base;
2b2c6148f   Alexandre Belloni   power/reset: at91...
48
  static struct clk *sclk;
ecfe64d8c   Maxime Ripard   power: reset: Add...
49
50
51
52
53
54
  
  /*
  * unless the SDRAM is cleanly shutdown before we hit the
  * reset register it can be left driving the data bus and
  * killing the chance of a subsequent boot from NAND
  */
481ff6ff9   Guenter Roeck   power/reset: at91...
55
56
  static int at91sam9260_restart(struct notifier_block *this, unsigned long mode,
  			       void *cmd)
ecfe64d8c   Maxime Ripard   power: reset: Add...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  {
  	asm volatile(
  		/* Align to cache lines */
  		".balign 32
  \t"
  
  		/* Disable SDRAM accesses */
  		"str	%2, [%0, #" __stringify(AT91_SDRAMC_TR) "]
  \t"
  
  		/* Power down SDRAM */
  		"str	%3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]
  \t"
  
  		/* Reset CPU */
  		"str	%4, [%1, #" __stringify(AT91_RSTC_CR) "]
  \t"
  
  		"b	.
  \t"
  		:
  		: "r" (at91_ramc_base[0]),
  		  "r" (at91_rstc_base),
  		  "r" (1),
7be5ac2c3   Ben Dooks   power/reset: at91...
81
82
  		  "r" cpu_to_le32(AT91_SDRAMC_LPCB_POWER_DOWN),
  		  "r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
481ff6ff9   Guenter Roeck   power/reset: at91...
83
84
  
  	return NOTIFY_DONE;
ecfe64d8c   Maxime Ripard   power: reset: Add...
85
  }
481ff6ff9   Guenter Roeck   power/reset: at91...
86
87
  static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
  			       void *cmd)
ecfe64d8c   Maxime Ripard   power: reset: Add...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  {
  	asm volatile(
  		/*
  		 * Test wether we have a second RAM controller to care
  		 * about.
  		 *
  		 * First, test that we can dereference the virtual address.
  		 */
  		"cmp	%1, #0
  \t"
  		"beq	1f
  \t"
  
  		/* Then, test that the RAM controller is enabled */
  		"ldr	r0, [%1]
  \t"
  		"cmp	r0, #0
  \t"
  
  		/* Align to cache lines */
  		".balign 32
  \t"
  
  		/* Disable SDRAM0 accesses */
  		"1:	str	%3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]
  \t"
  		/* Power down SDRAM0 */
7cb4e717d   Alexandre Belloni   power: reset: at9...
115
116
  		"	str	%4, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]
  \t"
ecfe64d8c   Maxime Ripard   power: reset: Add...
117
118
119
120
  		/* Disable SDRAM1 accesses */
  		"	strne	%3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]
  \t"
  		/* Power down SDRAM1 */
7cb4e717d   Alexandre Belloni   power: reset: at9...
121
122
  		"	strne	%4, [%1, #" __stringify(AT91_DDRSDRC_LPR) "]
  \t"
ecfe64d8c   Maxime Ripard   power: reset: Add...
123
124
125
126
127
128
129
130
131
132
133
  		/* Reset CPU */
  		"	str	%5, [%2, #" __stringify(AT91_RSTC_CR) "]
  \t"
  
  		"	b	.
  \t"
  		:
  		: "r" (at91_ramc_base[0]),
  		  "r" (at91_ramc_base[1]),
  		  "r" (at91_rstc_base),
  		  "r" (1),
7be5ac2c3   Ben Dooks   power/reset: at91...
134
135
  		  "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN),
  		  "r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
ecfe64d8c   Maxime Ripard   power: reset: Add...
136
  		: "r0");
481ff6ff9   Guenter Roeck   power/reset: at91...
137
138
  
  	return NOTIFY_DONE;
ecfe64d8c   Maxime Ripard   power: reset: Add...
139
  }
1ae25d626   Josh Wu   power: reset: at9...
140
141
142
143
144
145
146
147
  static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
  			   void *cmd)
  {
  	writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST),
  	       at91_rstc_base);
  
  	return NOTIFY_DONE;
  }
ecfe64d8c   Maxime Ripard   power: reset: Add...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  static void __init at91_reset_status(struct platform_device *pdev)
  {
  	u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
  	char *reason;
  
  	switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
  	case RESET_TYPE_GENERAL:
  		reason = "general reset";
  		break;
  	case RESET_TYPE_WAKEUP:
  		reason = "wakeup";
  		break;
  	case RESET_TYPE_WATCHDOG:
  		reason = "watchdog reset";
  		break;
  	case RESET_TYPE_SOFTWARE:
  		reason = "software reset";
  		break;
  	case RESET_TYPE_USER:
  		reason = "user reset";
  		break;
  	default:
  		reason = "unknown reset";
  		break;
  	}
  
  	pr_info("AT91: Starting after %s
  ", reason);
  }
8fb088550   Fabian Frederick   power: constify o...
177
  static const struct of_device_id at91_ramc_of_match[] = {
ecfe64d8c   Maxime Ripard   power: reset: Add...
178
179
  	{ .compatible = "atmel,at91sam9260-sdramc", },
  	{ .compatible = "atmel,at91sam9g45-ddramc", },
ecfe64d8c   Maxime Ripard   power: reset: Add...
180
181
  	{ /* sentinel */ }
  };
8fb088550   Fabian Frederick   power: constify o...
182
  static const struct of_device_id at91_reset_of_match[] = {
ecfe64d8c   Maxime Ripard   power: reset: Add...
183
184
  	{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
  	{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
1ae25d626   Josh Wu   power: reset: at9...
185
  	{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart },
ecfe64d8c   Maxime Ripard   power: reset: Add...
186
187
  	{ /* sentinel */ }
  };
481ff6ff9   Guenter Roeck   power/reset: at91...
188
189
190
  static struct notifier_block at91_restart_nb = {
  	.priority = 192,
  };
6e64180a7   Alexandre Belloni   power/reset: at91...
191
  static int __init at91_reset_probe(struct platform_device *pdev)
ecfe64d8c   Maxime Ripard   power: reset: Add...
192
193
194
  {
  	const struct of_device_id *match;
  	struct device_node *np;
eacd8d09d   Alexandre Belloni   power/reset: at91...
195
  	int ret, idx = 0;
ecfe64d8c   Maxime Ripard   power: reset: Add...
196
197
198
199
200
201
202
  
  	at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
  	if (!at91_rstc_base) {
  		dev_err(&pdev->dev, "Could not map reset controller address
  ");
  		return -ENODEV;
  	}
1ae25d626   Josh Wu   power: reset: at9...
203
204
205
206
207
208
209
  	if (!of_device_is_compatible(pdev->dev.of_node, "atmel,sama5d3-rstc")) {
  		/* we need to shutdown the ddr controller, so get ramc base */
  		for_each_matching_node(np, at91_ramc_of_match) {
  			at91_ramc_base[idx] = of_iomap(np, 0);
  			if (!at91_ramc_base[idx]) {
  				dev_err(&pdev->dev, "Could not map ram controller address
  ");
c4c0edfbf   Julia Lawall   power/reset: at91...
210
  				of_node_put(np);
1ae25d626   Josh Wu   power: reset: at9...
211
212
213
  				return -ENODEV;
  			}
  			idx++;
ecfe64d8c   Maxime Ripard   power: reset: Add...
214
  		}
ecfe64d8c   Maxime Ripard   power: reset: Add...
215
216
217
  	}
  
  	match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
481ff6ff9   Guenter Roeck   power/reset: at91...
218
  	at91_restart_nb.notifier_call = match->data;
ecfe64d8c   Maxime Ripard   power: reset: Add...
219

2b2c6148f   Alexandre Belloni   power/reset: at91...
220
221
222
223
224
225
226
227
228
229
  	sclk = devm_clk_get(&pdev->dev, NULL);
  	if (IS_ERR(sclk))
  		return PTR_ERR(sclk);
  
  	ret = clk_prepare_enable(sclk);
  	if (ret) {
  		dev_err(&pdev->dev, "Could not enable slow clock
  ");
  		return ret;
  	}
eacd8d09d   Alexandre Belloni   power/reset: at91...
230
  	ret = register_restart_handler(&at91_restart_nb);
2b2c6148f   Alexandre Belloni   power/reset: at91...
231
232
  	if (ret) {
  		clk_disable_unprepare(sclk);
ecfe64d8c   Maxime Ripard   power: reset: Add...
233
  		return ret;
2b2c6148f   Alexandre Belloni   power/reset: at91...
234
  	}
ecfe64d8c   Maxime Ripard   power: reset: Add...
235
236
237
238
239
  
  	at91_reset_status(pdev);
  
  	return 0;
  }
6e64180a7   Alexandre Belloni   power/reset: at91...
240
241
242
  static int __exit at91_reset_remove(struct platform_device *pdev)
  {
  	unregister_restart_handler(&at91_restart_nb);
2b2c6148f   Alexandre Belloni   power/reset: at91...
243
  	clk_disable_unprepare(sclk);
6e64180a7   Alexandre Belloni   power/reset: at91...
244
245
246
  
  	return 0;
  }
323667209   Krzysztof Kozlowski   power: at91-reset...
247
  static const struct platform_device_id at91_reset_plat_match[] = {
ecfe64d8c   Maxime Ripard   power: reset: Add...
248
249
250
251
252
253
  	{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
  	{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
  	{ /* sentinel */ }
  };
  
  static struct platform_driver at91_reset_driver = {
6e64180a7   Alexandre Belloni   power/reset: at91...
254
  	.remove = __exit_p(at91_reset_remove),
ecfe64d8c   Maxime Ripard   power: reset: Add...
255
256
257
258
259
260
  	.driver = {
  		.name = "at91-reset",
  		.of_match_table = at91_reset_of_match,
  	},
  	.id_table = at91_reset_plat_match,
  };
6e64180a7   Alexandre Belloni   power/reset: at91...
261
262
263
264
265
  module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
  
  MODULE_AUTHOR("Atmel Corporation");
  MODULE_DESCRIPTION("Reset driver for Atmel SoCs");
  MODULE_LICENSE("GPL v2");