tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Nvidia MCP67 SATA controller



hi,

the Nvidia MCP67 SATA controller does not work using the viaide driver
(in native pci compatibility mode), as various users have reported.

  Attached is a diff that forces it into AHCI mode and thus attaches
ahcisata.  The method to switch to AHCI mode was taken from the
FreeBSD source tree.

  This works quite well most of the time, but sometimes the chip seems
to get in a strange state after the switch.  It then returns bogus
data when identifying the drive:
        wd0 at atabus1 drive 0: <ST506>
(or similar, written from memory) instead of 
        wd0 at atabus1 drive 0: <Hitachi HDT725032VLA360>
and hangs when trying to read from the disk (still during boot).

  If the boot is successful, the controller is stable.  I've used it
to reconstruct a 320gb raid1.


  Any idea why this sometimes fails?  Any other comments about the
patch (the debug printf()s will be removed)?  I want to commit it once
it's fixed and cleaned up.

Anyone have the datasheet for the MCP67 SATA controller?

                                        thanks,
                                        dillo
Index: ahcisata_pci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/ahcisata_pci.c,v
retrieving revision 1.11
diff -u -r1.11 ahcisata_pci.c
--- ahcisata_pci.c      20 Mar 2008 16:15:57 -0000      1.11
+++ ahcisata_pci.c      18 Jun 2008 17:21:32 -0000
@@ -60,6 +60,9 @@
 static void ahci_pci_attach(device_t, device_t, void *);
 static bool ahci_pci_resume(device_t PMF_FN_PROTO);
 
+static bool ahci_pci_enable_ahci_mode(bus_space_tag_t, bus_space_handle_t,
+                                     uint32_t);
+
 
 CFATTACH_DECL_NEW(ahcisata_pci, sizeof(struct ahci_pci_softc),
     ahci_pci_match, ahci_pci_attach, NULL, NULL);
@@ -71,12 +74,21 @@
        bus_space_tag_t regt;
        bus_space_handle_t regh;
        bus_size_t size;
+       uint32_t ghc;
        int ret = 0;
+       int force_ahci = 0;
+
+       if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA &&
+           PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NVIDIA_MCP67_SATA) {
+               printf("ahcisata: forcing ahci mode for MCP67\n");
+               force_ahci = 1;
+       }
 
        if (PCI_CLASS(pa->pa_class) == PCI_CLASS_MASS_STORAGE &&
            ((PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_SATA &&
             PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_SATA_AHCI) ||
-            PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_RAID)) {
+            PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_RAID ||
+            force_ahci)) {
                /* check if the chip is in ahci mode */
                if (pci_mapreg_map(pa, AHCI_PCI_ABAR,
                    PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0,
@@ -85,8 +97,16 @@
                if (PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_SATA
                    && PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_SATA_AHCI)
                        ret = 3;
-               else if (bus_space_read_4(regt, regh, AHCI_GHC) & AHCI_GHC_AE)
-                       ret = 3;
+               else {
+                       ghc = bus_space_read_4(regt, regh, AHCI_GHC);
+                       printf("ahcisata: ghc=[%08x]\n", (int)ghc);
+                       if (ghc & AHCI_GHC_AE)
+                               ret = 3;
+                       else if (force_ahci) {
+                               if (ahci_pci_enable_ahci_mode(regt, regh, ghc))
+                                       ret = 3;
+                       }
+               }
                bus_space_unmap(regt, regh, size);
                return ret;
        }
@@ -164,3 +184,36 @@
 
        return true;
 }
+
+
+static bool
+ahci_pci_enable_ahci_mode(bus_space_tag_t regt, bus_space_handle_t regh,
+                         uint32_t ghc)
+{
+       printf("ahcisata: putting chip in AHCI mode\n");
+
+       ghc |= AHCI_GHC_AE;
+       bus_space_write_4(regt, regh, AHCI_GHC, ghc);
+
+       ghc |= AHCI_GHC_HR;
+       bus_space_write_4(regt, regh, AHCI_GHC, ghc);
+
+       DELAY(1000000);
+
+       ghc = bus_space_read_4(regt, regh, AHCI_GHC);
+       if (ghc & AHCI_GHC_HR) {
+               printf("ahcisata: reset faild\n");
+               return false;
+       }
+       
+       ghc |= AHCI_GHC_AE;
+       bus_space_write_4(regt, regh, AHCI_GHC, ghc);
+
+       ghc = bus_space_read_4(regt, regh, AHCI_GHC);
+       if ((ghc & AHCI_GHC_AE) == 0) {
+               printf("ahcisata: enabling ahci mode failed\n");
+               return false;
+       }
+
+       return true;
+}


Home | Main Index | Thread Index | Old Index