1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2016, NVIDIA CORPORATION. 4 */ 5 6#define LOG_CATEGORY UCLASS_POWER_DOMAIN 7 8#include <common.h> 9#include <dm.h> 10#include <log.h> 11#include <malloc.h> 12#include <power-domain.h> 13#include <power-domain-uclass.h> 14#include <dm/device-internal.h> 15 16static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev) 17{ 18 return (struct power_domain_ops *)dev->driver->ops; 19} 20 21static int power_domain_of_xlate_default(struct power_domain *power_domain, 22 struct ofnode_phandle_args *args) 23{ 24 debug("%s(power_domain=%p)\n", __func__, power_domain); 25 26 if (args->args_count != 1) { 27 debug("Invalid args_count: %d\n", args->args_count); 28 return -EINVAL; 29 } 30 31 power_domain->id = args->args[0]; 32 33 return 0; 34} 35 36int power_domain_get_by_index(struct udevice *dev, 37 struct power_domain *power_domain, int index) 38{ 39 struct ofnode_phandle_args args; 40 int ret; 41 struct udevice *dev_power_domain; 42 struct power_domain_ops *ops; 43 44 debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain); 45 46 ret = dev_read_phandle_with_args(dev, "power-domains", 47 "#power-domain-cells", 0, index, 48 &args); 49 if (ret) { 50 debug("%s: dev_read_phandle_with_args failed: %d\n", 51 __func__, ret); 52 return ret; 53 } 54 55 ret = uclass_get_device_by_ofnode(UCLASS_POWER_DOMAIN, args.node, 56 &dev_power_domain); 57 if (ret) { 58 debug("%s: uclass_get_device_by_ofnode failed: %d\n", 59 __func__, ret); 60 return ret; 61 } 62 ops = power_domain_dev_ops(dev_power_domain); 63 64 power_domain->dev = dev_power_domain; 65 if (ops->of_xlate) 66 ret = ops->of_xlate(power_domain, &args); 67 else 68 ret = power_domain_of_xlate_default(power_domain, &args); 69 if (ret) { 70 debug("of_xlate() failed: %d\n", ret); 71 return ret; 72 } 73 74 ret = ops->request ? ops->request(power_domain) : 0; 75 if (ret) { 76 debug("ops->request() failed: %d\n", ret); 77 return ret; 78 } 79 80 return 0; 81} 82 83int power_domain_get_by_name(struct udevice *dev, 84 struct power_domain *power_domain, const char *name) 85{ 86 int index; 87 88 index = dev_read_stringlist_search(dev, "power-domain-names", name); 89 if (index < 0) { 90 debug("fdt_stringlist_search() failed: %d\n", index); 91 return index; 92 } 93 94 return power_domain_get_by_index(dev, power_domain, index); 95} 96 97int power_domain_get(struct udevice *dev, struct power_domain *power_domain) 98{ 99 return power_domain_get_by_index(dev, power_domain, 0); 100} 101 102int power_domain_free(struct power_domain *power_domain) 103{ 104 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 105 106 debug("%s(power_domain=%p)\n", __func__, power_domain); 107 108 return ops->rfree ? ops->rfree(power_domain) : 0; 109} 110 111int power_domain_on(struct power_domain *power_domain) 112{ 113 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 114 115 debug("%s(power_domain=%p)\n", __func__, power_domain); 116 117 return ops->on ? ops->on(power_domain) : 0; 118} 119 120int power_domain_off(struct power_domain *power_domain) 121{ 122 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 123 124 debug("%s(power_domain=%p)\n", __func__, power_domain); 125 126 return ops->off ? ops->off(power_domain) : 0; 127} 128 129#if CONFIG_IS_ENABLED(OF_REAL) 130static int dev_power_domain_ctrl(struct udevice *dev, bool on) 131{ 132 struct power_domain pd; 133 int i, count, ret = 0; 134 135 count = dev_count_phandle_with_args(dev, "power-domains", 136 "#power-domain-cells", 0); 137 for (i = 0; i < count; i++) { 138 ret = power_domain_get_by_index(dev, &pd, i); 139 if (ret) 140 return ret; 141 if (on) 142 ret = power_domain_on(&pd); 143 else 144 ret = power_domain_off(&pd); 145 } 146 147 /* 148 * For platforms with parent and child power-domain devices 149 * we may not run device_remove() on the power-domain parent 150 * because it will result in removing its children and switching 151 * off their power-domain parent. So we will get here again and 152 * again and will be stuck in an endless loop. 153 */ 154 if (count > 0 && !on && dev_get_parent(dev) == pd.dev && 155 device_get_uclass_id(dev) == UCLASS_POWER_DOMAIN) 156 return ret; 157 158 /* 159 * power_domain_get() bound the device, thus 160 * we must remove it again to prevent unbinding 161 * active devices (which would result in unbind 162 * error). 163 */ 164 if (count > 0 && !on) 165 device_remove(pd.dev, DM_REMOVE_NORMAL); 166 167 return ret; 168} 169 170int dev_power_domain_on(struct udevice *dev) 171{ 172 return dev_power_domain_ctrl(dev, true); 173} 174 175int dev_power_domain_off(struct udevice *dev) 176{ 177 return dev_power_domain_ctrl(dev, false); 178} 179#endif /* OF_REAL */ 180 181UCLASS_DRIVER(power_domain) = { 182 .id = UCLASS_POWER_DOMAIN, 183 .name = "power_domain", 184}; 185