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