| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* Copyright 2013-2018 IBM Corp. */ |
| |
| #include <skiboot.h> |
| #include <chip.h> |
| #include <phys-map.h> |
| #include <xscom.h> |
| #include <io.h> |
| #include <xive.h> |
| #include <interrupts.h> |
| #include <nvram.h> |
| #include <vas.h> |
| |
| #define vas_err(__fmt,...) prlog(PR_ERR,"VAS: " __fmt, ##__VA_ARGS__) |
| |
| #ifdef VAS_VERBOSE_DEBUG |
| #define vas_vdbg(__x,__fmt,...) prlog(PR_DEBUG,"VAS: " __fmt, ##__VA_ARGS__) |
| #else |
| #define vas_vdbg(__x,__fmt,...) do { } while (0) |
| #endif |
| |
| static int vas_initialized; |
| |
| struct vas { |
| uint32_t chip_id; |
| uint32_t vas_id; |
| uint64_t xscom_base; |
| uint64_t wcbs; |
| uint32_t vas_irq; |
| uint64_t vas_port; |
| }; |
| |
| static inline void get_hvwc_mmio_bar(int chipid, uint64_t *start, uint64_t *len) |
| { |
| phys_map_get(chipid, VAS_HYP_WIN, 0, start, len); |
| } |
| |
| static inline void get_uwc_mmio_bar(int chipid, uint64_t *start, uint64_t *len) |
| { |
| phys_map_get(chipid, VAS_USER_WIN, 0, start, len); |
| } |
| |
| static inline uint64_t compute_vas_scom_addr(struct vas *vas, uint64_t reg) |
| { |
| return vas->xscom_base + reg; |
| } |
| |
| static int vas_scom_write(struct proc_chip *chip, uint64_t reg, uint64_t val) |
| { |
| int rc; |
| uint64_t addr; |
| |
| addr = compute_vas_scom_addr(chip->vas, reg); |
| |
| rc = xscom_write(chip->id, addr, val); |
| if (rc != OPAL_SUCCESS) { |
| vas_err("Error writing 0x%llx to 0x%llx, rc %d\n", val, addr, |
| rc); |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Return true if NX crypto/compression is enabled on this processor. |
| * |
| * On POWER8, NX-842 crypto and compression are allowed, but they do not |
| * use VAS (return true). |
| * |
| * On POWER9, NX 842 and GZIP compressions use VAS but the PASTE instruction |
| * and hence VAS is not enabled in following revisions: |
| * |
| * - Nimbus DD1.X, DD2.01, DD2.1 |
| * - Cumulus DD1.0 |
| * |
| * Return false for these revisions. Return true otherwise. |
| */ |
| __attrconst inline bool vas_nx_enabled(void) |
| { |
| uint32_t pvr; |
| int major, minor; |
| struct proc_chip *chip; |
| |
| chip = next_chip(NULL); |
| |
| pvr = mfspr(SPR_PVR); |
| major = PVR_VERS_MAJ(pvr); |
| minor = PVR_VERS_MIN(pvr); |
| |
| switch (chip->type) { |
| case PROC_CHIP_P9_NIMBUS: |
| return (major > 2 || (major == 2 && minor > 1)); |
| case PROC_CHIP_P9_CUMULUS: |
| return (major > 1 || minor > 0); |
| default: |
| return true; |
| } |
| } |
| |
| /* Interface for NX - make sure VAS is fully initialized first */ |
| __attrconst inline uint64_t vas_get_hvwc_mmio_bar(const int chipid) |
| { |
| uint64_t addr; |
| |
| if (!vas_initialized) |
| return 0ULL; |
| |
| get_hvwc_mmio_bar(chipid, &addr, NULL); |
| |
| return addr; |
| } |
| |
| /* Interface for NX - make sure VAS is fully initialized first */ |
| __attrconst uint64_t vas_get_wcbs_bar(int chipid) |
| { |
| struct proc_chip *chip; |
| |
| if (!vas_initialized) |
| return 0ULL; |
| |
| chip = get_chip(chipid); |
| if (!chip) |
| return 0ULL; |
| |
| return chip->vas->wcbs; |
| } |
| |
| static int init_north_ctl(struct proc_chip *chip) |
| { |
| uint64_t val = 0ULL; |
| |
| val = SETFIELD(VAS_64K_MODE_MASK, val, true); |
| val = SETFIELD(VAS_ACCEPT_PASTE_MASK, val, true); |
| val = SETFIELD(VAS_ENABLE_WC_MMIO_BAR, val, true); |
| val = SETFIELD(VAS_ENABLE_UWC_MMIO_BAR, val, true); |
| val = SETFIELD(VAS_ENABLE_RMA_MMIO_BAR, val, true); |
| |
| return vas_scom_write(chip, VAS_MISC_N_CTL, val); |
| } |
| |
| /* |
| * Ensure paste instructions are not accepted and MMIO BARs are disabled. |
| */ |
| static inline int reset_north_ctl(struct proc_chip *chip) |
| { |
| return vas_scom_write(chip, VAS_MISC_N_CTL, 0ULL); |
| } |
| |
| static void reset_fir(struct proc_chip *chip) |
| { |
| vas_scom_write(chip, VAS_FIR0, 0x0000000000000000ULL); |
| /* From VAS workbook */ |
| vas_scom_write(chip, VAS_FIR_MASK, 0x000001000001ffffULL); |
| vas_scom_write(chip, VAS_FIR_ACTION0, 0xf800fdfc0001ffffull); |
| vas_scom_write(chip, VAS_FIR_ACTION1, 0xf8fffefffffc8000ull); |
| } |
| |
| /* VAS workbook: Section 1.3.3.1: Send Message w/ Paste Commands (cl_rma_w) */ |
| /* P9 paste base address format */ |
| #define P9_RMA_LSMP_64K_SYS_ID PPC_BITMASK(8, 12) |
| #define P9_RMA_LSMP_64K_NODE_ID PPC_BITMASK(15, 18) |
| #define P9_RMA_LSMP_64K_CHIP_ID PPC_BITMASK(19, 21) |
| |
| /* Paste base address format (on P10 or later) */ |
| #define RMA_FOREIGN_ADDR_ENABLE PPC_BITMASK(8, 11) |
| #define RMA_TOPOLOGY_INDEX PPC_BITMASK(15, 19) |
| |
| #define RMA_LSMP_WINID_START_BIT 32 |
| #define RMA_LSMP_WINID_NUM_BITS 16 |
| |
| /* |
| * The start/base of the paste BAR is computed using the tables 1.1 through |
| * 1.4 in Section 1.3.3.1 (Send Message w/Paste Commands (cl_rma_w)) of VAS |
| * P9 Workbook. |
| * |
| * With 64K mode and Large SMP Mode the bits are used as follows: |
| * |
| * Bits Values Comments |
| * -------------------------------------- |
| * 0:7 0b 0000_0000 Reserved |
| * 8:12 0b 0000_1 System id/Foreign Index 0:4 |
| * 13:14 0b 00 Foreign Index 5:6 |
| * |
| * 15:18 0 throuh 15 Node id (0 through 15) |
| * 19:21 0 through 7 Chip id (0 throuh 7) |
| * 22:23 0b 00 Unused, Foreign index 7:8 |
| * |
| * 24:31 0b 0000_0000 RPN 0:7, Reserved |
| * 32:47 0 through 64K Send Window Id |
| * 48:51 0b 0000 Spare |
| * |
| * 52 0b 0 Reserved |
| * 53 0b 1 Report Enable (Set to 1 for NX). |
| * 54 0b 0 Reserved |
| * |
| * 55:56 0b 00 Snoop Bus |
| * 57:63 0b 0000_000 Reserved |
| * |
| * Except for a few bits, the small SMP mode computation is similar. |
| * |
| * TODO: Detect and compute address for small SMP mode. |
| * |
| * Example: For Node 0, Chip 0, Window id 4, Report Enable 1: |
| * |
| * Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7 |
| * 00000000 00001000 00000000 00000000 00000000 00000100 00000100 00000000 |
| * | || | | | | |
| * +-+-++++ +-------+-------+ v |
| * | | | Report Enable |
| * v v v |
| * Node Chip Window id 4 |
| * |
| * Thus the paste address for window id 4 is 0x00080000_00040400 and |
| * the _base_ paste address for Node 0 Chip 0 is 0x00080000_00000000. |
| */ |
| |
| static void p9_get_rma_bar(int chipid, uint64_t *val) |
| { |
| uint64_t v; |
| |
| v = 0ULL; |
| v = SETFIELD(P9_RMA_LSMP_64K_SYS_ID, v, 1); |
| v = SETFIELD(P9_RMA_LSMP_64K_NODE_ID, v, P9_GCID2NODEID(chipid)); |
| v = SETFIELD(P9_RMA_LSMP_64K_CHIP_ID, v, P9_GCID2CHIPID(chipid)); |
| |
| *val = v; |
| } |
| |
| /* |
| * The start/base of the paste BAR is computed using the tables 1.1 through |
| * 1.3 in Section 1.3.3.1 (Send Message w/Paste Commands (cl_rma_w)) of VAS |
| * P10 Workbook. |
| * |
| * With 64K mode and Large SMP Mode the bits are used as follows: |
| * |
| * Bits Values Comments |
| * -------------------------------------- |
| * 0:7 0b 0000_0000 Reserved |
| * 8:11 0b 0001 Foreign Address Enable |
| * 12 0b 0 SMF |
| * 13:14 0b 00 Memory Select |
| * |
| * 15:19 0 throuh 16 Topology Index |
| * 20:23 0b 0000 Chip Internal Address |
| * |
| * 24:31 0b 0000_0000 RPN 0:7, Reserved |
| * 32:47 0 through 64K Send Window Id |
| * 48:51 0b 0000 Spare |
| * |
| * 52 0b 0 Reserved |
| * 53 0b 1 Report Enable (Set to 1 for NX). |
| * 54 0b 0 Reserved |
| * |
| * 55:56 0b 00 Snoop Bus |
| * 57:63 0b 0000_000 Reserved |
| * |
| * Example: For Node 0, Chip 0, Window id 4, Report Enable 1: |
| * |
| * Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7 |
| * 00000000 00010000 00000000 00000000 00000000 00000100 00000100 00000000 |
| * | | | | | |
| * +---+ +-------+-------+ v |
| * | | Report Enable |
| * v v |
| * Topology Index Window id 4 |
| * |
| * Thus the paste address for window id 4 is 0x00100000_00040400 and |
| * the _base_ paste address for Node 0 Chip 0 is 0x00100000_00000000. |
| * |
| * Note: Bit 11 (Foreign Address Enable) is set only for paste base address. |
| * Not for VAS/NX RMA BAR. RA(0:12) = 0 for VAS/NX RMA BAR. |
| */ |
| |
| static void get_rma_bar(struct proc_chip *chip, uint64_t *val) |
| { |
| uint64_t v; |
| |
| v = 0ULL; |
| v = SETFIELD(RMA_TOPOLOGY_INDEX, v, chip->primary_topology); |
| |
| *val = v; |
| } |
| |
| /* Interface for NX - make sure VAS is fully initialized first */ |
| __attrconst uint64_t vas_get_rma_bar(int chipid) |
| { |
| struct proc_chip *chip; |
| uint64_t addr; |
| |
| if (!vas_initialized) |
| return 0ULL; |
| |
| chip = get_chip(chipid); |
| if (!chip) |
| return 0ULL; |
| |
| get_rma_bar(chip, &addr); |
| |
| return addr; |
| } |
| |
| /* |
| * Initialize RMA BAR on this chip to correspond to its node/chip id. |
| * This will cause VAS to accept paste commands to targeted for this chip. |
| * Initialize RMA Base Address Mask Register (BAMR) to its default value. |
| */ |
| static int init_rma(struct proc_chip *chip) |
| { |
| int rc; |
| uint64_t val; |
| |
| if (proc_gen == proc_gen_p9) |
| p9_get_rma_bar(chip->id, &val); |
| else |
| get_rma_bar(chip, &val); |
| |
| rc = vas_scom_write(chip, VAS_RMA_BAR, val); |
| if (rc) |
| return rc; |
| |
| val = SETFIELD(VAS_RMA_BAMR_ADDR_MASK, 0ULL, 0xFFFC0000000ULL); |
| |
| return vas_scom_write(chip, VAS_RMA_BAMR, val); |
| } |
| |
| /* |
| * get_paste_bar(): |
| * |
| * Compute and return the "paste base address region" for @chipid. This |
| * BAR contains the "paste" addreses for all windows on the chip. Linux |
| * uses this paste BAR to compute the hardware paste address of a (send) |
| * window using: |
| * |
| * paste_addr = base + (winid << shift) |
| * |
| * where winid is the window index and shift is computed as: |
| * |
| * start = RMA_LSMP_WINID_START_BIT; |
| * nbits = RMA_LSMP_WINID_NUM_BITS; |
| * shift = 63 - (start + nbits - 1); |
| * |
| * See also get_paste_bitfield() below, which is used to export the 'start' |
| * and 'nbits' to Linux through the DT. |
| * |
| * Each chip supports VAS_WINDOWS_PER_CHIP (64K on Power9) windows. To |
| * provide proper isolation, the paste address for each window is on a |
| * separate page. Thus with a page size of 64K, the length of the paste |
| * BAR for a chip is VAS_WINDOWS_PER_CHIP times 64K (or 4GB for Power9). |
| * |
| */ |
| #define VAS_PASTE_BAR_LEN (1ULL << 32) /* 4GB - see above */ |
| |
| static inline void get_paste_bar(int chipid, uint64_t *start, uint64_t *len) |
| { |
| struct proc_chip *chip; |
| uint64_t val; |
| |
| if (proc_gen == proc_gen_p9) |
| p9_get_rma_bar(chipid, &val); |
| else { |
| chip = get_chip(chipid); |
| if (!chip) |
| return; |
| |
| get_rma_bar(chip, &val); |
| |
| /* |
| * RA(11) (Foreign Address Enable) is set only for paste |
| * base address. |
| */ |
| val = SETFIELD(RMA_FOREIGN_ADDR_ENABLE, val, 1); |
| } |
| |
| *start = val; |
| *len = VAS_PASTE_BAR_LEN; |
| } |
| |
| /* |
| * get_paste_bitfield(): |
| * |
| * As explained in the function header for get_paste_bar(), the window |
| * id is encoded in bits 32:47 of the paste address. Export this bitfield |
| * to Linux via the device tree as a reg property (with start bit and |
| * number of bits). |
| */ |
| static inline void get_paste_bitfield(uint64_t *start, uint64_t *n_bits) |
| { |
| *start = (uint64_t)RMA_LSMP_WINID_START_BIT; |
| *n_bits = (uint64_t)RMA_LSMP_WINID_NUM_BITS; |
| } |
| |
| /* |
| * Window Context MMIO (WCM) Region for each chip is assigned in the P9 |
| * MMIO MAP spreadsheet. Write this value to the SCOM address associated |
| * with WCM_BAR. |
| */ |
| static int init_wcm(struct proc_chip *chip) |
| { |
| uint64_t wcmbar; |
| |
| get_hvwc_mmio_bar(chip->id, &wcmbar, NULL); |
| |
| /* |
| * Write the entire WCMBAR address to the SCOM address. VAS will |
| * extract bits that it thinks are relevant i.e bits 8..38 |
| */ |
| return vas_scom_write(chip, VAS_WCM_BAR, wcmbar); |
| } |
| |
| /* |
| * OS/User Window Context MMIO (UWCM) Region for each is assigned in the |
| * P9 MMIO MAP spreadsheet. Write this value to the SCOM address associated |
| * with UWCM_BAR. |
| */ |
| static int init_uwcm(struct proc_chip *chip) |
| { |
| uint64_t uwcmbar; |
| |
| get_uwc_mmio_bar(chip->id, &uwcmbar, NULL); |
| |
| /* |
| * Write the entire UWCMBAR address to the SCOM address. VAS will |
| * extract bits that it thinks are relevant i.e bits 8..35. |
| */ |
| return vas_scom_write(chip, VAS_UWCM_BAR, uwcmbar); |
| } |
| |
| static inline void free_wcbs(struct proc_chip *chip) |
| { |
| if (chip->vas->wcbs) { |
| local_free((void *)chip->vas->wcbs); |
| chip->vas->wcbs = 0ULL; |
| } |
| } |
| |
| /* |
| * VAS needs a backing store for the 64K window contexts on a chip. |
| * (64K times 512 = 8MB). This region needs to be contiguous, so |
| * allocate during early boot. Then write the allocated address to |
| * the SCOM address for the Backing store BAR. |
| */ |
| static int alloc_init_wcbs(struct proc_chip *chip) |
| { |
| int rc; |
| uint64_t wcbs; |
| size_t size; |
| |
| /* align to the backing store size */ |
| size = (size_t)VAS_WCBS_SIZE; |
| wcbs = (uint64_t)local_alloc(chip->id, size, size); |
| if (!wcbs) { |
| vas_err("Unable to allocate memory for backing store\n"); |
| return -ENOMEM; |
| } |
| memset((void *)wcbs, 0ULL, size); |
| |
| /* |
| * Write entire WCBS_BAR address to the SCOM address. VAS will extract |
| * relevant bits. |
| */ |
| rc = vas_scom_write(chip, VAS_WCBS_BAR, wcbs); |
| if (rc != OPAL_SUCCESS) |
| goto out; |
| |
| chip->vas->wcbs = wcbs; |
| return OPAL_SUCCESS; |
| |
| out: |
| local_free((void *)wcbs); |
| return rc; |
| } |
| |
| static struct vas *alloc_vas(uint32_t chip_id, uint32_t vas_id, uint64_t base) |
| { |
| struct vas *vas; |
| |
| vas = zalloc(sizeof(struct vas)); |
| assert(vas); |
| |
| vas->chip_id = chip_id; |
| vas->vas_id = vas_id; |
| vas->xscom_base = base; |
| |
| return vas; |
| } |
| |
| static void create_mm_dt_node(struct proc_chip *chip) |
| { |
| struct dt_node *dn; |
| struct vas *vas; |
| const char *compat; |
| uint64_t hvwc_start, hvwc_len; |
| uint64_t uwc_start, uwc_len; |
| uint64_t pbf_start, pbf_nbits; |
| uint64_t pbar_start = 0, pbar_len = 0; |
| |
| vas = chip->vas; |
| get_hvwc_mmio_bar(chip->id, &hvwc_start, &hvwc_len); |
| get_uwc_mmio_bar(chip->id, &uwc_start, &uwc_len); |
| get_paste_bar(chip->id, &pbar_start, &pbar_len); |
| get_paste_bitfield(&pbf_start, &pbf_nbits); |
| |
| if (proc_gen == proc_gen_p9) |
| compat = "ibm,power9-vas"; |
| else |
| compat = "ibm,power10-vas"; |
| |
| dn = dt_new_addr(dt_root, "vas", hvwc_start); |
| |
| dt_add_property_strings(dn, "compatible", compat, |
| "ibm,vas"); |
| |
| dt_add_property_u64s(dn, "reg", hvwc_start, hvwc_len, |
| uwc_start, uwc_len, |
| pbar_start, pbar_len, |
| pbf_start, pbf_nbits); |
| |
| dt_add_property_cells(dn, "ibm,vas-id", vas->vas_id); |
| dt_add_property_cells(dn, "ibm,chip-id", chip->id); |
| if (vas->vas_irq) { |
| dt_add_property_cells(dn, "interrupts", vas->vas_irq, 0); |
| dt_add_property_cells(dn, "interrupt-parent", |
| get_ics_phandle()); |
| dt_add_property_u64(dn, "ibm,vas-port", vas->vas_port); |
| } |
| } |
| |
| /* |
| * Disable one VAS instance. |
| * |
| * Free memory and ensure chip does not accept paste instructions. |
| */ |
| static void disable_vas_inst(struct dt_node *np) |
| { |
| struct proc_chip *chip; |
| |
| chip = get_chip(dt_get_chip_id(np)); |
| |
| if (!chip->vas) |
| return; |
| |
| free_wcbs(chip); |
| |
| reset_north_ctl(chip); |
| } |
| |
| static void vas_setup_irq(struct proc_chip *chip) |
| { |
| uint64_t port; |
| uint32_t irq; |
| |
| irq = xive_alloc_ipi_irqs(chip->id, 1, 64); |
| if (irq == XIVE_IRQ_ERROR) { |
| vas_err("Failed to allocate interrupt sources for chipID %d\n", |
| chip->id); |
| return; |
| } |
| |
| vas_vdbg("trigger port: 0x%p\n", xive_get_trigger_port(irq)); |
| |
| port = (uint64_t)xive_get_trigger_port(irq); |
| |
| chip->vas->vas_irq = irq; |
| chip->vas->vas_port = port; |
| } |
| |
| /* |
| * Initialize one VAS instance and enable it if @enable is true. |
| */ |
| static int init_vas_inst(struct dt_node *np, bool enable) |
| { |
| uint32_t vas_id; |
| uint64_t xscom_base; |
| struct proc_chip *chip; |
| |
| chip = get_chip(dt_get_chip_id(np)); |
| vas_id = dt_prop_get_u32(np, "ibm,vas-id"); |
| xscom_base = dt_get_address(np, 0, NULL); |
| |
| chip->vas = alloc_vas(chip->id, vas_id, xscom_base); |
| |
| if (!enable) { |
| reset_north_ctl(chip); |
| return 0; |
| } |
| |
| if (alloc_init_wcbs(chip)) |
| return -1; |
| |
| reset_fir(chip); |
| |
| if (init_wcm(chip) || init_uwcm(chip) || init_north_ctl(chip) || |
| init_rma(chip)) |
| return -1; |
| |
| /* |
| * Use NVRAM 'vas-user-space' config for backward compatibility |
| * to older kernels. Remove this option in future if not needed. |
| */ |
| if (nvram_query_eq_dangerous("vas-user-space", "enable")) |
| vas_setup_irq(chip); |
| |
| create_mm_dt_node(chip); |
| |
| prlog(PR_INFO, "VAS: Initialized chip %d\n", chip->id); |
| return 0; |
| |
| } |
| |
| void vas_init(void) |
| { |
| bool enabled; |
| struct dt_node *np; |
| const char *compat; |
| |
| if (proc_gen == proc_gen_p9) |
| compat = "ibm,power9-vas-x"; |
| else if (proc_gen == proc_gen_p10) |
| compat = "ibm,power10-vas-x"; |
| else |
| return; |
| |
| enabled = vas_nx_enabled(); |
| |
| dt_for_each_compatible(dt_root, np, compat) { |
| if (init_vas_inst(np, enabled)) |
| goto out; |
| } |
| |
| vas_initialized = enabled; |
| return; |
| |
| out: |
| dt_for_each_compatible(dt_root, np, compat) |
| disable_vas_inst(np); |
| |
| vas_err("Disabled (failed initialization)\n"); |
| return; |
| } |
| |
| DEFINE_HWPROBE(vas, vas_init); |