[parisc-linux] Using PAT_IO calls for PCI config space reads and
writes.
Naresh Kumar
knaresh at india.hp.com
Tue Jan 27 01:39:38 MST 2004
Hi all,
When I was trying to bring up PA-Linux-2.4 on some of the newer boxes, I
discovered that reads/writes from the PCI config space were failing (
system crashes and hangs during the boot). Currently, reads and writes
to the config space happen through memory loads and stores to PCI config
addresses directly (lba_cfg_[read|write]##size in lba_pci.c ). Grant
Grundler advised me to use PDC_PAT_IO calls instead, for PAT based
systems, since they are more reliable and take care of border cases on
newer systems. I have made the changes and tested them on an L-Class
system. I am posting the diff of the files I have changed. The changes
have been made to three files:
1. arch/parisc/kernel/firmware.c - Rev 1.47
2. arch/parisc/kernel/lba_pci.c - Rev 1.54
3. include/asm-parisc/pdc.h - Rev 1.48
Kindly let me know your comments:
--------------------START------------------------------------------------------------------------------
--- lba_pci.c.1.54 Fri Jan 23 15:47:41 2004
+++ lba_pci.c.modified Fri Jan 23 15:53:15 2004
@@ -504,6 +504,13 @@ lba_rd_cfg(struct lba_device *d, u32 tok
return(data);
}
+#ifdef __LP64__
+#define PAT_CFG_READ(a,b,c) pdc_pat_io_pci_cfg_read(a,b,c)
+#define PAT_CFG_WRITE(a,b,c) pdc_pat_io_pci_cfg_write(a,b,c)
+#else
+#define PAT_CFG_READ(a,b,c)
+#define PAT_CFG_WRITE(a,b,c)
+#endif
#define LBA_CFG_RD(size, mask) \
static int lba_cfg_read##size (struct pci_dev *dev, int pos, u##size
*data) \
@@ -512,6 +519,21 @@ static int lba_cfg_read##size (struct pc
u32 local_bus = (dev->bus->parent == NULL) ? 0 :
dev->bus->secondary; \
u32 tok = LBA_CFG_TOK(local_bus,dev->devfn); \
\
+ if (is_pdc_pat()) { \
+ int ret; \
+ tok = LBA_CFG_TOK(dev->bus->number,dev->devfn); \
+ ret = PAT_CFG_READ((tok | pos ), \
+ sizeof(u##size), (u##size *)
data); \
+ if ( ret == 0 ) { \
+ DBG_CFG("%s(%s+%2x) -> 0x%x (c)\n",
__FUNCTION__, dev->slot_name, pos, *data
); \
+ return(*data == (u##size) -1); \
+ } else { \
+ DBG_CFG("LBA: CFG read failed: ret = %d,
d->hba.base_addr = 0x%lx\n", \
+ ret, d->hba.base_addr );
\
+ return (1); \
+ } \
+ } \
+ \
/* FIXME: B2K/C3600 workaround is always use old method... */ \
/* if (!LBA_TR4PLUS(d) && !LBA_SKIP_PROBE(d)) */ { \
/* original - Generate config cycle on broken elroy \
@@ -611,6 +633,11 @@ static int lba_cfg_write##size (struct p
} \
\
DBG_CFG("%s(%s+%2x) = 0x%x (c)\n", __FUNCTION__, dev->slot_name,
pos, data); \
+ if (is_pdc_pat()) { \
+ tok = LBA_CFG_TOK(dev->bus->number,dev->devfn); \
+ PAT_CFG_WRITE((tok | pos ), sizeof(u##size), (u##size
*)&data); \
+ return 0; \
+ } \
/* Basic Algorithm */ \
LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); \
WRITE_REG##size(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos
& mask)); \
-------------------------------------END---------------------------------------------------------------
------------------------------------START-------------------------------------------------------------
--- firmware.c.1.47 Fri Jan 23 16:57:51 2004
+++ firmware.c.modified Tue Jan 27 12:32:43 2004
@@ -1035,6 +1035,46 @@ int pdc_pat_pd_get_addr_map(unsigned lon
return retval;
}
+
+/**
+ * pdc_pat_io_pci_cfg_read - Read PCI configuration space.
+ * @pci_addr: PCI configuration space address for which the read
request is being made.
+ * @pci_size: Size of read in bytes. Valid values are 1, 2, and 4.
+ * @mem_addr: Pointer to return memory buffer.
+ *
+ */
+int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, void
*mem_addr)
+{
+ int retval;
+ spin_lock_irq(&pdc_lock);
+ retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_READ,
__pa(pdc_result),
+ pci_addr, pci_size);
+ memcpy((char *)mem_addr, (char *) ((char *)pdc_result +
(sizeof(unsigned long) - pci_size))
, pci_size);
+ spin_unlock_irq(&pdc_lock);
+
+ return retval;
+}
+
+/**
+ * pdc_pat_io_pci_cfg_write - Retrieve information about memory address
ranges.
+ * @pci_addr: PCI configuration space address for which the write
request is being made.
+ * @pci_size: Size of write in bytes. Valid values are 1, 2, and 4.
+ * @value: Pointer to 1, 2, or 4 byte value in low order end of
argument to be
+ * written to PCI Config space.
+ *
+ */
+int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, void
*value)
+{
+ int retval;
+ unsigned long *val_ptr;
+ spin_lock_irq(&pdc_lock);
+ memcpy((char *)((char *)val_ptr + (sizeof(unsigned long) -
pci_size)), (char *)value, pci_si
ze);
+ retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_WRITE,
pci_addr,
+ pci_size, *val_ptr);
+ spin_unlock_irq(&pdc_lock);
+
+ return retval;
+}
#endif /* __LP64__ */
-------------------------------------END---------------------------------------------------------------
------------------------------------START-------------------------------------------------------------
--- pdc.h.1.48 Fri Jan 23 16:57:52 2004
+++ pdc.h.modified Fri Jan 23 12:21:46 2004
@@ -972,6 +972,8 @@ int pdc_pat_get_irt_size(unsigned long *
int pdc_pat_get_irt(void *r_addr, unsigned long cell_num);
int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr,
unsigned long count, unsigned long offset);
+int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, void
*mem_addr);
+int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, void
*value);
/******************************************************************** *
*PDC_PAT_CELL[Return Cell Module] memaddr[0] conf_base_addr
-------------------------------------END---------------------------------------------------------------
A couple of questions:
1. In the definition of 'pdc_pat_io_pci_cfg_read( )' and
'pdc_pat_io_pci_cfg_write( )' above, can I use 'cpu_to_le64( )' kind of
function instead of ordering the bytes manually in the 'memcpy( )'?
2. Can these changes be propagated to 2.6 also?
Thanks,
Naresh.
More information about the parisc-linux
mailing list