Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

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