GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,75 @@
menu "Power Domain Support"
config POWER_DOMAIN
bool "Enable power domain support using Driver Model"
depends on DM && OF_CONTROL
help
Enable support for the power domain driver class. Many SoCs allow
power to be applied to or removed from portions of the SoC (power
domains). This may be used to save power. This API provides the
means to control such power management hardware.
config BCM6328_POWER_DOMAIN
bool "Enable the BCM6328 power domain driver"
depends on POWER_DOMAIN && ARCH_BMIPS
help
Enable support for manipulating BCM6345 power domains via MMIO
mapped registers.
config IMX8_POWER_DOMAIN
bool "Enable i.MX8 power domain driver"
depends on ARCH_IMX8
help
Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC
requests to the SCU.
config IMX8M_POWER_DOMAIN
bool "Enable i.MX8M power domain driver"
depends on POWER_DOMAIN && ARCH_IMX8M
help
Enable support for manipulating NXP i.MX8M on-SoC power domains via
requests to the ATF.
config MTK_POWER_DOMAIN
bool "Enable the MediaTek power domain driver"
depends on POWER_DOMAIN && ARCH_MEDIATEK
help
Enable support for manipulating MediaTek power domains via MMIO
mapped registers.
config MESON_GX_VPU_POWER_DOMAIN
bool "Enable Amlogic Meson GX VPU power domain driver"
depends on POWER_DOMAIN && ARCH_MESON
help
Enable support for manipulating Amlogic Meson GX Video Processing
Unit power domain.
config MESON_EE_POWER_DOMAIN
bool "Enable Amlogic Everything-Else power domain driver"
depends on POWER_DOMAIN && ARCH_MESON
help
Enable support for manipulating Amlogic Meson Everything-Else power
domains.
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
help
Enable support for a test power domain driver implementation, which
simply accepts requests to power on/off various HW modules without
actually doing anything beyond a little error checking.
config TEGRA186_POWER_DOMAIN
bool "Enable Tegra186 BPMP-based power domain driver"
depends on TEGRA186_BPMP
help
Enable support for manipulating Tegra's on-SoC power domains via IPC
requests to the BPMP (Boot and Power Management Processor).
config TI_SCI_POWER_DOMAIN
bool "Enable the TI SCI-based power domain driver"
depends on POWER_DOMAIN && TI_SCI_PROTOCOL
help
Generic power domain implementation for TI devices implementing the
TI SCI protocol.
endmenu
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2016, NVIDIA CORPORATION.
#
obj-$(CONFIG_$(SPL_)POWER_DOMAIN) += power-domain-uclass.o
obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o
obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o
obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o
obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/io.h>
#define MAX_DOMAINS 32
struct bcm6328_power_domain {
void __iomem *regs;
};
static int bcm6328_power_domain_request(struct power_domain *power_domain)
{
if (power_domain->id >= MAX_DOMAINS)
return -EINVAL;
return 0;
}
static int bcm6328_power_domain_free(struct power_domain *power_domain)
{
return 0;
}
static int bcm6328_power_domain_on(struct power_domain *power_domain)
{
struct bcm6328_power_domain *priv = dev_get_priv(power_domain->dev);
clrbits_be32(priv->regs, BIT(power_domain->id));
return 0;
}
static int bcm6328_power_domain_off(struct power_domain *power_domain)
{
struct bcm6328_power_domain *priv = dev_get_priv(power_domain->dev);
setbits_be32(priv->regs, BIT(power_domain->id));
return 0;
}
static int bcm6328_power_domain_probe(struct udevice *dev)
{
struct bcm6328_power_domain *priv = dev_get_priv(dev);
priv->regs = dev_remap_addr(dev);
if (!priv->regs)
return -EINVAL;
return 0;
}
static const struct udevice_id bcm6328_power_domain_ids[] = {
{ .compatible = "brcm,bcm6328-power-domain" },
{ /* sentinel */ }
};
struct power_domain_ops bcm6328_power_domain_ops = {
.free = bcm6328_power_domain_free,
.off = bcm6328_power_domain_off,
.on = bcm6328_power_domain_on,
.request = bcm6328_power_domain_request,
};
U_BOOT_DRIVER(bcm6328_power_domain) = {
.name = "bcm6328_power_domain",
.id = UCLASS_POWER_DOMAIN,
.of_match = bcm6328_power_domain_ids,
.ops = &bcm6328_power_domain_ops,
.priv_auto_alloc_size = sizeof(struct bcm6328_power_domain),
.probe = bcm6328_power_domain_probe,
};
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 NXP
*/
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/io.h>
#include <asm/arch/power-domain.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <asm/arch/sci/sci.h>
DECLARE_GLOBAL_DATA_PTR;
struct imx8_power_domain_priv {
bool state_on;
};
static int imx8_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int imx8_power_domain_free(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int imx8_power_domain_on(struct power_domain *power_domain)
{
struct udevice *dev = power_domain->dev;
struct imx8_power_domain_platdata *pdata;
struct imx8_power_domain_priv *ppriv;
sc_err_t ret;
int err;
struct power_domain parent_domain;
struct udevice *parent = dev_get_parent(dev);
/* Need to power on parent node first */
if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) {
parent_domain.dev = parent;
err = imx8_power_domain_on(&parent_domain);
if (err)
return err;
}
pdata = (struct imx8_power_domain_platdata *)dev_get_platdata(dev);
ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev);
debug("%s(power_domain=%s) resource_id %d\n", __func__, dev->name,
pdata->resource_id);
/* Already powered on */
if (ppriv->state_on)
return 0;
if (pdata->resource_id != SC_R_LAST) {
ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
SC_PM_PW_MODE_ON);
if (ret) {
printf("Error: %s Power up failed! (error = %d)\n",
dev->name, ret);
return -EIO;
}
}
ppriv->state_on = true;
debug("%s is powered on\n", dev->name);
return 0;
}
static int imx8_power_domain_off_node(struct power_domain *power_domain)
{
struct udevice *dev = power_domain->dev;
struct udevice *child;
struct imx8_power_domain_priv *ppriv;
struct imx8_power_domain_priv *child_ppriv;
struct imx8_power_domain_platdata *pdata;
sc_err_t ret;
ppriv = dev_get_priv(dev);
pdata = dev_get_platdata(dev);
debug("%s, %s, state_on %d\n", __func__, dev->name, ppriv->state_on);
/* Already powered off */
if (!ppriv->state_on)
return 0;
/* Check if all subnodes are off */
for (device_find_first_child(dev, &child);
child;
device_find_next_child(&child)) {
if (device_active(child)) {
child_ppriv =
(struct imx8_power_domain_priv *)dev_get_priv(child);
if (child_ppriv->state_on)
return -EPERM;
}
}
if (pdata->resource_id != SC_R_LAST) {
if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) {
printf("%s not owned by curr partition\n", dev->name);
return 0;
}
ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
SC_PM_PW_MODE_OFF);
if (ret) {
printf("Error: %s Power off failed! (error = %d)\n",
dev->name, ret);
return -EIO;
}
}
ppriv->state_on = false;
debug("%s is powered off\n", dev->name);
return 0;
}
static int imx8_power_domain_off_parentnodes(struct power_domain *power_domain)
{
struct udevice *dev = power_domain->dev;
struct udevice *parent = dev_get_parent(dev);
struct udevice *child;
struct imx8_power_domain_priv *ppriv;
struct imx8_power_domain_priv *child_ppriv;
struct imx8_power_domain_platdata *pdata;
sc_err_t ret;
struct power_domain parent_pd;
if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) {
pdata =
(struct imx8_power_domain_platdata *)dev_get_platdata(parent);
ppriv = (struct imx8_power_domain_priv *)dev_get_priv(parent);
debug("%s, %s, state_on %d\n", __func__, parent->name,
ppriv->state_on);
/* Already powered off */
if (!ppriv->state_on)
return 0;
/*
* Check if all sibling nodes are off. If yes,
* power off parent
*/
for (device_find_first_child(parent, &child); child;
device_find_next_child(&child)) {
if (device_active(child)) {
child_ppriv = (struct imx8_power_domain_priv *)
dev_get_priv(child);
/* Find a power on sibling */
if (child_ppriv->state_on) {
debug("sibling %s, state_on %d\n",
child->name,
child_ppriv->state_on);
return 0;
}
}
}
/* power off parent */
if (pdata->resource_id != SC_R_LAST) {
ret = sc_pm_set_resource_power_mode(-1,
pdata->resource_id,
SC_PM_PW_MODE_OFF);
if (ret) {
printf("%s Power off failed! (error = %d)\n",
parent->name, ret);
return -EIO;
}
}
ppriv->state_on = false;
debug("%s is powered off\n", parent->name);
parent_pd.dev = parent;
imx8_power_domain_off_parentnodes(&parent_pd);
}
return 0;
}
static int imx8_power_domain_off(struct power_domain *power_domain)
{
int ret;
debug("%s(power_domain=%p)\n", __func__, power_domain);
/* Turn off the node */
ret = imx8_power_domain_off_node(power_domain);
if (ret) {
debug("Can't power off the node of dev %s, ret = %d\n",
power_domain->dev->name, ret);
return ret;
}
/* Turn off parent nodes, if sibling nodes are all off */
ret = imx8_power_domain_off_parentnodes(power_domain);
if (ret) {
printf("Failed to power off parent nodes of dev %s, ret = %d\n",
power_domain->dev->name, ret);
return ret;
}
return 0;
}
static int imx8_power_domain_of_xlate(struct power_domain *power_domain,
struct ofnode_phandle_args *args)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
/* Do nothing to the xlate, since we don't have args used */
return 0;
}
static int imx8_power_domain_bind(struct udevice *dev)
{
int offset;
const char *name;
int ret = 0;
debug("%s(dev=%p)\n", __func__, dev);
offset = dev_of_offset(dev);
for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
offset = fdt_next_subnode(gd->fdt_blob, offset)) {
/* Bind the subnode to this driver */
name = fdt_get_name(gd->fdt_blob, offset, NULL);
ret = device_bind_with_driver_data(dev, dev->driver, name,
dev->driver_data,
offset_to_ofnode(offset),
NULL);
if (ret == -ENODEV)
printf("Driver '%s' refuses to bind\n",
dev->driver->name);
if (ret)
printf("Error binding driver '%s': %d\n",
dev->driver->name, ret);
}
return 0;
}
static int imx8_power_domain_probe(struct udevice *dev)
{
struct imx8_power_domain_priv *ppriv;
debug("%s(dev=%s)\n", __func__, dev->name);
ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev);
/* Set default to power off */
if (ppriv)
ppriv->state_on = false;
return 0;
}
static int imx8_power_domain_ofdata_to_platdata(struct udevice *dev)
{
int reg;
struct imx8_power_domain_platdata *pdata = dev_get_platdata(dev);
reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
if (reg == -1) {
debug("%s: Invalid resource id %d\n", __func__, reg);
return -EINVAL;
}
pdata->resource_id = (sc_rsrc_t)reg;
debug("%s resource_id %d\n", __func__, pdata->resource_id);
return 0;
}
static const struct udevice_id imx8_power_domain_ids[] = {
{ .compatible = "nxp,imx8-pd" },
{ }
};
struct power_domain_ops imx8_power_domain_ops = {
.request = imx8_power_domain_request,
.free = imx8_power_domain_free,
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
.of_xlate = imx8_power_domain_of_xlate,
};
U_BOOT_DRIVER(imx8_power_domain) = {
.name = "imx8_power_domain",
.id = UCLASS_POWER_DOMAIN,
.of_match = imx8_power_domain_ids,
.bind = imx8_power_domain_bind,
.probe = imx8_power_domain_probe,
.ofdata_to_platdata = imx8_power_domain_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct imx8_power_domain_platdata),
.priv_auto_alloc_size = sizeof(struct imx8_power_domain_priv),
.ops = &imx8_power_domain_ops,
};
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP
*/
#define DEBUG
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/arch/power-domain.h>
#include <asm/arch/sci/sci.h>
static int imx8_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int imx8_power_domain_free(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int imx8_power_domain_on(struct power_domain *power_domain)
{
u32 resource_id = power_domain->id;
int ret;
debug("%s: resource_id %u\n", __func__, resource_id);
ret = sc_pm_set_resource_power_mode(-1, resource_id, SC_PM_PW_MODE_ON);
if (ret) {
printf("Error: %u Power up failed! (error = %d)\n",
resource_id, ret);
return ret;
}
return 0;
}
static int imx8_power_domain_off(struct power_domain *power_domain)
{
u32 resource_id = power_domain->id;
int ret;
debug("%s: resource_id %u\n", __func__, resource_id);
ret = sc_pm_set_resource_power_mode(-1, resource_id, SC_PM_PW_MODE_OFF);
if (ret) {
printf("Error: %u Power off failed! (error = %d)\n",
resource_id, ret);
return ret;
}
return 0;
}
static int imx8_power_domain_probe(struct udevice *dev)
{
debug("%s(dev=%s)\n", __func__, dev->name);
return 0;
}
static const struct udevice_id imx8_power_domain_ids[] = {
{ .compatible = "fsl,imx8qxp-scu-pd" },
{ .compatible = "fsl,scu-pd" },
{ }
};
struct power_domain_ops imx8_power_domain_ops_v2 = {
.request = imx8_power_domain_request,
.free = imx8_power_domain_free,
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
};
U_BOOT_DRIVER(imx8_power_domain_v2) = {
.name = "imx8_power_domain_v2",
.id = UCLASS_POWER_DOMAIN,
.of_match = imx8_power_domain_ids,
.probe = imx8_power_domain_probe,
.ops = &imx8_power_domain_ops_v2,
};
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 NXP
*/
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/io.h>
#include <asm/arch/power-domain.h>
#include <asm/mach-imx/sys_proto.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <imx_sip.h>
DECLARE_GLOBAL_DATA_PTR;
static int imx8m_power_domain_request(struct power_domain *power_domain)
{
return 0;
}
static int imx8m_power_domain_free(struct power_domain *power_domain)
{
return 0;
}
static int imx8m_power_domain_on(struct power_domain *power_domain)
{
struct udevice *dev = power_domain->dev;
struct imx8m_power_domain_platdata *pdata;
pdata = dev_get_platdata(dev);
if (pdata->resource_id < 0)
return -EINVAL;
if (pdata->has_pd)
power_domain_on(&pdata->pd);
call_imx_sip(IMX_SIP_GPC, IMX_SIP_GPC_PM_DOMAIN,
pdata->resource_id, 1, 0);
return 0;
}
static int imx8m_power_domain_off(struct power_domain *power_domain)
{
struct udevice *dev = power_domain->dev;
struct imx8m_power_domain_platdata *pdata;
pdata = dev_get_platdata(dev);
if (pdata->resource_id < 0)
return -EINVAL;
call_imx_sip(IMX_SIP_GPC, IMX_SIP_GPC_PM_DOMAIN,
pdata->resource_id, 0, 0);
if (pdata->has_pd)
power_domain_off(&pdata->pd);
return 0;
}
static int imx8m_power_domain_of_xlate(struct power_domain *power_domain,
struct ofnode_phandle_args *args)
{
return 0;
}
static int imx8m_power_domain_bind(struct udevice *dev)
{
int offset;
const char *name;
int ret = 0;
offset = dev_of_offset(dev);
for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
offset = fdt_next_subnode(gd->fdt_blob, offset)) {
/* Bind the subnode to this driver */
name = fdt_get_name(gd->fdt_blob, offset, NULL);
ret = device_bind_with_driver_data(dev, dev->driver, name,
dev->driver_data,
offset_to_ofnode(offset),
NULL);
if (ret == -ENODEV)
printf("Driver '%s' refuses to bind\n",
dev->driver->name);
if (ret)
printf("Error binding driver '%s': %d\n",
dev->driver->name, ret);
}
return 0;
}
static int imx8m_power_domain_probe(struct udevice *dev)
{
return 0;
}
static int imx8m_power_domain_ofdata_to_platdata(struct udevice *dev)
{
struct imx8m_power_domain_platdata *pdata = dev_get_platdata(dev);
pdata->resource_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"reg", -1);
if (!power_domain_get(dev, &pdata->pd))
pdata->has_pd = 1;
return 0;
}
static const struct udevice_id imx8m_power_domain_ids[] = {
{ .compatible = "fsl,imx8mq-gpc" },
{ }
};
struct power_domain_ops imx8m_power_domain_ops = {
.request = imx8m_power_domain_request,
.free = imx8m_power_domain_free,
.on = imx8m_power_domain_on,
.off = imx8m_power_domain_off,
.of_xlate = imx8m_power_domain_of_xlate,
};
U_BOOT_DRIVER(imx8m_power_domain) = {
.name = "imx8m_power_domain",
.id = UCLASS_POWER_DOMAIN,
.of_match = imx8m_power_domain_ids,
.bind = imx8m_power_domain_bind,
.probe = imx8m_power_domain_probe,
.ofdata_to_platdata = imx8m_power_domain_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct imx8m_power_domain_platdata),
.ops = &imx8m_power_domain_ops,
};
@@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2019 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <common.h>
#include <dm.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>
/* 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, &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 = {
.free = 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_alloc_size = sizeof(struct meson_ee_pwrc_priv),
};
@@ -0,0 +1,333 @@
// 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 <power-domain-uclass.h>
#include <regmap.h>
#include <syscon.h>
#include <reset.h>
#include <clk.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 = {
.free = 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_alloc_size = sizeof(struct meson_gx_pwrc_vpu_priv),
};
@@ -0,0 +1,406 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 MediaTek Inc.
* Author: Ryder Lee <ryder.lee@mediatek.com>
*/
#include <clk.h>
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <linux/iopoll.h>
#include <dt-bindings/power/mt7623-power.h>
#include <dt-bindings/power/mt7629-power.h>
#define SPM_EN (0xb16 << 16 | 0x1)
#define SPM_VDE_PWR_CON 0x0210
#define SPM_MFG_PWR_CON 0x0214
#define SPM_ISP_PWR_CON 0x0238
#define SPM_DIS_PWR_CON 0x023c
#define SPM_CONN_PWR_CON 0x0280
#define SPM_BDP_PWR_CON 0x029c
#define SPM_ETH_PWR_CON 0x02a0
#define SPM_HIF_PWR_CON 0x02a4
#define SPM_IFR_MSC_PWR_CON 0x02a8
#define SPM_ETHSYS_PWR_CON 0x2e0
#define SPM_HIF0_PWR_CON 0x2e4
#define SPM_HIF1_PWR_CON 0x2e8
#define SPM_PWR_STATUS 0x60c
#define SPM_PWR_STATUS_2ND 0x610
#define PWR_RST_B_BIT BIT(0)
#define PWR_ISO_BIT BIT(1)
#define PWR_ON_BIT BIT(2)
#define PWR_ON_2ND_BIT BIT(3)
#define PWR_CLK_DIS_BIT BIT(4)
#define PWR_STATUS_CONN BIT(1)
#define PWR_STATUS_DISP BIT(3)
#define PWR_STATUS_MFG BIT(4)
#define PWR_STATUS_ISP BIT(5)
#define PWR_STATUS_VDEC BIT(7)
#define PWR_STATUS_BDP BIT(14)
#define PWR_STATUS_ETH BIT(15)
#define PWR_STATUS_HIF BIT(16)
#define PWR_STATUS_IFR_MSC BIT(17)
#define PWR_STATUS_ETHSYS BIT(24)
#define PWR_STATUS_HIF0 BIT(25)
#define PWR_STATUS_HIF1 BIT(26)
/* Infrasys configuration */
#define INFRA_TOPDCM_CTRL 0x10
#define INFRA_TOPAXI_PROT_EN 0x220
#define INFRA_TOPAXI_PROT_STA1 0x228
#define DCM_TOP_EN BIT(0)
enum scp_domain_type {
SCPSYS_MT7623,
SCPSYS_MT7629,
};
struct scp_domain;
struct scp_domain_data {
struct scp_domain *scpd;
u32 sta_mask;
int ctl_offs;
u32 sram_pdn_bits;
u32 sram_pdn_ack_bits;
u32 bus_prot_mask;
};
struct scp_domain {
void __iomem *base;
void __iomem *infracfg;
enum scp_domain_type type;
struct scp_domain_data *data;
};
static struct scp_domain_data scp_domain_mt7623[] = {
[MT7623_POWER_DOMAIN_CONN] = {
.sta_mask = PWR_STATUS_CONN,
.ctl_offs = SPM_CONN_PWR_CON,
.bus_prot_mask = BIT(8) | BIT(2),
},
[MT7623_POWER_DOMAIN_DISP] = {
.sta_mask = PWR_STATUS_DISP,
.ctl_offs = SPM_DIS_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.bus_prot_mask = BIT(2),
},
[MT7623_POWER_DOMAIN_MFG] = {
.sta_mask = PWR_STATUS_MFG,
.ctl_offs = SPM_MFG_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
},
[MT7623_POWER_DOMAIN_VDEC] = {
.sta_mask = PWR_STATUS_VDEC,
.ctl_offs = SPM_VDE_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
},
[MT7623_POWER_DOMAIN_ISP] = {
.sta_mask = PWR_STATUS_ISP,
.ctl_offs = SPM_ISP_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(13, 12),
},
[MT7623_POWER_DOMAIN_BDP] = {
.sta_mask = PWR_STATUS_BDP,
.ctl_offs = SPM_BDP_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
},
[MT7623_POWER_DOMAIN_ETH] = {
.sta_mask = PWR_STATUS_ETH,
.ctl_offs = SPM_ETH_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
},
[MT7623_POWER_DOMAIN_HIF] = {
.sta_mask = PWR_STATUS_HIF,
.ctl_offs = SPM_HIF_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
},
[MT7623_POWER_DOMAIN_IFR_MSC] = {
.sta_mask = PWR_STATUS_IFR_MSC,
.ctl_offs = SPM_IFR_MSC_PWR_CON,
},
};
static struct scp_domain_data scp_domain_mt7629[] = {
[MT7629_POWER_DOMAIN_ETHSYS] = {
.sta_mask = PWR_STATUS_ETHSYS,
.ctl_offs = SPM_ETHSYS_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
.bus_prot_mask = (BIT(3) | BIT(17)),
},
[MT7629_POWER_DOMAIN_HIF0] = {
.sta_mask = PWR_STATUS_HIF0,
.ctl_offs = SPM_HIF0_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
.bus_prot_mask = GENMASK(25, 24),
},
[MT7629_POWER_DOMAIN_HIF1] = {
.sta_mask = PWR_STATUS_HIF1,
.ctl_offs = SPM_HIF1_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
.bus_prot_mask = GENMASK(28, 26),
},
};
/**
* This function enables the bus protection bits for disabled power
* domains so that the system does not hang when some unit accesses the
* bus while in power down.
*/
static int mtk_infracfg_set_bus_protection(void __iomem *infracfg,
u32 mask)
{
u32 val;
clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask);
return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
(val & mask) == mask, 100);
}
static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg,
u32 mask)
{
u32 val;
clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask);
return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
!(val & mask), 100);
}
static int scpsys_domain_is_on(struct scp_domain_data *data)
{
struct scp_domain *scpd = data->scpd;
u32 sta = readl(scpd->base + SPM_PWR_STATUS) &
data->sta_mask;
u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) &
data->sta_mask;
/*
* A domain is on when both status bits are set. If only one is set
* return an error. This happens while powering up a domain
*/
if (sta && sta2)
return true;
if (!sta && !sta2)
return false;
return -EINVAL;
}
static int scpsys_power_on(struct power_domain *power_domain)
{
struct scp_domain *scpd = dev_get_priv(power_domain->dev);
struct scp_domain_data *data = &scpd->data[power_domain->id];
void __iomem *ctl_addr = scpd->base + data->ctl_offs;
u32 pdn_ack = data->sram_pdn_ack_bits;
u32 val;
int ret, tmp;
writel(SPM_EN, scpd->base);
val = readl(ctl_addr);
val |= PWR_ON_BIT;
writel(val, ctl_addr);
val |= PWR_ON_2ND_BIT;
writel(val, ctl_addr);
ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0,
100);
if (ret < 0)
return ret;
val &= ~PWR_CLK_DIS_BIT;
writel(val, ctl_addr);
val &= ~PWR_ISO_BIT;
writel(val, ctl_addr);
val |= PWR_RST_B_BIT;
writel(val, ctl_addr);
val &= ~data->sram_pdn_bits;
writel(val, ctl_addr);
ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
if (ret < 0)
return ret;
if (data->bus_prot_mask) {
ret = mtk_infracfg_clear_bus_protection(scpd->infracfg,
data->bus_prot_mask);
if (ret)
return ret;
}
return 0;
}
static int scpsys_power_off(struct power_domain *power_domain)
{
struct scp_domain *scpd = dev_get_priv(power_domain->dev);
struct scp_domain_data *data = &scpd->data[power_domain->id];
void __iomem *ctl_addr = scpd->base + data->ctl_offs;
u32 pdn_ack = data->sram_pdn_ack_bits;
u32 val;
int ret, tmp;
if (data->bus_prot_mask) {
ret = mtk_infracfg_set_bus_protection(scpd->infracfg,
data->bus_prot_mask);
if (ret)
return ret;
}
val = readl(ctl_addr);
val |= data->sram_pdn_bits;
writel(val, ctl_addr);
ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
100);
if (ret < 0)
return ret;
val |= PWR_ISO_BIT;
writel(val, ctl_addr);
val &= ~PWR_RST_B_BIT;
writel(val, ctl_addr);
val |= PWR_CLK_DIS_BIT;
writel(val, ctl_addr);
val &= ~PWR_ON_BIT;
writel(val, ctl_addr);
val &= ~PWR_ON_2ND_BIT;
writel(val, ctl_addr);
ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100);
if (ret < 0)
return ret;
return 0;
}
static int scpsys_power_request(struct power_domain *power_domain)
{
struct scp_domain *scpd = dev_get_priv(power_domain->dev);
struct scp_domain_data *data;
data = &scpd->data[power_domain->id];
data->scpd = scpd;
return 0;
}
static int scpsys_power_free(struct power_domain *power_domain)
{
return 0;
}
static int mtk_power_domain_hook(struct udevice *dev)
{
struct scp_domain *scpd = dev_get_priv(dev);
scpd->type = (enum scp_domain_type)dev_get_driver_data(dev);
switch (scpd->type) {
case SCPSYS_MT7623:
scpd->data = scp_domain_mt7623;
break;
case SCPSYS_MT7629:
scpd->data = scp_domain_mt7629;
break;
default:
return -EINVAL;
}
return 0;
}
static int mtk_power_domain_probe(struct udevice *dev)
{
struct ofnode_phandle_args args;
struct scp_domain *scpd = dev_get_priv(dev);
struct regmap *regmap;
struct clk_bulk bulk;
int err;
scpd->base = dev_read_addr_ptr(dev);
if (!scpd->base)
return -ENOENT;
err = mtk_power_domain_hook(dev);
if (err)
return err;
/* get corresponding syscon phandle */
err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args);
if (err)
return err;
regmap = syscon_node_to_regmap(args.node);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
scpd->infracfg = regmap_get_range(regmap, 0);
if (!scpd->infracfg)
return -ENOENT;
/* enable Infra DCM */
setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN);
err = clk_get_bulk(dev, &bulk);
if (err)
return err;
return clk_enable_bulk(&bulk);
}
static const struct udevice_id mtk_power_domain_ids[] = {
{
.compatible = "mediatek,mt7623-scpsys",
.data = SCPSYS_MT7623,
},
{
.compatible = "mediatek,mt7629-scpsys",
.data = SCPSYS_MT7629,
},
{ /* sentinel */ }
};
struct power_domain_ops mtk_power_domain_ops = {
.free = scpsys_power_free,
.off = scpsys_power_off,
.on = scpsys_power_on,
.request = scpsys_power_request,
};
U_BOOT_DRIVER(mtk_power_domain) = {
.name = "mtk_power_domain",
.id = UCLASS_POWER_DOMAIN,
.ops = &mtk_power_domain_ops,
.probe = mtk_power_domain_probe,
.of_match = mtk_power_domain_ids,
.priv_auto_alloc_size = sizeof(struct scp_domain),
};
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*/
#include <common.h>
#include <dm.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
#include <dm/device-internal.h>
static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
{
return (struct power_domain_ops *)dev->driver->ops;
}
static int power_domain_of_xlate_default(struct power_domain *power_domain,
struct ofnode_phandle_args *args)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
if (args->args_count != 1) {
debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
power_domain->id = args->args[0];
return 0;
}
int power_domain_get_by_index(struct udevice *dev,
struct power_domain *power_domain, int index)
{
struct ofnode_phandle_args args;
int ret;
struct udevice *dev_power_domain;
struct power_domain_ops *ops;
debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain);
ret = dev_read_phandle_with_args(dev, "power-domains",
"#power-domain-cells", 0, index,
&args);
if (ret) {
debug("%s: dev_read_phandle_with_args failed: %d\n",
__func__, ret);
return ret;
}
ret = uclass_get_device_by_ofnode(UCLASS_POWER_DOMAIN, args.node,
&dev_power_domain);
if (ret) {
debug("%s: uclass_get_device_by_ofnode failed: %d\n",
__func__, ret);
return ret;
}
ops = power_domain_dev_ops(dev_power_domain);
power_domain->dev = dev_power_domain;
if (ops->of_xlate)
ret = ops->of_xlate(power_domain, &args);
else
ret = power_domain_of_xlate_default(power_domain, &args);
if (ret) {
debug("of_xlate() failed: %d\n", ret);
return ret;
}
ret = ops->request(power_domain);
if (ret) {
debug("ops->request() failed: %d\n", ret);
return ret;
}
return 0;
}
int power_domain_get(struct udevice *dev, struct power_domain *power_domain)
{
return power_domain_get_by_index(dev, power_domain, 0);
}
int power_domain_free(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->free(power_domain);
}
int power_domain_on(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->on(power_domain);
}
int power_domain_off(struct power_domain *power_domain)
{
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
return ops->off(power_domain);
}
#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
static int dev_power_domain_ctrl(struct udevice *dev, bool on)
{
struct power_domain pd;
int i, count, ret = 0;
count = dev_count_phandle_with_args(dev, "power-domains",
"#power-domain-cells");
for (i = 0; i < count; i++) {
ret = power_domain_get_by_index(dev, &pd, i);
if (ret)
return ret;
if (on)
ret = power_domain_on(&pd);
else
ret = power_domain_off(&pd);
}
/*
* power_domain_get() bound the device, thus
* we must remove it again to prevent unbinding
* active devices (which would result in unbind
* error).
*/
if (count > 0 && !on)
device_remove(pd.dev, DM_REMOVE_NORMAL);
return ret;
}
int dev_power_domain_on(struct udevice *dev)
{
return dev_power_domain_ctrl(dev, true);
}
int dev_power_domain_off(struct udevice *dev)
{
return dev_power_domain_ctrl(dev, false);
}
#endif
UCLASS_DRIVER(power_domain) = {
.id = UCLASS_POWER_DOMAIN,
.name = "power_domain",
};
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*/
#include <common.h>
#include <dm.h>
#include <power-domain.h>
#include <asm/io.h>
#include <asm/power-domain.h>
struct sandbox_power_domain_test {
struct power_domain pd;
};
int sandbox_power_domain_test_get(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_get(dev, &sbrt->pd);
}
int sandbox_power_domain_test_on(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_on(&sbrt->pd);
}
int sandbox_power_domain_test_off(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_off(&sbrt->pd);
}
int sandbox_power_domain_test_free(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
return power_domain_free(&sbrt->pd);
}
static const struct udevice_id sandbox_power_domain_test_ids[] = {
{ .compatible = "sandbox,power-domain-test" },
{ }
};
U_BOOT_DRIVER(sandbox_power_domain_test) = {
.name = "sandbox_power_domain_test",
.id = UCLASS_MISC,
.of_match = sandbox_power_domain_test_ids,
.priv_auto_alloc_size = sizeof(struct sandbox_power_domain_test),
};
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*/
#include <common.h>
#include <dm.h>
#include <power-domain-uclass.h>
#include <asm/io.h>
#include <asm/power-domain.h>
#define SANDBOX_POWER_DOMAINS 3
struct sandbox_power_domain {
bool on[SANDBOX_POWER_DOMAINS];
};
static int sandbox_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
if (power_domain->id >= SANDBOX_POWER_DOMAINS)
return -EINVAL;
return 0;
}
static int sandbox_power_domain_free(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
return 0;
}
static int sandbox_power_domain_on(struct power_domain *power_domain)
{
struct sandbox_power_domain *sbr = dev_get_priv(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
sbr->on[power_domain->id] = true;
return 0;
}
static int sandbox_power_domain_off(struct power_domain *power_domain)
{
struct sandbox_power_domain *sbr = dev_get_priv(power_domain->dev);
debug("%s(power_domain=%p)\n", __func__, power_domain);
sbr->on[power_domain->id] = false;
return 0;
}
static int sandbox_power_domain_bind(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
static int sandbox_power_domain_probe(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
static const struct udevice_id sandbox_power_domain_ids[] = {
{ .compatible = "sandbox,power-domain" },
{ }
};
struct power_domain_ops sandbox_power_domain_ops = {
.request = sandbox_power_domain_request,
.free = sandbox_power_domain_free,
.on = sandbox_power_domain_on,
.off = sandbox_power_domain_off,
};
U_BOOT_DRIVER(sandbox_power_domain) = {
.name = "sandbox_power_domain",
.id = UCLASS_POWER_DOMAIN,
.of_match = sandbox_power_domain_ids,
.bind = sandbox_power_domain_bind,
.probe = sandbox_power_domain_probe,
.priv_auto_alloc_size = sizeof(struct sandbox_power_domain),
.ops = &sandbox_power_domain_ops,
};
int sandbox_power_domain_query(struct udevice *dev, unsigned long id)
{
struct sandbox_power_domain *sbr = dev_get_priv(dev);
debug("%s(dev=%p, id=%ld)\n", __func__, dev, id);
if (id >= SANDBOX_POWER_DOMAINS)
return -EINVAL;
return sbr->on[id];
}
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
*/
#include <common.h>
#include <dm.h>
#include <misc.h>
#include <power-domain-uclass.h>
#include <asm/arch-tegra/bpmp_abi.h>
#define UPDATE BIT(0)
#define ON BIT(1)
static int tegra186_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__,
power_domain, power_domain->dev, power_domain->id);
return 0;
}
static int tegra186_power_domain_free(struct power_domain *power_domain)
{
debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__,
power_domain, power_domain->dev, power_domain->id);
return 0;
}
static int tegra186_power_domain_common(struct power_domain *power_domain,
bool on)
{
struct mrq_pg_update_state_request req;
int on_state = on ? ON : 0;
int ret;
req.partition_id = power_domain->id;
req.logic_state = UPDATE | on_state;
req.sram_state = UPDATE | on_state;
/*
* Drivers manage their own clocks so they don't get out of sync, and
* since some power domains have many clocks, only a subset of which
* are actually needed depending on use-case.
*/
req.clock_state = UPDATE;
ret = misc_call(power_domain->dev->parent, MRQ_PG_UPDATE_STATE, &req,
sizeof(req), NULL, 0);
if (ret < 0)
return ret;
return 0;
}
static int tegra186_power_domain_on(struct power_domain *power_domain)
{
debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__,
power_domain, power_domain->dev, power_domain->id);
return tegra186_power_domain_common(power_domain, true);
}
static int tegra186_power_domain_off(struct power_domain *power_domain)
{
debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__,
power_domain, power_domain->dev, power_domain->id);
return tegra186_power_domain_common(power_domain, false);
}
struct power_domain_ops tegra186_power_domain_ops = {
.request = tegra186_power_domain_request,
.free = tegra186_power_domain_free,
.on = tegra186_power_domain_on,
.off = tegra186_power_domain_off,
};
static int tegra186_power_domain_probe(struct udevice *dev)
{
debug("%s(dev=%p)\n", __func__, dev);
return 0;
}
U_BOOT_DRIVER(tegra186_power_domain) = {
.name = "tegra186_power_domain",
.id = UCLASS_POWER_DOMAIN,
.probe = tegra186_power_domain_probe,
.ops = &tegra186_power_domain_ops,
};
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Texas Instruments System Control Interface (TI SCI) power domain driver
*
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
* Andreas Dannenberg <dannenberg@ti.com>
*
* Loosely based on Linux kernel ti_sci_pm_domains.c...
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <power-domain-uclass.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>
/**
* struct ti_sci_power_domain_data - pm domain controller information structure
* @sci: TI SCI handle used for communication with system controller
*/
struct ti_sci_power_domain_data {
const struct ti_sci_handle *sci;
};
static int ti_sci_power_domain_probe(struct udevice *dev)
{
struct ti_sci_power_domain_data *data = dev_get_priv(dev);
debug("%s(dev=%p)\n", __func__, dev);
if (!data)
return -ENOMEM;
/* Store handle for communication with the system controller */
data->sci = ti_sci_get_handle(dev);
if (IS_ERR(data->sci))
return PTR_ERR(data->sci);
return 0;
}
static int ti_sci_power_domain_request(struct power_domain *pd)
{
debug("%s(pd=%p)\n", __func__, pd);
return 0;
}
static int ti_sci_power_domain_free(struct power_domain *pd)
{
debug("%s(pd=%p)\n", __func__, pd);
return 0;
}
static int ti_sci_power_domain_on(struct power_domain *pd)
{
struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
const struct ti_sci_handle *sci = data->sci;
const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
u8 flags = (uintptr_t)pd->priv;
int ret;
debug("%s(pd=%p)\n", __func__, pd);
if (flags & TI_SCI_PD_EXCLUSIVE)
ret = dops->get_device_exclusive(sci, pd->id);
else
ret = dops->get_device(sci, pd->id);
if (ret)
dev_err(pd->dev, "%s: get_device(%lu) failed (%d)\n",
__func__, pd->id, ret);
return ret;
}
static int ti_sci_power_domain_off(struct power_domain *pd)
{
struct ti_sci_power_domain_data *data = dev_get_priv(pd->dev);
const struct ti_sci_handle *sci = data->sci;
const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops;
int ret;
debug("%s(pd=%p)\n", __func__, pd);
ret = dops->put_device(sci, pd->id);
if (ret)
dev_err(pd->dev, "%s: put_device(%lu) failed (%d)\n",
__func__, pd->id, ret);
return ret;
}
static int ti_sci_power_domain_of_xlate(struct power_domain *pd,
struct ofnode_phandle_args *args)
{
u8 flags;
debug("%s(power_domain=%p)\n", __func__, pd);
if (args->args_count < 1) {
debug("Invalid args_count: %d\n", args->args_count);
return -EINVAL;
}
pd->id = args->args[0];
/* By default request for device exclusive */
flags = TI_SCI_PD_EXCLUSIVE;
if (args->args_count == 2)
flags = args->args[1] & TI_SCI_PD_EXCLUSIVE;
pd->priv = (void *)(uintptr_t)flags;
return 0;
}
static const struct udevice_id ti_sci_power_domain_of_match[] = {
{ .compatible = "ti,sci-pm-domain" },
{ /* sentinel */ }
};
static struct power_domain_ops ti_sci_power_domain_ops = {
.request = ti_sci_power_domain_request,
.free = ti_sci_power_domain_free,
.on = ti_sci_power_domain_on,
.off = ti_sci_power_domain_off,
.of_xlate = ti_sci_power_domain_of_xlate,
};
U_BOOT_DRIVER(ti_sci_pm_domains) = {
.name = "ti-sci-pm-domains",
.id = UCLASS_POWER_DOMAIN,
.of_match = ti_sci_power_domain_of_match,
.probe = ti_sci_power_domain_probe,
.priv_auto_alloc_size = sizeof(struct ti_sci_power_domain_data),
.ops = &ti_sci_power_domain_ops,
};