Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SCMI Generic power domain support.
0004  *
0005  * Copyright (C) 2018-2021 ARM Ltd.
0006  */
0007 
0008 #include <linux/err.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/pm_domain.h>
0012 #include <linux/scmi_protocol.h>
0013 
0014 static const struct scmi_power_proto_ops *power_ops;
0015 
0016 struct scmi_pm_domain {
0017     struct generic_pm_domain genpd;
0018     const struct scmi_protocol_handle *ph;
0019     const char *name;
0020     u32 domain;
0021 };
0022 
0023 #define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
0024 
0025 static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
0026 {
0027     int ret;
0028     u32 state, ret_state;
0029     struct scmi_pm_domain *pd = to_scmi_pd(domain);
0030 
0031     if (power_on)
0032         state = SCMI_POWER_STATE_GENERIC_ON;
0033     else
0034         state = SCMI_POWER_STATE_GENERIC_OFF;
0035 
0036     ret = power_ops->state_set(pd->ph, pd->domain, state);
0037     if (!ret)
0038         ret = power_ops->state_get(pd->ph, pd->domain, &ret_state);
0039     if (!ret && state != ret_state)
0040         return -EIO;
0041 
0042     return ret;
0043 }
0044 
0045 static int scmi_pd_power_on(struct generic_pm_domain *domain)
0046 {
0047     return scmi_pd_power(domain, true);
0048 }
0049 
0050 static int scmi_pd_power_off(struct generic_pm_domain *domain)
0051 {
0052     return scmi_pd_power(domain, false);
0053 }
0054 
0055 static int scmi_pm_domain_probe(struct scmi_device *sdev)
0056 {
0057     int num_domains, i;
0058     struct device *dev = &sdev->dev;
0059     struct device_node *np = dev->of_node;
0060     struct scmi_pm_domain *scmi_pd;
0061     struct genpd_onecell_data *scmi_pd_data;
0062     struct generic_pm_domain **domains;
0063     const struct scmi_handle *handle = sdev->handle;
0064     struct scmi_protocol_handle *ph;
0065 
0066     if (!handle)
0067         return -ENODEV;
0068 
0069     power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph);
0070     if (IS_ERR(power_ops))
0071         return PTR_ERR(power_ops);
0072 
0073     num_domains = power_ops->num_domains_get(ph);
0074     if (num_domains < 0) {
0075         dev_err(dev, "number of domains not found\n");
0076         return num_domains;
0077     }
0078 
0079     scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
0080     if (!scmi_pd)
0081         return -ENOMEM;
0082 
0083     scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
0084     if (!scmi_pd_data)
0085         return -ENOMEM;
0086 
0087     domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
0088     if (!domains)
0089         return -ENOMEM;
0090 
0091     for (i = 0; i < num_domains; i++, scmi_pd++) {
0092         u32 state;
0093 
0094         if (power_ops->state_get(ph, i, &state)) {
0095             dev_warn(dev, "failed to get state for domain %d\n", i);
0096             continue;
0097         }
0098 
0099         scmi_pd->domain = i;
0100         scmi_pd->ph = ph;
0101         scmi_pd->name = power_ops->name_get(ph, i);
0102         scmi_pd->genpd.name = scmi_pd->name;
0103         scmi_pd->genpd.power_off = scmi_pd_power_off;
0104         scmi_pd->genpd.power_on = scmi_pd_power_on;
0105 
0106         pm_genpd_init(&scmi_pd->genpd, NULL,
0107                   state == SCMI_POWER_STATE_GENERIC_OFF);
0108 
0109         domains[i] = &scmi_pd->genpd;
0110     }
0111 
0112     scmi_pd_data->domains = domains;
0113     scmi_pd_data->num_domains = num_domains;
0114 
0115     dev_set_drvdata(dev, scmi_pd_data);
0116 
0117     return of_genpd_add_provider_onecell(np, scmi_pd_data);
0118 }
0119 
0120 static void scmi_pm_domain_remove(struct scmi_device *sdev)
0121 {
0122     int i;
0123     struct genpd_onecell_data *scmi_pd_data;
0124     struct device *dev = &sdev->dev;
0125     struct device_node *np = dev->of_node;
0126 
0127     of_genpd_del_provider(np);
0128 
0129     scmi_pd_data = dev_get_drvdata(dev);
0130     for (i = 0; i < scmi_pd_data->num_domains; i++) {
0131         if (!scmi_pd_data->domains[i])
0132             continue;
0133         pm_genpd_remove(scmi_pd_data->domains[i]);
0134     }
0135 }
0136 
0137 static const struct scmi_device_id scmi_id_table[] = {
0138     { SCMI_PROTOCOL_POWER, "genpd" },
0139     { },
0140 };
0141 MODULE_DEVICE_TABLE(scmi, scmi_id_table);
0142 
0143 static struct scmi_driver scmi_power_domain_driver = {
0144     .name = "scmi-power-domain",
0145     .probe = scmi_pm_domain_probe,
0146     .remove = scmi_pm_domain_remove,
0147     .id_table = scmi_id_table,
0148 };
0149 module_scmi_driver(scmi_power_domain_driver);
0150 
0151 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
0152 MODULE_DESCRIPTION("ARM SCMI power domain driver");
0153 MODULE_LICENSE("GPL v2");