cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scmi_pm_domain.c (3859B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * SCMI Generic power domain support.
      4 *
      5 * Copyright (C) 2018-2021 ARM Ltd.
      6 */
      7
      8#include <linux/err.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/pm_clock.h>
     12#include <linux/pm_domain.h>
     13#include <linux/scmi_protocol.h>
     14
     15static const struct scmi_power_proto_ops *power_ops;
     16
     17struct scmi_pm_domain {
     18	struct generic_pm_domain genpd;
     19	const struct scmi_protocol_handle *ph;
     20	const char *name;
     21	u32 domain;
     22};
     23
     24#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
     25
     26static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
     27{
     28	int ret;
     29	u32 state, ret_state;
     30	struct scmi_pm_domain *pd = to_scmi_pd(domain);
     31
     32	if (power_on)
     33		state = SCMI_POWER_STATE_GENERIC_ON;
     34	else
     35		state = SCMI_POWER_STATE_GENERIC_OFF;
     36
     37	ret = power_ops->state_set(pd->ph, pd->domain, state);
     38	if (!ret)
     39		ret = power_ops->state_get(pd->ph, pd->domain, &ret_state);
     40	if (!ret && state != ret_state)
     41		return -EIO;
     42
     43	return ret;
     44}
     45
     46static int scmi_pd_power_on(struct generic_pm_domain *domain)
     47{
     48	return scmi_pd_power(domain, true);
     49}
     50
     51static int scmi_pd_power_off(struct generic_pm_domain *domain)
     52{
     53	return scmi_pd_power(domain, false);
     54}
     55
     56static int scmi_pd_attach_dev(struct generic_pm_domain *pd, struct device *dev)
     57{
     58	int ret;
     59
     60	ret = pm_clk_create(dev);
     61	if (ret)
     62		return ret;
     63
     64	ret = of_pm_clk_add_clks(dev);
     65	if (ret >= 0)
     66		return 0;
     67
     68	pm_clk_destroy(dev);
     69	return ret;
     70}
     71
     72static void scmi_pd_detach_dev(struct generic_pm_domain *pd, struct device *dev)
     73{
     74	pm_clk_destroy(dev);
     75}
     76
     77static int scmi_pm_domain_probe(struct scmi_device *sdev)
     78{
     79	int num_domains, i;
     80	struct device *dev = &sdev->dev;
     81	struct device_node *np = dev->of_node;
     82	struct scmi_pm_domain *scmi_pd;
     83	struct genpd_onecell_data *scmi_pd_data;
     84	struct generic_pm_domain **domains;
     85	const struct scmi_handle *handle = sdev->handle;
     86	struct scmi_protocol_handle *ph;
     87
     88	if (!handle)
     89		return -ENODEV;
     90
     91	power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph);
     92	if (IS_ERR(power_ops))
     93		return PTR_ERR(power_ops);
     94
     95	num_domains = power_ops->num_domains_get(ph);
     96	if (num_domains < 0) {
     97		dev_err(dev, "number of domains not found\n");
     98		return num_domains;
     99	}
    100
    101	scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
    102	if (!scmi_pd)
    103		return -ENOMEM;
    104
    105	scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
    106	if (!scmi_pd_data)
    107		return -ENOMEM;
    108
    109	domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
    110	if (!domains)
    111		return -ENOMEM;
    112
    113	for (i = 0; i < num_domains; i++, scmi_pd++) {
    114		u32 state;
    115
    116		if (power_ops->state_get(ph, i, &state)) {
    117			dev_warn(dev, "failed to get state for domain %d\n", i);
    118			continue;
    119		}
    120
    121		scmi_pd->domain = i;
    122		scmi_pd->ph = ph;
    123		scmi_pd->name = power_ops->name_get(ph, i);
    124		scmi_pd->genpd.name = scmi_pd->name;
    125		scmi_pd->genpd.power_off = scmi_pd_power_off;
    126		scmi_pd->genpd.power_on = scmi_pd_power_on;
    127		scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
    128		scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
    129		scmi_pd->genpd.flags = GENPD_FLAG_PM_CLK |
    130				       GENPD_FLAG_ACTIVE_WAKEUP;
    131
    132		pm_genpd_init(&scmi_pd->genpd, NULL,
    133			      state == SCMI_POWER_STATE_GENERIC_OFF);
    134
    135		domains[i] = &scmi_pd->genpd;
    136	}
    137
    138	scmi_pd_data->domains = domains;
    139	scmi_pd_data->num_domains = num_domains;
    140
    141	return of_genpd_add_provider_onecell(np, scmi_pd_data);
    142}
    143
    144static const struct scmi_device_id scmi_id_table[] = {
    145	{ SCMI_PROTOCOL_POWER, "genpd" },
    146	{ },
    147};
    148MODULE_DEVICE_TABLE(scmi, scmi_id_table);
    149
    150static struct scmi_driver scmi_power_domain_driver = {
    151	.name = "scmi-power-domain",
    152	.probe = scmi_pm_domain_probe,
    153	.id_table = scmi_id_table,
    154};
    155module_scmi_driver(scmi_power_domain_driver);
    156
    157MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
    158MODULE_DESCRIPTION("ARM SCMI power domain driver");
    159MODULE_LICENSE("GPL v2");