u-boot/drivers/power/domain/meson-gx-pwrc-vpu.c
Simon Glass 41575d8e4c dm: treewide: Rename auto_alloc_size members to be shorter
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>
2020-12-13 08:00:25 -07:00

338 lines
7.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson VPU Power Domain Controller driver
*
* Copyright (c) 2018 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 <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
enum {
VPU_PWRC_COMPATIBLE_GX = 0,
VPU_PWRC_COMPATIBLE_G12A = 1,
};
/* AO Offsets */
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
#define GEN_PWR_VPU_HDMI BIT(8)
#define GEN_PWR_VPU_HDMI_ISO BIT(9)
/* 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_REG2 (0x4d << 2)
struct meson_gx_pwrc_vpu_priv {
struct regmap *regmap_ao;
struct regmap *regmap_hhi;
struct reset_ctl_bulk resets;
struct clk_bulk clks;
};
static int meson_pwrc_vpu_request(struct power_domain *power_domain)
{
return 0;
}
static int meson_pwrc_vpu_free(struct power_domain *power_domain)
{
return 0;
}
static int meson_gx_pwrc_vpu_on(struct power_domain *power_domain)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
int i, ret;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, 0);
udelay(20);
/* Power Up Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x3 << i, 0);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x3 << i, 0);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), 0);
udelay(5);
}
udelay(20);
ret = reset_assert_bulk(&priv->resets);
if (ret)
return ret;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, 0);
ret = reset_deassert_bulk(&priv->resets);
if (ret)
return ret;
ret = clk_enable_bulk(&priv->clks);
if (ret)
return ret;
return 0;
}
static int meson_g12a_pwrc_vpu_on(struct power_domain *power_domain)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
int i, ret;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, 0);
udelay(20);
/* Power Up Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x3 << i, 0);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x3 << i, 0);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
0x3 << i, 0);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), 0);
udelay(5);
}
udelay(20);
ret = reset_assert_bulk(&priv->resets);
if (ret)
return ret;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, 0);
ret = reset_deassert_bulk(&priv->resets);
if (ret)
return ret;
ret = clk_enable_bulk(&priv->clks);
if (ret)
return ret;
return 0;
}
static int meson_pwrc_vpu_on(struct power_domain *power_domain)
{
unsigned int compat = dev_get_driver_data(power_domain->dev);
switch (compat) {
case VPU_PWRC_COMPATIBLE_GX:
return meson_gx_pwrc_vpu_on(power_domain);
case VPU_PWRC_COMPATIBLE_G12A:
return meson_g12a_pwrc_vpu_on(power_domain);
}
return -EINVAL;
}
static int meson_gx_pwrc_vpu_off(struct power_domain *power_domain)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
int i;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
udelay(20);
/* Power Down Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x3 << i, 0x3 << i);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x3 << i, 0x3 << i);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), BIT(i));
udelay(5);
}
udelay(20);
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
mdelay(20);
clk_disable_bulk(&priv->clks);
return 0;
}
static int meson_g12a_pwrc_vpu_off(struct power_domain *power_domain)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(power_domain->dev);
int i;
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
udelay(20);
/* Power Down Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x3 << i, 0x3 << i);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x3 << i, 0x3 << i);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(priv->regmap_hhi, HHI_VPU_MEM_PD_REG2,
0x3 << i, 0x3 << i);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(priv->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), BIT(i));
udelay(5);
}
udelay(20);
regmap_update_bits(priv->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
mdelay(20);
clk_disable_bulk(&priv->clks);
return 0;
}
static int meson_pwrc_vpu_off(struct power_domain *power_domain)
{
unsigned int compat = dev_get_driver_data(power_domain->dev);
switch (compat) {
case VPU_PWRC_COMPATIBLE_GX:
return meson_gx_pwrc_vpu_off(power_domain);
case VPU_PWRC_COMPATIBLE_G12A:
return meson_g12a_pwrc_vpu_off(power_domain);
}
return -EINVAL;
}
static int meson_pwrc_vpu_of_xlate(struct power_domain *power_domain,
struct ofnode_phandle_args *args)
{
/* #power-domain-cells is 0 */
if (args->args_count != 0) {
debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
return 0;
}
struct power_domain_ops meson_gx_pwrc_vpu_ops = {
.rfree = meson_pwrc_vpu_free,
.off = meson_pwrc_vpu_off,
.on = meson_pwrc_vpu_on,
.request = meson_pwrc_vpu_request,
.of_xlate = meson_pwrc_vpu_of_xlate,
};
static const struct udevice_id meson_gx_pwrc_vpu_ids[] = {
{
.compatible = "amlogic,meson-gx-pwrc-vpu",
.data = VPU_PWRC_COMPATIBLE_GX,
},
{
.compatible = "amlogic,meson-g12a-pwrc-vpu",
.data = VPU_PWRC_COMPATIBLE_G12A,
},
{ }
};
static int meson_gx_pwrc_vpu_probe(struct udevice *dev)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev);
u32 hhi_phandle;
ofnode hhi_node;
int ret;
priv->regmap_ao = syscon_node_to_regmap(dev_get_parent(dev)->node);
if (IS_ERR(priv->regmap_ao))
return PTR_ERR(priv->regmap_ao);
ret = ofnode_read_u32(dev->node, "amlogic,hhi-sysctrl",
&hhi_phandle);
if (ret)
return ret;
hhi_node = ofnode_get_by_phandle(hhi_phandle);
if (!ofnode_valid(hhi_node))
return -EINVAL;
priv->regmap_hhi = syscon_node_to_regmap(hhi_node);
if (IS_ERR(priv->regmap_hhi))
return PTR_ERR(priv->regmap_hhi);
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_gx_pwrc_vpu) = {
.name = "meson_gx_pwrc_vpu",
.id = UCLASS_POWER_DOMAIN,
.of_match = meson_gx_pwrc_vpu_ids,
.probe = meson_gx_pwrc_vpu_probe,
.ops = &meson_gx_pwrc_vpu_ops,
.priv_auto = sizeof(struct meson_gx_pwrc_vpu_priv),
};