mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it as merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Signed-off-by: Rob Herring <robh@kernel.org> Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org> Link: https://lore.kernel.org/r/20230714175124.4066972-1-robh@kernel.org Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
258 lines
6.7 KiB
C
258 lines
6.7 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc.
|
|
* Author: Jianxin Pan <jianxin.pan@amlogic.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_domain.h>
|
|
#include <dt-bindings/power/meson-a1-power.h>
|
|
#include <dt-bindings/power/amlogic,c3-pwrc.h>
|
|
#include <dt-bindings/power/meson-s4-power.h>
|
|
#include <linux/arm-smccc.h>
|
|
#include <linux/firmware/meson/meson_sm.h>
|
|
#include <linux/module.h>
|
|
|
|
#define PWRC_ON 1
|
|
#define PWRC_OFF 0
|
|
|
|
struct meson_secure_pwrc_domain {
|
|
struct generic_pm_domain base;
|
|
unsigned int index;
|
|
struct meson_secure_pwrc *pwrc;
|
|
};
|
|
|
|
struct meson_secure_pwrc {
|
|
struct meson_secure_pwrc_domain *domains;
|
|
struct genpd_onecell_data xlate;
|
|
struct meson_sm_firmware *fw;
|
|
};
|
|
|
|
struct meson_secure_pwrc_domain_desc {
|
|
unsigned int index;
|
|
unsigned int flags;
|
|
char *name;
|
|
bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain);
|
|
};
|
|
|
|
struct meson_secure_pwrc_domain_data {
|
|
unsigned int count;
|
|
struct meson_secure_pwrc_domain_desc *domains;
|
|
};
|
|
|
|
static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain)
|
|
{
|
|
int is_off = 1;
|
|
|
|
if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off,
|
|
pwrc_domain->index, 0, 0, 0, 0) < 0)
|
|
pr_err("failed to get power domain status\n");
|
|
|
|
return is_off;
|
|
}
|
|
|
|
static int meson_secure_pwrc_off(struct generic_pm_domain *domain)
|
|
{
|
|
int ret = 0;
|
|
struct meson_secure_pwrc_domain *pwrc_domain =
|
|
container_of(domain, struct meson_secure_pwrc_domain, base);
|
|
|
|
if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
|
|
pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) {
|
|
pr_err("failed to set power domain off\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int meson_secure_pwrc_on(struct generic_pm_domain *domain)
|
|
{
|
|
int ret = 0;
|
|
struct meson_secure_pwrc_domain *pwrc_domain =
|
|
container_of(domain, struct meson_secure_pwrc_domain, base);
|
|
|
|
if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL,
|
|
pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) {
|
|
pr_err("failed to set power domain on\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define SEC_PD(__name, __flag) \
|
|
[PWRC_##__name##_ID] = \
|
|
{ \
|
|
.name = #__name, \
|
|
.index = PWRC_##__name##_ID, \
|
|
.is_off = pwrc_secure_is_off, \
|
|
.flags = __flag, \
|
|
}
|
|
|
|
static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
|
|
SEC_PD(DSPA, 0),
|
|
SEC_PD(DSPB, 0),
|
|
/* UART should keep working in ATF after suspend and before resume */
|
|
SEC_PD(UART, GENPD_FLAG_ALWAYS_ON),
|
|
/* DMC is for DDR PHY ana/dig and DMC, and should be always on */
|
|
SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(I2C, 0),
|
|
SEC_PD(PSRAM, 0),
|
|
SEC_PD(ACODEC, 0),
|
|
SEC_PD(AUDIO, 0),
|
|
SEC_PD(OTP, 0),
|
|
SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE),
|
|
SEC_PD(SD_EMMC, 0),
|
|
SEC_PD(RAMA, 0),
|
|
/* SRAMB is used as ATF runtime memory, and should be always on */
|
|
SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(IR, 0),
|
|
SEC_PD(SPICC, 0),
|
|
SEC_PD(SPIFC, 0),
|
|
SEC_PD(USB, 0),
|
|
/* NIC is for the Arm NIC-400 interconnect, and should be always on */
|
|
SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(PDMIN, 0),
|
|
SEC_PD(RSA, 0),
|
|
};
|
|
|
|
static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = {
|
|
SEC_PD(C3_NNA, 0),
|
|
SEC_PD(C3_AUDIO, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_SDIOA, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_EMMC, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_USB_COMB, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_SDCARD, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_GE2D, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_CVE, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_GDC_WRAP, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_ISP_TOP, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_MIPI_ISP_WRAP, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(C3_VCODEC, 0),
|
|
};
|
|
|
|
static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = {
|
|
SEC_PD(S4_DOS_HEVC, 0),
|
|
SEC_PD(S4_DOS_VDEC, 0),
|
|
SEC_PD(S4_VPU_HDMI, 0),
|
|
SEC_PD(S4_USB_COMB, 0),
|
|
SEC_PD(S4_GE2D, 0),
|
|
/* ETH is for ethernet online wakeup, and should be always on */
|
|
SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON),
|
|
SEC_PD(S4_DEMOD, 0),
|
|
SEC_PD(S4_AUDIO, 0),
|
|
};
|
|
|
|
static int meson_secure_pwrc_probe(struct platform_device *pdev)
|
|
{
|
|
int i;
|
|
struct device_node *sm_np;
|
|
struct meson_secure_pwrc *pwrc;
|
|
const struct meson_secure_pwrc_domain_data *match;
|
|
|
|
match = of_device_get_match_data(&pdev->dev);
|
|
if (!match) {
|
|
dev_err(&pdev->dev, "failed to get match data\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm");
|
|
if (!sm_np) {
|
|
dev_err(&pdev->dev, "no secure-monitor node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
|
|
if (!pwrc) {
|
|
of_node_put(sm_np);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pwrc->fw = meson_sm_get(sm_np);
|
|
of_node_put(sm_np);
|
|
if (!pwrc->fw)
|
|
return -EPROBE_DEFER;
|
|
|
|
pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
|
|
sizeof(*pwrc->xlate.domains),
|
|
GFP_KERNEL);
|
|
if (!pwrc->xlate.domains)
|
|
return -ENOMEM;
|
|
|
|
pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
|
|
sizeof(*pwrc->domains), GFP_KERNEL);
|
|
if (!pwrc->domains)
|
|
return -ENOMEM;
|
|
|
|
pwrc->xlate.num_domains = match->count;
|
|
platform_set_drvdata(pdev, pwrc);
|
|
|
|
for (i = 0 ; i < match->count ; ++i) {
|
|
struct meson_secure_pwrc_domain *dom = &pwrc->domains[i];
|
|
|
|
if (!match->domains[i].name)
|
|
continue;
|
|
|
|
dom->pwrc = pwrc;
|
|
dom->index = match->domains[i].index;
|
|
dom->base.name = match->domains[i].name;
|
|
dom->base.flags = match->domains[i].flags;
|
|
dom->base.power_on = meson_secure_pwrc_on;
|
|
dom->base.power_off = meson_secure_pwrc_off;
|
|
|
|
pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom));
|
|
|
|
pwrc->xlate.domains[i] = &dom->base;
|
|
}
|
|
|
|
return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
|
|
}
|
|
|
|
static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
|
|
.domains = a1_pwrc_domains,
|
|
.count = ARRAY_SIZE(a1_pwrc_domains),
|
|
};
|
|
|
|
static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = {
|
|
.domains = c3_pwrc_domains,
|
|
.count = ARRAY_SIZE(c3_pwrc_domains),
|
|
};
|
|
|
|
static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = {
|
|
.domains = s4_pwrc_domains,
|
|
.count = ARRAY_SIZE(s4_pwrc_domains),
|
|
};
|
|
|
|
static const struct of_device_id meson_secure_pwrc_match_table[] = {
|
|
{
|
|
.compatible = "amlogic,meson-a1-pwrc",
|
|
.data = &meson_secure_a1_pwrc_data,
|
|
},
|
|
{
|
|
.compatible = "amlogic,c3-pwrc",
|
|
.data = &amlogic_secure_c3_pwrc_data,
|
|
},
|
|
{
|
|
.compatible = "amlogic,meson-s4-pwrc",
|
|
.data = &meson_secure_s4_pwrc_data,
|
|
},
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table);
|
|
|
|
static struct platform_driver meson_secure_pwrc_driver = {
|
|
.probe = meson_secure_pwrc_probe,
|
|
.driver = {
|
|
.name = "meson_secure_pwrc",
|
|
.of_match_table = meson_secure_pwrc_match_table,
|
|
},
|
|
};
|
|
module_platform_driver(meson_secure_pwrc_driver);
|
|
MODULE_LICENSE("Dual MIT/GPL");
|