Workaround? for hard lock caused by O2 Micro OZ601/6912/711E0

Daniel Ritz daniel.ritz at gmx.ch
Sat Apr 30 20:06:24 EDT 2005


On Sunday 01 May 2005 01:45, Sascha Sommer wrote:
> Hi,
> 
> On Sunday 01 May 2005 00:04, Daniel Ritz wrote:
> > hi
> >
> > good work tracking this down. the check you commented out was added to
> > fix problems on some bridges that don't get a already recognized up again
> > after redoing the interrogation. but the state your bridge initially
> > reports is so wrong that it's easy to see it...the following code in this
> > place should also do the trick while not breaking other setups...
> >
> > 	/* Redo card voltage interrogation */
> > 	state = cb_readl(socket, CB_SOCKET_STATE);
> >         if (!(state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) ||
> >              (state & (CB_CDETECT1 | CB_CDETECT2 | CB_NOTACARD |
> > CB_BADVCCREQ)) || ((state & (CB_16BITCARD | CB_CBCARD)) == (CB_16BITCARD |
> > CB_CBCARD))) cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST);
> >
> 
> Thanks will test that later. However I'm still searching for the real problem 
> as things go wrong a lot earlier. The socket is powered (wihout even a card 
> inserted) before the bridge is enabled... I found out that  

yep, that's what socket state is telling. 

> another difference between cold boot and reboot is that in the first case the
> card is in the D1 powermanagement state while in the second case it is in D3 
> as expected. The pci code assumes that the bridge is in D3cold when calling 

yep, i have a patch for that too...attached (not so new, but should still apply)

> pci_enable_device. So my guess is that it then will restore a bogus state 
> when going to D0
> 
> Sascha
>  

rgds
-daniel

[PATCH] PCI PM: read initial state from device

the PCI PM code tries not to write to the PM registers when there is no change
in state. this however fails when a device is initially set up. and because
some devices are broken they hate being forced to the state they are in. fix
it by reading the current state from the device itself. also does some other
things:
- support PCI PM CAP version 3 (as defined in PCI PM Interface Spec v1.2)
- add and export the function pci_get_power_state() to get it from the device
- minor cleanups

Signed-off-by: Daniel Ritz <daniel.ritz at gmx.ch> 

--- 1.151/include/linux/pci.h	2005-02-03 19:08:22 +01:00
+++ edited/include/linux/pci.h	2005-04-09 00:49:42 +02:00
@@ -501,6 +501,7 @@
 #define PCI_D2	((pci_power_t __force) 2)
 #define PCI_D3hot	((pci_power_t __force) 3)
 #define PCI_D3cold	((pci_power_t __force) 4)
+#define PCI_UNKNOWN	((pci_power_t __force) 5)
 
 /*
  * The pci_dev structure is used to describe PCI devices.
@@ -822,6 +823,7 @@
 /* Power management related routines */
 int pci_save_state(struct pci_dev *dev);
 int pci_restore_state(struct pci_dev *dev);
+pci_power_t pci_get_power_state(struct pci_dev *dev);
 int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
 pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable);
--- 1.81/drivers/pci/pci.c	2005-03-03 08:17:57 +01:00
+++ edited/drivers/pci/pci.c	2005-04-09 01:02:15 +02:00
@@ -227,6 +227,28 @@
 }
 
 /**
+ * pci_get_power_state - Gets the power state of a PCI device
+ * @dev: PCI device
+ *
+ * RETURN VALUE:
+ * pci_power_t the current state
+ */
+pci_power_t pci_get_power_state(struct pci_dev *dev)
+{
+	int pm;
+	u16 pmcsr;
+
+	pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+	if (!pm)
+		return PCI_UNKNOWN;
+
+	pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+	pmcsr &= PCI_PM_CTRL_STATE_MASK;
+
+	return (pci_power_t) pmcsr;
+}
+
+/**
  * pci_set_power_state - Set the power state of a PCI device
  * @dev: PCI device to be suspended
  * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
@@ -268,7 +290,7 @@
 		return -EIO; 
 
 	pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
-	if ((pmc & PCI_PM_CAP_VER_MASK) > 2) {
+	if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
 		printk(KERN_DEBUG
 		       "PCI: %s has unsupported PM cap regs version (%u)\n",
 		       pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
@@ -276,12 +298,10 @@
 	}
 
 	/* check if this device supports the desired state */
-	if (state == PCI_D1 || state == PCI_D2) {
-		if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
-			return -EIO;
-		else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
-			return -EIO;
-	}
+	if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
+		return -EIO;
+	else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+		return -EIO;
 
 	/* If we're in D3, force entire word to 0.
 	 * This doesn't affect PME_Status, disables PME_En, and
@@ -830,6 +850,7 @@
 EXPORT_SYMBOL(pci_assign_resource);
 EXPORT_SYMBOL(pci_find_parent_resource);
 
+EXPORT_SYMBOL(pci_get_power_state);
 EXPORT_SYMBOL(pci_set_power_state);
 EXPORT_SYMBOL(pci_save_state);
 EXPORT_SYMBOL(pci_restore_state);
--- 1.80/drivers/pci/probe.c	2005-02-03 03:21:51 +01:00
+++ edited/drivers/pci/probe.c	2005-04-09 00:57:21 +02:00
@@ -563,7 +563,7 @@
 	    dev->devfn, dev->vendor, dev->device, class, dev->hdr_type);
 
 	/* "Unknown power state" */
-	dev->current_state = 4;
+	dev->current_state = PCI_UNKNOWN;
 
 	/* Early fixups, before probing the BARs */
 	pci_fixup_device(pci_fixup_early, dev);
@@ -608,6 +608,9 @@
 		       pci_name(dev), class, dev->hdr_type);
 		dev->class = PCI_CLASS_NOT_DEFINED;
 	}
+
+	/* get the real power state from the device */
+	dev->current_state = pci_get_power_state(dev);
 
 	/* We found a fine healthy device, go go go... */
 	return 0;





More information about the linux-pcmcia mailing list