blob: cb8312619fa076799e38fc5b7596df80687e8e1e [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Dinh Nguyen2ac71882018-04-04 17:18:20 -05002/*
3 * Socfpga Reset Controller Driver
4 *
5 * Copyright 2014 Steffen Trumtrar <s.trumtrar@pengutronix.de>
6 *
7 * based on
8 * Allwinner SoCs Reset Controller driver
9 *
10 * Copyright 2013 Maxime Ripard
11 *
12 * Maxime Ripard <maxime.ripard@free-electrons.com>
Dinh Nguyen2ac71882018-04-04 17:18:20 -050013 */
14
15#include <common.h>
16#include <dm.h>
17#include <dm/of_access.h>
18#include <reset-uclass.h>
19#include <linux/bitops.h>
20#include <linux/io.h>
21#include <linux/sizes.h>
22
23#define BANK_INCREMENT 4
24#define NR_BANKS 8
25
26struct socfpga_reset_data {
Simon Goldschmidt1ea97502019-03-01 20:12:30 +010027 void __iomem *modrst_base;
Dinh Nguyen2ac71882018-04-04 17:18:20 -050028};
29
Simon Goldschmidtede6e7b2019-03-01 20:12:32 +010030/*
31 * For compatibility with Kernels that don't support peripheral reset, this
32 * driver can keep the old behaviour of not asserting peripheral reset before
33 * starting the OS and deasserting all peripheral resets (enabling all
34 * peripherals).
35 *
36 * For that, the reset driver checks the environment variable
37 * "socfpga_legacy_reset_compat". If this variable is '1', perihperals are not
38 * reset again once taken out of reset and all peripherals in 'permodrst' are
39 * taken out of reset before booting into the OS.
40 * Note that this should be required for gen5 systems only that are running
41 * Linux kernels without proper peripheral reset support for all drivers used.
42 */
43static bool socfpga_reset_keep_enabled(void)
44{
45#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(ENV_SUPPORT)
46 const char *env_str;
47 long val;
48
49 env_str = env_get("socfpga_legacy_reset_compat");
50 if (env_str) {
51 val = simple_strtol(env_str, NULL, 0);
52 if (val == 1)
53 return true;
54 }
55#endif
56
57 return false;
58}
59
Dinh Nguyen2ac71882018-04-04 17:18:20 -050060static int socfpga_reset_assert(struct reset_ctl *reset_ctl)
61{
62 struct socfpga_reset_data *data = dev_get_priv(reset_ctl->dev);
63 int id = reset_ctl->id;
64 int reg_width = sizeof(u32);
65 int bank = id / (reg_width * BITS_PER_BYTE);
66 int offset = id % (reg_width * BITS_PER_BYTE);
67
Simon Goldschmidt1ea97502019-03-01 20:12:30 +010068 setbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
Dinh Nguyen2ac71882018-04-04 17:18:20 -050069 return 0;
70}
71
72static int socfpga_reset_deassert(struct reset_ctl *reset_ctl)
73{
74 struct socfpga_reset_data *data = dev_get_priv(reset_ctl->dev);
75 int id = reset_ctl->id;
76 int reg_width = sizeof(u32);
77 int bank = id / (reg_width * BITS_PER_BYTE);
78 int offset = id % (reg_width * BITS_PER_BYTE);
79
Simon Goldschmidt1ea97502019-03-01 20:12:30 +010080 clrbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
Dinh Nguyen2ac71882018-04-04 17:18:20 -050081 return 0;
82}
83
84static int socfpga_reset_request(struct reset_ctl *reset_ctl)
85{
86 debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__,
87 reset_ctl, reset_ctl->dev, reset_ctl->id);
88
89 return 0;
90}
91
92static int socfpga_reset_free(struct reset_ctl *reset_ctl)
93{
94 debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl,
95 reset_ctl->dev, reset_ctl->id);
96
97 return 0;
98}
99
100static const struct reset_ops socfpga_reset_ops = {
101 .request = socfpga_reset_request,
102 .free = socfpga_reset_free,
103 .rst_assert = socfpga_reset_assert,
104 .rst_deassert = socfpga_reset_deassert,
105};
106
107static int socfpga_reset_probe(struct udevice *dev)
108{
109 struct socfpga_reset_data *data = dev_get_priv(dev);
110 const void *blob = gd->fdt_blob;
111 int node = dev_of_offset(dev);
112 u32 modrst_offset;
Simon Goldschmidt1ea97502019-03-01 20:12:30 +0100113 void __iomem *membase;
Dinh Nguyen2ac71882018-04-04 17:18:20 -0500114
Simon Goldschmidt1ea97502019-03-01 20:12:30 +0100115 membase = devfdt_get_addr_ptr(dev);
Dinh Nguyen2ac71882018-04-04 17:18:20 -0500116
117 modrst_offset = fdtdec_get_int(blob, node, "altr,modrst-offset", 0x10);
Simon Goldschmidt1ea97502019-03-01 20:12:30 +0100118 data->modrst_base = membase + modrst_offset;
Dinh Nguyen2ac71882018-04-04 17:18:20 -0500119
120 return 0;
121}
122
Simon Goldschmidtede6e7b2019-03-01 20:12:32 +0100123static int socfpga_reset_remove(struct udevice *dev)
124{
125 struct socfpga_reset_data *data = dev_get_priv(dev);
126
127 if (socfpga_reset_keep_enabled()) {
128 puts("Deasserting all peripheral resets\n");
129 writel(0, data->modrst_base + 4);
130 }
131
132 return 0;
133}
134
Dinh Nguyen2ac71882018-04-04 17:18:20 -0500135static const struct udevice_id socfpga_reset_match[] = {
136 { .compatible = "altr,rst-mgr" },
137 { /* sentinel */ },
138};
139
140U_BOOT_DRIVER(socfpga_reset) = {
141 .name = "socfpga-reset",
142 .id = UCLASS_RESET,
143 .of_match = socfpga_reset_match,
144 .probe = socfpga_reset_probe,
145 .priv_auto_alloc_size = sizeof(struct socfpga_reset_data),
146 .ops = &socfpga_reset_ops,
Simon Goldschmidtede6e7b2019-03-01 20:12:32 +0100147 .remove = socfpga_reset_remove,
148 .flags = DM_FLAG_OS_PREPARE,
Dinh Nguyen2ac71882018-04-04 17:18:20 -0500149};