| .. | .. |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * SCMI Generic power domain support. |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Copyright (C) 2018 ARM Ltd. |
|---|
| 5 | + * Copyright (C) 2018-2020 ARM Ltd. |
|---|
| 6 | 6 | */ |
|---|
| 7 | 7 | |
|---|
| 8 | 8 | #include <linux/err.h> |
|---|
| .. | .. |
|---|
| 11 | 11 | #include <linux/pm_domain.h> |
|---|
| 12 | 12 | #include <linux/scmi_protocol.h> |
|---|
| 13 | 13 | |
|---|
| 14 | +static const struct scmi_power_proto_ops *power_ops; |
|---|
| 15 | + |
|---|
| 14 | 16 | struct scmi_pm_domain { |
|---|
| 15 | 17 | struct generic_pm_domain genpd; |
|---|
| 16 | | - const struct scmi_handle *handle; |
|---|
| 18 | + const struct scmi_protocol_handle *ph; |
|---|
| 17 | 19 | const char *name; |
|---|
| 18 | 20 | u32 domain; |
|---|
| 19 | 21 | }; |
|---|
| .. | .. |
|---|
| 25 | 27 | int ret; |
|---|
| 26 | 28 | u32 state, ret_state; |
|---|
| 27 | 29 | struct scmi_pm_domain *pd = to_scmi_pd(domain); |
|---|
| 28 | | - const struct scmi_power_ops *ops = pd->handle->power_ops; |
|---|
| 29 | 30 | |
|---|
| 30 | 31 | if (power_on) |
|---|
| 31 | 32 | state = SCMI_POWER_STATE_GENERIC_ON; |
|---|
| 32 | 33 | else |
|---|
| 33 | 34 | state = SCMI_POWER_STATE_GENERIC_OFF; |
|---|
| 34 | 35 | |
|---|
| 35 | | - ret = ops->state_set(pd->handle, pd->domain, state); |
|---|
| 36 | + ret = power_ops->state_set(pd->ph, pd->domain, state); |
|---|
| 36 | 37 | if (!ret) |
|---|
| 37 | | - ret = ops->state_get(pd->handle, pd->domain, &ret_state); |
|---|
| 38 | + ret = power_ops->state_get(pd->ph, pd->domain, &ret_state); |
|---|
| 38 | 39 | if (!ret && state != ret_state) |
|---|
| 39 | 40 | return -EIO; |
|---|
| 40 | 41 | |
|---|
| .. | .. |
|---|
| 60 | 61 | struct genpd_onecell_data *scmi_pd_data; |
|---|
| 61 | 62 | struct generic_pm_domain **domains; |
|---|
| 62 | 63 | const struct scmi_handle *handle = sdev->handle; |
|---|
| 64 | + struct scmi_protocol_handle *ph; |
|---|
| 63 | 65 | |
|---|
| 64 | | - if (!handle || !handle->power_ops) |
|---|
| 66 | + if (!handle) |
|---|
| 65 | 67 | return -ENODEV; |
|---|
| 66 | 68 | |
|---|
| 67 | | - num_domains = handle->power_ops->num_domains_get(handle); |
|---|
| 69 | + power_ops = handle->devm_get_protocol(sdev, SCMI_PROTOCOL_POWER, &ph); |
|---|
| 70 | + if (IS_ERR(power_ops)) |
|---|
| 71 | + return PTR_ERR(power_ops); |
|---|
| 72 | + |
|---|
| 73 | + num_domains = power_ops->num_domains_get(ph); |
|---|
| 68 | 74 | if (num_domains < 0) { |
|---|
| 69 | 75 | dev_err(dev, "number of domains not found\n"); |
|---|
| 70 | 76 | return num_domains; |
|---|
| .. | .. |
|---|
| 85 | 91 | for (i = 0; i < num_domains; i++, scmi_pd++) { |
|---|
| 86 | 92 | u32 state; |
|---|
| 87 | 93 | |
|---|
| 88 | | - if (handle->power_ops->state_get(handle, i, &state)) { |
|---|
| 94 | + if (power_ops->state_get(ph, i, &state)) { |
|---|
| 89 | 95 | dev_warn(dev, "failed to get state for domain %d\n", i); |
|---|
| 90 | 96 | continue; |
|---|
| 91 | 97 | } |
|---|
| 92 | 98 | |
|---|
| 93 | 99 | scmi_pd->domain = i; |
|---|
| 94 | | - scmi_pd->handle = handle; |
|---|
| 95 | | - scmi_pd->name = handle->power_ops->name_get(handle, i); |
|---|
| 100 | + scmi_pd->ph = ph; |
|---|
| 101 | + scmi_pd->name = power_ops->name_get(ph, i); |
|---|
| 96 | 102 | scmi_pd->genpd.name = scmi_pd->name; |
|---|
| 97 | 103 | scmi_pd->genpd.power_off = scmi_pd_power_off; |
|---|
| 98 | 104 | scmi_pd->genpd.power_on = scmi_pd_power_on; |
|---|
| .. | .. |
|---|
| 106 | 112 | scmi_pd_data->domains = domains; |
|---|
| 107 | 113 | scmi_pd_data->num_domains = num_domains; |
|---|
| 108 | 114 | |
|---|
| 115 | + dev_set_drvdata(dev, scmi_pd_data); |
|---|
| 116 | + |
|---|
| 109 | 117 | return of_genpd_add_provider_onecell(np, scmi_pd_data); |
|---|
| 110 | 118 | } |
|---|
| 111 | 119 | |
|---|
| 120 | +static void scmi_pm_domain_remove(struct scmi_device *sdev) |
|---|
| 121 | +{ |
|---|
| 122 | + int i; |
|---|
| 123 | + struct genpd_onecell_data *scmi_pd_data; |
|---|
| 124 | + struct device *dev = &sdev->dev; |
|---|
| 125 | + struct device_node *np = dev->of_node; |
|---|
| 126 | + |
|---|
| 127 | + of_genpd_del_provider(np); |
|---|
| 128 | + |
|---|
| 129 | + scmi_pd_data = dev_get_drvdata(dev); |
|---|
| 130 | + for (i = 0; i < scmi_pd_data->num_domains; i++) { |
|---|
| 131 | + if (!scmi_pd_data->domains[i]) |
|---|
| 132 | + continue; |
|---|
| 133 | + pm_genpd_remove(scmi_pd_data->domains[i]); |
|---|
| 134 | + } |
|---|
| 135 | +} |
|---|
| 136 | + |
|---|
| 112 | 137 | static const struct scmi_device_id scmi_id_table[] = { |
|---|
| 113 | | - { SCMI_PROTOCOL_POWER }, |
|---|
| 138 | + { SCMI_PROTOCOL_POWER, "genpd" }, |
|---|
| 114 | 139 | { }, |
|---|
| 115 | 140 | }; |
|---|
| 116 | 141 | MODULE_DEVICE_TABLE(scmi, scmi_id_table); |
|---|
| .. | .. |
|---|
| 118 | 143 | static struct scmi_driver scmi_power_domain_driver = { |
|---|
| 119 | 144 | .name = "scmi-power-domain", |
|---|
| 120 | 145 | .probe = scmi_pm_domain_probe, |
|---|
| 146 | + .remove = scmi_pm_domain_remove, |
|---|
| 121 | 147 | .id_table = scmi_id_table, |
|---|
| 122 | 148 | }; |
|---|
| 123 | 149 | module_scmi_driver(scmi_power_domain_driver); |
|---|