aboutsummaryrefslogtreecommitdiff
path: root/drivers/firewire
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-03-27 06:59:59 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2013-04-28 23:36:44 +0200
commit8db491490b88c8b016b41ad56ac980c4cbb06d7a (patch)
treeb8f50183704e6e232fcf076f1ca543338a094b1e /drivers/firewire
parent247fd50b5953d2b07832a07bd1d1c3b8e221fe9e (diff)
downloadlinux-linaro-stable-8db491490b88c8b016b41ad56ac980c4cbb06d7a.tar.gz
firewire: ohci: Check LPS before register access on pci removal
A pci device can be removed while in its suspended state. If the ohci host controller is suspended, the PHY is also in low-power mode and LPS is disabled. If LPS is disabled, most of the host registers aren't accessible, including IntMaskClear. Furthermore, access to these registers when LPS is disabled can cause hard lockups on some hardware. Since interrupts are already disabled in this mode, further action is unnecessary. Test LPS before attempting to write IntMaskClear to disable interrupts. [Stefan R: whitespace changes] Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/ohci.c13
1 files changed, 9 insertions, 4 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 48889353723f..673c8970749e 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -3712,11 +3712,16 @@ static int pci_probe(struct pci_dev *dev,
static void pci_remove(struct pci_dev *dev)
{
- struct fw_ohci *ohci;
+ struct fw_ohci *ohci = pci_get_drvdata(dev);
- ohci = pci_get_drvdata(dev);
- reg_write(ohci, OHCI1394_IntMaskClear, ~0);
- flush_writes(ohci);
+ /*
+ * If the removal is happening from the suspend state, LPS won't be
+ * enabled and host registers (eg., IntMaskClear) won't be accessible.
+ */
+ if (reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS) {
+ reg_write(ohci, OHCI1394_IntMaskClear, ~0);
+ flush_writes(ohci);
+ }
cancel_work_sync(&ohci->bus_reset_work);
fw_core_remove_card(&ohci->card);