Commit bf14a58c authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] parisc: Fix LBA/SBA bugs

 - Fix Astro/Elroy LMMIO space reporting in /proc/iomem.
 - Output in /proc/iomem include PCI bus number and "Extra LMMIO" range.
 - LBA now reports proper addresses routed down from SBA.
 - add mercury_cfg_ops. This fixes the tg3 init delay problem.
   PDC took ~20ms per PCI cfg write. tg3 performs ~1600 writes (~30 seconds
   per NIC) during init. Alternative is the relocate PDC to host ram.
   But that would burn ~4MB of RAM. This is cheaper.
 - add PCI_F_EXTEND to asm/pci.h - replaces private definitions
Committed-by: default avatarGrant Grundler <grundler@parisc-linux.org>
parent f71c555d
This diff is collapsed.
......@@ -168,11 +168,21 @@ extern struct proc_dir_entry * proc_mckinley_root;
#define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */
#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */
/*
** Offsets into MBIB (Function 0 on Ike and hopefully Astro)
** Firmware programs this stuff. Don't touch it.
*/
#define LMMIO_DIRECT0_BASE 0x300
#define LMMIO_DIRECT0_MASK 0x308
#define LMMIO_DIRECT0_ROUTE 0x310
#define LMMIO_DIST_BASE 0x360
#define LMMIO_DIST_MASK 0x368
#define LMMIO_DIST_ROUTE 0x370
#define IOS_DIST_BASE 0x390
#define IOS_DIST_MASK 0x398
#define IOS_DIST_ROUTE 0x3A0
......@@ -290,6 +300,9 @@ struct sba_device {
unsigned int flags; /* state/functionality enabled */
unsigned int hw_rev; /* HW revision of chip */
struct resource chip_resv; /* MMIO reserved for chip */
struct resource iommu_resv; /* MMIO reserved for iommu */
unsigned int num_ioc; /* number of on-board IOC's */
struct ioc ioc[MAX_IOC];
};
......@@ -570,7 +583,7 @@ sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted)
while(res_ptr < res_end)
{
DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr);
ASSERT(0 != mask);
BUG_ON(0 == mask);
if(0 == ((*res_ptr) & mask)) {
*res_ptr |= mask; /* mark resources busy! */
pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
......@@ -1697,19 +1710,46 @@ sba_hw_init(struct sba_device *sba_dev)
} /* if !PLUTO */
if (IS_ASTRO(sba_dev->iodc)) {
int err;
/* PAT_PDC (L-class) also reports the same goofy base */
sba_dev->ioc[0].ioc_hpa = ASTRO_IOC_OFFSET;
num_ioc = 1;
sba_dev->chip_resv.name = "Astro Intr Ack";
sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfef00000UL;
sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff000000UL - 1) ;
err = request_resource(&iomem_resource, &(sba_dev->chip_resv));
if (err < 0) {
BUG();
}
} else if (IS_PLUTO(sba_dev->iodc)) {
int err;
/* We use a negative value for IOC HPA so it gets
* corrected when we add it with IKE's IOC offset.
* Doesnt look clean, but fewer code.
*/
sba_dev->ioc[0].ioc_hpa = -PLUTO_IOC_OFFSET;
num_ioc = 1;
sba_dev->chip_resv.name = "Pluto Intr/PIOP/VGA";
sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfee00000UL;
sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff200000UL - 1);
err = request_resource(&iomem_resource, &(sba_dev->chip_resv));
BUG_ON(err < 0);
sba_dev->iommu_resv.name = "IOVA Space";
sba_dev->iommu_resv.start = 0x40000000UL;
sba_dev->iommu_resv.end = 0x50000000UL - 1;
err = request_resource(&iomem_resource, &(sba_dev->iommu_resv));
BUG_ON(err < 0);
} else {
/* IS_IKE (ie N-class, L3000, L1500) */
sba_dev->ioc[0].ioc_hpa = sba_dev->ioc[1].ioc_hpa = 0;
num_ioc = 2;
/* TODO - LOOKUP Ike/Stretch chipset mem map */
}
sba_dev->num_ioc = num_ioc;
......@@ -1844,8 +1884,9 @@ static int sba_proc_info(char *buf, char **start, off_t offset, int len)
struct sba_device *sba_dev = sba_list;
struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */
int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */
unsigned long i;
#ifdef SBA_COLLECT_STATS
unsigned long i = 0, avg = 0, min, max;
unsigned long avg = 0, min, max;
#endif
sprintf(buf, "%s rev %d.%d\n",
......@@ -1861,6 +1902,21 @@ static int sba_proc_info(char *buf, char **start, off_t offset, int len)
sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n",
buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */
sprintf(buf, "%sLMMIO_BASE/MASK/ROUTE %08x %08x %08x\n",
buf,
READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_BASE),
READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_MASK),
READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_ROUTE)
);
for (i=0; i<4; i++)
sprintf(buf, "%sDIR%ld_BASE/MASK/ROUTE %08x %08x %08x\n",
buf, i,
READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_BASE + i*0x18),
READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_MASK + i*0x18),
READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_ROUTE + i*0x18)
);
#ifdef SBA_COLLECT_STATS
sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf,
total_pages - ioc->used_pages, ioc->used_pages,
......@@ -1950,7 +2006,7 @@ static struct parisc_driver sba_driver = {
};
/*
** Determine if lba should claim this chip (return 0) or not (return 1).
** Determine if sba should claim this chip (return 0) or not (return 1).
** If so, initialize the chip and tell other partners in crime they
** have work to do.
*/
......@@ -2063,17 +2119,95 @@ void __init sba_init(void)
* sba_get_iommu - Assign the iommu pointer for the pci bus controller.
* @dev: The parisc device.
*
* This function searches through the registerd IOMMU's and returns the
* appropriate IOMMU data for the given parisc PCI controller.
* Returns the appropriate IOMMU data for the given parisc PCI controller.
* This is cached and used later for PCI DMA Mapping.
*/
void * sba_get_iommu(struct parisc_device *pci_hba)
{
struct sba_device *sba = (struct sba_device *) pci_hba->parent->sysdata;
char t = pci_hba->parent->id.hw_type;
struct parisc_device *sba_dev = parisc_parent(pci_hba);
struct sba_device *sba = sba_dev->dev.driver_data;
char t = sba_dev->id.hw_type;
int iocnum = (pci_hba->hw_path >> 3); /* rope # */
BUG_ON((t != HPHW_IOA) && (t != HPHW_BCPORT));
return &(sba->ioc[iocnum]);
}
/**
* sba_directed_lmmio - return first directed LMMIO range routed to rope
* @pa_dev: The parisc device.
* @r: resource PCI host controller wants start/end fields assigned.
*
* For the given parisc PCI controller, determine if any direct ranges
* are routed down the corresponding rope.
*/
void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r)
{
struct parisc_device *sba_dev = parisc_parent(pci_hba);
struct sba_device *sba = sba_dev->dev.driver_data;
char t = sba_dev->id.hw_type;
int i;
int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */
if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT))
BUG();
return &(sba->ioc[iocnum]);
r->start = r->end = 0;
/* Astro has 4 directed ranges. Not sure about Ike/Pluto/et al */
for (i=0; i<4; i++) {
int base, size;
unsigned long reg = sba->sba_hpa + i*0x18;
base = READ_REG32(reg + LMMIO_DIRECT0_BASE);
if ((base & 1) == 0)
continue; /* not enabled */
size = READ_REG32(reg + LMMIO_DIRECT0_ROUTE);
if ((size & (ROPES_PER_IOC-1)) != rope)
continue; /* directed down different rope */
r->start = (base & ~1UL) | PCI_F_EXTEND;
size = ~ READ_REG32(reg + LMMIO_DIRECT0_MASK);
r->end = r->start + size;
}
}
/**
* sba_distributed_lmmio - return portion of distributed LMMIO range
* @pa_dev: The parisc device.
* @r: resource PCI host controller wants start/end fields assigned.
*
* For the given parisc PCI controller, return portion of distributed LMMIO
* range. The distributed LMMIO is always present and it's just a question
* of the base address and size of the range.
*/
void sba_distributed_lmmio(struct parisc_device *pci_hba, struct resource *r )
{
struct parisc_device *sba_dev = parisc_parent(pci_hba);
struct sba_device *sba = sba_dev->dev.driver_data;
char t = sba_dev->id.hw_type;
int base, size;
int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */
if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT))
BUG();
r->start = r->end = 0;
base = READ_REG32(sba->sba_hpa + LMMIO_DIST_BASE);
if ((base & 1) == 0) {
BUG(); /* Gah! Distr Range wasn't enabled! */
return;
}
r->start = (base & ~1UL) | PCI_F_EXTEND;
size = (~READ_REG32(sba->sba_hpa + LMMIO_DIST_MASK)) / ROPES_PER_IOC;
r->start += rope * (size + 1); /* adjust base for this rope */
r->end = r->start + size;
}
......@@ -4,6 +4,8 @@
#include <linux/config.h>
#include <asm/scatterlist.h>
/*
** HP PCI platforms generally support multiple bus adapters.
** (workstations 1-~4, servers 2-~32)
......@@ -35,6 +37,7 @@ struct pci_hba_data {
struct resource lmmio_space; /* bus addresses < 4Gb */
struct resource elmmio_space; /* additional bus addresses < 4Gb */
struct resource gmmio_space; /* bus addresses > 4Gb */
/* NOTE: Dino code assumes it can use *all* of the lmmio_space,
* elmmio_space and gmmio_space as a contiguous array of
* resources. This #define represents the array size */
......@@ -43,6 +46,12 @@ struct pci_hba_data {
unsigned long lmmio_space_offset; /* CPU view - PCI view */
void * iommu; /* IOMMU this device is under */
/* REVISIT - spinlock to protect resources? */
#define HBA_NAME_SIZE 16
char io_name[HBA_NAME_SIZE];
char lmmio_name[HBA_NAME_SIZE];
char elmmio_name[HBA_NAME_SIZE];
char gmmio_name[HBA_NAME_SIZE];
};
#define HBA_DATA(d) ((struct pci_hba_data *) (d))
......@@ -60,13 +69,36 @@ struct pci_hba_data {
#define PCI_PORT_HBA(a) ((a) >> HBA_PORT_SPACE_BITS)
#define PCI_PORT_ADDR(a) ((a) & (HBA_PORT_SPACE_SIZE - 1))
#if CONFIG_PARISC64
#define PCI_F_EXTEND 0xffffffff00000000UL
#define PCI_IS_LMMIO(hba,a) pci_is_lmmio(hba,a)
/* We need to know if an address is LMMMIO or GMMIO.
* LMMIO requires mangling and GMMIO we must use as-is.
*/
static __inline__ int pci_is_lmmio(struct pci_hba_data *hba, unsigned long a)
{
return(((a) & PCI_F_EXTEND) == PCI_F_EXTEND);
}
/*
** Convert between PCI (IO_VIEW) addresses and processor (PA_VIEW) addresses.
** Note that we currently support only LMMIO.
** See pcibios.c for more conversions used by Generic PCI code.
*/
#define PCI_BUS_ADDR(hba,a) ((a) - hba->lmmio_space_offset)
#define PCI_BUS_ADDR(hba,a) (PCI_IS_LMMIO(hba,a) \
? ((a) - hba->lmmio_space_offset) /* mangle LMMIO */ \
: (a)) /* GMMIO */
#define PCI_HOST_ADDR(hba,a) ((a) + hba->lmmio_space_offset)
#else /* !CONFIG_PARISC64 */
#define PCI_BUS_ADDR(hba,a) (a)
#define PCI_HOST_ADDR(hba,a) (a)
#define PCI_F_EXTEND 0UL
#define PCI_IS_LMMIO(hba,a) (1) /* 32-bit doesn't support GMMIO */
#endif /* !CONFIG_PARISC64 */
/*
** KLUGE: linux/pci.h include asm/pci.h BEFORE declaring struct pci_bus
** (This eliminates some of the warnings).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment