GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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, ®);
|
||||
|
||||
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,
|
||||
};
|
||||
Reference in New Issue
Block a user