18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Amlogic, Inc. 48c2ecf20Sopenharmony_ci * Author: Jianxin Pan <jianxin.pan@amlogic.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/of_device.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 138c2ecf20Sopenharmony_ci#include <dt-bindings/power/meson-a1-power.h> 148c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware/meson/meson_sm.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define PWRC_ON 1 188c2ecf20Sopenharmony_ci#define PWRC_OFF 0 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct meson_secure_pwrc_domain { 218c2ecf20Sopenharmony_ci struct generic_pm_domain base; 228c2ecf20Sopenharmony_ci unsigned int index; 238c2ecf20Sopenharmony_ci struct meson_secure_pwrc *pwrc; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct meson_secure_pwrc { 278c2ecf20Sopenharmony_ci struct meson_secure_pwrc_domain *domains; 288c2ecf20Sopenharmony_ci struct genpd_onecell_data xlate; 298c2ecf20Sopenharmony_ci struct meson_sm_firmware *fw; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct meson_secure_pwrc_domain_desc { 338c2ecf20Sopenharmony_ci unsigned int index; 348c2ecf20Sopenharmony_ci unsigned int flags; 358c2ecf20Sopenharmony_ci char *name; 368c2ecf20Sopenharmony_ci bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain); 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct meson_secure_pwrc_domain_data { 408c2ecf20Sopenharmony_ci unsigned int count; 418c2ecf20Sopenharmony_ci struct meson_secure_pwrc_domain_desc *domains; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci int is_off = 1; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off, 498c2ecf20Sopenharmony_ci pwrc_domain->index, 0, 0, 0, 0) < 0) 508c2ecf20Sopenharmony_ci pr_err("failed to get power domain status\n"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return is_off; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int meson_secure_pwrc_off(struct generic_pm_domain *domain) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int ret = 0; 588c2ecf20Sopenharmony_ci struct meson_secure_pwrc_domain *pwrc_domain = 598c2ecf20Sopenharmony_ci container_of(domain, struct meson_secure_pwrc_domain, base); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, 628c2ecf20Sopenharmony_ci pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) { 638c2ecf20Sopenharmony_ci pr_err("failed to set power domain off\n"); 648c2ecf20Sopenharmony_ci ret = -EINVAL; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int meson_secure_pwrc_on(struct generic_pm_domain *domain) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int ret = 0; 738c2ecf20Sopenharmony_ci struct meson_secure_pwrc_domain *pwrc_domain = 748c2ecf20Sopenharmony_ci container_of(domain, struct meson_secure_pwrc_domain, base); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, 778c2ecf20Sopenharmony_ci pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) { 788c2ecf20Sopenharmony_ci pr_err("failed to set power domain on\n"); 798c2ecf20Sopenharmony_ci ret = -EINVAL; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define SEC_PD(__name, __flag) \ 868c2ecf20Sopenharmony_ci[PWRC_##__name##_ID] = \ 878c2ecf20Sopenharmony_ci{ \ 888c2ecf20Sopenharmony_ci .name = #__name, \ 898c2ecf20Sopenharmony_ci .index = PWRC_##__name##_ID, \ 908c2ecf20Sopenharmony_ci .is_off = pwrc_secure_is_off, \ 918c2ecf20Sopenharmony_ci .flags = __flag, \ 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { 958c2ecf20Sopenharmony_ci SEC_PD(DSPA, 0), 968c2ecf20Sopenharmony_ci SEC_PD(DSPB, 0), 978c2ecf20Sopenharmony_ci /* UART should keep working in ATF after suspend and before resume */ 988c2ecf20Sopenharmony_ci SEC_PD(UART, GENPD_FLAG_ALWAYS_ON), 998c2ecf20Sopenharmony_ci /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ 1008c2ecf20Sopenharmony_ci SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON), 1018c2ecf20Sopenharmony_ci SEC_PD(I2C, 0), 1028c2ecf20Sopenharmony_ci SEC_PD(PSRAM, 0), 1038c2ecf20Sopenharmony_ci SEC_PD(ACODEC, 0), 1048c2ecf20Sopenharmony_ci SEC_PD(AUDIO, 0), 1058c2ecf20Sopenharmony_ci SEC_PD(OTP, 0), 1068c2ecf20Sopenharmony_ci SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), 1078c2ecf20Sopenharmony_ci SEC_PD(SD_EMMC, 0), 1088c2ecf20Sopenharmony_ci SEC_PD(RAMA, 0), 1098c2ecf20Sopenharmony_ci /* SRAMB is used as ATF runtime memory, and should be always on */ 1108c2ecf20Sopenharmony_ci SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON), 1118c2ecf20Sopenharmony_ci SEC_PD(IR, 0), 1128c2ecf20Sopenharmony_ci SEC_PD(SPICC, 0), 1138c2ecf20Sopenharmony_ci SEC_PD(SPIFC, 0), 1148c2ecf20Sopenharmony_ci SEC_PD(USB, 0), 1158c2ecf20Sopenharmony_ci /* NIC is for the Arm NIC-400 interconnect, and should be always on */ 1168c2ecf20Sopenharmony_ci SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON), 1178c2ecf20Sopenharmony_ci SEC_PD(PDMIN, 0), 1188c2ecf20Sopenharmony_ci SEC_PD(RSA, 0), 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int meson_secure_pwrc_probe(struct platform_device *pdev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci struct device_node *sm_np; 1258c2ecf20Sopenharmony_ci struct meson_secure_pwrc *pwrc; 1268c2ecf20Sopenharmony_ci const struct meson_secure_pwrc_domain_data *match; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci match = of_device_get_match_data(&pdev->dev); 1298c2ecf20Sopenharmony_ci if (!match) { 1308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get match data\n"); 1318c2ecf20Sopenharmony_ci return -ENODEV; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm"); 1358c2ecf20Sopenharmony_ci if (!sm_np) { 1368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no secure-monitor node\n"); 1378c2ecf20Sopenharmony_ci return -ENODEV; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (!pwrc) { 1428c2ecf20Sopenharmony_ci of_node_put(sm_np); 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pwrc->fw = meson_sm_get(sm_np); 1478c2ecf20Sopenharmony_ci of_node_put(sm_np); 1488c2ecf20Sopenharmony_ci if (!pwrc->fw) 1498c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, 1528c2ecf20Sopenharmony_ci sizeof(*pwrc->xlate.domains), 1538c2ecf20Sopenharmony_ci GFP_KERNEL); 1548c2ecf20Sopenharmony_ci if (!pwrc->xlate.domains) 1558c2ecf20Sopenharmony_ci return -ENOMEM; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pwrc->domains = devm_kcalloc(&pdev->dev, match->count, 1588c2ecf20Sopenharmony_ci sizeof(*pwrc->domains), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!pwrc->domains) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci pwrc->xlate.num_domains = match->count; 1638c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pwrc); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0 ; i < match->count ; ++i) { 1668c2ecf20Sopenharmony_ci struct meson_secure_pwrc_domain *dom = &pwrc->domains[i]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!match->domains[i].index) 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dom->pwrc = pwrc; 1728c2ecf20Sopenharmony_ci dom->index = match->domains[i].index; 1738c2ecf20Sopenharmony_ci dom->base.name = match->domains[i].name; 1748c2ecf20Sopenharmony_ci dom->base.flags = match->domains[i].flags; 1758c2ecf20Sopenharmony_ci dom->base.power_on = meson_secure_pwrc_on; 1768c2ecf20Sopenharmony_ci dom->base.power_off = meson_secure_pwrc_off; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom)); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pwrc->xlate.domains[i] = &dom->base; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { 1878c2ecf20Sopenharmony_ci .domains = a1_pwrc_domains, 1888c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(a1_pwrc_domains), 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct of_device_id meson_secure_pwrc_match_table[] = { 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci .compatible = "amlogic,meson-a1-pwrc", 1948c2ecf20Sopenharmony_ci .data = &meson_secure_a1_pwrc_data, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci { /* sentinel */ } 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic struct platform_driver meson_secure_pwrc_driver = { 2008c2ecf20Sopenharmony_ci .probe = meson_secure_pwrc_probe, 2018c2ecf20Sopenharmony_ci .driver = { 2028c2ecf20Sopenharmony_ci .name = "meson_secure_pwrc", 2038c2ecf20Sopenharmony_ci .of_match_table = meson_secure_pwrc_match_table, 2048c2ecf20Sopenharmony_ci }, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_cibuiltin_platform_driver(meson_secure_pwrc_driver); 207