firmware: rockchip: update sip interface
[firefly-linux-kernel-4.4.55.git] / drivers / soc / rockchip / rockchip_pm_config.c
1 /*
2  * Rockchip Generic power configuration support.
3  *
4  * Copyright (c) 2017 ROCKCHIP, Co. Ltd.
5  *
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.
9  */
10
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>
20
21 #define PM_INVALID_GPIO 0xffff
22
23 static const struct of_device_id pm_match_table[] = {
24         { .compatible = "rockchip,pm-rk3399",},
25         { },
26 };
27
28 #define MAX_PWRKEY_NUMS         20
29 #define MAX_NUM_KEYS            60
30
31 struct rkxx_remote_key_table {
32         int scancode;
33         int keycode;
34 };
35
36 static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey)
37 {
38         struct device_node *node;
39         struct device_node *child_node;
40         struct rkxx_remote_key_table key_table[MAX_NUM_KEYS];
41         int i;
42         int len = 0, nbuttons;
43         int num = 0;
44         u32 usercode, scancode;
45
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,
49                                                  "rockchip,usercode",
50                                                  &usercode))
51                                 break;
52
53                         if (of_get_property(child_node,
54                                             "rockchip,key_table",
55                                             &len) == NULL ||
56                             len <= 0)
57                                 break;
58
59                         len = len < sizeof(key_table) ? len : sizeof(key_table);
60                         len /= sizeof(u32);
61                         if (of_property_read_u32_array(child_node,
62                                                        "rockchip,key_table",
63                                                        (u32 *)key_table,
64                                                        len))
65                                 break;
66
67                         nbuttons = len / 2;
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",
72                                                  usercode, scancode);
73                                         pwrkey[num] = (usercode & 0xffff) << 16;
74                                         pwrkey[num] |= (scancode & 0xff) << 8;
75                                         ++num;
76                                 }
77                         }
78                 }
79         }
80
81         *nkey = num;
82
83         return num ? 0 : -1;
84 }
85
86 static void rockchip_pm_virt_pwroff_prepare(void)
87 {
88         int error;
89         int i, nkey;
90         u32 power_key[MAX_PWRKEY_NUMS];
91
92         if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) {
93                 pr_err("Parse ir powerkey code failed!\n");
94                 return;
95         }
96
97         for (i = 0; i < nkey; ++i)
98                 sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]);
99
100         regulator_suspend_prepare(PM_SUSPEND_MEM);
101
102         error = disable_nonboot_cpus();
103         if (error) {
104                 pr_err("Disable nonboot cpus failed!\n");
105                 return;
106         }
107
108         sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
109         sip_smc_virtual_poweroff();
110 }
111
112 static int __init pm_config_init(struct platform_device *pdev)
113 {
114         const struct of_device_id *match_id;
115         struct device_node *node;
116         u32 mode_config = 0;
117         u32 wakeup_config = 0;
118         u32 pwm_regulator_config = 0;
119         int gpio_temp[10];
120         u32 sleep_debug_en = 0;
121         u32 apios_suspend = 0;
122         u32 virtual_poweroff_en = 0;
123         enum of_gpio_flags flags;
124         int i = 0;
125         int length;
126
127         match_id = of_match_node(pm_match_table, pdev->dev.of_node);
128         if (!match_id)
129                 return -ENODEV;
130
131         node = of_find_node_by_name(NULL, "rockchip-suspend");
132
133         if (IS_ERR_OR_NULL(node)) {
134                 dev_err(&pdev->dev, "%s dev node err\n",  __func__);
135                 return -ENODEV;
136         }
137
138         if (of_property_read_u32_array(node,
139                                        "rockchip,sleep-mode-config",
140                                        &mode_config, 1))
141                 dev_warn(&pdev->dev, "not set sleep mode config\n");
142         else
143                 sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0);
144
145         if (of_property_read_u32_array(node,
146                                        "rockchip,wakeup-config",
147                                        &wakeup_config, 1))
148                 dev_warn(&pdev->dev, "not set wakeup-config\n");
149         else
150                 sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0);
151
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");
156         else
157                 sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG,
158                                          pwm_regulator_config,
159                                          0);
160
161         length = of_gpio_named_count(node, "rockchip,power-ctrl");
162
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",
167                                                              i,
168                                                              &flags);
169                         if (!gpio_is_valid(gpio_temp[i]))
170                                 break;
171                         sip_smc_set_suspend_mode(GPIO_POWER_CONFIG,
172                                                  i,
173                                                  gpio_temp[i]);
174                 }
175         }
176         sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO);
177
178         if (!of_property_read_u32_array(node,
179                                         "rockchip,sleep-debug-en",
180                                         &sleep_debug_en, 1))
181                 sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE,
182                                          sleep_debug_en,
183                                          0);
184
185         if (!of_property_read_u32_array(node,
186                                         "rockchip,apios-suspend",
187                                         &apios_suspend, 1))
188                 sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG,
189                                          apios_suspend,
190                                          0);
191
192         if (!of_property_read_u32_array(node,
193                                         "rockchip,virtual-poweroff",
194                                         &virtual_poweroff_en, 1) &&
195             virtual_poweroff_en)
196                 pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
197
198         return 0;
199 }
200
201 static struct platform_driver pm_driver = {
202         .driver = {
203                 .name = "rockchip-pm",
204                 .of_match_table = pm_match_table,
205         },
206 };
207
208 static int __init rockchip_pm_drv_register(void)
209 {
210         return platform_driver_probe(&pm_driver, pm_config_init);
211 }
212 subsys_initcall(rockchip_pm_drv_register);