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