|
20 | 20 | #include <linux/of_platform.h>
|
21 | 21 | #include <linux/slab.h>
|
22 | 22 | #include <linux/uaccess.h>
|
| 23 | +#include <linux/hashtable.h> |
23 | 24 |
|
24 | 25 | #include <linux/firmware/xlnx-zynqmp.h>
|
25 | 26 | #include "zynqmp-debug.h"
|
26 | 27 |
|
| 28 | +/* Max HashMap Order for PM API feature check (1<<7 = 128) */ |
| 29 | +#define PM_API_FEATURE_CHECK_MAX_ORDER 7 |
| 30 | + |
27 | 31 | static bool feature_check_enabled;
|
28 |
| -static u32 zynqmp_pm_features[PM_API_MAX]; |
| 32 | +DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); |
| 33 | + |
| 34 | +/** |
| 35 | + * struct pm_api_feature_data - PM API Feature data |
| 36 | + * @pm_api_id: PM API Id, used as key to index into hashmap |
| 37 | + * @feature_status: status of PM API feature: valid, invalid |
| 38 | + * @hentry: hlist_node that hooks this entry into hashtable |
| 39 | + */ |
| 40 | +struct pm_api_feature_data { |
| 41 | + u32 pm_api_id; |
| 42 | + int feature_status; |
| 43 | + struct hlist_node hentry; |
| 44 | +}; |
29 | 45 |
|
30 | 46 | static const struct mfd_cell firmware_devs[] = {
|
31 | 47 | {
|
@@ -142,29 +158,37 @@ static int zynqmp_pm_feature(u32 api_id)
|
142 | 158 | int ret;
|
143 | 159 | u32 ret_payload[PAYLOAD_ARG_CNT];
|
144 | 160 | u64 smc_arg[2];
|
| 161 | + struct pm_api_feature_data *feature_data; |
145 | 162 |
|
146 | 163 | if (!feature_check_enabled)
|
147 | 164 | return 0;
|
148 | 165 |
|
149 |
| - /* Return value if feature is already checked */ |
150 |
| - if (api_id > ARRAY_SIZE(zynqmp_pm_features)) |
151 |
| - return PM_FEATURE_INVALID; |
| 166 | + /* Check for existing entry in hash table for given api */ |
| 167 | + hash_for_each_possible(pm_api_features_map, feature_data, hentry, |
| 168 | + api_id) { |
| 169 | + if (feature_data->pm_api_id == api_id) |
| 170 | + return feature_data->feature_status; |
| 171 | + } |
152 | 172 |
|
153 |
| - if (zynqmp_pm_features[api_id] != PM_FEATURE_UNCHECKED) |
154 |
| - return zynqmp_pm_features[api_id]; |
| 173 | + /* Add new entry if not present */ |
| 174 | + feature_data = kmalloc(sizeof(*feature_data), GFP_KERNEL); |
| 175 | + if (!feature_data) |
| 176 | + return -ENOMEM; |
155 | 177 |
|
| 178 | + feature_data->pm_api_id = api_id; |
156 | 179 | smc_arg[0] = PM_SIP_SVC | PM_FEATURE_CHECK;
|
157 | 180 | smc_arg[1] = api_id;
|
158 | 181 |
|
159 | 182 | ret = do_fw_call(smc_arg[0], smc_arg[1], 0, ret_payload);
|
160 |
| - if (ret) { |
161 |
| - zynqmp_pm_features[api_id] = PM_FEATURE_INVALID; |
162 |
| - return PM_FEATURE_INVALID; |
163 |
| - } |
| 183 | + if (ret) |
| 184 | + ret = -EOPNOTSUPP; |
| 185 | + else |
| 186 | + ret = ret_payload[1]; |
164 | 187 |
|
165 |
| - zynqmp_pm_features[api_id] = ret_payload[1]; |
| 188 | + feature_data->feature_status = ret; |
| 189 | + hash_add(pm_api_features_map, &feature_data->hentry, api_id); |
166 | 190 |
|
167 |
| - return zynqmp_pm_features[api_id]; |
| 191 | + return ret; |
168 | 192 | }
|
169 | 193 |
|
170 | 194 | /**
|
@@ -200,9 +224,12 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
|
200 | 224 | * Make sure to stay in x0 register
|
201 | 225 | */
|
202 | 226 | u64 smc_arg[4];
|
| 227 | + int ret; |
203 | 228 |
|
204 |
| - if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID) |
205 |
| - return -ENOTSUPP; |
| 229 | + /* Check if feature is supported or not */ |
| 230 | + ret = zynqmp_pm_feature(pm_api_id); |
| 231 | + if (ret < 0) |
| 232 | + return ret; |
206 | 233 |
|
207 | 234 | smc_arg[0] = PM_SIP_SVC | pm_api_id;
|
208 | 235 | smc_arg[1] = ((u64)arg1 << 32) | arg0;
|
@@ -1252,9 +1279,17 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
|
1252 | 1279 |
|
1253 | 1280 | static int zynqmp_firmware_remove(struct platform_device *pdev)
|
1254 | 1281 | {
|
| 1282 | + struct pm_api_feature_data *feature_data; |
| 1283 | + int i; |
| 1284 | + |
1255 | 1285 | mfd_remove_devices(&pdev->dev);
|
1256 | 1286 | zynqmp_pm_api_debugfs_exit();
|
1257 | 1287 |
|
| 1288 | + hash_for_each(pm_api_features_map, i, feature_data, hentry) { |
| 1289 | + hash_del(&feature_data->hentry); |
| 1290 | + kfree(feature_data); |
| 1291 | + } |
| 1292 | + |
1258 | 1293 | return 0;
|
1259 | 1294 | }
|
1260 | 1295 |
|
|
0 commit comments