aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorIan Munsie <imunsie@au1.ibm.com>2015-02-04 03:09:01 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2015-02-05 19:16:56 -0500
commitb087e6190ddcd9ae4e8ff2c788d2b32f193e946b (patch)
treef62802d81710bb9a4c9c8b15ceb5fe4498828383 /drivers/misc
parentc2c896bee08e1461fc24f9bf7dd57e2c63f6db70 (diff)
cxl: Export optional AFU configuration record in sysfs
An AFU may optionally contain one or more PCIe like configuration records, which can be used to identify the AFU. This patch adds support for exposing the raw config space and the vendor, device and class code under sysfs. These will appear in a subdirectory of the AFU device corresponding with the configuration record number, e.g. cat /sys/class/cxl/afu0.0/cr0/vendor 0x1014 cat /sys/class/cxl/afu0.0/cr0/device 0x4350 cat /sys/class/cxl/afu0.0/cr0/class 0x120000 hexdump -C /sys/class/cxl/afu0.0/cr0/config 00000000 14 10 50 43 00 00 00 00 06 00 00 12 00 00 00 00 |..PC............| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000100 These files behave in much the same way as the equivalent files for PCI devices, with one exception being that the config file is currently read-only and restricted to the root user. It is not necessarily required to be this strict, but we currently do not have a compelling use-case to make it writable and/or world-readable, so I erred on the side of being restrictive. Signed-off-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/cxl/cxl.h13
-rw-r--r--drivers/misc/cxl/pci.c23
-rw-r--r--drivers/misc/cxl/sysfs.c179
3 files changed, 205 insertions, 10 deletions
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
index 6a6a487464c5..a1cee4767ec6 100644
--- a/drivers/misc/cxl/cxl.h
+++ b/drivers/misc/cxl/cxl.h
@@ -382,6 +382,10 @@ struct cxl_afu {
382 int slice; 382 int slice;
383 int modes_supported; 383 int modes_supported;
384 int current_mode; 384 int current_mode;
385 int crs_num;
386 u64 crs_len;
387 u64 crs_offset;
388 struct list_head crs;
385 enum prefault_modes prefault_mode; 389 enum prefault_modes prefault_mode;
386 bool psa; 390 bool psa;
387 bool pp_psa; 391 bool pp_psa;
@@ -551,6 +555,15 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg
551#define cxl_p2n_read(afu, reg) \ 555#define cxl_p2n_read(afu, reg) \
552 in_be64(_cxl_p2n_addr(afu, reg)) 556 in_be64(_cxl_p2n_addr(afu, reg))
553 557
558
559#define cxl_afu_cr_read64(afu, cr, off) \
560 in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off))
561#define cxl_afu_cr_read32(afu, cr, off) \
562 in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off))
563u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off);
564u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off);
565
566
554struct cxl_calls { 567struct cxl_calls {
555 void (*cxl_slbia)(struct mm_struct *mm); 568 void (*cxl_slbia)(struct mm_struct *mm);
556 struct module *owner; 569 struct module *owner;
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index cb250673b5c6..2b2e1b80d759 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -114,6 +114,24 @@
114#define AFUD_EB_LEN(val) EXTRACT_PPC_BITS(val, 8, 63) 114#define AFUD_EB_LEN(val) EXTRACT_PPC_BITS(val, 8, 63)
115#define AFUD_READ_EB_OFF(afu) AFUD_READ(afu, 0x48) 115#define AFUD_READ_EB_OFF(afu) AFUD_READ(afu, 0x48)
116 116
117u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off)
118{
119 u64 aligned_off = off & ~0x3L;
120 u32 val;
121
122 val = cxl_afu_cr_read32(afu, cr, aligned_off);
123 return (val >> ((off & 0x2) * 8)) & 0xffff;
124}
125
126u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off)
127{
128 u64 aligned_off = off & ~0x3L;
129 u32 val;
130
131 val = cxl_afu_cr_read32(afu, cr, aligned_off);
132 return (val >> ((off & 0x3) * 8)) & 0xff;
133}
134
117static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = { 135static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = {
118 { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), }, 136 { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), },
119 { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), }, 137 { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), },
@@ -556,6 +574,7 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu)
556 val = AFUD_READ_INFO(afu); 574 val = AFUD_READ_INFO(afu);
557 afu->pp_irqs = AFUD_NUM_INTS_PER_PROC(val); 575 afu->pp_irqs = AFUD_NUM_INTS_PER_PROC(val);
558 afu->max_procs_virtualised = AFUD_NUM_PROCS(val); 576 afu->max_procs_virtualised = AFUD_NUM_PROCS(val);
577 afu->crs_num = AFUD_NUM_CRS(val);
559 578
560 if (AFUD_AFU_DIRECTED(val)) 579 if (AFUD_AFU_DIRECTED(val))
561 afu->modes_supported |= CXL_MODE_DIRECTED; 580 afu->modes_supported |= CXL_MODE_DIRECTED;
@@ -570,6 +589,10 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu)
570 if ((afu->pp_psa = AFUD_PPPSA_PP(val))) 589 if ((afu->pp_psa = AFUD_PPPSA_PP(val)))
571 afu->pp_offset = AFUD_READ_PPPSA_OFF(afu); 590 afu->pp_offset = AFUD_READ_PPPSA_OFF(afu);
572 591
592 val = AFUD_READ_CR(afu);
593 afu->crs_len = AFUD_CR_LEN(val) * 256;
594 afu->crs_offset = AFUD_READ_CR_OFF(afu);
595
573 return 0; 596 return 0;
574} 597}
575 598
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c
index adf1f6d84913..d0c38c7bc0c4 100644
--- a/drivers/misc/cxl/sysfs.c
+++ b/drivers/misc/cxl/sysfs.c
@@ -10,6 +10,7 @@
10#include <linux/kernel.h> 10#include <linux/kernel.h>
11#include <linux/device.h> 11#include <linux/device.h>
12#include <linux/sysfs.h> 12#include <linux/sysfs.h>
13#include <linux/pci_regs.h>
13 14
14#include "cxl.h" 15#include "cxl.h"
15 16
@@ -367,8 +368,6 @@ static struct device_attribute afu_attrs[] = {
367 __ATTR(reset, S_IWUSR, NULL, reset_store_afu), 368 __ATTR(reset, S_IWUSR, NULL, reset_store_afu),
368}; 369};
369 370
370
371
372int cxl_sysfs_adapter_add(struct cxl *adapter) 371int cxl_sysfs_adapter_add(struct cxl *adapter)
373{ 372{
374 int i, rc; 373 int i, rc;
@@ -391,31 +390,191 @@ void cxl_sysfs_adapter_remove(struct cxl *adapter)
391 device_remove_file(&adapter->dev, &adapter_attrs[i]); 390 device_remove_file(&adapter->dev, &adapter_attrs[i]);
392} 391}
393 392
393struct afu_config_record {
394 struct kobject kobj;
395 struct bin_attribute config_attr;
396 struct list_head list;
397 int cr;
398 u16 device;
399 u16 vendor;
400 u32 class;
401};
402
403#define to_cr(obj) container_of(obj, struct afu_config_record, kobj)
404
405static ssize_t vendor_show(struct kobject *kobj,
406 struct kobj_attribute *attr, char *buf)
407{
408 struct afu_config_record *cr = to_cr(kobj);
409
410 return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor);
411}
412
413static ssize_t device_show(struct kobject *kobj,
414 struct kobj_attribute *attr, char *buf)
415{
416 struct afu_config_record *cr = to_cr(kobj);
417
418 return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device);
419}
420
421static ssize_t class_show(struct kobject *kobj,
422 struct kobj_attribute *attr, char *buf)
423{
424 struct afu_config_record *cr = to_cr(kobj);
425
426 return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class);
427}
428
429static ssize_t afu_read_config(struct file *filp, struct kobject *kobj,
430 struct bin_attribute *bin_attr, char *buf,
431 loff_t off, size_t count)
432{
433 struct afu_config_record *cr = to_cr(kobj);
434 struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj));
435
436 u64 i, j, val, size = afu->crs_len;
437
438 if (off > size)
439 return 0;
440 if (off + count > size)
441 count = size - off;
442
443 for (i = 0; i < count;) {
444 val = cxl_afu_cr_read64(afu, cr->cr, off & ~0x7);
445 for (j = off & 0x7; j < 8 && i < count; i++, j++, off++)
446 buf[i] = (val >> (j * 8)) & 0xff;
447 }
448
449 return count;
450}
451
452static struct kobj_attribute vendor_attribute =
453 __ATTR_RO(vendor);
454static struct kobj_attribute device_attribute =
455 __ATTR_RO(device);
456static struct kobj_attribute class_attribute =
457 __ATTR_RO(class);
458
459static struct attribute *afu_cr_attrs[] = {
460 &vendor_attribute.attr,
461 &device_attribute.attr,
462 &class_attribute.attr,
463 NULL,
464};
465
466static void release_afu_config_record(struct kobject *kobj)
467{
468 struct afu_config_record *cr = to_cr(kobj);
469
470 kfree(cr);
471}
472
473static struct kobj_type afu_config_record_type = {
474 .sysfs_ops = &kobj_sysfs_ops,
475 .release = release_afu_config_record,
476 .default_attrs = afu_cr_attrs,
477};
478
479static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx)
480{
481 struct afu_config_record *cr;
482 int rc;
483
484 cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL);
485 if (!cr)
486 return ERR_PTR(-ENOMEM);
487
488 cr->cr = cr_idx;
489 cr->device = cxl_afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID);
490 cr->vendor = cxl_afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID);
491 cr->class = cxl_afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION) >> 8;
492
493 /*
494 * Export raw AFU PCIe like config record. For now this is read only by
495 * root - we can expand that later to be readable by non-root and maybe
496 * even writable provided we have a good use-case. Once we suport
497 * exposing AFUs through a virtual PHB they will get that for free from
498 * Linux' PCI infrastructure, but until then it's not clear that we
499 * need it for anything since the main use case is just identifying
500 * AFUs, which can be done via the vendor, device and class attributes.
501 */
502 sysfs_bin_attr_init(&cr->config_attr);
503 cr->config_attr.attr.name = "config";
504 cr->config_attr.attr.mode = S_IRUSR;
505 cr->config_attr.size = afu->crs_len;
506 cr->config_attr.read = afu_read_config;
507
508 rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type,
509 &afu->dev.kobj, "cr%i", cr->cr);
510 if (rc)
511 goto err;
512
513 rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr);
514 if (rc)
515 goto err1;
516
517 rc = kobject_uevent(&cr->kobj, KOBJ_ADD);
518 if (rc)
519 goto err2;
520
521 return cr;
522err2:
523 sysfs_remove_bin_file(&cr->kobj, &cr->config_attr);
524err1:
525 kobject_put(&cr->kobj);
526 return ERR_PTR(rc);
527err:
528 kfree(cr);
529 return ERR_PTR(rc);
530}
531
532void cxl_sysfs_afu_remove(struct cxl_afu *afu)
533{
534 struct afu_config_record *cr, *tmp;
535 int i;
536
537 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
538 device_remove_file(&afu->dev, &afu_attrs[i]);
539
540 list_for_each_entry_safe(cr, tmp, &afu->crs, list) {
541 sysfs_remove_bin_file(&cr->kobj, &cr->config_attr);
542 kobject_put(&cr->kobj);
543 }
544}
545
394int cxl_sysfs_afu_add(struct cxl_afu *afu) 546int cxl_sysfs_afu_add(struct cxl_afu *afu)
395{ 547{
548 struct afu_config_record *cr;
396 int i, rc; 549 int i, rc;
397 550
551 INIT_LIST_HEAD(&afu->crs);
552
398 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { 553 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) {
399 if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) 554 if ((rc = device_create_file(&afu->dev, &afu_attrs[i])))
400 goto err; 555 goto err;
401 } 556 }
402 557
558 for (i = 0; i < afu->crs_num; i++) {
559 cr = cxl_sysfs_afu_new_cr(afu, i);
560 if (IS_ERR(cr)) {
561 rc = PTR_ERR(cr);
562 goto err1;
563 }
564 list_add(&cr->list, &afu->crs);
565 }
566
403 return 0; 567 return 0;
404 568
569err1:
570 cxl_sysfs_afu_remove(afu);
571 return rc;
405err: 572err:
406 for (i--; i >= 0; i--) 573 for (i--; i >= 0; i--)
407 device_remove_file(&afu->dev, &afu_attrs[i]); 574 device_remove_file(&afu->dev, &afu_attrs[i]);
408 return rc; 575 return rc;
409} 576}
410 577
411void cxl_sysfs_afu_remove(struct cxl_afu *afu)
412{
413 int i;
414
415 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
416 device_remove_file(&afu->dev, &afu_attrs[i]);
417}
418
419int cxl_sysfs_afu_m_add(struct cxl_afu *afu) 578int cxl_sysfs_afu_m_add(struct cxl_afu *afu)
420{ 579{
421 int i, rc; 580 int i, rc;