ti_sci_pm_domains.c (5670B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * TI SCI Generic Power Domain Driver 4 * 5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ 6 * J Keerthy <j-keerthy@ti.com> 7 * Dave Gerlach <d-gerlach@ti.com> 8 */ 9 10#include <linux/err.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/platform_device.h> 14#include <linux/pm_domain.h> 15#include <linux/slab.h> 16#include <linux/soc/ti/ti_sci_protocol.h> 17#include <dt-bindings/soc/ti,sci_pm_domain.h> 18 19/** 20 * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data 21 * @ti_sci: handle to TI SCI protocol driver that provides ops to 22 * communicate with system control processor. 23 * @dev: pointer to dev for the driver for devm allocs 24 * @pd_list: list of all the power domains on the device 25 * @data: onecell data for genpd core 26 */ 27struct ti_sci_genpd_provider { 28 const struct ti_sci_handle *ti_sci; 29 struct device *dev; 30 struct list_head pd_list; 31 struct genpd_onecell_data data; 32}; 33 34/** 35 * struct ti_sci_pm_domain: TI specific data needed for power domain 36 * @idx: index of the device that identifies it with the system 37 * control processor. 38 * @exclusive: Permissions for exclusive request or shared request of the 39 * device. 40 * @pd: generic_pm_domain for use with the genpd framework 41 * @node: link for the genpd list 42 * @parent: link to the parent TI SCI genpd provider 43 */ 44struct ti_sci_pm_domain { 45 int idx; 46 u8 exclusive; 47 struct generic_pm_domain pd; 48 struct list_head node; 49 struct ti_sci_genpd_provider *parent; 50}; 51 52#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) 53 54/* 55 * ti_sci_pd_power_off(): genpd power down hook 56 * @domain: pointer to the powerdomain to power off 57 */ 58static int ti_sci_pd_power_off(struct generic_pm_domain *domain) 59{ 60 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); 61 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 62 63 return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx); 64} 65 66/* 67 * ti_sci_pd_power_on(): genpd power up hook 68 * @domain: pointer to the powerdomain to power on 69 */ 70static int ti_sci_pd_power_on(struct generic_pm_domain *domain) 71{ 72 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); 73 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 74 75 if (pd->exclusive) 76 return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, 77 pd->idx); 78 else 79 return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx); 80} 81 82/* 83 * ti_sci_pd_xlate(): translation service for TI SCI genpds 84 * @genpdspec: DT identification data for the genpd 85 * @data: genpd core data for all the powerdomains on the device 86 */ 87static struct generic_pm_domain *ti_sci_pd_xlate( 88 struct of_phandle_args *genpdspec, 89 void *data) 90{ 91 struct genpd_onecell_data *genpd_data = data; 92 unsigned int idx = genpdspec->args[0]; 93 94 if (genpdspec->args_count != 1 && genpdspec->args_count != 2) 95 return ERR_PTR(-EINVAL); 96 97 if (idx >= genpd_data->num_domains) { 98 pr_err("%s: invalid domain index %u\n", __func__, idx); 99 return ERR_PTR(-EINVAL); 100 } 101 102 if (!genpd_data->domains[idx]) 103 return ERR_PTR(-ENOENT); 104 105 genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive = 106 genpdspec->args[1]; 107 108 return genpd_data->domains[idx]; 109} 110 111static const struct of_device_id ti_sci_pm_domain_matches[] = { 112 { .compatible = "ti,sci-pm-domain", }, 113 { }, 114}; 115MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); 116 117static int ti_sci_pm_domain_probe(struct platform_device *pdev) 118{ 119 struct device *dev = &pdev->dev; 120 struct ti_sci_genpd_provider *pd_provider; 121 struct ti_sci_pm_domain *pd; 122 struct device_node *np = NULL; 123 struct of_phandle_args args; 124 int ret; 125 u32 max_id = 0; 126 int index; 127 128 pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL); 129 if (!pd_provider) 130 return -ENOMEM; 131 132 pd_provider->ti_sci = devm_ti_sci_get_handle(dev); 133 if (IS_ERR(pd_provider->ti_sci)) 134 return PTR_ERR(pd_provider->ti_sci); 135 136 pd_provider->dev = dev; 137 138 INIT_LIST_HEAD(&pd_provider->pd_list); 139 140 /* Find highest device ID used for power domains */ 141 while (1) { 142 np = of_find_node_with_property(np, "power-domains"); 143 if (!np) 144 break; 145 146 index = 0; 147 148 while (1) { 149 ret = of_parse_phandle_with_args(np, "power-domains", 150 "#power-domain-cells", 151 index, &args); 152 if (ret) 153 break; 154 155 if (args.args_count >= 1 && args.np == dev->of_node) { 156 if (args.args[0] > max_id) 157 max_id = args.args[0]; 158 159 pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); 160 if (!pd) 161 return -ENOMEM; 162 163 pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, 164 "pd:%d", 165 args.args[0]); 166 if (!pd->pd.name) 167 return -ENOMEM; 168 169 pd->pd.power_off = ti_sci_pd_power_off; 170 pd->pd.power_on = ti_sci_pd_power_on; 171 pd->idx = args.args[0]; 172 pd->parent = pd_provider; 173 174 pm_genpd_init(&pd->pd, NULL, true); 175 176 list_add(&pd->node, &pd_provider->pd_list); 177 } 178 index++; 179 } 180 } 181 182 pd_provider->data.domains = 183 devm_kcalloc(dev, max_id + 1, 184 sizeof(*pd_provider->data.domains), 185 GFP_KERNEL); 186 if (!pd_provider->data.domains) 187 return -ENOMEM; 188 189 pd_provider->data.num_domains = max_id + 1; 190 pd_provider->data.xlate = ti_sci_pd_xlate; 191 192 list_for_each_entry(pd, &pd_provider->pd_list, node) 193 pd_provider->data.domains[pd->idx] = &pd->pd; 194 195 return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data); 196} 197 198static struct platform_driver ti_sci_pm_domains_driver = { 199 .probe = ti_sci_pm_domain_probe, 200 .driver = { 201 .name = "ti_sci_pm_domains", 202 .of_match_table = ti_sci_pm_domain_matches, 203 }, 204}; 205module_platform_driver(ti_sci_pm_domains_driver); 206MODULE_LICENSE("GPL v2"); 207MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver"); 208MODULE_AUTHOR("Dave Gerlach");