Ticket #12357: ahci-rework-v3.diff

File ahci-rework-v3.diff, 13.2 KB (added by kallisti5, 5 years ago)

rework v3

  • src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h

    diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h
    index b9c8c03..456bdaf 100644
    a b enum {  
    9797#define IPM_TRANSITIONS_TO_PARTIAL_DISABLED 0x1
    9898#define IPM_TRANSITIONS_TO_SLUMBER_DISABLED 0x2
    9999
     100// Device signatures
     101#define SATA_SIG_ATA            0x00000101 // SATA drive
     102#define SATA_SIG_ATAPI          0xEB140101 // ATAPI drive
     103#define SATA_SIG_SEMB           0xC33C0101 // Enclosure management bridge
     104#define SATA_SIG_PM             0x96690101 // Port multiplier
    100105
    101106typedef struct {
    102107    uint32      clb;            // Command List Base Address
    enum {  
    130135    PORT_CMD_ATAPI  = (1 << 24),    // Device is ATAPI
    131136    PORT_CMD_CR     = (1 << 15),    // Command List Running (DMA active)
    132137    PORT_CMD_FR     = (1 << 14),    // FIS Receive Running
    133     PORT_CMD_FER    = (1 << 4),     // FIS Receive Enable
     138    PORT_CMD_FRE    = (1 << 4),     // FIS Receive Enable
    134139    PORT_CMD_CLO    = (1 << 3),     // Command List Override
    135140    PORT_CMD_POD    = (1 << 2),     // Power On Device
    136141    PORT_CMD_SUD    = (1 << 1),     // Spin-up Device
  • src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp

    diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp
    index 94a0034..7618223 100644
    a b AHCIPort::AHCIPort(AHCIController* controller, int index)  
    5454    fSectorCount(0),
    5555    fIsATAPI(false),
    5656    fTestUnitReadyActive(false),
    57     fResetPort(false),
     57    fSoftReset(false),
    5858    fError(false),
    5959    fTrimSupported(false)
    6060{
    AHCIPort::Init1()  
    120120    fRegs->is = fRegs->is;
    121121
    122122    // clear error bits
    123     fRegs->serr = fRegs->serr;
     123    _ClearErrorRegister();
    124124
    125125    // power up device
    126126    fRegs->cmd |= PORT_CMD_POD;
    AHCIPort::Init1()  
    131131    // activate link
    132132    fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE;
    133133
    134     // enable FIS receive
    135     fRegs->cmd |= PORT_CMD_FER;
     134    // enable FIS receive (enabled when fb set, only to be disabled when unset)
     135    fRegs->cmd |= PORT_CMD_FRE;
    136136
    137137    FlushPostedWrites();
    138138
    AHCIPort::Init2()  
    146146{
    147147    TRACE("AHCIPort::Init2 port %d\n", fIndex);
    148148
    149     // start DMA engine
    150     fRegs->cmd |= PORT_CMD_ST;
     149    // enable port
     150    Enable();
    151151
    152152    // enable interrupts
    153153    fRegs->ie = PORT_INT_MASK;
    154154
    155155    FlushPostedWrites();
    156156
    157     ResetPort(true);
     157    // reset port and probe info
     158    SoftReset();
    158159
    159160    TRACE("ie   0x%08" B_PRIx32 "\n", fRegs->ie);
    160161    TRACE("is   0x%08" B_PRIx32 "\n", fRegs->is);
    AHCIPort::Init2()  
    172173
    173174    fDevicePresent = (fRegs->ssts & 0xf) == 0x3;
    174175
     176    TRACE("%s: port %d, device %s\n", __func__, fIndex,
     177        fDevicePresent ? "present" : "absent");
     178
    175179    return B_OK;
    176180}
    177181
    AHCIPort::Uninit()  
    181185{
    182186    TRACE("AHCIPort::Uninit port %d\n", fIndex);
    183187
    184     // disable FIS receive
    185     fRegs->cmd &= ~PORT_CMD_FER;
     188    // Spec v1.3.1, §10.3.2 - Shut down port before unsetting FRE
    186189
    187     // wait for receive completion, up to 500ms
    188     if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) {
    189         TRACE("AHCIPort::Uninit port %d error FIS rx still running\n", fIndex);
     190    // shutdown the port
     191    if (!Disable()) {
     192        ERROR("%s: port %d error, unable to shutdown before FRE clear!\n",
     193            __func__, fIndex);
     194        return;
    190195    }
    191196
    192     // stop DMA engine
    193     fRegs->cmd &= ~PORT_CMD_ST;
    194 
    195     // wait for DMA completion
    196     if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
    197         TRACE("AHCIPort::Uninit port %d error DMA engine still running\n",
    198             fIndex);
    199     }
     197    // Clear FRE and wait for completion
     198    fRegs->cmd &= ~PORT_CMD_FRE;
     199    if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK)
     200        ERROR("%s: port %d error FIS rx still running\n", __func__, fIndex);
    200201
    201202    // disable interrupts
    202203    fRegs->ie = 0;
    void  
    218219AHCIPort::ResetDevice()
    219220{
    220221    // perform a hard reset
    221     if (!_HardReset())
     222    if (PortReset() != B_OK) {
     223        ERROR("%s: port %d unable to hard reset device\n", __func__, fIndex);
    222224        return;
     225    }
    223226
    224227    if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK)
    225228        TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex);
    AHCIPort::ResetDevice()  
    238241
    239242
    240243status_t
    241 AHCIPort::ResetPort(bool forceDeviceReset)
     244AHCIPort::SoftReset()
    242245{
    243     if (!fTestUnitReadyActive)
    244         TRACE("AHCIPort::ResetPort port %d\n", fIndex);
     246    TRACE("AHCIPort::SoftReset port %d\n", fIndex);
    245247
    246     // stop DMA engine
    247     fRegs->cmd &= ~PORT_CMD_ST;
    248     FlushPostedWrites();
     248    // Spec v1.3.1, §10.4.1 Software Reset
     249    // A single device on one port is reset, HBA and phy comm remain intact.
    249250
    250     if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
    251         TRACE("AHCIPort::ResetPort port %d error DMA engine doesn't stop\n",
    252             fIndex);
     251    // stop port, flush transactions
     252    if (!Disable()) {
     253        // If the port doesn't power off, move on to a stronger reset.
     254        ERROR("%s: port %d soft reset failed. Moving on to port reset.\n",
     255            __func__, fIndex);
     256        return PortReset();
    253257    }
    254258
    255     bool deviceBusy = fRegs->tfd & (ATA_BSY | ATA_DRQ);
    256 
    257     if (!fTestUnitReadyActive) {
    258         TRACE("AHCIPort::ResetPort port %d, deviceBusy %d, "
    259             "forceDeviceReset %d\n", fIndex, deviceBusy, forceDeviceReset);
     259    // If the port is still busy, move on to port reset.
     260    if (fRegs->tfd & (ATA_BSY | ATA_DRQ)) {
     261        ERROR("%s: port %d still busy. Moving on to port reset.\n",
     262            __func__, fIndex);
     263        return PortReset();
    260264    }
    261265
    262     if (deviceBusy || forceDeviceReset)
    263         ResetDevice();
     266    // TODO: Proper Software reset here
     267    return PortReset();
     268    // ********************************
    264269
    265     // start DMA engine
    266     fRegs->cmd |= PORT_CMD_ST;
    267     FlushPostedWrites();
     270    // start port
     271    Enable();
    268272
    269273    return PostReset();
    270274}
    271275
    272276
    273277status_t
    274 AHCIPort::PostReset()
     278AHCIPort::PortReset()
    275279{
    276     if (!fTestUnitReadyActive)
    277         TRACE("AHCIPort::PostReset port %d\n", fIndex);
     280    TRACE("AHCIPort::PortReset port %d\n", fIndex);
    278281
    279     if ((fRegs->ssts & 0xf) != 0x3 || (fRegs->tfd & 0xff) == 0x7f) {
    280         TRACE("AHCIPort::PostReset port %d: no device\n", fIndex);
    281         return B_OK;
     282    // Spec v1.3.1, §10.4.2 Port Reset
     283    // Physical comm between HBA and port disabled. More Intrusive
     284    if (!Disable()) {
     285        ERROR("%s: port %d unable to reset!\n", __func__, fIndex);
     286        return B_ERROR;
    282287    }
    283288
     289    fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK)
     290        | DET_INITIALIZATION << SATA_CONTROL_DET_SHIFT;
     291    FlushPostedWrites();
     292    spin(1100);
     293        // You must wait 1ms at minimum
     294    fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK)
     295        | DET_NO_INITIALIZATION << SATA_CONTROL_DET_SHIFT;
     296    FlushPostedWrites();
     297
     298    if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) {
     299        TRACE("AHCIPort::PortReset port %d device present but no phy "
     300            "communication\n", fIndex);
     301        return B_ERROR;
     302    }
     303
     304    //if ((fRegs->tfd & 0xff) == 0x7f) {
     305    //  TRACE("AHCIPort::PostReset port %d: no device\n", fIndex);
     306    //  return B_OK;
     307    //}
     308
     309    Enable();
     310
     311    return PostReset();
     312}
     313
     314
     315status_t
     316AHCIPort::PostReset()
     317{
    284318    if ((fRegs->tfd & 0xff) == 0xff)
    285319        snooze(200000);
    286320
    AHCIPort::PostReset()  
    292326
    293327    wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000);
    294328
    295     fIsATAPI = fRegs->sig == 0xeb140101;
    296 
     329    fIsATAPI = fRegs->sig == SATA_SIG_ATAPI;
    297330    if (fIsATAPI)
    298331        fRegs->cmd |= PORT_CMD_ATAPI;
    299332    else
    AHCIPort::PostReset()  
    302335
    303336    if (!fTestUnitReadyActive) {
    304337        TRACE("device signature 0x%08" B_PRIx32 " (%s)\n", fRegs->sig,
    305             fRegs->sig == 0xeb140101 ? "ATAPI" : fRegs->sig == 0x00000101
     338            fRegs->sig == SATA_SIG_ATAPI ? "ATAPI" : fRegs->sig == SATA_SIG_ATA
    306339                ? "ATA" : "unknown");
    307340    }
    308341
     342    _ClearErrorRegister();
     343
    309344    return B_OK;
    310345}
    311346
    AHCIPort::InterruptErrorHandler(uint32 is)  
    374409    }
    375410
    376411    // read and clear SError
    377     uint32 serr = fRegs->serr;
    378     fRegs->serr = serr;
     412    _ClearErrorRegister();
    379413
    380414    if (is & PORT_INT_TFE) {
    381415        if (!fTestUnitReadyActive)
    382416            TRACE("Task File Error\n");
    383417
    384         fResetPort = true;
     418        fSoftReset = true;
    385419        fError = true;
    386420    }
    387421    if (is & PORT_INT_HBF) {
    388422        TRACE("Host Bus Fatal Error\n");
    389         fResetPort = true;
     423        fSoftReset = true;
    390424        fError = true;
    391425    }
    392426    if (is & PORT_INT_HBD) {
    393427        TRACE("Host Bus Data Error\n");
    394         fResetPort = true;
     428        fSoftReset = true;
    395429        fError = true;
    396430    }
    397431    if (is & PORT_INT_IF) {
    398432        TRACE("Interface Fatal Error\n");
    399         fResetPort = true;
     433        fSoftReset = true;
    400434        fError = true;
    401435    }
    402436    if (is & PORT_INT_INF) {
    AHCIPort::InterruptErrorHandler(uint32 is)  
    404438    }
    405439    if (is & PORT_INT_OF) {
    406440        TRACE("Overflow\n");
    407         fResetPort = true;
     441        fSoftReset = true;
    408442        fError = true;
    409443    }
    410444    if (is & PORT_INT_IPM) {
    AHCIPort::InterruptErrorHandler(uint32 is)  
    412446    }
    413447    if (is & PORT_INT_PRC) {
    414448        TRACE("PhyReady Change\n");
    415 //      fResetPort = true;
     449//      fSoftReset = true;
    416450    }
    417451    if (is & PORT_INT_PC) {
    418452        TRACE("Port Connect Change\n");
    419453        // Spec v1.3, §6.2.2.3 Recovery of Unsolicited COMINIT
    420 
    421         // perform a hard reset
    422         if (!_HardReset())
    423             return;
    424 
    425         // clear error bits to clear PxSERR.DIAG.X
     454        // Spec v1.3.1, §7.4 Interaction of command list and port change status
     455        // TODO: Issue COMRESET ?
     456        //PortReset();
     457        //HBAReset(); ???
    426458        _ClearErrorRegister();
    427459    }
    428460    if (is & PORT_INT_UF) {
    429461        TRACE("Unknown FIS\n");
    430         fResetPort = true;
     462        fSoftReset = true;
    431463    }
    432464
    433465    if (fError) {
    AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,  
    452484    status_t status = get_memory_map_etc(B_CURRENT_TEAM, data, dataSize,
    453485        entries, &entriesUsed);
    454486    if (status != B_OK) {
    455         TRACE("AHCIPort::FillPrdTable get_memory_map() failed: %s\n",
    456             strerror(status));
     487        TRACE("%s: get_memory_map() failed: %s\n", __func__, strerror(status));
    457488        return B_ERROR;
    458489    }
    459490
    AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,  
    500531        sgCount--;
    501532    }
    502533    if (*prdCount == 0) {
    503         TRACE("AHCIPort::FillPrdTable: count is 0\n");
     534        TRACE("%s: count is 0\n", __func__);
    504535        return B_ERROR;
    505536    }
    506537    if (dataSize > 0) {
    AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)  
    10591090
    10601091    if (wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 1000000) < B_OK) {
    10611092        TRACE("ExecuteAtaRequest port %d: device is busy\n", fIndex);
    1062         ResetPort();
     1093        SoftReset();
    10631094        FinishTransfer();
    10641095        request->Abort();
    10651096        return;
    AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)  
    10941125    TRACE("tfd  0x%08" B_PRIx32 "\n", fRegs->tfd);
    10951126*/
    10961127
    1097     if (fResetPort || status == B_TIMED_OUT) {
    1098         fResetPort = false;
    1099         ResetPort();
     1128    if (fSoftReset || status == B_TIMED_OUT) {
     1129        fSoftReset = false;
     1130        SoftReset();
    11001131    }
    11011132
    11021133    size_t bytesTransfered = fCommandList->prdbc;
    AHCIPort::ScsiGetRestrictions(bool* isATAPI, bool* noAutoSense,  
    13221353
    13231354
    13241355bool
    1325 AHCIPort::_HardReset()
     1356AHCIPort::Enable()
    13261357{
     1358    // Spec v1.3.1, §10.3.1 Start (PxCMD.ST)
     1359    TRACE("%s: port %d\n", __func__, fIndex);
    13271360    if ((fRegs->cmd & PORT_CMD_ST) != 0) {
    1328         // We shouldn't perform a reset, but at least document it
    1329         TRACE("AHCIPort::_HardReset() PORT_CMD_ST set, behaviour undefined\n");
     1361        TRACE("%s: Starting port already running!\n", __func__);
    13301362        return false;
    13311363    }
    13321364
    1333     fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK)
    1334         | DET_INITIALIZATION << SATA_CONTROL_DET_SHIFT;
     1365    if ((fRegs->cmd & PORT_CMD_FRE) == 0) {
     1366        TRACE("%s: Unable to start port without FRE enabled!\n", __func__);
     1367        return false;
     1368    }
     1369
     1370    // Clear DMA engine and wait for completion
     1371    if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
     1372        TRACE("%s: port %d error DMA engine still running\n", __func__,
     1373            fIndex);
     1374        return false;
     1375    }
     1376    // Start port
     1377    fRegs->cmd |= PORT_CMD_ST;
    13351378    FlushPostedWrites();
    1336     spin(1100);
    1337         // You must wait 1ms at minimum
    1338     fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK)
    1339         | DET_NO_INITIALIZATION << SATA_CONTROL_DET_SHIFT;
     1379    return true;
     1380}
     1381
     1382
     1383bool
     1384AHCIPort::Disable()
     1385{
     1386    TRACE("%s: port %d\n", __func__, fIndex);
     1387
     1388    if ((fRegs->cmd & PORT_CMD_ST) == 0) {
     1389        TRACE("%s: port %d attempting to disable stopped port.\n",
     1390            __func__, fIndex);
     1391    }
     1392
     1393    // Disable port
     1394    fRegs->cmd &= ~PORT_CMD_ST;
    13401395    FlushPostedWrites();
    13411396
     1397    // Spec v1.3.1, §10.4.2 Port Reset - assume hung after 500 mil.
     1398    // Clear DMA engine and wait for completion
     1399    if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
     1400        TRACE("%s: port %d error DMA engine still running\n", __func__,
     1401            fIndex);
     1402        return false;
     1403    }
     1404
    13421405    return true;
    13431406}
    13441407
  • src/add-ons/kernel/busses/scsi/ahci/ahci_port.h

    diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h
    index 0bc4e67..deed571 100644
    a b private:  
    4848    void        ExecuteSataRequest(sata_request *request, bool isWrite = false);
    4949
    5050    void        ResetDevice();
    51     status_t    ResetPort(bool forceDeviceReset = false);
     51    status_t    SoftReset();
     52    status_t    PortReset();
    5253    status_t    PostReset();
     54
     55    bool        Enable();
     56    bool        Disable();
     57
    5358    void        FlushPostedWrites();
    5459    void        DumpD2HFis();
    5560
    private:  
    5762    status_t    WaitForTransfer(int *tfd, bigtime_t timeout);
    5863    void        FinishTransfer();
    5964
    60     inline  bool                _HardReset();
    6165    inline  void                _ClearErrorRegister();
    6266
    6367//  uint8 *     SetCommandFis(volatile command_list_entry *cmd, volatile fis *fis, const void *data, size_t dataSize);
    private:  
    7983    uint64                          fSectorCount;
    8084    bool                            fIsATAPI;
    8185    bool                            fTestUnitReadyActive;
    82     bool                            fResetPort;
     86    bool                            fSoftReset;
    8387    bool                            fError;
    8488    bool                            fTrimSupported;
    8589    uint32                          fMaxTrimRangeBlocks;