diff options
author | Keck, David <david.keck@amd.com> | 2006-01-16 16:22:36 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-31 21:00:12 -0500 |
commit | 53044f357448693f218cc4f053affe92ed414f9d (patch) | |
tree | dabb2a73762270027d72828a055ba1dd243860af /drivers/pci | |
parent | 3c0c6441883be7676b795939e268b90d6acab360 (diff) |
[PATCH] PCI Hotplug: shpchp: AMD POGO errata fix
This patch fixes the AMD POGO errata on the hotplug controller where the
platform will lock up or reboot if PERR/SERR generation is enabled and a
slot is sent an enable command. This fix disables PERR/SERR generation
before a slot is sent the enable command by first saving related
registers, turning off SERR/PERR generation, enabling the slot, then
restoring the registers.
Signed-off-by: David Keck <david.keck@amd.com>
Cc: Kristen Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/shpchp.h | 94 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_ctrl.c | 12 |
2 files changed, 105 insertions, 1 deletions
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index ce0e9b6ce833..7d6f521d02ea 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h | |||
@@ -95,6 +95,7 @@ struct controller { | |||
95 | u8 function; | 95 | u8 function; |
96 | u8 slot_device_offset; | 96 | u8 slot_device_offset; |
97 | u8 add_support; | 97 | u8 add_support; |
98 | u32 pcix_misc2_reg; /* for amd pogo errata */ | ||
98 | enum pci_bus_speed speed; | 99 | enum pci_bus_speed speed; |
99 | u32 first_slot; /* First physical slot number */ | 100 | u32 first_slot; /* First physical slot number */ |
100 | u8 slot_bus; /* Bus where the slots handled by this controller sit */ | 101 | u8 slot_bus; /* Bus where the slots handled by this controller sit */ |
@@ -113,6 +114,26 @@ struct hotplug_params { | |||
113 | 114 | ||
114 | /* Define AMD SHPC ID */ | 115 | /* Define AMD SHPC ID */ |
115 | #define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 | 116 | #define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 |
117 | #define PCI_DEVICE_ID_AMD_POGO_7458 0x7458 | ||
118 | |||
119 | /* AMD PCIX bridge registers */ | ||
120 | |||
121 | #define PCIX_MEM_BASE_LIMIT_OFFSET 0x1C | ||
122 | #define PCIX_MISCII_OFFSET 0x48 | ||
123 | #define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80 | ||
124 | |||
125 | /* AMD PCIX_MISCII masks and offsets */ | ||
126 | #define PERRNONFATALENABLE_MASK 0x00040000 | ||
127 | #define PERRFATALENABLE_MASK 0x00080000 | ||
128 | #define PERRFLOODENABLE_MASK 0x00100000 | ||
129 | #define SERRNONFATALENABLE_MASK 0x00200000 | ||
130 | #define SERRFATALENABLE_MASK 0x00400000 | ||
131 | |||
132 | /* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */ | ||
133 | #define PERR_OBSERVED_MASK 0x00000001 | ||
134 | |||
135 | /* AMD PCIX_MEM_BASE_LIMIT masks */ | ||
136 | #define RSE_MASK 0x40000000 | ||
116 | 137 | ||
117 | #define INT_BUTTON_IGNORE 0 | 138 | #define INT_BUTTON_IGNORE 0 |
118 | #define INT_PRESENCE_ON 1 | 139 | #define INT_PRESENCE_ON 1 |
@@ -333,6 +354,79 @@ static inline int wait_for_ctrl_irq (struct controller *ctrl) | |||
333 | return retval; | 354 | return retval; |
334 | } | 355 | } |
335 | 356 | ||
357 | static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot) | ||
358 | { | ||
359 | u32 pcix_misc2_temp; | ||
360 | |||
361 | /* save MiscII register */ | ||
362 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp); | ||
363 | |||
364 | p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp; | ||
365 | |||
366 | /* clear SERR/PERR enable bits */ | ||
367 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; | ||
368 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; | ||
369 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; | ||
370 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; | ||
371 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; | ||
372 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); | ||
373 | } | ||
374 | |||
375 | static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot) | ||
376 | { | ||
377 | u32 pcix_misc2_temp; | ||
378 | u32 pcix_bridge_errors_reg; | ||
379 | u32 pcix_mem_base_reg; | ||
380 | u8 perr_set; | ||
381 | u8 rse_set; | ||
382 | |||
383 | /* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */ | ||
384 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg); | ||
385 | perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK; | ||
386 | if (perr_set) { | ||
387 | dbg ("%s W1C: Bridge_Errors[ PERR_OBSERVED = %08X]\n",__FUNCTION__ , perr_set); | ||
388 | |||
389 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set); | ||
390 | } | ||
391 | |||
392 | /* write-one-to-clear Memory_Base_Limit[ RSE ] */ | ||
393 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg); | ||
394 | rse_set = pcix_mem_base_reg & RSE_MASK; | ||
395 | if (rse_set) { | ||
396 | dbg ("%s W1C: Memory_Base_Limit[ RSE ]\n",__FUNCTION__ ); | ||
397 | |||
398 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set); | ||
399 | } | ||
400 | /* restore MiscII register */ | ||
401 | pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp ); | ||
402 | |||
403 | if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK) | ||
404 | pcix_misc2_temp |= SERRFATALENABLE_MASK; | ||
405 | else | ||
406 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; | ||
407 | |||
408 | if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK) | ||
409 | pcix_misc2_temp |= SERRNONFATALENABLE_MASK; | ||
410 | else | ||
411 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; | ||
412 | |||
413 | if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK) | ||
414 | pcix_misc2_temp |= PERRFLOODENABLE_MASK; | ||
415 | else | ||
416 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; | ||
417 | |||
418 | if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK) | ||
419 | pcix_misc2_temp |= PERRFATALENABLE_MASK; | ||
420 | else | ||
421 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; | ||
422 | |||
423 | if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK) | ||
424 | pcix_misc2_temp |= PERRNONFATALENABLE_MASK; | ||
425 | else | ||
426 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; | ||
427 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); | ||
428 | } | ||
429 | |||
336 | #define SLOT_NAME_SIZE 10 | 430 | #define SLOT_NAME_SIZE 10 |
337 | 431 | ||
338 | static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) | 432 | static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) |
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index 25ccb0e47593..643252d9bf3b 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c | |||
@@ -894,7 +894,17 @@ int shpchp_enable_slot (struct slot *p_slot) | |||
894 | dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save); | 894 | dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save); |
895 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 895 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
896 | 896 | ||
897 | rc = board_added(p_slot); | 897 | if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || |
898 | (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) | ||
899 | && p_slot->ctrl->num_slots == 1) { | ||
900 | /* handle amd pogo errata; this must be done before enable */ | ||
901 | amd_pogo_errata_save_misc_reg(p_slot); | ||
902 | rc = board_added(p_slot); | ||
903 | /* handle amd pogo errata; this must be done after enable */ | ||
904 | amd_pogo_errata_restore_misc_reg(p_slot); | ||
905 | } else | ||
906 | rc = board_added(p_slot); | ||
907 | |||
898 | if (rc) { | 908 | if (rc) { |
899 | p_slot->hpc_ops->get_adapter_status(p_slot, | 909 | p_slot->hpc_ops->get_adapter_status(p_slot, |
900 | &(p_slot->presence_save)); | 910 | &(p_slot->presence_save)); |