xref: /linux/drivers/misc/cxl/cxllib.c (revision 4fc0870d7e462fe3b86e0f938ae75ce884728c7d)
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