Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Jul 2008 12:55:58 +0200
From:      =?ISO-8859-1?Q?S=F8ren_Schmidt?= <sos@FreeBSD.ORG>
To:        "Andrey V. Elsukov" <bu7cher@yandex.ru>
Cc:        freebsd-current@FreeBSD.ORG, Sergey G Nasonov <snasonov@bcc.ru>
Subject:   Re: RFC, RFT: AHCI driver reorganization (was: Re: ATA subsystem lost drive after resume process)
Message-ID:  <4DCB1935-4248-472B-8328-E0365306B953@FreeBSD.ORG>
In-Reply-To: <4884668C.5060606@yandex.ru>
References:  <200807151124.36621.snasonov@bcc.ru> <4884668C.5060606@yandex.ru>

next in thread | previous in thread | raw e-mail | index | archive | help
Hi

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

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

At any rate, fixing the suspend / resume problems should be dealt with =20=

in a more generic manner, not just for AHCI, by splitting the work =20
done by the _chipinit and _allocate functions so that just the chipset =20=

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 =20=

still, so progress here is slow, heck my WIP on NCQ support is still =20
just that and touches the same code parts in ACHI so they willl get =20
even more behind, oh well...

I'm starting to wonder if I should just let it go and leave ATA to its =20=

own faith, and get on with other things...

laters...

-S=F8ren

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).
>
>  * =46rom 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.
>
> --=20
> WBR, Andrey V. Elsukov
>
> Index: src/sys/dev/ata/ata-all.h
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> 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
> @@ -188,6 +188,9 @@
> #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
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> 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
> @@ -62,6 +62,9 @@ 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 =20=

> *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);
> @@ -69,6 +72,11 @@ static int ata_ahci_end_transaction(stru
> static int ata_ahci_pm_read(device_t dev, int port, int reg, =20
> u_int32_t *result);
> static int ata_ahci_pm_write(device_t dev, int port, int reg, =20
> 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, =20
> int nsegs, int error);
> static void ata_ahci_dmainit(device_t dev);
> @@ -582,6 +590,29 @@ ata_ahci_ident(device_t dev)
> }
>
> static int
> +ata_ahci_reset_controller(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr =3D 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-=20
> >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 =3D device_get_softc(dev);
> @@ -602,20 +633,8 @@ 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-=20
> >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 =3D
> @@ -633,6 +652,8 @@ ata_ahci_chipinit(device_t dev)
>     ctlr->dmainit =3D ata_ahci_dmainit;
>     ctlr->allocate =3D ata_ahci_allocate;
>     ctlr->setmode =3D ata_sata_setmode;
> +    ctlr->resume =3D ata_ahci_resume;
> +    ctlr->suspend =3D ata_ahci_suspend;
>
>     /* enable PCI interrupt */
>     pci_write_config(dev, PCIR_COMMAND,
> @@ -655,9 +676,13 @@ ata_ahci_allocate(device_t dev)
> {
>     struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(dev));
>     struct ata_channel *ch =3D device_get_softc(dev);
> -    u_int64_t work;
>     int offset =3D 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 =3D ctlr->r_res2;
>     ch->r_io[ATA_SSTATUS].offset =3D ATA_AHCI_P_SSTS + offset;
> @@ -676,30 +701,45 @@ ata_ahci_allocate(device_t dev)
>     ch->hw.pm_read =3D ata_ahci_pm_read;
>     ch->hw.pm_write =3D ata_ahci_pm_write;
>
> -    /* setup work areas */
> -    work =3D ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & =20
> 0xffffffff);
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
> +    return 0;
> +}
>
> -    work =3D ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
> -    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & =20
> 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 =3D 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 =3D 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, =20
> 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;
> }
>
> @@ -716,38 +756,24 @@ ata_ahci_status(device_t dev)
> 	u_int32_t cstatus =3D 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 =3D ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + =
offset);
> -	    int timeout =3D 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 =3D 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
> @@ -998,46 +1024,107 @@ ata_ahci_pm_write(device_t dev, int port
>     return (ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset) >> 8) & =20
> 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 =3D =20
> device_get_softc(device_get_parent(dev));
> -    struct ata_channel *ch =3D device_get_softc(dev);
> -    u_int32_t cmd;
> +    struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(ch->dev));
>     int offset =3D ch->unit << 7;
> -    int timeout;
> +    int timeout =3D 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 =3D 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 =3D 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) & =20
> ATA_AHCI_P_CMD_CR);
> +}
> +
> +/* When PxCMD.FR and PxCMD.FRE are both cleared to '0', software =20
> may update
> + * the values of PxFB and PxFBU. Prior to setting PxCMD.FRE to '1', =20=

> software
> + * shall ensure that PxFB and PxFBU are set to valid values. =20
> 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 =3D =20
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t offset =3D ch->unit << 7;
> +    u_int64_t work;
> +
> +    /* setup work areas */
> +    work =3D ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & =20
> 0xffffffff);
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
> +
> +    work =3D ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & =20
> 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) | =20
> ATA_AHCI_P_CMD_FRE);
> +}
> +
> +static void
> +ata_ahci_fre_stop(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t offset =3D ch->unit << 7;
> +    int timeout =3D 0;
> +
> +    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
> +	    ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & =20
> (~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 =3D ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
> -	cmd |=3D ATA_AHCI_P_CMD_CLO;
> -	ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd);
> -	timeout =3D 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) & =20
> ATA_AHCI_P_CMD_FR);
> +}
> +
> +static void
> +ata_ahci_stop_channel(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(ch->dev));
> +    u_int32_t cmd, offset =3D ch->unit << 7;
> +    int timeout =3D 0;
> +
> +    cmd =3D ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
> +    if ((cmd & (ATA_AHCI_P_CMD_ST | ATA_AHCI_P_CMD_CR)) =3D=3D 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=20
> +offset)&ATA_AHCI_P_CMD_CLO);
> -    }
> +    } while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & =20
> ATA_AHCI_P_CMD_CR);
> +}
> +
> +
> +static void
> +ata_ahci_restart_channel(struct ata_channel *ch)
> +{
> +    struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(ch->dev));
> +    int offset =3D 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));
> @@ -1046,11 +1133,12 @@ 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) | =20
> ATA_AHCI_P_CMD_ST);
> }
>
> static u_int32_t
> @@ -1065,7 +1153,7 @@ 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);
> @@ -1094,7 +1182,7 @@ 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;
> 	    }
> @@ -1110,19 +1198,35 @@ ata_ahci_reset(device_t dev)
> {
>     struct ata_pci_controller *ctlr =3D =20
> device_get_softc(device_get_parent(dev));
>     struct ata_channel *ch =3D device_get_softc(dev);
> -    u_int32_t signature;
> +    u_int32_t signature, offset =3D ch->unit << 7;
> +    ch->devices =3D 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 =3D 0;
> 	return;
>     }
>
> @@ -1146,13 +1250,24 @@ ata_ahci_reset(device_t dev)
>     case 0xeb140101:
> 	ch->devices =3D ATA_ATAPI_MASTER;
> 	break;
> -    default: /* SOS XXX */
> +    default:
> 	if (bootverbose)
> 	    device_printf(dev, "No signature, asuming disk device\n");
> -	ch->devices =3D ATA_ATA_MASTER;
>     }
>     if (bootverbose)
>         device_printf(dev, "ahci_reset devices=3D%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) | =20
> ATA_AHCI_P_CMD_SUD |
> +		      ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | =20
> 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
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> 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
> @@ -262,6 +262,32 @@ ata_pci_detach(device_t dev)
>     return 0;
> }
>
> +int
> +ata_pci_suspend(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr =3D device_get_softc(dev);
> +    int error =3D 0;
> +
> +    bus_generic_suspend(dev);
> +    if (ctlr->suspend)
> +	error =3D ctlr->suspend(dev);
> +
> +    return error;
> +}
> +
> +int
> +ata_pci_resume(device_t dev)
> +{
> +    struct ata_pci_controller *ctlr =3D device_get_softc(dev);
> +    int error =3D 0;
> +
> +    if (ctlr->resume)
> +	error =3D ctlr->resume(dev);
> +    bus_generic_resume(dev);
> +
> +    return error;
> +}
> +
> struct resource *
> ata_pci_alloc_resource(device_t dev, device_t child, int type, int =20
> *rid,
> 		       u_long start, u_long end, u_long count, u_int =
flags)
> @@ -556,8 +582,8 @@ 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
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> 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
> @@ -55,6 +55,8 @@ 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;
> @@ -460,6 +462,8 @@ 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 =20
> child, int type, int *rid, u_long start, u_long end, u_long count, =20
> u_int flags);
> int ata_pci_release_resource(device_t dev, device_t child, int type, =20=

> int rid, struct resource *r);
> int ata_pci_setup_intr(device_t dev, device_t child, struct resource =20=

> *irq, int flags, driver_filter_t *filter, driver_intr_t *function, =20
> void *argument, void **cookiep);

-S=F8ren









Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4DCB1935-4248-472B-8328-E0365306B953>