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 {
|
97 | 97 | #define IPM_TRANSITIONS_TO_PARTIAL_DISABLED 0x1 |
98 | 98 | #define IPM_TRANSITIONS_TO_SLUMBER_DISABLED 0x2 |
99 | 99 | |
| 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 |
100 | 105 | |
101 | 106 | typedef struct { |
102 | 107 | uint32 clb; // Command List Base Address |
… |
… |
enum {
|
130 | 135 | PORT_CMD_ATAPI = (1 << 24), // Device is ATAPI |
131 | 136 | PORT_CMD_CR = (1 << 15), // Command List Running (DMA active) |
132 | 137 | 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 |
134 | 139 | PORT_CMD_CLO = (1 << 3), // Command List Override |
135 | 140 | PORT_CMD_POD = (1 << 2), // Power On Device |
136 | 141 | PORT_CMD_SUD = (1 << 1), // Spin-up Device |
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)
|
54 | 54 | fSectorCount(0), |
55 | 55 | fIsATAPI(false), |
56 | 56 | fTestUnitReadyActive(false), |
57 | | fResetPort(false), |
| 57 | fSoftReset(false), |
58 | 58 | fError(false), |
59 | 59 | fTrimSupported(false) |
60 | 60 | { |
… |
… |
AHCIPort::Init1()
|
120 | 120 | fRegs->is = fRegs->is; |
121 | 121 | |
122 | 122 | // clear error bits |
123 | | fRegs->serr = fRegs->serr; |
| 123 | _ClearErrorRegister(); |
124 | 124 | |
125 | 125 | // power up device |
126 | 126 | fRegs->cmd |= PORT_CMD_POD; |
… |
… |
AHCIPort::Init1()
|
131 | 131 | // activate link |
132 | 132 | fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE; |
133 | 133 | |
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; |
136 | 136 | |
137 | 137 | FlushPostedWrites(); |
138 | 138 | |
… |
… |
AHCIPort::Init2()
|
146 | 146 | { |
147 | 147 | TRACE("AHCIPort::Init2 port %d\n", fIndex); |
148 | 148 | |
149 | | // start DMA engine |
150 | | fRegs->cmd |= PORT_CMD_ST; |
| 149 | // enable port |
| 150 | Enable(); |
151 | 151 | |
152 | 152 | // enable interrupts |
153 | 153 | fRegs->ie = PORT_INT_MASK; |
154 | 154 | |
155 | 155 | FlushPostedWrites(); |
156 | 156 | |
157 | | ResetPort(true); |
| 157 | // reset port and probe info |
| 158 | SoftReset(); |
158 | 159 | |
159 | 160 | TRACE("ie 0x%08" B_PRIx32 "\n", fRegs->ie); |
160 | 161 | TRACE("is 0x%08" B_PRIx32 "\n", fRegs->is); |
… |
… |
AHCIPort::Init2()
|
172 | 173 | |
173 | 174 | fDevicePresent = (fRegs->ssts & 0xf) == 0x3; |
174 | 175 | |
| 176 | TRACE("%s: port %d, device %s\n", __func__, fIndex, |
| 177 | fDevicePresent ? "present" : "absent"); |
| 178 | |
175 | 179 | return B_OK; |
176 | 180 | } |
177 | 181 | |
… |
… |
AHCIPort::Uninit()
|
181 | 185 | { |
182 | 186 | TRACE("AHCIPort::Uninit port %d\n", fIndex); |
183 | 187 | |
184 | | // disable FIS receive |
185 | | fRegs->cmd &= ~PORT_CMD_FER; |
| 188 | // Spec v1.3.1, §10.3.2 - Shut down port before unsetting FRE |
186 | 189 | |
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; |
190 | 195 | } |
191 | 196 | |
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); |
200 | 201 | |
201 | 202 | // disable interrupts |
202 | 203 | fRegs->ie = 0; |
… |
… |
void
|
218 | 219 | AHCIPort::ResetDevice() |
219 | 220 | { |
220 | 221 | // 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); |
222 | 224 | return; |
| 225 | } |
223 | 226 | |
224 | 227 | if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) |
225 | 228 | TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex); |
… |
… |
AHCIPort::ResetDevice()
|
238 | 241 | |
239 | 242 | |
240 | 243 | status_t |
241 | | AHCIPort::ResetPort(bool forceDeviceReset) |
| 244 | AHCIPort::SoftReset() |
242 | 245 | { |
243 | | if (!fTestUnitReadyActive) |
244 | | TRACE("AHCIPort::ResetPort port %d\n", fIndex); |
| 246 | TRACE("AHCIPort::SoftReset port %d\n", fIndex); |
245 | 247 | |
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. |
249 | 250 | |
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(); |
253 | 257 | } |
254 | 258 | |
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(); |
260 | 264 | } |
261 | 265 | |
262 | | if (deviceBusy || forceDeviceReset) |
263 | | ResetDevice(); |
| 266 | // TODO: Proper Software reset here |
| 267 | return PortReset(); |
| 268 | // ******************************** |
264 | 269 | |
265 | | // start DMA engine |
266 | | fRegs->cmd |= PORT_CMD_ST; |
267 | | FlushPostedWrites(); |
| 270 | // start port |
| 271 | Enable(); |
268 | 272 | |
269 | 273 | return PostReset(); |
270 | 274 | } |
271 | 275 | |
272 | 276 | |
273 | 277 | status_t |
274 | | AHCIPort::PostReset() |
| 278 | AHCIPort::PortReset() |
275 | 279 | { |
276 | | if (!fTestUnitReadyActive) |
277 | | TRACE("AHCIPort::PostReset port %d\n", fIndex); |
| 280 | TRACE("AHCIPort::PortReset port %d\n", fIndex); |
278 | 281 | |
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; |
282 | 287 | } |
283 | 288 | |
| 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 | |
| 315 | status_t |
| 316 | AHCIPort::PostReset() |
| 317 | { |
284 | 318 | if ((fRegs->tfd & 0xff) == 0xff) |
285 | 319 | snooze(200000); |
286 | 320 | |
… |
… |
AHCIPort::PostReset()
|
292 | 326 | |
293 | 327 | wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000); |
294 | 328 | |
295 | | fIsATAPI = fRegs->sig == 0xeb140101; |
296 | | |
| 329 | fIsATAPI = fRegs->sig == SATA_SIG_ATAPI; |
297 | 330 | if (fIsATAPI) |
298 | 331 | fRegs->cmd |= PORT_CMD_ATAPI; |
299 | 332 | else |
… |
… |
AHCIPort::PostReset()
|
302 | 335 | |
303 | 336 | if (!fTestUnitReadyActive) { |
304 | 337 | 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 |
306 | 339 | ? "ATA" : "unknown"); |
307 | 340 | } |
308 | 341 | |
| 342 | _ClearErrorRegister(); |
| 343 | |
309 | 344 | return B_OK; |
310 | 345 | } |
311 | 346 | |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
374 | 409 | } |
375 | 410 | |
376 | 411 | // read and clear SError |
377 | | uint32 serr = fRegs->serr; |
378 | | fRegs->serr = serr; |
| 412 | _ClearErrorRegister(); |
379 | 413 | |
380 | 414 | if (is & PORT_INT_TFE) { |
381 | 415 | if (!fTestUnitReadyActive) |
382 | 416 | TRACE("Task File Error\n"); |
383 | 417 | |
384 | | fResetPort = true; |
| 418 | fSoftReset = true; |
385 | 419 | fError = true; |
386 | 420 | } |
387 | 421 | if (is & PORT_INT_HBF) { |
388 | 422 | TRACE("Host Bus Fatal Error\n"); |
389 | | fResetPort = true; |
| 423 | fSoftReset = true; |
390 | 424 | fError = true; |
391 | 425 | } |
392 | 426 | if (is & PORT_INT_HBD) { |
393 | 427 | TRACE("Host Bus Data Error\n"); |
394 | | fResetPort = true; |
| 428 | fSoftReset = true; |
395 | 429 | fError = true; |
396 | 430 | } |
397 | 431 | if (is & PORT_INT_IF) { |
398 | 432 | TRACE("Interface Fatal Error\n"); |
399 | | fResetPort = true; |
| 433 | fSoftReset = true; |
400 | 434 | fError = true; |
401 | 435 | } |
402 | 436 | if (is & PORT_INT_INF) { |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
404 | 438 | } |
405 | 439 | if (is & PORT_INT_OF) { |
406 | 440 | TRACE("Overflow\n"); |
407 | | fResetPort = true; |
| 441 | fSoftReset = true; |
408 | 442 | fError = true; |
409 | 443 | } |
410 | 444 | if (is & PORT_INT_IPM) { |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
412 | 446 | } |
413 | 447 | if (is & PORT_INT_PRC) { |
414 | 448 | TRACE("PhyReady Change\n"); |
415 | | // fResetPort = true; |
| 449 | // fSoftReset = true; |
416 | 450 | } |
417 | 451 | if (is & PORT_INT_PC) { |
418 | 452 | TRACE("Port Connect Change\n"); |
419 | 453 | // 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(); ??? |
426 | 458 | _ClearErrorRegister(); |
427 | 459 | } |
428 | 460 | if (is & PORT_INT_UF) { |
429 | 461 | TRACE("Unknown FIS\n"); |
430 | | fResetPort = true; |
| 462 | fSoftReset = true; |
431 | 463 | } |
432 | 464 | |
433 | 465 | if (fError) { |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
452 | 484 | status_t status = get_memory_map_etc(B_CURRENT_TEAM, data, dataSize, |
453 | 485 | entries, &entriesUsed); |
454 | 486 | 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)); |
457 | 488 | return B_ERROR; |
458 | 489 | } |
459 | 490 | |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
500 | 531 | sgCount--; |
501 | 532 | } |
502 | 533 | if (*prdCount == 0) { |
503 | | TRACE("AHCIPort::FillPrdTable: count is 0\n"); |
| 534 | TRACE("%s: count is 0\n", __func__); |
504 | 535 | return B_ERROR; |
505 | 536 | } |
506 | 537 | if (dataSize > 0) { |
… |
… |
AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)
|
1059 | 1090 | |
1060 | 1091 | if (wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 1000000) < B_OK) { |
1061 | 1092 | TRACE("ExecuteAtaRequest port %d: device is busy\n", fIndex); |
1062 | | ResetPort(); |
| 1093 | SoftReset(); |
1063 | 1094 | FinishTransfer(); |
1064 | 1095 | request->Abort(); |
1065 | 1096 | return; |
… |
… |
AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)
|
1094 | 1125 | TRACE("tfd 0x%08" B_PRIx32 "\n", fRegs->tfd); |
1095 | 1126 | */ |
1096 | 1127 | |
1097 | | if (fResetPort || status == B_TIMED_OUT) { |
1098 | | fResetPort = false; |
1099 | | ResetPort(); |
| 1128 | if (fSoftReset || status == B_TIMED_OUT) { |
| 1129 | fSoftReset = false; |
| 1130 | SoftReset(); |
1100 | 1131 | } |
1101 | 1132 | |
1102 | 1133 | size_t bytesTransfered = fCommandList->prdbc; |
… |
… |
AHCIPort::ScsiGetRestrictions(bool* isATAPI, bool* noAutoSense,
|
1322 | 1353 | |
1323 | 1354 | |
1324 | 1355 | bool |
1325 | | AHCIPort::_HardReset() |
| 1356 | AHCIPort::Enable() |
1326 | 1357 | { |
| 1358 | // Spec v1.3.1, §10.3.1 Start (PxCMD.ST) |
| 1359 | TRACE("%s: port %d\n", __func__, fIndex); |
1327 | 1360 | 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__); |
1330 | 1362 | return false; |
1331 | 1363 | } |
1332 | 1364 | |
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; |
1335 | 1378 | 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 | |
| 1383 | bool |
| 1384 | AHCIPort::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; |
1340 | 1395 | FlushPostedWrites(); |
1341 | 1396 | |
| 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 | |
1342 | 1405 | return true; |
1343 | 1406 | } |
1344 | 1407 | |
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:
|
48 | 48 | void ExecuteSataRequest(sata_request *request, bool isWrite = false); |
49 | 49 | |
50 | 50 | void ResetDevice(); |
51 | | status_t ResetPort(bool forceDeviceReset = false); |
| 51 | status_t SoftReset(); |
| 52 | status_t PortReset(); |
52 | 53 | status_t PostReset(); |
| 54 | |
| 55 | bool Enable(); |
| 56 | bool Disable(); |
| 57 | |
53 | 58 | void FlushPostedWrites(); |
54 | 59 | void DumpD2HFis(); |
55 | 60 | |
… |
… |
private:
|
57 | 62 | status_t WaitForTransfer(int *tfd, bigtime_t timeout); |
58 | 63 | void FinishTransfer(); |
59 | 64 | |
60 | | inline bool _HardReset(); |
61 | 65 | inline void _ClearErrorRegister(); |
62 | 66 | |
63 | 67 | // uint8 * SetCommandFis(volatile command_list_entry *cmd, volatile fis *fis, const void *data, size_t dataSize); |
… |
… |
private:
|
79 | 83 | uint64 fSectorCount; |
80 | 84 | bool fIsATAPI; |
81 | 85 | bool fTestUnitReadyActive; |
82 | | bool fResetPort; |
| 86 | bool fSoftReset; |
83 | 87 | bool fError; |
84 | 88 | bool fTrimSupported; |
85 | 89 | uint32 fMaxTrimRangeBlocks; |