Skip to contentSkip to conversation

Quirk macbook pro 6,2 into ahci mode

RFC PATCH In review Jens Axboe 6 participants 14 messages Jul 18, 2010 – Jul 19, 2010

Subsystems (2)
lore
Share & tools
lore

Jens Axboe

author
#0 lore raw

RFC PATCH 1/1 Quirk macbook pro 6,2 into ahci mode

Hi,

Recently got one of these laptops, and I've been trying to make it work a bit better in Linux. One thing that is really annoying is that it defaults to PIIX instead of AHCI when not booting OSX (it uses ahci just fine). So I tried to fiddle with this a bit and get it to use ahci, and after reading the intel specs I managed to switch it into ahci mode.

The performance is better (512b reads are 3x as fast) and more importantly it enables the use of ALPM would cuts about 1W of my power consumption. That's at 30 minutes of battery.

So while the below works, it doesn't work when the laptop is coming out of suspend. I can't seem to find out why, since it's a bit hard to debug when you are on the road. What happens is that it comes back with a bit of a delay, and then the disks are apparently gone. I've tried adding a resume quirk to re-AHCI the thing, but it doesn't help.

Looking at the specs, I don't see what I am missing to make this work for resume. I'm assuming that EFI did initialize some bits that am missing when coming out of resume, but I don't know which bits.

Anyone played with this before and has some clues???

drivers/pci/quirks.c +49 -0
index 477345d..3635260 100644
@@ -1020,6 +1020,55 @@ static void quirk_disable_pxb(struct pci_dev *pdev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82454NX,	quirk_disable_pxb);
 DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82454NX,	quirk_disable_pxb);
 
+/*
+ * This enables AHCI mode on Macbook Pro 6,2 and others based on Intels
+ * 5 series / 3400 series chipset.
+ *
+ * Info found here http://www.intel.com/Assets/PDF/datasheet/322169.pdf
+ */
+static void __devinit quirk_pch_ahci(struct pci_dev *pdev)
+{
+	u16 mode;
+
+	/*
+	 * 0x90 is the MAP register offset
+	 */
+	pci_read_config_word(pdev, 0x90, &mode);
+
+	printk("got mode %x\n", mode);
+
+	/*
+	 * MAP.MV must be 0x00 to allow ahci enable
+	 */
+	if (mode & 0x03)
+		return;
+
+	/*
+	 * Already in AHCI mode?
+	 */
+	if (mode & (1 << 6))
+		return;
+
+	/*
+	 * MAP.SMS bit 6 is AHCI mode, MAP.SC bit 5 is port config
+	 */
+	mode = (1 << 6) | (1 << 5);
+	pci_write_config_word(pdev, 0x90, mode);
+
+	/*
+	 * Re-read device id, it has now changed
+	 */
+	pci_read_config_word(pdev, PCI_DEVICE_ID, &pdev->device);
+
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3b28, quirk_pch_ahci);
+
+static void __devinit quirk_pch_ahci_resume(struct pci_dev *pdev)
+{
+	quirk_pch_ahci(pdev);
+}
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x3b29, quirk_pch_ahci_resume);
+
 static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
 {
 	/* set SBX00/Hudson-2 SATA in IDE mode to AHCI mode */
Signature · 1 line
Jens Axboe
#1 lore raw
Quoted 6 lines · Jens Axboe

So while the below works, it doesn't work when the laptop is coming out of suspend. I can't seem to find out why, since it's a bit hard to debug when you are on the road. What happens is that it comes back with a bit of a delay, and then the disks are apparently gone. I've tried adding a resume quirk to re-AHCI the thing, but it doesn't help.

You'll certainly need to re-quirk on resume, since there's no guarantee that the firmware will do it for you. My suspicion is that it comes up in PATA mode with legacy firmware in order to improve compatibility with XP, and you'd have ahci if you booted through EFI. I don't have something handy to test that right now, though.

Quoted 4 lines

Looking at the specs, I don't see what I am missing to make this work for resume. I'm assuming that EFI did initialize some bits that am missing when coming out of resume, but I don't know which bits.

There's no guarantee that the AHCI BAR is programmed by the firmware (it seems to be on the Macs, but won't be on most hardware). Is it getting reprogrammed on resume?

Signature · 1 line
Matthew Garrett | [email protected]
author
#2 lore raw
Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Jens Axboe

So while the below works, it doesn't work when the laptop is coming out of suspend. I can't seem to find out why, since it's a bit hard to debug when you are on the road. What happens is that it comes back with a bit of a delay, and then the disks are apparently gone. I've tried adding a resume quirk to re-AHCI the thing, but it doesn't help.

Quoted 5 lines

You'll certainly need to re-quirk on resume, since there's no guarantee that the firmware will do it for you. My suspicion is that it comes up in PATA mode with legacy firmware in order to improve compatibility with XP, and you'd have ahci if you booted through EFI. I don't have something handy to test that right now, though.

See this is where I'm getting confused, since I'm not entirely sure what the boot process is. If I boot a DVD directly, then it's in piix mode. Booting through refit -> Linux, it's in piix mode. So my logic was that it is probably getting switched into ahci mode by the OSX boot loader or perhaps the driver does it there.

The resume re-quirk makes no difference, so it's likely that it needs a bit more love when it's coming out of that state.

Quoted 8 lines
Quoted 4 lines

Looking at the specs, I don't see what I am missing to make this work for resume. I'm assuming that EFI did initialize some bits that am missing when coming out of resume, but I don't know which bits.

There's no guarantee that the AHCI BAR is programmed by the firmware (it seems to be on the Macs, but won't be on most hardware). Is it getting reprogrammed on resume?

The BARs are fine with just the patch I sent:

Region 0: I/O ports at 3128 [size=8]
Region 1: I/O ports at 313c [size=4]
Region 2: I/O ports at 0170 [size=8]
Region 3: I/O ports at 0374 [size=1]
Region 4: I/O ports at 3020 [size=32]
Region 5: Memory at a2800000 (32-bit, non-prefetchable) [size=2K]

Since everything is screwed on resume and all file systems are gone, there's no easy way for me to printk() and verify anything on resume. So unless anyone has any stellar ideas on what to try, I guess it'll have to wait until I can log some output somehow.

Signature · 1 line
Jens Axboe
#3 lore raw

On Sun, Jul 18, 2010 at 14:34:46 -0600, Jens Axboe wrote:

[...]

Quoted 5 lines

See this is where I'm getting confused, since I'm not entirely sure what the boot process is. If I boot a DVD directly, then it's in piix mode. Booting through refit -> Linux, it's in piix mode. So my logic was that it is probably getting switched into ahci mode by the OSX boot loader or perhaps the driver does it there.

If this helps: I switched from grub-pc to grub-efi on my oldish Mac mini Core 2 Duo (i945 chipset) and the hard disk used AHCI after this. Suspend also works fine. So I would guess that the AHCI/PIIX difference is caused by booting with EFI/legacy DOS bootblock, and the latter is just done for Windows compatibility.

Regards,
Tino

#4 lore raw
In reply to Jens Axboe

On Sun, Jul 18, 2010 at 14:34:46 -0600, Jens Axboe wrote:

[...]

Quoted 5 lines

See this is where I'm getting confused, since I'm not entirely sure what the boot process is. If I boot a DVD directly, then it's in piix mode. Booting through refit -> Linux, it's in piix mode. So my logic was that it is probably getting switched into ahci mode by the OSX boot loader or perhaps the driver does it there.

If this helps: I switched from grub-pc to grub-efi on my oldish Mac mini Core 2 Duo (i945 chipset) and the hard disk used AHCI after this. Suspend also works fine. So I would guess that the AHCI/PIIX difference is caused by booting with EFI/legacy DOS bootblock, and the latter is just done for Windows compatibility.

Regards,
Tino

author
#5 lore raw
Quoted 10 lines · Tino Keitel

On Sun, Jul 18, 2010 at 14:34:46 -0600, Jens Axboe wrote:

[...]

Quoted 5 lines

See this is where I'm getting confused, since I'm not entirely sure what the boot process is. If I boot a DVD directly, then it's in piix mode. Booting through refit -> Linux, it's in piix mode. So my logic was that it is probably getting switched into ahci mode by the OSX boot loader or perhaps the driver does it there.

Quoted 5 lines

If this helps: I switched from grub-pc to grub-efi on my oldish Mac mini Core 2 Duo (i945 chipset) and the hard disk used AHCI after this. Suspend also works fine. So I would guess that the AHCI/PIIX difference is caused by booting with EFI/legacy DOS bootblock, and the latter is just done for Windows compatibility.

Great! That sounds way better. DO you have a pointer to grub-efi? I'm finding some grub2 efi stuff with google, just want to make sure that is it. A grub 0.9x with efi would be preferable, I find grub2 to be horrible to work with in general...

Signature · 1 line
Jens Axboe
#6 lore raw

On Mon, Jul 19, 2010 at 09:49:49 -0600, Jens Axboe wrote:

[...]

Quoted 4 lines

Great! That sounds way better. DO you have a pointer to grub-efi? I'm finding some grub2 efi stuff with google, just want to make sure that is it. A grub 0.9x with efi would be preferable, I find grub2 to be horrible to work with in general...

With grub-efi I meant the EFI support in grub2. AFAIK grub 0.9x never supported EFI.

What information do you need?

Regards,
Tino

author
#7 lore raw
Quoted 9 lines · Tino Keitel

On Mon, Jul 19, 2010 at 09:49:49 -0600, Jens Axboe wrote:

[...]

Quoted 4 lines

Great! That sounds way better. DO you have a pointer to grub-efi? I'm finding some grub2 efi stuff with google, just want to make sure that is it. A grub 0.9x with efi would be preferable, I find grub2 to be horrible to work with in general...

Quoted 4 lines

With grub-efi I meant the EFI support in grub2. AFAIK grub 0.9x never supported EFI.

What information do you need?

Just that, if there was some special grub 0.9x efi variant or whether you meant grub2 when you said grub-efi.

I'll give it a spin here.

Signature · 1 line
Jens Axboe
#8 lore raw
Quoted 10 lines · Jens Axboe
Quoted 9 lines · Tino Keitel

On Mon, Jul 19, 2010 at 09:49:49 -0600, Jens Axboe wrote:

[...]

Quoted 4 lines

Great! That sounds way better. DO you have a pointer to grub-efi? I'm finding some grub2 efi stuff with google, just want to make sure that is it. A grub 0.9x with efi would be preferable, I find grub2 to be horrible to work with in general...

Quoted 7 lines
Quoted 4 lines

With grub-efi I meant the EFI support in grub2. AFAIK grub 0.9x never supported EFI.

What information do you need?

Just that, if there was some special grub 0.9x efi variant or whether you meant grub2 when you said grub-efi.

Fedora's grub 0.9x has efi support.

But that efi support hasn't been propagated to all the other distros since grub 0.9x really doesn't have an upstream.

Mike

author
#9 lore raw
Quoted 16 lines · Mike Snitzer
Quoted 15 lines · Jens Axboe
Quoted 13 lines · Tino Keitel

On Mon, Jul 19, 2010 at 09:49:49 -0600, Jens Axboe wrote:

[...]

Quoted 4 lines

Great! That sounds way better. DO you have a pointer to grub-efi? I'm finding some grub2 efi stuff with google, just want to make sure that is it. A grub 0.9x with efi would be preferable, I find grub2 to be horrible to work with in general...

With grub-efi I meant the EFI support in grub2. AFAIK grub 0.9x never supported EFI.

What information do you need?

Quoted 7 lines
Quoted 2 lines

Just that, if there was some special grub 0.9x efi variant or whether you meant grub2 when you said grub-efi.

Fedora's grub 0.9x has efi support.

But that efi support hasn't been propagated to all the other distros since grub 0.9x really doesn't have an upstream.

This is Fedora 13. What magic do I need to enable the EFI grub bits then?

Signature · 1 line
Jens Axboe
#10 lore raw
In reply to Matthew Garrett
Quoted 9 lines · Matthew Garrett
Quoted 4 lines · Jens Axboe

Looking at the specs, I don't see what I am missing to make this work for resume. I'm assuming that EFI did initialize some bits that am missing when coming out of resume, but I don't know which bits.

There's no guarantee that the AHCI BAR is programmed by the firmware (it seems to be on the Macs, but won't be on most hardware). Is it getting reprogrammed on resume?

That's definitely the key question.

I would have quirked AHCI/piix hardware into AHCI mode long ago, if firmwares defaulting to piix mode would actually program the PCI BAR correctly.

Or, if the firmware had some way to safely allocate a bus memory range, which we could ourselves program into the PCI BAR. Some non-PC platforms can do that.

Jeff

author
#11 lore raw
Quoted 13 lines · Jeff Garzik
Quoted 9 lines · Matthew Garrett
Quoted 4 lines · Jens Axboe

Looking at the specs, I don't see what I am missing to make this work for resume. I'm assuming that EFI did initialize some bits that am missing when coming out of resume, but I don't know which bits.

There's no guarantee that the AHCI BAR is programmed by the firmware (it seems to be on the Macs, but won't be on most hardware). Is it getting reprogrammed on resume?

That's definitely the key question.

Quoted 7 lines

I would have quirked AHCI/piix hardware into AHCI mode long ago, if firmwares defaulting to piix mode would actually program the PCI BAR correctly.

Or, if the firmware had some way to safely allocate a bus memory range, which we could ourselves program into the PCI BAR. Some non-PC platforms can do that.

So I've dug a bit further, and I discovered that the AHCI BAR is indeed not working on resume. I googled and found an older patch from Matthew that hacks it in, but it still failed on resume. But a bit differently, it seemed that now only interrupt delivery was problematic. I disabled msi for this controller, and bingo it now works as expected and suspend/resumes just fine.

The MSI registers were programmed on resume, so I'm quite sure which extra bit needs to be fiddled to ensure that MSI works as well.

The patch I'm testing with now is below, it works but of course it a hack in areas. I'm attaching the output from resume with the debug stuff in, in case it helps anyone.

drivers/ata/ahci.c +4 -0
index f252253..f0f3a9e 100644
@@ -1119,6 +1119,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (ahci_sb600_enable_64bit(pdev))
 		hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
 
+	/* turn off msi on mbp 6,2 ahci mode */
+	if (pdev->device == 0x3b29)
+		hpriv->flags |= AHCI_HFLAG_NO_MSI;
+
 	if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
 		pci_intx(pdev, 1);
 
drivers/pci/probe.c +1 -1
index f4adba2..45e37b9 100644
@@ -118,7 +118,7 @@ static inline unsigned int pci_calc_resource_flags(unsigned int flags)
 	return IORESOURCE_MEM;
 }
 
-static u64 pci_size(u64 base, u64 maxbase, u64 mask)
+u64 pci_size(u64 base, u64 maxbase, u64 mask)
 {
 	u64 size = mask & maxbase;	/* Find the significant bits */
 	if (!size)
drivers/pci/quirks.c +199 -0

205 lines not on this page · raw message

drivers/pci/setup-res.c +53 -0

59 lines not on this page · raw message

include/linux/pci.h +2 -0
index 7cb0084..4385c9d 100644
@@ -849,6 +849,8 @@ void pdev_sort_resources(struct pci_dev *, struct resource_list *);
 int pci_enable_resources(struct pci_dev *, int mask);
 void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
 		    int (*)(struct pci_dev *, u8, u8));
+int pci_allocate_resource(struct pci_dev *dev, int resno);
+u64 pci_size(u64 base, u64 maxbase, u64 mask);
 #define HAVE_PCI_REQ_REGIONS	2
 int __must_check pci_request_regions(struct pci_dev *, const char *);
 int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *);
Signature · 1 line
Jens Axboe

ahci 0000:00:1f.2: restoring config space at offset 0xf (was 0x200, writing 0x20b) ahci 0000:00:1f.2: restoring config space at offset 0xd (was 0x70, writing 0x80) ahci 0000:00:1f.2: restoring config space at offset 0x9 (was 0xffe1, writing 0xa2800000) ahci 0000:00:1f.2: restoring config space at offset 0x8 (was 0xff01, writing 0x3021) ahci 0000:00:1f.2: restoring config space at offset 0x7 (was 0xff2d, writing 0x3139) ahci 0000:00:1f.2: restoring config space at offset 0x6 (was 0xff19, writing 0x3121) ahci 0000:00:1f.2: restoring config space at offset 0x5 (was 0xff25, writing 0x313d) ahci 0000:00:1f.2: restoring config space at offset 0x4 (was 0xff11, writing 0x3129) ahci 0000:00:1f.2: restoring config space at offset 0x2 (was 0x1018b06, writing 0x1060106) ahci 0000:00:1f.2: restoring config space at offset 0x1 (was 0x2b00000, writing 0x2b00007) ahci 0000:00:1f.2: restoring config space at offset 0x0 (was 0x3b288086, writing 0x3b298086) resume:

  BAR 0, res [io  0x3128-0x312f]
  BAR 1, res [io  0x313c-0x313f]
  BAR 2, res [io  0x3120-0x3127]
  BAR 3, res [io  0x3138-0x313b]
  BAR 4, res [io  0x3020-0x303f]

BAR 5, res [mem 0xa2800000-0xa28007ff]
resume-pre:
reg PI(9): 8b
reg SCC(a): 1
reg INT_LN(3c): b
reg INT_PN(3d): 2
reg PMCS(74): 8
reg MSIMC(82): 0
reg MSIMA(84): 0
reg MSIMD(88): 0
reg MAP(90): 3c00
reg PCS(92): 8303
reg SCLKCG(94): 3c000183
reg SCLKGC(9c): 1
boot:
reg PI(9): 1
reg SCC(a): 6
reg INT_LN(3c): b
reg INT_PN(3d): 2
reg PMCS(74): 8
reg MSIMC(82): 0
reg MSIMA(84): 0
reg MSIMD(88): 0
reg MAP(90): 3c60
reg PCS(92): 8303
reg SCLKCG(94): 3c000183
reg SCLKGC(9c): 1
boot:

  BAR 0, res [io  0x3128-0x312f]
  BAR 1, res [io  0x313c-0x313f]
  BAR 2, res [io  0x3120-0x3127]
  BAR 3, res [io  0x3138-0x313b]
  BAR 4, res [io  0x3020-0x303f]

BAR 5, res [mem 0xa2800000-0xa28007ff]
resume-post:
reg PI(9): 1
reg SCC(a): 6
reg INT_LN(3c): b
reg INT_PN(3d): 2
reg PMCS(74): 8
reg MSIMC(82): 0
reg MSIMA(84): 0
reg MSIMD(88): 0
reg MAP(90): 3c60
reg PCS(92): 8303
reg SCLKCG(94): 3c000183
reg SCLKGC(9c): 1
ahci 0000:00:1f.2: BAR 5: can't reserve [mem 0xa2800000-0xa28007ff]
AHCI BAR not set - attempting to program...
ahci 0000:00:1f.2: BAR 5: set to [mem 0xa2800000-0xa28007ff] (PCI address [0xa2800000-0xa28007ff] Succeeded

#12 lore raw

Hello,

Quoted 5 lines · Jens Axboe
drivers/pci/quirks.c +5 -0
+static void pch_update_bar(struct pci_dev *pdev)
+{
+	if (pcim_iomap_regions(pdev, 1 << 5, "AHCI quirk")) {
+		int ret;
+

drivers/pci/quirks.c static void quirk_disable_pxb(struct pci_dev *pdev) · line 1202

Quoted 10 lines
drivers/pci/quirks.c +10 -0
+		printk("AHCI BAR not set - attempting to program... ");
+		pdev->resource[5].flags = IORESOURCE_MEM;
+		ret = pci_allocate_resource(pdev, 5);
+		if (ret)
+			printk (KERN_INFO "Failed to allocate new region\n");
+		else
+			printk (KERN_INFO "Succeeded\n");
+	} else
+		pcim_iounmap_regions(pdev, 1 << 5);
+}

Hmm... as the firmware is setting up the resource during boot, wouldn't it be better to save it and restore it during resume? That would be simpler and more robust.

Thanks.

Signature · 1 line
tejun
#13 lore raw
In reply to Jeff Garzik
Quoted 3 lines · Jeff Garzik

I would have quirked AHCI/piix hardware into AHCI mode long ago, if firmwares defaulting to piix mode would actually program the PCI BAR correctly.

If you perform the fixup early enough then the PCI resource allocation code should take a decent stab at it. I'd feel more comfortable with that once we start using ACPI _CRS by default, since that gives us a much better chance of avoiding conflicting with some bit of motherboard hardware.

Signature · 1 line
Matthew Garrett | [email protected]