1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT) 2 /* 3 * Copyright (c) 2019 Amlogic, Inc. 4 * Author: Jianxin Pan <jianxin.pan@amlogic.com> 5 */ 6 7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 8 9 #include <linux/io.h> 10 #include <linux/of.h> 11 #include <linux/platform_device.h> 12 #include <linux/pm_domain.h> 13 #include <dt-bindings/power/meson-a1-power.h> 14 #include <dt-bindings/power/amlogic,c3-pwrc.h> 15 #include <dt-bindings/power/meson-s4-power.h> 16 #include <linux/arm-smccc.h> 17 #include <linux/firmware/meson/meson_sm.h> 18 #include <linux/module.h> 19 20 #define PWRC_ON 1 21 #define PWRC_OFF 0 22 23 struct meson_secure_pwrc_domain { 24 struct generic_pm_domain base; 25 unsigned int index; 26 struct meson_secure_pwrc *pwrc; 27 }; 28 29 struct meson_secure_pwrc { 30 struct meson_secure_pwrc_domain *domains; 31 struct genpd_onecell_data xlate; 32 struct meson_sm_firmware *fw; 33 }; 34 35 struct meson_secure_pwrc_domain_desc { 36 unsigned int index; 37 unsigned int flags; 38 char *name; 39 bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain); 40 }; 41 42 struct meson_secure_pwrc_domain_data { 43 unsigned int count; 44 struct meson_secure_pwrc_domain_desc *domains; 45 }; 46 47 static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) 48 { 49 int is_off = 1; 50 51 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off, 52 pwrc_domain->index, 0, 0, 0, 0) < 0) 53 pr_err("failed to get power domain status\n"); 54 55 return is_off; 56 } 57 58 static int meson_secure_pwrc_off(struct generic_pm_domain *domain) 59 { 60 int ret = 0; 61 struct meson_secure_pwrc_domain *pwrc_domain = 62 container_of(domain, struct meson_secure_pwrc_domain, base); 63 64 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, 65 pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) { 66 pr_err("failed to set power domain off\n"); 67 ret = -EINVAL; 68 } 69 70 return ret; 71 } 72 73 static int meson_secure_pwrc_on(struct generic_pm_domain *domain) 74 { 75 int ret = 0; 76 struct meson_secure_pwrc_domain *pwrc_domain = 77 container_of(domain, struct meson_secure_pwrc_domain, base); 78 79 if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, 80 pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) { 81 pr_err("failed to set power domain on\n"); 82 ret = -EINVAL; 83 } 84 85 return ret; 86 } 87 88 #define SEC_PD(__name, __flag) \ 89 [PWRC_##__name##_ID] = \ 90 { \ 91 .name = #__name, \ 92 .index = PWRC_##__name##_ID, \ 93 .is_off = pwrc_secure_is_off, \ 94 .flags = __flag, \ 95 } 96 97 static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { 98 SEC_PD(DSPA, 0), 99 SEC_PD(DSPB, 0), 100 /* UART should keep working in ATF after suspend and before resume */ 101 SEC_PD(UART, GENPD_FLAG_ALWAYS_ON), 102 /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ 103 SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON), 104 SEC_PD(I2C, 0), 105 SEC_PD(PSRAM, 0), 106 SEC_PD(ACODEC, 0), 107 SEC_PD(AUDIO, 0), 108 SEC_PD(OTP, 0), 109 SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), 110 SEC_PD(SD_EMMC, 0), 111 SEC_PD(RAMA, 0), 112 /* SRAMB is used as ATF runtime memory, and should be always on */ 113 SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON), 114 SEC_PD(IR, 0), 115 SEC_PD(SPICC, 0), 116 SEC_PD(SPIFC, 0), 117 SEC_PD(USB, 0), 118 /* NIC is for the Arm NIC-400 interconnect, and should be always on */ 119 SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON), 120 SEC_PD(PDMIN, 0), 121 SEC_PD(RSA, 0), 122 }; 123 124 static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { 125 SEC_PD(C3_NNA, 0), 126 SEC_PD(C3_AUDIO, GENPD_FLAG_ALWAYS_ON), 127 SEC_PD(C3_SDIOA, GENPD_FLAG_ALWAYS_ON), 128 SEC_PD(C3_EMMC, GENPD_FLAG_ALWAYS_ON), 129 SEC_PD(C3_USB_COMB, GENPD_FLAG_ALWAYS_ON), 130 SEC_PD(C3_SDCARD, GENPD_FLAG_ALWAYS_ON), 131 SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON), 132 SEC_PD(C3_GE2D, GENPD_FLAG_ALWAYS_ON), 133 SEC_PD(C3_CVE, GENPD_FLAG_ALWAYS_ON), 134 SEC_PD(C3_GDC_WRAP, GENPD_FLAG_ALWAYS_ON), 135 SEC_PD(C3_ISP_TOP, GENPD_FLAG_ALWAYS_ON), 136 SEC_PD(C3_MIPI_ISP_WRAP, GENPD_FLAG_ALWAYS_ON), 137 SEC_PD(C3_VCODEC, 0), 138 }; 139 140 static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { 141 SEC_PD(S4_DOS_HEVC, 0), 142 SEC_PD(S4_DOS_VDEC, 0), 143 SEC_PD(S4_VPU_HDMI, 0), 144 SEC_PD(S4_USB_COMB, 0), 145 SEC_PD(S4_GE2D, 0), 146 /* ETH is for ethernet online wakeup, and should be always on */ 147 SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON), 148 SEC_PD(S4_DEMOD, 0), 149 SEC_PD(S4_AUDIO, 0), 150 }; 151 152 static int meson_secure_pwrc_probe(struct platform_device *pdev) 153 { 154 int i; 155 struct device_node *sm_np; 156 struct meson_secure_pwrc *pwrc; 157 const struct meson_secure_pwrc_domain_data *match; 158 159 match = of_device_get_match_data(&pdev->dev); 160 if (!match) { 161 dev_err(&pdev->dev, "failed to get match data\n"); 162 return -ENODEV; 163 } 164 165 sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm"); 166 if (!sm_np) { 167 dev_err(&pdev->dev, "no secure-monitor node\n"); 168 return -ENODEV; 169 } 170 171 pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); 172 if (!pwrc) { 173 of_node_put(sm_np); 174 return -ENOMEM; 175 } 176 177 pwrc->fw = meson_sm_get(sm_np); 178 of_node_put(sm_np); 179 if (!pwrc->fw) 180 return -EPROBE_DEFER; 181 182 pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, 183 sizeof(*pwrc->xlate.domains), 184 GFP_KERNEL); 185 if (!pwrc->xlate.domains) 186 return -ENOMEM; 187 188 pwrc->domains = devm_kcalloc(&pdev->dev, match->count, 189 sizeof(*pwrc->domains), GFP_KERNEL); 190 if (!pwrc->domains) 191 return -ENOMEM; 192 193 pwrc->xlate.num_domains = match->count; 194 platform_set_drvdata(pdev, pwrc); 195 196 for (i = 0 ; i < match->count ; ++i) { 197 struct meson_secure_pwrc_domain *dom = &pwrc->domains[i]; 198 199 if (!match->domains[i].name) 200 continue; 201 202 dom->pwrc = pwrc; 203 dom->index = match->domains[i].index; 204 dom->base.name = match->domains[i].name; 205 dom->base.flags = match->domains[i].flags; 206 dom->base.power_on = meson_secure_pwrc_on; 207 dom->base.power_off = meson_secure_pwrc_off; 208 209 pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom)); 210 211 pwrc->xlate.domains[i] = &dom->base; 212 } 213 214 return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); 215 } 216 217 static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { 218 .domains = a1_pwrc_domains, 219 .count = ARRAY_SIZE(a1_pwrc_domains), 220 }; 221 222 static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { 223 .domains = c3_pwrc_domains, 224 .count = ARRAY_SIZE(c3_pwrc_domains), 225 }; 226 227 static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = { 228 .domains = s4_pwrc_domains, 229 .count = ARRAY_SIZE(s4_pwrc_domains), 230 }; 231 232 static const struct of_device_id meson_secure_pwrc_match_table[] = { 233 { 234 .compatible = "amlogic,meson-a1-pwrc", 235 .data = &meson_secure_a1_pwrc_data, 236 }, 237 { 238 .compatible = "amlogic,c3-pwrc", 239 .data = &amlogic_secure_c3_pwrc_data, 240 }, 241 { 242 .compatible = "amlogic,meson-s4-pwrc", 243 .data = &meson_secure_s4_pwrc_data, 244 }, 245 { /* sentinel */ } 246 }; 247 MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table); 248 249 static struct platform_driver meson_secure_pwrc_driver = { 250 .probe = meson_secure_pwrc_probe, 251 .driver = { 252 .name = "meson_secure_pwrc", 253 .of_match_table = meson_secure_pwrc_match_table, 254 }, 255 }; 256 module_platform_driver(meson_secure_pwrc_driver); 257 MODULE_LICENSE("Dual MIT/GPL"); 258