linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
@ 2004-12-14 17:41 Jesse Barnes
  2004-12-14 23:55 ` Bjorn Helgaas
  2004-12-15 17:27 ` Jesse Barnes
  0 siblings, 2 replies; 11+ messages in thread
From: Jesse Barnes @ 2004-12-14 17:41 UTC (permalink / raw)
  To: linux-pci, linux-ia64, linux-kernel; +Cc: benh

[-- Attachment #1: Type: text/plain, Size: 584 bytes --]

This patch adds a new optional arch specific API to /proc/bus/pci to get at 
legacy I/O and memory space.  I know most platforms will correctly route 
legacy I/O and memory accesses to some well defined bus, but some don't (I 
think some PPC machines fall into the latter category?), thus the need for 
this patch.  I've also included draft documentation for the /proc/bus/pci API 
in general since it didn't appear to be documented anywhere but in the 
source.  Comments?  I'm successfully using this to bring up gfx stuff (i.e. 
card POSTing, X) on an Altix machine.

Thanks,
Jesse

[-- Attachment #2: legacy-ioctl-mem-api-4.patch --]
[-- Type: text/plain, Size: 20020 bytes --]

diff -Nru a/Documentation/filesystems/proc-pci.txt b/Documentation/filesystems/proc-pci.txt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/Documentation/filesystems/proc-pci.txt	2004-12-14 09:36:08 -08:00
@@ -0,0 +1,114 @@
+Directory layout
+
+/proc/bus/pci contains a list of PCI devices available on the system
+and can be used for driving PCI devices from userspace, getting a list
+of PCI devices, and hotplug support.  Example:
+
+  $ tree /proc/bus/pci
+  /proc/bus/pci/
+  |-- 01		directory for devices on bus 1 
+  |   |-- 01.0		single fn device in slot 1
+  |   |-- 03.0		single fn device in slot 3
+  |   `-- 04.0		single fn device in slot 4
+  |-- 02
+  |   |-- 01.0		single fn device in slot 1
+  |   |-- 02.0		<
+  |   |-- 02.1		< three function device on bus 2
+  |   `-- 02.2		< 
+  |-- 17
+  |   `-- 00.0		busses 17 and 1b have only one device each in
+  |			slot 0
+  |-- 1b
+  |   `-- 00.0
+  `-- devices		see below
+
+  4 directories, 10 files
+
+The devices file contains an entry for each PCI device in the system
+with information about the host addresses corresponding to the device
+BARs and which driver is bound to a device (if present).  This
+information can be used as an offset argument to mmap specific device
+files in the tree above.  Each field in the devices file is seperated
+by tabs with each device on a line by itself.  The fields are
+
+bus id
+?
+?
+host resource 0
+host resource 1
+host resource 2
+host resource 3
+host resource 4
+host resource 5
+host resource 6
+host resource 7
+BAR value 0
+BAR value 1
+BAR value 2
+BAR value 3
+BAR value 4
+BAR value 5
+BAR value 6
+driver name (if present)
+
+For example:
+
+0108	10a9100a	3e	c00000080f200000	0	0	0	0	0	0	100000	0	0	0	0	0	0	SGI-IOC4_IDE
+
+Available interfaces
+
+Each file corresponding to a device in /proc/bus/pci has open, read,
+write, lseek, ioctl, and mmap methods available.
+
+open
+  simply opens the device and returns a handle as expected
+
+read
+  reads from either PCI config space or legacy I/O space, using the
+  current file postion, depending on the current I/O mode setting.
+
+write
+  writes to either PCI config space or legacy I/O space, using the
+  current file postion, depending on the current I/O mode setting.
+
+  Note that reads and writes to legacy I/O space must be 1, 2 or 4
+  bytes in size with the appropriate buffer pointer.  Reads and writes
+  to config space can be arbitrarily sized.
+
+lseek
+  Can be used to set the current file position.  Note that the file
+  size is limited to 64k as that's how big legacy I/O space is.
+
+ioctl
+  ioctl is used to set the mode of a subsequent read, write or mmap
+  call.  Available ioctls (in linux/pci.h) include
+    PCIIOC_CONTROLLER - return PCI domain number
+    PCIIOC_MMAP_IS_IO - next mmap maps to I/O space
+    PCIIOC_MMAP_IS_MEM - next mmap maps to memory space
+    PCIIOC_WRITE_COMBINE - try to use write gathering for the new
+                           region if the ioctl argument is true,
+			   otherwise disable write gathering
+    PCIIOC_LEGACY_IO - read/write legacy I/O space if ioctl argument
+                       is true, otherwise subsequent read/writes will
+		       go to config space
+    PCIIOC_MMAP_IS_LEGACY_MEM - next mmap maps to legacy memory space
+
+mmap
+  The final argument of mmap(2) must be a host address obtained from
+  /proc/bus/pci/devices or an address between 0 and 1M for legacy
+  memory mappings.
+
+Examples
+
+	/* Read from the 3rd BAR of device in slot 1 of bus 1 */
+	int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
+	lseek(fd, 0x20, SEEK_SET);
+	read(fd, buf, 8);
+	close(fd);
+
+	/* Do a legacy read of 1 byte (inb) from port 0x3cc */
+	int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
+	ioctl(fd, PCIIOC_LEGACY_IO, 1); /* enable legacy I/O */
+	lseek(fd, 0x3cc, SEEK_SET);
+	read(fd, &val, 1);
+	close(fd);
diff -Nru a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
--- a/arch/ia64/pci/pci.c	2004-12-14 09:36:08 -08:00
+++ b/arch/ia64/pci/pci.c	2004-12-14 09:36:08 -08:00
@@ -514,11 +514,19 @@
 		 */
 		return -EINVAL;
 
+	if (mmap_state == pci_mmap_legacy_mem) {
+		unsigned long addr;
+		int ret;
+		if ((ret = pci_get_legacy_mem(dev, &addr)))
+			return ret;
+		vma->vm_pgoff += addr >> PAGE_SHIFT;
+	}
+
 	/*
 	 * Leave vm_pgoff as-is, the PCI space address is the physical
 	 * address on this platform.
 	 */
-	vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
+	vma->vm_flags |= (VM_SHM | VM_RESERVED | VM_IO);
 
 	if (write_combine)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
@@ -530,6 +538,101 @@
 		return -EAGAIN;
 
 	return 0;
+}
+
+/**
+ * ia64_pci_get_legacy_mem - generic legacy mem routine
+ * @dev: device pointer
+ * @addr: caller allocated variable for the base address
+ *
+ * Find the base of legacy memory for @dev.  This is typically the first
+ * megabyte of bus address space for @dev or is simply 0 on platforms whose
+ * chipsets support legacy I/O and memory routing.  Returns 0 on success
+ * or a standard error code on failure.
+ *
+ * This is the ia64 generic version of this routine.  Other platforms
+ * are free to override it with a machine vector.
+ */
+int ia64_pci_get_legacy_mem(struct pci_dev *dev, unsigned long *addr)
+{
+	*addr = 0;
+	return 0;
+}
+
+/**
+ * ia64_pci_legacy_read - read from legacy I/O space
+ * @dev: device to read
+ * @port: legacy port value
+ * @val: caller allocated storage for returned value
+ * @size: number of bytes to read
+ *
+ * Simply reads @size bytes from @port and puts the result in @val.
+ *
+ * Again, this (and the write routine) are generic versions that can be
+ * overridden by the platform.  This is necessary on platforms that don't
+ * support legacy I/O routing or that hard fail on legacy I/O timeouts.
+ */
+int ia64_pci_legacy_read(struct pci_dev *dev, u16 port, u32 *val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr = port;
+	unsigned long *addr;
+
+	switch (size) {
+	case 1:
+		addr = (unsigned long *)paddr;
+		*val = (u8)(*(volatile u8 *)(addr));
+		break;
+	case 2:
+		addr = (unsigned long *)paddr;
+		*val = (u16)(*(volatile u16 *)(addr));
+		break;
+	case 4:
+		addr = (unsigned long *)paddr;
+		*val = (u32)(*(volatile u32 *)(addr));
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * ia64_pci_legacy_write - perform a legacy I/O write
+ * @dev: device pointer
+ * @port: port to write
+ * @val: value to write
+ * @size: number of bytes to write from @val
+ *
+ * Simply writes @size bytes of @val to @port.
+ */
+int ia64_pci_legacy_write(struct pci_dev *dev, u16 port, u32 val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr = port;
+	unsigned long *addr;
+
+	switch (size) {
+	case 1:
+		addr = (unsigned long *)paddr;
+		*(volatile u8 *)(addr) = (u8)(val);
+		break;
+	case 2:
+		addr = (unsigned long *)paddr;
+		*(volatile u16 *)(addr) = (u16)(val);
+		break;
+	case 4:
+		addr = (unsigned long *)paddr;
+		*(volatile u32 *)(addr) = (u32)(val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
 }
 
 /**
diff -Nru a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
--- a/arch/ia64/sn/pci/pci_dma.c	2004-12-14 09:36:08 -08:00
+++ b/arch/ia64/sn/pci/pci_dma.c	2004-12-14 09:36:08 -08:00
@@ -475,3 +475,77 @@
 EXPORT_SYMBOL(sn_pci_free_consistent);
 EXPORT_SYMBOL(sn_pci_dma_supported);
 EXPORT_SYMBOL(sn_dma_mapping_error);
+
+int sn_pci_get_legacy_mem(struct pci_dev *dev, unsigned long *addr)
+{
+	if (!SN_PCIDEV_BUSSOFT(dev))
+		return -ENODEV;
+
+	*addr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_mem | __IA64_UNCACHED_OFFSET;
+
+	return 0;
+}
+
+int sn_pci_legacy_read(struct pci_dev *dev, u16 port, u32 *val, u8 size)
+{
+	int ret = 0;
+	unsigned long addr;
+
+	if (!SN_PCIDEV_BUSSOFT(dev)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	addr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_io | __IA64_UNCACHED_OFFSET;
+	addr += port;
+
+	ret = ia64_sn_probe_mem(addr, (long)size, (void *)val);
+
+	/* Read timed out, return -1 to emulate soft fail */
+	if (ret == 1)
+		*val = -1;
+
+	/* Invalid argument */
+	if (ret == 2)
+		ret = -EINVAL;
+
+ out:
+	return ret;
+}
+
+int sn_pci_legacy_write(struct pci_dev *dev, u16 port, u32 val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr;
+	unsigned long *addr;
+
+	if (!SN_PCIDEV_BUSSOFT(dev)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* Put the phys addr in uncached space */
+	paddr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_io | __IA64_UNCACHED_OFFSET;
+	paddr += port;
+	addr = (unsigned long *)paddr;
+
+	switch (size) {
+	case 1:
+		*(volatile u8 *)(addr) = (u8)(val);
+		ret = 1;
+		break;
+	case 2:
+		*(volatile u16 *)(addr) = (u16)(val);
+		ret = 2;
+		break;
+	case 4:
+		*(volatile u32 *)(addr) = (u32)(val);
+		ret = 4;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+ out:
+	return ret;
+}
diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
--- a/drivers/pci/proc.c	2004-12-14 09:36:08 -08:00
+++ b/drivers/pci/proc.c	2004-12-14 09:36:08 -08:00
@@ -44,12 +44,9 @@
 	return new;
 }
 
-static ssize_t
-proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_config_read(struct pci_dev *dev, char __user *buf,
+					size_t nbytes, loff_t *ppos)
 {
-	const struct inode *ino = file->f_dentry->d_inode;
-	const struct proc_dir_entry *dp = PDE(ino);
-	struct pci_dev *dev = dp->data;
 	unsigned int pos = *ppos;
 	unsigned int cnt, size;
 
@@ -126,12 +123,10 @@
 	return nbytes;
 }
 
-static ssize_t
-proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_config_write(struct pci_dev *dev,
+					 const char __user *buf, size_t nbytes,
+					 loff_t *ppos)
 {
-	const struct inode *ino = file->f_dentry->d_inode;
-	const struct proc_dir_entry *dp = PDE(ino);
-	struct pci_dev *dev = dp->data;
 	int pos = *ppos;
 	int size = dev->cfg_size;
 	int cnt;
@@ -196,11 +191,77 @@
 	return nbytes;
 }
 
+#ifdef HAVE_PCI_LEGACY
+static int proc_bus_pci_legacy_read(struct pci_dev *dev, char __user *buf,
+				    size_t size, loff_t *ppos)
+{
+	int ret;
+	u32 v;
+
+	/* Only support 1, 2 or 4 byte accesses */
+	if (size != 1 && size != 2 && size != 4)
+		return -EINVAL;
+
+	if ((ret = pci_legacy_read(dev, *ppos, &v, size)))
+		return ret;
+
+	if (copy_to_user(buf, &v, size))
+		return -EFAULT;
+
+	return size;
+}
+
+static int proc_bus_pci_legacy_write(struct pci_dev *dev,
+				     const char __user *buf, size_t size,
+				     loff_t *ppos)
+{
+	u32 v = 0;
+
+	/* Only support 1, 2 or 4 byte accesses */
+	if (size != 1 && size != 2 && size != 4)
+		return -EINVAL;
+
+	if (copy_from_user(&v, buf, size))
+		return -EFAULT;
+
+	return pci_legacy_write(dev, *ppos, v, size);
+}
+#endif /* HAVE_PCI_LEGACY */
+
 struct pci_filp_private {
 	enum pci_mmap_state mmap_state;
 	int write_combine;
+	int legacy_io; /* read/write corresponds to legacy space */
 };
 
+static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
+				 size_t nbytes, loff_t *ppos)
+{
+	const struct inode *ino = file->f_dentry->d_inode;
+	const struct proc_dir_entry *dp = PDE(ino);
+	struct pci_dev *dev = dp->data;
+	struct pci_filp_private *fpriv = file->private_data;
+
+	if (!fpriv->legacy_io)
+		return proc_bus_pci_config_read(dev, buf, nbytes, ppos);
+	else
+		return proc_bus_pci_legacy_read(dev, buf, nbytes, ppos);
+}
+
+static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
+				  size_t nbytes, loff_t *ppos)
+{
+	const struct inode *ino = file->f_dentry->d_inode;
+	const struct proc_dir_entry *dp = PDE(ino);
+	struct pci_dev *dev = dp->data;
+	struct pci_filp_private *fpriv = file->private_data;
+
+	if (!fpriv->legacy_io)
+		return proc_bus_pci_config_write(dev, buf, nbytes, ppos);
+	else
+		return proc_bus_pci_legacy_write(dev, buf, nbytes, ppos);
+}
+
 static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	const struct proc_dir_entry *dp = PDE(inode);
@@ -224,6 +285,10 @@
 		fpriv->mmap_state = pci_mmap_mem;
 		break;
 
+	case PCIIOC_MMAP_IS_LEGACY_MEM:
+		fpriv->mmap_state = pci_mmap_legacy_mem;
+		break;
+
 	case PCIIOC_WRITE_COMBINE:
 		if (arg)
 			fpriv->write_combine = 1;
@@ -233,6 +298,16 @@
 
 #endif /* HAVE_PCI_MMAP */
 
+#ifdef HAVE_PCI_LEGACY
+	case PCIIOC_LEGACY_IO:
+		if (arg)
+			fpriv->legacy_io = 1;
+		else
+			fpriv->legacy_io = 0;
+		break;
+
+#endif /* HAVE_PCI_LEGACY */
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -271,6 +346,7 @@
 
 	fpriv->mmap_state = pci_mmap_io;
 	fpriv->write_combine = 0;
+	fpriv->legacy_io = 0;
 
 	file->private_data = fpriv;
 
@@ -403,7 +479,8 @@
 		return -ENOMEM;
 	e->proc_fops = &proc_bus_pci_operations;
 	e->data = dev;
-	e->size = dev->cfg_size;
+	/* includes config space *and* legacy I/O port space */
+	e->size = 0xffff;
 
 	return 0;
 }
diff -Nru a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h
--- a/include/asm-ia64/machvec.h	2004-12-14 09:36:08 -08:00
+++ b/include/asm-ia64/machvec.h	2004-12-14 09:36:08 -08:00
@@ -20,6 +20,7 @@
 struct irq_desc;
 struct page;
 struct mm_struct;
+struct pci_dev;
 
 typedef void ia64_mv_setup_t (char **);
 typedef void ia64_mv_cpu_init_t (void);
@@ -31,6 +32,11 @@
 typedef struct irq_desc *ia64_mv_irq_desc (unsigned int);
 typedef u8 ia64_mv_irq_to_vector (unsigned int);
 typedef unsigned int ia64_mv_local_vector_to_irq (u8);
+typedef int ia64_mv_pci_get_legacy_mem_t (struct pci_dev *, unsigned long *);
+typedef int ia64_mv_pci_legacy_read_t (struct pci_dev *, u16 port, u32 *val,
+				       u8 size);
+typedef int ia64_mv_pci_legacy_write_t (struct pci_dev *, u16 port, u32 val,
+					u8 size);
 
 /* DMA-mapping interface: */
 typedef void ia64_mv_dma_init (void);
@@ -125,6 +131,9 @@
 #  define platform_irq_desc		ia64_mv.irq_desc
 #  define platform_irq_to_vector	ia64_mv.irq_to_vector
 #  define platform_local_vector_to_irq	ia64_mv.local_vector_to_irq
+#  define platform_pci_get_legacy_mem	ia64_mv.pci_get_legacy_mem
+#  define platform_pci_legacy_read	ia64_mv.pci_legacy_read
+#  define platform_pci_legacy_write	ia64_mv.pci_legacy_write
 #  define platform_inb		ia64_mv.inb
 #  define platform_inw		ia64_mv.inw
 #  define platform_inl		ia64_mv.inl
@@ -172,6 +181,9 @@
 	ia64_mv_irq_desc *irq_desc;
 	ia64_mv_irq_to_vector *irq_to_vector;
 	ia64_mv_local_vector_to_irq *local_vector_to_irq;
+	ia64_mv_pci_get_legacy_mem_t *pci_get_legacy_mem;
+	ia64_mv_pci_legacy_read_t *pci_legacy_read;
+	ia64_mv_pci_legacy_write_t *pci_legacy_write;
 	ia64_mv_inb_t *inb;
 	ia64_mv_inw_t *inw;
 	ia64_mv_inl_t *inl;
@@ -215,6 +227,9 @@
 	platform_irq_desc,			\
 	platform_irq_to_vector,			\
 	platform_local_vector_to_irq,		\
+	platform_pci_get_legacy_mem,		\
+	platform_pci_legacy_read,		\
+	platform_pci_legacy_write,		\
 	platform_inb,				\
 	platform_inw,				\
 	platform_inl,				\
@@ -329,6 +344,15 @@
 #endif
 #ifndef platform_local_vector_to_irq
 # define platform_local_vector_to_irq	__ia64_local_vector_to_irq
+#endif
+#ifndef platform_pci_get_legacy_mem
+# define platform_pci_get_legacy_mem	ia64_pci_get_legacy_mem
+#endif
+#ifndef platform_pci_legacy_read
+# define platform_pci_legacy_read	ia64_pci_legacy_read
+#endif
+#ifndef platform_pci_legacy_write
+# define platform_pci_legacy_write	ia64_pci_legacy_write
 #endif
 #ifndef platform_inb
 # define platform_inb		__ia64_inb
diff -Nru a/include/asm-ia64/machvec_init.h b/include/asm-ia64/machvec_init.h
--- a/include/asm-ia64/machvec_init.h	2004-12-14 09:36:08 -08:00
+++ b/include/asm-ia64/machvec_init.h	2004-12-14 09:36:08 -08:00
@@ -5,6 +5,9 @@
 extern ia64_mv_irq_desc __ia64_irq_desc;
 extern ia64_mv_irq_to_vector __ia64_irq_to_vector;
 extern ia64_mv_local_vector_to_irq __ia64_local_vector_to_irq;
+extern ia64_mv_pci_get_legacy_mem_t ia64_pci_get_legacy_mem;
+extern ia64_mv_pci_legacy_read_t ia64_pci_legacy_read;
+extern ia64_mv_pci_legacy_write_t ia64_pci_legacy_write;
 
 extern ia64_mv_inb_t __ia64_inb;
 extern ia64_mv_inw_t __ia64_inw;
diff -Nru a/include/asm-ia64/machvec_sn2.h b/include/asm-ia64/machvec_sn2.h
--- a/include/asm-ia64/machvec_sn2.h	2004-12-14 09:36:08 -08:00
+++ b/include/asm-ia64/machvec_sn2.h	2004-12-14 09:36:08 -08:00
@@ -43,6 +43,9 @@
 extern ia64_mv_irq_desc sn_irq_desc;
 extern ia64_mv_irq_to_vector sn_irq_to_vector;
 extern ia64_mv_local_vector_to_irq sn_local_vector_to_irq;
+extern ia64_mv_pci_get_legacy_mem_t sn_pci_get_legacy_mem;
+extern ia64_mv_pci_legacy_read_t sn_pci_legacy_read;
+extern ia64_mv_pci_legacy_write_t sn_pci_legacy_write;
 extern ia64_mv_inb_t __sn_inb;
 extern ia64_mv_inw_t __sn_inw;
 extern ia64_mv_inl_t __sn_inl;
@@ -105,6 +108,9 @@
 #define platform_irq_desc		sn_irq_desc
 #define platform_irq_to_vector		sn_irq_to_vector
 #define platform_local_vector_to_irq	sn_local_vector_to_irq
+#define platform_pci_get_legacy_mem	sn_pci_get_legacy_mem
+#define platform_pci_legacy_read	sn_pci_legacy_read
+#define platform_pci_legacy_write	sn_pci_legacy_write
 #define platform_dma_init		machvec_noop
 #define platform_dma_alloc_coherent	sn_dma_alloc_coherent
 #define platform_dma_free_coherent	sn_dma_free_coherent
diff -Nru a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h
--- a/include/asm-ia64/pci.h	2004-12-14 09:36:08 -08:00
+++ b/include/asm-ia64/pci.h	2004-12-14 09:36:08 -08:00
@@ -85,6 +85,10 @@
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
 				enum pci_mmap_state mmap_state, int write_combine);
+#define HAVE_PCI_LEGACY
+#define pci_get_legacy_mem platform_pci_get_legacy_mem
+#define pci_legacy_read platform_pci_legacy_read
+#define pci_legacy_write platform_pci_legacy_write
 
 struct pci_window {
 	struct resource resource;
diff -Nru a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h
--- a/include/asm-ia64/sn/sn_sal.h	2004-12-14 09:36:08 -08:00
+++ b/include/asm-ia64/sn/sn_sal.h	2004-12-14 09:36:08 -08:00
@@ -474,6 +474,53 @@
 	return isrv.v0;
 }
 
+/**
+ * ia64_sn_probe_mem - read from memory safely
+ * @addr: address to probe
+ * @size: number bytes to read (1,2,4,8)
+ * @data_ptr: address to store value read by probe (-1 returned if probe fails)
+ *
+ * Call into the SAL to do a memory read.  If the read generates a machine
+ * check, this routine will recover gracefully and return -1 to the caller.
+ * @addr is usually a kernel virtual address in uncached space (i.e. the
+ * address starts with 0xc), but if called in physical mode, @addr should
+ * be a physical address.
+ *
+ * Return values:
+ *  0 - probe successful
+ *  1 - probe failed (generated MCA)
+ *  2 - Bad arg
+ * <0 - PAL error
+ */
+static inline u64
+ia64_sn_probe_mem(long addr, long size, void *data_ptr)
+{
+	struct ia64_sal_retval isrv;
+
+	SAL_CALL(isrv, SN_SAL_PROBE, addr, size, 0, 0, 0, 0, 0);
+
+	if (data_ptr) {
+		switch (size) {
+			case 1:
+				*((u8*)data_ptr) = (u8)isrv.v0;
+				break;
+			case 2:
+				*((u16*)data_ptr) = (u16)isrv.v0;
+				break;
+			case 4:
+				*((u32*)data_ptr) = (u32)isrv.v0;
+				break;
+			case 8:
+				*((u64*)data_ptr) = (u64)isrv.v0;
+				break;
+			default:
+				isrv.status = 2;
+		}
+	}
+
+	return isrv.status;
+}
+
 /*
  * Retrieve the system serial number as an ASCII string.
  */
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	2004-12-14 09:36:08 -08:00
+++ b/include/linux/pci.h	2004-12-14 09:36:08 -08:00
@@ -455,6 +455,8 @@
 #define PCIIOC_MMAP_IS_IO	(PCIIOC_BASE | 0x01)	/* Set mmap state to I/O space. */
 #define PCIIOC_MMAP_IS_MEM	(PCIIOC_BASE | 0x02)	/* Set mmap state to MEM space. */
 #define PCIIOC_WRITE_COMBINE	(PCIIOC_BASE | 0x03)	/* Enable/disable write-combining. */
+#define PCIIOC_LEGACY_IO	(PCIIOC_BASE | 0x04)	/* Read/write access functions go to legacy space */
+#define PCIIOC_MMAP_IS_LEGACY_MEM (PCIIOC_BASE | 0x5)	/* Legacy memory */
 
 #ifdef __KERNEL__
 
@@ -468,7 +470,8 @@
 /* File state for mmap()s on /proc/bus/pci/X/Y */
 enum pci_mmap_state {
 	pci_mmap_io,
-	pci_mmap_mem
+	pci_mmap_mem,
+	pci_mmap_legacy_mem
 };
 
 /* This defines the direction arg to the DMA mapping routines. */

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-14 17:41 [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API Jesse Barnes
@ 2004-12-14 23:55 ` Bjorn Helgaas
  2004-12-15  0:11   ` Jesse Barnes
  2004-12-15  8:57   ` Benjamin Herrenschmidt
  2004-12-15 17:27 ` Jesse Barnes
  1 sibling, 2 replies; 11+ messages in thread
From: Bjorn Helgaas @ 2004-12-14 23:55 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-pci, linux-ia64, linux-kernel, benh

> +/proc/bus/pci contains a list of PCI devices available on the system
> +and can be used for driving PCI devices from userspace, getting a list
> +of PCI devices, and hotplug support.  Example:
> +
> +  $ tree /proc/bus/pci
> +  /proc/bus/pci/
> +  |-- 01  directory for devices on bus 1 
> +  |   |-- 01.0  single fn device in slot 1

What about multiple PCI domains?  Should /proc/bus/pci be extended
somehow to deal with them?

HP machines are currently configured to compress all PCI bus numbers
into one domain, but we plan to turn off that compression soon, which
means we'll have duplicate bus numbers in different domains.

> +read
> +  reads from either PCI config space or legacy I/O space, using the
> +  current file postion, depending on the current I/O mode setting.

I think by "legacy I/O space", you mean specifically "legacy
I/O *port* space", right?  Maybe there's no current use for it,
but I can imagine supporting MMIO accesses this way, too.

> +lseek
> +  Can be used to set the current file position.  Note that the file
> +  size is limited to 64k as that's how big legacy I/O space is.

On i386, anyway ;-)  But on ia64, we support multiple 64k I/O port
spaces (one of them being the 0-64K space that corresponds to the
i386 "legacy" space).  Shouldn't we be able to access them with this
interface, too?

> +ioctl
> +  ioctl is used to set the mode of a subsequent read, write or mmap
> +  call.  Available ioctls (in linux/pci.h) include
> +    PCIIOC_CONTROLLER - return PCI domain number
> +    PCIIOC_MMAP_IS_IO - next mmap maps to I/O space
> +    PCIIOC_MMAP_IS_MEM - next mmap maps to memory space
> +    PCIIOC_WRITE_COMBINE - try to use write gathering for the new
> +                           region if the ioctl argument is true,
> +      otherwise disable write gathering
> +    PCIIOC_LEGACY_IO - read/write legacy I/O space if ioctl argument
> +                       is true, otherwise subsequent read/writes will
> +         go to config space

Wouldn't it be nicer to have PCIIOC_{IO,CONFIG_SPACE}?  Then
PCIIOC_MMIO could be added someday.  Using PCIIOC_LEGACY_IO with a
boolean kind of locks you into having only two choices, ever.  (Well,
you could extend the boolean to an int, but the *name* still suggests
an on/off switch.)

> +    PCIIOC_MMAP_IS_LEGACY_MEM - next mmap maps to legacy memory space

I'm always confused about exactly what "legacy memory space" refers
to.  I guess (from below) that it's MMIO space between 0 and 1M?

This seems similar to PCIIOC_MMAP_IS_MEM, except that for MMAP_IS_MEM,
the user-supplied address is a host address that must be in one of the
device's memory resources.  For MMAP_IS_LEGACY_MEM, it looks like you
get a mapping to something in the first megabyte of the memory aperture
that's routed to the device.

So for MMAP_IS_LEGACY_MEM, you have no way of knowing whether the
device will respond to the region you're mapping, right?

The comment on ia64_pci_get_legacy_mem() says we want to map the
first megabyte of bus address space for the device.  But I don't
think we can generate a bus address between 0-1M on any arbitrary
PCI bus.  For example, the MMIO apertures on zx1 are rather small
(on the order of 128-256MB), and we typically map them to non-
overlapping bus address space to make peer-to-peer transactions
possible.

> + /* Do a legacy read of 1 byte (inb) from port 0x3cc */
> + int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
> + ioctl(fd, PCIIOC_LEGACY_IO, 1); /* enable legacy I/O */
> + lseek(fd, 0x3cc, SEEK_SET);

In this example, the particular device ("01/01.0") you open
makes no difference, right?  The I/O port routing is determined
by the chipset, not by which /proc/bus/pci/... file you open.

HP chipsets allow you to change the routing of VGA-related
MMIO and I/O port space.  X currently twiddles this by hand.
I've always thought there should be an ioctl that says "please
route VGA resources to this PCI device", so X could do this
twiddling in a chipset-independent way.

I'd be interested in seeing the X side of your work, too.
There's currently an ugly mess of figuring out what chipset
we've got, fiddling with VGA routing and soft-fail settings,
etc.  Maybe seeing your X support will help me figure out
how to take advantage of your new kernel hooks to clean
things up for zx1 and sx1000.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-14 23:55 ` Bjorn Helgaas
@ 2004-12-15  0:11   ` Jesse Barnes
  2004-12-15  3:06     ` Jesse Barnes
  2004-12-15  8:57   ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 11+ messages in thread
From: Jesse Barnes @ 2004-12-15  0:11 UTC (permalink / raw)
  To: Bjorn Helgaas; +Cc: linux-ia64, linux-kernel, benh

On Tuesday, December 14, 2004 3:55 pm, Bjorn Helgaas wrote:
> > +/proc/bus/pci contains a list of PCI devices available on the system
> > +and can be used for driving PCI devices from userspace, getting a list
> > +of PCI devices, and hotplug support.  Example:
> > +
> > +  $ tree /proc/bus/pci
> > +  /proc/bus/pci/
> > +  |-- 01  directory for devices on bus 1
> > +  |   |-- 01.0  single fn device in slot 1
>
> What about multiple PCI domains?  Should /proc/bus/pci be extended
> somehow to deal with them?

This might look different on a machine with domains, I ran the tree command on 
a machine /wo them.

> HP machines are currently configured to compress all PCI bus numbers
> into one domain, but we plan to turn off that compression soon, which
> means we'll have duplicate bus numbers in different domains.

Sure, ok.  Domains seem useful in other ways too, so I'm looking forward to 
seeing that.

> > +read
> > +  reads from either PCI config space or legacy I/O space, using the
> > +  current file postion, depending on the current I/O mode setting.
>
> I think by "legacy I/O space", you mean specifically "legacy
> I/O *port* space", right?  Maybe there's no current use for it,
> but I can imagine supporting MMIO accesses this way, too.

Yeah, I could clarify that, legacy memory space is supported via direct mmap.

> > +lseek
> > +  Can be used to set the current file position.  Note that the file
> > +  size is limited to 64k as that's how big legacy I/O space is.
>
> On i386, anyway ;-)  But on ia64, we support multiple 64k I/O port
> spaces (one of them being the 0-64K space that corresponds to the
> i386 "legacy" space).  Shouldn't we be able to access them with this
> interface, too?

Yeah, we could do that, any suggestions?  If I split the ioctl commands into 
PCIIOC_LEGACY_IO and PCIIOC_CONFIG the former could take an argument for the 
domain, would that work?  Then again, isn't having the pci_dev enough?

> > +ioctl
> > +  ioctl is used to set the mode of a subsequent read, write or mmap
> > +  call.  Available ioctls (in linux/pci.h) include
> > +    PCIIOC_CONTROLLER - return PCI domain number
> > +    PCIIOC_MMAP_IS_IO - next mmap maps to I/O space
> > +    PCIIOC_MMAP_IS_MEM - next mmap maps to memory space
> > +    PCIIOC_WRITE_COMBINE - try to use write gathering for the new
> > +                           region if the ioctl argument is true,
> > +      otherwise disable write gathering
> > +    PCIIOC_LEGACY_IO - read/write legacy I/O space if ioctl argument
> > +                       is true, otherwise subsequent read/writes will
> > +         go to config space
>
> Wouldn't it be nicer to have PCIIOC_{IO,CONFIG_SPACE}?  Then
> PCIIOC_MMIO could be added someday.  Using PCIIOC_LEGACY_IO with a
> boolean kind of locks you into having only two choices, ever.  (Well,
> you could extend the boolean to an int, but the *name* still suggests
> an on/off switch.)

Yeah, proc.c was a little inconsistent here.  Having two separate ioctls is ok 
with me.

> > +    PCIIOC_MMAP_IS_LEGACY_MEM - next mmap maps to legacy memory space
>
> I'm always confused about exactly what "legacy memory space" refers
> to.  I guess (from below) that it's MMIO space between 0 and 1M?

Yep.

> This seems similar to PCIIOC_MMAP_IS_MEM, except that for MMAP_IS_MEM,
> the user-supplied address is a host address that must be in one of the
> device's memory resources.  For MMAP_IS_LEGACY_MEM, it looks like you
> get a mapping to something in the first megabyte of the memory aperture
> that's routed to the device.

Exactly.

> So for MMAP_IS_LEGACY_MEM, you have no way of knowing whether the
> device will respond to the region you're mapping, right?

Correct.

> The comment on ia64_pci_get_legacy_mem() says we want to map the
> first megabyte of bus address space for the device.  But I don't
> think we can generate a bus address between 0-1M on any arbitrary
> PCI bus.  For example, the MMIO apertures on zx1 are rather small
> (on the order of 128-256MB), and we typically map them to non-
> overlapping bus address space to make peer-to-peer transactions
> possible.

Right, you can return -EINVAL or -ENODEV in this case.

> > + /* Do a legacy read of 1 byte (inb) from port 0x3cc */
> > + int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
> > + ioctl(fd, PCIIOC_LEGACY_IO, 1); /* enable legacy I/O */
> > + lseek(fd, 0x3cc, SEEK_SET);
>
> In this example, the particular device ("01/01.0") you open
> makes no difference, right?  The I/O port routing is determined
> by the chipset, not by which /proc/bus/pci/... file you open.

But the chipset can be programmed to route things correctly or remap the 
correct legacy I/O port domain in the callback routine.

> HP chipsets allow you to change the routing of VGA-related
> MMIO and I/O port space.  X currently twiddles this by hand.
> I've always thought there should be an ioctl that says "please
> route VGA resources to this PCI device", so X could do this
> twiddling in a chipset-independent way.

I think this routine could do that.

> I'd be interested in seeing the X side of your work, too.
> There's currently an ugly mess of figuring out what chipset
> we've got, fiddling with VGA routing and soft-fail settings,
> etc.  Maybe seeing your X support will help me figure out
> how to take advantage of your new kernel hooks to clean
> things up for zx1 and sx1000.

Hopefully we'll be able to post those shortly to xorg@freedesktop.org.  We're 
working through some driver issues atm.

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-15  0:11   ` Jesse Barnes
@ 2004-12-15  3:06     ` Jesse Barnes
  0 siblings, 0 replies; 11+ messages in thread
From: Jesse Barnes @ 2004-12-15  3:06 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Bjorn Helgaas, linux-ia64, linux-kernel, benh

On Tuesday, December 14, 2004 4:11 pm, Jesse Barnes wrote:
> > In this example, the particular device ("01/01.0") you open
> > makes no difference, right?  The I/O port routing is determined
> > by the chipset, not by which /proc/bus/pci/... file you open.
>
> But the chipset can be programmed to route things correctly or remap the
> correct legacy I/O port domain in the callback routine.

I should clarify here, the device *does* matter, since the legacy I/O port 
space mapping will point at different addresses depending on the device.  
Generally, devices on the same bus will get the same address, but two devices 
on different busses will have different addresses mapped (at least on Altix).  
In your case, where you can route I/O ports to arbitrary busses, you could do 
the routing at the time of the call and return -EBUSY if another device was 
already using the route and hadn't released it yet.

Jesse

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-14 23:55 ` Bjorn Helgaas
  2004-12-15  0:11   ` Jesse Barnes
@ 2004-12-15  8:57   ` Benjamin Herrenschmidt
  2004-12-15 17:00     ` Jesse Barnes
  1 sibling, 1 reply; 11+ messages in thread
From: Benjamin Herrenschmidt @ 2004-12-15  8:57 UTC (permalink / raw)
  To: Bjorn Helgaas; +Cc: Jesse Barnes, linux-pci, linux-ia64, Linux Kernel list


> What about multiple PCI domains?  Should /proc/bus/pci be extended
> somehow to deal with them?
> 
> HP machines are currently configured to compress all PCI bus numbers
> into one domain, but we plan to turn off that compression soon, which
> means we'll have duplicate bus numbers in different domains.

I do the same kind of thing in ppc32 and want to get rid of it too.

> > +read
> > +  reads from either PCI config space or legacy I/O space, using the
> > +  current file postion, depending on the current I/O mode setting.
> 
> I think by "legacy I/O space", you mean specifically "legacy
> I/O *port* space", right?  Maybe there's no current use for it,
> but I can imagine supporting MMIO accesses this way, too.

Legacy IO ports, there should be one space per PCI domain. There is also
legacy ISA memory space though on some ppc's, this doesn't exist at all.
I suspect we want to expose both in a way.

> > +lseek
> > +  Can be used to set the current file position.  Note that the file
> > +  size is limited to 64k as that's how big legacy I/O space is.
> 
> On i386, anyway ;-)  But on ia64, we support multiple 64k I/O port
> spaces (one of them being the 0-64K space that corresponds to the
> i386 "legacy" space).  Shouldn't we be able to access them with this
> interface, too?

We should imho. On ppc, we have a 64k space per domain. One of the main
set of HW that has use for these are VGA cards. It's perfectly possible
to have a Mac with an AGP card in the AGP port and a PCI video card in
one of hte PCI slots, and those are on 2 different domains with
different legacy (0...64k) IO spaces.

We defininitely want whatever interface we define to deal with that.

> > +ioctl
> > +  ioctl is used to set the mode of a subsequent read, write or mmap
> > +  call.  Available ioctls (in linux/pci.h) include
> > +    PCIIOC_CONTROLLER - return PCI domain number
> > +    PCIIOC_MMAP_IS_IO - next mmap maps to I/O space
> > +    PCIIOC_MMAP_IS_MEM - next mmap maps to memory space
> > +    PCIIOC_WRITE_COMBINE - try to use write gathering for the new
> > +                           region if the ioctl argument is true,
> > +      otherwise disable write gathering
> > +    PCIIOC_LEGACY_IO - read/write legacy I/O space if ioctl argument
> > +                       is true, otherwise subsequent read/writes will
> > +         go to config space
> 
> Wouldn't it be nicer to have PCIIOC_{IO,CONFIG_SPACE}?  Then
> PCIIOC_MMIO could be added someday.  Using PCIIOC_LEGACY_IO with a
> boolean kind of locks you into having only two choices, ever.  (Well,
> you could extend the boolean to an int, but the *name* still suggests
> an on/off switch.)

That and legacy memory space, which we may want to mmap as well in a
more portable way than there is now (for platforms where it is
available).

> > +    PCIIOC_MMAP_IS_LEGACY_MEM - next mmap maps to legacy memory space
> 
> I'm always confused about exactly what "legacy memory space" refers
> to.  I guess (from below) that it's MMIO space between 0 and 1M?

I think so, it's an ISA thing, but some video and token ring cards, for
example, still rely on it. Not all host bridges on non-x86 are able to
generate PCI MMIO cycles in that range though. Some do provide a special
window for this tho.

> This seems similar to PCIIOC_MMAP_IS_MEM, except that for MMAP_IS_MEM,
> the user-supplied address is a host address that must be in one of the
> device's memory resources.  For MMAP_IS_LEGACY_MEM, it looks like you
> get a mapping to something in the first megabyte of the memory aperture
> that's routed to the device.
> 
> So for MMAP_IS_LEGACY_MEM, you have no way of knowing whether the
> device will respond to the region you're mapping, right?

Usually, you know the device will before using it. Like using the VGA
memory space on a VGA card etc...

> The comment on ia64_pci_get_legacy_mem() says we want to map the
> first megabyte of bus address space for the device.  But I don't
> think we can generate a bus address between 0-1M on any arbitrary
> PCI bus.  For example, the MMIO apertures on zx1 are rather small
> (on the order of 128-256MB), and we typically map them to non-
> overlapping bus address space to make peer-to-peer transactions
> possible.

Yes, not all bridges allow generations of those cycles, but it's useful
to have an interface letting things like X map this space in a proper
way that can also deal with domains etc... for platforms where this
space exists.

> > + /* Do a legacy read of 1 byte (inb) from port 0x3cc */
> > + int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
> > + ioctl(fd, PCIIOC_LEGACY_IO, 1); /* enable legacy I/O */
> > + lseek(fd, 0x3cc, SEEK_SET);
> 
> In this example, the particular device ("01/01.0") you open
> makes no difference, right?  The I/O port routing is determined
> by the chipset, not by which /proc/bus/pci/... file you open.
> 
> HP chipsets allow you to change the routing of VGA-related
> MMIO and I/O port space.  X currently twiddles this by hand.
> I've always thought there should be an ioctl that says "please
> route VGA resources to this PCI device", so X could do this
> twiddling in a chipset-independent way.

There is some work done by Jon Smirl in this area (a VGA access
arbitration driver).

> I'd be interested in seeing the X side of your work, too.
> There's currently an ugly mess of figuring out what chipset
> we've got, fiddling with VGA routing and soft-fail settings,
> etc.  Maybe seeing your X support will help me figure out
> how to take advantage of your new kernel hooks to clean
> things up for zx1 and sx1000.
-- 
Benjamin Herrenschmidt <benh@kernel.crashing.org>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-15  8:57   ` Benjamin Herrenschmidt
@ 2004-12-15 17:00     ` Jesse Barnes
  2004-12-16 14:55       ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 11+ messages in thread
From: Jesse Barnes @ 2004-12-15 17:00 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Bjorn Helgaas, linux-pci, linux-ia64, Linux Kernel list

On Wednesday, December 15, 2004 12:57 am, Benjamin Herrenschmidt wrote:
> > I think by "legacy I/O space", you mean specifically "legacy
> > I/O *port* space", right?  Maybe there's no current use for it,
> > but I can imagine supporting MMIO accesses this way, too.
>
> Legacy IO ports, there should be one space per PCI domain. There is also
> legacy ISA memory space though on some ppc's, this doesn't exist at all.
> I suspect we want to expose both in a way.

This interface exports both.

> > On i386, anyway ;-)  But on ia64, we support multiple 64k I/O port
> > spaces (one of them being the 0-64K space that corresponds to the
> > i386 "legacy" space).  Shouldn't we be able to access them with this
> > interface, too?
>
> We should imho. On ppc, we have a 64k space per domain. One of the main
> set of HW that has use for these are VGA cards. It's perfectly possible
> to have a Mac with an AGP card in the AGP port and a PCI video card in
> one of hte PCI slots, and those are on 2 different domains with
> different legacy (0...64k) IO spaces.
>
> We defininitely want whatever interface we define to deal with that.

Good, because that's exactly what it does.  The arch is responsible for 
returning the legacy I/O port or legacy ISA memory base address given a 
pci_dev, which is used as a base for the page offset passed into mmap.  So 
e.g. mmap(..., 0xa0000) after doing ioctl(fd, PCIIOC_MMAP_IS_LEGACY_MEM, ...) 
would get you the VGA framebuffer for the device corresponding to 'fd'.

> There is some work done by Jon Smirl in this area (a VGA access
> arbitration driver).

I think Dave Airlie did a version of the vga class driver, and the backend 
used for /proc/bus/pci could be used for both drivers.  I'm 
using /proc/bus/pci because it's available now and nearly good enough (i.e. 
this patch was all I needed to get going).

Anyway, I'll post another version with Bjorn's suggestion about the ioctl for 
choosing config or legacy I/O port read/writes, since it looks like the rest 
of your concerns are dealt with.

Thanks,
Jesse

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-14 17:41 [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API Jesse Barnes
  2004-12-14 23:55 ` Bjorn Helgaas
@ 2004-12-15 17:27 ` Jesse Barnes
  2004-12-15 21:03   ` Andreas Dilger
  1 sibling, 1 reply; 11+ messages in thread
From: Jesse Barnes @ 2004-12-15 17:27 UTC (permalink / raw)
  To: linux-pci; +Cc: linux-ia64, linux-kernel, benh, bjorn.helgaas

[-- Attachment #1: Type: text/plain, Size: 854 bytes --]

On Tuesday, December 14, 2004 9:41 am, Jesse Barnes wrote:
> This patch adds a new optional arch specific API to /proc/bus/pci to get at
> legacy I/O and memory space.  I know most platforms will correctly route
> legacy I/O and memory accesses to some well defined bus, but some don't (I
> think some PPC machines fall into the latter category?), thus the need for
> this patch.  I've also included draft documentation for the /proc/bus/pci
> API in general since it didn't appear to be documented anywhere but in the
> source.  Comments?  I'm successfully using this to bring up gfx stuff (i.e.
> card POSTing, X) on an Altix machine.

Here's a new revision including Bjorn's suggestions about making the 
read/write state more flexible (and readable IMO).  I've also updated the 
documentation a bit, hopefully clarifying things a bit.

Thanks,
Jesse

[-- Attachment #2: legacy-ioctl-mem-api-5.patch --]
[-- Type: text/plain, Size: 23256 bytes --]

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/12/15 09:19:54-08:00 jbarnes@tomahawk.engr.sgi.com 
#   update ioctls
# 
# include/linux/pci.h
#   2004/12/15 09:19:43-08:00 jbarnes@tomahawk.engr.sgi.com +9 -2
#   use rw_state enum instead of bool ioctl
# 
# drivers/pci/proc.c
#   2004/12/15 09:19:43-08:00 jbarnes@tomahawk.engr.sgi.com +10 -9
#   use rw_state enum instead of bool ioctl
# 
# Documentation/filesystems/proc-pci.txt
#   2004/12/15 09:19:43-08:00 jbarnes@tomahawk.engr.sgi.com +21 -9
#   update documentation for ioctl reorg
# 
# ChangeSet
#   2004/12/14 09:35:33-08:00 jbarnes@tomahawk.engr.sgi.com 
#   legacy memory and I/O api
# 
# Documentation/filesystems/proc-pci.txt
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +114 -0
# 
# include/linux/pci.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +4 -1
#   legacy memory and I/O api
# 
# include/asm-ia64/sn/sn_sal.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +47 -0
#   legacy memory and I/O api
# 
# include/asm-ia64/pci.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +4 -0
#   legacy memory and I/O api
# 
# include/asm-ia64/machvec_sn2.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +6 -0
#   legacy memory and I/O api
# 
# include/asm-ia64/machvec_init.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +3 -0
#   legacy memory and I/O api
# 
# include/asm-ia64/machvec.h
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +24 -0
#   legacy memory and I/O api
# 
# drivers/pci/proc.c
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +88 -11
#   legacy memory and I/O api
# 
# arch/ia64/sn/pci/pci_dma.c
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +74 -0
#   legacy memory and I/O api
# 
# arch/ia64/pci/pci.c
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +104 -1
#   legacy memory and I/O api
# 
# Documentation/filesystems/proc-pci.txt
#   2004/12/14 09:35:22-08:00 jbarnes@tomahawk.engr.sgi.com +0 -0
#   BitKeeper file /home/jbarnes/working/linux-2.5-pciapi/Documentation/filesystems/proc-pci.txt
# 
diff -Nru a/Documentation/filesystems/proc-pci.txt b/Documentation/filesystems/proc-pci.txt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/Documentation/filesystems/proc-pci.txt	2004-12-15 09:26:14 -08:00
@@ -0,0 +1,126 @@
+Directory layout
+
+/proc/bus/pci contains a list of PCI devices available on the system
+and can be used for driving PCI devices from userspace, getting a list
+of PCI devices, and hotplug support.  Example:
+
+  $ tree /proc/bus/pci
+  /proc/bus/pci/
+  |-- 01		directory for devices on bus 1 
+  |   |-- 01.0		single fn device in slot 1
+  |   |-- 03.0		single fn device in slot 3
+  |   `-- 04.0		single fn device in slot 4
+  |-- 02
+  |   |-- 01.0		single fn device in slot 1
+  |   |-- 02.0		<
+  |   |-- 02.1		< three function device on bus 2
+  |   `-- 02.2		< 
+  |-- 17
+  |   `-- 00.0		busses 17 and 1b have only one device each in
+  |			slot 0
+  |-- 1b
+  |   `-- 00.0
+  `-- devices		see below
+
+  4 directories, 10 files
+
+The devices file contains an entry for each PCI device in the system
+with information about the host addresses corresponding to the device
+BARs and which driver is bound to a device (if present).  This
+information can be used as an offset argument to mmap specific device
+files in the tree above.  Each field in the devices file is seperated
+by tabs with each device on a line by itself.  The fields are
+
+bus id
+?
+?
+host resource 0
+host resource 1
+host resource 2
+host resource 3
+host resource 4
+host resource 5
+host resource 6
+host resource 7
+BAR value 0
+BAR value 1
+BAR value 2
+BAR value 3
+BAR value 4
+BAR value 5
+BAR value 6
+driver name (if present)
+
+For example:
+
+0108	10a9100a	3e	c00000080f200000	0	0	0	0	0	0	100000	0	0	0	0	0	0	SGI-IOC4_IDE
+
+Available interfaces
+
+Each file corresponding to a device in /proc/bus/pci has open, read,
+write, lseek, ioctl, and mmap methods available.
+
+open
+  simply opens the device and returns a handle as expected
+
+read
+  reads from either PCI config space or legacy I/O space, using the
+  current file postion, depending on the current I/O mode setting.
+
+write
+  writes to either PCI config space or legacy I/O space, using the
+  current file postion, depending on the current I/O mode setting.
+
+  Note that reads and writes to legacy I/O space must be 1, 2 or 4
+  bytes in size with the appropriate buffer pointer.  Reads and writes
+  to config space can be arbitrarily sized.  Legacy I/O port space
+  reads and writes must also be to a file position >64k--the kernel will
+  route them to the target device.
+
+lseek
+  Can be used to set the current file position.  Note that the file
+  size is limited to 64k as that's how big legacy I/O space is.
+
+ioctl
+  ioctl is used to set the mode of a subsequent read, write or mmap
+  call.  Available ioctls (in linux/pci.h) include
+    PCIIOC_CONTROLLER - return PCI domain number
+    PCIIOC_MMAP_IS_IO - next mmap maps to I/O space
+    PCIIOC_MMAP_IS_MEM - next mmap maps to memory space
+    PCIIOC_MMAP_IS_LEGACY_MEM - next mmap maps to legacy memory space
+    PCIIOC_WRITE_COMBINE - try to use write gathering for the new
+                           region if the ioctl argument is true,
+                           otherwise disable write gathering
+    PCIIOC_RW_LEGACY_IO - read/writes will go to legacy I/O space
+    PCIIOC_RW_CONFIG - read/writes will go to config space
+  Note that not all architectures support the *_MMAP_* or *_RW_* ioctl
+  commands.  If they're not supported, ioctl will return -EINVAL.
+
+mmap
+  The final argument of mmap(2) must be a host address obtained from
+  /proc/bus/pci/devices or an address between 0 and 1M for legacy
+  memory mappings.  Only supported on some architectures.
+
+Examples
+
+	/* Read from the 3rd BAR of device in slot 1 of bus 1 */
+	int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
+	ioctl(fd, PCIIOC_RW_CONFIG); /* make sure we're using config space */
+	lseek(fd, 0x20, SEEK_SET);
+	read(fd, buf, 8);
+	close(fd);
+
+	/*
+	 * Do a legacy read of 1 byte (inb) from port 0x3cc from 1:1.0.
+	 * Note that this will just generate a bus cycle on bus 1 for port
+	 * 0x3cc, which the device in question may or may not respond to.
+	 * If there is no response, -1 will be returned.  Also note that
+	 * it may not be possible to generate legacy I/O port requests on
+	 * the bus or device specified, in that case the write will fail
+	 * with an appropriate error code.
+	 */
+	int fd = open("/proc/bus/pci/01/01.0", O_RDONLY);
+	ioctl(fd, PCIIOC_RW_LEGACY_IO); /* enable legacy I/O */
+	lseek(fd, 0x3cc, SEEK_SET);
+	read(fd, &val, 1);
+	close(fd);
diff -Nru a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
--- a/arch/ia64/pci/pci.c	2004-12-15 09:26:14 -08:00
+++ b/arch/ia64/pci/pci.c	2004-12-15 09:26:14 -08:00
@@ -514,11 +514,19 @@
 		 */
 		return -EINVAL;
 
+	if (mmap_state == pci_mmap_legacy_mem) {
+		unsigned long addr;
+		int ret;
+		if ((ret = pci_get_legacy_mem(dev, &addr)))
+			return ret;
+		vma->vm_pgoff += addr >> PAGE_SHIFT;
+	}
+
 	/*
 	 * Leave vm_pgoff as-is, the PCI space address is the physical
 	 * address on this platform.
 	 */
-	vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
+	vma->vm_flags |= (VM_SHM | VM_RESERVED | VM_IO);
 
 	if (write_combine)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
@@ -530,6 +538,101 @@
 		return -EAGAIN;
 
 	return 0;
+}
+
+/**
+ * ia64_pci_get_legacy_mem - generic legacy mem routine
+ * @dev: device pointer
+ * @addr: caller allocated variable for the base address
+ *
+ * Find the base of legacy memory for @dev.  This is typically the first
+ * megabyte of bus address space for @dev or is simply 0 on platforms whose
+ * chipsets support legacy I/O and memory routing.  Returns 0 on success
+ * or a standard error code on failure.
+ *
+ * This is the ia64 generic version of this routine.  Other platforms
+ * are free to override it with a machine vector.
+ */
+int ia64_pci_get_legacy_mem(struct pci_dev *dev, unsigned long *addr)
+{
+	*addr = 0;
+	return 0;
+}
+
+/**
+ * ia64_pci_legacy_read - read from legacy I/O space
+ * @dev: device to read
+ * @port: legacy port value
+ * @val: caller allocated storage for returned value
+ * @size: number of bytes to read
+ *
+ * Simply reads @size bytes from @port and puts the result in @val.
+ *
+ * Again, this (and the write routine) are generic versions that can be
+ * overridden by the platform.  This is necessary on platforms that don't
+ * support legacy I/O routing or that hard fail on legacy I/O timeouts.
+ */
+int ia64_pci_legacy_read(struct pci_dev *dev, u16 port, u32 *val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr = port;
+	unsigned long *addr;
+
+	switch (size) {
+	case 1:
+		addr = (unsigned long *)paddr;
+		*val = (u8)(*(volatile u8 *)(addr));
+		break;
+	case 2:
+		addr = (unsigned long *)paddr;
+		*val = (u16)(*(volatile u16 *)(addr));
+		break;
+	case 4:
+		addr = (unsigned long *)paddr;
+		*val = (u32)(*(volatile u32 *)(addr));
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * ia64_pci_legacy_write - perform a legacy I/O write
+ * @dev: device pointer
+ * @port: port to write
+ * @val: value to write
+ * @size: number of bytes to write from @val
+ *
+ * Simply writes @size bytes of @val to @port.
+ */
+int ia64_pci_legacy_write(struct pci_dev *dev, u16 port, u32 val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr = port;
+	unsigned long *addr;
+
+	switch (size) {
+	case 1:
+		addr = (unsigned long *)paddr;
+		*(volatile u8 *)(addr) = (u8)(val);
+		break;
+	case 2:
+		addr = (unsigned long *)paddr;
+		*(volatile u16 *)(addr) = (u16)(val);
+		break;
+	case 4:
+		addr = (unsigned long *)paddr;
+		*(volatile u32 *)(addr) = (u32)(val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
 }
 
 /**
diff -Nru a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
--- a/arch/ia64/sn/pci/pci_dma.c	2004-12-15 09:26:14 -08:00
+++ b/arch/ia64/sn/pci/pci_dma.c	2004-12-15 09:26:14 -08:00
@@ -475,3 +475,77 @@
 EXPORT_SYMBOL(sn_pci_free_consistent);
 EXPORT_SYMBOL(sn_pci_dma_supported);
 EXPORT_SYMBOL(sn_dma_mapping_error);
+
+int sn_pci_get_legacy_mem(struct pci_dev *dev, unsigned long *addr)
+{
+	if (!SN_PCIDEV_BUSSOFT(dev))
+		return -ENODEV;
+
+	*addr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_mem | __IA64_UNCACHED_OFFSET;
+
+	return 0;
+}
+
+int sn_pci_legacy_read(struct pci_dev *dev, u16 port, u32 *val, u8 size)
+{
+	int ret = 0;
+	unsigned long addr;
+
+	if (!SN_PCIDEV_BUSSOFT(dev)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	addr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_io | __IA64_UNCACHED_OFFSET;
+	addr += port;
+
+	ret = ia64_sn_probe_mem(addr, (long)size, (void *)val);
+
+	/* Read timed out, return -1 to emulate soft fail */
+	if (ret == 1)
+		*val = -1;
+
+	/* Invalid argument */
+	if (ret == 2)
+		ret = -EINVAL;
+
+ out:
+	return ret;
+}
+
+int sn_pci_legacy_write(struct pci_dev *dev, u16 port, u32 val, u8 size)
+{
+	int ret = 0;
+	unsigned long paddr;
+	unsigned long *addr;
+
+	if (!SN_PCIDEV_BUSSOFT(dev)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* Put the phys addr in uncached space */
+	paddr = SN_PCIDEV_BUSSOFT(dev)->bs_legacy_io | __IA64_UNCACHED_OFFSET;
+	paddr += port;
+	addr = (unsigned long *)paddr;
+
+	switch (size) {
+	case 1:
+		*(volatile u8 *)(addr) = (u8)(val);
+		ret = 1;
+		break;
+	case 2:
+		*(volatile u16 *)(addr) = (u16)(val);
+		ret = 2;
+		break;
+	case 4:
+		*(volatile u32 *)(addr) = (u32)(val);
+		ret = 4;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+ out:
+	return ret;
+}
diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c
--- a/drivers/pci/proc.c	2004-12-15 09:26:14 -08:00
+++ b/drivers/pci/proc.c	2004-12-15 09:26:14 -08:00
@@ -44,12 +44,9 @@
 	return new;
 }
 
-static ssize_t
-proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_config_read(struct pci_dev *dev, char __user *buf,
+					size_t nbytes, loff_t *ppos)
 {
-	const struct inode *ino = file->f_dentry->d_inode;
-	const struct proc_dir_entry *dp = PDE(ino);
-	struct pci_dev *dev = dp->data;
 	unsigned int pos = *ppos;
 	unsigned int cnt, size;
 
@@ -126,12 +123,10 @@
 	return nbytes;
 }
 
-static ssize_t
-proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_config_write(struct pci_dev *dev,
+					 const char __user *buf, size_t nbytes,
+					 loff_t *ppos)
 {
-	const struct inode *ino = file->f_dentry->d_inode;
-	const struct proc_dir_entry *dp = PDE(ino);
-	struct pci_dev *dev = dp->data;
 	int pos = *ppos;
 	int size = dev->cfg_size;
 	int cnt;
@@ -196,11 +191,77 @@
 	return nbytes;
 }
 
+#ifdef HAVE_PCI_LEGACY
+static int proc_bus_pci_legacy_read(struct pci_dev *dev, char __user *buf,
+				    size_t size, loff_t *ppos)
+{
+	int ret;
+	u32 v;
+
+	/* Only support 1, 2 or 4 byte accesses */
+	if (size != 1 && size != 2 && size != 4)
+		return -EINVAL;
+
+	if ((ret = pci_legacy_read(dev, *ppos, &v, size)))
+		return ret;
+
+	if (copy_to_user(buf, &v, size))
+		return -EFAULT;
+
+	return size;
+}
+
+static int proc_bus_pci_legacy_write(struct pci_dev *dev,
+				     const char __user *buf, size_t size,
+				     loff_t *ppos)
+{
+	u32 v = 0;
+
+	/* Only support 1, 2 or 4 byte accesses */
+	if (size != 1 && size != 2 && size != 4)
+		return -EINVAL;
+
+	if (copy_from_user(&v, buf, size))
+		return -EFAULT;
+
+	return pci_legacy_write(dev, *ppos, v, size);
+}
+#endif /* HAVE_PCI_LEGACY */
+
 struct pci_filp_private {
 	enum pci_mmap_state mmap_state;
 	int write_combine;
+	enum pci_rw_state rw_state;
 };
 
+static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
+				 size_t nbytes, loff_t *ppos)
+{
+	const struct inode *ino = file->f_dentry->d_inode;
+	const struct proc_dir_entry *dp = PDE(ino);
+	struct pci_dev *dev = dp->data;
+	struct pci_filp_private *fpriv = file->private_data;
+
+	if (fpriv->rw_state == pci_rw_config)
+		return proc_bus_pci_config_read(dev, buf, nbytes, ppos);
+	else
+		return proc_bus_pci_legacy_read(dev, buf, nbytes, ppos);
+}
+
+static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
+				  size_t nbytes, loff_t *ppos)
+{
+	const struct inode *ino = file->f_dentry->d_inode;
+	const struct proc_dir_entry *dp = PDE(ino);
+	struct pci_dev *dev = dp->data;
+	struct pci_filp_private *fpriv = file->private_data;
+
+	if (fpriv->rw_state == pci_rw_config)
+		return proc_bus_pci_config_write(dev, buf, nbytes, ppos);
+	else
+		return proc_bus_pci_legacy_write(dev, buf, nbytes, ppos);
+}
+
 static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	const struct proc_dir_entry *dp = PDE(inode);
@@ -224,6 +285,10 @@
 		fpriv->mmap_state = pci_mmap_mem;
 		break;
 
+	case PCIIOC_MMAP_IS_LEGACY_MEM:
+		fpriv->mmap_state = pci_mmap_legacy_mem;
+		break;
+
 	case PCIIOC_WRITE_COMBINE:
 		if (arg)
 			fpriv->write_combine = 1;
@@ -233,6 +298,17 @@
 
 #endif /* HAVE_PCI_MMAP */
 
+#ifdef HAVE_PCI_LEGACY
+	case PCIIOC_RW_LEGACY_IO:
+		fpriv->rw_state = pci_rw_legacy_io;
+		break;
+
+	case PCIIOC_RW_CONFIG:
+		fpriv->rw_state = pci_rw_config;
+		break;
+
+#endif /* HAVE_PCI_LEGACY */
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -271,6 +347,7 @@
 
 	fpriv->mmap_state = pci_mmap_io;
 	fpriv->write_combine = 0;
+	fpriv->rw_state = pci_rw_config;
 
 	file->private_data = fpriv;
 
@@ -403,7 +480,8 @@
 		return -ENOMEM;
 	e->proc_fops = &proc_bus_pci_operations;
 	e->data = dev;
-	e->size = dev->cfg_size;
+	/* includes config space *and* legacy I/O port space */
+	e->size = 0xffff;
 
 	return 0;
 }
diff -Nru a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h
--- a/include/asm-ia64/machvec.h	2004-12-15 09:26:14 -08:00
+++ b/include/asm-ia64/machvec.h	2004-12-15 09:26:14 -08:00
@@ -20,6 +20,7 @@
 struct irq_desc;
 struct page;
 struct mm_struct;
+struct pci_dev;
 
 typedef void ia64_mv_setup_t (char **);
 typedef void ia64_mv_cpu_init_t (void);
@@ -31,6 +32,11 @@
 typedef struct irq_desc *ia64_mv_irq_desc (unsigned int);
 typedef u8 ia64_mv_irq_to_vector (unsigned int);
 typedef unsigned int ia64_mv_local_vector_to_irq (u8);
+typedef int ia64_mv_pci_get_legacy_mem_t (struct pci_dev *, unsigned long *);
+typedef int ia64_mv_pci_legacy_read_t (struct pci_dev *, u16 port, u32 *val,
+				       u8 size);
+typedef int ia64_mv_pci_legacy_write_t (struct pci_dev *, u16 port, u32 val,
+					u8 size);
 
 /* DMA-mapping interface: */
 typedef void ia64_mv_dma_init (void);
@@ -125,6 +131,9 @@
 #  define platform_irq_desc		ia64_mv.irq_desc
 #  define platform_irq_to_vector	ia64_mv.irq_to_vector
 #  define platform_local_vector_to_irq	ia64_mv.local_vector_to_irq
+#  define platform_pci_get_legacy_mem	ia64_mv.pci_get_legacy_mem
+#  define platform_pci_legacy_read	ia64_mv.pci_legacy_read
+#  define platform_pci_legacy_write	ia64_mv.pci_legacy_write
 #  define platform_inb		ia64_mv.inb
 #  define platform_inw		ia64_mv.inw
 #  define platform_inl		ia64_mv.inl
@@ -172,6 +181,9 @@
 	ia64_mv_irq_desc *irq_desc;
 	ia64_mv_irq_to_vector *irq_to_vector;
 	ia64_mv_local_vector_to_irq *local_vector_to_irq;
+	ia64_mv_pci_get_legacy_mem_t *pci_get_legacy_mem;
+	ia64_mv_pci_legacy_read_t *pci_legacy_read;
+	ia64_mv_pci_legacy_write_t *pci_legacy_write;
 	ia64_mv_inb_t *inb;
 	ia64_mv_inw_t *inw;
 	ia64_mv_inl_t *inl;
@@ -215,6 +227,9 @@
 	platform_irq_desc,			\
 	platform_irq_to_vector,			\
 	platform_local_vector_to_irq,		\
+	platform_pci_get_legacy_mem,		\
+	platform_pci_legacy_read,		\
+	platform_pci_legacy_write,		\
 	platform_inb,				\
 	platform_inw,				\
 	platform_inl,				\
@@ -329,6 +344,15 @@
 #endif
 #ifndef platform_local_vector_to_irq
 # define platform_local_vector_to_irq	__ia64_local_vector_to_irq
+#endif
+#ifndef platform_pci_get_legacy_mem
+# define platform_pci_get_legacy_mem	ia64_pci_get_legacy_mem
+#endif
+#ifndef platform_pci_legacy_read
+# define platform_pci_legacy_read	ia64_pci_legacy_read
+#endif
+#ifndef platform_pci_legacy_write
+# define platform_pci_legacy_write	ia64_pci_legacy_write
 #endif
 #ifndef platform_inb
 # define platform_inb		__ia64_inb
diff -Nru a/include/asm-ia64/machvec_init.h b/include/asm-ia64/machvec_init.h
--- a/include/asm-ia64/machvec_init.h	2004-12-15 09:26:14 -08:00
+++ b/include/asm-ia64/machvec_init.h	2004-12-15 09:26:14 -08:00
@@ -5,6 +5,9 @@
 extern ia64_mv_irq_desc __ia64_irq_desc;
 extern ia64_mv_irq_to_vector __ia64_irq_to_vector;
 extern ia64_mv_local_vector_to_irq __ia64_local_vector_to_irq;
+extern ia64_mv_pci_get_legacy_mem_t ia64_pci_get_legacy_mem;
+extern ia64_mv_pci_legacy_read_t ia64_pci_legacy_read;
+extern ia64_mv_pci_legacy_write_t ia64_pci_legacy_write;
 
 extern ia64_mv_inb_t __ia64_inb;
 extern ia64_mv_inw_t __ia64_inw;
diff -Nru a/include/asm-ia64/machvec_sn2.h b/include/asm-ia64/machvec_sn2.h
--- a/include/asm-ia64/machvec_sn2.h	2004-12-15 09:26:14 -08:00
+++ b/include/asm-ia64/machvec_sn2.h	2004-12-15 09:26:14 -08:00
@@ -43,6 +43,9 @@
 extern ia64_mv_irq_desc sn_irq_desc;
 extern ia64_mv_irq_to_vector sn_irq_to_vector;
 extern ia64_mv_local_vector_to_irq sn_local_vector_to_irq;
+extern ia64_mv_pci_get_legacy_mem_t sn_pci_get_legacy_mem;
+extern ia64_mv_pci_legacy_read_t sn_pci_legacy_read;
+extern ia64_mv_pci_legacy_write_t sn_pci_legacy_write;
 extern ia64_mv_inb_t __sn_inb;
 extern ia64_mv_inw_t __sn_inw;
 extern ia64_mv_inl_t __sn_inl;
@@ -105,6 +108,9 @@
 #define platform_irq_desc		sn_irq_desc
 #define platform_irq_to_vector		sn_irq_to_vector
 #define platform_local_vector_to_irq	sn_local_vector_to_irq
+#define platform_pci_get_legacy_mem	sn_pci_get_legacy_mem
+#define platform_pci_legacy_read	sn_pci_legacy_read
+#define platform_pci_legacy_write	sn_pci_legacy_write
 #define platform_dma_init		machvec_noop
 #define platform_dma_alloc_coherent	sn_dma_alloc_coherent
 #define platform_dma_free_coherent	sn_dma_free_coherent
diff -Nru a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h
--- a/include/asm-ia64/pci.h	2004-12-15 09:26:14 -08:00
+++ b/include/asm-ia64/pci.h	2004-12-15 09:26:14 -08:00
@@ -85,6 +85,10 @@
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
 				enum pci_mmap_state mmap_state, int write_combine);
+#define HAVE_PCI_LEGACY
+#define pci_get_legacy_mem platform_pci_get_legacy_mem
+#define pci_legacy_read platform_pci_legacy_read
+#define pci_legacy_write platform_pci_legacy_write
 
 struct pci_window {
 	struct resource resource;
diff -Nru a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h
--- a/include/asm-ia64/sn/sn_sal.h	2004-12-15 09:26:14 -08:00
+++ b/include/asm-ia64/sn/sn_sal.h	2004-12-15 09:26:14 -08:00
@@ -474,6 +474,53 @@
 	return isrv.v0;
 }
 
+/**
+ * ia64_sn_probe_mem - read from memory safely
+ * @addr: address to probe
+ * @size: number bytes to read (1,2,4,8)
+ * @data_ptr: address to store value read by probe (-1 returned if probe fails)
+ *
+ * Call into the SAL to do a memory read.  If the read generates a machine
+ * check, this routine will recover gracefully and return -1 to the caller.
+ * @addr is usually a kernel virtual address in uncached space (i.e. the
+ * address starts with 0xc), but if called in physical mode, @addr should
+ * be a physical address.
+ *
+ * Return values:
+ *  0 - probe successful
+ *  1 - probe failed (generated MCA)
+ *  2 - Bad arg
+ * <0 - PAL error
+ */
+static inline u64
+ia64_sn_probe_mem(long addr, long size, void *data_ptr)
+{
+	struct ia64_sal_retval isrv;
+
+	SAL_CALL(isrv, SN_SAL_PROBE, addr, size, 0, 0, 0, 0, 0);
+
+	if (data_ptr) {
+		switch (size) {
+			case 1:
+				*((u8*)data_ptr) = (u8)isrv.v0;
+				break;
+			case 2:
+				*((u16*)data_ptr) = (u16)isrv.v0;
+				break;
+			case 4:
+				*((u32*)data_ptr) = (u32)isrv.v0;
+				break;
+			case 8:
+				*((u64*)data_ptr) = (u64)isrv.v0;
+				break;
+			default:
+				isrv.status = 2;
+		}
+	}
+
+	return isrv.status;
+}
+
 /*
  * Retrieve the system serial number as an ASCII string.
  */
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	2004-12-15 09:26:14 -08:00
+++ b/include/linux/pci.h	2004-12-15 09:26:14 -08:00
@@ -455,6 +455,9 @@
 #define PCIIOC_MMAP_IS_IO	(PCIIOC_BASE | 0x01)	/* Set mmap state to I/O space. */
 #define PCIIOC_MMAP_IS_MEM	(PCIIOC_BASE | 0x02)	/* Set mmap state to MEM space. */
 #define PCIIOC_WRITE_COMBINE	(PCIIOC_BASE | 0x03)	/* Enable/disable write-combining. */
+#define PCIIOC_RW_LEGACY_IO	(PCIIOC_BASE | 0x04)	/* Read/write access functions go to legacy space */
+#define PCIIOC_RW_CONFIG	(PCIIOC_BASE | 0x05)	/* Read/write access functions go to config space */
+#define PCIIOC_MMAP_IS_LEGACY_MEM (PCIIOC_BASE | 0x6)	/* Legacy memory */
 
 #ifdef __KERNEL__
 
@@ -468,7 +471,14 @@
 /* File state for mmap()s on /proc/bus/pci/X/Y */
 enum pci_mmap_state {
 	pci_mmap_io,
-	pci_mmap_mem
+	pci_mmap_mem,
+	pci_mmap_legacy_mem
+};
+
+/* Mode state for read/write routines on /proc/bus/pci/X/Y */
+enum pci_rw_state {
+	pci_rw_config, /* r/w config space */
+	pci_rw_legacy_io /* r/w legacy I/O port space */
 };
 
 /* This defines the direction arg to the DMA mapping routines. */

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-15 17:27 ` Jesse Barnes
@ 2004-12-15 21:03   ` Andreas Dilger
  2004-12-15 23:15     ` Jesse Barnes
  0 siblings, 1 reply; 11+ messages in thread
From: Andreas Dilger @ 2004-12-15 21:03 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: linux-pci, linux-ia64, linux-kernel, benh, bjorn.helgaas

[-- Attachment #1: Type: text/plain, Size: 877 bytes --]

On Dec 15, 2004  09:27 -0800, Jesse Barnes wrote:
> +write
> +  Legacy I/O port space reads and writes must also be to a file
> +  position >64k--the kernel will route them to the target device.

Shouldn't that be < 64k based on the description of lseek?

> +lseek
> +  Can be used to set the current file position.  Note that the file
> +  size is limited to 64k as that's how big legacy I/O space is.

> +ioctl
> +  Note that not all architectures support the *_MMAP_* or *_RW_* ioctl
> +  commands.  If they're not supported, ioctl will return -EINVAL.

Shouldn't they return -ENOTTY?  That indicates to the caller that the
ioctl isn't handled, vs -EINVAL which indicates bad value being passed
(e.g. bad write size).

Cheers, Andreas
--
Andreas Dilger
http://sourceforge.net/projects/ext2resize/
http://members.shaw.ca/adilger/             http://members.shaw.ca/golinux/


[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-15 21:03   ` Andreas Dilger
@ 2004-12-15 23:15     ` Jesse Barnes
  0 siblings, 0 replies; 11+ messages in thread
From: Jesse Barnes @ 2004-12-15 23:15 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-pci, linux-ia64, linux-kernel, benh, bjorn.helgaas

On Wednesday, December 15, 2004 1:03 pm, Andreas Dilger wrote:
> On Dec 15, 2004  09:27 -0800, Jesse Barnes wrote:
> > +write
> > +  Legacy I/O port space reads and writes must also be to a file
> > +  position >64k--the kernel will route them to the target device.
>
> Shouldn't that be < 64k based on the description of lseek?

Err.. yes

> > +lseek
> > +  Can be used to set the current file position.  Note that the file
> > +  size is limited to 64k as that's how big legacy I/O space is.
> >
> > +ioctl
> > +  Note that not all architectures support the *_MMAP_* or *_RW_* ioctl
> > +  commands.  If they're not supported, ioctl will return -EINVAL.
>
> Shouldn't they return -ENOTTY?  That indicates to the caller that the
> ioctl isn't handled, vs -EINVAL which indicates bad value being passed
> (e.g. bad write size).

Maybe, but that's not how /proc/bus/pci behaves right now...

Jesse

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-15 17:00     ` Jesse Barnes
@ 2004-12-16 14:55       ` Benjamin Herrenschmidt
  2004-12-16 16:26         ` Jesse Barnes
  0 siblings, 1 reply; 11+ messages in thread
From: Benjamin Herrenschmidt @ 2004-12-16 14:55 UTC (permalink / raw)
  To: Jesse Barnes; +Cc: Bjorn Helgaas, linux-pci, linux-ia64, Linux Kernel list

On Wed, 2004-12-15 at 09:00 -0800, Jesse Barnes wrote:

> Good, because that's exactly what it does.  The arch is responsible for 
> returning the legacy I/O port or legacy ISA memory base address given a 
> pci_dev, which is used as a base for the page offset passed into mmap.  So 
> e.g. mmap(..., 0xa0000) after doing ioctl(fd, PCIIOC_MMAP_IS_LEGACY_MEM, ...) 
> would get you the VGA framebuffer for the device corresponding to 'fd'.

Sounds good... The only thing is a pci_dev may not be available if you
have a PCI->ISA bridge, tho you may just use the pci_dev of the
bridge...

> > There is some work done by Jon Smirl in this area (a VGA access
> > arbitration driver).
> 
> I think Dave Airlie did a version of the vga class driver, and the backend 
> used for /proc/bus/pci could be used for both drivers.  I'm 
> using /proc/bus/pci because it's available now and nearly good enough (i.e. 
> this patch was all I needed to get going).
> 
> Anyway, I'll post another version with Bjorn's suggestion about the ioctl for 
> choosing config or legacy I/O port read/writes, since it looks like the rest 
> of your concerns are dealt with.
> 
> Thanks,
> Jesse
-- 
Benjamin Herrenschmidt <benh@kernel.crashing.org>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API
  2004-12-16 14:55       ` Benjamin Herrenschmidt
@ 2004-12-16 16:26         ` Jesse Barnes
  0 siblings, 0 replies; 11+ messages in thread
From: Jesse Barnes @ 2004-12-16 16:26 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Bjorn Helgaas, linux-ia64, Linux Kernel list

On Thursday, December 16, 2004 6:55 am, Benjamin Herrenschmidt wrote:
> On Wed, 2004-12-15 at 09:00 -0800, Jesse Barnes wrote:
> > Good, because that's exactly what it does.  The arch is responsible for
> > returning the legacy I/O port or legacy ISA memory base address given a
> > pci_dev, which is used as a base for the page offset passed into mmap. 
> > So e.g. mmap(..., 0xa0000) after doing ioctl(fd,
> > PCIIOC_MMAP_IS_LEGACY_MEM, ...) would get you the VGA framebuffer for the
> > device corresponding to 'fd'.
>
> Sounds good... The only thing is a pci_dev may not be available if you
> have a PCI->ISA bridge, tho you may just use the pci_dev of the
> bridge...

Well, you'll have to have a fake one at least.  Remember the fds used in the 
above example come from fd = open("/proc/bus/pci/BB/SS.F", O_RDWR)...

Jesse

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2004-12-16 16:27 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-12-14 17:41 [PATCH] add legacy I/O and memory access routines to /proc/bus/pci API Jesse Barnes
2004-12-14 23:55 ` Bjorn Helgaas
2004-12-15  0:11   ` Jesse Barnes
2004-12-15  3:06     ` Jesse Barnes
2004-12-15  8:57   ` Benjamin Herrenschmidt
2004-12-15 17:00     ` Jesse Barnes
2004-12-16 14:55       ` Benjamin Herrenschmidt
2004-12-16 16:26         ` Jesse Barnes
2004-12-15 17:27 ` Jesse Barnes
2004-12-15 21:03   ` Andreas Dilger
2004-12-15 23:15     ` Jesse Barnes

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).