Commit | Line | Data |
---|---|---|
b5beae5e OH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | #define pr_fmt(fmt) "papr-scm: " fmt | |
4 | ||
5 | #include <linux/of.h> | |
6 | #include <linux/kernel.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/ioport.h> | |
9 | #include <linux/slab.h> | |
10 | #include <linux/ndctl.h> | |
11 | #include <linux/sched.h> | |
12 | #include <linux/libnvdimm.h> | |
13 | #include <linux/platform_device.h> | |
0d7fc080 | 14 | #include <linux/delay.h> |
b791abf3 | 15 | #include <linux/seq_buf.h> |
85343a8d | 16 | #include <linux/nd.h> |
b5beae5e OH |
17 | |
18 | #include <asm/plpar_wrappers.h> | |
dbc8fc9d SB |
19 | #include <uapi/linux/papr_pdsm.h> |
20 | #include <linux/papr_scm.h> | |
85343a8d | 21 | #include <asm/mce.h> |
0e8554b5 | 22 | #include <asm/unaligned.h> |
4c08d4bb | 23 | #include <linux/perf_event.h> |
b5beae5e OH |
24 | |
25 | #define BIND_ANY_ADDR (~0ul) | |
26 | ||
27 | #define PAPR_SCM_DIMM_CMD_MASK \ | |
28 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ | |
29 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ | |
f517f792 VJ |
30 | (1ul << ND_CMD_SET_CONFIG_DATA) | \ |
31 | (1ul << ND_CMD_CALL)) | |
b5beae5e | 32 | |
2d02bf83 VJ |
33 | /* Struct holding a single performance metric */ |
34 | struct papr_scm_perf_stat { | |
35 | u8 stat_id[8]; | |
36 | __be64 stat_val; | |
37 | } __packed; | |
38 | ||
39 | /* Struct exchanged between kernel and PHYP for fetching drc perf stats */ | |
40 | struct papr_scm_perf_stats { | |
41 | u8 eye_catcher[8]; | |
42 | /* Should be PAPR_SCM_PERF_STATS_VERSION */ | |
43 | __be32 stats_version; | |
44 | /* Number of stats following */ | |
45 | __be32 num_statistics; | |
46 | /* zero or more performance matrics */ | |
47 | struct papr_scm_perf_stat scm_statistic[]; | |
48 | } __packed; | |
49 | ||
b791abf3 | 50 | /* private struct associated with each region */ |
b5beae5e OH |
51 | struct papr_scm_priv { |
52 | struct platform_device *pdev; | |
53 | struct device_node *dn; | |
54 | uint32_t drc_index; | |
55 | uint64_t blocks; | |
56 | uint64_t block_size; | |
57 | int metadata_size; | |
2a0ffbd4 | 58 | bool is_volatile; |
75b7c05e | 59 | bool hcall_flush_required; |
b5beae5e OH |
60 | |
61 | uint64_t bound_addr; | |
62 | ||
63 | struct nvdimm_bus_descriptor bus_desc; | |
64 | struct nvdimm_bus *bus; | |
65 | struct nvdimm *nvdimm; | |
66 | struct resource res; | |
67 | struct nd_region *region; | |
68 | struct nd_interleave_set nd_set; | |
85343a8d | 69 | struct list_head region_list; |
b791abf3 VJ |
70 | |
71 | /* Protect dimm health data from concurrent read/writes */ | |
72 | struct mutex health_mutex; | |
73 | ||
74 | /* Last time the health information of the dimm was updated */ | |
75 | unsigned long lasthealth_jiffies; | |
76 | ||
77 | /* Health information for the dimm */ | |
78 | u64 health_bitmap; | |
2d02bf83 | 79 | |
de21e137 VJ |
80 | /* Holds the last known dirty shutdown counter value */ |
81 | u64 dirty_shutdown_counter; | |
82 | ||
2d02bf83 VJ |
83 | /* length of the stat buffer as expected by phyp */ |
84 | size_t stat_buffer_len; | |
bbbca723 VJ |
85 | |
86 | /* The bits which needs to be overridden */ | |
87 | u64 health_bitmap_inject_mask; | |
b5beae5e OH |
88 | }; |
89 | ||
75b7c05e SB |
90 | static int papr_scm_pmem_flush(struct nd_region *nd_region, |
91 | struct bio *bio __maybe_unused) | |
92 | { | |
93 | struct papr_scm_priv *p = nd_region_provider_data(nd_region); | |
94 | unsigned long ret_buf[PLPAR_HCALL_BUFSIZE], token = 0; | |
95 | long rc; | |
96 | ||
97 | dev_dbg(&p->pdev->dev, "flush drc 0x%x", p->drc_index); | |
98 | ||
99 | do { | |
100 | rc = plpar_hcall(H_SCM_FLUSH, ret_buf, p->drc_index, token); | |
101 | token = ret_buf[0]; | |
102 | ||
103 | /* Check if we are stalled for some time */ | |
104 | if (H_IS_LONG_BUSY(rc)) { | |
105 | msleep(get_longbusy_msecs(rc)); | |
106 | rc = H_BUSY; | |
107 | } else if (rc == H_BUSY) { | |
108 | cond_resched(); | |
109 | } | |
110 | } while (rc == H_BUSY); | |
111 | ||
112 | if (rc) { | |
7767d9ac | 113 | dev_err(&p->pdev->dev, "flush error: %ld", rc); |
75b7c05e SB |
114 | rc = -EIO; |
115 | } else { | |
116 | dev_dbg(&p->pdev->dev, "flush drc 0x%x complete", p->drc_index); | |
117 | } | |
118 | ||
119 | return rc; | |
120 | } | |
121 | ||
19a551b2 WY |
122 | static LIST_HEAD(papr_nd_regions); |
123 | static DEFINE_MUTEX(papr_ndr_lock); | |
85343a8d | 124 | |
b5beae5e OH |
125 | static int drc_pmem_bind(struct papr_scm_priv *p) |
126 | { | |
127 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; | |
5a3840a4 | 128 | uint64_t saved = 0; |
3a855b7a VJ |
129 | uint64_t token; |
130 | int64_t rc; | |
b5beae5e OH |
131 | |
132 | /* | |
133 | * When the hypervisor cannot map all the requested memory in a single | |
134 | * hcall it returns H_BUSY and we call again with the token until | |
135 | * we get H_SUCCESS. Aborting the retry loop before getting H_SUCCESS | |
136 | * leave the system in an undefined state, so we wait. | |
137 | */ | |
138 | token = 0; | |
139 | ||
140 | do { | |
141 | rc = plpar_hcall(H_SCM_BIND_MEM, ret, p->drc_index, 0, | |
142 | p->blocks, BIND_ANY_ADDR, token); | |
409dd7dc | 143 | token = ret[0]; |
5a3840a4 OH |
144 | if (!saved) |
145 | saved = ret[1]; | |
b5beae5e OH |
146 | cond_resched(); |
147 | } while (rc == H_BUSY); | |
148 | ||
faa6d211 | 149 | if (rc) |
4111cdef | 150 | return rc; |
b5beae5e | 151 | |
5a3840a4 | 152 | p->bound_addr = saved; |
0eb59382 AK |
153 | dev_dbg(&p->pdev->dev, "bound drc 0x%x to 0x%lx\n", |
154 | p->drc_index, (unsigned long)saved); | |
4111cdef | 155 | return rc; |
b5beae5e OH |
156 | } |
157 | ||
4111cdef | 158 | static void drc_pmem_unbind(struct papr_scm_priv *p) |
b5beae5e OH |
159 | { |
160 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; | |
0d7fc080 VJ |
161 | uint64_t token = 0; |
162 | int64_t rc; | |
b5beae5e | 163 | |
4111cdef | 164 | dev_dbg(&p->pdev->dev, "unbind drc 0x%x\n", p->drc_index); |
b5beae5e | 165 | |
0d7fc080 | 166 | /* NB: unbind has the same retry requirements as drc_pmem_bind() */ |
b5beae5e | 167 | do { |
0d7fc080 VJ |
168 | |
169 | /* Unbind of all SCM resources associated with drcIndex */ | |
170 | rc = plpar_hcall(H_SCM_UNBIND_ALL, ret, H_UNBIND_SCOPE_DRC, | |
171 | p->drc_index, token); | |
409dd7dc | 172 | token = ret[0]; |
0d7fc080 VJ |
173 | |
174 | /* Check if we are stalled for some time */ | |
175 | if (H_IS_LONG_BUSY(rc)) { | |
176 | msleep(get_longbusy_msecs(rc)); | |
177 | rc = H_BUSY; | |
178 | } else if (rc == H_BUSY) { | |
179 | cond_resched(); | |
180 | } | |
181 | ||
b5beae5e OH |
182 | } while (rc == H_BUSY); |
183 | ||
184 | if (rc) | |
185 | dev_err(&p->pdev->dev, "unbind error: %lld\n", rc); | |
0d7fc080 | 186 | else |
4111cdef | 187 | dev_dbg(&p->pdev->dev, "unbind drc 0x%x complete\n", |
0d7fc080 | 188 | p->drc_index); |
b5beae5e | 189 | |
4111cdef | 190 | return; |
b5beae5e OH |
191 | } |
192 | ||
faa6d211 AK |
193 | static int drc_pmem_query_n_bind(struct papr_scm_priv *p) |
194 | { | |
195 | unsigned long start_addr; | |
196 | unsigned long end_addr; | |
197 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; | |
198 | int64_t rc; | |
199 | ||
200 | ||
201 | rc = plpar_hcall(H_SCM_QUERY_BLOCK_MEM_BINDING, ret, | |
202 | p->drc_index, 0); | |
203 | if (rc) | |
204 | goto err_out; | |
205 | start_addr = ret[0]; | |
206 | ||
207 | /* Make sure the full region is bound. */ | |
208 | rc = plpar_hcall(H_SCM_QUERY_BLOCK_MEM_BINDING, ret, | |
209 | p->drc_index, p->blocks - 1); | |
210 | if (rc) | |
211 | goto err_out; | |
212 | end_addr = ret[0]; | |
213 | ||
214 | if ((end_addr - start_addr) != ((p->blocks - 1) * p->block_size)) | |
215 | goto err_out; | |
216 | ||
217 | p->bound_addr = start_addr; | |
0eb59382 | 218 | dev_dbg(&p->pdev->dev, "bound drc 0x%x to 0x%lx\n", p->drc_index, start_addr); |
faa6d211 AK |
219 | return rc; |
220 | ||
221 | err_out: | |
222 | dev_info(&p->pdev->dev, | |
223 | "Failed to query, trying an unbind followed by bind"); | |
224 | drc_pmem_unbind(p); | |
225 | return drc_pmem_bind(p); | |
226 | } | |
227 | ||
2d02bf83 VJ |
228 | /* |
229 | * Query the Dimm performance stats from PHYP and copy them (if returned) to | |
230 | * provided struct papr_scm_perf_stats instance 'stats' that can hold atleast | |
231 | * (num_stats + header) bytes. | |
d2827e5e | 232 | * - If buff_stats == NULL the return value is the size in bytes of the buffer |
2d02bf83 VJ |
233 | * needed to hold all supported performance-statistics. |
234 | * - If buff_stats != NULL and num_stats == 0 then we copy all known | |
235 | * performance-statistics to 'buff_stat' and expect to be large enough to | |
236 | * hold them. | |
237 | * - if buff_stats != NULL and num_stats > 0 then copy the requested | |
238 | * performance-statistics to buff_stats. | |
239 | */ | |
240 | static ssize_t drc_pmem_query_stats(struct papr_scm_priv *p, | |
241 | struct papr_scm_perf_stats *buff_stats, | |
242 | unsigned int num_stats) | |
243 | { | |
244 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; | |
245 | size_t size; | |
246 | s64 rc; | |
247 | ||
248 | /* Setup the out buffer */ | |
249 | if (buff_stats) { | |
250 | memcpy(buff_stats->eye_catcher, | |
251 | PAPR_SCM_PERF_STATS_EYECATCHER, 8); | |
252 | buff_stats->stats_version = | |
253 | cpu_to_be32(PAPR_SCM_PERF_STATS_VERSION); | |
254 | buff_stats->num_statistics = | |
255 | cpu_to_be32(num_stats); | |
256 | ||
257 | /* | |
258 | * Calculate the buffer size based on num-stats provided | |
259 | * or use the prefetched max buffer length | |
260 | */ | |
261 | if (num_stats) | |
262 | /* Calculate size from the num_stats */ | |
263 | size = sizeof(struct papr_scm_perf_stats) + | |
264 | num_stats * sizeof(struct papr_scm_perf_stat); | |
265 | else | |
266 | size = p->stat_buffer_len; | |
267 | } else { | |
268 | /* In case of no out buffer ignore the size */ | |
269 | size = 0; | |
270 | } | |
271 | ||
272 | /* Do the HCALL asking PHYP for info */ | |
273 | rc = plpar_hcall(H_SCM_PERFORMANCE_STATS, ret, p->drc_index, | |
274 | buff_stats ? virt_to_phys(buff_stats) : 0, | |
275 | size); | |
276 | ||
277 | /* Check if the error was due to an unknown stat-id */ | |
278 | if (rc == H_PARTIAL) { | |
279 | dev_err(&p->pdev->dev, | |
280 | "Unknown performance stats, Err:0x%016lX\n", ret[0]); | |
281 | return -ENOENT; | |
f3f6d184 VJ |
282 | } else if (rc == H_AUTHORITY) { |
283 | dev_info(&p->pdev->dev, | |
284 | "Permission denied while accessing performance stats"); | |
285 | return -EPERM; | |
286 | } else if (rc == H_UNSUPPORTED) { | |
287 | dev_dbg(&p->pdev->dev, "Performance stats unsupported\n"); | |
288 | return -EOPNOTSUPP; | |
2d02bf83 VJ |
289 | } else if (rc != H_SUCCESS) { |
290 | dev_err(&p->pdev->dev, | |
291 | "Failed to query performance stats, Err:%lld\n", rc); | |
292 | return -EIO; | |
293 | ||
294 | } else if (!size) { | |
295 | /* Handle case where stat buffer size was requested */ | |
296 | dev_dbg(&p->pdev->dev, | |
297 | "Performance stats size %ld\n", ret[0]); | |
298 | return ret[0]; | |
299 | } | |
300 | ||
301 | /* Successfully fetched the requested stats from phyp */ | |
302 | dev_dbg(&p->pdev->dev, | |
303 | "Performance stats returned %d stats\n", | |
304 | be32_to_cpu(buff_stats->num_statistics)); | |
305 | return 0; | |
306 | } | |
307 | ||
d0007eb1 KJ |
308 | #ifdef CONFIG_PERF_EVENTS |
309 | #define to_nvdimm_pmu(_pmu) container_of(_pmu, struct nvdimm_pmu, pmu) | |
310 | ||
9b1ac046 KJ |
311 | static const char * const nvdimm_events_map[] = { |
312 | [1] = "CtlResCt", | |
313 | [2] = "CtlResTm", | |
314 | [3] = "PonSecs ", | |
315 | [4] = "MemLife ", | |
316 | [5] = "CritRscU", | |
317 | [6] = "HostLCnt", | |
318 | [7] = "HostSCnt", | |
319 | [8] = "HostSDur", | |
320 | [9] = "HostLDur", | |
321 | [10] = "MedRCnt ", | |
322 | [11] = "MedWCnt ", | |
323 | [12] = "MedRDur ", | |
324 | [13] = "MedWDur ", | |
325 | [14] = "CchRHCnt", | |
326 | [15] = "CchWHCnt", | |
327 | [16] = "FastWCnt", | |
328 | }; | |
329 | ||
4c08d4bb KJ |
330 | static int papr_scm_pmu_get_value(struct perf_event *event, struct device *dev, u64 *count) |
331 | { | |
332 | struct papr_scm_perf_stat *stat; | |
333 | struct papr_scm_perf_stats *stats; | |
d9abe36d | 334 | struct papr_scm_priv *p = dev_get_drvdata(dev); |
4c08d4bb KJ |
335 | int rc, size; |
336 | ||
9b1ac046 KJ |
337 | /* Invalid eventcode */ |
338 | if (event->attr.config == 0 || event->attr.config >= ARRAY_SIZE(nvdimm_events_map)) | |
339 | return -EINVAL; | |
340 | ||
4c08d4bb KJ |
341 | /* Allocate request buffer enough to hold single performance stat */ |
342 | size = sizeof(struct papr_scm_perf_stats) + | |
343 | sizeof(struct papr_scm_perf_stat); | |
344 | ||
9b1ac046 | 345 | if (!p) |
4c08d4bb KJ |
346 | return -EINVAL; |
347 | ||
348 | stats = kzalloc(size, GFP_KERNEL); | |
349 | if (!stats) | |
350 | return -ENOMEM; | |
351 | ||
352 | stat = &stats->scm_statistic[0]; | |
353 | memcpy(&stat->stat_id, | |
9b1ac046 | 354 | nvdimm_events_map[event->attr.config], |
4c08d4bb KJ |
355 | sizeof(stat->stat_id)); |
356 | stat->stat_val = 0; | |
357 | ||
358 | rc = drc_pmem_query_stats(p, stats, 1); | |
359 | if (rc < 0) { | |
360 | kfree(stats); | |
361 | return rc; | |
362 | } | |
363 | ||
364 | *count = be64_to_cpu(stat->stat_val); | |
365 | kfree(stats); | |
366 | return 0; | |
367 | } | |
368 | ||
369 | static int papr_scm_pmu_event_init(struct perf_event *event) | |
370 | { | |
371 | struct nvdimm_pmu *nd_pmu = to_nvdimm_pmu(event->pmu); | |
372 | struct papr_scm_priv *p; | |
373 | ||
374 | if (!nd_pmu) | |
375 | return -EINVAL; | |
376 | ||
377 | /* test the event attr type for PMU enumeration */ | |
378 | if (event->attr.type != event->pmu->type) | |
379 | return -ENOENT; | |
380 | ||
381 | /* it does not support event sampling mode */ | |
382 | if (is_sampling_event(event)) | |
383 | return -EOPNOTSUPP; | |
384 | ||
385 | /* no branch sampling */ | |
386 | if (has_branch_stack(event)) | |
387 | return -EOPNOTSUPP; | |
388 | ||
389 | p = (struct papr_scm_priv *)nd_pmu->dev->driver_data; | |
390 | if (!p) | |
391 | return -EINVAL; | |
392 | ||
393 | /* Invalid eventcode */ | |
394 | if (event->attr.config == 0 || event->attr.config > 16) | |
395 | return -EINVAL; | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static int papr_scm_pmu_add(struct perf_event *event, int flags) | |
401 | { | |
402 | u64 count; | |
403 | int rc; | |
404 | struct nvdimm_pmu *nd_pmu = to_nvdimm_pmu(event->pmu); | |
405 | ||
406 | if (!nd_pmu) | |
407 | return -EINVAL; | |
408 | ||
409 | if (flags & PERF_EF_START) { | |
410 | rc = papr_scm_pmu_get_value(event, nd_pmu->dev, &count); | |
411 | if (rc) | |
412 | return rc; | |
413 | ||
414 | local64_set(&event->hw.prev_count, count); | |
415 | } | |
416 | ||
417 | return 0; | |
418 | } | |
419 | ||
420 | static void papr_scm_pmu_read(struct perf_event *event) | |
421 | { | |
422 | u64 prev, now; | |
423 | int rc; | |
424 | struct nvdimm_pmu *nd_pmu = to_nvdimm_pmu(event->pmu); | |
425 | ||
426 | if (!nd_pmu) | |
427 | return; | |
428 | ||
429 | rc = papr_scm_pmu_get_value(event, nd_pmu->dev, &now); | |
430 | if (rc) | |
431 | return; | |
432 | ||
433 | prev = local64_xchg(&event->hw.prev_count, now); | |
434 | local64_add(now - prev, &event->count); | |
435 | } | |
436 | ||
437 | static void papr_scm_pmu_del(struct perf_event *event, int flags) | |
438 | { | |
439 | papr_scm_pmu_read(event); | |
440 | } | |
441 | ||
4c08d4bb KJ |
442 | static void papr_scm_pmu_register(struct papr_scm_priv *p) |
443 | { | |
444 | struct nvdimm_pmu *nd_pmu; | |
445 | int rc, nodeid; | |
446 | ||
447 | nd_pmu = kzalloc(sizeof(*nd_pmu), GFP_KERNEL); | |
448 | if (!nd_pmu) { | |
449 | rc = -ENOMEM; | |
450 | goto pmu_err_print; | |
451 | } | |
452 | ||
6cf07810 NC |
453 | if (!p->stat_buffer_len) { |
454 | rc = -ENOENT; | |
4c08d4bb | 455 | goto pmu_check_events_err; |
6cf07810 | 456 | } |
4c08d4bb KJ |
457 | |
458 | nd_pmu->pmu.task_ctx_nr = perf_invalid_context; | |
459 | nd_pmu->pmu.name = nvdimm_name(p->nvdimm); | |
460 | nd_pmu->pmu.event_init = papr_scm_pmu_event_init; | |
461 | nd_pmu->pmu.read = papr_scm_pmu_read; | |
462 | nd_pmu->pmu.add = papr_scm_pmu_add; | |
463 | nd_pmu->pmu.del = papr_scm_pmu_del; | |
464 | ||
465 | nd_pmu->pmu.capabilities = PERF_PMU_CAP_NO_INTERRUPT | | |
466 | PERF_PMU_CAP_NO_EXCLUDE; | |
467 | ||
468 | /*updating the cpumask variable */ | |
469 | nodeid = numa_map_to_online_node(dev_to_node(&p->pdev->dev)); | |
470 | nd_pmu->arch_cpumask = *cpumask_of_node(nodeid); | |
471 | ||
472 | rc = register_nvdimm_pmu(nd_pmu, p->pdev); | |
473 | if (rc) | |
9b1ac046 | 474 | goto pmu_check_events_err; |
4c08d4bb KJ |
475 | |
476 | /* | |
477 | * Set archdata.priv value to nvdimm_pmu structure, to handle the | |
478 | * unregistering of pmu device. | |
479 | */ | |
480 | p->pdev->archdata.priv = nd_pmu; | |
481 | return; | |
482 | ||
4c08d4bb KJ |
483 | pmu_check_events_err: |
484 | kfree(nd_pmu); | |
485 | pmu_err_print: | |
486 | dev_info(&p->pdev->dev, "nvdimm pmu didn't register rc=%d\n", rc); | |
487 | } | |
488 | ||
d0007eb1 KJ |
489 | #else |
490 | static void papr_scm_pmu_register(struct papr_scm_priv *p) { } | |
491 | #endif | |
492 | ||
b791abf3 VJ |
493 | /* |
494 | * Issue hcall to retrieve dimm health info and populate papr_scm_priv with the | |
495 | * health information. | |
496 | */ | |
497 | static int __drc_pmem_query_health(struct papr_scm_priv *p) | |
498 | { | |
499 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; | |
bbbca723 | 500 | u64 bitmap = 0; |
b791abf3 VJ |
501 | long rc; |
502 | ||
503 | /* issue the hcall */ | |
504 | rc = plpar_hcall(H_SCM_HEALTH, ret, p->drc_index); | |
bbbca723 VJ |
505 | if (rc == H_SUCCESS) |
506 | bitmap = ret[0] & ret[1]; | |
507 | else if (rc == H_FUNCTION) | |
508 | dev_info_once(&p->pdev->dev, | |
509 | "Hcall H_SCM_HEALTH not implemented, assuming empty health bitmap"); | |
510 | else { | |
511 | ||
b791abf3 VJ |
512 | dev_err(&p->pdev->dev, |
513 | "Failed to query health information, Err:%ld\n", rc); | |
514 | return -ENXIO; | |
515 | } | |
516 | ||
517 | p->lasthealth_jiffies = jiffies; | |
bbbca723 VJ |
518 | /* Allow injecting specific health bits via inject mask. */ |
519 | if (p->health_bitmap_inject_mask) | |
520 | bitmap = (bitmap & ~p->health_bitmap_inject_mask) | | |
521 | p->health_bitmap_inject_mask; | |
522 | WRITE_ONCE(p->health_bitmap, bitmap); | |
b791abf3 VJ |
523 | dev_dbg(&p->pdev->dev, |
524 | "Queried dimm health info. Bitmap:0x%016lx Mask:0x%016lx\n", | |
525 | ret[0], ret[1]); | |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | /* Min interval in seconds for assuming stable dimm health */ | |
531 | #define MIN_HEALTH_QUERY_INTERVAL 60 | |
532 | ||
533 | /* Query cached health info and if needed call drc_pmem_query_health */ | |
534 | static int drc_pmem_query_health(struct papr_scm_priv *p) | |
535 | { | |
536 | unsigned long cache_timeout; | |
537 | int rc; | |
538 | ||
539 | /* Protect concurrent modifications to papr_scm_priv */ | |
540 | rc = mutex_lock_interruptible(&p->health_mutex); | |
541 | if (rc) | |
542 | return rc; | |
543 | ||
544 | /* Jiffies offset for which the health data is assumed to be same */ | |
545 | cache_timeout = p->lasthealth_jiffies + | |
546 | msecs_to_jiffies(MIN_HEALTH_QUERY_INTERVAL * 1000); | |
547 | ||
548 | /* Fetch new health info is its older than MIN_HEALTH_QUERY_INTERVAL */ | |
549 | if (time_after(jiffies, cache_timeout)) | |
550 | rc = __drc_pmem_query_health(p); | |
551 | else | |
552 | /* Assume cached health data is valid */ | |
553 | rc = 0; | |
554 | ||
555 | mutex_unlock(&p->health_mutex); | |
556 | return rc; | |
557 | } | |
faa6d211 | 558 | |
b5beae5e | 559 | static int papr_scm_meta_get(struct papr_scm_priv *p, |
53e80bd0 | 560 | struct nd_cmd_get_config_data_hdr *hdr) |
b5beae5e OH |
561 | { |
562 | unsigned long data[PLPAR_HCALL_BUFSIZE]; | |
53e80bd0 AK |
563 | unsigned long offset, data_offset; |
564 | int len, read; | |
b5beae5e OH |
565 | int64_t ret; |
566 | ||
612ee81b | 567 | if ((hdr->in_offset + hdr->in_length) > p->metadata_size) |
b5beae5e OH |
568 | return -EINVAL; |
569 | ||
53e80bd0 AK |
570 | for (len = hdr->in_length; len; len -= read) { |
571 | ||
572 | data_offset = hdr->in_length - len; | |
573 | offset = hdr->in_offset + data_offset; | |
574 | ||
575 | if (len >= 8) | |
576 | read = 8; | |
577 | else if (len >= 4) | |
578 | read = 4; | |
579 | else if (len >= 2) | |
580 | read = 2; | |
581 | else | |
582 | read = 1; | |
583 | ||
584 | ret = plpar_hcall(H_SCM_READ_METADATA, data, p->drc_index, | |
585 | offset, read); | |
586 | ||
587 | if (ret == H_PARAMETER) /* bad DRC index */ | |
588 | return -ENODEV; | |
589 | if (ret) | |
590 | return -EINVAL; /* other invalid parameter */ | |
591 | ||
592 | switch (read) { | |
593 | case 8: | |
594 | *(uint64_t *)(hdr->out_buf + data_offset) = be64_to_cpu(data[0]); | |
595 | break; | |
596 | case 4: | |
597 | *(uint32_t *)(hdr->out_buf + data_offset) = be32_to_cpu(data[0] & 0xffffffff); | |
598 | break; | |
599 | ||
600 | case 2: | |
601 | *(uint16_t *)(hdr->out_buf + data_offset) = be16_to_cpu(data[0] & 0xffff); | |
602 | break; | |
603 | ||
604 | case 1: | |
605 | *(uint8_t *)(hdr->out_buf + data_offset) = (data[0] & 0xff); | |
606 | break; | |
607 | } | |
608 | } | |
b5beae5e OH |
609 | return 0; |
610 | } | |
611 | ||
612 | static int papr_scm_meta_set(struct papr_scm_priv *p, | |
53e80bd0 | 613 | struct nd_cmd_set_config_hdr *hdr) |
b5beae5e | 614 | { |
53e80bd0 AK |
615 | unsigned long offset, data_offset; |
616 | int len, wrote; | |
617 | unsigned long data; | |
618 | __be64 data_be; | |
b5beae5e OH |
619 | int64_t ret; |
620 | ||
612ee81b | 621 | if ((hdr->in_offset + hdr->in_length) > p->metadata_size) |
b5beae5e OH |
622 | return -EINVAL; |
623 | ||
53e80bd0 AK |
624 | for (len = hdr->in_length; len; len -= wrote) { |
625 | ||
626 | data_offset = hdr->in_length - len; | |
627 | offset = hdr->in_offset + data_offset; | |
628 | ||
629 | if (len >= 8) { | |
630 | data = *(uint64_t *)(hdr->in_buf + data_offset); | |
631 | data_be = cpu_to_be64(data); | |
632 | wrote = 8; | |
633 | } else if (len >= 4) { | |
634 | data = *(uint32_t *)(hdr->in_buf + data_offset); | |
635 | data &= 0xffffffff; | |
636 | data_be = cpu_to_be32(data); | |
637 | wrote = 4; | |
638 | } else if (len >= 2) { | |
639 | data = *(uint16_t *)(hdr->in_buf + data_offset); | |
640 | data &= 0xffff; | |
641 | data_be = cpu_to_be16(data); | |
642 | wrote = 2; | |
643 | } else { | |
644 | data_be = *(uint8_t *)(hdr->in_buf + data_offset); | |
645 | data_be &= 0xff; | |
646 | wrote = 1; | |
647 | } | |
648 | ||
649 | ret = plpar_hcall_norets(H_SCM_WRITE_METADATA, p->drc_index, | |
650 | offset, data_be, wrote); | |
651 | if (ret == H_PARAMETER) /* bad DRC index */ | |
652 | return -ENODEV; | |
653 | if (ret) | |
654 | return -EINVAL; /* other invalid parameter */ | |
655 | } | |
b5beae5e OH |
656 | |
657 | return 0; | |
658 | } | |
659 | ||
f517f792 VJ |
660 | /* |
661 | * Do a sanity checks on the inputs args to dimm-control function and return | |
662 | * '0' if valid. Validation of PDSM payloads happens later in | |
663 | * papr_scm_service_pdsm. | |
664 | */ | |
665 | static int is_cmd_valid(struct nvdimm *nvdimm, unsigned int cmd, void *buf, | |
666 | unsigned int buf_len) | |
667 | { | |
668 | unsigned long cmd_mask = PAPR_SCM_DIMM_CMD_MASK; | |
669 | struct nd_cmd_pkg *nd_cmd; | |
670 | struct papr_scm_priv *p; | |
671 | enum papr_pdsm pdsm; | |
672 | ||
673 | /* Only dimm-specific calls are supported atm */ | |
674 | if (!nvdimm) | |
675 | return -EINVAL; | |
676 | ||
677 | /* get the provider data from struct nvdimm */ | |
678 | p = nvdimm_provider_data(nvdimm); | |
679 | ||
680 | if (!test_bit(cmd, &cmd_mask)) { | |
681 | dev_dbg(&p->pdev->dev, "Unsupported cmd=%u\n", cmd); | |
682 | return -EINVAL; | |
683 | } | |
684 | ||
685 | /* For CMD_CALL verify pdsm request */ | |
686 | if (cmd == ND_CMD_CALL) { | |
687 | /* Verify the envelope and envelop size */ | |
688 | if (!buf || | |
689 | buf_len < (sizeof(struct nd_cmd_pkg) + ND_PDSM_HDR_SIZE)) { | |
690 | dev_dbg(&p->pdev->dev, "Invalid pkg size=%u\n", | |
691 | buf_len); | |
692 | return -EINVAL; | |
693 | } | |
694 | ||
695 | /* Verify that the nd_cmd_pkg.nd_family is correct */ | |
696 | nd_cmd = (struct nd_cmd_pkg *)buf; | |
697 | ||
698 | if (nd_cmd->nd_family != NVDIMM_FAMILY_PAPR) { | |
699 | dev_dbg(&p->pdev->dev, "Invalid pkg family=0x%llx\n", | |
700 | nd_cmd->nd_family); | |
701 | return -EINVAL; | |
702 | } | |
703 | ||
704 | pdsm = (enum papr_pdsm)nd_cmd->nd_command; | |
705 | ||
706 | /* Verify if the pdsm command is valid */ | |
707 | if (pdsm <= PAPR_PDSM_MIN || pdsm >= PAPR_PDSM_MAX) { | |
708 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid PDSM\n", | |
709 | pdsm); | |
710 | return -EINVAL; | |
711 | } | |
712 | ||
713 | /* Have enough space to hold returned 'nd_pkg_pdsm' header */ | |
714 | if (nd_cmd->nd_size_out < ND_PDSM_HDR_SIZE) { | |
715 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid payload\n", | |
716 | pdsm); | |
717 | return -EINVAL; | |
718 | } | |
719 | } | |
720 | ||
721 | /* Let the command be further processed */ | |
722 | return 0; | |
723 | } | |
724 | ||
af0870c4 VJ |
725 | static int papr_pdsm_fuel_gauge(struct papr_scm_priv *p, |
726 | union nd_pdsm_payload *payload) | |
727 | { | |
728 | int rc, size; | |
729 | u64 statval; | |
730 | struct papr_scm_perf_stat *stat; | |
731 | struct papr_scm_perf_stats *stats; | |
732 | ||
733 | /* Silently fail if fetching performance metrics isn't supported */ | |
734 | if (!p->stat_buffer_len) | |
735 | return 0; | |
736 | ||
737 | /* Allocate request buffer enough to hold single performance stat */ | |
738 | size = sizeof(struct papr_scm_perf_stats) + | |
739 | sizeof(struct papr_scm_perf_stat); | |
740 | ||
741 | stats = kzalloc(size, GFP_KERNEL); | |
742 | if (!stats) | |
743 | return -ENOMEM; | |
744 | ||
745 | stat = &stats->scm_statistic[0]; | |
746 | memcpy(&stat->stat_id, "MemLife ", sizeof(stat->stat_id)); | |
747 | stat->stat_val = 0; | |
748 | ||
749 | /* Fetch the fuel gauge and populate it in payload */ | |
750 | rc = drc_pmem_query_stats(p, stats, 1); | |
751 | if (rc < 0) { | |
752 | dev_dbg(&p->pdev->dev, "Err(%d) fetching fuel gauge\n", rc); | |
753 | goto free_stats; | |
754 | } | |
755 | ||
756 | statval = be64_to_cpu(stat->stat_val); | |
757 | dev_dbg(&p->pdev->dev, | |
758 | "Fetched fuel-gauge %llu", statval); | |
759 | payload->health.extension_flags |= | |
760 | PDSM_DIMM_HEALTH_RUN_GAUGE_VALID; | |
761 | payload->health.dimm_fuel_gauge = statval; | |
762 | ||
763 | rc = sizeof(struct nd_papr_pdsm_health); | |
764 | ||
765 | free_stats: | |
766 | kfree(stats); | |
767 | return rc; | |
768 | } | |
769 | ||
de21e137 VJ |
770 | /* Add the dirty-shutdown-counter value to the pdsm */ |
771 | static int papr_pdsm_dsc(struct papr_scm_priv *p, | |
772 | union nd_pdsm_payload *payload) | |
773 | { | |
774 | payload->health.extension_flags |= PDSM_DIMM_DSC_VALID; | |
775 | payload->health.dimm_dsc = p->dirty_shutdown_counter; | |
776 | ||
777 | return sizeof(struct nd_papr_pdsm_health); | |
778 | } | |
779 | ||
d35f18b5 VJ |
780 | /* Fetch the DIMM health info and populate it in provided package. */ |
781 | static int papr_pdsm_health(struct papr_scm_priv *p, | |
782 | union nd_pdsm_payload *payload) | |
783 | { | |
784 | int rc; | |
785 | ||
786 | /* Ensure dimm health mutex is taken preventing concurrent access */ | |
787 | rc = mutex_lock_interruptible(&p->health_mutex); | |
788 | if (rc) | |
789 | goto out; | |
790 | ||
791 | /* Always fetch upto date dimm health data ignoring cached values */ | |
792 | rc = __drc_pmem_query_health(p); | |
793 | if (rc) { | |
794 | mutex_unlock(&p->health_mutex); | |
795 | goto out; | |
796 | } | |
797 | ||
798 | /* update health struct with various flags derived from health bitmap */ | |
799 | payload->health = (struct nd_papr_pdsm_health) { | |
800 | .extension_flags = 0, | |
801 | .dimm_unarmed = !!(p->health_bitmap & PAPR_PMEM_UNARMED_MASK), | |
802 | .dimm_bad_shutdown = !!(p->health_bitmap & PAPR_PMEM_BAD_SHUTDOWN_MASK), | |
803 | .dimm_bad_restore = !!(p->health_bitmap & PAPR_PMEM_BAD_RESTORE_MASK), | |
804 | .dimm_scrubbed = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), | |
805 | .dimm_locked = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), | |
806 | .dimm_encrypted = !!(p->health_bitmap & PAPR_PMEM_ENCRYPTED), | |
807 | .dimm_health = PAPR_PDSM_DIMM_HEALTHY, | |
808 | }; | |
809 | ||
810 | /* Update field dimm_health based on health_bitmap flags */ | |
811 | if (p->health_bitmap & PAPR_PMEM_HEALTH_FATAL) | |
812 | payload->health.dimm_health = PAPR_PDSM_DIMM_FATAL; | |
813 | else if (p->health_bitmap & PAPR_PMEM_HEALTH_CRITICAL) | |
814 | payload->health.dimm_health = PAPR_PDSM_DIMM_CRITICAL; | |
815 | else if (p->health_bitmap & PAPR_PMEM_HEALTH_UNHEALTHY) | |
816 | payload->health.dimm_health = PAPR_PDSM_DIMM_UNHEALTHY; | |
817 | ||
818 | /* struct populated hence can release the mutex now */ | |
819 | mutex_unlock(&p->health_mutex); | |
af0870c4 VJ |
820 | |
821 | /* Populate the fuel gauge meter in the payload */ | |
822 | papr_pdsm_fuel_gauge(p, payload); | |
de21e137 VJ |
823 | /* Populate the dirty-shutdown-counter field */ |
824 | papr_pdsm_dsc(p, payload); | |
af0870c4 | 825 | |
d35f18b5 VJ |
826 | rc = sizeof(struct nd_papr_pdsm_health); |
827 | ||
828 | out: | |
829 | return rc; | |
830 | } | |
831 | ||
bbbca723 VJ |
832 | /* Inject a smart error Add the dirty-shutdown-counter value to the pdsm */ |
833 | static int papr_pdsm_smart_inject(struct papr_scm_priv *p, | |
834 | union nd_pdsm_payload *payload) | |
835 | { | |
836 | int rc; | |
837 | u32 supported_flags = 0; | |
838 | u64 inject_mask = 0, clear_mask = 0; | |
839 | u64 mask; | |
840 | ||
841 | /* Check for individual smart error flags and update inject/clear masks */ | |
842 | if (payload->smart_inject.flags & PDSM_SMART_INJECT_HEALTH_FATAL) { | |
843 | supported_flags |= PDSM_SMART_INJECT_HEALTH_FATAL; | |
844 | if (payload->smart_inject.fatal_enable) | |
845 | inject_mask |= PAPR_PMEM_HEALTH_FATAL; | |
846 | else | |
847 | clear_mask |= PAPR_PMEM_HEALTH_FATAL; | |
848 | } | |
849 | ||
850 | if (payload->smart_inject.flags & PDSM_SMART_INJECT_BAD_SHUTDOWN) { | |
851 | supported_flags |= PDSM_SMART_INJECT_BAD_SHUTDOWN; | |
852 | if (payload->smart_inject.unsafe_shutdown_enable) | |
853 | inject_mask |= PAPR_PMEM_SHUTDOWN_DIRTY; | |
854 | else | |
855 | clear_mask |= PAPR_PMEM_SHUTDOWN_DIRTY; | |
856 | } | |
857 | ||
858 | dev_dbg(&p->pdev->dev, "[Smart-inject] inject_mask=%#llx clear_mask=%#llx\n", | |
859 | inject_mask, clear_mask); | |
860 | ||
861 | /* Prevent concurrent access to dimm health bitmap related members */ | |
862 | rc = mutex_lock_interruptible(&p->health_mutex); | |
863 | if (rc) | |
864 | return rc; | |
865 | ||
866 | /* Use inject/clear masks to set health_bitmap_inject_mask */ | |
867 | mask = READ_ONCE(p->health_bitmap_inject_mask); | |
868 | mask = (mask & ~clear_mask) | inject_mask; | |
869 | WRITE_ONCE(p->health_bitmap_inject_mask, mask); | |
870 | ||
871 | /* Invalidate cached health bitmap */ | |
872 | p->lasthealth_jiffies = 0; | |
873 | ||
874 | mutex_unlock(&p->health_mutex); | |
875 | ||
876 | /* Return the supported flags back to userspace */ | |
877 | payload->smart_inject.flags = supported_flags; | |
878 | ||
879 | return sizeof(struct nd_papr_pdsm_health); | |
880 | } | |
881 | ||
f517f792 VJ |
882 | /* |
883 | * 'struct pdsm_cmd_desc' | |
884 | * Identifies supported PDSMs' expected length of in/out payloads | |
885 | * and pdsm service function. | |
886 | * | |
887 | * size_in : Size of input payload if any in the PDSM request. | |
888 | * size_out : Size of output payload if any in the PDSM request. | |
889 | * service : Service function for the PDSM request. Return semantics: | |
890 | * rc < 0 : Error servicing PDSM and rc indicates the error. | |
891 | * rc >=0 : Serviced successfully and 'rc' indicate number of | |
892 | * bytes written to payload. | |
893 | */ | |
894 | struct pdsm_cmd_desc { | |
895 | u32 size_in; | |
896 | u32 size_out; | |
897 | int (*service)(struct papr_scm_priv *dimm, | |
898 | union nd_pdsm_payload *payload); | |
899 | }; | |
900 | ||
901 | /* Holds all supported PDSMs' command descriptors */ | |
902 | static const struct pdsm_cmd_desc __pdsm_cmd_descriptors[] = { | |
903 | [PAPR_PDSM_MIN] = { | |
904 | .size_in = 0, | |
905 | .size_out = 0, | |
906 | .service = NULL, | |
907 | }, | |
908 | /* New PDSM command descriptors to be added below */ | |
909 | ||
d35f18b5 VJ |
910 | [PAPR_PDSM_HEALTH] = { |
911 | .size_in = 0, | |
912 | .size_out = sizeof(struct nd_papr_pdsm_health), | |
913 | .service = papr_pdsm_health, | |
914 | }, | |
bbbca723 VJ |
915 | |
916 | [PAPR_PDSM_SMART_INJECT] = { | |
917 | .size_in = sizeof(struct nd_papr_pdsm_smart_inject), | |
918 | .size_out = sizeof(struct nd_papr_pdsm_smart_inject), | |
919 | .service = papr_pdsm_smart_inject, | |
920 | }, | |
f517f792 VJ |
921 | /* Empty */ |
922 | [PAPR_PDSM_MAX] = { | |
923 | .size_in = 0, | |
924 | .size_out = 0, | |
925 | .service = NULL, | |
926 | }, | |
927 | }; | |
928 | ||
929 | /* Given a valid pdsm cmd return its command descriptor else return NULL */ | |
930 | static inline const struct pdsm_cmd_desc *pdsm_cmd_desc(enum papr_pdsm cmd) | |
931 | { | |
932 | if (cmd >= 0 || cmd < ARRAY_SIZE(__pdsm_cmd_descriptors)) | |
933 | return &__pdsm_cmd_descriptors[cmd]; | |
934 | ||
935 | return NULL; | |
936 | } | |
937 | ||
938 | /* | |
939 | * For a given pdsm request call an appropriate service function. | |
940 | * Returns errors if any while handling the pdsm command package. | |
941 | */ | |
942 | static int papr_scm_service_pdsm(struct papr_scm_priv *p, | |
943 | struct nd_cmd_pkg *pkg) | |
944 | { | |
945 | /* Get the PDSM header and PDSM command */ | |
946 | struct nd_pkg_pdsm *pdsm_pkg = (struct nd_pkg_pdsm *)pkg->nd_payload; | |
947 | enum papr_pdsm pdsm = (enum papr_pdsm)pkg->nd_command; | |
948 | const struct pdsm_cmd_desc *pdsc; | |
949 | int rc; | |
950 | ||
951 | /* Fetch corresponding pdsm descriptor for validation and servicing */ | |
952 | pdsc = pdsm_cmd_desc(pdsm); | |
953 | ||
954 | /* Validate pdsm descriptor */ | |
955 | /* Ensure that reserved fields are 0 */ | |
956 | if (pdsm_pkg->reserved[0] || pdsm_pkg->reserved[1]) { | |
957 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid reserved field\n", | |
958 | pdsm); | |
959 | return -EINVAL; | |
960 | } | |
961 | ||
962 | /* If pdsm expects some input, then ensure that the size_in matches */ | |
963 | if (pdsc->size_in && | |
964 | pkg->nd_size_in != (pdsc->size_in + ND_PDSM_HDR_SIZE)) { | |
965 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_in=%d\n", | |
966 | pdsm, pkg->nd_size_in); | |
967 | return -EINVAL; | |
968 | } | |
969 | ||
970 | /* If pdsm wants to return data, then ensure that size_out matches */ | |
971 | if (pdsc->size_out && | |
972 | pkg->nd_size_out != (pdsc->size_out + ND_PDSM_HDR_SIZE)) { | |
973 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_out=%d\n", | |
974 | pdsm, pkg->nd_size_out); | |
975 | return -EINVAL; | |
976 | } | |
977 | ||
978 | /* Service the pdsm */ | |
979 | if (pdsc->service) { | |
980 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Servicing..\n", pdsm); | |
981 | ||
982 | rc = pdsc->service(p, &pdsm_pkg->payload); | |
983 | ||
984 | if (rc < 0) { | |
985 | /* error encountered while servicing pdsm */ | |
986 | pdsm_pkg->cmd_status = rc; | |
987 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; | |
988 | } else { | |
989 | /* pdsm serviced and 'rc' bytes written to payload */ | |
990 | pdsm_pkg->cmd_status = 0; | |
991 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE + rc; | |
992 | } | |
993 | } else { | |
994 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Unsupported PDSM request\n", | |
995 | pdsm); | |
996 | pdsm_pkg->cmd_status = -ENOENT; | |
997 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; | |
998 | } | |
999 | ||
1000 | return pdsm_pkg->cmd_status; | |
1001 | } | |
1002 | ||
72c4ebba VJ |
1003 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, |
1004 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, | |
1005 | unsigned int buf_len, int *cmd_rc) | |
b5beae5e OH |
1006 | { |
1007 | struct nd_cmd_get_config_size *get_size_hdr; | |
f517f792 | 1008 | struct nd_cmd_pkg *call_pkg = NULL; |
b5beae5e | 1009 | struct papr_scm_priv *p; |
b5f38f09 | 1010 | int rc; |
b5beae5e | 1011 | |
f517f792 VJ |
1012 | rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); |
1013 | if (rc) { | |
1014 | pr_debug("Invalid cmd=0x%x. Err=%d\n", cmd, rc); | |
1015 | return rc; | |
1016 | } | |
b5beae5e | 1017 | |
b5f38f09 VJ |
1018 | /* Use a local variable in case cmd_rc pointer is NULL */ |
1019 | if (!cmd_rc) | |
1020 | cmd_rc = &rc; | |
1021 | ||
b5beae5e OH |
1022 | p = nvdimm_provider_data(nvdimm); |
1023 | ||
1024 | switch (cmd) { | |
1025 | case ND_CMD_GET_CONFIG_SIZE: | |
1026 | get_size_hdr = buf; | |
1027 | ||
1028 | get_size_hdr->status = 0; | |
53e80bd0 | 1029 | get_size_hdr->max_xfer = 8; |
b5beae5e OH |
1030 | get_size_hdr->config_size = p->metadata_size; |
1031 | *cmd_rc = 0; | |
1032 | break; | |
1033 | ||
1034 | case ND_CMD_GET_CONFIG_DATA: | |
1035 | *cmd_rc = papr_scm_meta_get(p, buf); | |
1036 | break; | |
1037 | ||
1038 | case ND_CMD_SET_CONFIG_DATA: | |
1039 | *cmd_rc = papr_scm_meta_set(p, buf); | |
1040 | break; | |
1041 | ||
f517f792 VJ |
1042 | case ND_CMD_CALL: |
1043 | call_pkg = (struct nd_cmd_pkg *)buf; | |
1044 | *cmd_rc = papr_scm_service_pdsm(p, call_pkg); | |
1045 | break; | |
1046 | ||
b5beae5e | 1047 | default: |
b5f38f09 | 1048 | dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); |
b5beae5e OH |
1049 | return -EINVAL; |
1050 | } | |
1051 | ||
1052 | dev_dbg(&p->pdev->dev, "returned with cmd_rc = %d\n", *cmd_rc); | |
1053 | ||
1054 | return 0; | |
1055 | } | |
1056 | ||
bbbca723 VJ |
1057 | static ssize_t health_bitmap_inject_show(struct device *dev, |
1058 | struct device_attribute *attr, | |
1059 | char *buf) | |
1060 | { | |
1061 | struct nvdimm *dimm = to_nvdimm(dev); | |
1062 | struct papr_scm_priv *p = nvdimm_provider_data(dimm); | |
1063 | ||
1064 | return sprintf(buf, "%#llx\n", | |
1065 | READ_ONCE(p->health_bitmap_inject_mask)); | |
1066 | } | |
1067 | ||
1068 | static DEVICE_ATTR_ADMIN_RO(health_bitmap_inject); | |
1069 | ||
2d02bf83 VJ |
1070 | static ssize_t perf_stats_show(struct device *dev, |
1071 | struct device_attribute *attr, char *buf) | |
1072 | { | |
ca78ef2f VJ |
1073 | int index; |
1074 | ssize_t rc; | |
2d02bf83 VJ |
1075 | struct seq_buf s; |
1076 | struct papr_scm_perf_stat *stat; | |
1077 | struct papr_scm_perf_stats *stats; | |
1078 | struct nvdimm *dimm = to_nvdimm(dev); | |
1079 | struct papr_scm_priv *p = nvdimm_provider_data(dimm); | |
1080 | ||
1081 | if (!p->stat_buffer_len) | |
1082 | return -ENOENT; | |
1083 | ||
1084 | /* Allocate the buffer for phyp where stats are written */ | |
1085 | stats = kzalloc(p->stat_buffer_len, GFP_KERNEL); | |
1086 | if (!stats) | |
1087 | return -ENOMEM; | |
1088 | ||
1089 | /* Ask phyp to return all dimm perf stats */ | |
1090 | rc = drc_pmem_query_stats(p, stats, 0); | |
1091 | if (rc) | |
1092 | goto free_stats; | |
1093 | /* | |
1094 | * Go through the returned output buffer and print stats and | |
1095 | * values. Since stat_id is essentially a char string of | |
1096 | * 8 bytes, simply use the string format specifier to print it. | |
1097 | */ | |
1098 | seq_buf_init(&s, buf, PAGE_SIZE); | |
1099 | for (index = 0, stat = stats->scm_statistic; | |
1100 | index < be32_to_cpu(stats->num_statistics); | |
1101 | ++index, ++stat) { | |
1102 | seq_buf_printf(&s, "%.8s = 0x%016llX\n", | |
1103 | stat->stat_id, | |
1104 | be64_to_cpu(stat->stat_val)); | |
1105 | } | |
1106 | ||
1107 | free_stats: | |
1108 | kfree(stats); | |
ca78ef2f | 1109 | return rc ? rc : (ssize_t)seq_buf_used(&s); |
2d02bf83 | 1110 | } |
43663374 | 1111 | static DEVICE_ATTR_ADMIN_RO(perf_stats); |
2d02bf83 | 1112 | |
b791abf3 VJ |
1113 | static ssize_t flags_show(struct device *dev, |
1114 | struct device_attribute *attr, char *buf) | |
1115 | { | |
1116 | struct nvdimm *dimm = to_nvdimm(dev); | |
1117 | struct papr_scm_priv *p = nvdimm_provider_data(dimm); | |
1118 | struct seq_buf s; | |
1119 | u64 health; | |
1120 | int rc; | |
1121 | ||
1122 | rc = drc_pmem_query_health(p); | |
1123 | if (rc) | |
1124 | return rc; | |
1125 | ||
1126 | /* Copy health_bitmap locally, check masks & update out buffer */ | |
1127 | health = READ_ONCE(p->health_bitmap); | |
1128 | ||
1129 | seq_buf_init(&s, buf, PAGE_SIZE); | |
1130 | if (health & PAPR_PMEM_UNARMED_MASK) | |
1131 | seq_buf_printf(&s, "not_armed "); | |
1132 | ||
1133 | if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK) | |
1134 | seq_buf_printf(&s, "flush_fail "); | |
1135 | ||
1136 | if (health & PAPR_PMEM_BAD_RESTORE_MASK) | |
1137 | seq_buf_printf(&s, "restore_fail "); | |
1138 | ||
1139 | if (health & PAPR_PMEM_ENCRYPTED) | |
1140 | seq_buf_printf(&s, "encrypted "); | |
1141 | ||
1142 | if (health & PAPR_PMEM_SMART_EVENT_MASK) | |
1143 | seq_buf_printf(&s, "smart_notify "); | |
1144 | ||
1145 | if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED) | |
1146 | seq_buf_printf(&s, "scrubbed locked "); | |
1147 | ||
1148 | if (seq_buf_used(&s)) | |
1149 | seq_buf_printf(&s, "\n"); | |
1150 | ||
1151 | return seq_buf_used(&s); | |
1152 | } | |
1153 | DEVICE_ATTR_RO(flags); | |
1154 | ||
de21e137 VJ |
1155 | static ssize_t dirty_shutdown_show(struct device *dev, |
1156 | struct device_attribute *attr, char *buf) | |
1157 | { | |
1158 | struct nvdimm *dimm = to_nvdimm(dev); | |
1159 | struct papr_scm_priv *p = nvdimm_provider_data(dimm); | |
1160 | ||
1161 | return sysfs_emit(buf, "%llu\n", p->dirty_shutdown_counter); | |
1162 | } | |
1163 | DEVICE_ATTR_RO(dirty_shutdown); | |
1164 | ||
ed78f56e VJ |
1165 | static umode_t papr_nd_attribute_visible(struct kobject *kobj, |
1166 | struct attribute *attr, int n) | |
1167 | { | |
1168 | struct device *dev = kobj_to_dev(kobj); | |
1169 | struct nvdimm *nvdimm = to_nvdimm(dev); | |
1170 | struct papr_scm_priv *p = nvdimm_provider_data(nvdimm); | |
1171 | ||
1172 | /* For if perf-stats not available remove perf_stats sysfs */ | |
1173 | if (attr == &dev_attr_perf_stats.attr && p->stat_buffer_len == 0) | |
1174 | return 0; | |
1175 | ||
1176 | return attr->mode; | |
1177 | } | |
1178 | ||
b791abf3 VJ |
1179 | /* papr_scm specific dimm attributes */ |
1180 | static struct attribute *papr_nd_attributes[] = { | |
1181 | &dev_attr_flags.attr, | |
2d02bf83 | 1182 | &dev_attr_perf_stats.attr, |
de21e137 | 1183 | &dev_attr_dirty_shutdown.attr, |
bbbca723 | 1184 | &dev_attr_health_bitmap_inject.attr, |
b791abf3 VJ |
1185 | NULL, |
1186 | }; | |
1187 | ||
6b3a3e12 | 1188 | static const struct attribute_group papr_nd_attribute_group = { |
b791abf3 | 1189 | .name = "papr", |
ed78f56e | 1190 | .is_visible = papr_nd_attribute_visible, |
b791abf3 VJ |
1191 | .attrs = papr_nd_attributes, |
1192 | }; | |
1193 | ||
1194 | static const struct attribute_group *papr_nd_attr_groups[] = { | |
1195 | &papr_nd_attribute_group, | |
1196 | NULL, | |
1197 | }; | |
1198 | ||
b5beae5e OH |
1199 | static int papr_scm_nvdimm_init(struct papr_scm_priv *p) |
1200 | { | |
1201 | struct device *dev = &p->pdev->dev; | |
1202 | struct nd_mapping_desc mapping; | |
1203 | struct nd_region_desc ndr_desc; | |
1204 | unsigned long dimm_flags; | |
da1115fd | 1205 | int target_nid, online_nid; |
b5beae5e OH |
1206 | |
1207 | p->bus_desc.ndctl = papr_scm_ndctl; | |
1208 | p->bus_desc.module = THIS_MODULE; | |
1209 | p->bus_desc.of_node = p->pdev->dev.of_node; | |
b5beae5e OH |
1210 | p->bus_desc.provider_name = kstrdup(p->pdev->name, GFP_KERNEL); |
1211 | ||
13135b46 VJ |
1212 | /* Set the dimm command family mask to accept PDSMs */ |
1213 | set_bit(NVDIMM_FAMILY_PAPR, &p->bus_desc.dimm_family_mask); | |
1214 | ||
b5beae5e OH |
1215 | if (!p->bus_desc.provider_name) |
1216 | return -ENOMEM; | |
1217 | ||
1218 | p->bus = nvdimm_bus_register(NULL, &p->bus_desc); | |
1219 | if (!p->bus) { | |
1220 | dev_err(dev, "Error creating nvdimm bus %pOF\n", p->dn); | |
5649607a | 1221 | kfree(p->bus_desc.provider_name); |
b5beae5e OH |
1222 | return -ENXIO; |
1223 | } | |
1224 | ||
1225 | dimm_flags = 0; | |
a0e37452 | 1226 | set_bit(NDD_LABELING, &dimm_flags); |
b5beae5e | 1227 | |
adb68c38 VJ |
1228 | /* |
1229 | * Check if the nvdimm is unarmed. No locking needed as we are still | |
1230 | * initializing. Ignore error encountered if any. | |
1231 | */ | |
1232 | __drc_pmem_query_health(p); | |
1233 | ||
1234 | if (p->health_bitmap & PAPR_PMEM_UNARMED_MASK) | |
1235 | set_bit(NDD_UNARMED, &dimm_flags); | |
1236 | ||
b791abf3 VJ |
1237 | p->nvdimm = nvdimm_create(p->bus, p, papr_nd_attr_groups, |
1238 | dimm_flags, PAPR_SCM_DIMM_CMD_MASK, 0, NULL); | |
b5beae5e OH |
1239 | if (!p->nvdimm) { |
1240 | dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); | |
1241 | goto err; | |
1242 | } | |
1243 | ||
b0d65a8c OH |
1244 | if (nvdimm_bus_check_dimm_count(p->bus, 1)) |
1245 | goto err; | |
1246 | ||
b5beae5e OH |
1247 | /* now add the region */ |
1248 | ||
1249 | memset(&mapping, 0, sizeof(mapping)); | |
1250 | mapping.nvdimm = p->nvdimm; | |
1251 | mapping.start = 0; | |
1252 | mapping.size = p->blocks * p->block_size; // XXX: potential overflow? | |
1253 | ||
1254 | memset(&ndr_desc, 0, sizeof(ndr_desc)); | |
da1115fd | 1255 | target_nid = dev_to_node(&p->pdev->dev); |
575e23b6 | 1256 | online_nid = numa_map_to_online_node(target_nid); |
da1115fd AK |
1257 | ndr_desc.numa_node = online_nid; |
1258 | ndr_desc.target_node = target_nid; | |
b5beae5e OH |
1259 | ndr_desc.res = &p->res; |
1260 | ndr_desc.of_node = p->dn; | |
1261 | ndr_desc.provider_data = p; | |
1262 | ndr_desc.mapping = &mapping; | |
1263 | ndr_desc.num_mappings = 1; | |
1264 | ndr_desc.nd_set = &p->nd_set; | |
b5beae5e | 1265 | |
75b7c05e SB |
1266 | if (p->hcall_flush_required) { |
1267 | set_bit(ND_REGION_ASYNC, &ndr_desc.flags); | |
1268 | ndr_desc.flush = papr_scm_pmem_flush; | |
1269 | } | |
1270 | ||
2a0ffbd4 AK |
1271 | if (p->is_volatile) |
1272 | p->region = nvdimm_volatile_region_create(p->bus, &ndr_desc); | |
338f6dac AK |
1273 | else { |
1274 | set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc.flags); | |
2a0ffbd4 | 1275 | p->region = nvdimm_pmem_region_create(p->bus, &ndr_desc); |
338f6dac | 1276 | } |
b5beae5e OH |
1277 | if (!p->region) { |
1278 | dev_err(dev, "Error registering region %pR from %pOF\n", | |
1279 | ndr_desc.res, p->dn); | |
1280 | goto err; | |
1281 | } | |
da1115fd AK |
1282 | if (target_nid != online_nid) |
1283 | dev_info(dev, "Region registered with target node %d and online node %d", | |
1284 | target_nid, online_nid); | |
b5beae5e | 1285 | |
85343a8d SS |
1286 | mutex_lock(&papr_ndr_lock); |
1287 | list_add_tail(&p->region_list, &papr_nd_regions); | |
1288 | mutex_unlock(&papr_ndr_lock); | |
1289 | ||
b5beae5e OH |
1290 | return 0; |
1291 | ||
1292 | err: nvdimm_bus_unregister(p->bus); | |
1293 | kfree(p->bus_desc.provider_name); | |
1294 | return -ENXIO; | |
1295 | } | |
1296 | ||
85343a8d SS |
1297 | static void papr_scm_add_badblock(struct nd_region *region, |
1298 | struct nvdimm_bus *bus, u64 phys_addr) | |
1299 | { | |
1300 | u64 aligned_addr = ALIGN_DOWN(phys_addr, L1_CACHE_BYTES); | |
1301 | ||
1302 | if (nvdimm_bus_add_badrange(bus, aligned_addr, L1_CACHE_BYTES)) { | |
1303 | pr_err("Bad block registration for 0x%llx failed\n", phys_addr); | |
1304 | return; | |
1305 | } | |
1306 | ||
1307 | pr_debug("Add memory range (0x%llx - 0x%llx) as bad range\n", | |
1308 | aligned_addr, aligned_addr + L1_CACHE_BYTES); | |
1309 | ||
1310 | nvdimm_region_notify(region, NVDIMM_REVALIDATE_POISON); | |
1311 | } | |
1312 | ||
1313 | static int handle_mce_ue(struct notifier_block *nb, unsigned long val, | |
1314 | void *data) | |
1315 | { | |
1316 | struct machine_check_event *evt = data; | |
1317 | struct papr_scm_priv *p; | |
1318 | u64 phys_addr; | |
1319 | bool found = false; | |
1320 | ||
1321 | if (evt->error_type != MCE_ERROR_TYPE_UE) | |
1322 | return NOTIFY_DONE; | |
1323 | ||
1324 | if (list_empty(&papr_nd_regions)) | |
1325 | return NOTIFY_DONE; | |
1326 | ||
1327 | /* | |
1328 | * The physical address obtained here is PAGE_SIZE aligned, so get the | |
1329 | * exact address from the effective address | |
1330 | */ | |
1331 | phys_addr = evt->u.ue_error.physical_address + | |
1332 | (evt->u.ue_error.effective_address & ~PAGE_MASK); | |
1333 | ||
1334 | if (!evt->u.ue_error.physical_address_provided || | |
1335 | !is_zone_device_page(pfn_to_page(phys_addr >> PAGE_SHIFT))) | |
1336 | return NOTIFY_DONE; | |
1337 | ||
1338 | /* mce notifier is called from a process context, so mutex is safe */ | |
1339 | mutex_lock(&papr_ndr_lock); | |
1340 | list_for_each_entry(p, &papr_nd_regions, region_list) { | |
1341 | if (phys_addr >= p->res.start && phys_addr <= p->res.end) { | |
1342 | found = true; | |
1343 | break; | |
1344 | } | |
1345 | } | |
1346 | ||
1347 | if (found) | |
1348 | papr_scm_add_badblock(p->region, p->bus, phys_addr); | |
1349 | ||
1350 | mutex_unlock(&papr_ndr_lock); | |
1351 | ||
1352 | return found ? NOTIFY_OK : NOTIFY_DONE; | |
1353 | } | |
1354 | ||
1355 | static struct notifier_block mce_ue_nb = { | |
1356 | .notifier_call = handle_mce_ue | |
1357 | }; | |
1358 | ||
b5beae5e OH |
1359 | static int papr_scm_probe(struct platform_device *pdev) |
1360 | { | |
b5beae5e | 1361 | struct device_node *dn = pdev->dev.of_node; |
683ec0e0 OH |
1362 | u32 drc_index, metadata_size; |
1363 | u64 blocks, block_size; | |
b5beae5e | 1364 | struct papr_scm_priv *p; |
0e8554b5 | 1365 | u8 uuid_raw[UUID_SIZE]; |
43001c52 | 1366 | const char *uuid_str; |
ed78f56e | 1367 | ssize_t stat_size; |
0e8554b5 | 1368 | uuid_t uuid; |
b5beae5e OH |
1369 | int rc; |
1370 | ||
1371 | /* check we have all the required DT properties */ | |
1372 | if (of_property_read_u32(dn, "ibm,my-drc-index", &drc_index)) { | |
1373 | dev_err(&pdev->dev, "%pOF: missing drc-index!\n", dn); | |
1374 | return -ENODEV; | |
1375 | } | |
1376 | ||
683ec0e0 OH |
1377 | if (of_property_read_u64(dn, "ibm,block-size", &block_size)) { |
1378 | dev_err(&pdev->dev, "%pOF: missing block-size!\n", dn); | |
1379 | return -ENODEV; | |
1380 | } | |
1381 | ||
1382 | if (of_property_read_u64(dn, "ibm,number-of-blocks", &blocks)) { | |
1383 | dev_err(&pdev->dev, "%pOF: missing number-of-blocks!\n", dn); | |
b5beae5e OH |
1384 | return -ENODEV; |
1385 | } | |
1386 | ||
43001c52 OH |
1387 | if (of_property_read_string(dn, "ibm,unit-guid", &uuid_str)) { |
1388 | dev_err(&pdev->dev, "%pOF: missing unit-guid!\n", dn); | |
1389 | return -ENODEV; | |
1390 | } | |
1391 | ||
b277fc79 AK |
1392 | /* |
1393 | * open firmware platform device create won't update the NUMA | |
1394 | * distance table. For PAPR SCM devices we use numa_map_to_online_node() | |
1395 | * to find the nearest online NUMA node and that requires correct | |
1396 | * distance table information. | |
1397 | */ | |
1398 | update_numa_distance(dn); | |
2a0ffbd4 | 1399 | |
b5beae5e OH |
1400 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
1401 | if (!p) | |
1402 | return -ENOMEM; | |
1403 | ||
b791abf3 VJ |
1404 | /* Initialize the dimm mutex */ |
1405 | mutex_init(&p->health_mutex); | |
1406 | ||
b5beae5e OH |
1407 | /* optional DT properties */ |
1408 | of_property_read_u32(dn, "ibm,metadata-size", &metadata_size); | |
1409 | ||
1410 | p->dn = dn; | |
1411 | p->drc_index = drc_index; | |
683ec0e0 OH |
1412 | p->block_size = block_size; |
1413 | p->blocks = blocks; | |
2a0ffbd4 | 1414 | p->is_volatile = !of_property_read_bool(dn, "ibm,cache-flush-required"); |
75b7c05e | 1415 | p->hcall_flush_required = of_property_read_bool(dn, "ibm,hcall-flush-required"); |
b5beae5e | 1416 | |
de21e137 VJ |
1417 | if (of_property_read_u64(dn, "ibm,persistence-failed-count", |
1418 | &p->dirty_shutdown_counter)) | |
1419 | p->dirty_shutdown_counter = 0; | |
1420 | ||
43001c52 | 1421 | /* We just need to ensure that set cookies are unique across */ |
0e8554b5 AS |
1422 | uuid_parse(uuid_str, &uuid); |
1423 | ||
259a948c | 1424 | /* |
0e8554b5 AS |
1425 | * The cookie1 and cookie2 are not really little endian. |
1426 | * We store a raw buffer representation of the | |
1427 | * uuid string so that we can compare this with the label | |
1428 | * area cookie irrespective of the endian configuration | |
1429 | * with which the kernel is built. | |
1430 | * | |
1431 | * Historically we stored the cookie in the below format. | |
1432 | * for a uuid string 72511b67-0b3b-42fd-8d1d-5be3cae8bcaa | |
1433 | * cookie1 was 0xfd423b0b671b5172 | |
1434 | * cookie2 was 0xaabce8cae35b1d8d | |
259a948c | 1435 | */ |
0e8554b5 AS |
1436 | export_uuid(uuid_raw, &uuid); |
1437 | p->nd_set.cookie1 = get_unaligned_le64(&uuid_raw[0]); | |
1438 | p->nd_set.cookie2 = get_unaligned_le64(&uuid_raw[8]); | |
43001c52 | 1439 | |
b5beae5e OH |
1440 | /* might be zero */ |
1441 | p->metadata_size = metadata_size; | |
1442 | p->pdev = pdev; | |
1443 | ||
1444 | /* request the hypervisor to bind this region to somewhere in memory */ | |
1445 | rc = drc_pmem_bind(p); | |
3a855b7a VJ |
1446 | |
1447 | /* If phyp says drc memory still bound then force unbound and retry */ | |
faa6d211 AK |
1448 | if (rc == H_OVERLAP) |
1449 | rc = drc_pmem_query_n_bind(p); | |
3a855b7a | 1450 | |
4111cdef | 1451 | if (rc != H_SUCCESS) { |
faa6d211 | 1452 | dev_err(&p->pdev->dev, "bind err: %d\n", rc); |
4111cdef | 1453 | rc = -ENXIO; |
b5beae5e | 1454 | goto err; |
4111cdef | 1455 | } |
b5beae5e OH |
1456 | |
1457 | /* setup the resource for the newly bound range */ | |
1458 | p->res.start = p->bound_addr; | |
59613526 | 1459 | p->res.end = p->bound_addr + p->blocks * p->block_size - 1; |
b5beae5e OH |
1460 | p->res.name = pdev->name; |
1461 | p->res.flags = IORESOURCE_MEM; | |
1462 | ||
ed78f56e VJ |
1463 | /* Try retrieving the stat buffer and see if its supported */ |
1464 | stat_size = drc_pmem_query_stats(p, NULL, 0); | |
1465 | if (stat_size > 0) { | |
1466 | p->stat_buffer_len = stat_size; | |
1467 | dev_dbg(&p->pdev->dev, "Max perf-stat size %lu-bytes\n", | |
1468 | p->stat_buffer_len); | |
1469 | } | |
1470 | ||
b5beae5e OH |
1471 | rc = papr_scm_nvdimm_init(p); |
1472 | if (rc) | |
1473 | goto err2; | |
1474 | ||
1475 | platform_set_drvdata(pdev, p); | |
4c08d4bb | 1476 | papr_scm_pmu_register(p); |
b5beae5e OH |
1477 | |
1478 | return 0; | |
1479 | ||
1480 | err2: drc_pmem_unbind(p); | |
1481 | err: kfree(p); | |
1482 | return rc; | |
1483 | } | |
1484 | ||
18a4a261 | 1485 | static void papr_scm_remove(struct platform_device *pdev) |
b5beae5e OH |
1486 | { |
1487 | struct papr_scm_priv *p = platform_get_drvdata(pdev); | |
1488 | ||
85343a8d SS |
1489 | mutex_lock(&papr_ndr_lock); |
1490 | list_del(&p->region_list); | |
1491 | mutex_unlock(&papr_ndr_lock); | |
1492 | ||
b5beae5e OH |
1493 | nvdimm_bus_unregister(p->bus); |
1494 | drc_pmem_unbind(p); | |
4c08d4bb KJ |
1495 | |
1496 | if (pdev->archdata.priv) | |
1497 | unregister_nvdimm_pmu(pdev->archdata.priv); | |
1498 | ||
1499 | pdev->archdata.priv = NULL; | |
5649607a | 1500 | kfree(p->bus_desc.provider_name); |
b5beae5e | 1501 | kfree(p); |
b5beae5e OH |
1502 | } |
1503 | ||
1504 | static const struct of_device_id papr_scm_match[] = { | |
1505 | { .compatible = "ibm,pmemory" }, | |
8c26ab72 | 1506 | { .compatible = "ibm,pmemory-v2" }, |
b5beae5e OH |
1507 | { }, |
1508 | }; | |
1509 | ||
1510 | static struct platform_driver papr_scm_driver = { | |
1511 | .probe = papr_scm_probe, | |
18a4a261 | 1512 | .remove_new = papr_scm_remove, |
b5beae5e OH |
1513 | .driver = { |
1514 | .name = "papr_scm", | |
b5beae5e OH |
1515 | .of_match_table = papr_scm_match, |
1516 | }, | |
1517 | }; | |
1518 | ||
85343a8d SS |
1519 | static int __init papr_scm_init(void) |
1520 | { | |
1521 | int ret; | |
1522 | ||
1523 | ret = platform_driver_register(&papr_scm_driver); | |
1524 | if (!ret) | |
1525 | mce_register_notifier(&mce_ue_nb); | |
1526 | ||
1527 | return ret; | |
1528 | } | |
1529 | module_init(papr_scm_init); | |
1530 | ||
1531 | static void __exit papr_scm_exit(void) | |
1532 | { | |
1533 | mce_unregister_notifier(&mce_ue_nb); | |
1534 | platform_driver_unregister(&papr_scm_driver); | |
1535 | } | |
1536 | module_exit(papr_scm_exit); | |
1537 | ||
b5beae5e OH |
1538 | MODULE_DEVICE_TABLE(of, papr_scm_match); |
1539 | MODULE_LICENSE("GPL"); | |
1540 | MODULE_AUTHOR("IBM Corporation"); |