.. | .. |
---|
27 | 27 | #define MAX_ON_OFF_REG_PROP_NAME_LEN 60 |
---|
28 | 28 | #define MAX_CONFIG_PROP_NAME_LEN 60 |
---|
29 | 29 | |
---|
| 30 | +#define RK_ATAG_MCU_SLP_CORE 0x526b0001 |
---|
| 31 | +#define RK_ATAG_MCU_SLP_MAX 0x526b00ff |
---|
| 32 | +#define RK_ATAG_NONE 0x00000000 |
---|
| 33 | + |
---|
30 | 34 | enum rk_pm_state { |
---|
31 | 35 | RK_PM_MEM = 0, |
---|
32 | 36 | RK_PM_MEM_LITE, |
---|
.. | .. |
---|
52 | 56 | u32 wakeup_config; |
---|
53 | 57 | } sleep_config[RK_PM_STATE_MAX]; |
---|
54 | 58 | |
---|
| 59 | +/* rk_tag related defines */ |
---|
| 60 | +#define sleep_tag_next(t) \ |
---|
| 61 | + ((struct rk_sleep_tag *)((__u32 *)(t) + (t)->hdr.size)) |
---|
| 62 | + |
---|
| 63 | +struct rk_tag_header { |
---|
| 64 | + u32 size; |
---|
| 65 | + u32 tag; |
---|
| 66 | +}; |
---|
| 67 | + |
---|
| 68 | +struct rk_sleep_tag { |
---|
| 69 | + struct rk_tag_header hdr; |
---|
| 70 | + u32 params[]; |
---|
| 71 | +}; |
---|
| 72 | + |
---|
| 73 | +struct rk_mcu_sleep_core_tag { |
---|
| 74 | + struct rk_tag_header hdr; |
---|
| 75 | + u32 total_size; |
---|
| 76 | + u32 reserve[13]; |
---|
| 77 | +}; |
---|
| 78 | + |
---|
| 79 | +struct rk_mcu_sleep_tags { |
---|
| 80 | + struct rk_mcu_sleep_core_tag core; |
---|
| 81 | + struct rk_sleep_tag slp_tags; |
---|
| 82 | +}; |
---|
| 83 | + |
---|
55 | 84 | static const struct of_device_id pm_match_table[] = { |
---|
56 | 85 | { .compatible = "rockchip,pm-px30",}, |
---|
57 | 86 | { .compatible = "rockchip,pm-rk1808",}, |
---|
.. | .. |
---|
70 | 99 | }; |
---|
71 | 100 | |
---|
72 | 101 | #ifndef MODULE |
---|
| 102 | +enum { |
---|
| 103 | + RK_PM_VIRT_PWROFF_EN = 0, |
---|
| 104 | + RK_PM_VIRT_PWROFF_IRQ_CFG = 1, |
---|
| 105 | + RK_PM_VIRT_PWROFF_MAX, |
---|
| 106 | +}; |
---|
| 107 | + |
---|
| 108 | +static u32 *virtual_pwroff_irqs; |
---|
| 109 | + |
---|
73 | 110 | static void rockchip_pm_virt_pwroff_prepare(void) |
---|
74 | 111 | { |
---|
75 | | - int error; |
---|
| 112 | + int error, i; |
---|
| 113 | + |
---|
| 114 | + pm_wakeup_clear(0); |
---|
76 | 115 | |
---|
77 | 116 | regulator_suspend_prepare(PM_SUSPEND_MEM); |
---|
78 | 117 | |
---|
.. | .. |
---|
82 | 121 | return; |
---|
83 | 122 | } |
---|
84 | 123 | |
---|
85 | | - sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1); |
---|
| 124 | + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, RK_PM_VIRT_PWROFF_EN, 1); |
---|
| 125 | + |
---|
| 126 | + if (virtual_pwroff_irqs) { |
---|
| 127 | + for (i = 0; virtual_pwroff_irqs[i]; i++) { |
---|
| 128 | + error = sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, |
---|
| 129 | + RK_PM_VIRT_PWROFF_IRQ_CFG, |
---|
| 130 | + virtual_pwroff_irqs[i]); |
---|
| 131 | + if (error) { |
---|
| 132 | + pr_err("%s: config virtual_pwroff_irqs[%d] error, overflow or update trust!\n", |
---|
| 133 | + __func__, i); |
---|
| 134 | + break; |
---|
| 135 | + } |
---|
| 136 | + } |
---|
| 137 | + } |
---|
| 138 | + |
---|
86 | 139 | sip_smc_virtual_poweroff(); |
---|
| 140 | +} |
---|
| 141 | + |
---|
| 142 | +static int parse_virtual_pwroff_config(struct device_node *node) |
---|
| 143 | +{ |
---|
| 144 | + int ret = 0, cnt; |
---|
| 145 | + u32 virtual_poweroff_en = 0; |
---|
| 146 | + |
---|
| 147 | + if (!of_property_read_u32_array(node, |
---|
| 148 | + "rockchip,virtual-poweroff", |
---|
| 149 | + &virtual_poweroff_en, 1) && |
---|
| 150 | + virtual_poweroff_en) |
---|
| 151 | + pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare; |
---|
| 152 | + |
---|
| 153 | + if (!virtual_poweroff_en) |
---|
| 154 | + return 0; |
---|
| 155 | + |
---|
| 156 | + cnt = of_property_count_u32_elems(node, "rockchip,virtual-poweroff-irqs"); |
---|
| 157 | + if (cnt > 0) { |
---|
| 158 | + /* 0 as the last element of virtual_pwroff_irqs */ |
---|
| 159 | + virtual_pwroff_irqs = kzalloc((cnt + 1) * sizeof(u32), GFP_KERNEL); |
---|
| 160 | + if (!virtual_pwroff_irqs) { |
---|
| 161 | + ret = -ENOMEM; |
---|
| 162 | + goto out; |
---|
| 163 | + } |
---|
| 164 | + |
---|
| 165 | + ret = of_property_read_u32_array(node, "rockchip,virtual-poweroff-irqs", |
---|
| 166 | + virtual_pwroff_irqs, cnt); |
---|
| 167 | + if (ret) { |
---|
| 168 | + pr_err("%s: get rockchip,virtual-poweroff-irqs error\n", |
---|
| 169 | + __func__); |
---|
| 170 | + goto out; |
---|
| 171 | + } |
---|
| 172 | + } |
---|
| 173 | + |
---|
| 174 | +out: |
---|
| 175 | + return ret; |
---|
87 | 176 | } |
---|
88 | 177 | |
---|
89 | 178 | static int parse_sleep_config(struct device_node *node, enum rk_pm_state state) |
---|
.. | .. |
---|
166 | 255 | } |
---|
167 | 256 | #endif |
---|
168 | 257 | |
---|
| 258 | +static int parse_mcu_sleep_config(struct device_node *node) |
---|
| 259 | +{ |
---|
| 260 | + int ret, cnt; |
---|
| 261 | + struct arm_smccc_res res; |
---|
| 262 | + struct device_node *mcu_sleep_node; |
---|
| 263 | + struct device_node *child; |
---|
| 264 | + struct rk_mcu_sleep_tags *config; |
---|
| 265 | + struct rk_sleep_tag *slp_tag; |
---|
| 266 | + char *end; |
---|
| 267 | + |
---|
| 268 | + mcu_sleep_node = of_find_node_by_name(node, "rockchip-mcu-sleep-cfg"); |
---|
| 269 | + if (IS_ERR_OR_NULL(mcu_sleep_node)) { |
---|
| 270 | + ret = -ENODEV; |
---|
| 271 | + goto out; |
---|
| 272 | + } |
---|
| 273 | + |
---|
| 274 | + cnt = of_get_child_count(mcu_sleep_node); |
---|
| 275 | + if (!cnt) { |
---|
| 276 | + ret = -EINVAL; |
---|
| 277 | + goto free_mcu_mode; |
---|
| 278 | + } |
---|
| 279 | + |
---|
| 280 | + /* |
---|
| 281 | + * 4kb for sleep parameters |
---|
| 282 | + */ |
---|
| 283 | + res = sip_smc_request_share_mem(1, SHARE_PAGE_TYPE_SLEEP); |
---|
| 284 | + if (res.a0 != 0) { |
---|
| 285 | + pr_err("%s: no trust memory for mcu_sleep\n", __func__); |
---|
| 286 | + ret = -ENOMEM; |
---|
| 287 | + goto free_mcu_mode; |
---|
| 288 | + } |
---|
| 289 | + |
---|
| 290 | + /* Initialize core tag */ |
---|
| 291 | + memset((void *)res.a1, 0, sizeof(struct rk_mcu_sleep_tags)); |
---|
| 292 | + config = (struct rk_mcu_sleep_tags *)res.a1; |
---|
| 293 | + config->core.hdr.tag = RK_ATAG_MCU_SLP_CORE; |
---|
| 294 | + config->core.hdr.size = sizeof(struct rk_mcu_sleep_core_tag) / sizeof(u32); |
---|
| 295 | + config->core.total_size = sizeof(struct rk_mcu_sleep_tags) - |
---|
| 296 | + sizeof(struct rk_sleep_tag); |
---|
| 297 | + |
---|
| 298 | + slp_tag = &config->slp_tags; |
---|
| 299 | + |
---|
| 300 | + /* End point of sleep data */ |
---|
| 301 | + end = (char *)config + PAGE_SIZE - sizeof(struct rk_sleep_tag); |
---|
| 302 | + |
---|
| 303 | + for_each_available_child_of_node(mcu_sleep_node, child) { |
---|
| 304 | + /* Is overflow? */ |
---|
| 305 | + if ((char *)slp_tag->params >= end) |
---|
| 306 | + break; |
---|
| 307 | + |
---|
| 308 | + ret = of_property_read_u32_array(child, "rockchip,tag", |
---|
| 309 | + &slp_tag->hdr.tag, 1); |
---|
| 310 | + if (ret || |
---|
| 311 | + slp_tag->hdr.tag <= RK_ATAG_MCU_SLP_CORE || |
---|
| 312 | + slp_tag->hdr.tag >= RK_ATAG_MCU_SLP_MAX) { |
---|
| 313 | + pr_info("%s: no or invalid rockchip,tag in %s\n", |
---|
| 314 | + __func__, child->name); |
---|
| 315 | + |
---|
| 316 | + continue; |
---|
| 317 | + } |
---|
| 318 | + |
---|
| 319 | + cnt = of_property_count_u32_elems(child, "rockchip,params"); |
---|
| 320 | + if (cnt > 0) { |
---|
| 321 | + /* Is overflow? */ |
---|
| 322 | + if ((char *)(slp_tag->params + cnt) >= end) { |
---|
| 323 | + pr_warn("%s: no more space for rockchip,tag in %s\n", |
---|
| 324 | + __func__, child->name); |
---|
| 325 | + break; |
---|
| 326 | + } |
---|
| 327 | + |
---|
| 328 | + ret = of_property_read_u32_array(child, "rockchip,params", |
---|
| 329 | + slp_tag->params, cnt); |
---|
| 330 | + if (ret) { |
---|
| 331 | + pr_err("%s: rockchip,params error in %s\n", |
---|
| 332 | + __func__, child->name); |
---|
| 333 | + break; |
---|
| 334 | + } |
---|
| 335 | + |
---|
| 336 | + slp_tag->hdr.size = |
---|
| 337 | + cnt + sizeof(struct rk_tag_header) / sizeof(u32); |
---|
| 338 | + } else if (cnt == 0) { |
---|
| 339 | + slp_tag->hdr.size = 0; |
---|
| 340 | + } else { |
---|
| 341 | + continue; |
---|
| 342 | + } |
---|
| 343 | + |
---|
| 344 | + config->core.total_size += slp_tag->hdr.size * sizeof(u32); |
---|
| 345 | + |
---|
| 346 | + slp_tag = sleep_tag_next(slp_tag); |
---|
| 347 | + } |
---|
| 348 | + |
---|
| 349 | + /* Add none tag. |
---|
| 350 | + * Compiler will combine the follow code as "str xzr, [x28]", but |
---|
| 351 | + * "slp->hdr" may not be 8-byte alignment. So we use memset_io instead: |
---|
| 352 | + * slp_tag->hdr.size = 0; |
---|
| 353 | + * slp_tag->hdr.tag = RK_ATAG_NONE; |
---|
| 354 | + */ |
---|
| 355 | + memset_io(&slp_tag->hdr, 0, sizeof(slp_tag->hdr)); |
---|
| 356 | + |
---|
| 357 | + config->core.total_size += sizeof(struct rk_sleep_tag); |
---|
| 358 | + |
---|
| 359 | + ret = 0; |
---|
| 360 | + |
---|
| 361 | +free_mcu_mode: |
---|
| 362 | + of_node_put(mcu_sleep_node); |
---|
| 363 | +out: |
---|
| 364 | + return ret; |
---|
| 365 | +} |
---|
| 366 | + |
---|
169 | 367 | static int pm_config_probe(struct platform_device *pdev) |
---|
170 | 368 | { |
---|
171 | 369 | const struct of_device_id *match_id; |
---|
.. | .. |
---|
177 | 375 | u32 apios_suspend = 0; |
---|
178 | 376 | u32 io_ret_config = 0; |
---|
179 | 377 | u32 sleep_pin_config[2] = {0}; |
---|
180 | | -#ifndef MODULE |
---|
181 | | - u32 virtual_poweroff_en = 0; |
---|
182 | | -#endif |
---|
| 378 | + |
---|
183 | 379 | enum of_gpio_flags flags; |
---|
184 | 380 | int i = 0; |
---|
185 | 381 | int length; |
---|
.. | .. |
---|
270 | 466 | ret); |
---|
271 | 467 | } |
---|
272 | 468 | |
---|
| 469 | + parse_mcu_sleep_config(node); |
---|
| 470 | + |
---|
273 | 471 | #ifndef MODULE |
---|
274 | | - if (!of_property_read_u32_array(node, |
---|
275 | | - "rockchip,virtual-poweroff", |
---|
276 | | - &virtual_poweroff_en, 1) && |
---|
277 | | - virtual_poweroff_en) |
---|
278 | | - pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare; |
---|
| 472 | + parse_virtual_pwroff_config(node); |
---|
279 | 473 | |
---|
280 | 474 | for (i = RK_PM_MEM; i < RK_PM_STATE_MAX; i++) { |
---|
281 | 475 | parse_sleep_config(node, i); |
---|