1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2017 NXP 4 */ 5 6 #include <common.h> 7 #include <dm.h> 8 #include <power-domain-uclass.h> 9 #include <asm/io.h> 10 #include <asm/arch/power-domain.h> 11 #include <dm/device-internal.h> 12 #include <dm/device.h> 13 #include <asm/arch/sci/sci.h> 14 15 DECLARE_GLOBAL_DATA_PTR; 16 17 struct imx8_power_domain_priv { 18 bool state_on; 19 }; 20 21 static int imx8_power_domain_request(struct power_domain *power_domain) 22 { 23 debug("%s(power_domain=%p)\n", __func__, power_domain); 24 25 return 0; 26 } 27 28 static int imx8_power_domain_free(struct power_domain *power_domain) 29 { 30 debug("%s(power_domain=%p)\n", __func__, power_domain); 31 32 return 0; 33 } 34 35 static int imx8_power_domain_on(struct power_domain *power_domain) 36 { 37 struct udevice *dev = power_domain->dev; 38 struct imx8_power_domain_platdata *pdata; 39 struct imx8_power_domain_priv *ppriv; 40 sc_err_t ret; 41 int err; 42 43 struct power_domain parent_domain; 44 struct udevice *parent = dev_get_parent(dev); 45 46 /* Need to power on parent node first */ 47 if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) { 48 parent_domain.dev = parent; 49 err = imx8_power_domain_on(&parent_domain); 50 if (err) 51 return err; 52 } 53 54 pdata = (struct imx8_power_domain_platdata *)dev_get_platdata(dev); 55 ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev); 56 57 debug("%s(power_domain=%s) resource_id %d\n", __func__, dev->name, 58 pdata->resource_id); 59 60 /* Already powered on */ 61 if (ppriv->state_on) 62 return 0; 63 64 if (pdata->resource_id != SC_R_LAST) { 65 ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id, 66 SC_PM_PW_MODE_ON); 67 if (ret) { 68 printf("Error: %s Power up failed! (error = %d)\n", 69 dev->name, ret); 70 return -EIO; 71 } 72 } 73 74 ppriv->state_on = true; 75 debug("%s is powered on\n", dev->name); 76 77 return 0; 78 } 79 80 static int imx8_power_domain_off_node(struct power_domain *power_domain) 81 { 82 struct udevice *dev = power_domain->dev; 83 struct udevice *child; 84 struct imx8_power_domain_priv *ppriv; 85 struct imx8_power_domain_priv *child_ppriv; 86 struct imx8_power_domain_platdata *pdata; 87 sc_err_t ret; 88 89 ppriv = dev_get_priv(dev); 90 pdata = dev_get_platdata(dev); 91 92 debug("%s, %s, state_on %d\n", __func__, dev->name, ppriv->state_on); 93 94 /* Already powered off */ 95 if (!ppriv->state_on) 96 return 0; 97 98 /* Check if all subnodes are off */ 99 for (device_find_first_child(dev, &child); 100 child; 101 device_find_next_child(&child)) { 102 if (device_active(child)) { 103 child_ppriv = 104 (struct imx8_power_domain_priv *)dev_get_priv(child); 105 if (child_ppriv->state_on) 106 return -EPERM; 107 } 108 } 109 110 if (pdata->resource_id != SC_R_LAST) { 111 if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) { 112 printf("%s not owned by curr partition\n", dev->name); 113 return 0; 114 } 115 ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id, 116 SC_PM_PW_MODE_OFF); 117 if (ret) { 118 printf("Error: %s Power off failed! (error = %d)\n", 119 dev->name, ret); 120 return -EIO; 121 } 122 } 123 124 ppriv->state_on = false; 125 debug("%s is powered off\n", dev->name); 126 127 return 0; 128 } 129 130 static int imx8_power_domain_off_parentnodes(struct power_domain *power_domain) 131 { 132 struct udevice *dev = power_domain->dev; 133 struct udevice *parent = dev_get_parent(dev); 134 struct udevice *child; 135 struct imx8_power_domain_priv *ppriv; 136 struct imx8_power_domain_priv *child_ppriv; 137 struct imx8_power_domain_platdata *pdata; 138 sc_err_t ret; 139 struct power_domain parent_pd; 140 141 if (device_get_uclass_id(parent) == UCLASS_POWER_DOMAIN) { 142 pdata = 143 (struct imx8_power_domain_platdata *)dev_get_platdata(parent); 144 ppriv = (struct imx8_power_domain_priv *)dev_get_priv(parent); 145 146 debug("%s, %s, state_on %d\n", __func__, parent->name, 147 ppriv->state_on); 148 149 /* Already powered off */ 150 if (!ppriv->state_on) 151 return 0; 152 153 /* 154 * Check if all sibling nodes are off. If yes, 155 * power off parent 156 */ 157 for (device_find_first_child(parent, &child); child; 158 device_find_next_child(&child)) { 159 if (device_active(child)) { 160 child_ppriv = (struct imx8_power_domain_priv *) 161 dev_get_priv(child); 162 /* Find a power on sibling */ 163 if (child_ppriv->state_on) { 164 debug("sibling %s, state_on %d\n", 165 child->name, 166 child_ppriv->state_on); 167 return 0; 168 } 169 } 170 } 171 172 /* power off parent */ 173 if (pdata->resource_id != SC_R_LAST) { 174 ret = sc_pm_set_resource_power_mode(-1, 175 pdata->resource_id, 176 SC_PM_PW_MODE_OFF); 177 if (ret) { 178 printf("%s Power off failed! (error = %d)\n", 179 parent->name, ret); 180 return -EIO; 181 } 182 } 183 184 ppriv->state_on = false; 185 debug("%s is powered off\n", parent->name); 186 187 parent_pd.dev = parent; 188 imx8_power_domain_off_parentnodes(&parent_pd); 189 } 190 191 return 0; 192 } 193 194 static int imx8_power_domain_off(struct power_domain *power_domain) 195 { 196 int ret; 197 198 debug("%s(power_domain=%p)\n", __func__, power_domain); 199 200 /* Turn off the node */ 201 ret = imx8_power_domain_off_node(power_domain); 202 if (ret) { 203 debug("Can't power off the node of dev %s, ret = %d\n", 204 power_domain->dev->name, ret); 205 return ret; 206 } 207 208 /* Turn off parent nodes, if sibling nodes are all off */ 209 ret = imx8_power_domain_off_parentnodes(power_domain); 210 if (ret) { 211 printf("Failed to power off parent nodes of dev %s, ret = %d\n", 212 power_domain->dev->name, ret); 213 return ret; 214 } 215 216 return 0; 217 } 218 219 static int imx8_power_domain_of_xlate(struct power_domain *power_domain, 220 struct ofnode_phandle_args *args) 221 { 222 debug("%s(power_domain=%p)\n", __func__, power_domain); 223 224 /* Do nothing to the xlate, since we don't have args used */ 225 226 return 0; 227 } 228 229 static int imx8_power_domain_bind(struct udevice *dev) 230 { 231 int offset; 232 const char *name; 233 int ret = 0; 234 235 debug("%s(dev=%p)\n", __func__, dev); 236 237 offset = dev_of_offset(dev); 238 for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0; 239 offset = fdt_next_subnode(gd->fdt_blob, offset)) { 240 /* Bind the subnode to this driver */ 241 name = fdt_get_name(gd->fdt_blob, offset, NULL); 242 243 ret = device_bind_with_driver_data(dev, dev->driver, name, 244 dev->driver_data, 245 offset_to_ofnode(offset), 246 NULL); 247 248 if (ret == -ENODEV) 249 printf("Driver '%s' refuses to bind\n", 250 dev->driver->name); 251 252 if (ret) 253 printf("Error binding driver '%s': %d\n", 254 dev->driver->name, ret); 255 } 256 257 return 0; 258 } 259 260 static int imx8_power_domain_probe(struct udevice *dev) 261 { 262 struct imx8_power_domain_priv *ppriv; 263 264 debug("%s(dev=%s)\n", __func__, dev->name); 265 266 ppriv = (struct imx8_power_domain_priv *)dev_get_priv(dev); 267 268 /* Set default to power off */ 269 if (ppriv) 270 ppriv->state_on = false; 271 272 return 0; 273 } 274 275 static int imx8_power_domain_ofdata_to_platdata(struct udevice *dev) 276 { 277 int reg; 278 struct imx8_power_domain_platdata *pdata = dev_get_platdata(dev); 279 280 reg = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1); 281 if (reg == -1) { 282 debug("%s: Invalid resource id %d\n", __func__, reg); 283 return -EINVAL; 284 } 285 pdata->resource_id = (sc_rsrc_t)reg; 286 287 debug("%s resource_id %d\n", __func__, pdata->resource_id); 288 289 return 0; 290 } 291 292 static const struct udevice_id imx8_power_domain_ids[] = { 293 { .compatible = "nxp,imx8-pd" }, 294 { } 295 }; 296 297 struct power_domain_ops imx8_power_domain_ops = { 298 .request = imx8_power_domain_request, 299 .free = imx8_power_domain_free, 300 .on = imx8_power_domain_on, 301 .off = imx8_power_domain_off, 302 .of_xlate = imx8_power_domain_of_xlate, 303 }; 304 305 U_BOOT_DRIVER(imx8_power_domain) = { 306 .name = "imx8_power_domain", 307 .id = UCLASS_POWER_DOMAIN, 308 .of_match = imx8_power_domain_ids, 309 .bind = imx8_power_domain_bind, 310 .probe = imx8_power_domain_probe, 311 .ofdata_to_platdata = imx8_power_domain_ofdata_to_platdata, 312 .platdata_auto_alloc_size = sizeof(struct imx8_power_domain_platdata), 313 .priv_auto_alloc_size = sizeof(struct imx8_power_domain_priv), 314 .ops = &imx8_power_domain_ops, 315 }; 316