uboot/drivers/power/domain/imx8-power-domain.c
<<
>>
Prefs
   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
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17struct imx8_power_domain_priv {
  18        bool state_on;
  19};
  20
  21static 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
  28static 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
  35static 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
  80static 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
 130static 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
 194static 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
 219static 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
 229static 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
 260static 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
 275static 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
 292static const struct udevice_id imx8_power_domain_ids[] = {
 293        { .compatible = "nxp,imx8-pd" },
 294        { }
 295};
 296
 297struct 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
 305U_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