1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2023 Rockchip Electronics Co., Ltd 4 */ 5 6 #include <common.h> 7 #include <dm.h> 8 #include <misc.h> 9 #include <asm/io.h> 10 #include <linux/bitops.h> 11 #include <asm/arch/rockchip_smccc.h> 12 13 DECLARE_GLOBAL_DATA_PTR; 14 15 #define RK_ATAG_MCU_SLP_CORE 0x526b0001 16 #define RK_ATAG_MCU_SLP_MAX 0x526b00ff 17 #define RK_ATAG_NONE 0x00000000 18 19 /* rk_tag related defines */ 20 #define sleep_tag_next(t) \ 21 ((struct rk_sleep_tag *)((__u32 *)(t) + (t)->hdr.size)) 22 23 struct rk_tag_header { 24 u32 size; 25 u32 tag; 26 }; 27 28 struct rk_sleep_tag { 29 struct rk_tag_header hdr; 30 u32 params[]; 31 }; 32 33 struct rk_mcu_sleep_core_tag { 34 struct rk_tag_header hdr; 35 u32 total_size; 36 u32 reserve[13]; 37 }; 38 39 struct rk_mcu_sleep_tags { 40 struct rk_mcu_sleep_core_tag core; 41 struct rk_sleep_tag slp_tags; 42 }; 43 44 static int rockchip_pm_config_ioctl(struct udevice *dev, 45 unsigned long request, 46 void *buf) 47 { 48 int ret = -EINVAL; 49 50 switch (request) { 51 case IOCTL_REQ_START: 52 break; 53 default: 54 printf("Unsupported ioctl: %ld\n", (ulong)request); 55 break; 56 } 57 58 return ret; 59 } 60 61 static const struct misc_ops rockchip_pm_config_ops = { 62 .ioctl = rockchip_pm_config_ioctl, 63 }; 64 65 static int parse_mcu_sleep_config(ofnode node) 66 { 67 int ret, len; 68 ofnode mcu_sleep_node; 69 ofnode child; 70 struct arm_smccc_res res; 71 struct rk_mcu_sleep_tags *config; 72 struct rk_sleep_tag *slp_tag; 73 char *end; 74 75 mcu_sleep_node = ofnode_find_subnode(node, "rockchip-mcu-sleep-cfg"); 76 if (ofnode_valid(mcu_sleep_node) == 0) { 77 ret = -ENODEV; 78 goto out; 79 } 80 81 child = ofnode_first_subnode(mcu_sleep_node); 82 if (ofnode_valid(child) == 0) { 83 pr_err("%s: no valid child node in rockchip-mcu-sleep-cfg\n", 84 __func__); 85 ret = -ENODEV; 86 goto out; 87 } 88 89 /* 90 * 4kb for sleep parameters 91 */ 92 res = sip_smc_request_share_mem(1, SHARE_PAGE_TYPE_SLEEP); 93 if (res.a0 != 0) { 94 pr_err("%s: no trust memory for mcu_sleep\n", __func__); 95 ret = -ENOMEM; 96 goto out; 97 } 98 99 /* Initialize core tag */ 100 memset((void *)res.a1, 0, sizeof(struct rk_mcu_sleep_tags)); 101 config = (struct rk_mcu_sleep_tags *)res.a1; 102 config->core.hdr.tag = RK_ATAG_MCU_SLP_CORE; 103 config->core.hdr.size = sizeof(struct rk_mcu_sleep_core_tag) / sizeof(u32); 104 config->core.total_size = sizeof(struct rk_mcu_sleep_tags) - 105 sizeof(struct rk_sleep_tag); 106 107 slp_tag = &config->slp_tags; 108 109 /* End point of sleep data */ 110 end = (char *)config + PAGE_SIZE - sizeof(struct rk_sleep_tag); 111 112 ofnode_for_each_subnode(child, mcu_sleep_node) { 113 /* Is overflow? */ 114 if ((char *)slp_tag->params >= end) 115 break; 116 117 ret = ofnode_read_u32_array(child, "rockchip,tag", 118 &slp_tag->hdr.tag, 1); 119 if (ret || 120 slp_tag->hdr.tag <= RK_ATAG_MCU_SLP_CORE || 121 slp_tag->hdr.tag >= RK_ATAG_MCU_SLP_MAX) { 122 pr_err("%s: no or invalid rockchip,tag in %s\n", 123 __func__, ofnode_get_name(child)); 124 125 continue; 126 } 127 128 len = ofnode_read_size(child, "rockchip,params"); 129 if (len > 0) { 130 /* Is overflow? */ 131 if ((char *)(slp_tag->params + len) >= end) { 132 pr_warn("%s: no more space for rockchip,tag in %s\n", 133 __func__, ofnode_get_name(child)); 134 break; 135 } 136 137 ret = ofnode_read_u32_array(child, "rockchip,params", 138 slp_tag->params, 139 len / sizeof(u32)); 140 if (ret) { 141 pr_err("%s: read rockchip,params error in %s\n", 142 __func__, ofnode_get_name(child)); 143 break; 144 } 145 146 slp_tag->hdr.size = 147 (len + sizeof(struct rk_tag_header)) / sizeof(u32); 148 } else if (len == 0) { 149 slp_tag->hdr.size = 0; 150 } else { 151 continue; 152 } 153 154 config->core.total_size += slp_tag->hdr.size * sizeof(u32); 155 156 slp_tag = sleep_tag_next(slp_tag); 157 } 158 159 /* Add none tag. 160 * Compiler will combine the follow code as "str xzr, [x28]", but 161 * "slp->hdr" may not be 8-byte alignment. So we use memset_io instead: 162 * slp_tag->hdr.size = 0; 163 * slp_tag->hdr.tag = RK_ATAG_NONE; 164 */ 165 memset_io(&slp_tag->hdr, 0, sizeof(slp_tag->hdr)); 166 167 config->core.total_size += sizeof(struct rk_sleep_tag); 168 169 ret = 0; 170 171 out: 172 return ret; 173 } 174 175 static int rockchip_pm_config_probe(struct udevice *dev) 176 { 177 parse_mcu_sleep_config(dev_ofnode(dev)); 178 179 return 0; 180 } 181 182 static const struct udevice_id rockchip_pm_config_ids[] = { 183 { .compatible = "rockchip,pm-rk3588" }, 184 {} 185 }; 186 187 U_BOOT_DRIVER(rockchip_pm_config) = { 188 .name = "rockchip_pm_config", 189 .id = UCLASS_MISC, 190 .of_match = rockchip_pm_config_ids, 191 .probe = rockchip_pm_config_probe, 192 .ops = &rockchip_pm_config_ops, 193 }; 194