u-boot/drivers/power/domain/meson-secure-pwrc.c
Alexey Romanov 58edf5773a drivers: meson: introduce secure power controller driver
This patch adds Power controller driver support for Amlogic
A1 family using secure monitor calls. The power domains register
only can access in secure world.

Signed-off-by: Alexey Romanov <avromanov@sberdevices.ru>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20230531093156.29240-4-avromanov@sberdevices.ru
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
2023-06-28 10:05:34 +02:00

160 lines
3.5 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 SberDevices, Inc.
* Author: Alexey Romanov <avromanov@sberdevices.ru>
*/
#include <dm.h>
#include <asm/arch/sm.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
#include <dt-bindings/power/meson-a1-power.h>
struct meson_secure_pwrc_domain_desc {
char *name;
size_t index;
};
struct meson_secure_pwrc_domain_data {
unsigned int count;
struct meson_secure_pwrc_domain_desc *domains;
};
struct meson_secure_pwrc_priv {
const struct meson_secure_pwrc_domain_data *data;
};
static int meson_secure_pwrc_on(struct power_domain *power_domain)
{
struct meson_secure_pwrc_priv *priv = dev_get_priv(power_domain->dev);
struct meson_secure_pwrc_domain_desc *pwrc_domain;
int err;
pwrc_domain = &priv->data->domains[power_domain->id];
err = meson_sm_pwrdm_on(pwrc_domain->index);
if (err) {
pr_err("meson_sm_pwrdm_on() failed (%d)\n", err);
return err;
}
pr_debug("enable %s power domain\n", pwrc_domain->name);
return 0;
}
static int meson_secure_pwrc_off(struct power_domain *power_domain)
{
struct meson_secure_pwrc_priv *priv = dev_get_priv(power_domain->dev);
struct meson_secure_pwrc_domain_desc *pwrc_domain;
int err;
pwrc_domain = &priv->data->domains[power_domain->id];
err = meson_sm_pwrdm_off(pwrc_domain->index);
if (err) {
pr_err("meson_sm_pwrdm_off() failed (%d)\n", err);
return err;
}
pr_debug("disable %s power domain\n", pwrc_domain->name);
return 0;
}
static int meson_secure_pwrc_of_xlate(struct power_domain *power_domain,
struct ofnode_phandle_args *args)
{
struct meson_secure_pwrc_priv *priv = dev_get_priv(power_domain->dev);
struct meson_secure_pwrc_domain_desc *pwrc_domain;
if (args->args_count < 1) {
pr_err("invalid args count: %d\n", args->args_count);
return -EINVAL;
}
power_domain->id = args->args[0];
if (power_domain->id >= priv->data->count) {
pr_err("domain with ID=%lu is invalid\n", power_domain->id);
return -EINVAL;
}
pwrc_domain = &priv->data->domains[power_domain->id];
if (!pwrc_domain->name) {
pr_err("domain with ID=%lu is invalid\n", power_domain->id);
return -EINVAL;
}
return 0;
}
#define SEC_PD(__name) \
[PWRC_##__name##_ID] = \
{ \
.name = #__name, \
.index = PWRC_##__name##_ID, \
}
static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
SEC_PD(DSPA),
SEC_PD(DSPB),
SEC_PD(UART),
SEC_PD(DMC),
SEC_PD(I2C),
SEC_PD(PSRAM),
SEC_PD(ACODEC),
SEC_PD(AUDIO),
SEC_PD(OTP),
SEC_PD(DMA),
SEC_PD(SD_EMMC),
SEC_PD(RAMA),
SEC_PD(RAMB),
SEC_PD(IR),
SEC_PD(SPICC),
SEC_PD(SPIFC),
SEC_PD(USB),
SEC_PD(NIC),
SEC_PD(PDMIN),
SEC_PD(RSA),
};
struct power_domain_ops meson_secure_pwrc_ops = {
.on = meson_secure_pwrc_on,
.off = meson_secure_pwrc_off,
.of_xlate = meson_secure_pwrc_of_xlate,
};
static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = {
.count = ARRAY_SIZE(a1_pwrc_domains),
.domains = a1_pwrc_domains,
};
static const struct udevice_id meson_secure_pwrc_ids[] = {
{
.compatible = "amlogic,meson-a1-pwrc",
.data = (unsigned long)&meson_secure_a1_pwrc_data,
},
{ }
};
static int meson_secure_pwrc_probe(struct udevice *dev)
{
struct meson_secure_pwrc_priv *priv = dev_get_priv(dev);
priv->data = (void *)dev_get_driver_data(dev);
if (!priv->data)
return -EINVAL;
return 0;
}
U_BOOT_DRIVER(meson_secure_pwrc) = {
.name = "meson_secure_pwrc",
.id = UCLASS_POWER_DOMAIN,
.of_match = meson_secure_pwrc_ids,
.probe = meson_secure_pwrc_probe,
.ops = &meson_secure_pwrc_ops,
.priv_auto = sizeof(struct meson_secure_pwrc_priv),
};