Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
0002 /*
0003  * Copyright (c) 2019 Amlogic, Inc.
0004  * Author: Jianxin Pan <jianxin.pan@amlogic.com>
0005  */
0006 
0007 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0008 
0009 #include <linux/io.h>
0010 #include <linux/of_device.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/pm_domain.h>
0013 #include <dt-bindings/power/meson-a1-power.h>
0014 #include <dt-bindings/power/meson-s4-power.h>
0015 #include <linux/arm-smccc.h>
0016 #include <linux/firmware/meson/meson_sm.h>
0017 #include <linux/module.h>
0018 
0019 #define PWRC_ON     1
0020 #define PWRC_OFF    0
0021 
0022 struct meson_secure_pwrc_domain {
0023     struct generic_pm_domain base;
0024     unsigned int index;
0025     struct meson_secure_pwrc *pwrc;
0026 };
0027 
0028 struct meson_secure_pwrc {
0029     struct meson_secure_pwrc_domain *domains;
0030     struct genpd_onecell_data xlate;
0031     struct meson_sm_firmware *fw;
0032 };
0033 
0034 struct meson_secure_pwrc_domain_desc {
0035     unsigned int index;
0036     unsigned int flags;
0037     char *name;
0038     bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain);
0039 };
0040 
0041 struct meson_secure_pwrc_domain_data {
0042     unsigned int count;
0043     struct meson_secure_pwrc_domain_desc *domains;
0044 };
0045 
0046 static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain)
0047 {
0048     int is_off = 1;
0049 
0050     if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off,
0051               pwrc_domain->index, 0, 0, 0, 0) < 0)
0052         pr_err("failed to get power domain status\n");
0053 
0054     return is_off;
0055 }
0056 
0057 static int meson_secure_pwrc_off(struct generic_pm_domain *domain)
0058 {
0059     int ret = 0;
0060     struct meson_secure_pwrc_domain *pwrc_domain =
0061         container_of(domain, struct meson_secure_pwrc_domain, base);
0062 
0063     if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
0064               pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) {
0065         pr_err("failed to set power domain off\n");
0066         ret = -EINVAL;
0067     }
0068 
0069     return ret;
0070 }
0071 
0072 static int meson_secure_pwrc_on(struct generic_pm_domain *domain)
0073 {
0074     int ret = 0;
0075     struct meson_secure_pwrc_domain *pwrc_domain =
0076         container_of(domain, struct meson_secure_pwrc_domain, base);
0077 
0078     if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
0079               pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) {
0080         pr_err("failed to set power domain on\n");
0081         ret = -EINVAL;
0082     }
0083 
0084     return ret;
0085 }
0086 
0087 #define SEC_PD(__name, __flag)          \
0088 [PWRC_##__name##_ID] =              \
0089 {                       \
0090     .name = #__name,            \
0091     .index = PWRC_##__name##_ID,        \
0092     .is_off = pwrc_secure_is_off,   \
0093     .flags = __flag,            \
0094 }
0095 
0096 static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
0097     SEC_PD(DSPA,    0),
0098     SEC_PD(DSPB,    0),
0099     /* UART should keep working in ATF after suspend and before resume */
0100     SEC_PD(UART,    GENPD_FLAG_ALWAYS_ON),
0101     /* DMC is for DDR PHY ana/dig and DMC, and should be always on */
0102     SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON),
0103     SEC_PD(I2C, 0),
0104     SEC_PD(PSRAM,   0),
0105     SEC_PD(ACODEC,  0),
0106     SEC_PD(AUDIO,   0),
0107     SEC_PD(OTP, 0),
0108     SEC_PD(DMA, 0),
0109     SEC_PD(SD_EMMC, 0),
0110     SEC_PD(RAMA,    0),
0111     /* SRAMB is used as ATF runtime memory, and should be always on */
0112     SEC_PD(RAMB,    GENPD_FLAG_ALWAYS_ON),
0113     SEC_PD(IR,  0),
0114     SEC_PD(SPICC,   0),
0115     SEC_PD(SPIFC,   0),
0116     SEC_PD(USB, 0),
0117     /* NIC is for the Arm NIC-400 interconnect, and should be always on */
0118     SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON),
0119     SEC_PD(PDMIN,   0),
0120     SEC_PD(RSA, 0),
0121 };
0122 
0123 static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = {
0124     SEC_PD(S4_DOS_HEVC, 0),
0125     SEC_PD(S4_DOS_VDEC, 0),
0126     SEC_PD(S4_VPU_HDMI, 0),
0127     SEC_PD(S4_USB_COMB, 0),
0128     SEC_PD(S4_GE2D,     0),
0129     /* ETH is for ethernet online wakeup, and should be always on */
0130     SEC_PD(S4_ETH,      GENPD_FLAG_ALWAYS_ON),
0131     SEC_PD(S4_DEMOD,    0),
0132     SEC_PD(S4_AUDIO,    0),
0133 };
0134 
0135 static int meson_secure_pwrc_probe(struct platform_device *pdev)
0136 {
0137     int i;
0138     struct device_node *sm_np;
0139     struct meson_secure_pwrc *pwrc;
0140     const struct meson_secure_pwrc_domain_data *match;
0141 
0142     match = of_device_get_match_data(&pdev->dev);
0143     if (!match) {
0144         dev_err(&pdev->dev, "failed to get match data\n");
0145         return -ENODEV;
0146     }
0147 
0148     sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm");
0149     if (!sm_np) {
0150         dev_err(&pdev->dev, "no secure-monitor node\n");
0151         return -ENODEV;
0152     }
0153 
0154     pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
0155     if (!pwrc) {
0156         of_node_put(sm_np);
0157         return -ENOMEM;
0158     }
0159 
0160     pwrc->fw = meson_sm_get(sm_np);
0161     of_node_put(sm_np);
0162     if (!pwrc->fw)
0163         return -EPROBE_DEFER;
0164 
0165     pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
0166                        sizeof(*pwrc->xlate.domains),
0167                        GFP_KERNEL);
0168     if (!pwrc->xlate.domains)
0169         return -ENOMEM;
0170 
0171     pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
0172                      sizeof(*pwrc->domains), GFP_KERNEL);
0173     if (!pwrc->domains)
0174         return -ENOMEM;
0175 
0176     pwrc->xlate.num_domains = match->count;
0177     platform_set_drvdata(pdev, pwrc);
0178 
0179     for (i = 0 ; i < match->count ; ++i) {
0180         struct meson_secure_pwrc_domain *dom = &pwrc->domains[i];
0181 
0182         if (!match->domains[i].index)
0183             continue;
0184 
0185         dom->pwrc = pwrc;
0186         dom->index = match->domains[i].index;
0187         dom->base.name = match->domains[i].name;
0188         dom->base.flags = match->domains[i].flags;
0189         dom->base.power_on = meson_secure_pwrc_on;
0190         dom->base.power_off = meson_secure_pwrc_off;
0191 
0192         pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom));
0193 
0194         pwrc->xlate.domains[i] = &dom->base;
0195     }
0196 
0197     return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
0198 }
0199 
0200 static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
0201     .domains = a1_pwrc_domains,
0202     .count = ARRAY_SIZE(a1_pwrc_domains),
0203 };
0204 
0205 static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {
0206     .domains = s4_pwrc_domains,
0207     .count = ARRAY_SIZE(s4_pwrc_domains),
0208 };
0209 
0210 static const struct of_device_id meson_secure_pwrc_match_table[] = {
0211     {
0212         .compatible = "amlogic,meson-a1-pwrc",
0213         .data = &meson_secure_a1_pwrc_data,
0214     },
0215     {
0216         .compatible = "amlogic,meson-s4-pwrc",
0217         .data = &meson_secure_s4_pwrc_data,
0218     },
0219     { /* sentinel */ }
0220 };
0221 MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table);
0222 
0223 static struct platform_driver meson_secure_pwrc_driver = {
0224     .probe = meson_secure_pwrc_probe,
0225     .driver = {
0226         .name       = "meson_secure_pwrc",
0227         .of_match_table = meson_secure_pwrc_match_table,
0228     },
0229 };
0230 module_platform_driver(meson_secure_pwrc_driver);
0231 MODULE_LICENSE("Dual MIT/GPL");