summaryrefslogtreecommitdiffstats
path: root/drivers/power/reset/st-poweroff.c
blob: a488877a3538c8c9761d1c8e468cdf1c3edb91de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * Copyright (C) 2014 STMicroelectronics
 *
 * Power off Restart driver, used in STMicroelectronics devices.
 *
 * Author: Christophe Kerello <christophe.kerello@st.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/reboot.h>
#include <linux/regmap.h>

struct reset_syscfg {
	struct regmap *regmap;
	/* syscfg used for reset */
	unsigned int offset_rst;
	unsigned int mask_rst;
	/* syscfg used for unmask the reset */
	unsigned int offset_rst_msk;
	unsigned int mask_rst_msk;
};

/* STiH415 */
#define STIH415_SYSCFG_11	0x2c
#define STIH415_SYSCFG_15	0x3c

static struct reset_syscfg stih415_reset = {
	.offset_rst = STIH415_SYSCFG_11,
	.mask_rst = BIT(0),
	.offset_rst_msk = STIH415_SYSCFG_15,
	.mask_rst_msk = BIT(0)
};

/* STiH416 */
#define STIH416_SYSCFG_500	0x7d0
#define STIH416_SYSCFG_504	0x7e0

static struct reset_syscfg stih416_reset = {
	.offset_rst = STIH416_SYSCFG_500,
	.mask_rst = BIT(0),
	.offset_rst_msk = STIH416_SYSCFG_504,
	.mask_rst_msk = BIT(0)
};

/* STiH407 */
#define STIH407_SYSCFG_4000	0x0
#define STIH407_SYSCFG_4008	0x20

static struct reset_syscfg stih407_reset = {
	.offset_rst = STIH407_SYSCFG_4000,
	.mask_rst = BIT(0),
	.offset_rst_msk = STIH407_SYSCFG_4008,
	.mask_rst_msk = BIT(0)
};

/* STiD127 */
#define STID127_SYSCFG_700	0x0
#define STID127_SYSCFG_773	0x124

static struct reset_syscfg stid127_reset = {
	.offset_rst = STID127_SYSCFG_773,
	.mask_rst = BIT(0),
	.offset_rst_msk = STID127_SYSCFG_700,
	.mask_rst_msk = BIT(8)
};

static struct reset_syscfg *st_restart_syscfg;

static int st_restart(struct notifier_block *this, unsigned long mode,
		      void *cmd)
{
	/* reset syscfg updated */
	regmap_update_bits(st_restart_syscfg->regmap,
			   st_restart_syscfg->offset_rst,
			   st_restart_syscfg->mask_rst,
			   0);

	/* unmask the reset */
	regmap_update_bits(st_restart_syscfg->regmap,
			   st_restart_syscfg->offset_rst_msk,
			   st_restart_syscfg->mask_rst_msk,
			   0);

	return NOTIFY_DONE;
}

static struct notifier_block st_restart_nb = {
	.notifier_call = st_restart,
	.priority = 192,
};

static const struct of_device_id st_reset_of_match[] = {
	{
		.compatible = "st,stih415-restart",
		.data = (void *)&stih415_reset,
	}, {
		.compatible = "st,stih416-restart",
		.data = (void *)&stih416_reset,
	}, {
		.compatible = "st,stih407-restart",
		.data = (void *)&stih407_reset,
	}, {
		.compatible = "st,stid127-restart",
		.data = (void *)&stid127_reset,
	},
	{}
};

static int st_reset_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *match;
	struct device *dev = &pdev->dev;

	match = of_match_device(st_reset_of_match, dev);
	if (!match)
		return -ENODEV;

	st_restart_syscfg = (struct reset_syscfg *)match->data;

	st_restart_syscfg->regmap =
		syscon_regmap_lookup_by_phandle(np, "st,syscfg");
	if (IS_ERR(st_restart_syscfg->regmap)) {
		dev_err(dev, "No syscfg phandle specified\n");
		return PTR_ERR(st_restart_syscfg->regmap);
	}

	return register_restart_handler(&st_restart_nb);
}

static struct platform_driver st_reset_driver = {
	.probe = st_reset_probe,
	.driver = {
		.name = "st_reset",
		.of_match_table = st_reset_of_match,
	},
};

static int __init st_reset_init(void)
{
	return platform_driver_register(&st_reset_driver);
}

device_initcall(st_reset_init);

MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver");
MODULE_LICENSE("GPL v2");