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