Cell support and fix PCI PDC paths

Matthew Wilcox <[email protected]>
Newsgroups gmane.linux.ports.hppa
Message-ID <[email protected]>
Grant noted that the way we construct a PDC
address for a PCI device is wrong on some machines
(ftp://ftp.parisc-linux.org/patches/diff-2.6.20-rc1-PCI_hwpath).
I noticed the PDC_PAT_IO_GET_HW_FROM_PCI_CONFIG procedure in the PAT
PDC document, and decided we should use it instead where we can.

I also noticed that the definition of the flags byte has been changed
for PAT machines -- the bottom 6 bits now define which cell the device is
attached to.  This explains (for example) why we get a duplicate hwpath
for Vesa's rp8400.

We would have got wrong results from the pdc_stable driver when it tried
to read the timeout value, so I've just disabled that sysfs file on pdc
machines for now.

I chose to implement cellular support by tagging each parisc_device
with a 'cell' byte, rather than the more obvious creation of a set of
cell parisc_devices as I thought this would minimise code churn and
memory consumption.

I didn't like the use of PF_* for flag definitions.  To me, PF_* are
networking protocol families (eg PF_INET).  So I changed that to HWP
for hardware path.

Testing appreciated; I've only tried it on an N-class.

diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c
index 6274cd2..51a6384 100644
--- a/arch/parisc/kernel/drivers.c
+++ b/arch/parisc/kernel/drivers.c
@@ -36,6 +36,7 @@
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/pdc.h>
+#include <asm/pdcpat.h>
 #include <asm/parisc-device.h>
 
 /* See comments in include/asm-parisc/pci.h */
@@ -306,9 +307,15 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
 	memset(&path->bc, -1, 6);
 
 	if (is_pci_dev(dev)) {
-		unsigned int devfn = to_pci_dev(dev)->devfn;
-		path->mod = PCI_FUNC(devfn);
-		path->bc[i--] = PCI_SLOT(devfn);
+		struct pci_dev *pdev = to_pci_dev(dev);
+		if (is_pdc_pat()) {
+			int ret = pdc_pat_get_hwpath_from_pci(
+						pdc_address(pdev), path);
+			if (ret == PDC_OK)
+				return;
+		}
+		path->mod = PCI_FUNC(pdev->devfn);
+		path->bc[i--] = PCI_SLOT(pdev->devfn);
 		dev = dev->parent;
 	}
 
@@ -318,6 +325,8 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
 			path->bc[i--] = PCI_SLOT(devfn) | (PCI_FUNC(devfn)<< 5);
 		} else if (dev->bus == &parisc_bus_type) {
 			path->bc[i--] = to_parisc_device(dev)->hw_path;
+			if (is_pdc_pat())
+				path->flags = to_parisc_device(dev)->cell;
 		}
 		dev = dev->parent;
 	}
@@ -326,6 +335,10 @@ static void get_node_path(struct device *dev, struct hardware_path *path)
 static char *print_hwpath(struct hardware_path *path, char *output)
 {
 	int i;
+
+	if (is_pdc_pat())
+		output += sprintf(output, "%u/", path->flags & HWP_CELL);
+
 	for (i = 0; i < 6; i++) {
 		if (path->bc[i] == -1)
 			continue;
@@ -406,13 +419,15 @@ static void setup_bus_id(struct parisc_device *padev)
 	sprintf(output, "%u", (unsigned char) padev->hw_path);
 }
 
-struct parisc_device * create_tree_node(char id, struct device *parent)
+static struct parisc_device *
+create_tree_node(struct device *parent, char cell, char id)
 {
 	struct parisc_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (!dev)
 		return NULL;
 
 	dev->hw_path = id;
+	dev->cell = cell;
 	dev->id.hw_type = HPHW_FAULTY;
 
 	dev->dev.parent = parent;
@@ -434,6 +449,7 @@ struct parisc_device * create_tree_node(char id, struct device *parent)
 
 struct match_id_data {
 	char id;
+	char cell;
 	struct parisc_device * dev;
 };
 
@@ -442,7 +458,7 @@ static int match_by_id(struct device * dev, void * data)
 	struct parisc_device * pdev = to_parisc_device(dev);
 	struct match_id_data * d = data;
 
-	if (pdev->hw_path == d->id) {
+	if (pdev->hw_path == d->id && pdev->cell == d->cell) {
 		d->dev = pdev;
 		return 1;
 	}
@@ -457,27 +473,29 @@ static int match_by_id(struct device * dev, void * data)
  * Checks all the children of @parent for a matching @id.  If none
  * found, it allocates a new device and returns it.
  */
-static struct parisc_device * alloc_tree_node(struct device *parent, char id)
+static struct parisc_device * alloc_tree_node(struct device *parent, char cell, char id)
 {
 	struct match_id_data d = {
 		.id = id,
+		.cell = cell,
 	};
 	if (device_for_each_child(parent, &d, match_by_id))
 		return d.dev;
 	else
-		return create_tree_node(id, parent);
+		return create_tree_node(parent, cell, id);
 }
 
 static struct parisc_device *create_parisc_device(struct hardware_path *modpath)
 {
 	int i;
+	char cell = is_pdc_pat() ? modpath->flags & HWP_CELL : 0;
 	struct device *parent = &root;
 	for (i = 0; i < 6; i++) {
 		if (modpath->bc[i] == -1)
 			continue;
-		parent = &alloc_tree_node(parent, modpath->bc[i])->dev;
+		parent = &alloc_tree_node(parent, cell, modpath->bc[i])->dev;
 	}
-	return alloc_tree_node(parent, modpath->mod);
+	return alloc_tree_node(parent, cell, modpath->mod);
 }
 
 struct parisc_device *
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
index 9158b70..7516790 100644
--- a/arch/parisc/kernel/firmware.c
+++ b/arch/parisc/kernel/firmware.c
@@ -1387,6 +1387,20 @@ int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val)
 
 	return retval;
 }
+
+int pdc_pat_get_hwpath_from_pci(unsigned long pci_addr, struct hardware_path *path)
+{
+	int retval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdc_lock, flags);
+	retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_GET_HW_FROM_PCI_CONFIG,
+			__pa(pdc_result), pci_addr);
+	memcpy(path, pdc_result, 8);
+	spin_unlock_irqrestore(&pdc_lock, flags);
+
+	return retval;
+}
 #endif /* __LP64__ */
 
 
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index ea1b7a6..66afab8 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -67,6 +67,7 @@
 #include <linux/spinlock.h>
 
 #include <asm/pdc.h>
+#include <asm/pdcpat.h>
 #include <asm/page.h>
 #include <asm/uaccess.h>
 #include <asm/hardware.h>
@@ -541,7 +542,7 @@ pdcs_size_read(struct subsystem *entry, char *buf)
  * pdcs_auto_read - Stable Storage autoboot/search flag output.
  * @entry: An allocated and populated subsytem struct. We don't use it tho.
  * @buf: The output buffer to write to.
- * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
+ * @knob: The HWP_AUTOBOOT or HWP_AUTOSEARCH flag
  */
 static ssize_t
 pdcs_auto_read(struct subsystem *entry, char *buf, int knob)
@@ -571,7 +572,7 @@ pdcs_auto_read(struct subsystem *entry, char *buf, int knob)
 static inline ssize_t
 pdcs_autoboot_read(struct subsystem *entry, char *buf)
 {
-	return pdcs_auto_read(entry, buf, PF_AUTOBOOT);
+	return pdcs_auto_read(entry, buf, HWP_AUTOBOOT);
 }
 
 /**
@@ -582,7 +583,7 @@ pdcs_autoboot_read(struct subsystem *entry, char *buf)
 static inline ssize_t
 pdcs_autosearch_read(struct subsystem *entry, char *buf)
 {
-	return pdcs_auto_read(entry, buf, PF_AUTOSEARCH);
+	return pdcs_auto_read(entry, buf, HWP_AUTOSEARCH);
 }
 
 /**
@@ -597,19 +598,28 @@ pdcs_timer_read(struct subsystem *entry, char *buf)
 {
 	char *out = buf;
 	struct pdcspath_entry *pathentry;
+	int timer;
 
 	if (!entry || !buf)
 		return -EINVAL;
 
+	/*
+	 * PDC PAT machines use the timer field for something different.
+	 * I don't know where they keep the timer field now ...
+	 */
+	if (is_pdc_pat())
+		return -ENODEV;
+
 	/* Current flags are stored in primary boot path entry */
 	pathentry = &pdcspath_entry_primary;
 
-	/* print the timer value in seconds */
 	read_lock(&pathentry->rw_lock);
-	out += sprintf(out, "%u\n", (pathentry->devpath.flags & PF_TIMER) ?
-				(1 << (pathentry->devpath.flags & PF_TIMER)) : 0);
+	timer = pathentry->devpath.flags & HWP_TIMER;
 	read_unlock(&pathentry->rw_lock);
 
+	/* print the timer value in seconds */
+	out += sprintf(out, "%u\n", timer ? (1 << timer) : 0);
+
 	return out - buf;
 }
 
@@ -751,7 +761,7 @@ pdcs_osdep2_read(struct subsystem *entry, char *buf)
  * @entry: An allocated and populated subsytem struct. We don't use it tho.
  * @buf: The input buffer to read from.
  * @count: The number of bytes to be read.
- * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
+ * @knob: The HWP_AUTOBOOT or HWP_AUTOSEARCH flag
  * 
  * We will call this function to change the current autoboot flag.
  * We expect a precise syntax:
@@ -811,7 +821,7 @@ pdcs_auto_write(struct subsystem *entry, const char *buf, size_t count, int knob
 	write_unlock(&pathentry->rw_lock);
 	
 	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n",
-		(knob & PF_AUTOBOOT) ? "autoboot" : "autosearch",
+		(knob & HWP_AUTOBOOT) ? "autoboot" : "autosearch",
 		(flags & knob) ? "On" : "Off");
 	
 	return count;
@@ -834,7 +844,7 @@ parse_error:
 static inline ssize_t
 pdcs_autoboot_write(struct subsystem *entry, const char *buf, size_t count)
 {
-	return pdcs_auto_write(entry, buf, count, PF_AUTOBOOT);
+	return pdcs_auto_write(entry, buf, count, HWP_AUTOBOOT);
 }
 
 /**
@@ -850,7 +860,7 @@ pdcs_autoboot_write(struct subsystem *entry, const char *buf, size_t count)
 static inline ssize_t
 pdcs_autosearch_write(struct subsystem *entry, const char *buf, size_t count)
 {
-	return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH);
+	return pdcs_auto_write(entry, buf, count, HWP_AUTOSEARCH);
 }
 
 /**
diff --git a/include/asm-parisc/parisc-device.h b/include/asm-parisc/parisc-device.h
index e12624d..feb1861 100644
--- a/include/asm-parisc/parisc-device.h
+++ b/include/asm-parisc/parisc-device.h
@@ -12,6 +12,7 @@ struct parisc_device {
 	int		aux_irq;	/* Some devices have a second IRQ */
 
 	char		hw_path;        /* The module number on this bus */
+	char		cell;		/* Only on PAT machines */
 	unsigned int	num_addrs;	/* some devices have additional address ranges. */
 	unsigned long	*addr;          /* which will be stored here */
  
diff --git a/include/asm-parisc/pdc.h b/include/asm-parisc/pdc.h
index 423c2b8..7f8f4ef 100644
--- a/include/asm-parisc/pdc.h
+++ b/include/asm-parisc/pdc.h
@@ -561,9 +561,10 @@ struct pdc_hpmc_pim_20 { /* PDC_PIM */
 #endif /* __ASSEMBLY__ */
 
 /* flags of the device_path (see below) */
-#define	PF_AUTOBOOT	0x80
-#define	PF_AUTOSEARCH	0x40
-#define	PF_TIMER	0x0F
+#define HWP_AUTOBOOT	0x80
+#define HWP_AUTOSEARCH	0x40
+#define HWP_CELL	0x3F	/* On PAT systems */
+#define HWP_TIMER	0x0F	/* Non-PAT systems */
 
 #ifndef __ASSEMBLY__
 
diff --git a/include/asm-parisc/pdcpat.h b/include/asm-parisc/pdcpat.h
index 47539f1..35882d8 100644
--- a/include/asm-parisc/pdcpat.h
+++ b/include/asm-parisc/pdcpat.h
@@ -192,15 +192,20 @@
 
 #ifdef CONFIG_64BIT
 #define is_pdc_pat()	(PDC_TYPE_PAT == pdc_type)
-extern int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num);
+extern int pdc_pat_get_irt_size(unsigned long *num_entries,
+						unsigned long cell_num);
 extern int pdc_pat_get_irt(void *r_addr, unsigned long cell_num);
+extern int pdc_pat_get_hwpath_from_pci(unsigned long address,
+						struct hardware_path *path);
 #else	/* ! CONFIG_64BIT */
 /* No PAT support for 32-bit kernels...sorry */
 #define is_pdc_pat()	(0)
 #define pdc_pat_get_irt_size(num_entries, cell_numn)	PDC_BAD_PROC
 #define pdc_pat_get_irt(r_addr, cell_num)		PDC_BAD_PROC
+#define pdc_pat_get_hwpath_from_pci(address, path)	PDC_BAD_PROC
 #endif	/* ! CONFIG_64BIT */
 
+#define pdc_address(dev) (dev->bus->number << 16) | (dev->devfn << 8)
 
 struct pdc_pat_cell_num {
 	unsigned long cell_num;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.