1*3ced8d73SChristophe Lombard /* 2*3ced8d73SChristophe Lombard * Copyright 2017 IBM Corp. 3*3ced8d73SChristophe Lombard * 4*3ced8d73SChristophe Lombard * This program is free software; you can redistribute it and/or 5*3ced8d73SChristophe Lombard * modify it under the terms of the GNU General Public License 6*3ced8d73SChristophe Lombard * as published by the Free Software Foundation; either version 7*3ced8d73SChristophe Lombard * 2 of the License, or (at your option) any later version. 8*3ced8d73SChristophe Lombard */ 9*3ced8d73SChristophe Lombard 10*3ced8d73SChristophe Lombard #include <linux/hugetlb.h> 11*3ced8d73SChristophe Lombard #include <linux/sched/mm.h> 12*3ced8d73SChristophe Lombard #include <asm/pnv-pci.h> 13*3ced8d73SChristophe Lombard #include <misc/cxllib.h> 14*3ced8d73SChristophe Lombard 15*3ced8d73SChristophe Lombard #include "cxl.h" 16*3ced8d73SChristophe Lombard 17*3ced8d73SChristophe Lombard #define CXL_INVALID_DRA ~0ull 18*3ced8d73SChristophe Lombard #define CXL_DUMMY_READ_SIZE 128 19*3ced8d73SChristophe Lombard #define CXL_DUMMY_READ_ALIGN 8 20*3ced8d73SChristophe Lombard #define CXL_CAPI_WINDOW_START 0x2000000000000ull 21*3ced8d73SChristophe Lombard #define CXL_CAPI_WINDOW_LOG_SIZE 48 22*3ced8d73SChristophe Lombard #define CXL_XSL_CONFIG_CURRENT_VERSION CXL_XSL_CONFIG_VERSION1 23*3ced8d73SChristophe Lombard 24*3ced8d73SChristophe Lombard 25*3ced8d73SChristophe Lombard bool cxllib_slot_is_supported(struct pci_dev *dev, unsigned long flags) 26*3ced8d73SChristophe Lombard { 27*3ced8d73SChristophe Lombard int rc; 28*3ced8d73SChristophe Lombard u32 phb_index; 29*3ced8d73SChristophe Lombard u64 chip_id, capp_unit_id; 30*3ced8d73SChristophe Lombard 31*3ced8d73SChristophe Lombard /* No flags currently supported */ 32*3ced8d73SChristophe Lombard if (flags) 33*3ced8d73SChristophe Lombard return false; 34*3ced8d73SChristophe Lombard 35*3ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 36*3ced8d73SChristophe Lombard return false; 37*3ced8d73SChristophe Lombard 38*3ced8d73SChristophe Lombard if (!cxl_is_power9()) 39*3ced8d73SChristophe Lombard return false; 40*3ced8d73SChristophe Lombard 41*3ced8d73SChristophe Lombard if (cxl_slot_is_switched(dev)) 42*3ced8d73SChristophe Lombard return false; 43*3ced8d73SChristophe Lombard 44*3ced8d73SChristophe Lombard /* on p9, some pci slots are not connected to a CAPP unit */ 45*3ced8d73SChristophe Lombard rc = cxl_calc_capp_routing(dev, &chip_id, &phb_index, &capp_unit_id); 46*3ced8d73SChristophe Lombard if (rc) 47*3ced8d73SChristophe Lombard return false; 48*3ced8d73SChristophe Lombard 49*3ced8d73SChristophe Lombard return true; 50*3ced8d73SChristophe Lombard } 51*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_slot_is_supported); 52*3ced8d73SChristophe Lombard 53*3ced8d73SChristophe Lombard static DEFINE_MUTEX(dra_mutex); 54*3ced8d73SChristophe Lombard static u64 dummy_read_addr = CXL_INVALID_DRA; 55*3ced8d73SChristophe Lombard 56*3ced8d73SChristophe Lombard static int allocate_dummy_read_buf(void) 57*3ced8d73SChristophe Lombard { 58*3ced8d73SChristophe Lombard u64 buf, vaddr; 59*3ced8d73SChristophe Lombard size_t buf_size; 60*3ced8d73SChristophe Lombard 61*3ced8d73SChristophe Lombard /* 62*3ced8d73SChristophe Lombard * Dummy read buffer is 128-byte long, aligned on a 63*3ced8d73SChristophe Lombard * 256-byte boundary and we need the physical address. 64*3ced8d73SChristophe Lombard */ 65*3ced8d73SChristophe Lombard buf_size = CXL_DUMMY_READ_SIZE + (1ull << CXL_DUMMY_READ_ALIGN); 66*3ced8d73SChristophe Lombard buf = (u64) kzalloc(buf_size, GFP_KERNEL); 67*3ced8d73SChristophe Lombard if (!buf) 68*3ced8d73SChristophe Lombard return -ENOMEM; 69*3ced8d73SChristophe Lombard 70*3ced8d73SChristophe Lombard vaddr = (buf + (1ull << CXL_DUMMY_READ_ALIGN) - 1) & 71*3ced8d73SChristophe Lombard (~0ull << CXL_DUMMY_READ_ALIGN); 72*3ced8d73SChristophe Lombard 73*3ced8d73SChristophe Lombard WARN((vaddr + CXL_DUMMY_READ_SIZE) > (buf + buf_size), 74*3ced8d73SChristophe Lombard "Dummy read buffer alignment issue"); 75*3ced8d73SChristophe Lombard dummy_read_addr = virt_to_phys((void *) vaddr); 76*3ced8d73SChristophe Lombard return 0; 77*3ced8d73SChristophe Lombard } 78*3ced8d73SChristophe Lombard 79*3ced8d73SChristophe Lombard int cxllib_get_xsl_config(struct pci_dev *dev, struct cxllib_xsl_config *cfg) 80*3ced8d73SChristophe Lombard { 81*3ced8d73SChristophe Lombard int rc; 82*3ced8d73SChristophe Lombard u32 phb_index; 83*3ced8d73SChristophe Lombard u64 chip_id, capp_unit_id; 84*3ced8d73SChristophe Lombard 85*3ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 86*3ced8d73SChristophe Lombard return -EINVAL; 87*3ced8d73SChristophe Lombard 88*3ced8d73SChristophe Lombard mutex_lock(&dra_mutex); 89*3ced8d73SChristophe Lombard if (dummy_read_addr == CXL_INVALID_DRA) { 90*3ced8d73SChristophe Lombard rc = allocate_dummy_read_buf(); 91*3ced8d73SChristophe Lombard if (rc) { 92*3ced8d73SChristophe Lombard mutex_unlock(&dra_mutex); 93*3ced8d73SChristophe Lombard return rc; 94*3ced8d73SChristophe Lombard } 95*3ced8d73SChristophe Lombard } 96*3ced8d73SChristophe Lombard mutex_unlock(&dra_mutex); 97*3ced8d73SChristophe Lombard 98*3ced8d73SChristophe Lombard rc = cxl_calc_capp_routing(dev, &chip_id, &phb_index, &capp_unit_id); 99*3ced8d73SChristophe Lombard if (rc) 100*3ced8d73SChristophe Lombard return rc; 101*3ced8d73SChristophe Lombard 102*3ced8d73SChristophe Lombard rc = cxl_get_xsl9_dsnctl(capp_unit_id, &cfg->dsnctl); 103*3ced8d73SChristophe Lombard if (rc) 104*3ced8d73SChristophe Lombard return rc; 105*3ced8d73SChristophe Lombard if (cpu_has_feature(CPU_FTR_POWER9_DD1)) { 106*3ced8d73SChristophe Lombard /* workaround for DD1 - nbwind = capiind */ 107*3ced8d73SChristophe Lombard cfg->dsnctl |= ((u64)0x02 << (63-47)); 108*3ced8d73SChristophe Lombard } 109*3ced8d73SChristophe Lombard 110*3ced8d73SChristophe Lombard cfg->version = CXL_XSL_CONFIG_CURRENT_VERSION; 111*3ced8d73SChristophe Lombard cfg->log_bar_size = CXL_CAPI_WINDOW_LOG_SIZE; 112*3ced8d73SChristophe Lombard cfg->bar_addr = CXL_CAPI_WINDOW_START; 113*3ced8d73SChristophe Lombard cfg->dra = dummy_read_addr; 114*3ced8d73SChristophe Lombard return 0; 115*3ced8d73SChristophe Lombard } 116*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_get_xsl_config); 117*3ced8d73SChristophe Lombard 118*3ced8d73SChristophe Lombard int cxllib_switch_phb_mode(struct pci_dev *dev, enum cxllib_mode mode, 119*3ced8d73SChristophe Lombard unsigned long flags) 120*3ced8d73SChristophe Lombard { 121*3ced8d73SChristophe Lombard int rc = 0; 122*3ced8d73SChristophe Lombard 123*3ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 124*3ced8d73SChristophe Lombard return -EINVAL; 125*3ced8d73SChristophe Lombard 126*3ced8d73SChristophe Lombard switch (mode) { 127*3ced8d73SChristophe Lombard case CXL_MODE_PCI: 128*3ced8d73SChristophe Lombard /* 129*3ced8d73SChristophe Lombard * We currently don't support going back to PCI mode 130*3ced8d73SChristophe Lombard * However, we'll turn the invalidations off, so that 131*3ced8d73SChristophe Lombard * the firmware doesn't have to ack them and can do 132*3ced8d73SChristophe Lombard * things like reset, etc.. with no worries. 133*3ced8d73SChristophe Lombard * So always return EPERM (can't go back to PCI) or 134*3ced8d73SChristophe Lombard * EBUSY if we couldn't even turn off snooping 135*3ced8d73SChristophe Lombard */ 136*3ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_OFF); 137*3ced8d73SChristophe Lombard if (rc) 138*3ced8d73SChristophe Lombard rc = -EBUSY; 139*3ced8d73SChristophe Lombard else 140*3ced8d73SChristophe Lombard rc = -EPERM; 141*3ced8d73SChristophe Lombard break; 142*3ced8d73SChristophe Lombard case CXL_MODE_CXL: 143*3ced8d73SChristophe Lombard /* DMA only supported on TVT1 for the time being */ 144*3ced8d73SChristophe Lombard if (flags != CXL_MODE_DMA_TVT1) 145*3ced8d73SChristophe Lombard return -EINVAL; 146*3ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_DMA_TVT1); 147*3ced8d73SChristophe Lombard if (rc) 148*3ced8d73SChristophe Lombard return rc; 149*3ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON); 150*3ced8d73SChristophe Lombard break; 151*3ced8d73SChristophe Lombard default: 152*3ced8d73SChristophe Lombard rc = -EINVAL; 153*3ced8d73SChristophe Lombard } 154*3ced8d73SChristophe Lombard return rc; 155*3ced8d73SChristophe Lombard } 156*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_switch_phb_mode); 157*3ced8d73SChristophe Lombard 158*3ced8d73SChristophe Lombard /* 159*3ced8d73SChristophe Lombard * When switching the PHB to capi mode, the TVT#1 entry for 160*3ced8d73SChristophe Lombard * the Partitionable Endpoint is set in bypass mode, like 161*3ced8d73SChristophe Lombard * in PCI mode. 162*3ced8d73SChristophe Lombard * Configure the device dma to use TVT#1, which is done 163*3ced8d73SChristophe Lombard * by calling dma_set_mask() with a mask large enough. 164*3ced8d73SChristophe Lombard */ 165*3ced8d73SChristophe Lombard int cxllib_set_device_dma(struct pci_dev *dev, unsigned long flags) 166*3ced8d73SChristophe Lombard { 167*3ced8d73SChristophe Lombard int rc; 168*3ced8d73SChristophe Lombard 169*3ced8d73SChristophe Lombard if (flags) 170*3ced8d73SChristophe Lombard return -EINVAL; 171*3ced8d73SChristophe Lombard 172*3ced8d73SChristophe Lombard rc = dma_set_mask(&dev->dev, DMA_BIT_MASK(64)); 173*3ced8d73SChristophe Lombard return rc; 174*3ced8d73SChristophe Lombard } 175*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_set_device_dma); 176*3ced8d73SChristophe Lombard 177*3ced8d73SChristophe Lombard int cxllib_get_PE_attributes(struct task_struct *task, 178*3ced8d73SChristophe Lombard unsigned long translation_mode, 179*3ced8d73SChristophe Lombard struct cxllib_pe_attributes *attr) 180*3ced8d73SChristophe Lombard { 181*3ced8d73SChristophe Lombard struct mm_struct *mm = NULL; 182*3ced8d73SChristophe Lombard 183*3ced8d73SChristophe Lombard if (translation_mode != CXL_TRANSLATED_MODE && 184*3ced8d73SChristophe Lombard translation_mode != CXL_REAL_MODE) 185*3ced8d73SChristophe Lombard return -EINVAL; 186*3ced8d73SChristophe Lombard 187*3ced8d73SChristophe Lombard attr->sr = cxl_calculate_sr(false, 188*3ced8d73SChristophe Lombard task == NULL, 189*3ced8d73SChristophe Lombard translation_mode == CXL_REAL_MODE, 190*3ced8d73SChristophe Lombard true); 191*3ced8d73SChristophe Lombard attr->lpid = mfspr(SPRN_LPID); 192*3ced8d73SChristophe Lombard if (task) { 193*3ced8d73SChristophe Lombard mm = get_task_mm(task); 194*3ced8d73SChristophe Lombard if (mm == NULL) 195*3ced8d73SChristophe Lombard return -EINVAL; 196*3ced8d73SChristophe Lombard /* 197*3ced8d73SChristophe Lombard * Caller is keeping a reference on mm_users for as long 198*3ced8d73SChristophe Lombard * as XSL uses the memory context 199*3ced8d73SChristophe Lombard */ 200*3ced8d73SChristophe Lombard attr->pid = mm->context.id; 201*3ced8d73SChristophe Lombard mmput(mm); 202*3ced8d73SChristophe Lombard } else { 203*3ced8d73SChristophe Lombard attr->pid = 0; 204*3ced8d73SChristophe Lombard } 205*3ced8d73SChristophe Lombard attr->tid = 0; 206*3ced8d73SChristophe Lombard return 0; 207*3ced8d73SChristophe Lombard } 208*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_get_PE_attributes); 209*3ced8d73SChristophe Lombard 210*3ced8d73SChristophe Lombard int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags) 211*3ced8d73SChristophe Lombard { 212*3ced8d73SChristophe Lombard int rc; 213*3ced8d73SChristophe Lombard u64 dar; 214*3ced8d73SChristophe Lombard struct vm_area_struct *vma = NULL; 215*3ced8d73SChristophe Lombard unsigned long page_size; 216*3ced8d73SChristophe Lombard 217*3ced8d73SChristophe Lombard if (mm == NULL) 218*3ced8d73SChristophe Lombard return -EFAULT; 219*3ced8d73SChristophe Lombard 220*3ced8d73SChristophe Lombard down_read(&mm->mmap_sem); 221*3ced8d73SChristophe Lombard 222*3ced8d73SChristophe Lombard for (dar = addr; dar < addr + size; dar += page_size) { 223*3ced8d73SChristophe Lombard if (!vma || dar < vma->vm_start || dar > vma->vm_end) { 224*3ced8d73SChristophe Lombard vma = find_vma(mm, addr); 225*3ced8d73SChristophe Lombard if (!vma) { 226*3ced8d73SChristophe Lombard pr_err("Can't find vma for addr %016llx\n", addr); 227*3ced8d73SChristophe Lombard rc = -EFAULT; 228*3ced8d73SChristophe Lombard goto out; 229*3ced8d73SChristophe Lombard } 230*3ced8d73SChristophe Lombard /* get the size of the pages allocated */ 231*3ced8d73SChristophe Lombard page_size = vma_kernel_pagesize(vma); 232*3ced8d73SChristophe Lombard } 233*3ced8d73SChristophe Lombard 234*3ced8d73SChristophe Lombard rc = cxl_handle_mm_fault(mm, flags, dar); 235*3ced8d73SChristophe Lombard if (rc) { 236*3ced8d73SChristophe Lombard pr_err("cxl_handle_mm_fault failed %d", rc); 237*3ced8d73SChristophe Lombard rc = -EFAULT; 238*3ced8d73SChristophe Lombard goto out; 239*3ced8d73SChristophe Lombard } 240*3ced8d73SChristophe Lombard } 241*3ced8d73SChristophe Lombard rc = 0; 242*3ced8d73SChristophe Lombard out: 243*3ced8d73SChristophe Lombard up_read(&mm->mmap_sem); 244*3ced8d73SChristophe Lombard return rc; 245*3ced8d73SChristophe Lombard } 246*3ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_handle_fault); 247