diff mbox series

[PATCHv3,14/26] power: domain: Introduce driver for raw TI K3 PDs

Message ID 20210505175540.15006-15-kristo@kernel.org
State Superseded
Delegated to: Lokesh Vutla
Headers show
Series J72xx: HSM rearch support series | expand

Commit Message

Tero Kristo May 5, 2021, 5:55 p.m. UTC
From: Tero Kristo <t-kristo@ti.com>

Normally, power domains are handled via TI-SCI in K3 SoCs. However,
SPL is not going to have access to sysfw resources, so it must control
them directly. Add driver for supporting this.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Tero Kristo <kristo@kernel.org>
---
 drivers/power/domain/Kconfig           |   7 +
 drivers/power/domain/Makefile          |   1 +
 drivers/power/domain/ti-power-domain.c | 377 +++++++++++++++++++++++++
 include/k3-dev.h                       |  76 +++++
 4 files changed, 461 insertions(+)
 create mode 100644 drivers/power/domain/ti-power-domain.c
 create mode 100644 include/k3-dev.h

Comments

Jaehoon Chung May 7, 2021, 12:03 a.m. UTC | #1
Hi Tero,

On 5/6/21 2:55 AM, Tero Kristo wrote:
> From: Tero Kristo <t-kristo@ti.com>
> 
> Normally, power domains are handled via TI-SCI in K3 SoCs. However,
> SPL is not going to have access to sysfw resources, so it must control
> them directly. Add driver for supporting this.
> 
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> Signed-off-by: Tero Kristo <kristo@kernel.org>
> ---
>  drivers/power/domain/Kconfig           |   7 +
>  drivers/power/domain/Makefile          |   1 +
>  drivers/power/domain/ti-power-domain.c | 377 +++++++++++++++++++++++++
>  include/k3-dev.h                       |  76 +++++
>  4 files changed, 461 insertions(+)
>  create mode 100644 drivers/power/domain/ti-power-domain.c
>  create mode 100644 include/k3-dev.h
> 
> diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
> index a0fd980752..b03a82d82c 100644
> --- a/drivers/power/domain/Kconfig
> +++ b/drivers/power/domain/Kconfig
> @@ -72,4 +72,11 @@ config TI_SCI_POWER_DOMAIN
>  	help
>  	  Generic power domain implementation for TI devices implementing the
>  	  TI SCI protocol.
> +
> +config TI_POWER_DOMAIN
> +	bool "Enable the TI K3 Power domain driver"
> +	depends on POWER_DOMAIN

Add your ARCH config as "depends on". This is TI specific thing.

> +	help
> +	  Generic power domain implementation for TI K3 devices.
> +
>  endmenu
> diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
> index 45bf9f6383..3d1e5f073c 100644
> --- a/drivers/power/domain/Makefile
> +++ b/drivers/power/domain/Makefile
> @@ -14,3 +14,4 @@ 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
> +obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
> diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c
> new file mode 100644
> index 0000000000..ee2dc698ae
> --- /dev/null
> +++ b/drivers/power/domain/ti-power-domain.c
> @@ -0,0 +1,377 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Texas Instruments power domain driver
> + *
> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/

2021?

> + *	Tero Kristo <t-kristo@ti.com>
> + */
> +
> +#include <asm/io.h>
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <power-domain-uclass.h>
> +#include <soc.h>
> +#include <k3-dev.h>
> +
> +#define PSC_PTCMD		0x120
> +#define PSC_PTSTAT		0x128
> +#define PSC_PDSTAT		0x200
> +#define PSC_PDCTL		0x300
> +#define PSC_MDSTAT		0x800
> +#define PSC_MDCTL		0xa00
> +
> +#define PDCTL_STATE_MASK		0x1
> +#define PDCTL_STATE_OFF			0x0
> +#define PDCTL_STATE_ON			0x1
> +
> +#define MDSTAT_STATE_MASK		0x3f
> +#define MDSTAT_BUSY_MASK		0x30
> +#define MDSTAT_STATE_SWRSTDISABLE	0x0
> +#define MDSTAT_STATE_ENABLE		0x3
> +
> +#define LPSC_TIMEOUT		100000
> +#define PD_TIMEOUT		100000
> +
> +static u32 psc_read(struct ti_psc *psc, u32 reg)
> +{
> +	u32 val;
> +
> +	val = readl(psc->base + reg);
> +	debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
> +	return val;
> +}
> +
> +static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
> +{
> +	debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
> +	writel(val, psc->base + reg);
> +}
> +
> +static u32 pd_read(struct ti_pd *pd, u32 reg)
> +{
> +	return psc_read(pd->psc, reg + 4 * pd->id);
> +}
> +
> +static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
> +{
> +	psc_write(val, pd->psc, reg + 4 * pd->id);
> +}
> +
> +static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
> +{
> +	return psc_read(lpsc->psc, reg + 4 * lpsc->id);
> +}
> +
> +static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
> +{
> +	psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
> +}
> +
> +static const struct soc_attr ti_k3_soc_pd_data[] = {
> +#ifdef CONFIG_SOC_K3_J721E
> +	{
> +		.family = "J721E",
> +		.data = &j721e_pd_platdata,
> +	},
> +	{
> +		.family = "J7200",
> +		.data = &j7200_pd_platdata,
> +	},
> +#endif
> +	{ /* sentinel */ }
> +};
> +
> +static int ti_power_domain_probe(struct udevice *dev)
> +{
> +	struct ti_k3_pd_platdata *data = dev_get_priv(dev);
> +	const struct soc_attr *soc_match_data;
> +	const struct ti_k3_pd_platdata *pdata;
> +
> +	printf("%s(dev=%p)\n", __func__, dev);
> +
> +	if (!data)
> +		return -ENOMEM;
> +
> +	soc_match_data = soc_device_match(ti_k3_soc_pd_data);
> +	if (!soc_match_data)
> +		return -ENODEV;
> +
> +	pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
> +
> +	data->psc = pdata->psc;
> +	data->pd = pdata->pd;
> +	data->lpsc = pdata->lpsc;
> +	data->devs = pdata->devs;
> +	data->num_psc = pdata->num_psc;
> +	data->num_pd = pdata->num_pd;
> +	data->num_lpsc = pdata->num_lpsc;
> +	data->num_devs = pdata->num_devs;
> +
> +	return 0;
> +}
> +
> +static int ti_pd_wait(struct ti_pd *pd)
> +{
> +	u32 ptstat;
> +	int i = PD_TIMEOUT;
> +
> +	while (i) {
> +		ptstat = psc_read(pd->psc, PSC_PTSTAT);
> +		if (!(ptstat & BIT(pd->id)))
> +			return 0;
> +		i--;
> +	}

doesn't use readl_pool_timeout() function?

> +
> +	debug("%s: psc%d, pd%d failed to transition.\n", __func__,
> +	      pd->psc->id, pd->id);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static void ti_pd_transition(struct ti_pd *pd)
> +{
> +	psc_write(BIT(pd->id), pd->psc, PSC_PTCMD);
> +}
> +
> +static u8 ti_pd_state(struct ti_pd *pd)
> +{
> +	u32 pdctl;

Not need to use pdctl variable.

return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;

> +
> +	pdctl = pd_read(pd, PSC_PDCTL);
> +	return pdctl & PDCTL_STATE_MASK;
> +}
> +
> +static int ti_pd_get(struct ti_pd *pd)
> +{
> +	u32 pdctl;
> +	int ret;
> +
> +	pd->usecount++;
> +
> +	if (pd->usecount > 1)
> +		return 0;
> +
> +	if (pd->depend) {
> +		ret = ti_pd_get(pd->depend);
> +		if (ret)
> +			return ret;
> +		ti_pd_transition(pd->depend);
> +		ret = ti_pd_wait(pd->depend);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	pdctl = pd_read(pd, PSC_PDCTL);
> +
> +	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
> +		return 0;
> +
> +	debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
> +
> +	pdctl &= ~PDCTL_STATE_MASK;
> +	pdctl |= PDCTL_STATE_ON;
> +
> +	pd_write(pdctl, pd, PSC_PDCTL);
> +
> +	return 0;
> +}
> +
> +static int ti_pd_put(struct ti_pd *pd)
> +{
> +	u32 pdctl;
> +	int ret;
> +
> +	pd->usecount--;
> +
> +	if (pd->usecount > 0)
> +		return 0;
> +
> +	pdctl = pd_read(pd, PSC_PDCTL);
> +	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
> +		return 0;
> +
> +	pdctl &= ~PDCTL_STATE_MASK;
> +	pdctl |= PDCTL_STATE_OFF;
> +
> +	debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
> +
> +	pd_write(pdctl, pd, PSC_PDCTL);
> +
> +	if (pd->depend) {
> +		ti_pd_transition(pd);
> +		ret = ti_pd_wait(pd);
> +		if (ret)
> +			return ret;
> +
> +		ret = ti_pd_put(pd->depend);
> +		if (ret)
> +			return ret;
> +		ti_pd_transition(pd->depend);
> +		ret = ti_pd_wait(pd->depend);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ti_lpsc_wait(struct ti_lpsc *lpsc)
> +{
> +	u32 mdstat;
> +	int i = LPSC_TIMEOUT;
> +
> +	while (i) {
> +		mdstat = lpsc_read(lpsc, PSC_MDSTAT);
> +		if (!(mdstat & MDSTAT_BUSY_MASK))
> +			return 0;
> +		i--;
> +	}
> +
> +	printf("%s: module %d failed to transition.\n", __func__, lpsc->id);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static u8 lpsc_get_state(struct ti_lpsc *lpsc)
> +{
> +	u32 mdctl;
> +
> +	mdctl = lpsc_read(lpsc, PSC_MDCTL);
> +	return mdctl & MDSTAT_STATE_MASK;

Ditto. return lpas_rad() & MDSTATE_STATE_MASK;

> +}
> +
> +static int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
> +{
> +	struct ti_pd *psc_pd;
> +	int ret;
> +	u32 mdctl;
> +
> +	psc_pd = lpsc->pd;
> +
> +	if (state == MDSTAT_STATE_ENABLE) {
> +		lpsc->usecount++;
> +		if (lpsc->usecount > 1)
> +			return 0;
> +	} else {
> +		lpsc->usecount--;
> +		if (lpsc->usecount >= 1)
> +			return 0;
> +	}
> +
> +	debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
> +	      lpsc->psc->id, lpsc->id, state);
> +
> +	if (lpsc->depend)
> +		ti_lpsc_transition(lpsc->depend, state);
> +
> +	mdctl = lpsc_read(lpsc, PSC_MDCTL);
> +	if ((mdctl & MDSTAT_STATE_MASK) == state)
> +		return 0;
> +
> +	if (state == MDSTAT_STATE_ENABLE)
> +		ti_pd_get(psc_pd);
> +	else
> +		ti_pd_put(psc_pd);
> +
> +	mdctl &= ~MDSTAT_STATE_MASK;
> +	mdctl |= state;
> +
> +	lpsc_write(mdctl, lpsc, PSC_MDCTL);
> +
> +	ti_pd_transition(psc_pd);
> +	ret = ti_pd_wait(psc_pd);
> +	if (ret)
> +		return ret;
> +
> +	return ti_lpsc_wait(lpsc);
> +}
> +
> +static int ti_power_domain_transition(struct power_domain *pd, u8 state)
> +{
> +	struct ti_lpsc *lpsc = pd->priv;
> +
> +	return ti_lpsc_transition(lpsc, state);
> +}
> +
> +static int ti_power_domain_on(struct power_domain *pd)
> +{
> +	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
> +
> +	return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
> +}
> +
> +static int ti_power_domain_off(struct power_domain *pd)
> +{
> +	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
> +
> +	return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
> +}
> +
> +static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
> +{
> +	int idx;
> +
> +	for (idx = 0; idx < data->num_devs; idx++)
> +		if (data->devs[idx].id == id)
> +			return data->devs[idx].lpsc;
> +
> +	return NULL;
> +}
> +
> +static int ti_power_domain_of_xlate(struct power_domain *pd,
> +				    struct ofnode_phandle_args *args)
> +{
> +	struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
> +	struct ti_lpsc *lpsc;
> +
> +	debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
> +
> +	if (args->args_count < 1) {
> +		debug("Invalid args_count: %d\n", args->args_count);
> +		return -EINVAL;
> +	}
> +
> +	lpsc = lpsc_lookup(data, args->args[0]);
> +	if (!lpsc) {
> +		printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);

Well, you're using debug to display error message. but it's used printf at here.
how about choosing one of them?

> +		return -ENOENT;
> +	}
> +
> +	pd->id = lpsc->id;
> +	pd->priv = lpsc;
> +
> +	return 0;
> +}
> +
> +static int ti_power_domain_request(struct power_domain *pd)
> +{
> +	return 0;
> +}
> +
> +static int ti_power_domain_free(struct power_domain *pd)
> +{
> +	return 0;
> +}
> +
> +static const struct udevice_id ti_power_domain_of_match[] = {
> +	{ .compatible = "ti,sci-pm-domain" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct power_domain_ops ti_power_domain_ops = {
> +	.on = ti_power_domain_on,
> +	.off = ti_power_domain_off,
> +	.of_xlate = ti_power_domain_of_xlate,
> +	.request = ti_power_domain_request,
> +	.rfree = ti_power_domain_free,
> +};
> +
> +U_BOOT_DRIVER(ti_pm_domains) = {
> +	.name = "ti-pm-domains",
> +	.id = UCLASS_POWER_DOMAIN,
> +	.of_match = ti_power_domain_of_match,
> +	.probe = ti_power_domain_probe,
> +	.priv_auto = sizeof(struct ti_k3_pd_platdata),
> +	.ops = &ti_power_domain_ops,
> +};
> diff --git a/include/k3-dev.h b/include/k3-dev.h
> new file mode 100644
> index 0000000000..de3a8bdf9e
> --- /dev/null
> +++ b/include/k3-dev.h
> @@ -0,0 +1,76 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Texas Instruments K3 Device Platform Data
> + *
> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
> + */
> +#ifndef __K3_DEV_H__
> +#define __K3_DEV_H__
> +
> +#include <asm/io.h>
> +#include <linux/types.h>
> +#include <stdint.h>
> +
> +#define LPSC_MODULE_EXISTS      BIT(0)
> +#define LPSC_NO_CLOCK_GATING    BIT(1)
> +#define LPSC_DEPENDS            BIT(2)
> +#define LPSC_HAS_RESET_ISO      BIT(3)
> +#define LPSC_HAS_LOCAL_RESET    BIT(4)
> +#define LPSC_NO_MODULE_RESET    BIT(5)
> +
> +#define PSC_PD_EXISTS           BIT(0)
> +#define PSC_PD_ALWAYSON         BIT(1)
> +#define PSC_PD_DEPENDS          BIT(2)
> +
> +struct ti_psc {
> +	int id;
> +	void __iomem *base;
> +};
> +
> +struct ti_pd;
> +
> +struct ti_pd {
> +	int id;
> +	int usecount;
> +	struct ti_psc *psc;
> +	struct ti_pd *depend;
> +};
> +
> +struct ti_lpsc;
> +
> +struct ti_lpsc {
> +	int id;
> +	int usecount;
> +	struct ti_psc *psc;
> +	struct ti_pd *pd;
> +	struct ti_lpsc *depend;
> +};
> +
> +struct ti_dev {
> +	struct ti_lpsc *lpsc;
> +	int id;
> +};
> +
> +/**
> + * struct ti_k3_pd_platdata - pm domain controller information structure
> + */
> +struct ti_k3_pd_platdata {
> +	struct ti_psc *psc;
> +	struct ti_pd *pd;
> +	struct ti_lpsc *lpsc;
> +	struct ti_dev *devs;
> +	int num_psc;
> +	int num_pd;
> +	int num_lpsc;
> +	int num_devs;
> +};
> +
> +#define PSC(_id, _base) { .id = _id, .base = (void *)_base, }
> +#define PSC_PD(_id, _psc, _depend) { .id = _id, .psc = _psc, .depend = _depend }
> +#define PSC_LPSC(_id, _psc, _pd, _depend) { .id = _id, .psc = _psc, .pd = _pd, .depend = _depend }
> +#define PSC_DEV(_id, _lpsc) { .id = _id, .lpsc = _lpsc }
> +
> +extern const struct ti_k3_pd_platdata j721e_pd_platdata;
> +extern const struct ti_k3_pd_platdata j7200_pd_platdata;
> +
> +#endif
>
Tero Kristo May 7, 2021, 7:19 a.m. UTC | #2
On 07/05/2021 03:03, Jaehoon Chung wrote:
> Hi Tero,
> 
> On 5/6/21 2:55 AM, Tero Kristo wrote:
>> From: Tero Kristo <t-kristo@ti.com>
>>
>> Normally, power domains are handled via TI-SCI in K3 SoCs. However,
>> SPL is not going to have access to sysfw resources, so it must control
>> them directly. Add driver for supporting this.
>>
>> Signed-off-by: Tero Kristo <t-kristo@ti.com>
>> Signed-off-by: Tero Kristo <kristo@kernel.org>
>> ---
>>   drivers/power/domain/Kconfig           |   7 +
>>   drivers/power/domain/Makefile          |   1 +
>>   drivers/power/domain/ti-power-domain.c | 377 +++++++++++++++++++++++++
>>   include/k3-dev.h                       |  76 +++++
>>   4 files changed, 461 insertions(+)
>>   create mode 100644 drivers/power/domain/ti-power-domain.c
>>   create mode 100644 include/k3-dev.h
>>
>> diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
>> index a0fd980752..b03a82d82c 100644
>> --- a/drivers/power/domain/Kconfig
>> +++ b/drivers/power/domain/Kconfig
>> @@ -72,4 +72,11 @@ config TI_SCI_POWER_DOMAIN
>>   	help
>>   	  Generic power domain implementation for TI devices implementing the
>>   	  TI SCI protocol.
>> +
>> +config TI_POWER_DOMAIN
>> +	bool "Enable the TI K3 Power domain driver"
>> +	depends on POWER_DOMAIN
> 
> Add your ARCH config as "depends on". This is TI specific thing.

Right, will do that.

> 
>> +	help
>> +	  Generic power domain implementation for TI K3 devices.
>> +
>>   endmenu
>> diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
>> index 45bf9f6383..3d1e5f073c 100644
>> --- a/drivers/power/domain/Makefile
>> +++ b/drivers/power/domain/Makefile
>> @@ -14,3 +14,4 @@ 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
>> +obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
>> diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c
>> new file mode 100644
>> index 0000000000..ee2dc698ae
>> --- /dev/null
>> +++ b/drivers/power/domain/ti-power-domain.c
>> @@ -0,0 +1,377 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Texas Instruments power domain driver
>> + *
>> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
> 
> 2021?

Will change to 2020-2021.

> 
>> + *	Tero Kristo <t-kristo@ti.com>
>> + */
>> +
>> +#include <asm/io.h>
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <power-domain-uclass.h>
>> +#include <soc.h>
>> +#include <k3-dev.h>
>> +
>> +#define PSC_PTCMD		0x120
>> +#define PSC_PTSTAT		0x128
>> +#define PSC_PDSTAT		0x200
>> +#define PSC_PDCTL		0x300
>> +#define PSC_MDSTAT		0x800
>> +#define PSC_MDCTL		0xa00
>> +
>> +#define PDCTL_STATE_MASK		0x1
>> +#define PDCTL_STATE_OFF			0x0
>> +#define PDCTL_STATE_ON			0x1
>> +
>> +#define MDSTAT_STATE_MASK		0x3f
>> +#define MDSTAT_BUSY_MASK		0x30
>> +#define MDSTAT_STATE_SWRSTDISABLE	0x0
>> +#define MDSTAT_STATE_ENABLE		0x3
>> +
>> +#define LPSC_TIMEOUT		100000
>> +#define PD_TIMEOUT		100000
>> +
>> +static u32 psc_read(struct ti_psc *psc, u32 reg)
>> +{
>> +	u32 val;
>> +
>> +	val = readl(psc->base + reg);
>> +	debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
>> +	return val;
>> +}
>> +
>> +static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
>> +{
>> +	debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
>> +	writel(val, psc->base + reg);
>> +}
>> +
>> +static u32 pd_read(struct ti_pd *pd, u32 reg)
>> +{
>> +	return psc_read(pd->psc, reg + 4 * pd->id);
>> +}
>> +
>> +static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
>> +{
>> +	psc_write(val, pd->psc, reg + 4 * pd->id);
>> +}
>> +
>> +static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
>> +{
>> +	return psc_read(lpsc->psc, reg + 4 * lpsc->id);
>> +}
>> +
>> +static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
>> +{
>> +	psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
>> +}
>> +
>> +static const struct soc_attr ti_k3_soc_pd_data[] = {
>> +#ifdef CONFIG_SOC_K3_J721E
>> +	{
>> +		.family = "J721E",
>> +		.data = &j721e_pd_platdata,
>> +	},
>> +	{
>> +		.family = "J7200",
>> +		.data = &j7200_pd_platdata,
>> +	},
>> +#endif
>> +	{ /* sentinel */ }
>> +};
>> +
>> +static int ti_power_domain_probe(struct udevice *dev)
>> +{
>> +	struct ti_k3_pd_platdata *data = dev_get_priv(dev);
>> +	const struct soc_attr *soc_match_data;
>> +	const struct ti_k3_pd_platdata *pdata;
>> +
>> +	printf("%s(dev=%p)\n", __func__, dev);
>> +
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	soc_match_data = soc_device_match(ti_k3_soc_pd_data);
>> +	if (!soc_match_data)
>> +		return -ENODEV;
>> +
>> +	pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
>> +
>> +	data->psc = pdata->psc;
>> +	data->pd = pdata->pd;
>> +	data->lpsc = pdata->lpsc;
>> +	data->devs = pdata->devs;
>> +	data->num_psc = pdata->num_psc;
>> +	data->num_pd = pdata->num_pd;
>> +	data->num_lpsc = pdata->num_lpsc;
>> +	data->num_devs = pdata->num_devs;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ti_pd_wait(struct ti_pd *pd)
>> +{
>> +	u32 ptstat;
>> +	int i = PD_TIMEOUT;
>> +
>> +	while (i) {
>> +		ptstat = psc_read(pd->psc, PSC_PTSTAT);
>> +		if (!(ptstat & BIT(pd->id)))
>> +			return 0;
>> +		i--;
>> +	}
> 
> doesn't use readl_pool_timeout() function?

This was based on assumption that the timer would not work very early in 
boot, but seems I was mistaken. It does work so I'll replace this.

There is another poll in the file within the ti_lpsc_wait, will change 
that also.

> 
>> +
>> +	debug("%s: psc%d, pd%d failed to transition.\n", __func__,
>> +	      pd->psc->id, pd->id);
>> +
>> +	return -ETIMEDOUT;
>> +}
>> +
>> +static void ti_pd_transition(struct ti_pd *pd)
>> +{
>> +	psc_write(BIT(pd->id), pd->psc, PSC_PTCMD);
>> +}
>> +
>> +static u8 ti_pd_state(struct ti_pd *pd)
>> +{
>> +	u32 pdctl;
> 
> Not need to use pdctl variable.
> 
> return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;

Sure, will change that.

> 
>> +
>> +	pdctl = pd_read(pd, PSC_PDCTL);
>> +	return pdctl & PDCTL_STATE_MASK;
>> +}
>> +
>> +static int ti_pd_get(struct ti_pd *pd)
>> +{
>> +	u32 pdctl;
>> +	int ret;
>> +
>> +	pd->usecount++;
>> +
>> +	if (pd->usecount > 1)
>> +		return 0;
>> +
>> +	if (pd->depend) {
>> +		ret = ti_pd_get(pd->depend);
>> +		if (ret)
>> +			return ret;
>> +		ti_pd_transition(pd->depend);
>> +		ret = ti_pd_wait(pd->depend);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	pdctl = pd_read(pd, PSC_PDCTL);
>> +
>> +	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
>> +		return 0;
>> +
>> +	debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
>> +
>> +	pdctl &= ~PDCTL_STATE_MASK;
>> +	pdctl |= PDCTL_STATE_ON;
>> +
>> +	pd_write(pdctl, pd, PSC_PDCTL);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ti_pd_put(struct ti_pd *pd)
>> +{
>> +	u32 pdctl;
>> +	int ret;
>> +
>> +	pd->usecount--;
>> +
>> +	if (pd->usecount > 0)
>> +		return 0;
>> +
>> +	pdctl = pd_read(pd, PSC_PDCTL);
>> +	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
>> +		return 0;
>> +
>> +	pdctl &= ~PDCTL_STATE_MASK;
>> +	pdctl |= PDCTL_STATE_OFF;
>> +
>> +	debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
>> +
>> +	pd_write(pdctl, pd, PSC_PDCTL);
>> +
>> +	if (pd->depend) {
>> +		ti_pd_transition(pd);
>> +		ret = ti_pd_wait(pd);
>> +		if (ret)
>> +			return ret;
>> +
>> +		ret = ti_pd_put(pd->depend);
>> +		if (ret)
>> +			return ret;
>> +		ti_pd_transition(pd->depend);
>> +		ret = ti_pd_wait(pd->depend);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int ti_lpsc_wait(struct ti_lpsc *lpsc)
>> +{
>> +	u32 mdstat;
>> +	int i = LPSC_TIMEOUT;
>> +
>> +	while (i) {
>> +		mdstat = lpsc_read(lpsc, PSC_MDSTAT);
>> +		if (!(mdstat & MDSTAT_BUSY_MASK))
>> +			return 0;
>> +		i--;
>> +	}

Here is another manual iopoll to be replaced.

>> +
>> +	printf("%s: module %d failed to transition.\n", __func__, lpsc->id);
>> +
>> +	return -ETIMEDOUT;
>> +}
>> +
>> +static u8 lpsc_get_state(struct ti_lpsc *lpsc)
>> +{
>> +	u32 mdctl;
>> +
>> +	mdctl = lpsc_read(lpsc, PSC_MDCTL);
>> +	return mdctl & MDSTAT_STATE_MASK;
> 
> Ditto. return lpas_rad() & MDSTATE_STATE_MASK;

Will fix that also.

> 
>> +}
>> +
>> +static int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
>> +{
>> +	struct ti_pd *psc_pd;
>> +	int ret;
>> +	u32 mdctl;
>> +
>> +	psc_pd = lpsc->pd;
>> +
>> +	if (state == MDSTAT_STATE_ENABLE) {
>> +		lpsc->usecount++;
>> +		if (lpsc->usecount > 1)
>> +			return 0;
>> +	} else {
>> +		lpsc->usecount--;
>> +		if (lpsc->usecount >= 1)
>> +			return 0;
>> +	}
>> +
>> +	debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
>> +	      lpsc->psc->id, lpsc->id, state);
>> +
>> +	if (lpsc->depend)
>> +		ti_lpsc_transition(lpsc->depend, state);
>> +
>> +	mdctl = lpsc_read(lpsc, PSC_MDCTL);
>> +	if ((mdctl & MDSTAT_STATE_MASK) == state)
>> +		return 0;
>> +
>> +	if (state == MDSTAT_STATE_ENABLE)
>> +		ti_pd_get(psc_pd);
>> +	else
>> +		ti_pd_put(psc_pd);
>> +
>> +	mdctl &= ~MDSTAT_STATE_MASK;
>> +	mdctl |= state;
>> +
>> +	lpsc_write(mdctl, lpsc, PSC_MDCTL);
>> +
>> +	ti_pd_transition(psc_pd);
>> +	ret = ti_pd_wait(psc_pd);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return ti_lpsc_wait(lpsc);
>> +}
>> +
>> +static int ti_power_domain_transition(struct power_domain *pd, u8 state)
>> +{
>> +	struct ti_lpsc *lpsc = pd->priv;
>> +
>> +	return ti_lpsc_transition(lpsc, state);
>> +}
>> +
>> +static int ti_power_domain_on(struct power_domain *pd)
>> +{
>> +	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
>> +
>> +	return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
>> +}
>> +
>> +static int ti_power_domain_off(struct power_domain *pd)
>> +{
>> +	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
>> +
>> +	return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
>> +}
>> +
>> +static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
>> +{
>> +	int idx;
>> +
>> +	for (idx = 0; idx < data->num_devs; idx++)
>> +		if (data->devs[idx].id == id)
>> +			return data->devs[idx].lpsc;
>> +
>> +	return NULL;
>> +}
>> +
>> +static int ti_power_domain_of_xlate(struct power_domain *pd,
>> +				    struct ofnode_phandle_args *args)
>> +{
>> +	struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
>> +	struct ti_lpsc *lpsc;
>> +
>> +	debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
>> +
>> +	if (args->args_count < 1) {
>> +		debug("Invalid args_count: %d\n", args->args_count);
>> +		return -EINVAL;
>> +	}
>> +
>> +	lpsc = lpsc_lookup(data, args->args[0]);
>> +	if (!lpsc) {
>> +		printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
> 
> Well, you're using debug to display error message. but it's used printf at here.
> how about choosing one of them?

True, I'll change these both to be printfs as they are actual error 
conditions.

Thanks for the review, I'll wait for additional comments on the series 
and post updated version next week.

-Tero

> 
>> +		return -ENOENT;
>> +	}
>> +
>> +	pd->id = lpsc->id;
>> +	pd->priv = lpsc;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ti_power_domain_request(struct power_domain *pd)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int ti_power_domain_free(struct power_domain *pd)
>> +{
>> +	return 0;
>> +}
>> +
>> +static const struct udevice_id ti_power_domain_of_match[] = {
>> +	{ .compatible = "ti,sci-pm-domain" },
>> +	{ /* sentinel */ }
>> +};
>> +
>> +static struct power_domain_ops ti_power_domain_ops = {
>> +	.on = ti_power_domain_on,
>> +	.off = ti_power_domain_off,
>> +	.of_xlate = ti_power_domain_of_xlate,
>> +	.request = ti_power_domain_request,
>> +	.rfree = ti_power_domain_free,
>> +};
>> +
>> +U_BOOT_DRIVER(ti_pm_domains) = {
>> +	.name = "ti-pm-domains",
>> +	.id = UCLASS_POWER_DOMAIN,
>> +	.of_match = ti_power_domain_of_match,
>> +	.probe = ti_power_domain_probe,
>> +	.priv_auto = sizeof(struct ti_k3_pd_platdata),
>> +	.ops = &ti_power_domain_ops,
>> +};
>> diff --git a/include/k3-dev.h b/include/k3-dev.h
>> new file mode 100644
>> index 0000000000..de3a8bdf9e
>> --- /dev/null
>> +++ b/include/k3-dev.h
>> @@ -0,0 +1,76 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Texas Instruments K3 Device Platform Data
>> + *
>> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
>> + */
>> +#ifndef __K3_DEV_H__
>> +#define __K3_DEV_H__
>> +
>> +#include <asm/io.h>
>> +#include <linux/types.h>
>> +#include <stdint.h>
>> +
>> +#define LPSC_MODULE_EXISTS      BIT(0)
>> +#define LPSC_NO_CLOCK_GATING    BIT(1)
>> +#define LPSC_DEPENDS            BIT(2)
>> +#define LPSC_HAS_RESET_ISO      BIT(3)
>> +#define LPSC_HAS_LOCAL_RESET    BIT(4)
>> +#define LPSC_NO_MODULE_RESET    BIT(5)
>> +
>> +#define PSC_PD_EXISTS           BIT(0)
>> +#define PSC_PD_ALWAYSON         BIT(1)
>> +#define PSC_PD_DEPENDS          BIT(2)
>> +
>> +struct ti_psc {
>> +	int id;
>> +	void __iomem *base;
>> +};
>> +
>> +struct ti_pd;
>> +
>> +struct ti_pd {
>> +	int id;
>> +	int usecount;
>> +	struct ti_psc *psc;
>> +	struct ti_pd *depend;
>> +};
>> +
>> +struct ti_lpsc;
>> +
>> +struct ti_lpsc {
>> +	int id;
>> +	int usecount;
>> +	struct ti_psc *psc;
>> +	struct ti_pd *pd;
>> +	struct ti_lpsc *depend;
>> +};
>> +
>> +struct ti_dev {
>> +	struct ti_lpsc *lpsc;
>> +	int id;
>> +};
>> +
>> +/**
>> + * struct ti_k3_pd_platdata - pm domain controller information structure
>> + */
>> +struct ti_k3_pd_platdata {
>> +	struct ti_psc *psc;
>> +	struct ti_pd *pd;
>> +	struct ti_lpsc *lpsc;
>> +	struct ti_dev *devs;
>> +	int num_psc;
>> +	int num_pd;
>> +	int num_lpsc;
>> +	int num_devs;
>> +};
>> +
>> +#define PSC(_id, _base) { .id = _id, .base = (void *)_base, }
>> +#define PSC_PD(_id, _psc, _depend) { .id = _id, .psc = _psc, .depend = _depend }
>> +#define PSC_LPSC(_id, _psc, _pd, _depend) { .id = _id, .psc = _psc, .pd = _pd, .depend = _depend }
>> +#define PSC_DEV(_id, _lpsc) { .id = _id, .lpsc = _lpsc }
>> +
>> +extern const struct ti_k3_pd_platdata j721e_pd_platdata;
>> +extern const struct ti_k3_pd_platdata j7200_pd_platdata;
>> +
>> +#endif
>>
>
diff mbox series

Patch

diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index a0fd980752..b03a82d82c 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -72,4 +72,11 @@  config TI_SCI_POWER_DOMAIN
 	help
 	  Generic power domain implementation for TI devices implementing the
 	  TI SCI protocol.
+
+config TI_POWER_DOMAIN
+	bool "Enable the TI K3 Power domain driver"
+	depends on POWER_DOMAIN
+	help
+	  Generic power domain implementation for TI K3 devices.
+
 endmenu
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index 45bf9f6383..3d1e5f073c 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -14,3 +14,4 @@  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
+obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c
new file mode 100644
index 0000000000..ee2dc698ae
--- /dev/null
+++ b/drivers/power/domain/ti-power-domain.c
@@ -0,0 +1,377 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments power domain driver
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ *	Tero Kristo <t-kristo@ti.com>
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <power-domain-uclass.h>
+#include <soc.h>
+#include <k3-dev.h>
+
+#define PSC_PTCMD		0x120
+#define PSC_PTSTAT		0x128
+#define PSC_PDSTAT		0x200
+#define PSC_PDCTL		0x300
+#define PSC_MDSTAT		0x800
+#define PSC_MDCTL		0xa00
+
+#define PDCTL_STATE_MASK		0x1
+#define PDCTL_STATE_OFF			0x0
+#define PDCTL_STATE_ON			0x1
+
+#define MDSTAT_STATE_MASK		0x3f
+#define MDSTAT_BUSY_MASK		0x30
+#define MDSTAT_STATE_SWRSTDISABLE	0x0
+#define MDSTAT_STATE_ENABLE		0x3
+
+#define LPSC_TIMEOUT		100000
+#define PD_TIMEOUT		100000
+
+static u32 psc_read(struct ti_psc *psc, u32 reg)
+{
+	u32 val;
+
+	val = readl(psc->base + reg);
+	debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
+	return val;
+}
+
+static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
+{
+	debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
+	writel(val, psc->base + reg);
+}
+
+static u32 pd_read(struct ti_pd *pd, u32 reg)
+{
+	return psc_read(pd->psc, reg + 4 * pd->id);
+}
+
+static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
+{
+	psc_write(val, pd->psc, reg + 4 * pd->id);
+}
+
+static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
+{
+	return psc_read(lpsc->psc, reg + 4 * lpsc->id);
+}
+
+static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
+{
+	psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
+}
+
+static const struct soc_attr ti_k3_soc_pd_data[] = {
+#ifdef CONFIG_SOC_K3_J721E
+	{
+		.family = "J721E",
+		.data = &j721e_pd_platdata,
+	},
+	{
+		.family = "J7200",
+		.data = &j7200_pd_platdata,
+	},
+#endif
+	{ /* sentinel */ }
+};
+
+static int ti_power_domain_probe(struct udevice *dev)
+{
+	struct ti_k3_pd_platdata *data = dev_get_priv(dev);
+	const struct soc_attr *soc_match_data;
+	const struct ti_k3_pd_platdata *pdata;
+
+	printf("%s(dev=%p)\n", __func__, dev);
+
+	if (!data)
+		return -ENOMEM;
+
+	soc_match_data = soc_device_match(ti_k3_soc_pd_data);
+	if (!soc_match_data)
+		return -ENODEV;
+
+	pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
+
+	data->psc = pdata->psc;
+	data->pd = pdata->pd;
+	data->lpsc = pdata->lpsc;
+	data->devs = pdata->devs;
+	data->num_psc = pdata->num_psc;
+	data->num_pd = pdata->num_pd;
+	data->num_lpsc = pdata->num_lpsc;
+	data->num_devs = pdata->num_devs;
+
+	return 0;
+}
+
+static int ti_pd_wait(struct ti_pd *pd)
+{
+	u32 ptstat;
+	int i = PD_TIMEOUT;
+
+	while (i) {
+		ptstat = psc_read(pd->psc, PSC_PTSTAT);
+		if (!(ptstat & BIT(pd->id)))
+			return 0;
+		i--;
+	}
+
+	debug("%s: psc%d, pd%d failed to transition.\n", __func__,
+	      pd->psc->id, pd->id);
+
+	return -ETIMEDOUT;
+}
+
+static void ti_pd_transition(struct ti_pd *pd)
+{
+	psc_write(BIT(pd->id), pd->psc, PSC_PTCMD);
+}
+
+static u8 ti_pd_state(struct ti_pd *pd)
+{
+	u32 pdctl;
+
+	pdctl = pd_read(pd, PSC_PDCTL);
+	return pdctl & PDCTL_STATE_MASK;
+}
+
+static int ti_pd_get(struct ti_pd *pd)
+{
+	u32 pdctl;
+	int ret;
+
+	pd->usecount++;
+
+	if (pd->usecount > 1)
+		return 0;
+
+	if (pd->depend) {
+		ret = ti_pd_get(pd->depend);
+		if (ret)
+			return ret;
+		ti_pd_transition(pd->depend);
+		ret = ti_pd_wait(pd->depend);
+		if (ret)
+			return ret;
+	}
+
+	pdctl = pd_read(pd, PSC_PDCTL);
+
+	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
+		return 0;
+
+	debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
+
+	pdctl &= ~PDCTL_STATE_MASK;
+	pdctl |= PDCTL_STATE_ON;
+
+	pd_write(pdctl, pd, PSC_PDCTL);
+
+	return 0;
+}
+
+static int ti_pd_put(struct ti_pd *pd)
+{
+	u32 pdctl;
+	int ret;
+
+	pd->usecount--;
+
+	if (pd->usecount > 0)
+		return 0;
+
+	pdctl = pd_read(pd, PSC_PDCTL);
+	if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
+		return 0;
+
+	pdctl &= ~PDCTL_STATE_MASK;
+	pdctl |= PDCTL_STATE_OFF;
+
+	debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
+
+	pd_write(pdctl, pd, PSC_PDCTL);
+
+	if (pd->depend) {
+		ti_pd_transition(pd);
+		ret = ti_pd_wait(pd);
+		if (ret)
+			return ret;
+
+		ret = ti_pd_put(pd->depend);
+		if (ret)
+			return ret;
+		ti_pd_transition(pd->depend);
+		ret = ti_pd_wait(pd->depend);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ti_lpsc_wait(struct ti_lpsc *lpsc)
+{
+	u32 mdstat;
+	int i = LPSC_TIMEOUT;
+
+	while (i) {
+		mdstat = lpsc_read(lpsc, PSC_MDSTAT);
+		if (!(mdstat & MDSTAT_BUSY_MASK))
+			return 0;
+		i--;
+	}
+
+	printf("%s: module %d failed to transition.\n", __func__, lpsc->id);
+
+	return -ETIMEDOUT;
+}
+
+static u8 lpsc_get_state(struct ti_lpsc *lpsc)
+{
+	u32 mdctl;
+
+	mdctl = lpsc_read(lpsc, PSC_MDCTL);
+	return mdctl & MDSTAT_STATE_MASK;
+}
+
+static int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
+{
+	struct ti_pd *psc_pd;
+	int ret;
+	u32 mdctl;
+
+	psc_pd = lpsc->pd;
+
+	if (state == MDSTAT_STATE_ENABLE) {
+		lpsc->usecount++;
+		if (lpsc->usecount > 1)
+			return 0;
+	} else {
+		lpsc->usecount--;
+		if (lpsc->usecount >= 1)
+			return 0;
+	}
+
+	debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
+	      lpsc->psc->id, lpsc->id, state);
+
+	if (lpsc->depend)
+		ti_lpsc_transition(lpsc->depend, state);
+
+	mdctl = lpsc_read(lpsc, PSC_MDCTL);
+	if ((mdctl & MDSTAT_STATE_MASK) == state)
+		return 0;
+
+	if (state == MDSTAT_STATE_ENABLE)
+		ti_pd_get(psc_pd);
+	else
+		ti_pd_put(psc_pd);
+
+	mdctl &= ~MDSTAT_STATE_MASK;
+	mdctl |= state;
+
+	lpsc_write(mdctl, lpsc, PSC_MDCTL);
+
+	ti_pd_transition(psc_pd);
+	ret = ti_pd_wait(psc_pd);
+	if (ret)
+		return ret;
+
+	return ti_lpsc_wait(lpsc);
+}
+
+static int ti_power_domain_transition(struct power_domain *pd, u8 state)
+{
+	struct ti_lpsc *lpsc = pd->priv;
+
+	return ti_lpsc_transition(lpsc, state);
+}
+
+static int ti_power_domain_on(struct power_domain *pd)
+{
+	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
+
+	return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
+}
+
+static int ti_power_domain_off(struct power_domain *pd)
+{
+	debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
+
+	return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
+}
+
+static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
+{
+	int idx;
+
+	for (idx = 0; idx < data->num_devs; idx++)
+		if (data->devs[idx].id == id)
+			return data->devs[idx].lpsc;
+
+	return NULL;
+}
+
+static int ti_power_domain_of_xlate(struct power_domain *pd,
+				    struct ofnode_phandle_args *args)
+{
+	struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
+	struct ti_lpsc *lpsc;
+
+	debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
+
+	if (args->args_count < 1) {
+		debug("Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	lpsc = lpsc_lookup(data, args->args[0]);
+	if (!lpsc) {
+		printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
+		return -ENOENT;
+	}
+
+	pd->id = lpsc->id;
+	pd->priv = lpsc;
+
+	return 0;
+}
+
+static int ti_power_domain_request(struct power_domain *pd)
+{
+	return 0;
+}
+
+static int ti_power_domain_free(struct power_domain *pd)
+{
+	return 0;
+}
+
+static const struct udevice_id ti_power_domain_of_match[] = {
+	{ .compatible = "ti,sci-pm-domain" },
+	{ /* sentinel */ }
+};
+
+static struct power_domain_ops ti_power_domain_ops = {
+	.on = ti_power_domain_on,
+	.off = ti_power_domain_off,
+	.of_xlate = ti_power_domain_of_xlate,
+	.request = ti_power_domain_request,
+	.rfree = ti_power_domain_free,
+};
+
+U_BOOT_DRIVER(ti_pm_domains) = {
+	.name = "ti-pm-domains",
+	.id = UCLASS_POWER_DOMAIN,
+	.of_match = ti_power_domain_of_match,
+	.probe = ti_power_domain_probe,
+	.priv_auto = sizeof(struct ti_k3_pd_platdata),
+	.ops = &ti_power_domain_ops,
+};
diff --git a/include/k3-dev.h b/include/k3-dev.h
new file mode 100644
index 0000000000..de3a8bdf9e
--- /dev/null
+++ b/include/k3-dev.h
@@ -0,0 +1,76 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Texas Instruments K3 Device Platform Data
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ */
+#ifndef __K3_DEV_H__
+#define __K3_DEV_H__
+
+#include <asm/io.h>
+#include <linux/types.h>
+#include <stdint.h>
+
+#define LPSC_MODULE_EXISTS      BIT(0)
+#define LPSC_NO_CLOCK_GATING    BIT(1)
+#define LPSC_DEPENDS            BIT(2)
+#define LPSC_HAS_RESET_ISO      BIT(3)
+#define LPSC_HAS_LOCAL_RESET    BIT(4)
+#define LPSC_NO_MODULE_RESET    BIT(5)
+
+#define PSC_PD_EXISTS           BIT(0)
+#define PSC_PD_ALWAYSON         BIT(1)
+#define PSC_PD_DEPENDS          BIT(2)
+
+struct ti_psc {
+	int id;
+	void __iomem *base;
+};
+
+struct ti_pd;
+
+struct ti_pd {
+	int id;
+	int usecount;
+	struct ti_psc *psc;
+	struct ti_pd *depend;
+};
+
+struct ti_lpsc;
+
+struct ti_lpsc {
+	int id;
+	int usecount;
+	struct ti_psc *psc;
+	struct ti_pd *pd;
+	struct ti_lpsc *depend;
+};
+
+struct ti_dev {
+	struct ti_lpsc *lpsc;
+	int id;
+};
+
+/**
+ * struct ti_k3_pd_platdata - pm domain controller information structure
+ */
+struct ti_k3_pd_platdata {
+	struct ti_psc *psc;
+	struct ti_pd *pd;
+	struct ti_lpsc *lpsc;
+	struct ti_dev *devs;
+	int num_psc;
+	int num_pd;
+	int num_lpsc;
+	int num_devs;
+};
+
+#define PSC(_id, _base) { .id = _id, .base = (void *)_base, }
+#define PSC_PD(_id, _psc, _depend) { .id = _id, .psc = _psc, .depend = _depend }
+#define PSC_LPSC(_id, _psc, _pd, _depend) { .id = _id, .psc = _psc, .pd = _pd, .depend = _depend }
+#define PSC_DEV(_id, _lpsc) { .id = _id, .lpsc = _lpsc }
+
+extern const struct ti_k3_pd_platdata j721e_pd_platdata;
+extern const struct ti_k3_pd_platdata j7200_pd_platdata;
+
+#endif