|
|
|
/* vxbAhciStorage.c - AHCI SATA and ATAPI vxBus storage device driver */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2011-2016, 2018-2019 Wind River Systems, Inc.
|
|
|
|
*
|
|
|
|
* The right to copy, distribute, modify or otherwise make use
|
|
|
|
* of this software may be licensed only pursuant to the terms
|
|
|
|
* of an applicable Wind River license agreement.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
modification history
|
|
|
|
--------------------
|
|
|
|
02a,09sep19,mw1 add sataReqMutex semaphore to protect freeReqPrivList. (VXW6-87076)
|
|
|
|
01z,26aug19,mw1 Use vxAtomic32Cas to protect access to queuedMode (VXW6-87119)
|
|
|
|
01y,19sep18,syt Enable Task File error interrupt enable (VXW6-86615)
|
|
|
|
01x,21dec16,yjl Fix VXW6-85923, AHCI storage slowing bootup when no disk is
|
|
|
|
physically connected
|
|
|
|
01w,17jun16,hma fix the sata test error (VXW6-83903)
|
|
|
|
01v,18jan16,hma add rename sata disk or patition function(VXW6-10898)
|
|
|
|
01u,21dec15,hma fix sata test error sometimes (VXW6-83231)
|
|
|
|
01t,21dec15,hma fix AHCI does not work for some board (VXW6-85077)
|
|
|
|
01s,25jun15,zly clear busy and data request flag via set AHCI_PCMD_CLO.
|
|
|
|
(VXW6-84548)
|
|
|
|
01r,05mar15,txu fix the error condition for ATA_SMART_EXCEEDED (VXW6-84062)
|
|
|
|
01q,27feb15,m_y export the sem and watch dog timeout value (VXW6-84034)
|
|
|
|
01p,19nov14,m_y modify watch dog time (VXW6-83672)
|
|
|
|
01o,17sep14,txu add S.M.A.R.T support (VXW6-82629)
|
|
|
|
01n,22feb14,m_y modify prdcount and wait time (VXW6-80627)
|
|
|
|
01m,22oct13,m_y remove unused function
|
|
|
|
01l,18jun13,m_y add code to support XBD sched policy and NCQ
|
|
|
|
01k,27mar13,clx added vxbXbdDevCreate method. (WIND00399793)
|
|
|
|
01j,05nov12,sye removed handle swap for PCI bus. (WIND00380133)
|
|
|
|
01i,30oct12,sye set detach state before remove logical device. (WIND00383457)
|
|
|
|
01h,19oct12,mpc fixed address translation for 32 bit GOS working on
|
|
|
|
64 bit Hypervisor. (WIND00383488)
|
|
|
|
01g,01aug12,sye spin up port only when HBA supports. (WIND00367545)
|
|
|
|
01f,02jul12,sye fixed static analyze issue. (WIND00354953)
|
|
|
|
01e,29may12,e_d check attach status before remove logical device. (WIND00351659)
|
|
|
|
01d,15mar12,e_d check port number to malloc sata device struct to
|
|
|
|
fix page fault exception on discontiguous sata
|
|
|
|
ports. (WIND00335995)
|
|
|
|
01c,08mar12,e_d moved ATA and ATAPI device initialization
|
|
|
|
to vxbSataLib.c. (WIND00333858)
|
|
|
|
01b,19feb12,syt added register swap operation to fit big endian type AHCI
|
|
|
|
register bank.
|
|
|
|
01a,22oct11,e_d adapted from vxbIntelAhciStorage.c version 01w. (WIND00307297)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
DESCRIPTION:
|
|
|
|
|
|
|
|
This is a low level device driver for ATA/ATAPI devices on AHCI host controller.
|
|
|
|
It also provides necessary functions to the user for device and its control
|
|
|
|
features which are not utilized by file system.
|
|
|
|
|
|
|
|
As each controller is initialized, the sata controller is examined. A port
|
|
|
|
monitor task is spawned for each port. Initialization of the driver is handled
|
|
|
|
by the vxBus interface. The driver is fully initialized in vxBus "connection"
|
|
|
|
phase.
|
|
|
|
|
|
|
|
This monitor task is blocked on the message receiving. When it is
|
|
|
|
recognized that a device has been inserted (via an ISR) the "AHCI_ATTACH_MSG"
|
|
|
|
is given to the port monitor task.
|
|
|
|
|
|
|
|
If the device change indicates that a device is present, the XBD interface
|
|
|
|
to the device is created. 2 semaphores are used in this interface. A mutex
|
|
|
|
to force mutual exclusion between the driver and the other users of the xbd
|
|
|
|
interface, and a bio ready semaphore. Both of these semaphores are used by
|
|
|
|
the "bio task" which is created at this time for each device (port) as the
|
|
|
|
device becomes active. The bio ready indicates that there is work for the
|
|
|
|
driver to do. Part of the creation of the xbd interface is to send an
|
|
|
|
insertion event to the event reporting framework. This allows the filesystem
|
|
|
|
to use the newly created xbd.
|
|
|
|
|
|
|
|
If the device change indicates that a device has gone away, the XBD interface
|
|
|
|
is deleted, the bio service task is deleted and other resources used by the
|
|
|
|
bio service task are recovered. The device change indication allow for hot
|
|
|
|
removal/insertion of sata devices.
|
|
|
|
|
|
|
|
The interrupt handler has the controller specific control block passed as an
|
|
|
|
argument by the pci interrupt routine. The isr scans each port on that
|
|
|
|
controller for status bits. If the device change has occurred, the
|
|
|
|
"device change" is given to the waiting port monitor task. If the command has
|
|
|
|
completed, the command complete semaphore is given. This semaphore serializes
|
|
|
|
access to the driver. The bio task will call the function to read/write to
|
|
|
|
the device, and uses this semaphore for that purpose.
|
|
|
|
|
|
|
|
References:
|
|
|
|
1) ATAPI-5 specification "T13-1321D Revision 1b, 7 July 1999"
|
|
|
|
2) ATAPI for CD-ROMs "SFF-8020i Revision 2.6, Jan 22,1996"
|
|
|
|
3) Intel 82801BA (ICH2), 82801AA (ICH), and 82801AB (ICH0) IDE Controller
|
|
|
|
Programmer's Reference Manual, Revision 1.0 July 2000
|
|
|
|
4) Serial ATA advanced Host Controller Interface
|
|
|
|
|
|
|
|
SEE ALSO:
|
|
|
|
\tb VxWorks Programmer's Guide: I/O System
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* includes */
|
|
|
|
|
|
|
|
#include <vxWorks.h>
|
|
|
|
#include <taskLib.h>
|
|
|
|
#include <ioLib.h>
|
|
|
|
#include <memLib.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errnoLib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <private/semLibP.h>
|
|
|
|
#include <intLib.h>
|
|
|
|
#include <iv.h>
|
|
|
|
#include "cacheLib.h"
|
|
|
|
#include <wdLib.h>
|
|
|
|
#include <sysLib.h>
|
|
|
|
#include <sys/fcntlcom.h>
|
|
|
|
#include <logLib.h>
|
|
|
|
#include <drv/xbd/xbd.h>
|
|
|
|
#include <drv/erf/erfLib.h>
|
|
|
|
#include <drv/pcmcia/pccardLib.h>
|
|
|
|
#include <vxBusLib.h>
|
|
|
|
#include <hwif/vxbus/vxBus.h>
|
|
|
|
#include <hwif/vxbus/vxbPciLib.h>
|
|
|
|
#include <hwif/vxbus/hwConf.h>
|
|
|
|
#include <drv/pci/pciConfigLib.h>
|
|
|
|
#include <drv/pci/pciIntLib.h>
|
|
|
|
#include <fsMonitor.h>
|
|
|
|
#include <../h/vxbus/vxbAccess.h>
|
|
|
|
#include <../src/hwif/h/storage/vxbAhciStorage.h>
|
|
|
|
#include <../h/vmLib.h>
|
|
|
|
#include <../../../h/taskLib.h>
|
|
|
|
#include <spinLockLib.h>
|
|
|
|
|
|
|
|
/* extern */
|
|
|
|
|
|
|
|
IMPORT int usrAhciWatchdogValGet();
|
|
|
|
IMPORT int usrAhciSemTimoutValGet();
|
|
|
|
#if defined(INCLUDE_DRV_STORAGE_AHCI)
|
|
|
|
/* defines */
|
|
|
|
|
|
|
|
#ifdef AHCI_DEBUG
|
|
|
|
|
|
|
|
# ifdef LOCAL
|
|
|
|
# undef LOCAL
|
|
|
|
# define LOCAL
|
|
|
|
# endif /* LOCAL */
|
|
|
|
|
|
|
|
# define DEBUG_INIT 0x00000001
|
|
|
|
# define DEBUG_CMD 0x00000002
|
|
|
|
# define DEBUG_MON 0x00000004
|
|
|
|
# define DEBUG_INT 0x00000008
|
|
|
|
# define DEBUG_REG 0x00000010
|
|
|
|
# define DEBUG_BIST 0x00000020
|
|
|
|
# define DEBUG_ALWAYS 0xffffffff
|
|
|
|
# define DEBUG_NONE 0x00000000
|
|
|
|
|
|
|
|
UINT32 ahciSataDebug = DEBUG_NONE;
|
|
|
|
|
|
|
|
IMPORT FUNCPTR _func_logMsg;
|
|
|
|
|
|
|
|
# define AHCISATA_DBG_LOG(mask, string, a, b, c, d, e, f) \
|
|
|
|
if ((ahciSataDebug & mask) || (mask == DEBUG_ALWAYS)) \
|
|
|
|
{ \
|
|
|
|
if (_func_logMsg != NULL) \
|
|
|
|
_func_logMsg(string, (_Vx_usr_arg_t)a, (_Vx_usr_arg_t)b, \
|
|
|
|
(_Vx_usr_arg_t)c, (_Vx_usr_arg_t)d, \
|
|
|
|
(_Vx_usr_arg_t)e, (_Vx_usr_arg_t)f); \
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define AHCISATA_DBG_LOG(mask, string, a, b, c, d, e, f)
|
|
|
|
#endif /* AHCI_DEBUG */
|
|
|
|
|
|
|
|
#define AHCI_QUEUE_SIZE (AHCI_MAX_DRIVES * 2)
|
|
|
|
#define AHCI_MON_PRIORITY 49
|
|
|
|
|
|
|
|
LOCAL BOOL ahciMonitorStarted = FALSE;
|
|
|
|
LOCAL MSG_Q_ID ahciMonQueue;
|
|
|
|
LOCAL UINT32 ahciBitMask[AHCI_MAX_DRIVES] =
|
|
|
|
{
|
|
|
|
0x00000001, 0x00000002, 0x00000004, 0x00000008,
|
|
|
|
0x00000010, 0x00000020, 0x00000040, 0x00000080,
|
|
|
|
0x00000100, 0x00000200, 0x00000400, 0x00000800,
|
|
|
|
0x00001000, 0x00002000, 0x00004000, 0x00008000,
|
|
|
|
0x00010000, 0x00020000, 0x00040000, 0x00080000,
|
|
|
|
0x00100000, 0x00200000, 0x00400000, 0x00800000,
|
|
|
|
0x01000000, 0x02000000, 0x04000000, 0x08000000,
|
|
|
|
0x10000000, 0x20000000, 0x40000000, 0x80000000
|
|
|
|
};
|
|
|
|
|
|
|
|
LOCAL int ahciTypes[AHCI_MAX_DRIVES] =
|
|
|
|
{
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL,
|
|
|
|
AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL, AHCI_MODE_ALL
|
|
|
|
};
|
|
|
|
|
|
|
|
/* list of supported device IDs */
|
|
|
|
|
|
|
|
LOCAL PCI_DEVVEND vxbIntelAhciIdList[] =
|
|
|
|
{
|
|
|
|
{0xFFFF, 0xFFFF}
|
|
|
|
};
|
|
|
|
|
|
|
|
LOCAL int ahciRetry = 3;
|
|
|
|
LOCAL void ahciWdog(AHCI_DRIVE *);
|
|
|
|
LOCAL STATUS ahciDrv(SATA_HOST *);
|
|
|
|
LOCAL void ahciCmdWaitForResource(AHCI_DRIVE *, BOOL);
|
|
|
|
LOCAL void ahciCmdReleaseResource(AHCI_DRIVE *, BOOL);
|
|
|
|
LOCAL void ahciMon(void);
|
|
|
|
LOCAL size_t ahciPrdSetup(UINT8 *, UINT32, AHCI_PRD *);
|
|
|
|
LOCAL void vxbAhciInstInit(VXB_DEVICE_ID);
|
|
|
|
LOCAL void vxbAhciInstInit2(VXB_DEVICE_ID);
|
|
|
|
LOCAL void vxbAhciInstConnect(VXB_DEVICE_ID);
|
|
|
|
LOCAL BOOL vxbAhciDevProbe (VXB_DEVICE_ID);
|
|
|
|
LOCAL STATUS ahciDriveInit (SATA_HOST *, int);
|
|
|
|
LOCAL STATUS ahciIntr(SATA_HOST *);
|
|
|
|
LOCAL STATUS ahciInit (SATA_HOST *, int);
|
|
|
|
LOCAL void * ahciVirtToPciAddr (void *);
|
|
|
|
LOCAL STATUS ahciSataCmdIssue(SATA_DEVICE *, FIS_ATA_REG *, SATA_DATA *);
|
|
|
|
|
|
|
|
LOCAL void ahciXferReq
|
|
|
|
(
|
|
|
|
SATA_DEVICE * pCtrl,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
);
|
|
|
|
LOCAL STATUS ahciReqResourceGet
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
);
|
|
|
|
LOCAL VOID ahciReqResourceRelease
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
);
|
|
|
|
|
|
|
|
LOCAL DRIVER_INITIALIZATION vxbAhciFuncs =
|
|
|
|
{
|
|
|
|
vxbAhciInstInit,
|
|
|
|
vxbAhciInstInit2,
|
|
|
|
vxbAhciInstConnect
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* With AHCI 1.0 standard, the PCI class code should be set
|
|
|
|
* 0x010601. Now we will probe class code to check this device
|
|
|
|
* and not add more PCI device ID and vendor ID in this file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL PCI_DRIVER_REGISTRATION vxbAhciStoragePciRegistration =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
NULL, /* pNext == NULL */
|
|
|
|
VXB_DEVID_DEVICE, /* This is a device driver */
|
|
|
|
VXB_BUSID_PCI, /* hardware resides on a PCI bus */
|
|
|
|
VXB_VER_5_0_0, /* targeting vxbus version 2 api */
|
|
|
|
AHCI_NAME, /* driver named */
|
|
|
|
&vxbAhciFuncs, /* set of 3 pass functions */
|
|
|
|
NULL, /* no methods */
|
|
|
|
vxbAhciDevProbe, /* probe function */
|
|
|
|
},
|
|
|
|
NELEMENTS(vxbIntelAhciIdList),
|
|
|
|
&vxbIntelAhciIdList[0],
|
|
|
|
};
|
|
|
|
|
|
|
|
LOCAL struct vxbPlbRegister vxbAhciStoragePlbRegistration =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
(struct vxbDevRegInfo *)&vxbAhciStoragePciRegistration,
|
|
|
|
VXB_DEVID_DEVICE,
|
|
|
|
VXB_BUSID_PLB,
|
|
|
|
VXB_VER_5_0_0,
|
|
|
|
AHCI_NAME,
|
|
|
|
&vxbAhciFuncs,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* vxbIntelAhciStorageRegister - register driver with vxbus
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO
|
|
|
|
*/
|
|
|
|
|
|
|
|
void vxbAhciStorageRegister (void)
|
|
|
|
{
|
|
|
|
(void) vxbDevRegister ((struct vxbDevRegInfo *)&vxbAhciStoragePlbRegistration);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* vxbIntelAhciInstInit - First pass initialization
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void vxbAhciInstInit
|
|
|
|
(
|
|
|
|
VXB_DEVICE_ID pDev
|
|
|
|
)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* get the next available unit number */
|
|
|
|
|
|
|
|
if (pDev->busID == VXB_BUSID_PCI)
|
|
|
|
(void) vxbNextUnitGet (pDev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* vxbIntelAhciInstInit2 - Second pass initialization
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void vxbAhciInstInit2
|
|
|
|
(
|
|
|
|
VXB_DEVICE_ID pDev
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
SATA_HOST * pAhciDrvCtrl;
|
|
|
|
UINT32 ulPhyAddrLow = 0;
|
|
|
|
pAhciDrvCtrl = (SATA_HOST *)malloc (sizeof (SATA_HOST));
|
|
|
|
if (pAhciDrvCtrl == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bzero ( (char *)pAhciDrvCtrl, sizeof (SATA_HOST) );
|
|
|
|
|
|
|
|
pAhciDrvCtrl->pDev = pDev;
|
|
|
|
pDev->pDrvCtrl = pAhciDrvCtrl;
|
|
|
|
|
|
|
|
if (pDev->busID == VXB_BUSID_PCI)
|
|
|
|
{
|
|
|
|
/* As defined in AHCI spec, BAR[5] is for AHCI base address,
|
|
|
|
* read the AHCI BASE register, and find the virture address.
|
|
|
|
*/
|
|
|
|
(void) VXB_PCI_BUS_CFG_READ (pDev, PCI_CFG_BASE_ADDRESS_5, 4, ulPhyAddrLow);
|
|
|
|
|
|
|
|
for (i = 0; i < VXB_MAXBARS; i++)
|
|
|
|
{
|
|
|
|
if ((pDev->regBaseFlags[i] == VXB_REG_MEM) \
|
|
|
|
&& (pDev->pRegBase[i] == (void *)(long)ulPhyAddrLow))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < VXB_MAXBARS; i++)
|
|
|
|
{
|
|
|
|
if (pDev->regBaseFlags[i] == VXB_REG_MEM)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == VXB_MAXBARS)
|
|
|
|
{
|
|
|
|
free (pAhciDrvCtrl);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
(void) vxbRegMap (pDev, i, &pAhciDrvCtrl->regHandle);
|
|
|
|
|
|
|
|
pAhciDrvCtrl->regBase[0] = pDev->pRegBase[i];
|
|
|
|
|
|
|
|
/* PCI BUS already swapped handle */
|
|
|
|
|
|
|
|
if (pDev->busID != VXB_BUSID_PCI)
|
|
|
|
pAhciDrvCtrl->regHandle = (void *)AHCI_REG_HANDLE_SWAP((ULONG)pAhciDrvCtrl->regHandle);
|
|
|
|
|
|
|
|
|
|
|
|
/* reset AHCI controller. Here we must reset AHCI as soon as early when using PCI legacy interrupt!
|
|
|
|
* Because both Net Card and SATA Card use int-pin 1, with the same int line 83 for FT1500 board.
|
|
|
|
* When Net Card init, there are too many SATA interrupts to boot board if the AHCI not reset here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
(void) CTRL_REG_READ(pAhciDrvCtrl, AHCI_GHC);
|
|
|
|
CTRL_REG_WRITE(pAhciDrvCtrl, AHCI_GHC, AHCI_GHC_HR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* vxbIntelAhciInstConnect - Final initialization
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO
|
|
|
|
*/
|
|
|
|
VXB_DEVICE_ID ahciDev = NULL;
|
|
|
|
LOCAL void vxbAhciInstConnect
|
|
|
|
(
|
|
|
|
VXB_DEVICE_ID pDev
|
|
|
|
)
|
|
|
|
{
|
|
|
|
SATA_HOST * pAhciDrvCtrl;
|
|
|
|
STATUS rc = OK;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL_V(pDev)
|
|
|
|
|
|
|
|
pAhciDrvCtrl = pDev->pDrvCtrl;
|
|
|
|
if (pAhciDrvCtrl == NULL)
|
|
|
|
return;
|
|
|
|
ahciDev = pDev;
|
|
|
|
pAhciDrvCtrl->ops.cmdIssue = (FUNCPTR)ahciSataCmdIssue;
|
|
|
|
pAhciDrvCtrl->ops.xferReq = (FUNCPTR)ahciXferReq;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the monitor task is not started yet, create the message queue
|
|
|
|
* for it, and start it
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!ahciMonitorStarted)
|
|
|
|
{
|
|
|
|
if ((ahciMonQueue = msgQCreate(AHCI_QUEUE_SIZE, VXB_AHCI_MSG_SIZE,
|
|
|
|
MSG_Q_FIFO)) == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create SATA message queue\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (taskSpawn("tAhciMon", AHCI_MON_PRIORITY, 0, 4096,
|
|
|
|
(FUNCPTR)ahciMon,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == TASK_ID_ERROR)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create AHCI monitor task\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
(void) msgQDelete (ahciMonQueue);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ahciMonitorStarted = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rc = ahciDrv(pAhciDrvCtrl);
|
|
|
|
|
|
|
|
|
|
|
|
if(rc != OK)
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"vxbIntelAhciInstConnect() ahciDrv init error\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int usrAtaRename(char * oldName, char *newName)
|
|
|
|
{
|
|
|
|
|
|
|
|
DOS_VOLUME_DESC_ID pVolDesc;
|
|
|
|
SATA_HOST * pAhciDrvCtrl;
|
|
|
|
char *copiedName;
|
|
|
|
DEV_HDR *pDevHdr;
|
|
|
|
|
|
|
|
if (ahciDev ==NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
pAhciDrvCtrl = ahciDev->pDrvCtrl;
|
|
|
|
|
|
|
|
fsmNameInstall (oldName, newName);
|
|
|
|
|
|
|
|
pVolDesc = dosFsVolDescGet (oldName, NULL);
|
|
|
|
|
|
|
|
if (pVolDesc == NULL)
|
|
|
|
{
|
|
|
|
printf("--can't find %s--\n",oldName);
|
|
|
|
return (ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
pDevHdr = (DEV_HDR *)pVolDesc;
|
|
|
|
|
|
|
|
copiedName = (char *) malloc ((unsigned) (strlen (newName) + 1));
|
|
|
|
if (copiedName == NULL)
|
|
|
|
return (ERROR);
|
|
|
|
|
|
|
|
strcpy (copiedName, newName);
|
|
|
|
|
|
|
|
pDevHdr->name = copiedName;
|
|
|
|
|
|
|
|
return (OK);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* vxbAhciDevProbe - vxbus probe function
|
|
|
|
*
|
|
|
|
* This function is called by vxBus to probe device.
|
|
|
|
*
|
|
|
|
* RETURNS: TRUE if probe passes and assumed a valid ahci SATA
|
|
|
|
* (or compatible) device. FALSE otherwise.
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL BOOL vxbAhciDevProbe
|
|
|
|
(
|
|
|
|
VXB_DEVICE_ID pDev
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT16 devId = 0;
|
|
|
|
UINT16 vendorId = 0;
|
|
|
|
UINT32 classValue = 0;
|
|
|
|
|
|
|
|
VXB_PCI_BUS_CFG_READ (pDev, PCI_CFG_VENDOR_ID, 2, vendorId);
|
|
|
|
VXB_PCI_BUS_CFG_READ (pDev, PCI_CFG_DEVICE_ID, 2, devId);
|
|
|
|
|
|
|
|
if (vendorId == INTEL_VENDOR_ID)
|
|
|
|
{
|
|
|
|
switch (devId)
|
|
|
|
{
|
|
|
|
case ICH6R_DEVICE_ID:
|
|
|
|
case ICH6M_DEVICE_ID:
|
|
|
|
case ESB2_DEVICE_ID:
|
|
|
|
case ICH7M_DEVICE_ID:
|
|
|
|
case ICH8M_DEVICE_ID:
|
|
|
|
case ICH9R_DEVICE_ID:
|
|
|
|
case ICH9M_DEVICE_ID:
|
|
|
|
case ICH10R_DEVICE_ID:
|
|
|
|
case ICH10_DEVICE_ID:
|
|
|
|
case PCH_6PORT_DEVICE_ID_0:
|
|
|
|
case PCH_6PORT_DEVICE_ID_1:
|
|
|
|
case PCH_PATSBURG_DEVICE_ID:
|
|
|
|
case PCH_COUGAR_POINT_DEVICE_ID:
|
|
|
|
case IOH_TOPCILFF_DEVICE_ID:
|
|
|
|
return (TRUE);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VXB_PCI_BUS_CFG_READ (pDev, PCI_CFG_REVISION, 4, classValue);
|
|
|
|
|
|
|
|
if ((classValue & 0xFFFFFF00) == AHCI_CLASS_ID)
|
|
|
|
return (TRUE);
|
|
|
|
else
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciVirtToPciAddr - Translate a virtual CPU address to a physical PCI address
|
|
|
|
*
|
|
|
|
* Perform the necessary translations to change an address from virtual CPU to
|
|
|
|
* physical CPU, then to physical PCI.
|
|
|
|
*
|
|
|
|
* RETURNS: physical address
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void * ahciVirtToPciAddr
|
|
|
|
(
|
|
|
|
void * addr
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return (CACHE_DMA_VIRT_TO_PHYS(addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciIntr - AHCI controller interrupt handler.
|
|
|
|
*
|
|
|
|
* This function is the AHCI controller interrupt handler.
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciIntr
|
|
|
|
(
|
|
|
|
SATA_HOST * pCtrl
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
UINT32 ctrlIntr, sataIntr, portIntr, taskStatus, sataStatus;
|
|
|
|
UINT32 cmdActive, sataActive, active;
|
|
|
|
AHCI_DRIVE *pDrive;
|
|
|
|
VXB_AHCI_MSG ctrlMsg;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL(pCtrl,ERROR)
|
|
|
|
ctrlMsg.pCtrl = pCtrl;
|
|
|
|
|
|
|
|
while (CTRL_REG_READ(pCtrl, AHCI_IS))
|
|
|
|
{
|
|
|
|
for (i = 0; i < pCtrl->numImpPorts; i++)
|
|
|
|
{
|
|
|
|
pDrive = pCtrl->sataDev[i];
|
|
|
|
ctrlIntr = CTRL_REG_READ(pCtrl, AHCI_IS) &
|
|
|
|
(ahciBitMask[pDrive->portPhyNum]);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() ctrlIntr = 0x%x\n",
|
|
|
|
ctrlIntr,0,0,0,0,0);
|
|
|
|
|
|
|
|
if (ctrlIntr & (ahciBitMask[pDrive->portPhyNum]))
|
|
|
|
{
|
|
|
|
pDrive->intCount++;
|
|
|
|
|
|
|
|
/* read all of the status registers */
|
|
|
|
|
|
|
|
sataIntr = PORT_REG_READ(pDrive, AHCI_PxSERR);
|
|
|
|
portIntr = PORT_REG_READ(pDrive, AHCI_PxIS);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() sataIntr = 0x%x portIntr = 0x%x\n",
|
|
|
|
sataIntr,portIntr,0,0,0,0);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSERR, sataIntr);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxIS, portIntr);
|
|
|
|
CTRL_REG_WRITE(pCtrl, AHCI_IS, ctrlIntr);
|
|
|
|
taskStatus = PORT_REG_READ(pDrive, AHCI_PxTFD);
|
|
|
|
sataStatus = PORT_REG_READ(pDrive, AHCI_PxSSTS);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() taskStatus = 0x%x sataStatus = 0x%x\n",
|
|
|
|
taskStatus,sataStatus,0,0,0,0);
|
|
|
|
|
|
|
|
pDrive->intStatus = taskStatus & 0xFF;
|
|
|
|
pDrive->intError = (taskStatus & 0xFF00) >> 8;
|
|
|
|
cmdActive = PORT_REG_READ(pDrive,AHCI_PxCI);
|
|
|
|
sataActive = PORT_REG_READ(pDrive, AHCI_PxSACT);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() cmdActive = 0x%x sataActive = 0x%x\n",
|
|
|
|
cmdActive,sataActive,0,0,0,0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it was a drive attached interrupt, signal
|
|
|
|
* the monitor task.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((portIntr & (AHCI_PIS_PRCS | AHCI_PIS_PCS)) &&
|
|
|
|
((sataStatus & AHCI_PSSTS_IPM_MSK) == AHCI_PSSTS_IPM_ACTIVE))
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to make sure we didn't cause the
|
|
|
|
* drive attached by issuing a COMRESET
|
|
|
|
*/
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() sata device attached\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
if (!pDrive->initActive)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"SATA Drive inserted on ctrl %d,"
|
|
|
|
"logic port number %d\n",
|
|
|
|
pCtrl->pDev->unitNumber,
|
|
|
|
pDrive->sataPortDev.num,
|
|
|
|
0,0,0,0);
|
|
|
|
|
|
|
|
ctrlMsg.msgId = AHCI_ATTACH_MSG;
|
|
|
|
ctrlMsg.drive = (char)i;
|
|
|
|
|
|
|
|
/* return from msgQSend is not needed */
|
|
|
|
|
|
|
|
(void)msgQSend(ahciMonQueue, (char *)&ctrlMsg,
|
|
|
|
VXB_AHCI_MSG_SIZE,
|
|
|
|
NO_WAIT,MSG_PRI_NORMAL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it was a drive removed interrupt, mark it
|
|
|
|
* as removed, and signal any active transfers
|
|
|
|
* that they are now done....whether they like
|
|
|
|
* it or not...
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (portIntr & AHCI_PIS_PRCS &&
|
|
|
|
((sataStatus & AHCI_PSSTS_IPM_MSK) == AHCI_PSSTS_IPM_NO_DEVICE))
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to make sure we didn't cause the
|
|
|
|
* drive remove by issuing a COMRESET
|
|
|
|
*/
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() sata device removed\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
|
|
|
|
if (!pDrive->initActive)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"SATA Drive inserted on ctrl %d,"
|
|
|
|
"logic port number %d\n",
|
|
|
|
pCtrl->pDev->unitNumber,
|
|
|
|
pDrive->sataPortDev.num,
|
|
|
|
0,0,0,0);
|
|
|
|
pDrive->state = AHCI_DEV_NONE;
|
|
|
|
for (j = 0; j < pDrive->queueDepth; j++)
|
|
|
|
{
|
|
|
|
if (pDrive->cmdStarted & ahciBitMask[j])
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->syncSem[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctrlMsg.msgId = AHCI_REMOVE_MSG;
|
|
|
|
ctrlMsg.drive = (char)i;
|
|
|
|
|
|
|
|
/* return from msgQSend is not needed */
|
|
|
|
|
|
|
|
(void)msgQSend(ahciMonQueue, (char *)&ctrlMsg,
|
|
|
|
VXB_AHCI_MSG_SIZE,
|
|
|
|
NO_WAIT,MSG_PRI_NORMAL);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it was a task file error reported by the
|
|
|
|
* drive, signal the monitor task
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (portIntr & AHCI_PIS_TFES)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INT,
|
|
|
|
"ahciIntr() task file error reported\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
pDrive->portError = TRUE;
|
|
|
|
pDrive->taskFileErrorCount++;
|
|
|
|
for (j=0; j < pDrive->queueDepth; j++)
|
|
|
|
{
|
|
|
|
if (pDrive->cmdStarted & ahciBitMask[j])
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->syncSem[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pDrive->cmdStarted = 0;
|
|
|
|
ctrlMsg.msgId = AHCI_PORT_ERROR_MSG;
|
|
|
|
ctrlMsg.drive = (char)i;
|
|
|
|
|
|
|
|
/* return from msgQSend is not needed */
|
|
|
|
|
|
|
|
(void)msgQSend(ahciMonQueue, (char *)&ctrlMsg,
|
|
|
|
VXB_AHCI_MSG_SIZE,
|
|
|
|
NO_WAIT,MSG_PRI_NORMAL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for completed commands, and signal the
|
|
|
|
* tasks that issued them
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (pDrive->queuedMode)
|
|
|
|
active = sataActive;
|
|
|
|
else
|
|
|
|
active = cmdActive;
|
|
|
|
for (j = 0; j < pDrive->queueDepth; j++)
|
|
|
|
{
|
|
|
|
UINT32 slotBit;
|
|
|
|
|
|
|
|
slotBit = ahciBitMask[j];
|
|
|
|
if (pDrive->cmdStarted & slotBit)
|
|
|
|
{
|
|
|
|
if (!(active & slotBit))
|
|
|
|
{
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->syncSem[j]);
|
|
|
|
pDrive->cmdStarted &= ~slotBit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciInit - Initialize a SATA disk controller
|
|
|
|
*
|
|
|
|
* This routine resets an SATA disk controller.
|
|
|
|
*
|
|
|
|
* RETURNS: OK, ERROR if the command didn't succeed.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciInit
|
|
|
|
(
|
|
|
|
SATA_HOST * pCtrl,
|
|
|
|
int drive
|
|
|
|
)
|
|
|
|
{
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
UINT32 reg;
|
|
|
|
int i;
|
|
|
|
STATUS rc;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL(pCtrl,ERROR)
|
|
|
|
|
|
|
|
pDrive = (AHCI_DRIVE *)(pCtrl->sataDev[drive]);
|
|
|
|
if (pDrive == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
ahciCmdWaitForResource(pDrive,FALSE);
|
|
|
|
|
|
|
|
pCtrl->numCtrl = pCtrl->pDev->unitNumber;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we don't notify the monitor task for any insert/remove
|
|
|
|
* events that we cause by doing a COMRESET on the device
|
|
|
|
*/
|
|
|
|
|
|
|
|
pDrive->initActive = TRUE;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> Stopping Port \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
|
|
|
|
/* Stop the port in the controller */
|
|
|
|
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCMD);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> AHCI_PxCMD reg = 0x%x \n",
|
|
|
|
reg,0,0,0,0,0);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCMD,
|
|
|
|
reg & ~(AHCI_PCMD_FRE | AHCI_PCMD_POD | AHCI_PCMD_ST));
|
|
|
|
|
|
|
|
/* Wait for the port to stop */
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((PORT_REG_READ(pDrive, AHCI_PxCMD) & AHCI_PCMD_CR) &&
|
|
|
|
(pDrive->wdgOkay))
|
|
|
|
(void) taskDelay(3);
|
|
|
|
|
|
|
|
rc = wdCancel(pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* Issue a COMRESET over Serial ATA */
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> Issue a COMRESET \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSCTL, AHCI_PSCTL_DET_RESET |
|
|
|
|
AHCI_PSCTL_IPM_NO_PARSLUM);
|
|
|
|
|
|
|
|
/* return from wdCancel is not needed */
|
|
|
|
|
|
|
|
(void)taskDelay(10);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSCTL, 0);
|
|
|
|
|
|
|
|
/* Wait for the Link to reactivate */
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> Waiting for Link \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (((PORT_REG_READ(pDrive, AHCI_PxSSTS) & AHCI_PSSTS_DET_MSK) !=
|
|
|
|
AHCI_PSSTS_DET_PHY) &&
|
|
|
|
(pDrive->wdgOkay))
|
|
|
|
(void) taskDelay(1);
|
|
|
|
rc = wdCancel(pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to AHCI 1.1, the port should be in idle state and AHCI_PCMD_FRE
|
|
|
|
* should be set before start the port DMA engine
|
|
|
|
*/
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> Restarting Port \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCMD);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCMD,
|
|
|
|
(reg | AHCI_PCMD_ICC_A | AHCI_PCMD_FRE | AHCI_PCMD_POD));
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((pDrive->wdgOkay) &&
|
|
|
|
((PORT_REG_READ(pDrive, AHCI_PxCMD) & AHCI_PCMD_CR) ||
|
|
|
|
(PORT_REG_READ(pDrive, AHCI_PxTFD) & AHCI_STAT_ACCESS)))
|
|
|
|
(void) taskDelay(3);
|
|
|
|
rc = wdCancel (pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* Start the port DMA engine */
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCMD, (PORT_REG_READ(pDrive, AHCI_PxCMD) |
|
|
|
|
AHCI_PCMD_ST));
|
|
|
|
|
|
|
|
/* The port should be in idle state before issue a soft reset command */
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((pDrive->wdgOkay) &&
|
|
|
|
(PORT_REG_READ(pDrive, AHCI_PxTFD) & AHCI_STAT_ACCESS))
|
|
|
|
taskDelay(3);
|
|
|
|
rc = wdCancel (pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* Wait for the command to be issued */
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((PORT_REG_READ(pDrive, AHCI_PxCI) & 1) && (pDrive->wdgOkay))
|
|
|
|
(void)taskDelay(1); /* return from taskDelay is not needed */
|
|
|
|
rc = wdCancel(pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* Wait for the command to be issued */
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((PORT_REG_READ(pDrive, AHCI_PxCI) & 1) && (pDrive->wdgOkay))
|
|
|
|
(void)taskDelay(1); /* return from taskDelay is not needed */
|
|
|
|
rc = wdCancel(pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciInit> Port Stat %08x\n",
|
|
|
|
PORT_REG_READ(pDrive, AHCI_PxTFD),
|
|
|
|
0,0,0,0,0);
|
|
|
|
|
|
|
|
/* Reset the command slot semaphores */
|
|
|
|
|
|
|
|
for (i = 0; i < pDrive->queueDepth; i++)
|
|
|
|
{
|
|
|
|
rc = semBInit(pDrive->syncSem[i], SEM_Q_FIFO, SEM_EMPTY);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
pDrive->cmdStarted = 0;
|
|
|
|
pDrive->queuedMode = FALSE;
|
|
|
|
|
|
|
|
pDrive->initActive = FALSE;
|
|
|
|
ahciCmdReleaseResource(pDrive,FALSE);
|
|
|
|
|
|
|
|
if (pDrive->wdgOkay)
|
|
|
|
return (OK);
|
|
|
|
else
|
|
|
|
return (ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciDriveInit - initialize SATA drive
|
|
|
|
*
|
|
|
|
* This routine checks the drive presence, identifies its type, initializes
|
|
|
|
* the drive and driver control structures with the parameters of the drive.
|
|
|
|
*
|
|
|
|
* RETURNS: OK if drive was initialized successful, or ERROR.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciDriveInit
|
|
|
|
(
|
|
|
|
SATA_HOST * pCtrl,
|
|
|
|
int drive
|
|
|
|
)
|
|
|
|
{
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
int configType = pCtrl->configType[drive];
|
|
|
|
UINT32 reg;
|
|
|
|
STATUS rc = OK;
|
|
|
|
device_t xbd;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL(pCtrl,ERROR)
|
|
|
|
|
|
|
|
pDrive = (AHCI_DRIVE *)(pCtrl->sataDev[drive]);
|
|
|
|
if (pDrive == NULL)
|
|
|
|
return (ERROR);
|
|
|
|
|
|
|
|
rc = semTake (pDrive->muteSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* Check to see if this is an ATAPI device */
|
|
|
|
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxSIG);
|
|
|
|
if ((((reg & 0x00FF0000) >> 16) == 0x14) &&
|
|
|
|
(((reg & 0xFF000000) >> 24) == 0xEB))
|
|
|
|
pDrive->sataPortDev.type = ATA_TYPE_ATAPI;
|
|
|
|
else
|
|
|
|
pDrive->sataPortDev.type = ATA_TYPE_ATA;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDriveInit> device type = %d\n",
|
|
|
|
pDrive->sataPortDev.type,
|
|
|
|
0,0,0,0,0);
|
|
|
|
|
|
|
|
if (pDrive->sataPortDev.type == ATA_TYPE_ATA)
|
|
|
|
{
|
|
|
|
rc = sataAtaNormalInit((SATA_DEVICE *)&pDrive->sataPortDev);
|
|
|
|
}
|
|
|
|
else if (pDrive->sataPortDev.type == ATA_TYPE_ATAPI)
|
|
|
|
{
|
|
|
|
rc = sataAtapiNormalInit((SATA_DEVICE *)&pDrive->sataPortDev);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->sataPortDev.attached = FALSE;
|
|
|
|
pDrive->state = AHCI_DEV_PREAD_F;
|
|
|
|
(void)semGive (pDrive->muteSem);
|
|
|
|
goto driveInitExit;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDrive->sataPortDev.attached = TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Native Command Queuing is OK if both controller and drive support it,
|
|
|
|
* and it is enabled in the Config word for the drive.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (pCtrl->okNcq && (configType & AHCI_NCQ_MODE))
|
|
|
|
pDrive->sataPortDev.okNcq = (short)
|
|
|
|
((pDrive->sataPortDev.info.sataCapabilities
|
|
|
|
& 0x0100) ? TRUE : FALSE);
|
|
|
|
else
|
|
|
|
pDrive->sataPortDev.okNcq = FALSE;
|
|
|
|
|
|
|
|
if (pDrive->sataPortDev.okNcq)
|
|
|
|
pDrive->queueDepth = min((pDrive->sataPortDev.info.queueDepth + 1),
|
|
|
|
pCtrl->numCommandSlots);
|
|
|
|
else
|
|
|
|
pDrive->queueDepth = 1;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDriveInit> Setting transfer mode to 0x%x\n",
|
|
|
|
pDrive->sataPortDev.rwMode,
|
|
|
|
0,0,0,0,0);
|
|
|
|
|
|
|
|
pDrive->sataPortDev.prdMaxSize = MAX_BYTES_PER_PRD;
|
|
|
|
pDrive->sataPortDev.maxActiveReqs = pDrive->queueDepth;
|
|
|
|
pDrive->sataPortDev.maxBiosPerReq = AHCI_MAX_PRD_ENTRIES;
|
|
|
|
|
|
|
|
if(pDrive->sataPortDev.okLba48)
|
|
|
|
pDrive->sataPortDev.fisMaxSector = MAX_SECTORS_FISLBA48;
|
|
|
|
else
|
|
|
|
pDrive->sataPortDev.fisMaxSector = MAX_SECTORS_FISNOMAL;
|
|
|
|
|
|
|
|
pDrive->sataPortDev.maxXferBlks = pDrive->sataPortDev.fisMaxSector;
|
|
|
|
|
|
|
|
pDrive->sataPortDev.xbdDev.pPort = (void *)(&pDrive->sataPortDev);
|
|
|
|
|
|
|
|
/* Set multiple mode (multisector read/write) */
|
|
|
|
|
|
|
|
if ((pDrive->sataPortDev.rwMode == AHCI_PIO_MULTI) &&
|
|
|
|
(pDrive->sataPortDev.type == ATA_TYPE_ATA))
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pDrive->sataPortDev.okMulti)
|
|
|
|
sataSetMulSector(&pDrive->sataPortDev);
|
|
|
|
else
|
|
|
|
pDrive->sataPortDev.rwMode = AHCI_PIO_SINGLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive (pDrive->muteSem);
|
|
|
|
|
|
|
|
xbd = sataXbdDevCreate(&pDrive->sataPortDev, (char *)NULL);
|
|
|
|
if (xbd != NULLDEV)
|
|
|
|
{
|
|
|
|
pDrive->state = AHCI_DEV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
driveInitExit:
|
|
|
|
|
|
|
|
if (pDrive->state != AHCI_DEV_OK)
|
|
|
|
{
|
|
|
|
return (ERROR);
|
|
|
|
}
|
|
|
|
return (OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciDrv - initialize the AHCI driver
|
|
|
|
*
|
|
|
|
* This routine initializes the AHCI driver, sets up interrupt
|
|
|
|
* vectors, and performs hardware initialization of the AHCI chip.
|
|
|
|
*
|
|
|
|
* RETURNS: OK, or ERROR if initialization fails.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciDrv
|
|
|
|
(
|
|
|
|
SATA_HOST * pCtrl
|
|
|
|
)
|
|
|
|
{
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
int drive;
|
|
|
|
int i,j;
|
|
|
|
int currPort;
|
|
|
|
int count;
|
|
|
|
/*size_t memRequired;*/
|
|
|
|
UINT8 * cmdListBlock;
|
|
|
|
/*UINT8 * cmdListBlockBak;*/
|
|
|
|
UINT8 * otherMemBlock;
|
|
|
|
UINT32 reg, reg2;
|
|
|
|
void * physAddr;
|
|
|
|
STATUS rc;
|
|
|
|
UINT32 cmdListSize;
|
|
|
|
UINT32 maxCmdListSize;
|
|
|
|
UINT32 recvFisSize;
|
|
|
|
UINT32 cmdTableSize;
|
|
|
|
UINT32 eachCmdAreaSize;
|
|
|
|
UINT32 totalCmdAreaSize;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL(pCtrl,ERROR)
|
|
|
|
|
|
|
|
pCtrl->svcTaskCount = AHCI_SVC_TASK_COUNT_DEF;
|
|
|
|
pCtrl->configType = &ahciTypes[0];
|
|
|
|
|
|
|
|
/* reset AHCI controller */
|
|
|
|
|
|
|
|
(void) CTRL_REG_READ(pCtrl, AHCI_GHC);
|
|
|
|
CTRL_REG_WRITE(pCtrl, AHCI_GHC, AHCI_GHC_HR);
|
|
|
|
|
|
|
|
/* HBA reset should be done within 1 second */
|
|
|
|
|
|
|
|
count = sysClkRateGet();
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from taskDelay is not needed */
|
|
|
|
|
|
|
|
(void)taskDelay(1);
|
|
|
|
|
|
|
|
if ((CTRL_REG_READ(pCtrl, AHCI_GHC) & AHCI_GHC_HR) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (i == count)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Reset AHCI Controller Failed\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* install the interrupt and enable it */
|
|
|
|
|
|
|
|
rc = vxbIntConnect(pCtrl->pDev, 0, (VOIDFUNCPTR)ahciIntr,
|
|
|
|
(void *)pCtrl);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
rc = vxbIntEnable(pCtrl->pDev,0,(VOIDFUNCPTR)ahciIntr, (void *)pCtrl);
|
|
|
|
if (rc != OK)
|
|
|
|
return (rc);
|
|
|
|
|
|
|
|
/* enable AHCI mode */
|
|
|
|
|
|
|
|
CTRL_REG_WRITE(pCtrl, AHCI_GHC, AHCI_GHC_AE);
|
|
|
|
|
|
|
|
/* get the number ports in the controller */
|
|
|
|
|
|
|
|
pCtrl->numPorts =
|
|
|
|
(int)((CTRL_REG_READ(pCtrl, AHCI_CAP) & AHCI_CAP_NP) + 1);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> AHCI controller reports %d ports\n",
|
|
|
|
pCtrl->numPorts,0,0,0,0,0);
|
|
|
|
|
|
|
|
/* get the number of command slots on each port */
|
|
|
|
|
|
|
|
pCtrl->numCommandSlots = (int)(((CTRL_REG_READ(pCtrl, AHCI_CAP)
|
|
|
|
& AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHFT) + 1);
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> AHCI controller reports %d command slots\n",
|
|
|
|
pCtrl->numCommandSlots,0,0,0,0,0);
|
|
|
|
|
|
|
|
/* Find out if the controller supports NCQ */
|
|
|
|
|
|
|
|
if (CTRL_REG_READ(pCtrl, AHCI_CAP) & AHCI_CAP_SNCQ)
|
|
|
|
pCtrl->okNcq = TRUE;
|
|
|
|
else
|
|
|
|
pCtrl->okNcq = FALSE;
|
|
|
|
|
|
|
|
/* Calculate the number of implemented ports */
|
|
|
|
|
|
|
|
pCtrl->numImpPorts = 0;
|
|
|
|
reg = CTRL_REG_READ(pCtrl, AHCI_PI);
|
|
|
|
|
|
|
|
for (i = 0; i < AHCI_PI_WIDTH; i++)
|
|
|
|
{
|
|
|
|
if (reg & 1)
|
|
|
|
pCtrl->numImpPorts++;
|
|
|
|
reg >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> AHCI controller reports %d implemented ports\n",
|
|
|
|
pCtrl->numImpPorts,0,0,0,0,0);
|
|
|
|
|
|
|
|
|
|
|
|
/* Update the sataHostDmaParentTag */
|
|
|
|
|
|
|
|
pCtrl->sataHostDmaParentTag = vxbDmaBufTagParentGet (pCtrl->pDev, 0);
|
|
|
|
lstInit(&pCtrl->freeReqPrivList);
|
|
|
|
|
|
|
|
/* Create user buf tag ID */
|
|
|
|
|
|
|
|
pCtrl->userBufTagId = vxbDmaBufTagCreate(
|
|
|
|
pCtrl->pDev, /* pInst */
|
|
|
|
pCtrl->sataHostDmaParentTag, /* parent */
|
|
|
|
1, /* alignment */
|
|
|
|
0, /* boundary */
|
|
|
|
VXB_SPACE_MAXADDR_32BIT, /* lowaddr */
|
|
|
|
VXB_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, /* filter */
|
|
|
|
NULL, /* filterarg */
|
|
|
|
ATA_MAX_SG_CNT * AHCI_PRD_MAX_BYTES, /* max size */
|
|
|
|
ATA_MAX_SG_CNT, /* nSegments */
|
|
|
|
AHCI_PRD_MAX_BYTES, /* max seg size */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, /* lockfunc */
|
|
|
|
NULL, /* lockarg */
|
|
|
|
NULL); /* ppDmaTag */
|
|
|
|
|
|
|
|
if (pCtrl->userBufTagId == NULL)
|
|
|
|
{
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdListSize = sizeof(AHCI_CMD_LIST);
|
|
|
|
recvFisSize = sizeof(AHCI_RECV_FIS);
|
|
|
|
cmdTableSize = (sizeof(AHCI_CMD_TABLE) * (size_t)pCtrl->numCommandSlots);
|
|
|
|
maxCmdListSize = cmdListSize * AHCI_MAX_CMD_SLOTS;
|
|
|
|
|
|
|
|
/* CMD_LIST + RECV_FIS + CMD_TABLE */
|
|
|
|
|
|
|
|
eachCmdAreaSize = (maxCmdListSize +
|
|
|
|
recvFisSize +
|
|
|
|
cmdTableSize);
|
|
|
|
|
|
|
|
totalCmdAreaSize = (size_t)pCtrl->numImpPorts * eachCmdAreaSize;
|
|
|
|
|
|
|
|
/* Create cmdListTagId for command list area */
|
|
|
|
|
|
|
|
pCtrl->sataCmdListTag = vxbDmaBufTagCreate (
|
|
|
|
pCtrl->pDev,
|
|
|
|
pCtrl->sataHostDmaParentTag, /* parent */
|
|
|
|
1024, /* alignment */
|
|
|
|
0, /* boundary */
|
|
|
|
VXB_SPACE_MAXADDR_32BIT, /* lowaddr */
|
|
|
|
VXB_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, /* filter */
|
|
|
|
NULL, /* filterarg */
|
|
|
|
totalCmdAreaSize, /* max size */
|
|
|
|
1, /* nSegments */
|
|
|
|
totalCmdAreaSize, /* max seg size */
|
|
|
|
VXB_DMABUF_NOCACHE, /* flags */
|
|
|
|
NULL, /* lockfunc */
|
|
|
|
NULL, /* lockarg */
|
|
|
|
NULL); /* ppDmaTag */
|
|
|
|
|
|
|
|
if (pCtrl->sataCmdListTag == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> sataCmdListTag create fail\n",
|
|
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory for command list area */
|
|
|
|
|
|
|
|
pCtrl->pCmdList = vxbDmaBufMemAlloc (
|
|
|
|
pCtrl->pDev,
|
|
|
|
pCtrl->sataCmdListTag,
|
|
|
|
NULL,
|
|
|
|
VXB_DMABUF_MEMALLOC_CLEAR_BUF,
|
|
|
|
&pCtrl->sataCmdListMap);
|
|
|
|
|
|
|
|
if (pCtrl->pCmdList == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> pCmdList create fail\n",
|
|
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load the DMA MAP so that we get the correct BUS address
|
|
|
|
* to program the hardware
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(OK != vxbDmaBufMapLoad (pCtrl->pDev,
|
|
|
|
pCtrl->sataCmdListTag,
|
|
|
|
pCtrl->sataCmdListMap,
|
|
|
|
pCtrl->pCmdList,
|
|
|
|
totalCmdAreaSize,
|
|
|
|
0))
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> vxbDmaBufMapLoad pCmdList create fail\n",
|
|
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdListBlock = (UINT8 *) (pCtrl->pCmdList);
|
|
|
|
otherMemBlock = cmdListBlock + pCtrl->numImpPorts * maxCmdListSize;
|
|
|
|
/* Now setup the implemented port structures */
|
|
|
|
|
|
|
|
currPort = 0;
|
|
|
|
reg = CTRL_REG_READ(pCtrl, AHCI_PI);
|
|
|
|
for (i = 0; i < AHCI_PI_WIDTH; i++)
|
|
|
|
{
|
|
|
|
if (reg & 1)
|
|
|
|
{
|
|
|
|
pCtrl->sataDev[currPort] = (void *)malloc(sizeof(AHCI_DRIVE));
|
|
|
|
|
|
|
|
if (pCtrl->sataDev[currPort] == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to allocate memory for"
|
|
|
|
"controller structures\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
bzero((char *)pCtrl->sataDev[currPort], sizeof(AHCI_DRIVE));
|
|
|
|
pDrive = (AHCI_DRIVE *)(pCtrl->sataDev[currPort]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store logic port number to fix discontiguous
|
|
|
|
* port and port number not begin from 0 issue.
|
|
|
|
* Now sataPortDev.num is set logic port num,
|
|
|
|
* portPhyNum is set physical port number.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pDrive->sataPortDev.host = pCtrl;
|
|
|
|
pDrive->sataPortDev.num = (UINT32)currPort;
|
|
|
|
pDrive->sataPortDev.okSpin = FALSE;
|
|
|
|
pDrive->regsAddr = (void *)((ULONG)pCtrl->regBase[0] +
|
|
|
|
(ULONG)i * 0x80 + 0x100);
|
|
|
|
pDrive->intCount = 0;
|
|
|
|
pDrive->timeoutErrorCount = 0;
|
|
|
|
pDrive->taskFileErrorCount = 0;
|
|
|
|
pDrive->nextTag = 0;
|
|
|
|
pDrive->portPhyNum = (UINT8)i;
|
|
|
|
currPort++;
|
|
|
|
|
|
|
|
/* Allocate and set the pointers to the command list */
|
|
|
|
|
|
|
|
pDrive->commandList = (AHCI_CMD_LIST *)cmdListBlock;
|
|
|
|
|
|
|
|
physAddr = ahciVirtToPciAddr (cmdListBlock);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCLB, VXB_ADDR_LOW32(physAddr));
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCLBU, VXB_ADDR_HIGH32(physAddr));
|
|
|
|
cmdListBlock += maxCmdListSize;
|
|
|
|
|
|
|
|
/* Allocate and set the pointers to the received first buffer */
|
|
|
|
|
|
|
|
pDrive->recvFis = (AHCI_RECV_FIS *)otherMemBlock;
|
|
|
|
|
|
|
|
physAddr = ahciVirtToPciAddr (otherMemBlock);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxFB, VXB_ADDR_LOW32(physAddr));
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxFBU, VXB_ADDR_HIGH32(physAddr));
|
|
|
|
otherMemBlock += recvFisSize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
*Allocate the command tables, and link them
|
|
|
|
*to the command list
|
|
|
|
*/
|
|
|
|
|
|
|
|
pDrive->commandTable = (AHCI_CMD_TABLE *)otherMemBlock;
|
|
|
|
for (j = 0; j < pCtrl->numCommandSlots; j++)
|
|
|
|
{
|
|
|
|
physAddr = ahciVirtToPciAddr (&pDrive->commandTable[j]);
|
|
|
|
|
|
|
|
pDrive->commandList[j].cmdTableAddressLow =
|
|
|
|
AHCI_SWAP(VXB_ADDR_LOW32(physAddr));
|
|
|
|
pDrive->commandList[j].cmdTableAddressHigh =
|
|
|
|
AHCI_SWAP(VXB_ADDR_HIGH32(physAddr));
|
|
|
|
}
|
|
|
|
otherMemBlock += cmdTableSize;
|
|
|
|
|
|
|
|
/* Flush the cache */
|
|
|
|
|
|
|
|
vxbDmaBufMapSync(pCtrl->pDev,
|
|
|
|
pCtrl->sataCmdListTag,
|
|
|
|
pCtrl->sataCmdListMap,
|
|
|
|
((off_t)pDrive->commandList - (off_t)pCtrl->pCmdList),
|
|
|
|
(size_t)pCtrl->numCommandSlots * cmdListSize,
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Flush the cache */
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Setup Logic Port Number %d: %p %p %p\n",
|
|
|
|
pDrive->sataPortDev.num,pDrive->commandList,
|
|
|
|
pDrive->recvFis,pDrive->commandTable,0,0);
|
|
|
|
|
|
|
|
/* Create the sync and mutex semaphores for the port */
|
|
|
|
|
|
|
|
for (j = 0; j < pCtrl->numCommandSlots; j++)
|
|
|
|
{
|
|
|
|
pDrive->syncSem[j] = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
|
|
|
|
if (pDrive->syncSem[j] == SEM_ID_NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create syncSem for"
|
|
|
|
"%d slot\n",
|
|
|
|
j,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pDrive->cmdStarted = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the queue depth to the number of command slots, until
|
|
|
|
* we can read it from the drive.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pDrive->queueDepth = pCtrl->numCommandSlots;
|
|
|
|
pDrive->queuedMode = FALSE;
|
|
|
|
pDrive->muteSem = semMCreate(SEM_Q_PRIORITY |
|
|
|
|
SEM_DELETE_SAFE |
|
|
|
|
SEM_INVERSION_SAFE);
|
|
|
|
if (pDrive->muteSem == SEM_ID_NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create muteSem \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
pDrive->tagMuteSem = semMCreate(SEM_Q_FIFO | SEM_DELETE_SAFE);
|
|
|
|
if (pDrive->tagMuteSem == SEM_ID_NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create tagMuteSem \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDrive->queueSlotSem = semCCreate(SEM_Q_PRIORITY, 1);
|
|
|
|
if (pDrive->queueSlotSem == SEM_ID_NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create queueSlotSem \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDrive->monSyncSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
|
|
|
|
if (pDrive->monSyncSem == SEM_ID_NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> Unable to create monSyncSem \n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
pDrive->wdgId = wdCreate ();
|
|
|
|
if (pDrive->wdgId == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_INIT,
|
|
|
|
"<ahciDrv> wdCreate() fail",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
pDrive->wdgTimeout =
|
|
|
|
( (usrAhciWatchdogValGet() > 0) ? usrAhciWatchdogValGet(): AHCI_WDG_TIMEOUT_DEF);
|
|
|
|
pDrive->semTimeout =
|
|
|
|
((usrAhciSemTimoutValGet() > 0) ? usrAhciSemTimoutValGet(): AHCI_SEM_TIMEOUT_DEF);
|
|
|
|
pDrive->slotBit = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
/* Clear any interrupts that have occurred on the port */
|
|
|
|
|
|
|
|
reg2 = PORT_REG_READ(pDrive, AHCI_PxSERR);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSERR, reg2);
|
|
|
|
reg2 = PORT_REG_READ(pDrive, AHCI_PxIS);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxIS, reg2);
|
|
|
|
|
|
|
|
/* Enable desired interrupts on the port */
|
|
|
|
|
|
|
|
PORT_REG_WRITE (pDrive, AHCI_PxIE,
|
|
|
|
(AHCI_PIE_PRCE | AHCI_PIE_PCE |
|
|
|
|
AHCI_PIE_PSE | AHCI_PIE_DSE |
|
|
|
|
AHCI_PIE_DPE | AHCI_PIE_DHRE |
|
|
|
|
AHCI_PIS_SDBS));
|
|
|
|
|
|
|
|
/* activate the port */
|
|
|
|
|
|
|
|
reg2 = AHCI_PCMD_ICC_A | AHCI_PCMD_FRE | AHCI_PCMD_POD;
|
|
|
|
PORT_REG_WRITE (pDrive, AHCI_PxCMD, reg2);
|
|
|
|
|
|
|
|
/* spin up the port */
|
|
|
|
|
|
|
|
if (CTRL_REG_READ(pCtrl, AHCI_CAP) & AHCI_CAP_SSS)
|
|
|
|
{
|
|
|
|
reg2 |= AHCI_PCMD_SUD;
|
|
|
|
PORT_REG_WRITE (pDrive, AHCI_PxCMD, reg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear BUSY and DATA REQUEST flag via setting AHCI_PCMD_CLO*/
|
|
|
|
if (CTRL_REG_READ(pCtrl, AHCI_CAP) & AHCI_CAP_SCLO)
|
|
|
|
{
|
|
|
|
reg2 = PORT_REG_READ(pDrive, AHCI_PxCMD);
|
|
|
|
reg2 |= AHCI_PCMD_CLO;
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCMD, reg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for setting complete */
|
|
|
|
|
|
|
|
pDrive->wdgOkay = TRUE;
|
|
|
|
rc = wdStart (pDrive->wdgId, (pDrive->wdgTimeout),
|
|
|
|
(WD_RTN) ahciWdog, (_Vx_usr_arg_t) pDrive);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((PORT_REG_READ(pDrive, AHCI_PxTFD) & AHCI_STAT_BUSY /* AHCI_STAT_ACCESS */) &&
|
|
|
|
(pDrive->wdgOkay))
|
|
|
|
taskDelay(3);
|
|
|
|
rc = wdCancel (pDrive->wdgId);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
goto errorOut;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reg >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable AHCI interrupts */
|
|
|
|
|
|
|
|
CTRL_REG_WRITE(pCtrl, AHCI_GHC,
|
|
|
|
CTRL_REG_READ(pCtrl, AHCI_GHC) | AHCI_GHC_IE);
|
|
|
|
|
|
|
|
for (drive = 0; drive < pCtrl->numImpPorts ; drive++)
|
|
|
|
{
|
|
|
|
pDrive = (AHCI_DRIVE *)(pCtrl->sataDev[drive]);
|
|
|
|
pDrive->state = AHCI_DEV_INIT;
|
|
|
|
pDrive->sataPortDev.type = AHCI_TYPE_NONE;
|
|
|
|
pDrive->sataPortDev.okNcq = FALSE;
|
|
|
|
pDrive->portError = FALSE;
|
|
|
|
pDrive->initActive = FALSE;
|
|
|
|
|
|
|
|
if ((PORT_REG_READ(pDrive, AHCI_PxSSTS) & AHCI_PSSTS_DET_MSK) ==
|
|
|
|
AHCI_PSSTS_DET_PHY)
|
|
|
|
{
|
|
|
|
/* check return during initialization is not needed */
|
|
|
|
|
|
|
|
(void)ahciInit(pCtrl, drive);
|
|
|
|
(void)ahciDriveInit (pCtrl, drive);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pDrive->sataPortDev.type = AHCI_TYPE_NONE;
|
|
|
|
pDrive->state = AHCI_DEV_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (OK);
|
|
|
|
|
|
|
|
errorOut:
|
|
|
|
|
|
|
|
for (i = 0; i < pCtrl->numImpPorts; i++)
|
|
|
|
{
|
|
|
|
pDrive = (AHCI_DRIVE *)pCtrl->sataDev[i];
|
|
|
|
if (pDrive != NULL)
|
|
|
|
{
|
|
|
|
if (pDrive->wdgId != NULL)
|
|
|
|
(void) wdDelete (pDrive->wdgId);
|
|
|
|
if (pDrive->monSyncSem != SEM_ID_NULL)
|
|
|
|
(void) semDelete (pDrive->monSyncSem);
|
|
|
|
if (pDrive->queueSlotSem != SEM_ID_NULL)
|
|
|
|
(void) semDelete (pDrive->queueSlotSem);
|
|
|
|
if (pDrive->tagMuteSem != SEM_ID_NULL)
|
|
|
|
(void) semDelete (pDrive->tagMuteSem);
|
|
|
|
if (pDrive->muteSem != SEM_ID_NULL)
|
|
|
|
(void) semDelete (pDrive->muteSem);
|
|
|
|
for (j = 0; j < pCtrl->numCommandSlots; j++)
|
|
|
|
{
|
|
|
|
if (pDrive->syncSem[j] != SEM_ID_NULL)
|
|
|
|
(void) semDelete (pDrive->syncSem[j]);
|
|
|
|
}
|
|
|
|
free (pDrive);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCtrl->sataCmdListTag != NULL)
|
|
|
|
{
|
|
|
|
if (pCtrl->pCmdList != NULL)
|
|
|
|
vxbDmaBufMemFree(pCtrl->sataCmdListTag,
|
|
|
|
pCtrl->pCmdList,
|
|
|
|
pCtrl->sataCmdListMap);
|
|
|
|
|
|
|
|
(void) vxbDmaBufTagDestroy(pCtrl->sataCmdListTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCtrl->userBufTagId != NULL)
|
|
|
|
{
|
|
|
|
(void) vxbDmaBufTagDestroy(pCtrl->sataCmdListTag);
|
|
|
|
}
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciCommand - Send a command to a AHCI Drive
|
|
|
|
*
|
|
|
|
* This is the common routine used to send all FIS commands to a SATA AHCI drive.
|
|
|
|
*
|
|
|
|
* RETURNS: OK, ERROR if the command didn't succeed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciSataCmdIssue
|
|
|
|
(
|
|
|
|
SATA_DEVICE * pCtrl,
|
|
|
|
FIS_ATA_REG * pFisAta,
|
|
|
|
SATA_DATA * pSataData
|
|
|
|
)
|
|
|
|
{
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
AHCI_PRD *prd;
|
|
|
|
UINT8 *commandFis;
|
|
|
|
UINT8 *pktCmd;
|
|
|
|
AHCI_CMD_LIST *commandList;
|
|
|
|
UINT32 flagsPrdLength, reg;
|
|
|
|
size_t prdCount = 0;
|
|
|
|
int key;
|
|
|
|
int tag;
|
|
|
|
int wait;
|
|
|
|
VXB_AHCI_MSG ctrlMsg;
|
|
|
|
STATUS rc;
|
|
|
|
UINT32 tagBit;
|
|
|
|
BOOL queued = (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_NCQ);
|
|
|
|
SATA_HOST * pSataHost;
|
|
|
|
off_t fisOffset;
|
|
|
|
off_t prdOffset;
|
|
|
|
off_t listOffset;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL(pCtrl,ERROR)
|
|
|
|
VXB_ASSERT_NONNULL(pFisAta,ERROR)
|
|
|
|
|
|
|
|
pDrive = (AHCI_DRIVE *)(pCtrl->host->sataDev[pCtrl->num]);
|
|
|
|
if (pDrive == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
pSataHost = pCtrl->host;
|
|
|
|
|
|
|
|
if ((pFisAta->fisCmd.fisCmdFlag &
|
|
|
|
(ATA_FLAG_SRST_ON | ATA_FLAG_SRST_OFF)) == 0)
|
|
|
|
ahciCmdWaitForResource(pDrive,queued);
|
|
|
|
|
|
|
|
if (queued)
|
|
|
|
{
|
|
|
|
rc = semTake(pDrive->tagMuteSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop while incrementing the tag until we find a free one */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
tag = pDrive->nextTag;
|
|
|
|
pDrive->nextTag = (pDrive->nextTag + 1) % pDrive->queueDepth;
|
|
|
|
} while (pDrive->cmdStarted & (ahciBitMask[tag]));
|
|
|
|
|
|
|
|
tagBit = ahciBitMask[tag];
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->tagMuteSem);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/* If we aren't queueing, we always use slot zero for the command */
|
|
|
|
|
|
|
|
tag = 0;
|
|
|
|
tagBit = 0x00000001;
|
|
|
|
}
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciDrv> ahciCommand: pCtrl=%p drive=%d tag=%d"
|
|
|
|
"flags=%x qm=%d\n",
|
|
|
|
pCtrl, pDrive, tag,pFisAta->fisCmd.fisCmdFlag,
|
|
|
|
pDrive->queuedMode,0);
|
|
|
|
|
|
|
|
prd = &pDrive->commandTable[tag].prd[0];
|
|
|
|
commandFis = pDrive->commandTable[tag].commandFis;
|
|
|
|
pktCmd = pDrive->commandTable[tag].atapiCommand;
|
|
|
|
commandList = &pDrive->commandList[tag];
|
|
|
|
|
|
|
|
prdOffset = ((off_t)prd - (off_t)pSataHost->pCmdList);
|
|
|
|
fisOffset = ((off_t)commandFis - (off_t)pSataHost->pCmdList);
|
|
|
|
listOffset = ((off_t)commandList - (off_t)pSataHost->pCmdList);
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_SRST_ON)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a soft reset on FIS to the device. We don't wait for an
|
|
|
|
* interrupt in this case, as there are none in response to
|
|
|
|
* this FIS
|
|
|
|
*/
|
|
|
|
|
|
|
|
memcpy(commandFis, &(pFisAta->fisCmd.fisAtaCmd),
|
|
|
|
(pFisAta->fisCmd.fisCmdLen * sizeof(UINT32)));
|
|
|
|
commandList->flagsPrdLength = AHCI_SWAP(AHCI_CMD_LIST_C |
|
|
|
|
AHCI_CMD_LIST_R |
|
|
|
|
pFisAta->fisCmd.fisCmdLen);
|
|
|
|
/* Flush Command fis */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
fisOffset,
|
|
|
|
pFisAta->fisCmd.fisCmdLen * sizeof(UINT32),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
return(OK);
|
|
|
|
}
|
|
|
|
else if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_SRST_OFF)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a soft reset off FIS to the device. We don't wait for an
|
|
|
|
* interrupt in this case, as there are none in response to
|
|
|
|
* this FIS
|
|
|
|
*/
|
|
|
|
|
|
|
|
memcpy(commandFis, &(pFisAta->fisCmd.fisAtaCmd),
|
|
|
|
(pFisAta->fisCmd.fisCmdLen * sizeof(UINT32)));
|
|
|
|
commandList->flagsPrdLength = AHCI_SWAP(pFisAta->fisCmd.fisCmdLen);
|
|
|
|
|
|
|
|
/* Flush Command fis */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
fisOffset,
|
|
|
|
pFisAta->fisCmd.fisCmdLen * sizeof(UINT32),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
return(OK);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Build a FIS with an ATAPI command packet in it */
|
|
|
|
|
|
|
|
memcpy(commandFis, &(pFisAta->fisCmd.fisAtaCmd),
|
|
|
|
(pFisAta->fisCmd.fisCmdLen * sizeof(UINT32)));
|
|
|
|
if(pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_ATAPI)
|
|
|
|
memcpy(pktCmd, &(pFisAta->fisCmd.fisApatiCmd),
|
|
|
|
AHCI_ATAPI_MAX_CMD_LENGTH);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> fis: %02x %02x %02x %02x %02x %02x\n",
|
|
|
|
commandFis[0],commandFis[1],commandFis[2],
|
|
|
|
commandFis[3],commandFis[4],commandFis[5]);
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> fis: %02x %02x %02x %02x %02x %02x\n",
|
|
|
|
commandFis[6],commandFis[7],commandFis[8],
|
|
|
|
commandFis[9],commandFis[10],commandFis[11]);
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> fis: %02x %02x %02x %02x %02x %02x\n",
|
|
|
|
commandFis[12],commandFis[13],commandFis[14],
|
|
|
|
commandFis[15],commandFis[16],commandFis[17]);
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> fis: %02x %02x\n",
|
|
|
|
commandFis[18],commandFis[19],0,0,0,0);
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_NON_DATA)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* All of our command FIS's are 5 32 bit words long */
|
|
|
|
|
|
|
|
flagsPrdLength = pFisAta->fisCmd.fisCmdLen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prdCount = ahciPrdSetup((UINT8 *)pSataData->buffer,
|
|
|
|
(UINT32)(pSataData->blkSize * pSataData->blkNum),
|
|
|
|
prd);
|
|
|
|
if (prdCount == (size_t)ERROR)
|
|
|
|
{
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store the length of the table, along with any flags that
|
|
|
|
* are needed
|
|
|
|
*/
|
|
|
|
|
|
|
|
flagsPrdLength = (UINT32)(prdCount << AHCI_CMD_LIST_PRDTL_SHFT) | 5;
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag == ATA_FLAG_OUT_DATA)
|
|
|
|
flagsPrdLength |= AHCI_CMD_LIST_W;
|
|
|
|
if ((pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_NCQ) == 0)
|
|
|
|
flagsPrdLength |= AHCI_CMD_LIST_P;
|
|
|
|
}
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_ATAPI)
|
|
|
|
flagsPrdLength |= (AHCI_CMD_LIST_A | AHCI_CMD_LIST_P);
|
|
|
|
commandList->flagsPrdLength = AHCI_SWAP(flagsPrdLength);
|
|
|
|
commandList->recvByteCount = 0;
|
|
|
|
|
|
|
|
/* Flush the memory areas we updated for the controller */
|
|
|
|
|
|
|
|
/* Flush the prd table */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
prdOffset,
|
|
|
|
prdCount * sizeof(AHCI_PRD),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Flush Command list */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
listOffset,
|
|
|
|
sizeof(AHCI_CMD_LIST),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Flush Command fis */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
fisOffset,
|
|
|
|
pFisAta->fisCmd.fisCmdLen * sizeof(UINT32),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_ATAPI)
|
|
|
|
{
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
((off_t)pktCmd - (off_t)pSataHost->pCmdList),
|
|
|
|
AHCI_ATAPI_MAX_CMD_LENGTH,
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> flagsPrdLength: %08x\n",
|
|
|
|
commandList->flagsPrdLength,0,0,0,0,0);
|
|
|
|
|
|
|
|
/* Flush the data buffer for writes */
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_OUT_DATA)
|
|
|
|
CACHE_USER_FLUSH(pSataData->buffer,
|
|
|
|
pSataData->blkSize * pSataData->blkNum);
|
|
|
|
|
|
|
|
/* Start the command */
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_NCQ)
|
|
|
|
{
|
|
|
|
key = intCpuLock();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For queued commands we must write both the SATA active
|
|
|
|
* and command init registers
|
|
|
|
*/
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSACT, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxSACT);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCI);
|
|
|
|
pDrive->cmdStarted |= tagBit;
|
|
|
|
intCpuUnlock(key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
key = intCpuLock();
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCI);
|
|
|
|
pDrive->cmdStarted |= tagBit;
|
|
|
|
intCpuUnlock(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for completion interrupt
|
|
|
|
* If it was a command that needs to wait for spinup, then
|
|
|
|
* wait 20 seconds to complete, otherwise, just wait
|
|
|
|
* the set delay.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_WAIT_SPINUP)
|
|
|
|
wait = sysClkRateGet() * 20;
|
|
|
|
else
|
|
|
|
wait = pDrive->semTimeout;
|
|
|
|
if (semTake (pDrive->syncSem[tag], wait) == ERROR)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> semTake syncSem timeout:"
|
|
|
|
"tag = %d pCtrl=%p\n",
|
|
|
|
pCtrl,tag,0,0,0,0);
|
|
|
|
|
|
|
|
pDrive->timeoutErrorCount++;
|
|
|
|
ctrlMsg.msgId = AHCI_PORT_ERROR_MSG;
|
|
|
|
ctrlMsg.drive = (char)pDrive->sataPortDev.num;
|
|
|
|
ctrlMsg.pCtrl = pCtrl->host;
|
|
|
|
|
|
|
|
/* return from msgQSend is not needed */
|
|
|
|
|
|
|
|
(void)msgQSend(ahciMonQueue, (char *)&ctrlMsg,
|
|
|
|
VXB_AHCI_MSG_SIZE, NO_WAIT, MSG_PRI_NORMAL);
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If drive isn't there, or an error occurred and the transfer
|
|
|
|
* did not complete, return error
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((pDrive->state != AHCI_DEV_OK && pDrive->state != AHCI_DEV_INIT) ||
|
|
|
|
pDrive->portError)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSataCmdIssue> port error: pDriver=%02x,"
|
|
|
|
" pCtrl=%p\n",
|
|
|
|
pCtrl,pDrive,0,0,0,0);
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
if (pDrive->state == AHCI_DEV_NONE)
|
|
|
|
(void) errnoSet (S_ioLib_DISK_NOT_PRESENT);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* invalidate the read buffer */
|
|
|
|
|
|
|
|
if (pFisAta->fisCmd.fisCmdFlag & ATA_FLAG_IN_DATA)
|
|
|
|
CACHE_USER_INVALIDATE(pSataData->buffer,
|
|
|
|
pSataData->blkSize * pSataData->blkNum);
|
|
|
|
|
|
|
|
}
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
|
|
|
|
if ((commandFis[3] == ATA_SMART_CMD_RETURN_STATUS) &&
|
|
|
|
(commandFis[2] == ATA_CMD_SMART))
|
|
|
|
{
|
|
|
|
UINT8 cylLo, cylHi;
|
|
|
|
int * ptr;
|
|
|
|
int lrec = ERROR;
|
|
|
|
|
|
|
|
ptr = (int *)pSataData->buffer;
|
|
|
|
cylLo = pDrive->recvFis->d2hRegisterFis[5];
|
|
|
|
cylHi = pDrive->recvFis->d2hRegisterFis[6];
|
|
|
|
if (cylLo == 0x4F && cylHi == 0xC2 )
|
|
|
|
{
|
|
|
|
lrec = ATA_SMART_OK;
|
|
|
|
}
|
|
|
|
else if (cylLo == 0xF4 && cylHi == 0x2C )
|
|
|
|
{
|
|
|
|
lrec = ATA_SMART_EXCEEDED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lrec = ERROR;
|
|
|
|
}
|
|
|
|
if (NULL != ptr)
|
|
|
|
{
|
|
|
|
*ptr = lrec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciCmdWaitForResource - Wait for access to the drive port.
|
|
|
|
*
|
|
|
|
* This routine waits for the drive (if in non-queued mode), or a available
|
|
|
|
* command slot/tag in the drive (if in queued mode).
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void ahciCmdWaitForResource
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
BOOL queued
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
STATUS rc;
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL_V(pDrive)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the drive doesn't support native command queueing, just take the
|
|
|
|
* drive mutex to hold of other tasks and do one command at a time
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!pDrive->sataPortDev.okNcq)
|
|
|
|
{
|
|
|
|
rc = semTake(pDrive->muteSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a queued mode command, then we need to take one of the
|
|
|
|
* slot semaphores to guarantee we do not try to queue more commands
|
|
|
|
* than we have slots
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (queued)
|
|
|
|
{
|
|
|
|
rc = semTake(pDrive->queueSlotSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are not in queued mode, then set queued mode to true,
|
|
|
|
* and give all but one of the slot semaphores (the one that we
|
|
|
|
* hold), so that other tasks may queue commands. We lock
|
|
|
|
* tasks while we are doing this, so that the queued mode flag
|
|
|
|
* is not read, then changed before we get a chance to change
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
(void) taskCpuLock();
|
|
|
|
if (!pDrive->queuedMode)
|
|
|
|
{
|
|
|
|
pDrive->queuedMode = TRUE;
|
|
|
|
(void) taskCpuUnlock();
|
|
|
|
for (i = 0; i < pDrive->queueDepth-1; i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from taskDelay is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->queueSlotSem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(void) taskCpuUnlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For an unqueued command, we take the drive mutex, to make sure
|
|
|
|
* we only do one unqueued command at a time
|
|
|
|
*/
|
|
|
|
|
|
|
|
rc = semTake(pDrive->muteSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we were in queued mode, then we need to take all of the slot
|
|
|
|
* semaphores, which will mean all of the queued commands have
|
|
|
|
* completed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (pDrive->queuedMode)
|
|
|
|
{
|
|
|
|
for (i=0;i<pDrive->queueDepth;i++)
|
|
|
|
{
|
|
|
|
rc = semTake(pDrive->queueSlotSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pDrive->queuedMode = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, just take the single queued slot sem which is
|
|
|
|
* available in unqueued mode, to prevent a queued command from
|
|
|
|
* being executed until we are done
|
|
|
|
*/
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = semTake(pDrive->queueSlotSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciCmdReleaseResource - Release a drive port.
|
|
|
|
*
|
|
|
|
* This routine releases a drive (if in non-queued mode), or an available
|
|
|
|
* command slot/tag in the drive (if in queued mode).
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void ahciCmdReleaseResource
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
BOOL queued
|
|
|
|
)
|
|
|
|
{
|
|
|
|
|
|
|
|
VXB_ASSERT_NONNULL_V(pDrive)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the drive doesn't support native command queueing, just give the
|
|
|
|
* drive mutex to allow other tasks to run
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!pDrive->sataPortDev.okNcq)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->muteSem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, give the queue slot semaphore. If it wasn't a queued
|
|
|
|
* command, then we need to also give the mutex to release our
|
|
|
|
* exclusive access to the device
|
|
|
|
*/
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->queueSlotSem);
|
|
|
|
if (!queued)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from semGive is not needed */
|
|
|
|
|
|
|
|
(void)semGive(pDrive->muteSem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciMon - Main routine of SATA monitor task
|
|
|
|
*
|
|
|
|
* This routine runs in an infinite loop, waiting for a message that a new drive
|
|
|
|
* has been inserted on a SATA controller, a port error has occurred, or a halt,
|
|
|
|
* stop, or start has been requested on a port.
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void ahciMon (void)
|
|
|
|
{
|
|
|
|
VXB_AHCI_MSG msg;
|
|
|
|
char msgId;
|
|
|
|
int drive;
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
int retry;
|
|
|
|
SATA_HOST * pCtrl;
|
|
|
|
BOOL attached;
|
|
|
|
|
|
|
|
FOREVER
|
|
|
|
{
|
|
|
|
|
|
|
|
/* return from msgQReceive is not needed */
|
|
|
|
|
|
|
|
(void) msgQReceive(ahciMonQueue, (char *)&msg, VXB_AHCI_MSG_SIZE, WAIT_FOREVER);
|
|
|
|
msgId = msg.msgId;
|
|
|
|
drive = msg.drive;
|
|
|
|
pCtrl = msg.pCtrl;
|
|
|
|
|
|
|
|
if (pCtrl == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pDrive = pCtrl->sataDev[drive];
|
|
|
|
if (pDrive == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch(msgId)
|
|
|
|
{
|
|
|
|
case AHCI_ATTACH_MSG:
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> Attach msg received\n"
|
|
|
|
" for logic port number %d\n",
|
|
|
|
pDrive->sataPortDev.num,0,0,0,0,0);
|
|
|
|
|
|
|
|
if (pDrive->state != AHCI_DEV_NONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> starting Init for logic"
|
|
|
|
" port number %d\n",
|
|
|
|
pDrive->sataPortDev.num,0,0,0,0,0);
|
|
|
|
|
|
|
|
retry = 0;
|
|
|
|
|
|
|
|
pDrive->state = AHCI_DEV_INIT;
|
|
|
|
|
|
|
|
while (ahciInit(pCtrl,drive) == ERROR)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> ataInit failed"
|
|
|
|
" on logic port number %d, retry %d\n",
|
|
|
|
pDrive->sataPortDev.num,retry,0,0,0,0);
|
|
|
|
retry++;
|
|
|
|
if (retry >= ahciRetry)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
retry = 0;
|
|
|
|
while (ahciDriveInit(pCtrl,drive) == ERROR)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> ataDrivInit"
|
|
|
|
" failed on logic port number %d,"
|
|
|
|
" retry %d\n",
|
|
|
|
pDrive->sataPortDev.num,retry,0,0,0,0);
|
|
|
|
retry++;
|
|
|
|
if (retry >= ahciRetry)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case AHCI_REMOVE_MSG:
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> Remove msg received"
|
|
|
|
" for logic port number %d\n",
|
|
|
|
pDrive->sataPortDev.num,0,0,0,0,0);
|
|
|
|
|
|
|
|
/* physical device was removed, remove logical device */
|
|
|
|
|
|
|
|
attached = pDrive->sataPortDev.attached;
|
|
|
|
pDrive->sataPortDev.attached = FALSE;
|
|
|
|
|
|
|
|
/* return from sataXbdDevDelete is not needed */
|
|
|
|
|
|
|
|
if (attached)
|
|
|
|
(void)sataXbdDevDelete(&pDrive->sataPortDev);
|
|
|
|
|
|
|
|
pDrive->sataPortDev.type = AHCI_TYPE_NONE;
|
|
|
|
break;
|
|
|
|
case AHCI_PORT_ERROR_MSG:
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_MON,
|
|
|
|
"<ahciMon> Port Error msg received"
|
|
|
|
" for logic port number %d\n",
|
|
|
|
pDrive->sataPortDev.num,0,0,0,0,0);
|
|
|
|
(void)ahciInit(pCtrl,drive);
|
|
|
|
pDrive->portError = FALSE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciPrdSetup - Setup the DMA PRD table in memory
|
|
|
|
*
|
|
|
|
* RETURNS: OK, ERROR if the setup didn't succeed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL size_t ahciPrdSetup
|
|
|
|
(
|
|
|
|
UINT8 * data,
|
|
|
|
UINT32 nBytes,
|
|
|
|
AHCI_PRD * prd
|
|
|
|
)
|
|
|
|
{
|
|
|
|
size_t prdCount = 0;
|
|
|
|
UINT32 byteCount;
|
|
|
|
UINT32 size;
|
|
|
|
void * addr;
|
|
|
|
AHCI_PRD *currPrd = prd;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdSetup> data=0x%x nBytes=%d\n",
|
|
|
|
data,nBytes,0,0,0,0);
|
|
|
|
|
|
|
|
/* Translate the address to PCI memory space */
|
|
|
|
|
|
|
|
addr = ahciVirtToPciAddr (data);
|
|
|
|
|
|
|
|
size = nBytes;
|
|
|
|
|
|
|
|
if ((ULONG)addr & 1)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdSetup> misaligned DMA buffer\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
return ((size_t)ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (size)
|
|
|
|
{
|
|
|
|
if (++prdCount > AHCI_MAX_PRD_ENTRIES)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdSetup> DMA table too small\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
return ((size_t) ERROR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (size > AHCI_PRD_MAX_BYTES)
|
|
|
|
byteCount = AHCI_PRD_MAX_BYTES;
|
|
|
|
else
|
|
|
|
byteCount = size;
|
|
|
|
|
|
|
|
currPrd->dataBaseAddressLow = AHCI_SWAP(VXB_ADDR_LOW32(addr));
|
|
|
|
currPrd->dataBaseAddressHigh = AHCI_SWAP(VXB_ADDR_HIGH32(addr));
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdSetup> Table addr %p\n",
|
|
|
|
addr,0,0,0,0,0);
|
|
|
|
|
|
|
|
addr = (void *)((ULONG)addr + byteCount);
|
|
|
|
size -= byteCount;
|
|
|
|
|
|
|
|
byteCount--;
|
|
|
|
if (size == 0)
|
|
|
|
byteCount |= AHCI_PRD_I;
|
|
|
|
|
|
|
|
currPrd->dataByteCountInterrupt = AHCI_SWAP(byteCount);
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdSetup> Table count 0x%x\n",
|
|
|
|
byteCount,0,0,0,0,0);
|
|
|
|
|
|
|
|
currPrd++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (prdCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciWdog - SATA controller watchdog handler
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void ahciWdog
|
|
|
|
(
|
|
|
|
AHCI_DRIVE *pDrive
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (pDrive == NULL)
|
|
|
|
return;
|
|
|
|
pDrive->wdgOkay = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciCmdSlotGet - get a free command slot
|
|
|
|
*
|
|
|
|
* This routine gets a free command slot.
|
|
|
|
*
|
|
|
|
* RETURNS: slot number or AHCI_MAX_CMD_SLOTS indicate no slot avaliable
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL UINT32 ahciCmdSlotGet
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
BOOL queued
|
|
|
|
)
|
|
|
|
{
|
|
|
|
STATUS rc;
|
|
|
|
UINT32 slot = AHCI_MAX_CMD_SLOTS;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we can find a free slot return the slot bit
|
|
|
|
* or else return AHCI_MAX_CMD_SLOTS(32)
|
|
|
|
* bit "1" menas the slot is free
|
|
|
|
*/
|
|
|
|
|
|
|
|
rc = semTake(pDrive->tagMuteSem, WAIT_FOREVER);
|
|
|
|
if (rc != OK)
|
|
|
|
{
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (queued)
|
|
|
|
{
|
|
|
|
/* Loop while incrementing the tag until we find a free one */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
slot = pDrive->nextTag;
|
|
|
|
pDrive->nextTag = (pDrive->nextTag + 1) % pDrive->queueDepth;
|
|
|
|
} while (!(pDrive->slotBit & (1 << slot)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pDrive->slotBit & 0x01)
|
|
|
|
slot = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* occupy the slot */
|
|
|
|
|
|
|
|
if (slot < AHCI_MAX_CMD_SLOTS)
|
|
|
|
pDrive->slotBit &= ~(1 << slot);
|
|
|
|
(void)semGive(pDrive->tagMuteSem);
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciCmdSlotRelease - release the command slot
|
|
|
|
*
|
|
|
|
* This routine release the command slot.
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL VOID ahciCmdSlotRelease
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
UINT32 slot
|
|
|
|
)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (slot >= AHCI_MAX_CMD_SLOTS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Release the slot */
|
|
|
|
|
|
|
|
if (semTake(pDrive->tagMuteSem, WAIT_FOREVER) == ERROR)
|
|
|
|
return;
|
|
|
|
pDrive->slotBit |= (1 << slot);
|
|
|
|
(void)semGive(pDrive->tagMuteSem);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciReqResourceRelease - release the request resource
|
|
|
|
*
|
|
|
|
* This routine releases the request resource.
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL VOID ahciReqResourceRelease
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
)
|
|
|
|
{
|
|
|
|
pXBD_REQ_PRIV pReqPriv;
|
|
|
|
pSATA_HOST pSataHost;
|
|
|
|
|
|
|
|
pSataHost = pDrive->sataPortDev.host;
|
|
|
|
|
|
|
|
/* Break the connection */
|
|
|
|
|
|
|
|
pReqPriv = (pXBD_REQ_PRIV)pXbdReq->drvSpecific;
|
|
|
|
if (pReqPriv != NULL)
|
|
|
|
{
|
|
|
|
/* Map Unload */
|
|
|
|
|
|
|
|
ataMapUnLoad(pSataHost, pXbdReq);
|
|
|
|
|
|
|
|
/* Release the command slot */
|
|
|
|
|
|
|
|
ahciCmdSlotRelease(pDrive, pReqPriv->cmdTag);
|
|
|
|
pXbdReq->drvSpecific = NULL;
|
|
|
|
ataReqPrivRelease(pSataHost, pReqPriv);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciReqResourceGet - get resource to handle the request
|
|
|
|
*
|
|
|
|
* This routine gets resource to handle the request
|
|
|
|
*
|
|
|
|
* RETURNS: OK, or ERROR if fail
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciReqResourceGet
|
|
|
|
(
|
|
|
|
AHCI_DRIVE * pDrive,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int slot;
|
|
|
|
pXBD_REQ_PRIV pReqPriv;
|
|
|
|
pSATA_HOST pSataHost;
|
|
|
|
|
|
|
|
pSataHost = pDrive->sataPortDev.host;
|
|
|
|
|
|
|
|
pReqPriv = ataReqPrivGet(pSataHost, VXB_DMABUF_MAPCREATE_NOBOUNCE_BUF);
|
|
|
|
if (pReqPriv == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The request only handle read/write command, so if the device support
|
|
|
|
* NCQ, the command must be queued command
|
|
|
|
*/
|
|
|
|
|
|
|
|
slot = ahciCmdSlotGet(pDrive, pDrive->sataPortDev.okNcq);
|
|
|
|
if (slot == AHCI_MAX_CMD_SLOTS)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: no free slot\n",
|
|
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
ataReqPrivRelease(pSataHost, pReqPriv);
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
pXbdReq->drvSpecific = pReqPriv;
|
|
|
|
pReqPriv->pXbdReq = pXbdReq;
|
|
|
|
pReqPriv->cmdTag = slot;
|
|
|
|
|
|
|
|
/* Map request */
|
|
|
|
|
|
|
|
if (OK != ataMapLoad(pSataHost, pXbdReq))
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: can not map the request\n",
|
|
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
ahciCmdSlotRelease(pDrive, slot);
|
|
|
|
ataReqPrivRelease(pSataHost, pReqPriv);
|
|
|
|
pXbdReq->drvSpecific = NULL;
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciPrdBuild - build prd table
|
|
|
|
*
|
|
|
|
* This routine builds prd table.
|
|
|
|
*
|
|
|
|
* RETURNS: prd count
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL size_t ahciPrdBuild
|
|
|
|
(
|
|
|
|
pSG_LIST pSgList,
|
|
|
|
AHCI_PRD * pPrd
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
UINT32 byteCount;
|
|
|
|
bus_addr_t busAddr;
|
|
|
|
UINT32 size;
|
|
|
|
size_t prdCount = 0;
|
|
|
|
|
|
|
|
/* Build prd table */
|
|
|
|
|
|
|
|
for (i = 0; i < pSgList->bufCnt; i++)
|
|
|
|
{
|
|
|
|
size = pSgList->bufVec[i].iov_len;
|
|
|
|
busAddr = (bus_addr_t)pSgList->bufVec[i].iov_base;
|
|
|
|
|
|
|
|
while (size)
|
|
|
|
{
|
|
|
|
if (++prdCount > AHCI_MAX_PRD_ENTRIES)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdBuild> DMA table too small\n",
|
|
|
|
0,0,0,0,0,0);
|
|
|
|
return ((size_t) ERROR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (size > AHCI_PRD_MAX_BYTES)
|
|
|
|
byteCount = AHCI_PRD_MAX_BYTES;
|
|
|
|
else
|
|
|
|
byteCount = size;
|
|
|
|
|
|
|
|
pPrd->dataBaseAddressLow = AHCI_SWAP(VXB_ADDR_LOW32(busAddr));
|
|
|
|
pPrd->dataBaseAddressHigh = AHCI_SWAP(VXB_ADDR_HIGH32(busAddr));
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdBuild> Table addr %p\n",
|
|
|
|
busAddr,0,0,0,0,0);
|
|
|
|
|
|
|
|
busAddr = (busAddr + byteCount);
|
|
|
|
size -= byteCount;
|
|
|
|
|
|
|
|
byteCount--;
|
|
|
|
if ((size == 0) && (i == (pSgList->bufCnt - 1)))
|
|
|
|
byteCount |= AHCI_PRD_I;
|
|
|
|
|
|
|
|
pPrd->dataByteCountInterrupt = AHCI_SWAP(byteCount);
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciPrdBuild> Table count 0x%x\n",
|
|
|
|
byteCount,0,0,0,0,0);
|
|
|
|
|
|
|
|
pPrd++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return prdCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciSgTrans - transfer the scatter-gather list
|
|
|
|
*
|
|
|
|
* This routine transfer the scatter-gather list
|
|
|
|
*
|
|
|
|
* RETURNS: OK, or ERROR if fail
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL STATUS ahciSgTrans
|
|
|
|
(
|
|
|
|
SATA_DEVICE * pSataDev,
|
|
|
|
pATA_CMD_DESC pAtaCmdDesc,
|
|
|
|
pSG_LIST pSgList
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int key;
|
|
|
|
int wait;
|
|
|
|
BOOL queued;
|
|
|
|
UINT32 tag;
|
|
|
|
UINT32 factor;
|
|
|
|
UINT8 * commandFis;
|
|
|
|
UINT32 reg;
|
|
|
|
UINT32 tagBit;
|
|
|
|
UINT32 flagsPrdLength;
|
|
|
|
AHCI_PRD * pPrd ;
|
|
|
|
AHCI_CMD_LIST * commandList;
|
|
|
|
VXB_AHCI_MSG ctrlMsg;
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
SATA_HOST * pSataHost;
|
|
|
|
off_t fisOffset;
|
|
|
|
off_t prdOffset;
|
|
|
|
off_t listOffset;
|
|
|
|
size_t prdCount = 0;
|
|
|
|
|
|
|
|
pSataHost = pSataDev->host;
|
|
|
|
pDrive = (AHCI_DRIVE *)(pSataHost->sataDev[pSataDev->num]);
|
|
|
|
queued = (pAtaCmdDesc->flags & ATA_FLAG_NCQ);
|
|
|
|
tag = pAtaCmdDesc->tag;
|
|
|
|
|
|
|
|
ahciCmdWaitForResource(pDrive, queued);
|
|
|
|
|
|
|
|
tagBit = (1 << tag);
|
|
|
|
pPrd = &pDrive->commandTable[tag].prd[0];
|
|
|
|
commandFis = pDrive->commandTable[tag].commandFis;
|
|
|
|
commandList = &pDrive->commandList[tag];
|
|
|
|
|
|
|
|
prdOffset = ((off_t)pPrd - (off_t)pSataHost->pCmdList);
|
|
|
|
fisOffset = ((off_t)commandFis - (off_t)pSataHost->pCmdList);
|
|
|
|
listOffset = ((off_t)commandList - (off_t)pSataHost->pCmdList);
|
|
|
|
|
|
|
|
/* Build Fis */
|
|
|
|
|
|
|
|
sataCmdDescToFis(pAtaCmdDesc, commandFis);
|
|
|
|
|
|
|
|
/* Build prd table */
|
|
|
|
|
|
|
|
prdCount = ahciPrdBuild(pSgList, pPrd);
|
|
|
|
if (prdCount == (size_t)ERROR)
|
|
|
|
{
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store the length of the table, along with any flags that are needed */
|
|
|
|
|
|
|
|
flagsPrdLength = (UINT32)(prdCount << AHCI_CMD_LIST_PRDTL_SHFT) | 5;
|
|
|
|
|
|
|
|
if (pAtaCmdDesc->flags & ATA_FLAG_OUT_DATA)
|
|
|
|
flagsPrdLength |= AHCI_CMD_LIST_W;
|
|
|
|
if (!queued)
|
|
|
|
flagsPrdLength |= AHCI_CMD_LIST_P;
|
|
|
|
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"<ahciSgTrans> flagsPrdLength: %08x prdCount %d\n",
|
|
|
|
commandList->flagsPrdLength, prdCount,0,0,0,0);
|
|
|
|
|
|
|
|
commandList->flagsPrdLength = AHCI_SWAP(flagsPrdLength);
|
|
|
|
commandList->recvByteCount = 0;
|
|
|
|
|
|
|
|
/* Flush the prd table */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
prdOffset,
|
|
|
|
prdCount * sizeof(AHCI_PRD),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Flush Command list */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
listOffset,
|
|
|
|
sizeof(AHCI_CMD_LIST),
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Flush Command fis */
|
|
|
|
|
|
|
|
(void) vxbDmaBufMapSync(pSataHost->pDev,
|
|
|
|
pSataHost->sataCmdListTag,
|
|
|
|
pSataHost->sataCmdListMap,
|
|
|
|
fisOffset,
|
|
|
|
20,
|
|
|
|
_VXB_DMABUFSYNC_DMA_PREWRITE);
|
|
|
|
|
|
|
|
/* Start the command */
|
|
|
|
|
|
|
|
if (queued)
|
|
|
|
{
|
|
|
|
key = intCpuLock();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For queued commands we must write both the SATA active
|
|
|
|
* and command init registers
|
|
|
|
*/
|
|
|
|
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxSACT, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxSACT);
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCI);
|
|
|
|
pDrive->cmdStarted |= tagBit;
|
|
|
|
intCpuUnlock(key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
key = intCpuLock();
|
|
|
|
PORT_REG_WRITE(pDrive, AHCI_PxCI, tagBit);
|
|
|
|
reg = PORT_REG_READ(pDrive, AHCI_PxCI);
|
|
|
|
pDrive->cmdStarted |= tagBit;
|
|
|
|
intCpuUnlock(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cacluate wait factor */
|
|
|
|
|
|
|
|
if (pSgList->bufCnt > 32)
|
|
|
|
factor = pSgList->bufCnt >> 3;
|
|
|
|
else
|
|
|
|
factor = 1;
|
|
|
|
|
|
|
|
wait = 10 * pDrive->semTimeout * factor;
|
|
|
|
|
|
|
|
if (semTake (pDrive->syncSem[tag], wait) == ERROR)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSgTrans> semTake syncSem timeout:"
|
|
|
|
"tag = %d \n",
|
|
|
|
tag, 0, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
pDrive->timeoutErrorCount++;
|
|
|
|
ctrlMsg.msgId = AHCI_PORT_ERROR_MSG;
|
|
|
|
ctrlMsg.drive = (char)pDrive->sataPortDev.num;
|
|
|
|
ctrlMsg.pCtrl = pSataDev->host;
|
|
|
|
|
|
|
|
/* return from msgQSend is not needed */
|
|
|
|
|
|
|
|
(void)msgQSend(ahciMonQueue, (char *)&ctrlMsg,
|
|
|
|
VXB_AHCI_MSG_SIZE, NO_WAIT, MSG_PRI_NORMAL);
|
|
|
|
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If drive isn't there, or an error occurred and the transfer
|
|
|
|
* did not complete, return error
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((pDrive->state != AHCI_DEV_OK && pDrive->state != AHCI_DEV_INIT) ||
|
|
|
|
pDrive->portError)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,
|
|
|
|
"<ahciSgTrans> port error: pDriver=%02x,"
|
|
|
|
" pSataDev=%p\n",
|
|
|
|
pSataDev, pDrive,0,0,0,0);
|
|
|
|
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
if (pDrive->state == AHCI_DEV_NONE)
|
|
|
|
(void) errnoSet (S_ioLib_DISK_NOT_PRESENT);
|
|
|
|
return(ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
ahciCmdReleaseResource(pDrive,queued);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
*
|
|
|
|
* ahciXferReq - request transfer routine
|
|
|
|
*
|
|
|
|
* This routine used to transfer the request.
|
|
|
|
*
|
|
|
|
* RETURNS: N/A
|
|
|
|
*
|
|
|
|
* ERRNO: N/A
|
|
|
|
*
|
|
|
|
* \NOMANUAL
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCAL void ahciXferReq
|
|
|
|
(
|
|
|
|
SATA_DEVICE * pSataDev,
|
|
|
|
XBD_REQUEST * pXbdReq
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int errCode;
|
|
|
|
UINT8 rwCmd;
|
|
|
|
UINT8 index;
|
|
|
|
UINT32 tag;
|
|
|
|
sector_t curLen;
|
|
|
|
sector_t transLen;
|
|
|
|
AHCI_DRIVE * pDrive;
|
|
|
|
SATA_DEVICE * pSataDevice;
|
|
|
|
pXBD_REQ_PRIV pReqPriv;
|
|
|
|
pATA_CMD_DESC pAtaCmdDesc;
|
|
|
|
pSG_LIST pSgList;
|
|
|
|
bus_addr_t busAddr;
|
|
|
|
UINT32 maxXferSize;
|
|
|
|
struct bio * pBio;
|
|
|
|
|
|
|
|
pDrive = (AHCI_DRIVE *)(pSataDev->host->sataDev[pSataDev->num]);
|
|
|
|
if (pDrive == NULL)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: pDrive is NULL\n",
|
|
|
|
0, 0, 0, 0, 0,0);
|
|
|
|
sataXbdReqDone(pXbdReq, EIO);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pXbdReq->numBios > ATA_MAX_SG_CNT)
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: numBios %d > %d\n",
|
|
|
|
pXbdReq->numBios, ATA_MAX_SG_CNT, 0, 0, 0,0);
|
|
|
|
errCode = EIO;
|
|
|
|
goto Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (OK != ahciReqResourceGet(pDrive, pXbdReq))
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: ahciReqResourceGet fail\n",
|
|
|
|
0, 0, 0, 0, 0,0);
|
|
|
|
errCode = EIO;
|
|
|
|
goto Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pSataDevice = &pDrive->sataPortDev;
|
|
|
|
pReqPriv = (pXBD_REQ_PRIV)pXbdReq->drvSpecific;
|
|
|
|
pAtaCmdDesc = &(pReqPriv->ataCmdDesc);
|
|
|
|
pSgList = &(pReqPriv->sgList);
|
|
|
|
tag = pReqPriv->cmdTag;
|
|
|
|
|
|
|
|
/* Get command code */
|
|
|
|
|
|
|
|
if (pXbdReq->reqflags & BIO_WRITE)
|
|
|
|
{
|
|
|
|
rwCmd = sataRwCmdGet(pSataDevice, TRUE);
|
|
|
|
pAtaCmdDesc->flags |= ATA_FLAG_OUT_DATA;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rwCmd = sataRwCmdGet(pSataDevice, FALSE);
|
|
|
|
pAtaCmdDesc->flags |= ATA_FLAG_IN_DATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pSataDevice->okNcq)
|
|
|
|
{
|
|
|
|
pAtaCmdDesc->flags |= ATA_FLAG_NCQ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The driver work in dma mode */
|
|
|
|
|
|
|
|
if (pSataDevice->okDma)
|
|
|
|
{
|
|
|
|
sataRwCmdDescBuild(pSataDevice,
|
|
|
|
pAtaCmdDesc,
|
|
|
|
pXbdReq->reqFirstBlk,
|
|
|
|
pXbdReq->reqNumBlks,
|
|
|
|
0,
|
|
|
|
rwCmd,
|
|
|
|
tag);
|
|
|
|
|
|
|
|
/* Init buffer vec */
|
|
|
|
|
|
|
|
pSgList->bufCnt = pXbdReq->numBios;
|
|
|
|
for (i = 0; i < pSgList->bufCnt; i++)
|
|
|
|
{
|
|
|
|
pSgList->bufVec[i].iov_base = ataBusAddrGet(pXbdReq, i);
|
|
|
|
pSgList->bufVec[i].iov_len = ataBufLenGet(pXbdReq, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer the request */
|
|
|
|
|
|
|
|
if (OK != ahciSgTrans(pSataDevice, pAtaCmdDesc, pSgList))
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: Transfer request fail \n",
|
|
|
|
0, 0, 0, 0, 0,0);
|
|
|
|
errCode = EIO;
|
|
|
|
goto Out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
index = 0;
|
|
|
|
maxXferSize = (pSataDevice->fisMaxSector * pSataDevice->bytes);
|
|
|
|
|
|
|
|
for(pBio = pXbdReq->pBioHead; pBio != NULL; pBio = pBio->bio_chain, index ++)
|
|
|
|
{
|
|
|
|
transLen = 0;
|
|
|
|
|
|
|
|
/* Store the bus address */
|
|
|
|
|
|
|
|
busAddr = ataBusAddrGet(pXbdReq, index);
|
|
|
|
|
|
|
|
while (transLen < pBio->bio_bcount)
|
|
|
|
{
|
|
|
|
curLen = min(maxXferSize, (pBio->bio_bcount - transLen));
|
|
|
|
|
|
|
|
/* StartBlk = cur bio blkno + transfer sector */
|
|
|
|
|
|
|
|
sataRwCmdDescBuild(pSataDevice,
|
|
|
|
pAtaCmdDesc,
|
|
|
|
(pBio->bio_blkno + transLen / pSataDevice->bytes),
|
|
|
|
(UINT16)(curLen / pSataDevice->bytes),
|
|
|
|
0,
|
|
|
|
rwCmd,
|
|
|
|
tag);
|
|
|
|
|
|
|
|
/* Init buffer vec */
|
|
|
|
|
|
|
|
pSgList->bufCnt = 1;
|
|
|
|
pSgList->bufVec[0].iov_base = busAddr + transLen;
|
|
|
|
pSgList->bufVec[0].iov_len = curLen - 1;
|
|
|
|
|
|
|
|
/* Transfer the request */
|
|
|
|
|
|
|
|
if (OK != ahciSgTrans(pSataDevice, pAtaCmdDesc, pSgList))
|
|
|
|
{
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD,"ERR: Transfer request fail \n",
|
|
|
|
0, 0, 0, 0, 0,0);
|
|
|
|
errCode = EIO;
|
|
|
|
goto Out;
|
|
|
|
}
|
|
|
|
|
|
|
|
transLen += curLen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pAtaCmdDesc->flags & ATA_FLAG_IN_DATA)
|
|
|
|
{
|
|
|
|
vxbDmaBufSync(pSataDevice->host->pDev,
|
|
|
|
pSataDevice->host->userBufTagId,
|
|
|
|
pReqPriv->usrBufMapId,
|
|
|
|
_VXB_DMABUFSYNC_DMA_POSTREAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the resource */
|
|
|
|
|
|
|
|
ahciReqResourceRelease(pDrive, pXbdReq);
|
|
|
|
|
|
|
|
/* Indicate all the bio complete */
|
|
|
|
|
|
|
|
sataXbdReqDone(pXbdReq, 0);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
Out:
|
|
|
|
AHCISATA_DBG_LOG(DEBUG_CMD, "drive %d req %p handle fail %d,release slot %d\n",
|
|
|
|
pDrive->portPhyNum, pXbdReq, errCode, pXbdReq, 0, 0);
|
|
|
|
ahciReqResourceRelease(pDrive, pXbdReq);
|
|
|
|
sataXbdReqDone(pXbdReq, errCode);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|