0001
0002
0003
0004
0005
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");