mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-17 02:08:38 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
435 lines
11 KiB
C
435 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2019 BayLibre, SAS
|
|
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <power-domain-uclass.h>
|
|
#include <regmap.h>
|
|
#include <syscon.h>
|
|
#include <reset.h>
|
|
#include <clk.h>
|
|
#include <dt-bindings/power/meson-g12a-power.h>
|
|
#include <dt-bindings/power/meson-sm1-power.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
|
|
/* AO Offsets */
|
|
|
|
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
|
|
#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2)
|
|
|
|
/* HHI Offsets */
|
|
|
|
#define HHI_MEM_PD_REG0 (0x40 << 2)
|
|
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
|
|
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
|
|
#define HHI_VPU_MEM_PD_REG3 (0x43 << 2)
|
|
#define HHI_VPU_MEM_PD_REG4 (0x44 << 2)
|
|
#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2)
|
|
#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2)
|
|
#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2)
|
|
#define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
|
|
|
|
struct meson_ee_pwrc;
|
|
struct meson_ee_pwrc_domain;
|
|
|
|
struct meson_ee_pwrc_mem_domain {
|
|
unsigned int reg;
|
|
unsigned int mask;
|
|
};
|
|
|
|
struct meson_ee_pwrc_top_domain {
|
|
unsigned int sleep_reg;
|
|
unsigned int sleep_mask;
|
|
unsigned int iso_reg;
|
|
unsigned int iso_mask;
|
|
};
|
|
|
|
struct meson_ee_pwrc_domain_desc {
|
|
char *name;
|
|
unsigned int reset_names_count;
|
|
unsigned int clk_names_count;
|
|
struct meson_ee_pwrc_top_domain *top_pd;
|
|
unsigned int mem_pd_count;
|
|
struct meson_ee_pwrc_mem_domain *mem_pd;
|
|
bool (*get_power)(struct power_domain *power_domain);
|
|
};
|
|
|
|
struct meson_ee_pwrc_domain_data {
|
|
unsigned int count;
|
|
struct meson_ee_pwrc_domain_desc *domains;
|
|
};
|
|
|
|
/* TOP Power Domains */
|
|
|
|
static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = {
|
|
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0,
|
|
.sleep_mask = BIT(8),
|
|
.iso_reg = AO_RTI_GEN_PWR_SLEEP0,
|
|
.iso_mask = BIT(9),
|
|
};
|
|
|
|
#define SM1_EE_PD(__bit) \
|
|
{ \
|
|
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \
|
|
.sleep_mask = BIT(__bit), \
|
|
.iso_reg = AO_RTI_GEN_PWR_ISO0, \
|
|
.iso_mask = BIT(__bit), \
|
|
}
|
|
|
|
static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8);
|
|
static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16);
|
|
static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17);
|
|
static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18);
|
|
static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19);
|
|
|
|
/* Memory PD Domains */
|
|
|
|
#define VPU_MEMPD(__reg) \
|
|
{ __reg, GENMASK(1, 0) }, \
|
|
{ __reg, GENMASK(3, 2) }, \
|
|
{ __reg, GENMASK(5, 4) }, \
|
|
{ __reg, GENMASK(7, 6) }, \
|
|
{ __reg, GENMASK(9, 8) }, \
|
|
{ __reg, GENMASK(11, 10) }, \
|
|
{ __reg, GENMASK(13, 12) }, \
|
|
{ __reg, GENMASK(15, 14) }, \
|
|
{ __reg, GENMASK(17, 16) }, \
|
|
{ __reg, GENMASK(19, 18) }, \
|
|
{ __reg, GENMASK(21, 20) }, \
|
|
{ __reg, GENMASK(23, 22) }, \
|
|
{ __reg, GENMASK(25, 24) }, \
|
|
{ __reg, GENMASK(27, 26) }, \
|
|
{ __reg, GENMASK(29, 28) }, \
|
|
{ __reg, GENMASK(31, 30) }
|
|
|
|
#define VPU_HHI_MEMPD(__reg) \
|
|
{ __reg, BIT(8) }, \
|
|
{ __reg, BIT(9) }, \
|
|
{ __reg, BIT(10) }, \
|
|
{ __reg, BIT(11) }, \
|
|
{ __reg, BIT(12) }, \
|
|
{ __reg, BIT(13) }, \
|
|
{ __reg, BIT(14) }, \
|
|
{ __reg, BIT(15) }
|
|
|
|
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = {
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
|
|
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = {
|
|
{ HHI_MEM_PD_REG0, GENMASK(3, 2) },
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = {
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
|
|
VPU_MEMPD(HHI_VPU_MEM_PD_REG3),
|
|
{ HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) },
|
|
{ HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) },
|
|
{ HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) },
|
|
{ HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) },
|
|
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = {
|
|
{ HHI_NANOQ_MEM_PD_REG0, 0xff },
|
|
{ HHI_NANOQ_MEM_PD_REG1, 0xff },
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = {
|
|
{ HHI_MEM_PD_REG0, GENMASK(31, 30) },
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = {
|
|
{ HHI_MEM_PD_REG0, GENMASK(29, 26) },
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = {
|
|
{ HHI_MEM_PD_REG0, GENMASK(25, 18) },
|
|
};
|
|
|
|
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = {
|
|
{ HHI_MEM_PD_REG0, GENMASK(5, 4) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) },
|
|
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) },
|
|
};
|
|
|
|
#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \
|
|
{ \
|
|
.name = __name, \
|
|
.reset_names_count = __resets, \
|
|
.clk_names_count = __clks, \
|
|
.top_pd = __top_pd, \
|
|
.mem_pd_count = ARRAY_SIZE(__mem), \
|
|
.mem_pd = __mem, \
|
|
.get_power = __get_power, \
|
|
}
|
|
|
|
#define TOP_PD(__name, __top_pd, __mem, __get_power) \
|
|
{ \
|
|
.name = __name, \
|
|
.top_pd = __top_pd, \
|
|
.mem_pd_count = ARRAY_SIZE(__mem), \
|
|
.mem_pd = __mem, \
|
|
.get_power = __get_power, \
|
|
}
|
|
|
|
#define MEM_PD(__name, __mem) \
|
|
TOP_PD(__name, NULL, __mem, NULL)
|
|
|
|
static bool pwrc_ee_get_power(struct power_domain *power_domain);
|
|
|
|
static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = {
|
|
[PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu,
|
|
pwrc_ee_get_power, 11, 2),
|
|
[PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
|
|
};
|
|
|
|
static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = {
|
|
[PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu,
|
|
pwrc_ee_get_power, 11, 2),
|
|
[PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna,
|
|
pwrc_ee_get_power),
|
|
[PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb,
|
|
pwrc_ee_get_power),
|
|
[PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie,
|
|
pwrc_ee_get_power),
|
|
[PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d,
|
|
pwrc_ee_get_power),
|
|
[PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio),
|
|
[PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
|
|
};
|
|
|
|
struct meson_ee_pwrc_priv {
|
|
struct regmap *regmap_ao;
|
|
struct regmap *regmap_hhi;
|
|
struct reset_ctl_bulk resets;
|
|
struct clk_bulk clks;
|
|
const struct meson_ee_pwrc_domain_data *data;
|
|
};
|
|
|
|
static bool pwrc_ee_get_power(struct power_domain *power_domain)
|
|
{
|
|
struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev);
|
|
struct meson_ee_pwrc_domain_desc *pwrc_domain;
|
|
u32 reg;
|
|
|
|
pwrc_domain = &priv->data->domains[power_domain->id];
|
|
|
|
regmap_read(priv->regmap_ao,
|
|
pwrc_domain->top_pd->sleep_reg, ®);
|
|
|
|
return (reg & pwrc_domain->top_pd->sleep_mask);
|
|
}
|
|
|
|
static int meson_ee_pwrc_request(struct power_domain *power_domain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int meson_ee_pwrc_free(struct power_domain *power_domain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int meson_ee_pwrc_off(struct power_domain *power_domain)
|
|
{
|
|
struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev);
|
|
struct meson_ee_pwrc_domain_desc *pwrc_domain;
|
|
int i;
|
|
|
|
pwrc_domain = &priv->data->domains[power_domain->id];
|
|
|
|
if (pwrc_domain->top_pd)
|
|
regmap_update_bits(priv->regmap_ao,
|
|
pwrc_domain->top_pd->sleep_reg,
|
|
pwrc_domain->top_pd->sleep_mask,
|
|
pwrc_domain->top_pd->sleep_mask);
|
|
udelay(20);
|
|
|
|
for (i = 0 ; i < pwrc_domain->mem_pd_count ; ++i)
|
|
regmap_update_bits(priv->regmap_hhi,
|
|
pwrc_domain->mem_pd[i].reg,
|
|
pwrc_domain->mem_pd[i].mask,
|
|
pwrc_domain->mem_pd[i].mask);
|
|
|
|
udelay(20);
|
|
|
|
if (pwrc_domain->top_pd)
|
|
regmap_update_bits(priv->regmap_ao,
|
|
pwrc_domain->top_pd->iso_reg,
|
|
pwrc_domain->top_pd->iso_mask,
|
|
pwrc_domain->top_pd->iso_mask);
|
|
|
|
if (pwrc_domain->clk_names_count) {
|
|
mdelay(20);
|
|
clk_disable_bulk(&priv->clks);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_ee_pwrc_on(struct power_domain *power_domain)
|
|
{
|
|
struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev);
|
|
struct meson_ee_pwrc_domain_desc *pwrc_domain;
|
|
int i, ret;
|
|
|
|
pwrc_domain = &priv->data->domains[power_domain->id];
|
|
|
|
if (pwrc_domain->top_pd)
|
|
regmap_update_bits(priv->regmap_ao,
|
|
pwrc_domain->top_pd->sleep_reg,
|
|
pwrc_domain->top_pd->sleep_mask, 0);
|
|
udelay(20);
|
|
|
|
for (i = 0 ; i < pwrc_domain->mem_pd_count ; ++i)
|
|
regmap_update_bits(priv->regmap_hhi,
|
|
pwrc_domain->mem_pd[i].reg,
|
|
pwrc_domain->mem_pd[i].mask, 0);
|
|
|
|
udelay(20);
|
|
|
|
if (pwrc_domain->reset_names_count) {
|
|
ret = reset_assert_bulk(&priv->resets);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (pwrc_domain->top_pd)
|
|
regmap_update_bits(priv->regmap_ao,
|
|
pwrc_domain->top_pd->iso_reg,
|
|
pwrc_domain->top_pd->iso_mask, 0);
|
|
|
|
if (pwrc_domain->reset_names_count) {
|
|
ret = reset_deassert_bulk(&priv->resets);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (pwrc_domain->clk_names_count)
|
|
return clk_enable_bulk(&priv->clks);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_ee_pwrc_of_xlate(struct power_domain *power_domain,
|
|
struct ofnode_phandle_args *args)
|
|
{
|
|
struct meson_ee_pwrc_priv *priv = dev_get_priv(power_domain->dev);
|
|
|
|
/* #power-domain-cells is 1 */
|
|
|
|
if (args->args_count < 1) {
|
|
debug("Invalid args_count: %d\n", args->args_count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
power_domain->id = args->args[0];
|
|
|
|
if (power_domain->id >= priv->data->count) {
|
|
debug("Invalid domain ID: %lu\n", power_domain->id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct power_domain_ops meson_ee_pwrc_ops = {
|
|
.rfree = meson_ee_pwrc_free,
|
|
.off = meson_ee_pwrc_off,
|
|
.on = meson_ee_pwrc_on,
|
|
.request = meson_ee_pwrc_request,
|
|
.of_xlate = meson_ee_pwrc_of_xlate,
|
|
};
|
|
|
|
static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = {
|
|
.count = ARRAY_SIZE(g12a_pwrc_domains),
|
|
.domains = g12a_pwrc_domains,
|
|
};
|
|
|
|
static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = {
|
|
.count = ARRAY_SIZE(sm1_pwrc_domains),
|
|
.domains = sm1_pwrc_domains,
|
|
};
|
|
|
|
static const struct udevice_id meson_ee_pwrc_ids[] = {
|
|
{
|
|
.compatible = "amlogic,meson-g12a-pwrc",
|
|
.data = (unsigned long)&meson_ee_g12a_pwrc_data,
|
|
},
|
|
{
|
|
.compatible = "amlogic,meson-sm1-pwrc",
|
|
.data = (unsigned long)&meson_ee_sm1_pwrc_data,
|
|
},
|
|
{ }
|
|
};
|
|
|
|
static int meson_ee_pwrc_probe(struct udevice *dev)
|
|
{
|
|
struct meson_ee_pwrc_priv *priv = dev_get_priv(dev);
|
|
u32 ao_phandle;
|
|
ofnode ao_node;
|
|
int ret;
|
|
|
|
priv->data = (void *)dev_get_driver_data(dev);
|
|
if (!priv->data)
|
|
return -EINVAL;
|
|
|
|
priv->regmap_hhi = syscon_node_to_regmap(dev_get_parent(dev)->node);
|
|
if (IS_ERR(priv->regmap_hhi))
|
|
return PTR_ERR(priv->regmap_hhi);
|
|
|
|
ret = ofnode_read_u32(dev->node, "amlogic,ao-sysctrl",
|
|
&ao_phandle);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ao_node = ofnode_get_by_phandle(ao_phandle);
|
|
if (!ofnode_valid(ao_node))
|
|
return -EINVAL;
|
|
|
|
priv->regmap_ao = syscon_node_to_regmap(ao_node);
|
|
if (IS_ERR(priv->regmap_ao))
|
|
return PTR_ERR(priv->regmap_ao);
|
|
|
|
ret = reset_get_bulk(dev, &priv->resets);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = clk_get_bulk(dev, &priv->clks);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_DRIVER(meson_ee_pwrc) = {
|
|
.name = "meson_ee_pwrc",
|
|
.id = UCLASS_POWER_DOMAIN,
|
|
.of_match = meson_ee_pwrc_ids,
|
|
.probe = meson_ee_pwrc_probe,
|
|
.ops = &meson_ee_pwrc_ops,
|
|
.priv_auto = sizeof(struct meson_ee_pwrc_priv),
|
|
};
|