Skip to content

Commit cec3b46

Browse files
superna9999sre
authored andcommitted
power: reset: add Odroid Go Ultra poweroff driver
The Hardkernel Odroid Go Ultra poweroff scheme requires requesting a poweroff to its two PMICs in order, this represents the poweroff scheme needed to complete a clean poweroff of the system. This implement this scheme by implementing a self registering driver to permit using probe defer until both pmics are finally probed. Signed-off-by: Neil Armstrong <[email protected]> Signed-off-by: Sebastian Reichel <[email protected]>
1 parent 469bb60 commit cec3b46

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

drivers/power/reset/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ config POWER_RESET_OCELOT_RESET
141141
help
142142
This driver supports restart for Microsemi Ocelot SoC and similar.
143143

144+
config POWER_RESET_ODROID_GO_ULTRA_POWEROFF
145+
bool "Odroid Go Ultra power-off driver"
146+
depends on ARCH_MESON || COMPILE_TEST
147+
depends on I2C && OF
148+
help
149+
This driver supports Power off for Odroid Go Ultra device.
150+
144151
config POWER_RESET_OXNAS
145152
bool "OXNAS SoC restart driver"
146153
depends on ARCH_OXNAS

drivers/power/reset/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
1717
obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o
1818
obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
1919
obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
20+
obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.o
2021
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
2122
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
2223
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Copyright (c) 2023 Neil Armstrong <[email protected]>
4+
*/
5+
#include <linux/kernel.h>
6+
#include <linux/init.h>
7+
#include <linux/of_platform.h>
8+
#include <linux/mfd/rk808.h>
9+
#include <linux/regmap.h>
10+
#include <linux/module.h>
11+
#include <linux/reboot.h>
12+
#include <linux/i2c.h>
13+
14+
/*
15+
* The Odroid Go Ultra has 2 PMICs:
16+
* - RK818 (manages the battery and USB-C power supply)
17+
* - RK817
18+
* Both PMICs feeds power to the S922X SoC, so they must be powered-off in sequence.
19+
* Vendor does power-off the RK817 first, then the RK818 so here we follow this sequence.
20+
*/
21+
22+
struct odroid_go_ultra_poweroff_data {
23+
struct device *dev;
24+
struct device *rk817;
25+
struct device *rk818;
26+
};
27+
28+
static int odroid_go_ultra_poweroff_prepare(struct sys_off_data *data)
29+
{
30+
struct odroid_go_ultra_poweroff_data *poweroff_data = data->cb_data;
31+
struct regmap *rk817, *rk818;
32+
int ret;
33+
34+
/* RK817 Regmap */
35+
rk817 = dev_get_regmap(poweroff_data->rk817, NULL);
36+
if (!rk817) {
37+
dev_err(poweroff_data->dev, "failed to get rk817 regmap\n");
38+
return notifier_from_errno(-EINVAL);
39+
}
40+
41+
/* RK818 Regmap */
42+
rk818 = dev_get_regmap(poweroff_data->rk818, NULL);
43+
if (!rk818) {
44+
dev_err(poweroff_data->dev, "failed to get rk818 regmap\n");
45+
return notifier_from_errno(-EINVAL);
46+
}
47+
48+
dev_info(poweroff_data->dev, "Setting PMICs for power off");
49+
50+
/* RK817 */
51+
ret = regmap_update_bits(rk817, RK817_SYS_CFG(3), DEV_OFF, DEV_OFF);
52+
if (ret) {
53+
dev_err(poweroff_data->dev, "failed to poweroff rk817\n");
54+
return notifier_from_errno(ret);
55+
}
56+
57+
/* RK818 */
58+
ret = regmap_update_bits(rk818, RK818_DEVCTRL_REG, DEV_OFF, DEV_OFF);
59+
if (ret) {
60+
dev_err(poweroff_data->dev, "failed to poweroff rk818\n");
61+
return notifier_from_errno(ret);
62+
}
63+
64+
return NOTIFY_OK;
65+
}
66+
67+
static void odroid_go_ultra_poweroff_put_pmic_device(void *data)
68+
{
69+
struct device *dev = data;
70+
71+
put_device(dev);
72+
}
73+
74+
static int odroid_go_ultra_poweroff_get_pmic_device(struct device *dev, const char *compatible,
75+
struct device **pmic)
76+
{
77+
struct device_node *pmic_node;
78+
struct i2c_client *pmic_client;
79+
80+
pmic_node = of_find_compatible_node(NULL, NULL, compatible);
81+
if (!pmic_node)
82+
return -ENODEV;
83+
84+
pmic_client = of_find_i2c_device_by_node(pmic_node);
85+
of_node_put(pmic_node);
86+
if (!pmic_client)
87+
return -EPROBE_DEFER;
88+
89+
*pmic = &pmic_client->dev;
90+
91+
return devm_add_action_or_reset(dev, odroid_go_ultra_poweroff_put_pmic_device, *pmic);
92+
}
93+
94+
static int odroid_go_ultra_poweroff_probe(struct platform_device *pdev)
95+
{
96+
struct odroid_go_ultra_poweroff_data *poweroff_data;
97+
int ret;
98+
99+
poweroff_data = devm_kzalloc(&pdev->dev, sizeof(*poweroff_data), GFP_KERNEL);
100+
if (!poweroff_data)
101+
return -ENOMEM;
102+
103+
dev_set_drvdata(&pdev->dev, poweroff_data);
104+
105+
/* RK818 PMIC Device */
106+
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk818",
107+
&poweroff_data->rk818);
108+
if (ret)
109+
return dev_err_probe(&pdev->dev, ret, "failed to get rk818 mfd data\n");
110+
111+
/* RK817 PMIC Device */
112+
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk817",
113+
&poweroff_data->rk817);
114+
if (ret)
115+
return dev_err_probe(&pdev->dev, ret, "failed to get rk817 mfd data\n");
116+
117+
/* Register as SYS_OFF_MODE_POWER_OFF_PREPARE because regmap_update_bits may sleep */
118+
ret = devm_register_sys_off_handler(&pdev->dev,
119+
SYS_OFF_MODE_POWER_OFF_PREPARE,
120+
SYS_OFF_PRIO_DEFAULT,
121+
odroid_go_ultra_poweroff_prepare,
122+
poweroff_data);
123+
if (ret)
124+
return dev_err_probe(&pdev->dev, ret, "failed to register sys-off handler\n");
125+
126+
dev_info(&pdev->dev, "Registered Power-Off handler\n");
127+
128+
return 0;
129+
}
130+
static struct platform_device *pdev;
131+
132+
static struct platform_driver odroid_go_ultra_poweroff_driver = {
133+
.driver = {
134+
.name = "odroid-go-ultra-poweroff",
135+
},
136+
.probe = odroid_go_ultra_poweroff_probe,
137+
};
138+
139+
static int __init odroid_go_ultra_poweroff_init(void)
140+
{
141+
int ret;
142+
143+
/* Only create when running on the Odroid Go Ultra device */
144+
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra"))
145+
return -ENODEV;
146+
147+
ret = platform_driver_register(&odroid_go_ultra_poweroff_driver);
148+
if (ret)
149+
return ret;
150+
151+
pdev = platform_device_register_resndata(NULL, "odroid-go-ultra-poweroff", -1,
152+
NULL, 0, NULL, 0);
153+
154+
if (IS_ERR(pdev)) {
155+
platform_driver_unregister(&odroid_go_ultra_poweroff_driver);
156+
return PTR_ERR(pdev);
157+
}
158+
159+
return 0;
160+
}
161+
162+
static void __exit odroid_go_ultra_poweroff_exit(void)
163+
{
164+
/* Only delete when running on the Odroid Go Ultra device */
165+
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra"))
166+
return;
167+
168+
platform_device_unregister(pdev);
169+
platform_driver_unregister(&odroid_go_ultra_poweroff_driver);
170+
}
171+
172+
module_init(odroid_go_ultra_poweroff_init);
173+
module_exit(odroid_go_ultra_poweroff_exit);
174+
175+
MODULE_AUTHOR("Neil Armstrong <[email protected]>");
176+
MODULE_DESCRIPTION("Odroid Go Ultra poweroff driver");
177+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)