2 * Rockchip Generic power configuration support.
4 * Copyright (c) 2017 ROCKCHIP, Co. Ltd.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/arm-smccc.h>
12 #include <linux/bitops.h>
13 #include <linux/cpu.h>
14 #include <linux/of_gpio.h>
15 #include <linux/platform_device.h>
16 #include <linux/regulator/machine.h>
17 #include <linux/rockchip/rockchip_sip.h>
18 #include <linux/suspend.h>
19 #include <dt-bindings/input/input.h>
21 #define PM_INVALID_GPIO 0xffff
23 static const struct of_device_id pm_match_table[] = {
24 { .compatible = "rockchip,pm-rk3399",},
28 #define MAX_PWRKEY_NUMS 20
29 #define MAX_NUM_KEYS 60
31 struct rkxx_remote_key_table {
36 static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey)
38 struct device_node *node;
39 struct device_node *child_node;
40 struct rkxx_remote_key_table key_table[MAX_NUM_KEYS];
42 int len = 0, nbuttons;
44 u32 usercode, scancode;
46 for_each_node_by_name(node, "pwm") {
47 for_each_child_of_node(node, child_node) {
48 if (of_property_read_u32(child_node,
53 if (of_get_property(child_node,
59 len = len < sizeof(key_table) ? len : sizeof(key_table);
61 if (of_property_read_u32_array(child_node,
68 for (i = 0; i < nbuttons && num < size; ++i) {
69 if (key_table[i].keycode == KEY_POWER) {
70 scancode = key_table[i].scancode;
71 pr_debug("usercode=%x, key=%x\n",
73 pwrkey[num] = (usercode & 0xffff) << 16;
74 pwrkey[num] |= (scancode & 0xff) << 8;
86 static void rockchip_pm_virt_pwroff_prepare(void)
90 u32 power_key[MAX_PWRKEY_NUMS];
92 if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) {
93 pr_err("Parse ir powerkey code failed!\n");
97 for (i = 0; i < nkey; ++i)
98 sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]);
100 regulator_suspend_prepare(PM_SUSPEND_MEM);
102 error = disable_nonboot_cpus();
104 pr_err("Disable nonboot cpus failed!\n");
108 sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
109 sip_smc_virtual_poweroff();
112 static int __init pm_config_init(struct platform_device *pdev)
114 const struct of_device_id *match_id;
115 struct device_node *node;
117 u32 wakeup_config = 0;
118 u32 pwm_regulator_config = 0;
120 u32 sleep_debug_en = 0;
121 u32 apios_suspend = 0;
122 u32 virtual_poweroff_en = 0;
123 enum of_gpio_flags flags;
127 match_id = of_match_node(pm_match_table, pdev->dev.of_node);
131 node = of_find_node_by_name(NULL, "rockchip-suspend");
133 if (IS_ERR_OR_NULL(node)) {
134 dev_err(&pdev->dev, "%s dev node err\n", __func__);
138 if (of_property_read_u32_array(node,
139 "rockchip,sleep-mode-config",
141 dev_warn(&pdev->dev, "not set sleep mode config\n");
143 sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0);
145 if (of_property_read_u32_array(node,
146 "rockchip,wakeup-config",
148 dev_warn(&pdev->dev, "not set wakeup-config\n");
150 sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0);
152 if (of_property_read_u32_array(node,
153 "rockchip,pwm-regulator-config",
154 &pwm_regulator_config, 1))
155 dev_warn(&pdev->dev, "not set pwm-regulator-config\n");
157 sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG,
158 pwm_regulator_config,
161 length = of_gpio_named_count(node, "rockchip,power-ctrl");
163 if (length > 0 && length < 10) {
164 for (i = 0; i < length; i++) {
165 gpio_temp[i] = of_get_named_gpio_flags(node,
166 "rockchip,power-ctrl",
169 if (!gpio_is_valid(gpio_temp[i]))
171 sip_smc_set_suspend_mode(GPIO_POWER_CONFIG,
176 sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO);
178 if (!of_property_read_u32_array(node,
179 "rockchip,sleep-debug-en",
181 sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE,
185 if (!of_property_read_u32_array(node,
186 "rockchip,apios-suspend",
188 sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG,
192 if (!of_property_read_u32_array(node,
193 "rockchip,virtual-poweroff",
194 &virtual_poweroff_en, 1) &&
196 pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
201 static struct platform_driver pm_driver = {
203 .name = "rockchip-pm",
204 .of_match_table = pm_match_table,
208 static int __init rockchip_pm_drv_register(void)
210 return platform_driver_probe(&pm_driver, pm_config_init);
212 subsys_initcall(rockchip_pm_drv_register);