root/drivers/soc/xilinx/zynqmp_pm_domains.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zynqmp_gpd_is_active_wakeup_path
  2. zynqmp_gpd_power_on
  3. zynqmp_gpd_power_off
  4. zynqmp_gpd_attach_dev
  5. zynqmp_gpd_detach_dev
  6. zynqmp_gpd_probe
  7. zynqmp_gpd_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * ZynqMP Generic PM domain support
   4  *
   5  *  Copyright (C) 2015-2018 Xilinx, Inc.
   6  *
   7  *  Davorin Mista <davorin.mista@aggios.com>
   8  *  Jolly Shah <jollys@xilinx.com>
   9  *  Rajan Vaja <rajan.vaja@xilinx.com>
  10  */
  11 
  12 #include <linux/err.h>
  13 #include <linux/list.h>
  14 #include <linux/module.h>
  15 #include <linux/of_platform.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/pm_domain.h>
  18 #include <linux/slab.h>
  19 
  20 #include <linux/firmware/xlnx-zynqmp.h>
  21 
  22 #define ZYNQMP_NUM_DOMAINS              (100)
  23 /* Flag stating if PM nodes mapped to the PM domain has been requested */
  24 #define ZYNQMP_PM_DOMAIN_REQUESTED      BIT(0)
  25 
  26 static const struct zynqmp_eemi_ops *eemi_ops;
  27 
  28 /**
  29  * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
  30  * @gpd:                Generic power domain
  31  * @node_id:            PM node ID corresponding to device inside PM domain
  32  * @flags:              ZynqMP PM domain flags
  33  */
  34 struct zynqmp_pm_domain {
  35         struct generic_pm_domain gpd;
  36         u32 node_id;
  37         u8 flags;
  38 };
  39 
  40 /**
  41  * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source
  42  *                                      path
  43  * @dev:        Device to check for wakeup source path
  44  * @not_used:   Data member (not required)
  45  *
  46  * This function is checks device's child hierarchy and checks if any device is
  47  * set as wakeup source.
  48  *
  49  * Return: 1 if device is in wakeup source path else 0
  50  */
  51 static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
  52 {
  53         int may_wakeup;
  54 
  55         may_wakeup = device_may_wakeup(dev);
  56         if (may_wakeup)
  57                 return may_wakeup;
  58 
  59         return device_for_each_child(dev, NULL,
  60                         zynqmp_gpd_is_active_wakeup_path);
  61 }
  62 
  63 /**
  64  * zynqmp_gpd_power_on() - Power on PM domain
  65  * @domain:     Generic PM domain
  66  *
  67  * This function is called before devices inside a PM domain are resumed, to
  68  * power on PM domain.
  69  *
  70  * Return: 0 on success, error code otherwise
  71  */
  72 static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
  73 {
  74         int ret;
  75         struct zynqmp_pm_domain *pd;
  76 
  77         if (!eemi_ops->set_requirement)
  78                 return -ENXIO;
  79 
  80         pd = container_of(domain, struct zynqmp_pm_domain, gpd);
  81         ret = eemi_ops->set_requirement(pd->node_id,
  82                                         ZYNQMP_PM_CAPABILITY_ACCESS,
  83                                         ZYNQMP_PM_MAX_QOS,
  84                                         ZYNQMP_PM_REQUEST_ACK_BLOCKING);
  85         if (ret) {
  86                 pr_err("%s() %s set requirement for node %d failed: %d\n",
  87                        __func__, domain->name, pd->node_id, ret);
  88                 return ret;
  89         }
  90 
  91         pr_debug("%s() Powered on %s domain\n", __func__, domain->name);
  92         return 0;
  93 }
  94 
  95 /**
  96  * zynqmp_gpd_power_off() - Power off PM domain
  97  * @domain:     Generic PM domain
  98  *
  99  * This function is called after devices inside a PM domain are suspended, to
 100  * power off PM domain.
 101  *
 102  * Return: 0 on success, error code otherwise
 103  */
 104 static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
 105 {
 106         int ret;
 107         struct pm_domain_data *pdd, *tmp;
 108         struct zynqmp_pm_domain *pd;
 109         u32 capabilities = 0;
 110         bool may_wakeup;
 111 
 112         if (!eemi_ops->set_requirement)
 113                 return -ENXIO;
 114 
 115         pd = container_of(domain, struct zynqmp_pm_domain, gpd);
 116 
 117         /* If domain is already released there is nothing to be done */
 118         if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) {
 119                 pr_debug("%s() %s domain is already released\n",
 120                          __func__, domain->name);
 121                 return 0;
 122         }
 123 
 124         list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) {
 125                 /* If device is in wakeup path, set capability to WAKEUP */
 126                 may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL);
 127                 if (may_wakeup) {
 128                         dev_dbg(pdd->dev, "device is in wakeup path in %s\n",
 129                                 domain->name);
 130                         capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP;
 131                         break;
 132                 }
 133         }
 134 
 135         ret = eemi_ops->set_requirement(pd->node_id, capabilities, 0,
 136                                         ZYNQMP_PM_REQUEST_ACK_NO);
 137         /**
 138          * If powering down of any node inside this domain fails,
 139          * report and return the error
 140          */
 141         if (ret) {
 142                 pr_err("%s() %s set requirement for node %d failed: %d\n",
 143                        __func__, domain->name, pd->node_id, ret);
 144                 return ret;
 145         }
 146 
 147         pr_debug("%s() Powered off %s domain\n", __func__, domain->name);
 148         return 0;
 149 }
 150 
 151 /**
 152  * zynqmp_gpd_attach_dev() - Attach device to the PM domain
 153  * @domain:     Generic PM domain
 154  * @dev:        Device to attach
 155  *
 156  * Return: 0 on success, error code otherwise
 157  */
 158 static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
 159                                  struct device *dev)
 160 {
 161         int ret;
 162         struct zynqmp_pm_domain *pd;
 163 
 164         if (!eemi_ops->request_node)
 165                 return -ENXIO;
 166 
 167         pd = container_of(domain, struct zynqmp_pm_domain, gpd);
 168 
 169         /* If this is not the first device to attach there is nothing to do */
 170         if (domain->device_count)
 171                 return 0;
 172 
 173         ret = eemi_ops->request_node(pd->node_id, 0, 0,
 174                                      ZYNQMP_PM_REQUEST_ACK_BLOCKING);
 175         /* If requesting a node fails print and return the error */
 176         if (ret) {
 177                 pr_err("%s() %s request failed for node %d: %d\n",
 178                        __func__, domain->name, pd->node_id, ret);
 179                 return ret;
 180         }
 181 
 182         pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
 183 
 184         pr_debug("%s() %s attached to %s domain\n", __func__,
 185                  dev_name(dev), domain->name);
 186         return 0;
 187 }
 188 
 189 /**
 190  * zynqmp_gpd_detach_dev() - Detach device from the PM domain
 191  * @domain:     Generic PM domain
 192  * @dev:        Device to detach
 193  */
 194 static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
 195                                   struct device *dev)
 196 {
 197         int ret;
 198         struct zynqmp_pm_domain *pd;
 199 
 200         if (!eemi_ops->release_node)
 201                 return;
 202 
 203         pd = container_of(domain, struct zynqmp_pm_domain, gpd);
 204 
 205         /* If this is not the last device to detach there is nothing to do */
 206         if (domain->device_count)
 207                 return;
 208 
 209         ret = eemi_ops->release_node(pd->node_id);
 210         /* If releasing a node fails print the error and return */
 211         if (ret) {
 212                 pr_err("%s() %s release failed for node %d: %d\n",
 213                        __func__, domain->name, pd->node_id, ret);
 214                 return;
 215         }
 216 
 217         pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
 218 
 219         pr_debug("%s() %s detached from %s domain\n", __func__,
 220                  dev_name(dev), domain->name);
 221 }
 222 
 223 static struct generic_pm_domain *zynqmp_gpd_xlate
 224                                 (struct of_phandle_args *genpdspec, void *data)
 225 {
 226         struct genpd_onecell_data *genpd_data = data;
 227         unsigned int i, idx = genpdspec->args[0];
 228         struct zynqmp_pm_domain *pd;
 229 
 230         pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd);
 231 
 232         if (genpdspec->args_count != 1)
 233                 return ERR_PTR(-EINVAL);
 234 
 235         /* Check for existing pm domains */
 236         for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
 237                 if (pd[i].node_id == idx)
 238                         goto done;
 239         }
 240 
 241         /**
 242          * Add index in empty node_id of power domain list as no existing
 243          * power domain found for current index.
 244          */
 245         for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
 246                 if (pd[i].node_id == 0) {
 247                         pd[i].node_id = idx;
 248                         break;
 249                 }
 250         }
 251 
 252 done:
 253         if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS)
 254                 return ERR_PTR(-ENOENT);
 255 
 256         return genpd_data->domains[i];
 257 }
 258 
 259 static int zynqmp_gpd_probe(struct platform_device *pdev)
 260 {
 261         int i;
 262         struct genpd_onecell_data *zynqmp_pd_data;
 263         struct generic_pm_domain **domains;
 264         struct zynqmp_pm_domain *pd;
 265         struct device *dev = &pdev->dev;
 266 
 267         eemi_ops = zynqmp_pm_get_eemi_ops();
 268         if (IS_ERR(eemi_ops))
 269                 return PTR_ERR(eemi_ops);
 270 
 271         pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL);
 272         if (!pd)
 273                 return -ENOMEM;
 274 
 275         zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL);
 276         if (!zynqmp_pd_data)
 277                 return -ENOMEM;
 278 
 279         zynqmp_pd_data->xlate = zynqmp_gpd_xlate;
 280 
 281         domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains),
 282                                GFP_KERNEL);
 283         if (!domains)
 284                 return -ENOMEM;
 285 
 286         for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) {
 287                 pd->node_id = 0;
 288                 pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i);
 289                 pd->gpd.power_off = zynqmp_gpd_power_off;
 290                 pd->gpd.power_on = zynqmp_gpd_power_on;
 291                 pd->gpd.attach_dev = zynqmp_gpd_attach_dev;
 292                 pd->gpd.detach_dev = zynqmp_gpd_detach_dev;
 293 
 294                 domains[i] = &pd->gpd;
 295 
 296                 /* Mark all PM domains as initially powered off */
 297                 pm_genpd_init(&pd->gpd, NULL, true);
 298         }
 299 
 300         zynqmp_pd_data->domains = domains;
 301         zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS;
 302         of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data);
 303 
 304         return 0;
 305 }
 306 
 307 static int zynqmp_gpd_remove(struct platform_device *pdev)
 308 {
 309         of_genpd_del_provider(pdev->dev.parent->of_node);
 310 
 311         return 0;
 312 }
 313 
 314 static struct platform_driver zynqmp_power_domain_driver = {
 315         .driver = {
 316                 .name = "zynqmp_power_controller",
 317         },
 318         .probe = zynqmp_gpd_probe,
 319         .remove = zynqmp_gpd_remove,
 320 };
 321 module_platform_driver(zynqmp_power_domain_driver);
 322 
 323 MODULE_ALIAS("platform:zynqmp_power_controller");

/* [<][>][^][v][top][bottom][index][help] */