diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/memory/fsl-corenet-cf.c | 36 | ||||
-rw-r--r-- | drivers/misc/cxl/Makefile | 5 | ||||
-rw-r--r-- | drivers/misc/cxl/cxl.h | 22 | ||||
-rw-r--r-- | drivers/misc/cxl/fault.c | 11 | ||||
-rw-r--r-- | drivers/misc/cxl/file.c | 7 | ||||
-rw-r--r-- | drivers/misc/cxl/irq.c | 7 | ||||
-rw-r--r-- | drivers/misc/cxl/main.c | 2 | ||||
-rw-r--r-- | drivers/misc/cxl/native.c | 39 | ||||
-rw-r--r-- | drivers/misc/cxl/pci.c | 123 | ||||
-rw-r--r-- | drivers/misc/cxl/sysfs.c | 236 | ||||
-rw-r--r-- | drivers/misc/cxl/trace.c | 13 | ||||
-rw-r--r-- | drivers/misc/cxl/trace.h | 459 | ||||
-rw-r--r-- | drivers/ps3/ps3-vuart.c | 5 | ||||
-rw-r--r-- | drivers/ps3/sys-manager-core.c | 6 | ||||
-rw-r--r-- | drivers/ps3/vuart.h | 16 |
15 files changed, 951 insertions, 36 deletions
diff --git a/drivers/memory/fsl-corenet-cf.c b/drivers/memory/fsl-corenet-cf.c index fc7ab5a3561e..d708ded5457b 100644 --- a/drivers/memory/fsl-corenet-cf.c +++ b/drivers/memory/fsl-corenet-cf.c @@ -27,18 +27,29 @@ enum ccf_version { struct ccf_info { enum ccf_version version; int err_reg_offs; + bool has_brr; }; static const struct ccf_info ccf1_info = { .version = CCF1, .err_reg_offs = 0xa00, + .has_brr = false, }; static const struct ccf_info ccf2_info = { .version = CCF2, .err_reg_offs = 0xe40, + .has_brr = true, }; +/* + * This register is present but not documented, with different values for + * IP_ID, on other chips with fsl,corenet2-cf such as t4240 and b4860. + */ +#define CCF_BRR 0xbf8 +#define CCF_BRR_IPID 0xffff0000 +#define CCF_BRR_IPID_T1040 0x09310000 + static const struct of_device_id ccf_matches[] = { { .compatible = "fsl,corenet1-cf", @@ -66,6 +77,8 @@ struct ccf_err_regs { /* LAE/CV also valid for errdis and errinten */ #define ERRDET_LAE (1 << 0) /* Local Access Error */ #define ERRDET_CV (1 << 1) /* Coherency Violation */ +#define ERRDET_UTID (1 << 2) /* Unavailable Target ID (t1040) */ +#define ERRDET_MCST (1 << 3) /* Multicast Stash (t1040) */ #define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */ #define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT) #define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */ @@ -84,6 +97,7 @@ struct ccf_private { struct device *dev; void __iomem *regs; struct ccf_err_regs __iomem *err_regs; + bool t1040; }; static irqreturn_t ccf_irq(int irq, void *dev_id) @@ -142,6 +156,12 @@ static irqreturn_t ccf_irq(int irq, void *dev_id) if (errdet & ERRDET_CV) dev_crit(ccf->dev, "Coherency Violation\n"); + if (errdet & ERRDET_UTID) + dev_crit(ccf->dev, "Unavailable Target ID\n"); + + if (errdet & ERRDET_MCST) + dev_crit(ccf->dev, "Multicast Stash\n"); + if (cap_valid) { dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n", addr, src_id); @@ -157,6 +177,7 @@ static int ccf_probe(struct platform_device *pdev) struct ccf_private *ccf; struct resource *r; const struct of_device_id *match; + u32 errinten; int ret, irq; match = of_match_device(ccf_matches, &pdev->dev); @@ -183,6 +204,13 @@ static int ccf_probe(struct platform_device *pdev) ccf->info = match->data; ccf->err_regs = ccf->regs + ccf->info->err_reg_offs; + if (ccf->info->has_brr) { + u32 brr = ioread32be(ccf->regs + CCF_BRR); + + if ((brr & CCF_BRR_IPID) == CCF_BRR_IPID_T1040) + ccf->t1040 = true; + } + dev_set_drvdata(&pdev->dev, ccf); irq = platform_get_irq(pdev, 0); @@ -197,15 +225,19 @@ static int ccf_probe(struct platform_device *pdev) return ret; } + errinten = ERRDET_LAE | ERRDET_CV; + if (ccf->t1040) + errinten |= ERRDET_UTID | ERRDET_MCST; + switch (ccf->info->version) { case CCF1: /* On CCF1 this register enables rather than disables. */ - iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errdis); + iowrite32be(errinten, &ccf->err_regs->errdis); break; case CCF2: iowrite32be(0, &ccf->err_regs->errdis); - iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errinten); + iowrite32be(errinten, &ccf->err_regs->errinten); break; } diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index 165e98fef2c2..edb494d3ff27 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile @@ -1,3 +1,6 @@ -cxl-y += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o +cxl-y += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o trace.o obj-$(CONFIG_CXL) += cxl.o obj-$(CONFIG_CXL_BASE) += base.o + +# For tracepoints to include our trace.h from tracepoint infrastructure: +CFLAGS_trace.o := -I$(src) diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 28078f8894a5..a1cee4767ec6 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -287,6 +287,13 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PE_SOFTWARE_STATE_S (1ul << (31 - 30)) /* Suspend */ #define CXL_PE_SOFTWARE_STATE_T (1ul << (31 - 31)) /* Terminate */ +/****** CXL_PSL_RXCTL_An (Implementation Specific) ************************** + * Controls AFU Hang Pulse, which sets the timeout for the AFU to respond to + * the PSL for any response (except MMIO). Timeouts will occur between 1x to 2x + * of the hang pulse frequency. + */ +#define CXL_PSL_RXCTL_AFUHP_4S 0x7000000000000000ULL + /* SPA->sw_command_status */ #define CXL_SPA_SW_CMD_MASK 0xffff000000000000ULL #define CXL_SPA_SW_CMD_TERMINATE 0x0001000000000000ULL @@ -375,6 +382,10 @@ struct cxl_afu { int slice; int modes_supported; int current_mode; + int crs_num; + u64 crs_len; + u64 crs_offset; + struct list_head crs; enum prefault_modes prefault_mode; bool psa; bool pp_psa; @@ -481,6 +492,8 @@ void cxl_release_one_irq(struct cxl *adapter, int hwirq); int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num); void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter); int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, unsigned int virq); +int cxl_update_image_control(struct cxl *adapter); +int cxl_reset(struct cxl *adapter); /* common == phyp + powernv */ struct cxl_process_element_common { @@ -542,6 +555,15 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg #define cxl_p2n_read(afu, reg) \ in_be64(_cxl_p2n_addr(afu, reg)) + +#define cxl_afu_cr_read64(afu, cr, off) \ + in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)) +#define cxl_afu_cr_read32(afu, cr, off) \ + in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)) +u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off); +u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off); + + struct cxl_calls { void (*cxl_slbia)(struct mm_struct *mm); struct module *owner; diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index f8684bca2d79..5286b8b704f5 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -20,6 +20,7 @@ #include <asm/mmu.h> #include "cxl.h" +#include "trace.h" static bool sste_matches(struct cxl_sste *sste, struct copro_slb *slb) { @@ -75,6 +76,7 @@ static void cxl_load_segment(struct cxl_context *ctx, struct copro_slb *slb) pr_devel("CXL Populating SST[%li]: %#llx %#llx\n", sste - ctx->sstp, slb->vsid, slb->esid); + trace_cxl_ste_write(ctx, sste - ctx->sstp, slb->esid, slb->vsid); sste->vsid_data = cpu_to_be64(slb->vsid); sste->esid_data = cpu_to_be64(slb->esid); @@ -116,6 +118,7 @@ static int cxl_handle_segment_miss(struct cxl_context *ctx, int rc; pr_devel("CXL interrupt: Segment fault pe: %i ea: %#llx\n", ctx->pe, ea); + trace_cxl_ste_miss(ctx, ea); if ((rc = cxl_fault_segment(ctx, mm, ea))) cxl_ack_ae(ctx); @@ -135,6 +138,8 @@ static void cxl_handle_page_fault(struct cxl_context *ctx, int result; unsigned long access, flags, inv_flags = 0; + trace_cxl_pte_miss(ctx, dsisr, dar); + if ((result = copro_handle_mm_fault(mm, dar, dsisr, &flt))) { pr_devel("copro_handle_mm_fault failed: %#x\n", result); return cxl_ack_ae(ctx); @@ -180,6 +185,12 @@ void cxl_handle_fault(struct work_struct *fault_work) return; } + /* Early return if the context is being / has been detached */ + if (ctx->status == CLOSED) { + cxl_ack_ae(ctx); + return; + } + pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. " "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar); diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index b15d8113877c..2364bcadb9a9 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -23,6 +23,7 @@ #include <asm/copro.h> #include "cxl.h" +#include "trace.h" #define CXL_NUM_MINORS 256 /* Total to reserve */ #define CXL_DEV_MINORS 13 /* 1 control + 4 AFUs * 3 (dedicated/master/shared) */ @@ -186,9 +187,13 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, */ ctx->pid = get_pid(get_task_pid(current, PIDTYPE_PID)); + trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr); + if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor, - amr))) + amr))) { + afu_release_irqs(ctx); goto out; + } ctx->status = STARTED; rc = 0; diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index c294925f73ee..c8929c526691 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -17,6 +17,7 @@ #include <misc/cxl.h> #include "cxl.h" +#include "trace.h" /* XXX: This is implementation specific */ static irqreturn_t handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, u64 errstat) @@ -100,6 +101,8 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) dsisr = irq_info->dsisr; dar = irq_info->dar; + trace_cxl_psl_irq(ctx, irq, dsisr, dar); + pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar); if (dsisr & CXL_PSL_DSISR_An_DS) { @@ -167,6 +170,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) } cxl_ack_irq(ctx, CXL_PSL_TFC_An_A, 0); + return IRQ_HANDLED; } if (dsisr & CXL_PSL_DSISR_An_OC) pr_devel("CXL interrupt: OS Context Warning\n"); @@ -237,6 +241,7 @@ static irqreturn_t cxl_irq_afu(int irq, void *data) return IRQ_HANDLED; } + trace_cxl_afu_irq(ctx, afu_irq, irq, hwirq); pr_devel("Received AFU interrupt %i for pe: %i (virq %i hwirq %lx)\n", afu_irq, ctx->pe, irq, hwirq); @@ -436,7 +441,7 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count) */ INIT_LIST_HEAD(&ctx->irq_names); for (r = 1; r < CXL_IRQ_RANGES; r++) { - for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { + for (i = 0; i < ctx->irqs.range[r]; i++) { irq_name = kmalloc(sizeof(struct cxl_irq_name), GFP_KERNEL); if (!irq_name) diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index 4cde9b661642..8ccddceead66 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -23,6 +23,7 @@ #include <misc/cxl.h> #include "cxl.h" +#include "trace.h" static DEFINE_SPINLOCK(adapter_idr_lock); static DEFINE_IDR(cxl_adapter_idr); @@ -48,6 +49,7 @@ static inline void _cxl_slbia(struct cxl_context *ctx, struct mm_struct *mm) ctx->afu->adapter->adapter_num, ctx->afu->slice, ctx->pe); spin_lock_irqsave(&ctx->sste_lock, flags); + trace_cxl_slbia(ctx); memset(ctx->sstp, 0, ctx->sst_size); spin_unlock_irqrestore(&ctx->sste_lock, flags); mb(); diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index f2b37b41a0da..29185fc61276 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -18,24 +18,28 @@ #include <misc/cxl.h> #include "cxl.h" +#include "trace.h" static int afu_control(struct cxl_afu *afu, u64 command, u64 result, u64 mask, bool enabled) { u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + int rc = 0; spin_lock(&afu->afu_cntl_lock); pr_devel("AFU command starting: %llx\n", command); + trace_cxl_afu_ctrl(afu, command); + cxl_p2n_write(afu, CXL_AFU_Cntl_An, AFU_Cntl | command); AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); while ((AFU_Cntl & mask) != result) { if (time_after_eq(jiffies, timeout)) { dev_warn(&afu->dev, "WARNING: AFU control timed out!\n"); - spin_unlock(&afu->afu_cntl_lock); - return -EBUSY; + rc = -EBUSY; + goto out; } pr_devel_ratelimited("AFU control... (0x%.16llx)\n", AFU_Cntl | command); @@ -44,9 +48,11 @@ static int afu_control(struct cxl_afu *afu, u64 command, }; pr_devel("AFU command complete: %llx\n", command); afu->enabled = enabled; +out: + trace_cxl_afu_ctrl_done(afu, command, rc); spin_unlock(&afu->afu_cntl_lock); - return 0; + return rc; } static int afu_enable(struct cxl_afu *afu) @@ -91,6 +97,9 @@ int cxl_psl_purge(struct cxl_afu *afu) u64 dsisr, dar; u64 start, end; unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + int rc = 0; + + trace_cxl_psl_ctrl(afu, CXL_PSL_SCNTL_An_Pc); pr_devel("PSL purge request\n"); @@ -107,7 +116,8 @@ int cxl_psl_purge(struct cxl_afu *afu) == CXL_PSL_SCNTL_An_Ps_Pending) { if (time_after_eq(jiffies, timeout)) { dev_warn(&afu->dev, "WARNING: PSL Purge timed out!\n"); - return -EBUSY; + rc = -EBUSY; + goto out; } dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%.16llx PSL_DSISR: 0x%.16llx\n", PSL_CNTL, dsisr); @@ -128,7 +138,9 @@ int cxl_psl_purge(struct cxl_afu *afu) cxl_p1n_write(afu, CXL_PSL_SCNTL_An, PSL_CNTL & ~CXL_PSL_SCNTL_An_Pc); - return 0; +out: + trace_cxl_psl_ctrl_done(afu, CXL_PSL_SCNTL_An_Pc, rc); + return rc; } static int spa_max_procs(int spa_size) @@ -185,6 +197,7 @@ static int alloc_spa(struct cxl_afu *afu) static void release_spa(struct cxl_afu *afu) { + cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0); free_pages((unsigned long) afu->spa, afu->spa_order); } @@ -278,6 +291,9 @@ static int do_process_element_cmd(struct cxl_context *ctx, { u64 state; unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + int rc = 0; + + trace_cxl_llcmd(ctx, cmd); WARN_ON(!ctx->afu->enabled); @@ -289,12 +305,14 @@ static int do_process_element_cmd(struct cxl_context *ctx, while (1) { if (time_after_eq(jiffies, timeout)) { dev_warn(&ctx->afu->dev, "WARNING: Process Element Command timed out!\n"); - return -EBUSY; + rc = -EBUSY; + goto out; } state = be64_to_cpup(ctx->afu->sw_command_status); if (state == ~0ULL) { pr_err("cxl: Error adding process element to AFU\n"); - return -1; + rc = -1; + goto out; } if ((state & (CXL_SPA_SW_CMD_MASK | CXL_SPA_SW_STATE_MASK | CXL_SPA_SW_LINK_MASK)) == (cmd | (cmd >> 16) | ctx->pe)) @@ -309,7 +327,9 @@ static int do_process_element_cmd(struct cxl_context *ctx, schedule(); } - return 0; +out: + trace_cxl_llcmd_done(ctx, cmd, rc); + return rc; } static int add_process_element(struct cxl_context *ctx) @@ -629,6 +649,8 @@ static inline int detach_process_native_afu_directed(struct cxl_context *ctx) int cxl_detach_process(struct cxl_context *ctx) { + trace_cxl_detach(ctx); + if (ctx->afu->current_mode == CXL_MODE_DEDICATED) return detach_process_native_dedicated(ctx); @@ -667,6 +689,7 @@ static void recover_psl_err(struct cxl_afu *afu, u64 errstat) int cxl_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask) { + trace_cxl_psl_irq_ack(ctx, tfc); if (tfc) cxl_p2n_write(ctx->afu, CXL_PSL_TFC_An, tfc); if (psl_reset_mask) diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 0f2cc9f8b4db..1ef01647265f 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -21,6 +21,7 @@ #include <asm/msi_bitmap.h> #include <asm/pci-bridge.h> /* for struct pci_controller */ #include <asm/pnv-pci.h> +#include <asm/io.h> #include "cxl.h" @@ -113,6 +114,24 @@ #define AFUD_EB_LEN(val) EXTRACT_PPC_BITS(val, 8, 63) #define AFUD_READ_EB_OFF(afu) AFUD_READ(afu, 0x48) +u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off) +{ + u64 aligned_off = off & ~0x3L; + u32 val; + + val = cxl_afu_cr_read32(afu, cr, aligned_off); + return (val >> ((off & 0x2) * 8)) & 0xffff; +} + +u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off) +{ + u64 aligned_off = off & ~0x3L; + u32 val; + + val = cxl_afu_cr_read32(afu, cr, aligned_off); + return (val >> ((off & 0x3) * 8)) & 0xff; +} + static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), }, { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), }, @@ -316,7 +335,7 @@ static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev u64 psl_dsnctl; u64 chipid; - if (!(np = pnv_pci_to_phb_node(dev))) + if (!(np = pnv_pci_get_phb_node(dev))) return -ENODEV; while (np && !(prop = of_get_property(np, "ibm,chip-id", NULL))) @@ -348,7 +367,7 @@ static int init_implementation_afu_regs(struct cxl_afu *afu) cxl_p1n_write(afu, CXL_PSL_COALLOC_A, 0xFF000000FEFEFEFEULL); /* for debugging with trace arrays */ cxl_p1n_write(afu, CXL_PSL_SLICE_TRACE, 0x0000FFFF00000000ULL); - cxl_p1n_write(afu, CXL_PSL_RXCTL_A, 0xF000000000000000ULL); + cxl_p1n_write(afu, CXL_PSL_RXCTL_A, CXL_PSL_RXCTL_AFUHP_4S); return 0; } @@ -361,6 +380,41 @@ int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, return pnv_cxl_ioda_msi_setup(dev, hwirq, virq); } +int cxl_update_image_control(struct cxl *adapter) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + int rc; + int vsec; + u8 image_state; + + if (!(vsec = find_cxl_vsec(dev))) { + dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n"); + return -ENODEV; + } + + if ((rc = CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state))) { + dev_err(&dev->dev, "failed to read image state: %i\n", rc); + return rc; + } + + if (adapter->perst_loads_image) + image_state |= CXL_VSEC_PERST_LOADS_IMAGE; + else + image_state &= ~CXL_VSEC_PERST_LOADS_IMAGE; + + if (adapter->perst_select_user) + image_state |= CXL_VSEC_PERST_SELECT_USER; + else + image_state &= ~CXL_VSEC_PERST_SELECT_USER; + + if ((rc = CXL_WRITE_VSEC_IMAGE_STATE(dev, vsec, image_state))) { + dev_err(&dev->dev, "failed to update image control: %i\n", rc); + return rc; + } + + return 0; +} + int cxl_alloc_one_irq(struct cxl *adapter) { struct pci_dev *dev = to_pci_dev(adapter->dev.parent); @@ -520,6 +574,7 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu) val = AFUD_READ_INFO(afu); afu->pp_irqs = AFUD_NUM_INTS_PER_PROC(val); afu->max_procs_virtualised = AFUD_NUM_PROCS(val); + afu->crs_num = AFUD_NUM_CRS(val); if (AFUD_AFU_DIRECTED(val)) afu->modes_supported |= CXL_MODE_DIRECTED; @@ -534,11 +589,17 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu) if ((afu->pp_psa = AFUD_PPPSA_PP(val))) afu->pp_offset = AFUD_READ_PPPSA_OFF(afu); + val = AFUD_READ_CR(afu); + afu->crs_len = AFUD_CR_LEN(val) * 256; + afu->crs_offset = AFUD_READ_CR_OFF(afu); + return 0; } static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu) { + int i; + if (afu->psa && afu->adapter->ps_size < (afu->pp_offset + afu->pp_size*afu->max_procs_virtualised)) { dev_err(&afu->dev, "per-process PSA can't fit inside the PSA!\n"); @@ -548,6 +609,13 @@ static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu) if (afu->pp_psa && (afu->pp_size < PAGE_SIZE)) dev_warn(&afu->dev, "AFU uses < PAGE_SIZE per-process PSA!"); + for (i = 0; i < afu->crs_num; i++) { + if ((cxl_afu_cr_read32(afu, i, 0) == 0)) { + dev_err(&afu->dev, "ABORTING: AFU configuration record %i is invalid\n", i); + return -EINVAL; + } + } + return 0; } @@ -706,6 +774,42 @@ static void cxl_remove_afu(struct cxl_afu *afu) device_unregister(&afu->dev); } +int cxl_reset(struct cxl *adapter) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + int rc; + int i; + u32 val; + + dev_info(&dev->dev, "CXL reset\n"); + + for (i = 0; i < adapter->slices; i++) + cxl_remove_afu(adapter->afu[i]); + + /* pcie_warm_reset requests a fundamental pci reset which includes a + * PERST assert/deassert. PERST triggers a loading of the image + * if "user" or "factory" is selected in sysfs */ + if ((rc = pci_set_pcie_reset_state(dev, pcie_warm_reset))) { + dev_err(&dev->dev, "cxl: pcie_warm_reset failed\n"); + return rc; + } + + /* the PERST done above fences the PHB. So, reset depends on EEH + * to unbind the driver, tell Sapphire to reinit the PHB, and rebind + * the driver. Do an mmio read explictly to ensure EEH notices the + * fenced PHB. Retry for a few seconds before giving up. */ + i = 0; + while (((val = mmio_read32be(adapter->p1_mmio)) != 0xffffffff) && + (i < 5)) { + msleep(500); + i++; + } + + if (val != 0xffffffff) + dev_err(&dev->dev, "cxl: PERST failed to trigger EEH\n"); + + return rc; +} static int cxl_map_adapter_regs(struct cxl *adapter, struct pci_dev *dev) { @@ -770,8 +874,8 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image); CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state); adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); - adapter->perst_loads_image = !!(image_state & CXL_VSEC_PERST_LOADS_IMAGE); - adapter->perst_select_user = !!(image_state & CXL_VSEC_PERST_SELECT_USER); + adapter->perst_loads_image = true; + adapter->perst_select_user = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices); CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, &afu_desc_off); @@ -879,6 +983,9 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) if ((rc = cxl_vsec_looks_ok(adapter, dev))) goto err2; + if ((rc = cxl_update_image_control(adapter))) + goto err2; + if ((rc = cxl_map_adapter_regs(adapter, dev))) goto err2; @@ -888,9 +995,15 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) if ((rc = init_implementation_adapter_regs(adapter, dev))) goto err3; - if ((rc = pnv_phb_to_cxl(dev))) + if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_CAPI))) goto err3; + /* If recovery happened, the last step is to turn on snooping. + * In the non-recovery case this has no effect */ + if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON))) { + goto err3; + } + if ((rc = cxl_register_psl_err_irq(adapter))) goto err3; diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 461bdbd5d483..d0c38c7bc0c4 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/sysfs.h> +#include <linux/pci_regs.h> #include "cxl.h" @@ -56,11 +57,68 @@ static ssize_t image_loaded_show(struct device *device, return scnprintf(buf, PAGE_SIZE, "factory\n"); } +static ssize_t reset_adapter_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl *adapter = to_cxl_adapter(device); + int rc; + int val; + + rc = sscanf(buf, "%i", &val); + if ((rc != 1) || (val != 1)) + return -EINVAL; + + if ((rc = cxl_reset(adapter))) + return rc; + return count; +} + +static ssize_t load_image_on_perst_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + if (!adapter->perst_loads_image) + return scnprintf(buf, PAGE_SIZE, "none\n"); + + if (adapter->perst_select_user) + return scnprintf(buf, PAGE_SIZE, "user\n"); + return scnprintf(buf, PAGE_SIZE, "factory\n"); +} + +static ssize_t load_image_on_perst_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl *adapter = to_cxl_adapter(device); + int rc; + + if (!strncmp(buf, "none", 4)) + adapter->perst_loads_image = false; + else if (!strncmp(buf, "user", 4)) { + adapter->perst_select_user = true; + adapter->perst_loads_image = true; + } else if (!strncmp(buf, "factory", 7)) { + adapter->perst_select_user = false; + adapter->perst_loads_image = true; + } else + return -EINVAL; + + if ((rc = cxl_update_image_control(adapter))) + return rc; + + return count; +} + static struct device_attribute adapter_attrs[] = { __ATTR_RO(caia_version), __ATTR_RO(psl_revision), __ATTR_RO(base_image), __ATTR_RO(image_loaded), + __ATTR_RW(load_image_on_perst), + __ATTR(reset, S_IWUSR, NULL, reset_adapter_store), }; @@ -310,8 +368,6 @@ static struct device_attribute afu_attrs[] = { __ATTR(reset, S_IWUSR, NULL, reset_store_afu), }; - - int cxl_sysfs_adapter_add(struct cxl *adapter) { int i, rc; @@ -334,31 +390,191 @@ void cxl_sysfs_adapter_remove(struct cxl *adapter) device_remove_file(&adapter->dev, &adapter_attrs[i]); } +struct afu_config_record { + struct kobject kobj; + struct bin_attribute config_attr; + struct list_head list; + int cr; + u16 device; + u16 vendor; + u32 class; +}; + +#define to_cr(obj) container_of(obj, struct afu_config_record, kobj) + +static ssize_t vendor_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct afu_config_record *cr = to_cr(kobj); + + return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor); +} + +static ssize_t device_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct afu_config_record *cr = to_cr(kobj); + + return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device); +} + +static ssize_t class_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct afu_config_record *cr = to_cr(kobj); + + return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class); +} + +static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct afu_config_record *cr = to_cr(kobj); + struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj)); + + u64 i, j, val, size = afu->crs_len; + + if (off > size) + return 0; + if (off + count > size) + count = size - off; + + for (i = 0; i < count;) { + val = cxl_afu_cr_read64(afu, cr->cr, off & ~0x7); + for (j = off & 0x7; j < 8 && i < count; i++, j++, off++) + buf[i] = (val >> (j * 8)) & 0xff; + } + + return count; +} + +static struct kobj_attribute vendor_attribute = + __ATTR_RO(vendor); +static struct kobj_attribute device_attribute = + __ATTR_RO(device); +static struct kobj_attribute class_attribute = + __ATTR_RO(class); + +static struct attribute *afu_cr_attrs[] = { + &vendor_attribute.attr, + &device_attribute.attr, + &class_attribute.attr, + NULL, +}; + +static void release_afu_config_record(struct kobject *kobj) +{ + struct afu_config_record *cr = to_cr(kobj); + + kfree(cr); +} + +static struct kobj_type afu_config_record_type = { + .sysfs_ops = &kobj_sysfs_ops, + .release = release_afu_config_record, + .default_attrs = afu_cr_attrs, +}; + +static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx) +{ + struct afu_config_record *cr; + int rc; + + cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL); + if (!cr) + return ERR_PTR(-ENOMEM); + + cr->cr = cr_idx; + cr->device = cxl_afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID); + cr->vendor = cxl_afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID); + cr->class = cxl_afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION) >> 8; + + /* + * Export raw AFU PCIe like config record. For now this is read only by + * root - we can expand that later to be readable by non-root and maybe + * even writable provided we have a good use-case. Once we suport + * exposing AFUs through a virtual PHB they will get that for free from + * Linux' PCI infrastructure, but until then it's not clear that we + * need it for anything since the main use case is just identifying + * AFUs, which can be done via the vendor, device and class attributes. + */ + sysfs_bin_attr_init(&cr->config_attr); + cr->config_attr.attr.name = "config"; + cr->config_attr.attr.mode = S_IRUSR; + cr->config_attr.size = afu->crs_len; + cr->config_attr.read = afu_read_config; + + rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, + &afu->dev.kobj, "cr%i", cr->cr); + if (rc) + goto err; + + rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr); + if (rc) + goto err1; + + rc = kobject_uevent(&cr->kobj, KOBJ_ADD); + if (rc) + goto err2; + + return cr; +err2: + sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); +err1: + kobject_put(&cr->kobj); + return ERR_PTR(rc); +err: + kfree(cr); + return ERR_PTR(rc); +} + +void cxl_sysfs_afu_remove(struct cxl_afu *afu) +{ + struct afu_config_record *cr, *tmp; + int i; + + for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) + device_remove_file(&afu->dev, &afu_attrs[i]); + + list_for_each_entry_safe(cr, tmp, &afu->crs, list) { + sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); + kobject_put(&cr->kobj); + } +} + int cxl_sysfs_afu_add(struct cxl_afu *afu) { + struct afu_config_record *cr; int i, rc; + INIT_LIST_HEAD(&afu->crs); + for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) goto err; } + for (i = 0; i < afu->crs_num; i++) { + cr = cxl_sysfs_afu_new_cr(afu, i); + if (IS_ERR(cr)) { + rc = PTR_ERR(cr); + goto err1; + } + list_add(&cr->list, &afu->crs); + } + return 0; +err1: + cxl_sysfs_afu_remove(afu); + return rc; err: for (i--; i >= 0; i--) device_remove_file(&afu->dev, &afu_attrs[i]); return rc; } -void cxl_sysfs_afu_remove(struct cxl_afu *afu) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) - device_remove_file(&afu->dev, &afu_attrs[i]); -} - int cxl_sysfs_afu_m_add(struct cxl_afu *afu) { int i, rc; diff --git a/drivers/misc/cxl/trace.c b/drivers/misc/cxl/trace.c new file mode 100644 index 000000000000..c2b06d319e6e --- /dev/null +++ b/drivers/misc/cxl/trace.c @@ -0,0 +1,13 @@ +/* + * Copyright 2015 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "trace.h" +#endif diff --git a/drivers/misc/cxl/trace.h b/drivers/misc/cxl/trace.h new file mode 100644 index 000000000000..ae434d87887e --- /dev/null +++ b/drivers/misc/cxl/trace.h @@ -0,0 +1,459 @@ +/* + * Copyright 2015 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cxl + +#if !defined(_CXL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CXL_TRACE_H + +#include <linux/tracepoint.h> |