-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <neil.armstrong@linaro.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
- Loading branch information
Neil Armstrong
authored and
Sebastian Reichel
committed
Feb 14, 2023
1 parent
469bb60
commit cec3b46
Showing
3 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// SPDX-License-Identifier: GPL-2.0+ | ||
/* | ||
* Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org> | ||
*/ | ||
#include <linux/kernel.h> | ||
#include <linux/init.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/mfd/rk808.h> | ||
#include <linux/regmap.h> | ||
#include <linux/module.h> | ||
#include <linux/reboot.h> | ||
#include <linux/i2c.h> | ||
|
||
/* | ||
* The Odroid Go Ultra has 2 PMICs: | ||
* - RK818 (manages the battery and USB-C power supply) | ||
* - RK817 | ||
* Both PMICs feeds power to the S922X SoC, so they must be powered-off in sequence. | ||
* Vendor does power-off the RK817 first, then the RK818 so here we follow this sequence. | ||
*/ | ||
|
||
struct odroid_go_ultra_poweroff_data { | ||
struct device *dev; | ||
struct device *rk817; | ||
struct device *rk818; | ||
}; | ||
|
||
static int odroid_go_ultra_poweroff_prepare(struct sys_off_data *data) | ||
{ | ||
struct odroid_go_ultra_poweroff_data *poweroff_data = data->cb_data; | ||
struct regmap *rk817, *rk818; | ||
int ret; | ||
|
||
/* RK817 Regmap */ | ||
rk817 = dev_get_regmap(poweroff_data->rk817, NULL); | ||
if (!rk817) { | ||
dev_err(poweroff_data->dev, "failed to get rk817 regmap\n"); | ||
return notifier_from_errno(-EINVAL); | ||
} | ||
|
||
/* RK818 Regmap */ | ||
rk818 = dev_get_regmap(poweroff_data->rk818, NULL); | ||
if (!rk818) { | ||
dev_err(poweroff_data->dev, "failed to get rk818 regmap\n"); | ||
return notifier_from_errno(-EINVAL); | ||
} | ||
|
||
dev_info(poweroff_data->dev, "Setting PMICs for power off"); | ||
|
||
/* RK817 */ | ||
ret = regmap_update_bits(rk817, RK817_SYS_CFG(3), DEV_OFF, DEV_OFF); | ||
if (ret) { | ||
dev_err(poweroff_data->dev, "failed to poweroff rk817\n"); | ||
return notifier_from_errno(ret); | ||
} | ||
|
||
/* RK818 */ | ||
ret = regmap_update_bits(rk818, RK818_DEVCTRL_REG, DEV_OFF, DEV_OFF); | ||
if (ret) { | ||
dev_err(poweroff_data->dev, "failed to poweroff rk818\n"); | ||
return notifier_from_errno(ret); | ||
} | ||
|
||
return NOTIFY_OK; | ||
} | ||
|
||
static void odroid_go_ultra_poweroff_put_pmic_device(void *data) | ||
{ | ||
struct device *dev = data; | ||
|
||
put_device(dev); | ||
} | ||
|
||
static int odroid_go_ultra_poweroff_get_pmic_device(struct device *dev, const char *compatible, | ||
struct device **pmic) | ||
{ | ||
struct device_node *pmic_node; | ||
struct i2c_client *pmic_client; | ||
|
||
pmic_node = of_find_compatible_node(NULL, NULL, compatible); | ||
if (!pmic_node) | ||
return -ENODEV; | ||
|
||
pmic_client = of_find_i2c_device_by_node(pmic_node); | ||
of_node_put(pmic_node); | ||
if (!pmic_client) | ||
return -EPROBE_DEFER; | ||
|
||
*pmic = &pmic_client->dev; | ||
|
||
return devm_add_action_or_reset(dev, odroid_go_ultra_poweroff_put_pmic_device, *pmic); | ||
} | ||
|
||
static int odroid_go_ultra_poweroff_probe(struct platform_device *pdev) | ||
{ | ||
struct odroid_go_ultra_poweroff_data *poweroff_data; | ||
int ret; | ||
|
||
poweroff_data = devm_kzalloc(&pdev->dev, sizeof(*poweroff_data), GFP_KERNEL); | ||
if (!poweroff_data) | ||
return -ENOMEM; | ||
|
||
dev_set_drvdata(&pdev->dev, poweroff_data); | ||
|
||
/* RK818 PMIC Device */ | ||
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk818", | ||
&poweroff_data->rk818); | ||
if (ret) | ||
return dev_err_probe(&pdev->dev, ret, "failed to get rk818 mfd data\n"); | ||
|
||
/* RK817 PMIC Device */ | ||
ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk817", | ||
&poweroff_data->rk817); | ||
if (ret) | ||
return dev_err_probe(&pdev->dev, ret, "failed to get rk817 mfd data\n"); | ||
|
||
/* Register as SYS_OFF_MODE_POWER_OFF_PREPARE because regmap_update_bits may sleep */ | ||
ret = devm_register_sys_off_handler(&pdev->dev, | ||
SYS_OFF_MODE_POWER_OFF_PREPARE, | ||
SYS_OFF_PRIO_DEFAULT, | ||
odroid_go_ultra_poweroff_prepare, | ||
poweroff_data); | ||
if (ret) | ||
return dev_err_probe(&pdev->dev, ret, "failed to register sys-off handler\n"); | ||
|
||
dev_info(&pdev->dev, "Registered Power-Off handler\n"); | ||
|
||
return 0; | ||
} | ||
static struct platform_device *pdev; | ||
|
||
static struct platform_driver odroid_go_ultra_poweroff_driver = { | ||
.driver = { | ||
.name = "odroid-go-ultra-poweroff", | ||
}, | ||
.probe = odroid_go_ultra_poweroff_probe, | ||
}; | ||
|
||
static int __init odroid_go_ultra_poweroff_init(void) | ||
{ | ||
int ret; | ||
|
||
/* Only create when running on the Odroid Go Ultra device */ | ||
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) | ||
return -ENODEV; | ||
|
||
ret = platform_driver_register(&odroid_go_ultra_poweroff_driver); | ||
if (ret) | ||
return ret; | ||
|
||
pdev = platform_device_register_resndata(NULL, "odroid-go-ultra-poweroff", -1, | ||
NULL, 0, NULL, 0); | ||
|
||
if (IS_ERR(pdev)) { | ||
platform_driver_unregister(&odroid_go_ultra_poweroff_driver); | ||
return PTR_ERR(pdev); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static void __exit odroid_go_ultra_poweroff_exit(void) | ||
{ | ||
/* Only delete when running on the Odroid Go Ultra device */ | ||
if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) | ||
return; | ||
|
||
platform_device_unregister(pdev); | ||
platform_driver_unregister(&odroid_go_ultra_poweroff_driver); | ||
} | ||
|
||
module_init(odroid_go_ultra_poweroff_init); | ||
module_exit(odroid_go_ultra_poweroff_exit); | ||
|
||
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); | ||
MODULE_DESCRIPTION("Odroid Go Ultra poweroff driver"); | ||
MODULE_LICENSE("GPL"); |