Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 13715
b: refs/heads/master
c: 8b553f3
h: refs/heads/master
i:
  13713: 8995e5d
  13711: 889c901
v: v3
  • Loading branch information
Linas Vepstas authored and Paul Mackerras committed Nov 10, 2005
1 parent ceb7780 commit ab228fb
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6dee3fb94004c43ce09f6bf5e7c0b778ec5b8cc8
refs/heads/master: 8b553f32db3bf5d0ec0819c595932eb21cd45945
115 changes: 114 additions & 1 deletion trunk/arch/powerpc/platforms/pseries/eeh.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
*/
#define EEH_MAX_FAILS 100000

/* Misc forward declaraions */
static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn);

/* RTAS tokens */
static int ibm_set_eeh_option;
static int ibm_set_slot_reset;
Expand Down Expand Up @@ -366,6 +369,7 @@ static void pci_addr_cache_remove_device(struct pci_dev *dev)
*/
void __init pci_addr_cache_build(void)
{
struct device_node *dn;
struct pci_dev *dev = NULL;

if (!eeh_subsystem_enabled)
Expand All @@ -379,6 +383,10 @@ void __init pci_addr_cache_build(void)
continue;
}
pci_addr_cache_insert_device(dev);

/* Save the BAR's; firmware doesn't restore these after EEH reset */
dn = pci_device_to_OF_node(dev);
eeh_save_bars(dev, PCI_DN(dn));
}

#ifdef DEBUG
Expand Down Expand Up @@ -775,6 +783,108 @@ rtas_set_slot_reset(struct pci_dn *pdn)
}
}

/* ------------------------------------------------------- */
/** Save and restore of PCI BARs
*
* Although firmware will set up BARs during boot, it doesn't
* set up device BAR's after a device reset, although it will,
* if requested, set up bridge configuration. Thus, we need to
* configure the PCI devices ourselves.
*/

/**
* __restore_bars - Restore the Base Address Registers
* Loads the PCI configuration space base address registers,
* the expansion ROM base address, the latency timer, and etc.
* from the saved values in the device node.
*/
static inline void __restore_bars (struct pci_dn *pdn)
{
int i;

if (NULL==pdn->phb) return;
for (i=4; i<10; i++) {
rtas_write_config(pdn, i*4, 4, pdn->config_space[i]);
}

/* 12 == Expansion ROM Address */
rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]);

#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)])

rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1,
SAVED_BYTE(PCI_CACHE_LINE_SIZE));

rtas_write_config (pdn, PCI_LATENCY_TIMER, 1,
SAVED_BYTE(PCI_LATENCY_TIMER));

/* max latency, min grant, interrupt pin and line */
rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);
}

/**
* eeh_restore_bars - restore the PCI config space info
*
* This routine performs a recursive walk to the children
* of this device as well.
*/
void eeh_restore_bars(struct pci_dn *pdn)
{
struct device_node *dn;
if (!pdn)
return;

if (! pdn->eeh_is_bridge)
__restore_bars (pdn);

dn = pdn->node->child;
while (dn) {
eeh_restore_bars (PCI_DN(dn));
dn = dn->sibling;
}
}

/**
* eeh_save_bars - save device bars
*
* Save the values of the device bars. Unlike the restore
* routine, this routine is *not* recursive. This is because
* PCI devices are added individuallly; but, for the restore,
* an entire slot is reset at a time.
*/
static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn)
{
int i;

if (!pdev || !pdn )
return;

for (i = 0; i < 16; i++)
pci_read_config_dword(pdev, i * 4, &pdn->config_space[i]);

if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
pdn->eeh_is_bridge = 1;
}

void
rtas_configure_bridge(struct pci_dn *pdn)
{
int token = rtas_token ("ibm,configure-bridge");
int rc;

if (token == RTAS_UNKNOWN_SERVICE)
return;
rc = rtas_call(token,3,1, NULL,
pdn->eeh_config_addr,
BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid));
if (rc) {
printk (KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n",
rc, pdn->node->full_name);
}
}

/* ------------------------------------------------------------- */
/* The code below deals with enabling EEH for devices during the
* early boot sequence. EEH must be enabled before any PCI probing
Expand Down Expand Up @@ -977,6 +1087,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_early);
void eeh_add_device_late(struct pci_dev *dev)
{
struct device_node *dn;
struct pci_dn *pdn;

if (!dev || !eeh_subsystem_enabled)
return;
Expand All @@ -987,9 +1098,11 @@ void eeh_add_device_late(struct pci_dev *dev)

pci_dev_get (dev);
dn = pci_device_to_OF_node(dev);
PCI_DN(dn)->pcidev = dev;
pdn = PCI_DN(dn);
pdn->pcidev = dev;

pci_addr_cache_insert_device (dev);
eeh_save_bars(dev, pdn);
}
EXPORT_SYMBOL_GPL(eeh_add_device_late);

Expand Down
23 changes: 23 additions & 0 deletions trunk/include/asm-powerpc/ppc-pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@ extern int pci_read_irq_line(struct pci_dev *pci_dev);
*/
void rtas_set_slot_reset (struct pci_dn *);

/**
* eeh_restore_bars - Restore device configuration info.
*
* A reset of a PCI device will clear out its config space.
* This routines will restore the config space for this
* device, and is children, to values previously obtained
* from the firmware.
*/
void eeh_restore_bars(struct pci_dn *);

/**
* rtas_configure_bridge -- firmware initialization of pci bridge
*
* Ask the firmware to configure all PCI bridges devices
* located behind the indicated node. Required after a
* pci device reset. Does essentially the same hing as
* eeh_restore_bars, but for brdges, and lets firmware
* do the work.
*/
void rtas_configure_bridge(struct pci_dn *);

int rtas_write_config(struct pci_dn *, int where, int size, u32 val);

#endif

#endif /* _ASM_POWERPC_PPC_PCI_H */

0 comments on commit ab228fb

Please sign in to comment.