1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2014 STMicroelectronics |
4 | * |
5 | * Power off Restart driver, used in STMicroelectronics devices. |
6 | * |
7 | * Author: Christophe Kerello <christophe.kerello@st.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_device.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/reboot.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | struct reset_syscfg { |
19 | struct regmap *regmap; |
20 | /* syscfg used for reset */ |
21 | unsigned int offset_rst; |
22 | unsigned int mask_rst; |
23 | /* syscfg used for unmask the reset */ |
24 | unsigned int offset_rst_msk; |
25 | unsigned int mask_rst_msk; |
26 | }; |
27 | |
28 | /* STiH407 */ |
29 | #define STIH407_SYSCFG_4000 0x0 |
30 | #define STIH407_SYSCFG_4008 0x20 |
31 | |
32 | static struct reset_syscfg stih407_reset = { |
33 | .offset_rst = STIH407_SYSCFG_4000, |
34 | .mask_rst = BIT(0), |
35 | .offset_rst_msk = STIH407_SYSCFG_4008, |
36 | .mask_rst_msk = BIT(0) |
37 | }; |
38 | |
39 | |
40 | static struct reset_syscfg *st_restart_syscfg; |
41 | |
42 | static int st_restart(struct notifier_block *this, unsigned long mode, |
43 | void *cmd) |
44 | { |
45 | /* reset syscfg updated */ |
46 | regmap_update_bits(map: st_restart_syscfg->regmap, |
47 | reg: st_restart_syscfg->offset_rst, |
48 | mask: st_restart_syscfg->mask_rst, |
49 | val: 0); |
50 | |
51 | /* unmask the reset */ |
52 | regmap_update_bits(map: st_restart_syscfg->regmap, |
53 | reg: st_restart_syscfg->offset_rst_msk, |
54 | mask: st_restart_syscfg->mask_rst_msk, |
55 | val: 0); |
56 | |
57 | return NOTIFY_DONE; |
58 | } |
59 | |
60 | static struct notifier_block st_restart_nb = { |
61 | .notifier_call = st_restart, |
62 | .priority = 192, |
63 | }; |
64 | |
65 | static const struct of_device_id st_reset_of_match[] = { |
66 | { |
67 | .compatible = "st,stih407-restart" , |
68 | .data = (void *)&stih407_reset, |
69 | }, |
70 | {} |
71 | }; |
72 | |
73 | static int st_reset_probe(struct platform_device *pdev) |
74 | { |
75 | struct device_node *np = pdev->dev.of_node; |
76 | const struct of_device_id *match; |
77 | struct device *dev = &pdev->dev; |
78 | |
79 | match = of_match_device(matches: st_reset_of_match, dev); |
80 | if (!match) |
81 | return -ENODEV; |
82 | |
83 | st_restart_syscfg = (struct reset_syscfg *)match->data; |
84 | |
85 | st_restart_syscfg->regmap = |
86 | syscon_regmap_lookup_by_phandle(np, property: "st,syscfg" ); |
87 | if (IS_ERR(ptr: st_restart_syscfg->regmap)) { |
88 | dev_err(dev, "No syscfg phandle specified\n" ); |
89 | return PTR_ERR(ptr: st_restart_syscfg->regmap); |
90 | } |
91 | |
92 | return register_restart_handler(&st_restart_nb); |
93 | } |
94 | |
95 | static struct platform_driver st_reset_driver = { |
96 | .probe = st_reset_probe, |
97 | .driver = { |
98 | .name = "st_reset" , |
99 | .of_match_table = st_reset_of_match, |
100 | }, |
101 | }; |
102 | |
103 | builtin_platform_driver(st_reset_driver); |
104 | |
105 | MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>" ); |
106 | MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver" ); |
107 | MODULE_LICENSE("GPL v2" ); |
108 | |