Re: RFC, RFT: AHCI driver reorganization (was: Re: ATA subsystem lost drive after resume process)

From: Søren Schmidt <sos_at_FreeBSD.ORG>
Date: Mon, 21 Jul 2008 12:55:58 +0200
Hi

Just a short notice from here, I'm on vacation and cannot look into  
this for about 2 weeks.

This does massive changes to the way AHCI devices are treated, so it  
will need testing on all the AHCI platforms currently supported to  
make sure nothing breaks. The adding of PM learned me this is a  
critical part to touch, ouch :)

At any rate, fixing the suspend / resume problems should be dealt with  
in a more generic manner, not just for AHCI, by splitting the work  
done by the _chipinit and _allocate functions so that just the chipset  
specifics can be restored on resume, not the allocations etc.

I'm in doubt as to what makes most sense todo, I'm spare time limitted  
still, so progress here is slow, heck my WIP on NCQ support is still  
just that and touches the same code parts in ACHI so they willl get  
even more behind, oh well...

I'm starting to wonder if I should just let it go and leave ATA to its  
own faith, and get on with other things...

laters...

-Søren

On 21Jul, 2008, at 12:35 , Andrey V. Elsukov wrote:

> Sergey G Nasonov wrote:
>> to disk. So the basic issue preventing normal suspend/resume
>> process on modern Lenovo laptops is ata subsystem. Does anyone can
>> help with this problem? I can test any path or provide additional
>> info.
>
> Hi, All.
>
> I wrote patch for AHCI driver and Sergey tested it.
> So, He reported that now "suspend/resume" works on his laptop.
> I'll try to describe all changes which my patch makes.
>
> 1. Initialization:
>
>  * Global AHCI reset code moved to ata_ahci_reset_controller()
>    function (it will be reused in ata_ahci_resume).
>
>  * Detection of implemented ports moved to ata_ahci_allocate()
>    function (i think it isn't needed to detect it on each reset).
>
>  * From ata_ahci_allocate() function removed all working code,
>    only initialization code is here.
>
> 2. Resetting:
>
>  * ata_ahci_reset() function reorganized and splitted to several
>    functions:
>     ata_ahci_stop_channel() - stop all port activity;
>     ata_ahci_fre_stop() - disable FIS receiving;
>     ata_ahci_fre_start() - setup work areas and enable FIS receiving;
>     ata_ahci_clo_enable() - enable command list override.
>
>  * working code from ata_ahci_allocate moved to ata_ahci_reset.
>
>  * CLO shall only be set immediately prior to setting the PxCMD.ST
>    bit to '1' (from AHCI spec).
>
>  * Software shall not set PxCMD.ST to 1 until it is determined that a
>    functional device is present on the port (from AHCI spec).
>
>  * removed hack when didn't detect signature asuming disk device (it
>    may broke some systems, but it needs testing).
>
> 3. Interrupts handling:
>
>  * Call ata_sata_phy_check_events() only when PHY changing detected.
>
>  * Fatal error handling changed.
>
> 4. Suspend/resume:
>
>  * Added new methods to atapci(4) driver
>
>  * Added suspend/resume implementation for AHCI:
>    + Software must disable interrupts prior to requesting a
>      transition of the HBA to the D3 state.
>    + Reset controller and enable interrupts before resume.
>
>
> So, any comments and suggestions are welcome.
>
> -- 
> WBR, Andrey V. Elsukov
>
> Index: src/sys/dev/ata/ata-all.h
> ===================================================================
> RCS file: /ncvs/src/sys/dev/ata/ata-all.h,v
> retrieving revision 1.133
> diff -u -b -p -r1.133 ata-all.h
> --- src/sys/dev/ata/ata-all.h	17 Apr 2008 12:29:35 -0000	1.133
> +++ src/sys/dev/ata/ata-all.h	21 Jul 2008 09:23:31 -0000
> _at__at_ -188,6 +188,9 _at__at_
> #define         ATA_AHCI_P_IX_HBF       0x20000000
> #define         ATA_AHCI_P_IX_TFE       0x40000000
> #define         ATA_AHCI_P_IX_CPD       0x80000000
> +#define         ATA_AHCI_P_IX_FE \
> +                        (ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |\
> +                         ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF)
>
> #define ATA_AHCI_P_CMD                  0x118
> #define         ATA_AHCI_P_CMD_ST       0x00000001
> Index: src/sys/dev/ata/ata-chipset.c
> ===================================================================
> RCS file: /ncvs/src/sys/dev/ata/ata-chipset.c,v
> retrieving revision 1.224
> diff -u -b -p -r1.224 ata-chipset.c
> --- src/sys/dev/ata/ata-chipset.c	10 Jul 2008 21:36:53 -0000	1.224
> +++ src/sys/dev/ata/ata-chipset.c	21 Jul 2008 09:23:31 -0000
> _at__at_ -62,6 +62,9 _at__at_ static int ata_sata_connect(struct ata_c
> static void ata_sata_setmode(device_t dev, int mode);
> static int ata_request2fis_h2d(struct ata_request *request, u_int8_t  
> *fis);
> static int ata_ahci_chipinit(device_t dev);
> +static int ata_ahci_suspend(device_t dev);
> +static int ata_ahci_resume(device_t dev);
> +static int ata_ahci_reset_controller(device_t dev);
> static int ata_ahci_allocate(device_t dev);
> static int ata_ahci_status(device_t dev);
> static int ata_ahci_begin_transaction(struct ata_request *request);
> _at__at_ -69,6 +72,11 _at__at_ static int ata_ahci_end_transaction(stru
> static int ata_ahci_pm_read(device_t dev, int port, int reg,  
> u_int32_t *result);
> static int ata_ahci_pm_write(device_t dev, int port, int reg,  
> u_int32_t result);
> static u_int32_t ata_ahci_softreset(device_t dev, int port);
> +static void ata_ahci_fre_start(struct ata_channel *ch);
> +static void ata_ahci_fre_stop(struct ata_channel *ch);
> +static void ata_ahci_stop_channel(struct ata_channel *ch);
> +static void ata_ahci_restart_channel(struct ata_channel *ch);
> +static void ata_ahci_clo_enable(struct ata_channel *ch);
> static void ata_ahci_reset(device_t dev);
> static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs,  
> int nsegs, int error);
> static void ata_ahci_dmainit(device_t dev);
> _at__at_ -582,6 +590,29 _at__at_ ata_ahci_ident(device_t dev)
> }
>
> static int
> +ata_ahci_reset_controller(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr = device_get_softc(dev);
> +
> +    /* enable AHCI mode */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
> +
> +    /* reset AHCI controller */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_HR);
> +    DELAY(1000000);
> +    if (ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) {
> +	bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr- 
> >r_res2);
> +	device_printf(dev, "AHCI controller reset failure\n");
> +	return ENXIO;
> +    }
> +
> +    /* reenable AHCI mode */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
> +
> +    return 0;
> +}
> +
> +static int
> ata_ahci_chipinit(device_t dev)
> {
>     struct ata_pci_controller *ctlr = device_get_softc(dev);
> _at__at_ -602,20 +633,8 _at__at_ ata_ahci_chipinit(device_t dev)
>     else
> 	device_printf(dev, "AHCI called from vendor specific driver\n");
>
> -    /* enable AHCI mode */
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
> -
> -    /* reset AHCI controller */
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_HR);
> -    DELAY(1000000);
> -    if (ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) {
> -	bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr- 
> >r_res2);
> -	device_printf(dev, "AHCI controller reset failure\n");
> +    if (ata_ahci_reset_controller(dev))
> 	return ENXIO;
> -    }
> -
> -    /* reenable AHCI mode */
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
>
>     /* get the number of HW channels */
>     ctlr->channels =
> _at__at_ -633,6 +652,8 _at__at_ ata_ahci_chipinit(device_t dev)
>     ctlr->dmainit = ata_ahci_dmainit;
>     ctlr->allocate = ata_ahci_allocate;
>     ctlr->setmode = ata_sata_setmode;
> +    ctlr->resume = ata_ahci_resume;
> +    ctlr->suspend = ata_ahci_suspend;
>
>     /* enable PCI interrupt */
>     pci_write_config(dev, PCIR_COMMAND,
> _at__at_ -655,9 +676,13 _at__at_ ata_ahci_allocate(device_t dev)
> {
>     struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(dev));
>     struct ata_channel *ch = device_get_softc(dev);
> -    u_int64_t work;
>     int offset = ch->unit << 7;
>
> +    if (!(ATA_INL(ctlr->r_res2, ATA_AHCI_PI) & (1 << ch->unit))) {
> +	device_printf(dev, "port not implemented\n");
> +	return ENXIO;
> +    }
> +
>     /* set the SATA resources */
>     ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
>     ch->r_io[ATA_SSTATUS].offset = ATA_AHCI_P_SSTS + offset;
> _at__at_ -676,30 +701,45 _at__at_ ata_ahci_allocate(device_t dev)
>     ch->hw.pm_read = ata_ahci_pm_read;
>     ch->hw.pm_write = ata_ahci_pm_write;
>
> -    /* setup work areas */
> -    work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work &  
> 0xffffffff);
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
> +    return 0;
> +}
>
> -    work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work &  
> 0xffffffff);
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
> +static int
> +ata_ahci_suspend(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr = device_get_softc(dev);
>
> -    /* enable wanted port interrupts */
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
> -	     (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
> -	      ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
> -	      ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
> -	      ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
> -	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
> +    /* XXX: PxCMD.ST must be cleared to '0' before entry into the
> +     * D3 power state.
> +     */
>
> -    /* enable FIS based switching */
> -    //ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBS + offset, 0x00000003);
> +    /* Software must disable interrupts (GHC.IE must be cleared to 0)
> +     * prior to requesting a transition of the HBA to the D3 state.
> +     */
>
> -    /* start operations on this channel */
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> -	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
> -	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
> +	     ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & (~ATA_AHCI_GHC_IE));
> +
> +    return 0;
> +}
> +
> +static int
> +ata_ahci_resume(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr = device_get_softc(dev);
> +
> +    /* reset controller */
> +    if (ata_ahci_reset_controller(dev))
> +	return ENXIO; /* XXX */
> +
> +    /* clear interrupts */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, ATA_INL(ctlr->r_res2,  
> ATA_AHCI_IS));
> +
> +    /* enable AHCI interrupts */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
> +	     ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_IE);
> +
> +    /* next part will be done by ata_resume */
>     return 0;
> }
>
> _at__at_ -716,38 +756,24 _at__at_ ata_ahci_status(device_t dev)
> 	u_int32_t cstatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
>
> 	/* clear interrupt(s) */
> -	ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action & (1 << ch->unit));
> 	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, istatus);
> +	ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action & (1 << ch->unit));
>
> 	/* do we have any PHY events ? */
> -	/* XXX SOS check istatus phy bits */
> +	if (istatus & (ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC))
> 	ata_sata_phy_check_events(dev);
>
> 	/* do we have a potentially hanging engine to take care of? */
> 	/* XXX SOS what todo on NCQ */
> -	if ((istatus & 0x78400050) && (cstatus & 1)) {
> -
> -	    u_int32_t cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
> -	    int timeout = 0;
> -
> -	    /* kill off all activity on this channel */
> -	    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> -		     cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
> -
> -	    /* XXX SOS this is not entirely wrong */
> -	    do {
> -		DELAY(1000);
> -		if (timeout++ > 1000) {
> -		    device_printf(dev, "stopping AHCI engine failed\n");
> -		    break;
> -		}
> -    	    } while (ATA_INL(ctlr->r_res2,
> -			     ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
> -
> -	    /* start operations on this channel */
> -	    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> -		     cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
> -
> +	if ((istatus & ATA_AHCI_P_IX_FE) && (cstatus & 1)) {
> +	    if (bootverbose)
> +		device_printf(dev, "PHY fatal error: PxIS = 0x%08x\n",
> +			    istatus);
> +	    /* To recover fatal errors, the port must be restarted;
> +	     * the port is restarted by clearing PxCMD.ST to '0' and
> +	     * then setting PxCMD.ST to '1'.
> +	     */
> +	    ata_ahci_restart_channel(ch);
> 	    return 1;
> 	}
> 	else
> _at__at_ -998,46 +1024,107 _at__at_ ata_ahci_pm_write(device_t dev, int port
>     return (ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset) >> 8) &  
> 0xff;
> }
>
> +/* CLO shall only be set immediately prior to setting
> + * the PxCMD.ST bit to '1' from a previous value of '0'
> + */
> static void
> -ata_ahci_restart(device_t dev)
> +ata_ahci_clo_enable(struct ata_channel *ch)
> {
> -    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(dev));
> -    struct ata_channel *ch = device_get_softc(dev);
> -    u_int32_t cmd;
> +    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(ch->dev));
>     int offset = ch->unit << 7;
> -    int timeout;
> +    int timeout = 0;
> +    u_int32_t cmd;
>
> -    /* kill off all activity on this channel */
> +    if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
>     cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
>     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> -	     cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
> -
> -    /* XXX SOS this is not entirely wrong */
> -    timeout = 0;
> +			cmd | ATA_AHCI_P_CMD_CLO);
>     do {
> 	DELAY(1000);
> 	if (timeout++ > 1000) {
> -	    device_printf(dev, "stopping AHCI engine failed\n");
> +		device_printf(ch->dev, "executing CLO failed\n");
> 	    break;
> 	}
> +        } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset)
> +			& ATA_AHCI_P_CMD_CLO);
>     }
> -    while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &  
> ATA_AHCI_P_CMD_CR);
> +}
> +
> +/* When PxCMD.FR and PxCMD.FRE are both cleared to '0', software  
> may update
> + * the values of PxFB and PxFBU. Prior to setting PxCMD.FRE to '1',  
> software
> + * shall ensure that PxFB and PxFBU are set to valid values.  
> Software shall
> + * not write PxFB and PxFBU while PxCMD.FRE is set to '1'.
> + */
> +static void
> +ata_ahci_fre_start(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t offset = ch->unit << 7;
> +    u_int64_t work;
> +
> +    /* setup work areas */
> +    work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work &  
> 0xffffffff);
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
> +
> +    work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work &  
> 0xffffffff);
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
> +
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> +	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) |  
> ATA_AHCI_P_CMD_FRE);
> +}
> +
> +static void
> +ata_ahci_fre_stop(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t offset = ch->unit << 7;
> +    int timeout = 0;
> +
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> +	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &  
> (~ATA_AHCI_P_CMD_FRE));
>
> -    /* issue Command List Override if supported */
> -    if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
> -	cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
> -	cmd |= ATA_AHCI_P_CMD_CLO;
> -	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd);
> -	timeout = 0;
> 	do {
> 	    DELAY(1000);
> 	    if (timeout++ > 1000) {
> -		device_printf(dev, "executing CLO failed\n");
> +	    device_printf(ch->dev, "ata_ahci_fre_stop failed\n");
> 		break;
> 	    }
> +    } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &  
> ATA_AHCI_P_CMD_FR);
> +}
> +
> +static void
> +ata_ahci_stop_channel(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t cmd, offset = ch->unit << 7;
> +    int timeout = 0;
> +
> +    cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
> +    if ((cmd & (ATA_AHCI_P_CMD_ST | ATA_AHCI_P_CMD_CR)) == 0)
> +	return;
> +
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> +		    cmd & (~ATA_AHCI_P_CMD_ST));
> +    do {
> +	DELAY(1000);
> +	if (timeout++ > 1000) {
> +	    device_printf(ch->dev, "ata_ahci_stop_channel failed\n");
> +	    break;
>         }
> -	while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD 
> +offset)&ATA_AHCI_P_CMD_CLO);
> -    }
> +    } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &  
> ATA_AHCI_P_CMD_CR);
> +}
> +
> +
> +static void
> +ata_ahci_restart_channel(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(ch->dev));
> +    int offset = ch->unit << 7;
> +
> +    /* kill off all activity on this channel */
> +    ata_ahci_stop_channel(ch);
>
>     /* clear SATA error register */
>     ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
> _at__at_ -1046,11 +1133,12 _at__at_ ata_ahci_restart(device_t dev)
>     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
> 	     ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
>
> +    /* issue Command List Override if supported */
> +    ata_ahci_clo_enable(ch);
> +
>     /* start operations on this channel */
>     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> -	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
> -	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST)
> -	     | (ch->devices & ATA_PORTMULTIPLIER ? ATA_AHCI_P_CMD_PMA : 0));
> +	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) |  
> ATA_AHCI_P_CMD_ST);
> }
>
> static u_int32_t
> _at__at_ -1065,7 +1153,7 _at__at_ ata_ahci_softreset(device_t dev, int por
> 	(struct ata_ahci_cmd_tab *)(ch->dma.work + ATA_AHCI_CT_OFFSET);
>
>     /* kick controller into sane state if needed */
> -    ata_ahci_restart(dev);
> +    ata_ahci_restart_channel(ch);
>
>     /* pull reset active */
>     bzero(ctp->cfis, 64);
> _at__at_ -1094,7 +1182,7 _at__at_ ata_ahci_softreset(device_t dev, int por
> #endif
>     do {
> 	    DELAY(1000);
> -	    if (timeout++ > 1000) {
> +	    if (timeout++ > 5000) {
> 		device_printf(dev, "still BUSY after softreset\n");
> 		break;
> 	    }
> _at__at_ -1110,19 +1198,35 _at__at_ ata_ahci_reset(device_t dev)
> {
>     struct ata_pci_controller *ctlr =  
> device_get_softc(device_get_parent(dev));
>     struct ata_channel *ch = device_get_softc(dev);
> -    u_int32_t signature;
> +    u_int32_t signature, offset = ch->unit << 7;
> +    ch->devices = 0;
>
> -    if (!(ATA_INL(ctlr->r_res2, ATA_AHCI_PI) & (1 << ch->unit))) {
> -	device_printf(dev, "port not implemented\n");
> -	return;
> -    }
> +    /* kill off all activity on this channel */
> +    ata_ahci_stop_channel(ch);
> +    ata_ahci_fre_stop(ch);
> +
> +    /* setup work areas and enable FIS receiving */
> +    ata_ahci_fre_start(ch);
> +
> +    /* clear SATA error register */
> +    ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
> +
> +    /* clear port interrupts */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
> +		ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, (1 << ch->unit));
>
> -    ata_ahci_restart(dev);
> +    /* enable wanted port interrupts */
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
> +	     (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
> +	      ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
> +	      ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
> +	      ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
> +	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
>
>     if (!ata_sata_phy_reset(dev)) {
> 	if (bootverbose)
> 	    device_printf(dev, "phy reset found no device\n");
> -	ch->devices = 0;
> 	return;
>     }
>
> _at__at_ -1146,13 +1250,24 _at__at_ ata_ahci_reset(device_t dev)
>     case 0xeb140101:
> 	ch->devices = ATA_ATAPI_MASTER;
> 	break;
> -    default: /* SOS XXX */
> +    default:
> 	if (bootverbose)
> 	    device_printf(dev, "No signature, asuming disk device\n");
> -	ch->devices = ATA_ATA_MASTER;
>     }
>     if (bootverbose)
>         device_printf(dev, "ahci_reset devices=%08x\n", ch->devices);
> +
> +    /* Software shall not set PxCMD.ST to 1 until it is determined
> +     * that a functional device is present on the port.
> +     */
> +    if (ch->devices) {
> +        /* issue Command List Override if supported */
> +        ata_ahci_clo_enable(ch);
> +	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> +		ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) |  
> ATA_AHCI_P_CMD_SUD |
> +		      ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD |  
> ATA_AHCI_P_CMD_ST |
> +		      (ch->devices & ATA_PORTMULTIPLIER ? ATA_AHCI_P_CMD_PMA : 0));
> +    }
> }
>
> static void
> Index: src/sys/dev/ata/ata-pci.c
> ===================================================================
> RCS file: /ncvs/src/sys/dev/ata/ata-pci.c,v
> retrieving revision 1.128
> diff -u -b -p -r1.128 ata-pci.c
> --- src/sys/dev/ata/ata-pci.c	11 Jun 2008 06:44:58 -0000	1.128
> +++ src/sys/dev/ata/ata-pci.c	21 Jul 2008 09:23:31 -0000
> _at__at_ -262,6 +262,32 _at__at_ ata_pci_detach(device_t dev)
>     return 0;
> }
>
> +int
> +ata_pci_suspend(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr = device_get_softc(dev);
> +    int error = 0;
> +
> +    bus_generic_suspend(dev);
> +    if (ctlr->suspend)
> +	error = ctlr->suspend(dev);
> +
> +    return error;
> +}
> +
> +int
> +ata_pci_resume(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr = device_get_softc(dev);
> +    int error = 0;
> +
> +    if (ctlr->resume)
> +	error = ctlr->resume(dev);
> +    bus_generic_resume(dev);
> +
> +    return error;
> +}
> +
> struct resource *
> ata_pci_alloc_resource(device_t dev, device_t child, int type, int  
> *rid,
> 		       u_long start, u_long end, u_long count, u_int flags)
> _at__at_ -556,8 +582,8 _at__at_ static device_method_t ata_pci_methods[]
>     DEVMETHOD(device_attach,            ata_pci_attach),
>     DEVMETHOD(device_detach,            ata_pci_detach),
>     DEVMETHOD(device_shutdown,          bus_generic_shutdown),
> -    DEVMETHOD(device_suspend,           bus_generic_suspend),
> -    DEVMETHOD(device_resume,            bus_generic_resume),
> +    DEVMETHOD(device_suspend,           ata_pci_suspend),
> +    DEVMETHOD(device_resume,            ata_pci_resume),
>
>     /* bus methods */
>     DEVMETHOD(bus_alloc_resource,       ata_pci_alloc_resource),
> Index: src/sys/dev/ata/ata-pci.h
> ===================================================================
> RCS file: /ncvs/src/sys/dev/ata/ata-pci.h,v
> retrieving revision 1.89
> diff -u -b -p -r1.89 ata-pci.h
> --- src/sys/dev/ata/ata-pci.h	10 Jul 2008 21:36:53 -0000	1.89
> +++ src/sys/dev/ata/ata-pci.h	21 Jul 2008 09:23:31 -0000
> _at__at_ -55,6 +55,8 _at__at_ struct ata_pci_controller {
>     void                (*reset)(device_t);
>     void                (*dmainit)(device_t);
>     void                (*setmode)(device_t, int);
> +    int                 (*suspend)(device_t);
> +    int                 (*resume)(device_t);
>     struct {
>     void                (*function)(void *);
>     void                *argument;
> _at__at_ -460,6 +462,8 _at__at_ struct ata_connect_task {
> int ata_pci_probe(device_t dev);
> int ata_pci_attach(device_t dev);
> int ata_pci_detach(device_t dev);
> +int ata_pci_suspend(device_t dev);
> +int ata_pci_resume(device_t dev);
> struct resource * ata_pci_alloc_resource(device_t dev, device_t  
> child, int type, int *rid, u_long start, u_long end, u_long count,  
> u_int flags);
> int ata_pci_release_resource(device_t dev, device_t child, int type,  
> int rid, struct resource *r);
> int ata_pci_setup_intr(device_t dev, device_t child, struct resource  
> *irq, int flags, driver_filter_t *filter, driver_intr_t *function,  
> void *argument, void **cookiep);

-Søren
Received on Mon Jul 21 2008 - 09:31:46 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:39:33 UTC