Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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