Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI SCI Generic Power Domain Driver
0004  *
0005  * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
0006  *  J Keerthy <j-keerthy@ti.com>
0007  *  Dave Gerlach <d-gerlach@ti.com>
0008  */
0009 
0010 #include <linux/err.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/pm_domain.h>
0015 #include <linux/slab.h>
0016 #include <linux/soc/ti/ti_sci_protocol.h>
0017 #include <dt-bindings/soc/ti,sci_pm_domain.h>
0018 
0019 /**
0020  * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data
0021  * @ti_sci: handle to TI SCI protocol driver that provides ops to
0022  *      communicate with system control processor.
0023  * @dev: pointer to dev for the driver for devm allocs
0024  * @pd_list: list of all the power domains on the device
0025  * @data: onecell data for genpd core
0026  */
0027 struct ti_sci_genpd_provider {
0028     const struct ti_sci_handle *ti_sci;
0029     struct device *dev;
0030     struct list_head pd_list;
0031     struct genpd_onecell_data data;
0032 };
0033 
0034 /**
0035  * struct ti_sci_pm_domain: TI specific data needed for power domain
0036  * @idx: index of the device that identifies it with the system
0037  *   control processor.
0038  * @exclusive: Permissions for exclusive request or shared request of the
0039  *         device.
0040  * @pd: generic_pm_domain for use with the genpd framework
0041  * @node: link for the genpd list
0042  * @parent: link to the parent TI SCI genpd provider
0043  */
0044 struct ti_sci_pm_domain {
0045     int idx;
0046     u8 exclusive;
0047     struct generic_pm_domain pd;
0048     struct list_head node;
0049     struct ti_sci_genpd_provider *parent;
0050 };
0051 
0052 #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
0053 
0054 /*
0055  * ti_sci_pd_power_off(): genpd power down hook
0056  * @domain: pointer to the powerdomain to power off
0057  */
0058 static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
0059 {
0060     struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
0061     const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
0062 
0063     return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
0064 }
0065 
0066 /*
0067  * ti_sci_pd_power_on(): genpd power up hook
0068  * @domain: pointer to the powerdomain to power on
0069  */
0070 static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
0071 {
0072     struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
0073     const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
0074 
0075     if (pd->exclusive)
0076         return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci,
0077                                 pd->idx);
0078     else
0079         return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
0080 }
0081 
0082 /*
0083  * ti_sci_pd_xlate(): translation service for TI SCI genpds
0084  * @genpdspec: DT identification data for the genpd
0085  * @data: genpd core data for all the powerdomains on the device
0086  */
0087 static struct generic_pm_domain *ti_sci_pd_xlate(
0088                     struct of_phandle_args *genpdspec,
0089                     void *data)
0090 {
0091     struct genpd_onecell_data *genpd_data = data;
0092     unsigned int idx = genpdspec->args[0];
0093 
0094     if (genpdspec->args_count != 1 && genpdspec->args_count != 2)
0095         return ERR_PTR(-EINVAL);
0096 
0097     if (idx >= genpd_data->num_domains) {
0098         pr_err("%s: invalid domain index %u\n", __func__, idx);
0099         return ERR_PTR(-EINVAL);
0100     }
0101 
0102     if (!genpd_data->domains[idx])
0103         return ERR_PTR(-ENOENT);
0104 
0105     genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
0106         genpdspec->args[1];
0107 
0108     return genpd_data->domains[idx];
0109 }
0110 
0111 static const struct of_device_id ti_sci_pm_domain_matches[] = {
0112     { .compatible = "ti,sci-pm-domain", },
0113     { },
0114 };
0115 MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
0116 
0117 static int ti_sci_pm_domain_probe(struct platform_device *pdev)
0118 {
0119     struct device *dev = &pdev->dev;
0120     struct ti_sci_genpd_provider *pd_provider;
0121     struct ti_sci_pm_domain *pd;
0122     struct device_node *np = NULL;
0123     struct of_phandle_args args;
0124     int ret;
0125     u32 max_id = 0;
0126     int index;
0127 
0128     pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL);
0129     if (!pd_provider)
0130         return -ENOMEM;
0131 
0132     pd_provider->ti_sci = devm_ti_sci_get_handle(dev);
0133     if (IS_ERR(pd_provider->ti_sci))
0134         return PTR_ERR(pd_provider->ti_sci);
0135 
0136     pd_provider->dev = dev;
0137 
0138     INIT_LIST_HEAD(&pd_provider->pd_list);
0139 
0140     /* Find highest device ID used for power domains */
0141     while (1) {
0142         np = of_find_node_with_property(np, "power-domains");
0143         if (!np)
0144             break;
0145 
0146         index = 0;
0147 
0148         while (1) {
0149             ret = of_parse_phandle_with_args(np, "power-domains",
0150                              "#power-domain-cells",
0151                              index, &args);
0152             if (ret)
0153                 break;
0154 
0155             if (args.args_count >= 1 && args.np == dev->of_node) {
0156                 if (args.args[0] > max_id)
0157                     max_id = args.args[0];
0158 
0159                 pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
0160                 if (!pd)
0161                     return -ENOMEM;
0162 
0163                 pd->pd.name = devm_kasprintf(dev, GFP_KERNEL,
0164                                  "pd:%d",
0165                                  args.args[0]);
0166                 if (!pd->pd.name)
0167                     return -ENOMEM;
0168 
0169                 pd->pd.power_off = ti_sci_pd_power_off;
0170                 pd->pd.power_on = ti_sci_pd_power_on;
0171                 pd->idx = args.args[0];
0172                 pd->parent = pd_provider;
0173 
0174                 pm_genpd_init(&pd->pd, NULL, true);
0175 
0176                 list_add(&pd->node, &pd_provider->pd_list);
0177             }
0178             index++;
0179         }
0180     }
0181 
0182     pd_provider->data.domains =
0183         devm_kcalloc(dev, max_id + 1,
0184                  sizeof(*pd_provider->data.domains),
0185                  GFP_KERNEL);
0186     if (!pd_provider->data.domains)
0187         return -ENOMEM;
0188 
0189     pd_provider->data.num_domains = max_id + 1;
0190     pd_provider->data.xlate = ti_sci_pd_xlate;
0191 
0192     list_for_each_entry(pd, &pd_provider->pd_list, node)
0193         pd_provider->data.domains[pd->idx] = &pd->pd;
0194 
0195     return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
0196 }
0197 
0198 static struct platform_driver ti_sci_pm_domains_driver = {
0199     .probe = ti_sci_pm_domain_probe,
0200     .driver = {
0201         .name = "ti_sci_pm_domains",
0202         .of_match_table = ti_sci_pm_domain_matches,
0203     },
0204 };
0205 module_platform_driver(ti_sci_pm_domains_driver);
0206 MODULE_LICENSE("GPL v2");
0207 MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
0208 MODULE_AUTHOR("Dave Gerlach");