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/pm_qos.h> 16 #include <linux/pm_runtime.h> 17 #include <linux/slab.h> 18 #include <linux/soc/ti/ti_sci_protocol.h> 19 #include <dt-bindings/soc/ti,sci_pm_domain.h> 20 21 /** 22 * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data 23 * @ti_sci: handle to TI SCI protocol driver that provides ops to 24 * communicate with system control processor. 25 * @dev: pointer to dev for the driver for devm allocs 26 * @pd_list: list of all the power domains on the device 27 * @data: onecell data for genpd core 28 */ 29 struct ti_sci_genpd_provider { 30 const struct ti_sci_handle *ti_sci; 31 struct device *dev; 32 struct list_head pd_list; 33 struct genpd_onecell_data data; 34 }; 35 36 /** 37 * struct ti_sci_pm_domain: TI specific data needed for power domain 38 * @idx: index of the device that identifies it with the system 39 * control processor. 40 * @exclusive: Permissions for exclusive request or shared request of the 41 * device. 42 * @pd: generic_pm_domain for use with the genpd framework 43 * @node: link for the genpd list 44 * @parent: link to the parent TI SCI genpd provider 45 */ 46 struct ti_sci_pm_domain { 47 int idx; 48 u8 exclusive; 49 struct generic_pm_domain pd; 50 struct list_head node; 51 struct ti_sci_genpd_provider *parent; 52 }; 53 54 #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) 55 56 static inline bool ti_sci_pd_is_valid_constraint(s32 val) 57 { 58 return val != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; 59 } 60 61 #ifdef CONFIG_PM_SLEEP 62 static void ti_sci_pd_set_lat_constraint(struct device *dev, s32 val) 63 { 64 struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); 65 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd); 66 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 67 u16 val_ms; 68 int ret; 69 70 /* PM QoS latency unit is usecs, TI SCI uses msecs */ 71 val_ms = val / USEC_PER_MSEC; 72 ret = ti_sci->ops.pm_ops.set_latency_constraint(ti_sci, val_ms, TISCI_MSG_CONSTRAINT_SET); 73 if (ret) 74 dev_err(dev, "ti_sci_pd: set latency constraint failed: ret=%d\n", 75 ret); 76 else 77 dev_dbg(dev, "ti_sci_pd: ID:%d set latency constraint %d\n", 78 pd->idx, val); 79 } 80 #endif 81 82 static inline void ti_sci_pd_set_wkup_constraint(struct device *dev) 83 { 84 struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); 85 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(genpd); 86 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 87 int ret; 88 89 if (device_may_wakeup(dev)) { 90 /* 91 * If device can wakeup using IO daisy chain wakeups, 92 * we do not want to set a constraint. 93 */ 94 if (dev->power.wakeirq) { 95 dev_dbg(dev, "%s: has wake IRQ, not setting constraints\n", __func__); 96 return; 97 } 98 99 ret = ti_sci->ops.pm_ops.set_device_constraint(ti_sci, pd->idx, 100 TISCI_MSG_CONSTRAINT_SET); 101 if (!ret) 102 dev_dbg(dev, "ti_sci_pd: ID:%d set device constraint.\n", pd->idx); 103 } 104 } 105 106 /* 107 * ti_sci_pd_power_off(): genpd power down hook 108 * @domain: pointer to the powerdomain to power off 109 */ 110 static int ti_sci_pd_power_off(struct generic_pm_domain *domain) 111 { 112 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); 113 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 114 115 return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx); 116 } 117 118 /* 119 * ti_sci_pd_power_on(): genpd power up hook 120 * @domain: pointer to the powerdomain to power on 121 */ 122 static int ti_sci_pd_power_on(struct generic_pm_domain *domain) 123 { 124 struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); 125 const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; 126 127 if (pd->exclusive) 128 return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, 129 pd->idx); 130 else 131 return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx); 132 } 133 134 #ifdef CONFIG_PM_SLEEP 135 static int ti_sci_pd_suspend(struct device *dev) 136 { 137 int ret; 138 s32 val; 139 140 ret = pm_generic_suspend(dev); 141 if (ret) 142 return ret; 143 144 val = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY); 145 if (ti_sci_pd_is_valid_constraint(val)) 146 ti_sci_pd_set_lat_constraint(dev, val); 147 148 ti_sci_pd_set_wkup_constraint(dev); 149 150 return 0; 151 } 152 #else 153 #define ti_sci_pd_suspend NULL 154 #endif 155 156 /* 157 * ti_sci_pd_xlate(): translation service for TI SCI genpds 158 * @genpdspec: DT identification data for the genpd 159 * @data: genpd core data for all the powerdomains on the device 160 */ 161 static struct generic_pm_domain *ti_sci_pd_xlate( 162 const struct of_phandle_args *genpdspec, 163 void *data) 164 { 165 struct genpd_onecell_data *genpd_data = data; 166 unsigned int idx = genpdspec->args[0]; 167 168 if (genpdspec->args_count != 1 && genpdspec->args_count != 2) 169 return ERR_PTR(-EINVAL); 170 171 if (idx >= genpd_data->num_domains) { 172 pr_err("%s: invalid domain index %u\n", __func__, idx); 173 return ERR_PTR(-EINVAL); 174 } 175 176 if (!genpd_data->domains[idx]) 177 return ERR_PTR(-ENOENT); 178 179 genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive = 180 genpdspec->args[1]; 181 182 return genpd_data->domains[idx]; 183 } 184 185 static const struct of_device_id ti_sci_pm_domain_matches[] = { 186 { .compatible = "ti,sci-pm-domain", }, 187 { }, 188 }; 189 MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); 190 191 static bool ti_sci_pm_idx_exists(struct ti_sci_genpd_provider *pd_provider, u32 idx) 192 { 193 struct ti_sci_pm_domain *pd; 194 195 list_for_each_entry(pd, &pd_provider->pd_list, node) { 196 if (pd->idx == idx) 197 return true; 198 } 199 200 return false; 201 } 202 203 static bool ti_sci_pm_pd_is_on(struct ti_sci_genpd_provider *pd_provider, 204 int pd_idx) 205 { 206 bool is_on; 207 int ret; 208 209 if (!pd_provider->ti_sci->ops.dev_ops.is_on) 210 return false; 211 212 ret = pd_provider->ti_sci->ops.dev_ops.is_on(pd_provider->ti_sci, 213 pd_idx, NULL, &is_on); 214 if (ret) 215 return false; 216 217 return is_on; 218 } 219 220 static int ti_sci_pm_domain_probe(struct platform_device *pdev) 221 { 222 struct device *dev = &pdev->dev; 223 struct ti_sci_genpd_provider *pd_provider; 224 struct ti_sci_pm_domain *pd; 225 struct device_node *np __free(device_node) = NULL; 226 struct of_phandle_args args; 227 u32 max_id = 0; 228 int index; 229 230 pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL); 231 if (!pd_provider) 232 return -ENOMEM; 233 234 pd_provider->ti_sci = devm_ti_sci_get_handle(dev); 235 if (IS_ERR(pd_provider->ti_sci)) 236 return PTR_ERR(pd_provider->ti_sci); 237 238 pd_provider->dev = dev; 239 240 INIT_LIST_HEAD(&pd_provider->pd_list); 241 242 /* Find highest device ID used for power domains */ 243 for_each_node_with_property(np, "power-domains") { 244 index = 0; 245 246 while (!of_parse_phandle_with_args(np, "power-domains", 247 "#power-domain-cells", 248 index, &args)) { 249 250 if (args.args_count >= 1 && args.np == dev->of_node) { 251 bool is_on; 252 253 of_node_put(args.np); 254 if (args.args[0] > max_id) { 255 max_id = args.args[0]; 256 } else { 257 if (ti_sci_pm_idx_exists(pd_provider, args.args[0])) { 258 index++; 259 continue; 260 } 261 } 262 263 pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); 264 if (!pd) 265 return -ENOMEM; 266 267 pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, 268 "pd:%d", 269 args.args[0]); 270 if (!pd->pd.name) 271 return -ENOMEM; 272 273 pd->pd.power_off = ti_sci_pd_power_off; 274 pd->pd.power_on = ti_sci_pd_power_on; 275 pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; 276 pd->idx = args.args[0]; 277 pd->parent = pd_provider; 278 /* 279 * If SCI constraint functions are present, then firmware 280 * supports the constraints API. 281 */ 282 if (pd_provider->ti_sci->ops.pm_ops.set_device_constraint && 283 pd_provider->ti_sci->ops.pm_ops.set_latency_constraint) 284 pd->pd.domain.ops.suspend = ti_sci_pd_suspend; 285 286 is_on = ti_sci_pm_pd_is_on(pd_provider, 287 pd->idx); 288 289 pm_genpd_init(&pd->pd, NULL, !is_on); 290 291 list_add(&pd->node, &pd_provider->pd_list); 292 } else { 293 of_node_put(args.np); 294 } 295 296 index++; 297 } 298 } 299 300 pd_provider->data.domains = 301 devm_kcalloc(dev, max_id + 1, 302 sizeof(*pd_provider->data.domains), 303 GFP_KERNEL); 304 if (!pd_provider->data.domains) 305 return -ENOMEM; 306 307 pd_provider->data.num_domains = max_id + 1; 308 pd_provider->data.xlate = ti_sci_pd_xlate; 309 310 list_for_each_entry(pd, &pd_provider->pd_list, node) 311 pd_provider->data.domains[pd->idx] = &pd->pd; 312 313 return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data); 314 } 315 316 static struct platform_driver ti_sci_pm_domains_driver = { 317 .probe = ti_sci_pm_domain_probe, 318 .driver = { 319 .name = "ti_sci_pm_domains", 320 .of_match_table = ti_sci_pm_domain_matches, 321 }, 322 }; 323 module_platform_driver(ti_sci_pm_domains_driver); 324 MODULE_LICENSE("GPL v2"); 325 MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver"); 326 MODULE_AUTHOR("Dave Gerlach"); 327