linux/drivers/soc/ti/ti_sci_pm_domains.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * TI SCI Generic Power Domain Driver
   4 *
   5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
   6 *      J Keerthy <j-keerthy@ti.com>
   7 *      Dave Gerlach <d-gerlach@ti.com>
   8 */
   9
  10#include <linux/err.h>
  11#include <linux/module.h>
  12#include <linux/mutex.h>
  13#include <linux/of.h>
  14#include <linux/platform_device.h>
  15#include <linux/pm_domain.h>
  16#include <linux/slab.h>
  17#include <linux/soc/ti/ti_sci_protocol.h>
  18
  19/**
  20 * struct ti_sci_genpd_dev_data: holds data needed for every device attached
  21 *                               to this genpd
  22 * @idx: index of the device that identifies it with the system
  23 *       control processor.
  24 */
  25struct ti_sci_genpd_dev_data {
  26        int idx;
  27};
  28
  29/**
  30 * struct ti_sci_pm_domain: TI specific data needed for power domain
  31 * @ti_sci: handle to TI SCI protocol driver that provides ops to
  32 *          communicate with system control processor.
  33 * @dev: pointer to dev for the driver for devm allocs
  34 * @pd: generic_pm_domain for use with the genpd framework
  35 */
  36struct ti_sci_pm_domain {
  37        const struct ti_sci_handle *ti_sci;
  38        struct device *dev;
  39        struct generic_pm_domain pd;
  40};
  41
  42#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
  43
  44/**
  45 * ti_sci_dev_id(): get prepopulated ti_sci id from struct dev
  46 * @dev: pointer to device associated with this genpd
  47 *
  48 * Returns device_id stored from ti,sci_id property
  49 */
  50static int ti_sci_dev_id(struct device *dev)
  51{
  52        struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
  53        struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
  54
  55        return sci_dev_data->idx;
  56}
  57
  58/**
  59 * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
  60 * @dev: pointer to device associated with this genpd
  61 *
  62 * Returns ti_sci_handle to be used to communicate with system
  63 *         control processor.
  64 */
  65static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev)
  66{
  67        struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain);
  68        struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd);
  69
  70        return ti_sci_genpd->ti_sci;
  71}
  72
  73/**
  74 * ti_sci_dev_start(): genpd device start hook called to turn device on
  75 * @dev: pointer to device associated with this genpd to be powered on
  76 */
  77static int ti_sci_dev_start(struct device *dev)
  78{
  79        const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
  80        int idx = ti_sci_dev_id(dev);
  81
  82        return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
  83}
  84
  85/**
  86 * ti_sci_dev_stop(): genpd device stop hook called to turn device off
  87 * @dev: pointer to device associated with this genpd to be powered off
  88 */
  89static int ti_sci_dev_stop(struct device *dev)
  90{
  91        const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
  92        int idx = ti_sci_dev_id(dev);
  93
  94        return ti_sci->ops.dev_ops.put_device(ti_sci, idx);
  95}
  96
  97static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
  98                                struct device *dev)
  99{
 100        struct device_node *np = dev->of_node;
 101        struct of_phandle_args pd_args;
 102        struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
 103        const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
 104        struct ti_sci_genpd_dev_data *sci_dev_data;
 105        struct generic_pm_domain_data *genpd_data;
 106        int idx, ret = 0;
 107
 108        ret = of_parse_phandle_with_args(np, "power-domains",
 109                                         "#power-domain-cells", 0, &pd_args);
 110        if (ret < 0)
 111                return ret;
 112
 113        if (pd_args.args_count != 1)
 114                return -EINVAL;
 115
 116        idx = pd_args.args[0];
 117
 118        /*
 119         * Check the validity of the requested idx, if the index is not valid
 120         * the PMMC will return a NAK here and we will not allocate it.
 121         */
 122        ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
 123        if (ret)
 124                return -EINVAL;
 125
 126        sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
 127        if (!sci_dev_data)
 128                return -ENOMEM;
 129
 130        sci_dev_data->idx = idx;
 131
 132        genpd_data = dev_gpd_data(dev);
 133        genpd_data->data = sci_dev_data;
 134
 135        return 0;
 136}
 137
 138static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
 139                                 struct device *dev)
 140{
 141        struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
 142        struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
 143
 144        kfree(sci_dev_data);
 145        genpd_data->data = NULL;
 146}
 147
 148static const struct of_device_id ti_sci_pm_domain_matches[] = {
 149        { .compatible = "ti,sci-pm-domain", },
 150        { },
 151};
 152MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
 153
 154static int ti_sci_pm_domain_probe(struct platform_device *pdev)
 155{
 156        struct device *dev = &pdev->dev;
 157        struct device_node *np = dev->of_node;
 158        struct ti_sci_pm_domain *ti_sci_pd;
 159        int ret;
 160
 161        ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL);
 162        if (!ti_sci_pd)
 163                return -ENOMEM;
 164
 165        ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev);
 166        if (IS_ERR(ti_sci_pd->ti_sci))
 167                return PTR_ERR(ti_sci_pd->ti_sci);
 168
 169        ti_sci_pd->dev = dev;
 170
 171        ti_sci_pd->pd.name = "ti_sci_pd";
 172
 173        ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev;
 174        ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev;
 175
 176        ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start;
 177        ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop;
 178
 179        pm_genpd_init(&ti_sci_pd->pd, NULL, true);
 180
 181        ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd);
 182
 183        return ret;
 184}
 185
 186static struct platform_driver ti_sci_pm_domains_driver = {
 187        .probe = ti_sci_pm_domain_probe,
 188        .driver = {
 189                .name = "ti_sci_pm_domains",
 190                .of_match_table = ti_sci_pm_domain_matches,
 191        },
 192};
 193module_platform_driver(ti_sci_pm_domains_driver);
 194MODULE_LICENSE("GPL v2");
 195MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
 196MODULE_AUTHOR("Dave Gerlach");
 197