Stephen,

You want to get ATA/PI APM fixed?
"The Linux 'original' IDE guy' Mark Lord showed Alan what was trying to
bang over everyone's head, without success.

Here is his sample code for cs5530 chipset.

Look at this and comment.  I have part of the space setup to complete the
APM extenstion calls.  This will get you and me out of a jam with laptops.

LT, you in for the patches or tank the laptops until 2.5?

Andre Hedrick
The Linux ATA/IDE guy
--- linux-2.2.18pre15/drivers/block/cs5530.c.orig       Wed May  3 20:16:33 2000
+++ linux-2.2.18pre15/drivers/block/cs5530.c    Mon Oct 16 20:46:11 2000
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/cs5530.c                Version 0.4     Feb 6, 2000
+ * linux/drivers/block/cs5530.c                Version 0.6     Oct 16, 2000
  *
  * Copyright (C) 2000                  Mark Lord <[EMAIL PROTECTED]>
  * May be copied or modified under the terms of the GNU General Public License
@@ -20,6 +20,24 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 
+#ifdef CONFIG_APM
+/*
+ * This driver now supports APM SUSPEND/RESUME actions,
+ * for which it saves/restores chipset/drive timings.
+ *
+ * BUT.. this code may be slightly unsafe to use since
+ * there is no knowlege of APM in the higher level IDE code.
+ *
+ * The risk is that there may be I/O in progress when
+ * our RESUME function is invoked.. and we'll basically toast it.
+ * The high level code will eventually time out on its IRQ wait,
+ * and likely report a "lost interrupt" after assuming the
+ * operation completed successfully.  Ugh.
+ */
+#include <linux/apm_bios.h>
+static int cs5530_apm_callback(apm_event_t ae);
+#endif
+
 #ifndef SETFEATURES_XFER
        #define CS5530_2_2_KERNEL
 #endif
@@ -351,7 +369,7 @@
 /*
  * Initialize the cs5530 bridge for reliable IDE DMA operation.
  */
-unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name)
+unsigned int pci_init_cs5530 (struct pci_dev *dev, const char *name)
 {
        struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
        unsigned short pcicmd = 0;
@@ -429,6 +447,20 @@
        return 0;
 }
 
+#ifdef CONFIG_APM
+/*
+ * UGLY:  There really ought to be a way to give/receive
+ * a device tag of some kind for APM callbacks.
+ *
+ * As it stands, we haven't a friggin' clue which hwif
+ * we are being invoked on behalf of.  UGLY.
+ *
+ * So.. we have this UGLY global variable to keep track of it.
+ * There can only be two cs5530 hwifs in the hardware.
+ */
+static ide_hwif_t  *cs5530_hwifs[2] = {NULL,NULL};
+#endif /* CONFIG_APM */
+
 /*
  * This gets invoked by the IDE driver once for each channel,
  * and performs channel-specific pre-initialization before drive probing.
@@ -442,6 +474,17 @@
                (void) pci_init_cs5530 (hwif->pci_dev, "CS5530");
        }
 #endif /* CS5530_2_2_KERNEL */
+#ifdef CONFIG_APM
+       static int apm_registered = 0;
+
+       cs5530_hwifs[hwif->index] = hwif;       /* remember this HWIF for callbacks */
+       if (!apm_registered) {
+               apm_registered = 1;
+               if (apm_register_callback(cs5530_apm_callback)) {
+                       printk(KERN_WARNING "%s: apm suspend/resume might not work\n", 
+hwif->name);
+               }
+       }
+#endif /* CONFIG_APM */
        if (hwif->mate)
                hwif->serialized = hwif->mate->serialized = 1;
        if (!hwif->dma_base) {
@@ -465,3 +508,108 @@
                }
        }
 }
+
+#ifdef CONFIG_APM
+int cs5530_apm_callback (apm_event_t ae)
+{
+       static unsigned int saved_regs[2][4] = {{0,0,0,0},{0,0,0,0}};
+       static int saved_state = 0;
+       int h, d, r;
+       
+       /*
+        * UGLY:  There really ought to be a way to give/receive
+        * a device tag of some kind for APM callbacks.
+        *
+        * As it stands, we haven't a friggin' clue which hwif
+        * we are being invoked on behalf of.  UGLY.
+        */
+       switch(ae) {
+               case APM_SYS_SUSPEND: 
+               case APM_CRITICAL_SUSPEND: 
+               case APM_USER_SUSPEND: 
+                       for (h = 0; h < 2; ++h) {
+                               ide_hwif_t *hwif = cs5530_hwifs[h];
+                               if (hwif) {
+                                       unsigned int basereg = CS5530_BASEREG(hwif);
+                                       for (r = 0; r < 4; ++r) {
+                                               saved_regs[h][r] = inl(basereg + 
+(r<<2));
+                                       }
+                                       printk("%s: saved chipset timings\n", 
+hwif->name);
+                               }
+                       }
+                       saved_state = 1;
+                       break;
+
+               case APM_NORMAL_RESUME: 
+               case APM_CRITICAL_RESUME: 
+               case APM_STANDBY_RESUME: 
+                       if (saved_state) {
+                               /*
+                                * Restore chipset registers:
+                                */
+                               int already_done = 0;
+                               for (h = 0; h < 2; ++h) {
+                                       ide_hwif_t *hwif = cs5530_hwifs[h];
+                                       if (hwif) {
+                                               unsigned int basereg = 
+CS5530_BASEREG(hwif);
+                                               if (!already_done++)
+                                                       (void) pci_init_cs5530 
+(hwif->pci_dev, "CS5530");
+                                               for (r = 0; r < 4; ++r) {
+                                                       outl(saved_regs[h][r], basereg 
++ (r<<2));
+                                               }
+                                               printk("%s: restored chipset 
+timings\n", hwif->name);
+                                       }
+                               }
+                               /*
+                                * Re-program drive PIO modes
+                                */
+                               for (h = 0; h < 2; ++h) {
+                                       ide_hwif_t *hwif = cs5530_hwifs[h];
+                                       if (hwif) {
+                                               unsigned int format = 
+(saved_regs[h][1] >> 31) & 1;
+                                               for (d = 0; d < 2; ++d) {
+                                                       ide_drive_t *drive = 
+&(hwif->drives[d]);
+                                                       if (drive->present) {
+                                                               unsigned int pio, 
+timings;
+                                                               timings = 
+saved_regs[h][d<<1];
+                                                               for (pio = 0; pio <= 
+4; ++pio) {
+                                                                       if 
+(cs5530_pio_timings[format][pio] == timings)
+                                                                               break;
+                                                               }
+                                                               if  (pio > 4)
+                                                                       pio = 255; /* 
+autotune */
+                                                               
+(void)cs5530_tuneproc(drive, pio);
+                                                       }
+                                               }
+                                       }
+                               }
+                               /*
+                                * Re-program drive DMA modes
+                                */
+                               for (h = 0; h < 2; ++h) {
+                                       ide_hwif_t *hwif = cs5530_hwifs[h];
+                                       if (hwif && hwif->dmaproc != NULL) {
+                                               for (d = 0; d < MAX_DRIVES; ++d) {
+                                                       ide_drive_t *drive = 
+&(hwif->drives[d]);
+                                                       if (drive->present) {
+                                                               int was_using_dma = 
+drive->using_dma;
+                                                               
+(void)cs5530_dmaproc(ide_dma_off_quietly, drive);
+                                                               
+(void)cs5530_dmaproc(ide_dma_check, drive); 
+                                                               if (!was_using_dma && 
+drive->using_dma) {
+                                                                       
+(void)cs5530_dmaproc(ide_dma_off_quietly, drive);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               saved_state = 0;
+                       }
+                       break;
+
+               case APM_POWER_STATUS_CHANGE:
+               default:
+                       break;
+       }
+       return 0;
+}
+#endif

Reply via email to