Merge tag 'for-linus-6.0-rc1b-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-starfive.git] / drivers / cxl / acpi.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3 #include <linux/platform_device.h>
4 #include <linux/module.h>
5 #include <linux/device.h>
6 #include <linux/kernel.h>
7 #include <linux/acpi.h>
8 #include <linux/pci.h>
9 #include "cxlpci.h"
10 #include "cxl.h"
11
12 static unsigned long cfmws_to_decoder_flags(int restrictions)
13 {
14         unsigned long flags = CXL_DECODER_F_ENABLE;
15
16         if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
17                 flags |= CXL_DECODER_F_TYPE2;
18         if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
19                 flags |= CXL_DECODER_F_TYPE3;
20         if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
21                 flags |= CXL_DECODER_F_RAM;
22         if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
23                 flags |= CXL_DECODER_F_PMEM;
24         if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
25                 flags |= CXL_DECODER_F_LOCK;
26
27         return flags;
28 }
29
30 static int cxl_acpi_cfmws_verify(struct device *dev,
31                                  struct acpi_cedt_cfmws *cfmws)
32 {
33         int rc, expected_len;
34         unsigned int ways;
35
36         if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
37                 dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
38                 return -EINVAL;
39         }
40
41         if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
42                 dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
43                 return -EINVAL;
44         }
45
46         if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
47                 dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
48                 return -EINVAL;
49         }
50
51         rc = cxl_to_ways(cfmws->interleave_ways, &ways);
52         if (rc) {
53                 dev_err(dev, "CFMWS Interleave Ways (%d) invalid\n",
54                         cfmws->interleave_ways);
55                 return -EINVAL;
56         }
57
58         expected_len = struct_size(cfmws, interleave_targets, ways);
59
60         if (cfmws->header.length < expected_len) {
61                 dev_err(dev, "CFMWS length %d less than expected %d\n",
62                         cfmws->header.length, expected_len);
63                 return -EINVAL;
64         }
65
66         if (cfmws->header.length > expected_len)
67                 dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
68                         cfmws->header.length, expected_len);
69
70         return 0;
71 }
72
73 struct cxl_cfmws_context {
74         struct device *dev;
75         struct cxl_port *root_port;
76         struct resource *cxl_res;
77         int id;
78 };
79
80 static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
81                            const unsigned long end)
82 {
83         int target_map[CXL_DECODER_MAX_INTERLEAVE];
84         struct cxl_cfmws_context *ctx = arg;
85         struct cxl_port *root_port = ctx->root_port;
86         struct resource *cxl_res = ctx->cxl_res;
87         struct cxl_root_decoder *cxlrd;
88         struct device *dev = ctx->dev;
89         struct acpi_cedt_cfmws *cfmws;
90         struct cxl_decoder *cxld;
91         unsigned int ways, i, ig;
92         struct resource *res;
93         int rc;
94
95         cfmws = (struct acpi_cedt_cfmws *) header;
96
97         rc = cxl_acpi_cfmws_verify(dev, cfmws);
98         if (rc) {
99                 dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
100                         cfmws->base_hpa,
101                         cfmws->base_hpa + cfmws->window_size - 1);
102                 return 0;
103         }
104
105         rc = cxl_to_ways(cfmws->interleave_ways, &ways);
106         if (rc)
107                 return rc;
108         rc = cxl_to_granularity(cfmws->granularity, &ig);
109         if (rc)
110                 return rc;
111         for (i = 0; i < ways; i++)
112                 target_map[i] = cfmws->interleave_targets[i];
113
114         res = kzalloc(sizeof(*res), GFP_KERNEL);
115         if (!res)
116                 return -ENOMEM;
117
118         res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++);
119         if (!res->name)
120                 goto err_name;
121
122         res->start = cfmws->base_hpa;
123         res->end = cfmws->base_hpa + cfmws->window_size - 1;
124         res->flags = IORESOURCE_MEM;
125
126         /* add to the local resource tracking to establish a sort order */
127         rc = insert_resource(cxl_res, res);
128         if (rc)
129                 goto err_insert;
130
131         cxlrd = cxl_root_decoder_alloc(root_port, ways);
132         if (IS_ERR(cxlrd))
133                 return 0;
134
135         cxld = &cxlrd->cxlsd.cxld;
136         cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
137         cxld->target_type = CXL_DECODER_EXPANDER;
138         cxld->hpa_range = (struct range) {
139                 .start = res->start,
140                 .end = res->end,
141         };
142         cxld->interleave_ways = ways;
143         /*
144          * Minimize the x1 granularity to advertise support for any
145          * valid region granularity
146          */
147         if (ways == 1)
148                 ig = CXL_DECODER_MIN_GRANULARITY;
149         cxld->interleave_granularity = ig;
150
151         rc = cxl_decoder_add(cxld, target_map);
152         if (rc)
153                 put_device(&cxld->dev);
154         else
155                 rc = cxl_decoder_autoremove(dev, cxld);
156         if (rc) {
157                 dev_err(dev, "Failed to add decode range [%#llx - %#llx]\n",
158                         cxld->hpa_range.start, cxld->hpa_range.end);
159                 return 0;
160         }
161         dev_dbg(dev, "add: %s node: %d range [%#llx - %#llx]\n",
162                 dev_name(&cxld->dev),
163                 phys_to_target_node(cxld->hpa_range.start),
164                 cxld->hpa_range.start, cxld->hpa_range.end);
165
166         return 0;
167
168 err_insert:
169         kfree(res->name);
170 err_name:
171         kfree(res);
172         return -ENOMEM;
173 }
174
175 __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
176                                               struct device *dev)
177 {
178         struct acpi_device *adev = to_acpi_device(dev);
179
180         if (!acpi_pci_find_root(adev->handle))
181                 return NULL;
182
183         if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0)
184                 return adev;
185         return NULL;
186 }
187
188 /*
189  * A host bridge is a dport to a CFMWS decode and it is a uport to the
190  * dport (PCIe Root Ports) in the host bridge.
191  */
192 static int add_host_bridge_uport(struct device *match, void *arg)
193 {
194         struct cxl_port *root_port = arg;
195         struct device *host = root_port->dev.parent;
196         struct acpi_device *bridge = to_cxl_host_bridge(host, match);
197         struct acpi_pci_root *pci_root;
198         struct cxl_dport *dport;
199         struct cxl_port *port;
200         int rc;
201
202         if (!bridge)
203                 return 0;
204
205         dport = cxl_find_dport_by_dev(root_port, match);
206         if (!dport) {
207                 dev_dbg(host, "host bridge expected and not found\n");
208                 return 0;
209         }
210
211         /*
212          * Note that this lookup already succeeded in
213          * to_cxl_host_bridge(), so no need to check for failure here
214          */
215         pci_root = acpi_pci_find_root(bridge->handle);
216         rc = devm_cxl_register_pci_bus(host, match, pci_root->bus);
217         if (rc)
218                 return rc;
219
220         port = devm_cxl_add_port(host, match, dport->component_reg_phys, dport);
221         if (IS_ERR(port))
222                 return PTR_ERR(port);
223         dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
224
225         return 0;
226 }
227
228 struct cxl_chbs_context {
229         struct device *dev;
230         unsigned long long uid;
231         resource_size_t chbcr;
232 };
233
234 static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
235                          const unsigned long end)
236 {
237         struct cxl_chbs_context *ctx = arg;
238         struct acpi_cedt_chbs *chbs;
239
240         if (ctx->chbcr)
241                 return 0;
242
243         chbs = (struct acpi_cedt_chbs *) header;
244
245         if (ctx->uid != chbs->uid)
246                 return 0;
247         ctx->chbcr = chbs->base;
248
249         return 0;
250 }
251
252 static int add_host_bridge_dport(struct device *match, void *arg)
253 {
254         acpi_status status;
255         unsigned long long uid;
256         struct cxl_dport *dport;
257         struct cxl_chbs_context ctx;
258         struct cxl_port *root_port = arg;
259         struct device *host = root_port->dev.parent;
260         struct acpi_device *bridge = to_cxl_host_bridge(host, match);
261
262         if (!bridge)
263                 return 0;
264
265         status = acpi_evaluate_integer(bridge->handle, METHOD_NAME__UID, NULL,
266                                        &uid);
267         if (status != AE_OK) {
268                 dev_err(host, "unable to retrieve _UID of %s\n",
269                         dev_name(match));
270                 return -ENODEV;
271         }
272
273         ctx = (struct cxl_chbs_context) {
274                 .dev = host,
275                 .uid = uid,
276         };
277         acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
278
279         if (ctx.chbcr == 0) {
280                 dev_warn(host, "No CHBS found for Host Bridge: %s\n",
281                          dev_name(match));
282                 return 0;
283         }
284
285         dport = devm_cxl_add_dport(root_port, match, uid, ctx.chbcr);
286         if (IS_ERR(dport)) {
287                 dev_err(host, "failed to add downstream port: %s\n",
288                         dev_name(match));
289                 return PTR_ERR(dport);
290         }
291         dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
292         return 0;
293 }
294
295 static int add_root_nvdimm_bridge(struct device *match, void *data)
296 {
297         struct cxl_decoder *cxld;
298         struct cxl_port *root_port = data;
299         struct cxl_nvdimm_bridge *cxl_nvb;
300         struct device *host = root_port->dev.parent;
301
302         if (!is_root_decoder(match))
303                 return 0;
304
305         cxld = to_cxl_decoder(match);
306         if (!(cxld->flags & CXL_DECODER_F_PMEM))
307                 return 0;
308
309         cxl_nvb = devm_cxl_add_nvdimm_bridge(host, root_port);
310         if (IS_ERR(cxl_nvb)) {
311                 dev_dbg(host, "failed to register pmem\n");
312                 return PTR_ERR(cxl_nvb);
313         }
314         dev_dbg(host, "%s: add: %s\n", dev_name(&root_port->dev),
315                 dev_name(&cxl_nvb->dev));
316         return 1;
317 }
318
319 static struct lock_class_key cxl_root_key;
320
321 static void cxl_acpi_lock_reset_class(void *dev)
322 {
323         device_lock_reset_class(dev);
324 }
325
326 static void del_cxl_resource(struct resource *res)
327 {
328         kfree(res->name);
329         kfree(res);
330 }
331
332 static void cxl_set_public_resource(struct resource *priv, struct resource *pub)
333 {
334         priv->desc = (unsigned long) pub;
335 }
336
337 static struct resource *cxl_get_public_resource(struct resource *priv)
338 {
339         return (struct resource *) priv->desc;
340 }
341
342 static void remove_cxl_resources(void *data)
343 {
344         struct resource *res, *next, *cxl = data;
345
346         for (res = cxl->child; res; res = next) {
347                 struct resource *victim = cxl_get_public_resource(res);
348
349                 next = res->sibling;
350                 remove_resource(res);
351
352                 if (victim) {
353                         remove_resource(victim);
354                         kfree(victim);
355                 }
356
357                 del_cxl_resource(res);
358         }
359 }
360
361 /**
362  * add_cxl_resources() - reflect CXL fixed memory windows in iomem_resource
363  * @cxl_res: A standalone resource tree where each CXL window is a sibling
364  *
365  * Walk each CXL window in @cxl_res and add it to iomem_resource potentially
366  * expanding its boundaries to ensure that any conflicting resources become
367  * children. If a window is expanded it may then conflict with a another window
368  * entry and require the window to be truncated or trimmed. Consider this
369  * situation:
370  *
371  * |-- "CXL Window 0" --||----- "CXL Window 1" -----|
372  * |--------------- "System RAM" -------------|
373  *
374  * ...where platform firmware has established as System RAM resource across 2
375  * windows, but has left some portion of window 1 for dynamic CXL region
376  * provisioning. In this case "Window 0" will span the entirety of the "System
377  * RAM" span, and "CXL Window 1" is truncated to the remaining tail past the end
378  * of that "System RAM" resource.
379  */
380 static int add_cxl_resources(struct resource *cxl_res)
381 {
382         struct resource *res, *new, *next;
383
384         for (res = cxl_res->child; res; res = next) {
385                 new = kzalloc(sizeof(*new), GFP_KERNEL);
386                 if (!new)
387                         return -ENOMEM;
388                 new->name = res->name;
389                 new->start = res->start;
390                 new->end = res->end;
391                 new->flags = IORESOURCE_MEM;
392                 new->desc = IORES_DESC_CXL;
393
394                 /*
395                  * Record the public resource in the private cxl_res tree for
396                  * later removal.
397                  */
398                 cxl_set_public_resource(res, new);
399
400                 insert_resource_expand_to_fit(&iomem_resource, new);
401
402                 next = res->sibling;
403                 while (next && resource_overlaps(new, next)) {
404                         if (resource_contains(new, next)) {
405                                 struct resource *_next = next->sibling;
406
407                                 remove_resource(next);
408                                 del_cxl_resource(next);
409                                 next = _next;
410                         } else
411                                 next->start = new->end + 1;
412                 }
413         }
414         return 0;
415 }
416
417 static int pair_cxl_resource(struct device *dev, void *data)
418 {
419         struct resource *cxl_res = data;
420         struct resource *p;
421
422         if (!is_root_decoder(dev))
423                 return 0;
424
425         for (p = cxl_res->child; p; p = p->sibling) {
426                 struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
427                 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
428                 struct resource res = {
429                         .start = cxld->hpa_range.start,
430                         .end = cxld->hpa_range.end,
431                         .flags = IORESOURCE_MEM,
432                 };
433
434                 if (resource_contains(p, &res)) {
435                         cxlrd->res = cxl_get_public_resource(p);
436                         break;
437                 }
438         }
439
440         return 0;
441 }
442
443 static int cxl_acpi_probe(struct platform_device *pdev)
444 {
445         int rc;
446         struct resource *cxl_res;
447         struct cxl_port *root_port;
448         struct device *host = &pdev->dev;
449         struct acpi_device *adev = ACPI_COMPANION(host);
450         struct cxl_cfmws_context ctx;
451
452         device_lock_set_class(&pdev->dev, &cxl_root_key);
453         rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class,
454                                       &pdev->dev);
455         if (rc)
456                 return rc;
457
458         cxl_res = devm_kzalloc(host, sizeof(*cxl_res), GFP_KERNEL);
459         if (!cxl_res)
460                 return -ENOMEM;
461         cxl_res->name = "CXL mem";
462         cxl_res->start = 0;
463         cxl_res->end = -1;
464         cxl_res->flags = IORESOURCE_MEM;
465
466         root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
467         if (IS_ERR(root_port))
468                 return PTR_ERR(root_port);
469         dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
470
471         rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
472                               add_host_bridge_dport);
473         if (rc < 0)
474                 return rc;
475
476         rc = devm_add_action_or_reset(host, remove_cxl_resources, cxl_res);
477         if (rc)
478                 return rc;
479
480         ctx = (struct cxl_cfmws_context) {
481                 .dev = host,
482                 .root_port = root_port,
483                 .cxl_res = cxl_res,
484         };
485         rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);
486         if (rc < 0)
487                 return -ENXIO;
488
489         rc = add_cxl_resources(cxl_res);
490         if (rc)
491                 return rc;
492
493         /*
494          * Populate the root decoders with their related iomem resource,
495          * if present
496          */
497         device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
498
499         /*
500          * Root level scanned with host-bridge as dports, now scan host-bridges
501          * for their role as CXL uports to their CXL-capable PCIe Root Ports.
502          */
503         rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
504                               add_host_bridge_uport);
505         if (rc < 0)
506                 return rc;
507
508         if (IS_ENABLED(CONFIG_CXL_PMEM))
509                 rc = device_for_each_child(&root_port->dev, root_port,
510                                            add_root_nvdimm_bridge);
511         if (rc < 0)
512                 return rc;
513
514         /* In case PCI is scanned before ACPI re-trigger memdev attach */
515         return cxl_bus_rescan();
516 }
517
518 static const struct acpi_device_id cxl_acpi_ids[] = {
519         { "ACPI0017" },
520         { },
521 };
522 MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
523
524 static const struct platform_device_id cxl_test_ids[] = {
525         { "cxl_acpi" },
526         { },
527 };
528 MODULE_DEVICE_TABLE(platform, cxl_test_ids);
529
530 static struct platform_driver cxl_acpi_driver = {
531         .probe = cxl_acpi_probe,
532         .driver = {
533                 .name = KBUILD_MODNAME,
534                 .acpi_match_table = cxl_acpi_ids,
535         },
536         .id_table = cxl_test_ids,
537 };
538
539 module_platform_driver(cxl_acpi_driver);
540 MODULE_LICENSE("GPL v2");
541 MODULE_IMPORT_NS(CXL);
542 MODULE_IMPORT_NS(ACPI);