1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * TI keystone reboot driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2014 Texas Instruments Incorporated. https://www.ti.com/
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/io.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/notifier.h>
13*4882a593Smuzhiyun #include <linux/reboot.h>
14*4882a593Smuzhiyun #include <linux/regmap.h>
15*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
16*4882a593Smuzhiyun #include <linux/of_platform.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define RSTYPE_RG 0x0
19*4882a593Smuzhiyun #define RSCTRL_RG 0x4
20*4882a593Smuzhiyun #define RSCFG_RG 0x8
21*4882a593Smuzhiyun #define RSISO_RG 0xc
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define RSCTRL_KEY_MASK 0x0000ffff
24*4882a593Smuzhiyun #define RSCTRL_RESET_MASK BIT(16)
25*4882a593Smuzhiyun #define RSCTRL_KEY 0x5a69
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define RSMUX_OMODE_MASK 0xe
28*4882a593Smuzhiyun #define RSMUX_OMODE_RESET_ON 0xa
29*4882a593Smuzhiyun #define RSMUX_OMODE_RESET_OFF 0x0
30*4882a593Smuzhiyun #define RSMUX_LOCK_MASK 0x1
31*4882a593Smuzhiyun #define RSMUX_LOCK_SET 0x1
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #define RSCFG_RSTYPE_SOFT 0x300f
34*4882a593Smuzhiyun #define RSCFG_RSTYPE_HARD 0x0
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define WDT_MUX_NUMBER 0x4
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static int rspll_offset;
39*4882a593Smuzhiyun static struct regmap *pllctrl_regs;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun /**
42*4882a593Smuzhiyun * rsctrl_enable_rspll_write - enable access to RSCTRL, RSCFG
43*4882a593Smuzhiyun * To be able to access to RSCTRL, RSCFG registers
44*4882a593Smuzhiyun * we have to write a key before
45*4882a593Smuzhiyun */
rsctrl_enable_rspll_write(void)46*4882a593Smuzhiyun static inline int rsctrl_enable_rspll_write(void)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun return regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
49*4882a593Smuzhiyun RSCTRL_KEY_MASK, RSCTRL_KEY);
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
rsctrl_restart_handler(struct notifier_block * this,unsigned long mode,void * cmd)52*4882a593Smuzhiyun static int rsctrl_restart_handler(struct notifier_block *this,
53*4882a593Smuzhiyun unsigned long mode, void *cmd)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun /* enable write access to RSTCTRL */
56*4882a593Smuzhiyun rsctrl_enable_rspll_write();
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* reset the SOC */
59*4882a593Smuzhiyun regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
60*4882a593Smuzhiyun RSCTRL_RESET_MASK, 0);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun return NOTIFY_DONE;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun static struct notifier_block rsctrl_restart_nb = {
66*4882a593Smuzhiyun .notifier_call = rsctrl_restart_handler,
67*4882a593Smuzhiyun .priority = 128,
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun static const struct of_device_id rsctrl_of_match[] = {
71*4882a593Smuzhiyun {.compatible = "ti,keystone-reset", },
72*4882a593Smuzhiyun {},
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
rsctrl_probe(struct platform_device * pdev)75*4882a593Smuzhiyun static int rsctrl_probe(struct platform_device *pdev)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun int i;
78*4882a593Smuzhiyun int ret;
79*4882a593Smuzhiyun u32 val;
80*4882a593Smuzhiyun unsigned int rg;
81*4882a593Smuzhiyun u32 rsmux_offset;
82*4882a593Smuzhiyun struct regmap *devctrl_regs;
83*4882a593Smuzhiyun struct device *dev = &pdev->dev;
84*4882a593Smuzhiyun struct device_node *np = dev->of_node;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun if (!np)
87*4882a593Smuzhiyun return -ENODEV;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /* get regmaps */
90*4882a593Smuzhiyun pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll");
91*4882a593Smuzhiyun if (IS_ERR(pllctrl_regs))
92*4882a593Smuzhiyun return PTR_ERR(pllctrl_regs);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
95*4882a593Smuzhiyun if (IS_ERR(devctrl_regs))
96*4882a593Smuzhiyun return PTR_ERR(devctrl_regs);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset);
99*4882a593Smuzhiyun if (ret) {
100*4882a593Smuzhiyun dev_err(dev, "couldn't read the reset pll offset!\n");
101*4882a593Smuzhiyun return -EINVAL;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset);
105*4882a593Smuzhiyun if (ret) {
106*4882a593Smuzhiyun dev_err(dev, "couldn't read the rsmux offset!\n");
107*4882a593Smuzhiyun return -EINVAL;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun /* set soft/hard reset */
111*4882a593Smuzhiyun val = of_property_read_bool(np, "ti,soft-reset");
112*4882a593Smuzhiyun val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun ret = rsctrl_enable_rspll_write();
115*4882a593Smuzhiyun if (ret)
116*4882a593Smuzhiyun return ret;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun ret = regmap_write(pllctrl_regs, rspll_offset + RSCFG_RG, val);
119*4882a593Smuzhiyun if (ret)
120*4882a593Smuzhiyun return ret;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* disable a reset isolation for all module clocks */
123*4882a593Smuzhiyun ret = regmap_write(pllctrl_regs, rspll_offset + RSISO_RG, 0);
124*4882a593Smuzhiyun if (ret)
125*4882a593Smuzhiyun return ret;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* enable a reset for watchdogs from wdt-list */
128*4882a593Smuzhiyun for (i = 0; i < WDT_MUX_NUMBER; i++) {
129*4882a593Smuzhiyun ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val);
130*4882a593Smuzhiyun if (ret == -EOVERFLOW && !i) {
131*4882a593Smuzhiyun dev_err(dev, "ti,wdt-list property has to contain at"
132*4882a593Smuzhiyun "least one entry\n");
133*4882a593Smuzhiyun return -EINVAL;
134*4882a593Smuzhiyun } else if (ret) {
135*4882a593Smuzhiyun break;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (val >= WDT_MUX_NUMBER) {
139*4882a593Smuzhiyun dev_err(dev, "ti,wdt-list property can contain "
140*4882a593Smuzhiyun "only numbers < 4\n");
141*4882a593Smuzhiyun return -EINVAL;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun rg = rsmux_offset + val * 4;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun ret = regmap_update_bits(devctrl_regs, rg, RSMUX_OMODE_MASK,
147*4882a593Smuzhiyun RSMUX_OMODE_RESET_ON |
148*4882a593Smuzhiyun RSMUX_LOCK_SET);
149*4882a593Smuzhiyun if (ret)
150*4882a593Smuzhiyun return ret;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun ret = register_restart_handler(&rsctrl_restart_nb);
154*4882a593Smuzhiyun if (ret)
155*4882a593Smuzhiyun dev_err(dev, "cannot register restart handler (err=%d)\n", ret);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun return ret;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun static struct platform_driver rsctrl_driver = {
161*4882a593Smuzhiyun .probe = rsctrl_probe,
162*4882a593Smuzhiyun .driver = {
163*4882a593Smuzhiyun .name = KBUILD_MODNAME,
164*4882a593Smuzhiyun .of_match_table = rsctrl_of_match,
165*4882a593Smuzhiyun },
166*4882a593Smuzhiyun };
167*4882a593Smuzhiyun module_platform_driver(rsctrl_driver);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
170*4882a593Smuzhiyun MODULE_DESCRIPTION("Texas Instruments keystone reset driver");
171*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
172*4882a593Smuzhiyun MODULE_ALIAS("platform:" KBUILD_MODNAME);
173