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(power_domain); 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(struct udevice *dev, struct power_domain *power_domain) 83 { 84 return power_domain_get_by_index(dev, power_domain, 0); 85 } 86 87 int power_domain_free(struct power_domain *power_domain) 88 { 89 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 90 91 debug("%s(power_domain=%p)\n", __func__, power_domain); 92 93 return ops->rfree(power_domain); 94 } 95 96 int power_domain_on(struct power_domain *power_domain) 97 { 98 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 99 100 debug("%s(power_domain=%p)\n", __func__, power_domain); 101 102 return ops->on(power_domain); 103 } 104 105 int power_domain_off(struct power_domain *power_domain) 106 { 107 struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev); 108 109 debug("%s(power_domain=%p)\n", __func__, power_domain); 110 111 return ops->off(power_domain); 112 } 113 114 #if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) 115 static int dev_power_domain_ctrl(struct udevice *dev, bool on) 116 { 117 struct power_domain pd; 118 int i, count, ret = 0; 119 120 count = dev_count_phandle_with_args(dev, "power-domains", 121 "#power-domain-cells"); 122 for (i = 0; i < count; i++) { 123 ret = power_domain_get_by_index(dev, &pd, i); 124 if (ret) 125 return ret; 126 if (on) 127 ret = power_domain_on(&pd); 128 else 129 ret = power_domain_off(&pd); 130 } 131 132 /* 133 * power_domain_get() bound the device, thus 134 * we must remove it again to prevent unbinding 135 * active devices (which would result in unbind 136 * error). 137 */ 138 if (count > 0 && !on) 139 device_remove(pd.dev, DM_REMOVE_NORMAL); 140 141 return ret; 142 } 143 144 int dev_power_domain_on(struct udevice *dev) 145 { 146 return dev_power_domain_ctrl(dev, true); 147 } 148 149 int dev_power_domain_off(struct udevice *dev) 150 { 151 return dev_power_domain_ctrl(dev, false); 152 } 153 #endif 154 155 UCLASS_DRIVER(power_domain) = { 156 .id = UCLASS_POWER_DOMAIN, 157 .name = "power_domain", 158 }; 159