• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/module.h>
15 #include <linux/of_gpio.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm.h>
18 #include <linux/regulator/driver.h>
19 #include <linux/regulator/machine.h>
20 #include <linux/rockchip/rockchip_sip.h>
21 #include <linux/suspend.h>
22 #include <dt-bindings/input/input.h>
23 #include <../drivers/regulator/internal.h>
24 
25 #define PM_INVALID_GPIO			0xffff
26 #define MAX_ON_OFF_REG_NUM		30
27 #define MAX_ON_OFF_REG_PROP_NAME_LEN	60
28 
29 #if defined(CONFIG_NO_GKI)
30 enum rk_pm_state {
31 	RK_PM_MEM = 0,
32 	RK_PM_MEM_LITE,
33 	RK_PM_MEM_ULTRA,
34 	RK_PM_STATE_MAX
35 };
36 
37 static struct rk_on_off_regulator_list {
38 	struct regulator_dev *on_reg_list[MAX_ON_OFF_REG_NUM];
39 	struct regulator_dev *off_reg_list[MAX_ON_OFF_REG_NUM];
40 } on_off_regs_list[RK_PM_STATE_MAX];
41 #endif
42 
43 static const struct of_device_id pm_match_table[] = {
44 	{ .compatible = "rockchip,pm-px30",},
45 	{ .compatible = "rockchip,pm-rk1808",},
46 	{ .compatible = "rockchip,pm-rk322x",},
47 	{ .compatible = "rockchip,pm-rk3288",},
48 	{ .compatible = "rockchip,pm-rk3308",},
49 	{ .compatible = "rockchip,pm-rk3328",},
50 	{ .compatible = "rockchip,pm-rk3368",},
51 	{ .compatible = "rockchip,pm-rk3399",},
52 	{ .compatible = "rockchip,pm-rk3568",},
53 	{ .compatible = "rockchip,pm-rv1126",},
54 	{ },
55 };
56 
57 #if defined(CONFIG_NO_GKI)
rockchip_pm_virt_pwroff_prepare(void)58 static void rockchip_pm_virt_pwroff_prepare(void)
59 {
60 	int error;
61 
62 	regulator_suspend_prepare(PM_SUSPEND_MEM);
63 
64 	error = suspend_disable_secondary_cpus();
65 	if (error) {
66 		pr_err("Disable nonboot cpus failed!\n");
67 		return;
68 	}
69 
70 	sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
71 	sip_smc_virtual_poweroff();
72 }
73 
parse_on_off_regulator(struct device_node * node,enum rk_pm_state state)74 static int parse_on_off_regulator(struct device_node *node, enum rk_pm_state state)
75 {
76 	char on_prop_name[MAX_ON_OFF_REG_PROP_NAME_LEN] = {0};
77 	char off_prop_name[MAX_ON_OFF_REG_PROP_NAME_LEN] = {0};
78 	int i, j;
79 	struct device_node *dn;
80 	struct regulator_dev *reg;
81 	struct regulator_dev **on_list;
82 	struct regulator_dev **off_list;
83 
84 	switch (state) {
85 	case RK_PM_MEM:
86 		strncpy(on_prop_name, "rockchip,regulator-on-in-mem",
87 			MAX_ON_OFF_REG_PROP_NAME_LEN);
88 		strncpy(off_prop_name, "rockchip,regulator-off-in-mem",
89 			MAX_ON_OFF_REG_PROP_NAME_LEN);
90 	break;
91 
92 	case RK_PM_MEM_LITE:
93 		strncpy(on_prop_name, "rockchip,regulator-on-in-mem-lite",
94 			MAX_ON_OFF_REG_PROP_NAME_LEN);
95 		strncpy(off_prop_name, "rockchip,regulator-off-in-mem-lite",
96 			MAX_ON_OFF_REG_PROP_NAME_LEN);
97 	break;
98 
99 	case RK_PM_MEM_ULTRA:
100 		strncpy(on_prop_name, "rockchip,regulator-on-in-mem-ultra",
101 			MAX_ON_OFF_REG_PROP_NAME_LEN);
102 		strncpy(off_prop_name, "rockchip,regulator-off-in-mem-ultra",
103 			MAX_ON_OFF_REG_PROP_NAME_LEN);
104 	break;
105 
106 	default:
107 		return 0;
108 	}
109 
110 	on_list = on_off_regs_list[state].on_reg_list;
111 	off_list = on_off_regs_list[state].off_reg_list;
112 
113 	if (of_find_property(node, on_prop_name, NULL)) {
114 		for (i = 0, j = 0;
115 		     (dn = of_parse_phandle(node, on_prop_name, i));
116 		     i++) {
117 			reg = of_find_regulator_by_node(dn);
118 			if (reg == NULL) {
119 				pr_warn("failed to find regulator %s for %s\n",
120 					dn->name, on_prop_name);
121 			} else {
122 				pr_debug("%s on regulator=%s\n", __func__,
123 					 reg->desc->name);
124 				on_list[j++] = reg;
125 			}
126 			of_node_put(dn);
127 
128 			if (j >= MAX_ON_OFF_REG_NUM)
129 				return 0;
130 		}
131 	}
132 
133 	if (of_find_property(node, off_prop_name, NULL)) {
134 		for (i = 0, j = 0;
135 		     (dn = of_parse_phandle(node, off_prop_name, i));
136 		     i++) {
137 			reg = of_find_regulator_by_node(dn);
138 			if (reg == NULL) {
139 				pr_warn("failed to find regulator %s for %s\n",
140 					dn->name, off_prop_name);
141 			} else {
142 				pr_debug("%s off regulator=%s\n", __func__,
143 					 reg->desc->name);
144 				off_list[j++] = reg;
145 			}
146 			of_node_put(dn);
147 
148 			if (j >= MAX_ON_OFF_REG_NUM)
149 				return 0;
150 		}
151 	}
152 
153 	return 0;
154 }
155 #endif
156 
pm_config_probe(struct platform_device * pdev)157 static int pm_config_probe(struct platform_device *pdev)
158 {
159 	const struct of_device_id *match_id;
160 	struct device_node *node;
161 	u32 mode_config = 0;
162 	u32 wakeup_config = 0;
163 	u32 pwm_regulator_config = 0;
164 	int gpio_temp[10];
165 	u32 sleep_debug_en = 0;
166 	u32 apios_suspend = 0;
167 #if defined(CONFIG_NO_GKI)
168 	u32 virtual_poweroff_en = 0;
169 #endif
170 	enum of_gpio_flags flags;
171 	int i = 0;
172 	int length;
173 
174 	match_id = of_match_node(pm_match_table, pdev->dev.of_node);
175 	if (!match_id)
176 		return -ENODEV;
177 
178 	node = of_find_node_by_name(NULL, "rockchip-suspend");
179 
180 	if (IS_ERR_OR_NULL(node)) {
181 		dev_err(&pdev->dev, "%s dev node err\n",  __func__);
182 		return -ENODEV;
183 	}
184 
185 	if (of_property_read_u32_array(node,
186 				       "rockchip,sleep-mode-config",
187 				       &mode_config, 1))
188 		dev_warn(&pdev->dev, "not set sleep mode config\n");
189 	else
190 		sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0);
191 
192 	if (of_property_read_u32_array(node,
193 				       "rockchip,wakeup-config",
194 				       &wakeup_config, 1))
195 		dev_warn(&pdev->dev, "not set wakeup-config\n");
196 	else
197 		sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0);
198 
199 	if (of_property_read_u32_array(node,
200 				       "rockchip,pwm-regulator-config",
201 				       &pwm_regulator_config, 1))
202 		dev_warn(&pdev->dev, "not set pwm-regulator-config\n");
203 	else
204 		sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG,
205 					 pwm_regulator_config,
206 					 0);
207 
208 	length = of_gpio_named_count(node, "rockchip,power-ctrl");
209 
210 	if (length > 0 && length < 10) {
211 		for (i = 0; i < length; i++) {
212 			gpio_temp[i] = of_get_named_gpio_flags(node,
213 							     "rockchip,power-ctrl",
214 							     i,
215 							     &flags);
216 			if (!gpio_is_valid(gpio_temp[i]))
217 				break;
218 			sip_smc_set_suspend_mode(GPIO_POWER_CONFIG,
219 						 i,
220 						 gpio_temp[i]);
221 		}
222 	}
223 	sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO);
224 
225 	if (!of_property_read_u32_array(node,
226 					"rockchip,sleep-debug-en",
227 					&sleep_debug_en, 1))
228 		sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE,
229 					 sleep_debug_en,
230 					 0);
231 
232 	if (!of_property_read_u32_array(node,
233 					"rockchip,apios-suspend",
234 					&apios_suspend, 1))
235 		sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG,
236 					 apios_suspend,
237 					 0);
238 
239 #if defined(CONFIG_NO_GKI)
240 	if (!of_property_read_u32_array(node,
241 					"rockchip,virtual-poweroff",
242 					&virtual_poweroff_en, 1) &&
243 	    virtual_poweroff_en)
244 		pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
245 
246 	for (i = RK_PM_MEM; i < RK_PM_STATE_MAX; i++)
247 		parse_on_off_regulator(node, i);
248 #endif
249 
250 	return 0;
251 }
252 
253 #if defined(CONFIG_NO_GKI)
pm_config_prepare(struct device * dev)254 static int pm_config_prepare(struct device *dev)
255 {
256 	int i;
257 	suspend_state_t suspend_state = mem_sleep_current;
258 	enum rk_pm_state state = suspend_state - PM_SUSPEND_MEM;
259 	struct regulator_dev **on_list;
260 	struct regulator_dev **off_list;
261 
262 	sip_smc_set_suspend_mode(LINUX_PM_STATE,
263 				 suspend_state,
264 				 0);
265 
266 	if (state >= RK_PM_STATE_MAX)
267 		return 0;
268 
269 	on_list = on_off_regs_list[state].on_reg_list;
270 	off_list = on_off_regs_list[state].off_reg_list;
271 
272 	for (i = 0; i < MAX_ON_OFF_REG_NUM && on_list[i]; i++)
273 		regulator_suspend_enable(on_list[i], PM_SUSPEND_MEM);
274 
275 	for (i = 0; i < MAX_ON_OFF_REG_NUM && off_list[i]; i++)
276 		regulator_suspend_disable(off_list[i], PM_SUSPEND_MEM);
277 
278 	return 0;
279 }
280 
281 static const struct dev_pm_ops rockchip_pm_ops = {
282 	.prepare = pm_config_prepare,
283 };
284 #endif
285 
286 static struct platform_driver pm_driver = {
287 	.probe = pm_config_probe,
288 	.driver = {
289 		.name = "rockchip-pm",
290 		.of_match_table = pm_match_table,
291 #if defined(CONFIG_NO_GKI)
292 		.pm = &rockchip_pm_ops,
293 #endif
294 	},
295 };
296 
rockchip_pm_drv_register(void)297 static int __init rockchip_pm_drv_register(void)
298 {
299 	return platform_driver_register(&pm_driver);
300 }
301 late_initcall_sync(rockchip_pm_drv_register);
302 MODULE_DESCRIPTION("Rockchip suspend mode config");
303 MODULE_LICENSE("GPL");
304