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