Commit efa5c01c authored by Tero Kristo's avatar Tero Kristo Committed by Santosh Shilimkar

soc: ti: ti_sci_pm_domains: switch to use multiple genpds instead of one

Current implementation of the genpd support over TI SCI uses a single
genpd across the whole SoC, and attaches multiple devices to this. This
solution has its drawbacks, like it is currently impossible to attach
more than one power domain to a device; the core genpd implementation
requires one genpd per power-domain entry in DT for a single device.
Also, some devices like USB apparently require their own genpd during
probe time, the current shared approach in use does not work at all.

Switch the implementation over to use a single genpd per power domain
entry in DT. The domains are registered with the onecell approach, but
we also add our own xlate service due to recent introduction of the
extended flag for TI SCI PM domains; genpd core xlate service requires
a single cell per powerdomain, but we are using two cells.
Signed-off-by: default avatarTero Kristo <t-kristo@ti.com>
Signed-off-by: default avatarSantosh Shilimkar <santosh.shilimkar@oracle.com>
parent 557003a9
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
...@@ -18,150 +17,95 @@ ...@@ -18,150 +17,95 @@
#include <dt-bindings/soc/ti,sci_pm_domain.h> #include <dt-bindings/soc/ti,sci_pm_domain.h>
/** /**
* struct ti_sci_genpd_dev_data: holds data needed for every device attached * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data
* to this genpd * @ti_sci: handle to TI SCI protocol driver that provides ops to
* @idx: index of the device that identifies it with the system * communicate with system control processor.
* control processor. * @dev: pointer to dev for the driver for devm allocs
* @exclusive: Permissions for exclusive request or shared request of the * @pd_list: list of all the power domains on the device
* device. * @data: onecell data for genpd core
*/ */
struct ti_sci_genpd_dev_data { struct ti_sci_genpd_provider {
int idx; const struct ti_sci_handle *ti_sci;
u8 exclusive; struct device *dev;
struct list_head pd_list;
struct genpd_onecell_data data;
}; };
/** /**
* struct ti_sci_pm_domain: TI specific data needed for power domain * struct ti_sci_pm_domain: TI specific data needed for power domain
* @ti_sci: handle to TI SCI protocol driver that provides ops to * @idx: index of the device that identifies it with the system
* communicate with system control processor. * control processor.
* @dev: pointer to dev for the driver for devm allocs * @exclusive: Permissions for exclusive request or shared request of the
* device.
* @pd: generic_pm_domain for use with the genpd framework * @pd: generic_pm_domain for use with the genpd framework
* @node: link for the genpd list
* @parent: link to the parent TI SCI genpd provider
*/ */
struct ti_sci_pm_domain { struct ti_sci_pm_domain {
const struct ti_sci_handle *ti_sci; int idx;
struct device *dev; u8 exclusive;
struct generic_pm_domain pd; struct generic_pm_domain pd;
struct list_head node;
struct ti_sci_genpd_provider *parent;
}; };
#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
/** /*
* ti_sci_dev_id(): get prepopulated ti_sci id from struct dev * ti_sci_pd_power_off(): genpd power down hook
* @dev: pointer to device associated with this genpd * @domain: pointer to the powerdomain to power off
*
* Returns device_id stored from ti,sci_id property
*/
static int ti_sci_dev_id(struct device *dev)
{
struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
return sci_dev_data->idx;
}
static u8 is_ti_sci_dev_exclusive(struct device *dev)
{
struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
return sci_dev_data->exclusive;
}
/**
* ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
* @dev: pointer to device associated with this genpd
*
* Returns ti_sci_handle to be used to communicate with system
* control processor.
*/ */
static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev) static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
{ {
struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain); struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd); const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
return ti_sci_genpd->ti_sci; return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
} }
/** /*
* ti_sci_dev_start(): genpd device start hook called to turn device on * ti_sci_pd_power_on(): genpd power up hook
* @dev: pointer to device associated with this genpd to be powered on * @domain: pointer to the powerdomain to power on
*/ */
static int ti_sci_dev_start(struct device *dev) static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
{ {
const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
int idx = ti_sci_dev_id(dev); const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
if (is_ti_sci_dev_exclusive(dev)) if (pd->exclusive)
return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx); return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci,
pd->idx);
else else
return ti_sci->ops.dev_ops.get_device(ti_sci, idx); return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
} }
/** /*
* ti_sci_dev_stop(): genpd device stop hook called to turn device off * ti_sci_pd_xlate(): translation service for TI SCI genpds
* @dev: pointer to device associated with this genpd to be powered off * @genpdspec: DT identification data for the genpd
* @data: genpd core data for all the powerdomains on the device
*/ */
static int ti_sci_dev_stop(struct device *dev) static struct generic_pm_domain *ti_sci_pd_xlate(
struct of_phandle_args *genpdspec,
void *data)
{ {
const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); struct genpd_onecell_data *genpd_data = data;
int idx = ti_sci_dev_id(dev); unsigned int idx = genpdspec->args[0];
return ti_sci->ops.dev_ops.put_device(ti_sci, idx); if (genpdspec->args_count < 2)
} return ERR_PTR(-EINVAL);
static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, if (idx >= genpd_data->num_domains) {
struct device *dev) pr_err("%s: invalid domain index %u\n", __func__, idx);
{ return ERR_PTR(-EINVAL);
struct device_node *np = dev->of_node; }
struct of_phandle_args pd_args;
struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
struct ti_sci_genpd_dev_data *sci_dev_data;
struct generic_pm_domain_data *genpd_data;
int idx, ret = 0;
ret = of_parse_phandle_with_args(np, "power-domains",
"#power-domain-cells", 0, &pd_args);
if (ret < 0)
return ret;
if (pd_args.args_count != 1 && pd_args.args_count != 2)
return -EINVAL;
idx = pd_args.args[0];
/*
* Check the validity of the requested idx, if the index is not valid
* the PMMC will return a NAK here and we will not allocate it.
*/
ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
if (ret)
return -EINVAL;
sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
if (!sci_dev_data)
return -ENOMEM;
sci_dev_data->idx = idx; if (!genpd_data->domains[idx])
/* Enable the exclusive permissions by default */ return ERR_PTR(-ENOENT);
sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE;
if (pd_args.args_count == 2)
sci_dev_data->exclusive = pd_args.args[1] & 0x1;
genpd_data = dev_gpd_data(dev); genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
genpd_data->data = sci_dev_data; genpdspec->args[1];
return 0; return genpd_data->domains[idx];
}
static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
struct device *dev)
{
struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
kfree(sci_dev_data);
genpd_data->data = NULL;
} }
static const struct of_device_id ti_sci_pm_domain_matches[] = { static const struct of_device_id ti_sci_pm_domain_matches[] = {
...@@ -173,33 +117,80 @@ MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); ...@@ -173,33 +117,80 @@ MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
static int ti_sci_pm_domain_probe(struct platform_device *pdev) static int ti_sci_pm_domain_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; struct ti_sci_genpd_provider *pd_provider;
struct ti_sci_pm_domain *ti_sci_pd; struct ti_sci_pm_domain *pd;
struct device_node *np = NULL;
struct of_phandle_args args;
int ret; int ret;
u32 max_id = 0;
int index;
ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL); pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL);
if (!ti_sci_pd) if (!pd_provider)
return -ENOMEM; return -ENOMEM;
ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev); pd_provider->ti_sci = devm_ti_sci_get_handle(dev);
if (IS_ERR(ti_sci_pd->ti_sci)) if (IS_ERR(pd_provider->ti_sci))
return PTR_ERR(ti_sci_pd->ti_sci); return PTR_ERR(pd_provider->ti_sci);
pd_provider->dev = dev;
INIT_LIST_HEAD(&pd_provider->pd_list);
/* Find highest device ID used for power domains */
while (1) {
np = of_find_node_with_property(np, "power-domains");
if (!np)
break;
index = 0;
while (1) {
ret = of_parse_phandle_with_args(np, "power-domains",
"#power-domain-cells",
index, &args);
if (ret)
break;
if (args.args_count >= 1 && args.np == dev->of_node) {
if (args.args[0] > max_id)
max_id = args.args[0];
pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
pd->pd.name = devm_kasprintf(dev, GFP_KERNEL,
"pd:%d",
args.args[0]);
if (!pd->pd.name)
return -ENOMEM;
ti_sci_pd->dev = dev; pd->pd.power_off = ti_sci_pd_power_off;
pd->pd.power_on = ti_sci_pd_power_on;
pd->idx = args.args[0];
pd->parent = pd_provider;
ti_sci_pd->pd.name = "ti_sci_pd"; pm_genpd_init(&pd->pd, NULL, true);
ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev; list_add(&pd->node, &pd_provider->pd_list);
ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev; }
index++;
}
}
ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start; pd_provider->data.domains =
ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop; devm_kcalloc(dev, max_id + 1,
sizeof(*pd_provider->data.domains),
GFP_KERNEL);
pm_genpd_init(&ti_sci_pd->pd, NULL, true); pd_provider->data.num_domains = max_id + 1;
pd_provider->data.xlate = ti_sci_pd_xlate;
ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd); list_for_each_entry(pd, &pd_provider->pd_list, node)
pd_provider->data.domains[pd->idx] = &pd->pd;
return ret; return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
} }
static struct platform_driver ti_sci_pm_domains_driver = { static struct platform_driver ti_sci_pm_domains_driver = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment