linux/drivers/power/reset/odroid-go-ultra-poweroff.c
Rob Herring 2ce8284c31 power: Explicitly include correct DT includes
The DT of_device.h and of_platform.h date back to the separate
of_platform_bus_type before it as merged into the regular platform bus.
As part of that merge prepping Arm DT support 13 years ago, they
"temporarily" include each other. They also include platform_device.h
and of.h. As a result, there's a pretty much random mix of those include
files used throughout the tree. In order to detangle these headers and
replace the implicit includes with struct declarations, users need to
explicitly include the correct includes.

Acked-by: David Lechner <david@lechnology.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
2023-07-19 22:47:03 +02:00

179 lines
4.8 KiB
C

// 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.h>
#include <linux/platform_device.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");