13ced8d73SChristophe Lombard /* 23ced8d73SChristophe Lombard * Copyright 2017 IBM Corp. 33ced8d73SChristophe Lombard * 43ced8d73SChristophe Lombard * This program is free software; you can redistribute it and/or 53ced8d73SChristophe Lombard * modify it under the terms of the GNU General Public License 63ced8d73SChristophe Lombard * as published by the Free Software Foundation; either version 73ced8d73SChristophe Lombard * 2 of the License, or (at your option) any later version. 83ced8d73SChristophe Lombard */ 93ced8d73SChristophe Lombard 103ced8d73SChristophe Lombard #include <linux/hugetlb.h> 113ced8d73SChristophe Lombard #include <linux/sched/mm.h> 123ced8d73SChristophe Lombard #include <asm/pnv-pci.h> 133ced8d73SChristophe Lombard #include <misc/cxllib.h> 143ced8d73SChristophe Lombard 153ced8d73SChristophe Lombard #include "cxl.h" 163ced8d73SChristophe Lombard 173ced8d73SChristophe Lombard #define CXL_INVALID_DRA ~0ull 183ced8d73SChristophe Lombard #define CXL_DUMMY_READ_SIZE 128 193ced8d73SChristophe Lombard #define CXL_DUMMY_READ_ALIGN 8 203ced8d73SChristophe Lombard #define CXL_CAPI_WINDOW_START 0x2000000000000ull 213ced8d73SChristophe Lombard #define CXL_CAPI_WINDOW_LOG_SIZE 48 223ced8d73SChristophe Lombard #define CXL_XSL_CONFIG_CURRENT_VERSION CXL_XSL_CONFIG_VERSION1 233ced8d73SChristophe Lombard 243ced8d73SChristophe Lombard 253ced8d73SChristophe Lombard bool cxllib_slot_is_supported(struct pci_dev *dev, unsigned long flags) 263ced8d73SChristophe Lombard { 273ced8d73SChristophe Lombard int rc; 283ced8d73SChristophe Lombard u32 phb_index; 293ced8d73SChristophe Lombard u64 chip_id, capp_unit_id; 303ced8d73SChristophe Lombard 313ced8d73SChristophe Lombard /* No flags currently supported */ 323ced8d73SChristophe Lombard if (flags) 333ced8d73SChristophe Lombard return false; 343ced8d73SChristophe Lombard 353ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 363ced8d73SChristophe Lombard return false; 373ced8d73SChristophe Lombard 383ced8d73SChristophe Lombard if (!cxl_is_power9()) 393ced8d73SChristophe Lombard return false; 403ced8d73SChristophe Lombard 413ced8d73SChristophe Lombard if (cxl_slot_is_switched(dev)) 423ced8d73SChristophe Lombard return false; 433ced8d73SChristophe Lombard 443ced8d73SChristophe Lombard /* on p9, some pci slots are not connected to a CAPP unit */ 453ced8d73SChristophe Lombard rc = cxl_calc_capp_routing(dev, &chip_id, &phb_index, &capp_unit_id); 463ced8d73SChristophe Lombard if (rc) 473ced8d73SChristophe Lombard return false; 483ced8d73SChristophe Lombard 493ced8d73SChristophe Lombard return true; 503ced8d73SChristophe Lombard } 513ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_slot_is_supported); 523ced8d73SChristophe Lombard 533ced8d73SChristophe Lombard static DEFINE_MUTEX(dra_mutex); 543ced8d73SChristophe Lombard static u64 dummy_read_addr = CXL_INVALID_DRA; 553ced8d73SChristophe Lombard 563ced8d73SChristophe Lombard static int allocate_dummy_read_buf(void) 573ced8d73SChristophe Lombard { 583ced8d73SChristophe Lombard u64 buf, vaddr; 593ced8d73SChristophe Lombard size_t buf_size; 603ced8d73SChristophe Lombard 613ced8d73SChristophe Lombard /* 623ced8d73SChristophe Lombard * Dummy read buffer is 128-byte long, aligned on a 633ced8d73SChristophe Lombard * 256-byte boundary and we need the physical address. 643ced8d73SChristophe Lombard */ 653ced8d73SChristophe Lombard buf_size = CXL_DUMMY_READ_SIZE + (1ull << CXL_DUMMY_READ_ALIGN); 663ced8d73SChristophe Lombard buf = (u64) kzalloc(buf_size, GFP_KERNEL); 673ced8d73SChristophe Lombard if (!buf) 683ced8d73SChristophe Lombard return -ENOMEM; 693ced8d73SChristophe Lombard 703ced8d73SChristophe Lombard vaddr = (buf + (1ull << CXL_DUMMY_READ_ALIGN) - 1) & 713ced8d73SChristophe Lombard (~0ull << CXL_DUMMY_READ_ALIGN); 723ced8d73SChristophe Lombard 733ced8d73SChristophe Lombard WARN((vaddr + CXL_DUMMY_READ_SIZE) > (buf + buf_size), 743ced8d73SChristophe Lombard "Dummy read buffer alignment issue"); 753ced8d73SChristophe Lombard dummy_read_addr = virt_to_phys((void *) vaddr); 763ced8d73SChristophe Lombard return 0; 773ced8d73SChristophe Lombard } 783ced8d73SChristophe Lombard 793ced8d73SChristophe Lombard int cxllib_get_xsl_config(struct pci_dev *dev, struct cxllib_xsl_config *cfg) 803ced8d73SChristophe Lombard { 813ced8d73SChristophe Lombard int rc; 823ced8d73SChristophe Lombard u32 phb_index; 833ced8d73SChristophe Lombard u64 chip_id, capp_unit_id; 843ced8d73SChristophe Lombard 853ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 863ced8d73SChristophe Lombard return -EINVAL; 873ced8d73SChristophe Lombard 883ced8d73SChristophe Lombard mutex_lock(&dra_mutex); 893ced8d73SChristophe Lombard if (dummy_read_addr == CXL_INVALID_DRA) { 903ced8d73SChristophe Lombard rc = allocate_dummy_read_buf(); 913ced8d73SChristophe Lombard if (rc) { 923ced8d73SChristophe Lombard mutex_unlock(&dra_mutex); 933ced8d73SChristophe Lombard return rc; 943ced8d73SChristophe Lombard } 953ced8d73SChristophe Lombard } 963ced8d73SChristophe Lombard mutex_unlock(&dra_mutex); 973ced8d73SChristophe Lombard 983ced8d73SChristophe Lombard rc = cxl_calc_capp_routing(dev, &chip_id, &phb_index, &capp_unit_id); 993ced8d73SChristophe Lombard if (rc) 1003ced8d73SChristophe Lombard return rc; 1013ced8d73SChristophe Lombard 1023ced8d73SChristophe Lombard rc = cxl_get_xsl9_dsnctl(capp_unit_id, &cfg->dsnctl); 1033ced8d73SChristophe Lombard if (rc) 1043ced8d73SChristophe Lombard return rc; 1053ced8d73SChristophe Lombard if (cpu_has_feature(CPU_FTR_POWER9_DD1)) { 1063ced8d73SChristophe Lombard /* workaround for DD1 - nbwind = capiind */ 1073ced8d73SChristophe Lombard cfg->dsnctl |= ((u64)0x02 << (63-47)); 1083ced8d73SChristophe Lombard } 1093ced8d73SChristophe Lombard 1103ced8d73SChristophe Lombard cfg->version = CXL_XSL_CONFIG_CURRENT_VERSION; 1113ced8d73SChristophe Lombard cfg->log_bar_size = CXL_CAPI_WINDOW_LOG_SIZE; 1123ced8d73SChristophe Lombard cfg->bar_addr = CXL_CAPI_WINDOW_START; 1133ced8d73SChristophe Lombard cfg->dra = dummy_read_addr; 1143ced8d73SChristophe Lombard return 0; 1153ced8d73SChristophe Lombard } 1163ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_get_xsl_config); 1173ced8d73SChristophe Lombard 1183ced8d73SChristophe Lombard int cxllib_switch_phb_mode(struct pci_dev *dev, enum cxllib_mode mode, 1193ced8d73SChristophe Lombard unsigned long flags) 1203ced8d73SChristophe Lombard { 1213ced8d73SChristophe Lombard int rc = 0; 1223ced8d73SChristophe Lombard 1233ced8d73SChristophe Lombard if (!cpu_has_feature(CPU_FTR_HVMODE)) 1243ced8d73SChristophe Lombard return -EINVAL; 1253ced8d73SChristophe Lombard 1263ced8d73SChristophe Lombard switch (mode) { 1273ced8d73SChristophe Lombard case CXL_MODE_PCI: 1283ced8d73SChristophe Lombard /* 1293ced8d73SChristophe Lombard * We currently don't support going back to PCI mode 1303ced8d73SChristophe Lombard * However, we'll turn the invalidations off, so that 1313ced8d73SChristophe Lombard * the firmware doesn't have to ack them and can do 1323ced8d73SChristophe Lombard * things like reset, etc.. with no worries. 1333ced8d73SChristophe Lombard * So always return EPERM (can't go back to PCI) or 1343ced8d73SChristophe Lombard * EBUSY if we couldn't even turn off snooping 1353ced8d73SChristophe Lombard */ 1363ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_OFF); 1373ced8d73SChristophe Lombard if (rc) 1383ced8d73SChristophe Lombard rc = -EBUSY; 1393ced8d73SChristophe Lombard else 1403ced8d73SChristophe Lombard rc = -EPERM; 1413ced8d73SChristophe Lombard break; 1423ced8d73SChristophe Lombard case CXL_MODE_CXL: 1433ced8d73SChristophe Lombard /* DMA only supported on TVT1 for the time being */ 1443ced8d73SChristophe Lombard if (flags != CXL_MODE_DMA_TVT1) 1453ced8d73SChristophe Lombard return -EINVAL; 1463ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_DMA_TVT1); 1473ced8d73SChristophe Lombard if (rc) 1483ced8d73SChristophe Lombard return rc; 1493ced8d73SChristophe Lombard rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON); 1503ced8d73SChristophe Lombard break; 1513ced8d73SChristophe Lombard default: 1523ced8d73SChristophe Lombard rc = -EINVAL; 1533ced8d73SChristophe Lombard } 1543ced8d73SChristophe Lombard return rc; 1553ced8d73SChristophe Lombard } 1563ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_switch_phb_mode); 1573ced8d73SChristophe Lombard 1583ced8d73SChristophe Lombard /* 1593ced8d73SChristophe Lombard * When switching the PHB to capi mode, the TVT#1 entry for 1603ced8d73SChristophe Lombard * the Partitionable Endpoint is set in bypass mode, like 1613ced8d73SChristophe Lombard * in PCI mode. 1623ced8d73SChristophe Lombard * Configure the device dma to use TVT#1, which is done 1633ced8d73SChristophe Lombard * by calling dma_set_mask() with a mask large enough. 1643ced8d73SChristophe Lombard */ 1653ced8d73SChristophe Lombard int cxllib_set_device_dma(struct pci_dev *dev, unsigned long flags) 1663ced8d73SChristophe Lombard { 1673ced8d73SChristophe Lombard int rc; 1683ced8d73SChristophe Lombard 1693ced8d73SChristophe Lombard if (flags) 1703ced8d73SChristophe Lombard return -EINVAL; 1713ced8d73SChristophe Lombard 1723ced8d73SChristophe Lombard rc = dma_set_mask(&dev->dev, DMA_BIT_MASK(64)); 1733ced8d73SChristophe Lombard return rc; 1743ced8d73SChristophe Lombard } 1753ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_set_device_dma); 1763ced8d73SChristophe Lombard 1773ced8d73SChristophe Lombard int cxllib_get_PE_attributes(struct task_struct *task, 1783ced8d73SChristophe Lombard unsigned long translation_mode, 1793ced8d73SChristophe Lombard struct cxllib_pe_attributes *attr) 1803ced8d73SChristophe Lombard { 1813ced8d73SChristophe Lombard struct mm_struct *mm = NULL; 1823ced8d73SChristophe Lombard 1833ced8d73SChristophe Lombard if (translation_mode != CXL_TRANSLATED_MODE && 1843ced8d73SChristophe Lombard translation_mode != CXL_REAL_MODE) 1853ced8d73SChristophe Lombard return -EINVAL; 1863ced8d73SChristophe Lombard 1873ced8d73SChristophe Lombard attr->sr = cxl_calculate_sr(false, 1883ced8d73SChristophe Lombard task == NULL, 1893ced8d73SChristophe Lombard translation_mode == CXL_REAL_MODE, 1903ced8d73SChristophe Lombard true); 1913ced8d73SChristophe Lombard attr->lpid = mfspr(SPRN_LPID); 1923ced8d73SChristophe Lombard if (task) { 1933ced8d73SChristophe Lombard mm = get_task_mm(task); 1943ced8d73SChristophe Lombard if (mm == NULL) 1953ced8d73SChristophe Lombard return -EINVAL; 1963ced8d73SChristophe Lombard /* 1973ced8d73SChristophe Lombard * Caller is keeping a reference on mm_users for as long 1983ced8d73SChristophe Lombard * as XSL uses the memory context 1993ced8d73SChristophe Lombard */ 2003ced8d73SChristophe Lombard attr->pid = mm->context.id; 2013ced8d73SChristophe Lombard mmput(mm); 2023ced8d73SChristophe Lombard } else { 2033ced8d73SChristophe Lombard attr->pid = 0; 2043ced8d73SChristophe Lombard } 2053ced8d73SChristophe Lombard attr->tid = 0; 2063ced8d73SChristophe Lombard return 0; 2073ced8d73SChristophe Lombard } 2083ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_get_PE_attributes); 2093ced8d73SChristophe Lombard 2103ced8d73SChristophe Lombard int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags) 2113ced8d73SChristophe Lombard { 2123ced8d73SChristophe Lombard int rc; 2133ced8d73SChristophe Lombard u64 dar; 2143ced8d73SChristophe Lombard struct vm_area_struct *vma = NULL; 2153ced8d73SChristophe Lombard unsigned long page_size; 2163ced8d73SChristophe Lombard 2173ced8d73SChristophe Lombard if (mm == NULL) 2183ced8d73SChristophe Lombard return -EFAULT; 2193ced8d73SChristophe Lombard 2203ced8d73SChristophe Lombard down_read(&mm->mmap_sem); 2213ced8d73SChristophe Lombard 222*4fc0870dSChristophe Lombard vma = find_vma(mm, addr); 223*4fc0870dSChristophe Lombard if (!vma) { 224*4fc0870dSChristophe Lombard pr_err("Can't find vma for addr %016llx\n", addr); 225*4fc0870dSChristophe Lombard rc = -EFAULT; 226*4fc0870dSChristophe Lombard goto out; 227*4fc0870dSChristophe Lombard } 228*4fc0870dSChristophe Lombard /* get the size of the pages allocated */ 229*4fc0870dSChristophe Lombard page_size = vma_kernel_pagesize(vma); 230*4fc0870dSChristophe Lombard 231*4fc0870dSChristophe Lombard for (dar = (addr & ~(page_size - 1)); dar < (addr + size); dar += page_size) { 232*4fc0870dSChristophe Lombard if (dar < vma->vm_start || dar >= vma->vm_end) { 2333ced8d73SChristophe Lombard vma = find_vma(mm, addr); 2343ced8d73SChristophe Lombard if (!vma) { 2353ced8d73SChristophe Lombard pr_err("Can't find vma for addr %016llx\n", addr); 2363ced8d73SChristophe Lombard rc = -EFAULT; 2373ced8d73SChristophe Lombard goto out; 2383ced8d73SChristophe Lombard } 2393ced8d73SChristophe Lombard /* get the size of the pages allocated */ 2403ced8d73SChristophe Lombard page_size = vma_kernel_pagesize(vma); 2413ced8d73SChristophe Lombard } 2423ced8d73SChristophe Lombard 2433ced8d73SChristophe Lombard rc = cxl_handle_mm_fault(mm, flags, dar); 2443ced8d73SChristophe Lombard if (rc) { 2453ced8d73SChristophe Lombard pr_err("cxl_handle_mm_fault failed %d", rc); 2463ced8d73SChristophe Lombard rc = -EFAULT; 2473ced8d73SChristophe Lombard goto out; 2483ced8d73SChristophe Lombard } 2493ced8d73SChristophe Lombard } 2503ced8d73SChristophe Lombard rc = 0; 2513ced8d73SChristophe Lombard out: 2523ced8d73SChristophe Lombard up_read(&mm->mmap_sem); 2533ced8d73SChristophe Lombard return rc; 2543ced8d73SChristophe Lombard } 2553ced8d73SChristophe Lombard EXPORT_SYMBOL_GPL(cxllib_handle_fault); 256