cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");