Suspend/resume was broken on my 15" MBP (vanilla 2.6.18 with mactel-linux patches)

1) The quirk that re-enabled 'SCI_EN' was only selected on the MacBook.
   I added MacBookPro to the list.

2) On my MBP (and from postings I saw, others experience the same problem)
   suspend seems to freeze.
I found that this is actually not the case but the machine is waiting for the IDE BUSY signal to go clear during suspend. Eventually this times out
   and I get 'hda: drive not ready for command' messages.

It seems that ACPI 'prepare for suspend' does something to the IDE port/bus
   so that it always asserts BUSY [maybe something is powered off].

==> the patch adds a test for 'drive->blocked' and omits the ide_wait_stat() if 'blocked'. This should be OK since in the 'blocked' state no real (hw) commands
   are issued but only PM related 'pseudo' commands.

3) Even with 2) fixed there is another problem: The BUSY assertion of the IDE bus is only reversed by a real suspend/resume cycle but not if the suspend operation is rejected by anything. In this case, BUSY remains asserted and leaves the IDE interface dead (and you have to suffer through endless retries/timeouts).

I found that ACPI 'prepare for suspend' changes ICH7 GPIO pin [14] which is configured as an output and wiggling that bit is directly related with IDE BUSY (i.e., clearing GPIO[14] -> inb(0x1f7) == 0x80, setting GPIO[14] -> inb(0x1f7) == 0x00)

  ==> the patch adds a new ich7_quirk which whacks GPIO[14] from 'finish'

NOTES
- 3) is only enabled for MacbookPro since I can't test on another platform
  - Safety of 2) should be reviewed by somebody else
  - attached patch to be applied over mactel-2.6.18 patch

HTH
-- Till

PS:
- I observed two more suspend/resume issues [not IDE related though but just
    for the record]:
a) proprietary fglrx kernel driver 8.29.6-1 deadlocked on suspend. I discovered that the proprietary firegl_pci_save_state() routine corrupts memory. A patch I posted today on the mactel-linux-users list replacing the proprietary routines by generic linux' pci_save_state/pci_save_restore fixes this (a peek under the hood shows that the proprietary routine doesn't do anything special apart from
          messing up pointers - they really should use the generic ones).
b) if I load the bluetooth stack from /etc/init.d (debian etch) then I sometimes experience suspend rejections; these seem to be caused by the 'device firmware update' interface which is not claimed by any driver. The generic code seems to behave incorrectly
          under some circumstances and reject the suspend.
Strangely, if I start bluetooth after everything is up then suspend/resume work OK.

diff -rcN linux-2.6.18.orig/drivers/acpi/sleep/main.c linux-2.6.18/drivers/acpi/sleep/main.c
*** linux-2.6.18.orig/drivers/acpi/sleep/main.c	2006-10-12 10:58:22.000000000 -0700
--- linux-2.6.18/drivers/acpi/sleep/main.c	2006-10-11 12:27:27.000000000 -0700
***************
*** 35,41 ****
  };
  
  static int init_8259A_after_S1;
! static int ich7_sci_en_quirk_enabled;
  
  /**
   *	acpi_pm_prepare - Do preliminary suspend work.
--- 35,41 ----
  };
  
  static int init_8259A_after_S1;
! static int ich7_quirks;
  
  /**
   *	acpi_pm_prepare - Do preliminary suspend work.
***************
*** 95,102 ****
  	case PM_SUSPEND_MEM:
  		do_suspend_lowlevel();
  
! 		if (ich7_sci_en_quirk_enabled)
  		{
  			int pm1c = inw(0x404);
  			pm1c |= 0x01; /* SCI_EN */
  			outw (pm1c, 0x404);
--- 95,103 ----
  	case PM_SUSPEND_MEM:
  		do_suspend_lowlevel();
  
! 		if (ich7_quirks & 1)
  		{
+ 			/* ICH7 comes out of sleep with SCI_EN off; must whack */
  			int pm1c = inw(0x404);
  			pm1c |= 0x01; /* SCI_EN */
  			outw (pm1c, 0x404);
***************
*** 155,160 ****
--- 156,169 ----
  	/* reset firmware waking vector */
  	acpi_set_firmware_waking_vector((acpi_physical_address) 0);
  
+ 	if ( 2 & ich7_quirks ) {
+ 		/* if sleep was rejected we need to re-set GPIO bit 14
+ 		 *
+ 		 * FIXME: this ugly - we should look up the port number!
+ 		 */
+ 		outl(inl(0x50c) | (1<<14), 0x50c);
+ 	}
+ 
  	if (init_8259A_after_S1) {
  		printk("Broken toshiba laptop -> kicking interrupts\n");
  		init_8259A(0);
***************
*** 202,216 ****
  }
  
  /*
!  * Apple Macbook comes back from sleep with the SCI_EN bit disabled
!  * causing a flood of unacknowledged IRQ9s.  We need to set SCI_EN
!  * as soon as we come back
   */
! static int __init init_ich7_sci_en_quirk(struct dmi_system_id *d)
  {
! 	printk(KERN_WARNING "%s detected (ICH7 SCI_EN quirk enabled)\n",
!                d->ident);
! 	ich7_sci_en_quirk_enabled = 1;
  	return 0;
  }
  
--- 211,247 ----
  }
  
  /*
!  * Apple Macbook (Pro, too) comes back from sleep with the SCI_EN bit
!  * disabled causing a flood of unacknowledged IRQ9s.
!  * We need to set SCI_EN as soon as we come back.
!  *
!  * Apple Macbook Pro (non-pro unknown) has IDE (cdrom) BUSY
!  * all the time after acpi_sleep_prepare() returns. This state
!  * is cleared after a 'real' resume but not if sleep is rejected
!  * by any party.
!  * I (Till Straumann <[EMAIL PROTECTED]>) found that
!  * GPIO bit 14 (which is normally set) is cleared by acpi_sleep_prepare()
!  * and that setting it makes IDE operational again.
!  * Hence I added a quirk to whack the bit from the 'finish' method
!  * if we didn't really sleep.
   */
! static int __init init_ich7_quirks(struct dmi_system_id *d)
  {
! int quirks = (int)d->driver_data;
! char *sep  = "";
! 
! 	ich7_quirks = quirks;
! 
! 	printk(KERN_WARNING "%s detected (quirks: ",d->ident);
! 	if ( 1 & quirks ) {
! 		printk("ICH7 SCI_EN");
! 		sep = ", ";
! 	}
! 	if ( 2 & quirks ) {
! 		printk("%sGPIO[14]_EN", sep);
! 		sep = ", ";
! 	}
! 	printk(" enabled)\n");
  	return 0;
  }
  
***************
*** 222,232 ****
  	 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),},
  	 },
  	{
! 	 .callback = init_ich7_sci_en_quirk,
  	 .ident = "Apple MacBook",
  	 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),},
  	 },
! 	{},
  };
  
  static int __init acpi_sleep_init(void)
--- 253,270 ----
  	 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),},
  	 },
  	{
! 	 .callback = init_ich7_quirks,
  	 .ident = "Apple MacBook",
  	 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),},
+ 	 .driver_data = (void*) 1,
+ 	 },
+ 	{
+ 	 .callback = init_ich7_quirks,
+ 	 .ident = "Apple MacBookPro",
+ 	 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"),},
+ 	 .driver_data = (void*) 3,
  	 },
! 	 {},
  };
  
  static int __init acpi_sleep_init(void)
*** linux-2.6.18.orig/drivers/ide/ide-io.c	2006-10-12 10:58:22.000000000 -0700
--- linux-2.6.18/drivers/ide/ide-io.c	2006-10-07 01:47:58.000000000 -0700
***************
*** 424,430 ****
  		struct request_pm_state *pm = rq->end_io_data;
  #ifdef DEBUG_PM
  		printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",
! 			drive->name, rq->pm->pm_step, stat, err);
  #endif
  		ide_complete_power_step(drive, rq, stat, err);
  		if (pm->pm_step == ide_pm_state_completed)
--- 426,432 ----
  		struct request_pm_state *pm = rq->end_io_data;
  #ifdef DEBUG_PM
  		printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",
! 			drive->name, pm->pm_step, stat, err);
  #endif
  		ide_complete_power_step(drive, rq, stat, err);
  		if (pm->pm_step == ide_pm_state_completed)
***************
*** 1006,1012 ****
  		ide_check_pm_state(drive, rq);
  
  	SELECT_DRIVE(drive);
! 	if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
  		printk(KERN_ERR "%s: drive not ready for command\n", drive->name);
  		return startstop;
  	}
--- 1008,1015 ----
  		ide_check_pm_state(drive, rq);
  
  	SELECT_DRIVE(drive);
! 	/* Don't wait if we are going to sleep; ACPI seems to mark the channel busy; T.S, 2006/09 */
! 	if ( !drive->blocked && ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
  		printk(KERN_ERR "%s: drive not ready for command\n", drive->name);
  		return startstop;
  	}
***************
*** 1021,1027 ****
  			struct request_pm_state *pm = rq->end_io_data;
  #ifdef DEBUG_PM
  			printk("%s: start_power_step(step: %d)\n",
! 				drive->name, rq->pm->pm_step);
  #endif
  			startstop = ide_start_power_step(drive, rq);
  			if (startstop == ide_stopped &&
--- 1024,1030 ----
  			struct request_pm_state *pm = rq->end_io_data;
  #ifdef DEBUG_PM
  			printk("%s: start_power_step(step: %d)\n",
! 				drive->name, pm->pm_step);
  #endif
  			startstop = ide_start_power_step(drive, rq);
  			if (startstop == ide_stopped &&
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Mactel-linux-devel mailing list
Mactel-linux-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mactel-linux-devel

Reply via email to