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>
