Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc3 370 lines 8.6 kB view raw
1/* 2 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 */ 13 14#include <linux/of.h> 15#include <linux/platform_device.h> 16#include <linux/pm_domain.h> 17#include <linux/slab.h> 18#include <linux/version.h> 19 20#include <soc/tegra/bpmp.h> 21#include <soc/tegra/bpmp-abi.h> 22 23struct tegra_powergate_info { 24 unsigned int id; 25 char *name; 26}; 27 28struct tegra_powergate { 29 struct generic_pm_domain genpd; 30 struct tegra_bpmp *bpmp; 31 unsigned int id; 32}; 33 34static inline struct tegra_powergate * 35to_tegra_powergate(struct generic_pm_domain *genpd) 36{ 37 return container_of(genpd, struct tegra_powergate, genpd); 38} 39 40static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, 41 unsigned int id, u32 state) 42{ 43 struct mrq_pg_request request; 44 struct tegra_bpmp_message msg; 45 int err; 46 47 memset(&request, 0, sizeof(request)); 48 request.cmd = CMD_PG_SET_STATE; 49 request.id = id; 50 request.set_state.state = state; 51 52 memset(&msg, 0, sizeof(msg)); 53 msg.mrq = MRQ_PG; 54 msg.tx.data = &request; 55 msg.tx.size = sizeof(request); 56 57 err = tegra_bpmp_transfer(bpmp, &msg); 58 if (err < 0) 59 return err; 60 else if (msg.rx.ret < 0) 61 return -EINVAL; 62 63 return 0; 64} 65 66static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, 67 unsigned int id) 68{ 69 struct mrq_pg_response response; 70 struct mrq_pg_request request; 71 struct tegra_bpmp_message msg; 72 int err; 73 74 memset(&request, 0, sizeof(request)); 75 request.cmd = CMD_PG_GET_STATE; 76 request.id = id; 77 78 memset(&response, 0, sizeof(response)); 79 80 memset(&msg, 0, sizeof(msg)); 81 msg.mrq = MRQ_PG; 82 msg.tx.data = &request; 83 msg.tx.size = sizeof(request); 84 msg.rx.data = &response; 85 msg.rx.size = sizeof(response); 86 87 err = tegra_bpmp_transfer(bpmp, &msg); 88 if (err < 0) 89 return PG_STATE_OFF; 90 else if (msg.rx.ret < 0) 91 return -EINVAL; 92 93 return response.get_state.state; 94} 95 96static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) 97{ 98 struct mrq_pg_response response; 99 struct mrq_pg_request request; 100 struct tegra_bpmp_message msg; 101 int err; 102 103 memset(&request, 0, sizeof(request)); 104 request.cmd = CMD_PG_GET_MAX_ID; 105 106 memset(&response, 0, sizeof(response)); 107 108 memset(&msg, 0, sizeof(msg)); 109 msg.mrq = MRQ_PG; 110 msg.tx.data = &request; 111 msg.tx.size = sizeof(request); 112 msg.rx.data = &response; 113 msg.rx.size = sizeof(response); 114 115 err = tegra_bpmp_transfer(bpmp, &msg); 116 if (err < 0) 117 return err; 118 else if (msg.rx.ret < 0) 119 return -EINVAL; 120 121 return response.get_max_id.max_id; 122} 123 124static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, 125 unsigned int id) 126{ 127 struct mrq_pg_response response; 128 struct mrq_pg_request request; 129 struct tegra_bpmp_message msg; 130 int err; 131 132 memset(&request, 0, sizeof(request)); 133 request.cmd = CMD_PG_GET_NAME; 134 request.id = id; 135 136 memset(&response, 0, sizeof(response)); 137 138 memset(&msg, 0, sizeof(msg)); 139 msg.mrq = MRQ_PG; 140 msg.tx.data = &request; 141 msg.tx.size = sizeof(request); 142 msg.rx.data = &response; 143 msg.rx.size = sizeof(response); 144 145 err = tegra_bpmp_transfer(bpmp, &msg); 146 if (err < 0 || msg.rx.ret < 0) 147 return NULL; 148 149 return kstrdup(response.get_name.name, GFP_KERNEL); 150} 151 152static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp, 153 unsigned int id) 154{ 155 return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF; 156} 157 158static int tegra_powergate_power_on(struct generic_pm_domain *domain) 159{ 160 struct tegra_powergate *powergate = to_tegra_powergate(domain); 161 struct tegra_bpmp *bpmp = powergate->bpmp; 162 163 return tegra_bpmp_powergate_set_state(bpmp, powergate->id, 164 PG_STATE_ON); 165} 166 167static int tegra_powergate_power_off(struct generic_pm_domain *domain) 168{ 169 struct tegra_powergate *powergate = to_tegra_powergate(domain); 170 struct tegra_bpmp *bpmp = powergate->bpmp; 171 172 return tegra_bpmp_powergate_set_state(bpmp, powergate->id, 173 PG_STATE_OFF); 174} 175 176static struct tegra_powergate * 177tegra_powergate_add(struct tegra_bpmp *bpmp, 178 const struct tegra_powergate_info *info) 179{ 180 struct tegra_powergate *powergate; 181 bool off; 182 int err; 183 184 off = !tegra_bpmp_powergate_is_powered(bpmp, info->id); 185 186 powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL); 187 if (!powergate) 188 return ERR_PTR(-ENOMEM); 189 190 powergate->id = info->id; 191 powergate->bpmp = bpmp; 192 193 powergate->genpd.name = kstrdup(info->name, GFP_KERNEL); 194 powergate->genpd.power_on = tegra_powergate_power_on; 195 powergate->genpd.power_off = tegra_powergate_power_off; 196 197 err = pm_genpd_init(&powergate->genpd, NULL, off); 198 if (err < 0) { 199 kfree(powergate->genpd.name); 200 return ERR_PTR(err); 201 } 202 203 return powergate; 204} 205 206static void tegra_powergate_remove(struct tegra_powergate *powergate) 207{ 208 struct generic_pm_domain *genpd = &powergate->genpd; 209 struct tegra_bpmp *bpmp = powergate->bpmp; 210 int err; 211 212 err = pm_genpd_remove(genpd); 213 if (err < 0) 214 dev_err(bpmp->dev, "failed to remove power domain %s: %d\n", 215 genpd->name, err); 216 217 kfree(genpd->name); 218} 219 220static int 221tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, 222 struct tegra_powergate_info **powergatesp) 223{ 224 struct tegra_powergate_info *powergates; 225 unsigned int max_id, id, count = 0; 226 unsigned int num_holes = 0; 227 int err; 228 229 err = tegra_bpmp_powergate_get_max_id(bpmp); 230 if (err < 0) 231 return err; 232 233 max_id = err; 234 235 dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id); 236 237 powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL); 238 if (!powergates) 239 return -ENOMEM; 240 241 for (id = 0; id <= max_id; id++) { 242 struct tegra_powergate_info *info = &powergates[count]; 243 244 info->name = tegra_bpmp_powergate_get_name(bpmp, id); 245 if (!info->name || info->name[0] == '\0') { 246 num_holes++; 247 continue; 248 } 249 250 info->id = id; 251 count++; 252 } 253 254 dev_dbg(bpmp->dev, "holes: %u\n", num_holes); 255 256 *powergatesp = powergates; 257 258 return count; 259} 260 261static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp, 262 struct tegra_powergate_info *powergates, 263 unsigned int count) 264{ 265 struct genpd_onecell_data *genpd = &bpmp->genpd; 266 struct generic_pm_domain **domains; 267 struct tegra_powergate *powergate; 268 unsigned int i; 269 int err; 270 271 domains = kcalloc(count, sizeof(*domains), GFP_KERNEL); 272 if (!domains) 273 return -ENOMEM; 274 275 for (i = 0; i < count; i++) { 276 powergate = tegra_powergate_add(bpmp, &powergates[i]); 277 if (IS_ERR(powergate)) { 278 err = PTR_ERR(powergate); 279 goto remove; 280 } 281 282 dev_dbg(bpmp->dev, "added power domain %s\n", 283 powergate->genpd.name); 284 domains[i] = &powergate->genpd; 285 } 286 287 genpd->num_domains = count; 288 genpd->domains = domains; 289 290 return 0; 291 292remove: 293 while (i--) { 294 powergate = to_tegra_powergate(domains[i]); 295 tegra_powergate_remove(powergate); 296 } 297 298 kfree(genpd->domains); 299 return err; 300} 301 302static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) 303{ 304 struct genpd_onecell_data *genpd = &bpmp->genpd; 305 unsigned int i = genpd->num_domains; 306 struct tegra_powergate *powergate; 307 308 while (i--) { 309 dev_dbg(bpmp->dev, "removing power domain %s\n", 310 genpd->domains[i]->name); 311 powergate = to_tegra_powergate(genpd->domains[i]); 312 tegra_powergate_remove(powergate); 313 } 314} 315 316static struct generic_pm_domain * 317tegra_powergate_xlate(struct of_phandle_args *spec, void *data) 318{ 319 struct generic_pm_domain *domain = ERR_PTR(-ENOENT); 320 struct genpd_onecell_data *genpd = data; 321 unsigned int i; 322 323 for (i = 0; i < genpd->num_domains; i++) { 324 struct tegra_powergate *powergate; 325 326 powergate = to_tegra_powergate(genpd->domains[i]); 327 if (powergate->id == spec->args[0]) { 328 domain = &powergate->genpd; 329 break; 330 } 331 } 332 333 return domain; 334} 335 336int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) 337{ 338 struct device_node *np = bpmp->dev->of_node; 339 struct tegra_powergate_info *powergates; 340 struct device *dev = bpmp->dev; 341 unsigned int count, i; 342 int err; 343 344 err = tegra_bpmp_probe_powergates(bpmp, &powergates); 345 if (err < 0) 346 return err; 347 348 count = err; 349 350 dev_dbg(dev, "%u power domains probed\n", count); 351 352 err = tegra_bpmp_add_powergates(bpmp, powergates, count); 353 if (err < 0) 354 goto free; 355 356 bpmp->genpd.xlate = tegra_powergate_xlate; 357 358 err = of_genpd_add_provider_onecell(np, &bpmp->genpd); 359 if (err < 0) { 360 dev_err(dev, "failed to add power domain provider: %d\n", err); 361 tegra_bpmp_remove_powergates(bpmp); 362 } 363 364free: 365 for (i = 0; i < count; i++) 366 kfree(powergates[i].name); 367 368 kfree(powergates); 369 return err; 370}