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#include <dt-bindings/soc/ti,sci_pm_domain.h>
  19
  20/**
  21 * struct ti_sci_genpd_dev_data: holds data needed for every device attached
  22 *                               to this genpd
  23 * @idx: index of the device that identifies it with the system
  24 *       control processor.
  25 * @exclusive: Permissions for exclusive request or shared request of the
  26 *             device.
  27 */
  28struct ti_sci_genpd_dev_data {
  29        int idx;
  30        u8 exclusive;
  31};
  32
  33/**
  34 * struct ti_sci_pm_domain: TI specific data needed for power domain
  35 * @ti_sci: handle to TI SCI protocol driver that provides ops to
  36 *          communicate with system control processor.
  37 * @dev: pointer to dev for the driver for devm allocs
  38 * @pd: generic_pm_domain for use with the genpd framework
  39 */
  40struct ti_sci_pm_domain {
  41        const struct ti_sci_handle *ti_sci;
  42        struct device *dev;
  43        struct generic_pm_domain pd;
  44};
  45
  46#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
  47
  48/**
  49 * ti_sci_dev_id(): get prepopulated ti_sci id from struct dev
  50 * @dev: pointer to device associated with this genpd
  51 *
  52 * Returns device_id stored from ti,sci_id property
  53 */
  54static int ti_sci_dev_id(struct device *dev)
  55{
  56        struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
  57        struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
  58
  59        return sci_dev_data->idx;
  60}
  61
  62static u8 is_ti_sci_dev_exclusive(struct device *dev)
  63{
  64        struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
  65        struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
  66
  67        return sci_dev_data->exclusive;
  68}
  69
  70/**
  71 * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
  72 * @dev: pointer to device associated with this genpd
  73 *
  74 * Returns ti_sci_handle to be used to communicate with system
  75 *         control processor.
  76 */
  77static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev)
  78{
  79        struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain);
  80        struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd);
  81
  82        return ti_sci_genpd->ti_sci;
  83}
  84
  85/**
  86 * ti_sci_dev_start(): genpd device start hook called to turn device on
  87 * @dev: pointer to device associated with this genpd to be powered on
  88 */
  89static int ti_sci_dev_start(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        if (is_ti_sci_dev_exclusive(dev))
  95                return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx);
  96        else
  97                return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
  98}
  99
 100/**
 101 * ti_sci_dev_stop(): genpd device stop hook called to turn device off
 102 * @dev: pointer to device associated with this genpd to be powered off
 103 */
 104static int ti_sci_dev_stop(struct device *dev)
 105{
 106        const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
 107        int idx = ti_sci_dev_id(dev);
 108
 109        return ti_sci->ops.dev_ops.put_device(ti_sci, idx);
 110}
 111
 112static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
 113                                struct device *dev)
 114{
 115        struct device_node *np = dev->of_node;
 116        struct of_phandle_args pd_args;
 117        struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
 118        const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
 119        struct ti_sci_genpd_dev_data *sci_dev_data;
 120        struct generic_pm_domain_data *genpd_data;
 121        int idx, ret = 0;
 122
 123        ret = of_parse_phandle_with_args(np, "power-domains",
 124                                         "#power-domain-cells", 0, &pd_args);
 125        if (ret < 0)
 126                return ret;
 127
 128        if (pd_args.args_count != 1 && pd_args.args_count != 2)
 129                return -EINVAL;
 130
 131        idx = pd_args.args[0];
 132
 133        /*
 134         * Check the validity of the requested idx, if the index is not valid
 135         * the PMMC will return a NAK here and we will not allocate it.
 136         */
 137        ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
 138        if (ret)
 139                return -EINVAL;
 140
 141        sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
 142        if (!sci_dev_data)
 143                return -ENOMEM;
 144
 145        sci_dev_data->idx = idx;
 146        /* Enable the exclusive permissions by default */
 147        sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE;
 148        if (pd_args.args_count == 2)
 149                sci_dev_data->exclusive = pd_args.args[1] & 0x1;
 150
 151        genpd_data = dev_gpd_data(dev);
 152        genpd_data->data = sci_dev_data;
 153
 154        return 0;
 155}
 156
 157static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
 158                                 struct device *dev)
 159{
 160        struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
 161        struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
 162
 163        kfree(sci_dev_data);
 164        genpd_data->data = NULL;
 165}
 166
 167static const struct of_device_id ti_sci_pm_domain_matches[] = {
 168        { .compatible = "ti,sci-pm-domain", },
 169        { },
 170};
 171MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
 172
 173static int ti_sci_pm_domain_probe(struct platform_device *pdev)
 174{
 175        struct device *dev = &pdev->dev;
 176        struct device_node *np = dev->of_node;
 177        struct ti_sci_pm_domain *ti_sci_pd;
 178        int ret;
 179
 180        ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL);
 181        if (!ti_sci_pd)
 182                return -ENOMEM;
 183
 184        ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev);
 185        if (IS_ERR(ti_sci_pd->ti_sci))
 186                return PTR_ERR(ti_sci_pd->ti_sci);
 187
 188        ti_sci_pd->dev = dev;
 189
 190        ti_sci_pd->pd.name = "ti_sci_pd";
 191
 192        ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev;
 193        ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev;
 194
 195        ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start;
 196        ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop;
 197
 198        pm_genpd_init(&ti_sci_pd->pd, NULL, true);
 199
 200        ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd);
 201
 202        return ret;
 203}
 204
 205static struct platform_driver ti_sci_pm_domains_driver = {
 206        .probe = ti_sci_pm_domain_probe,
 207        .driver = {
 208                .name = "ti_sci_pm_domains",
 209                .of_match_table = ti_sci_pm_domain_matches,
 210        },
 211};
 212module_platform_driver(ti_sci_pm_domains_driver);
 213MODULE_LICENSE("GPL v2");
 214MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
 215MODULE_AUTHOR("Dave Gerlach");
 216