diff --git a/headers/private/drivers/ata_types.h b/headers/private/drivers/ata_types.h
index b855d4e..f06a3d1 100644
a
|
b
|
enum {
|
248 | 248 | ATA_STATUS_BUSY = 0x80 // busy |
249 | 249 | }; |
250 | 250 | |
251 | | // device control register |
| 251 | // device control register (ATA command block) |
252 | 252 | enum { |
253 | 253 | // bit 0 must be zero |
254 | 254 | ATA_DEVICE_CONTROL_DISABLE_INTS = 0x02, // disable INTRQ |
255 | 255 | ATA_DEVICE_CONTROL_SOFT_RESET = 0x04, // software device reset |
256 | | ATA_DEVICE_CONTROL_BIT3 = 0x08, // don't know, but must be set |
| 256 | ATA_DEVICE_CONTROL_BIT3 = 0x08, // obsolete. Must always be set |
257 | 257 | // bits inbetween are reserved |
258 | 258 | ATA_DEVICE_CONTROL_HIGH_ORDER_BYTE = 0x80 // read high order byte |
259 | 259 | // (for 48-bit lba) |
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..1e942cb 100644
a
|
b
|
|
5 | 5 | #ifndef _AHCI_DEFS_H |
6 | 6 | #define _AHCI_DEFS_H |
7 | 7 | |
| 8 | #include <ata_types.h> |
8 | 9 | #include <bus/PCI.h> |
9 | 10 | #include <bus/SCSI.h> |
10 | 11 | #include <PCI_x86.h> |
… |
… |
enum {
|
97 | 98 | #define IPM_TRANSITIONS_TO_PARTIAL_DISABLED 0x1 |
98 | 99 | #define IPM_TRANSITIONS_TO_SLUMBER_DISABLED 0x2 |
99 | 100 | |
| 101 | // Device signatures |
| 102 | #define SATA_SIG_ATA 0x00000101 // SATA drive |
| 103 | #define SATA_SIG_ATAPI 0xEB140101 // ATAPI drive |
| 104 | #define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge |
| 105 | #define SATA_SIG_PM 0x96690101 // Port multiplier |
100 | 106 | |
101 | 107 | typedef struct { |
102 | 108 | uint32 clb; // Command List Base Address |
… |
… |
enum {
|
130 | 136 | PORT_CMD_ATAPI = (1 << 24), // Device is ATAPI |
131 | 137 | PORT_CMD_CR = (1 << 15), // Command List Running (DMA active) |
132 | 138 | PORT_CMD_FR = (1 << 14), // FIS Receive Running |
133 | | PORT_CMD_FER = (1 << 4), // FIS Receive Enable |
| 139 | PORT_CMD_FRE = (1 << 4), // FIS Receive Enable |
134 | 140 | PORT_CMD_CLO = (1 << 3), // Command List Override |
135 | 141 | PORT_CMD_POD = (1 << 2), // Power On Device |
136 | 142 | PORT_CMD_SUD = (1 << 1), // Spin-up Device |
… |
… |
enum {
|
167 | 173 | | PORT_INT_DS | PORT_INT_PS | PORT_INT_DHR) |
168 | 174 | |
169 | 175 | enum { |
170 | | ATA_BSY = 0x80, |
171 | | ATA_DF = 0x20, |
172 | | ATA_DRQ = 0x08, |
173 | | ATA_ERR = 0x01, |
174 | | }; |
175 | | |
176 | | |
177 | | enum { |
178 | 176 | PORT_FBS_DWE_SHIFT = 16, // Device With Error |
179 | 177 | PORT_FBS_DWE_MASK = 0xf, |
180 | 178 | PORT_FBS_ADO_SHIFT = 12, // Active Device Optimization |
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 1f8b149..b07de2c 100644
a
|
b
|
|
2 | 2 | * Copyright 2008-2015 Haiku, Inc. All rights reserved. |
3 | 3 | * Copyright 2007-2009, Marcus Overhagen. All rights reserved. |
4 | 4 | * Distributed under the terms of the MIT License. |
| 5 | * |
| 6 | * Authors: |
| 7 | * Axel Dörfler, axeld@pinc-software.de |
| 8 | * Michael Lotz, mmlr@mlotz.ch |
| 9 | * Alexander von Gluck IV, kallisti5@unixzen.com |
5 | 10 | */ |
6 | 11 | |
7 | 12 | |
… |
… |
AHCIPort::AHCIPort(AHCIController* controller, int index)
|
54 | 59 | fSectorCount(0), |
55 | 60 | fIsATAPI(false), |
56 | 61 | fTestUnitReadyActive(false), |
57 | | fResetPort(false), |
| 62 | fSoftReset(false), |
58 | 63 | fError(false), |
59 | 64 | fTrimSupported(false) |
60 | 65 | { |
… |
… |
AHCIPort::Init1()
|
120 | 125 | fRegs->is = fRegs->is; |
121 | 126 | |
122 | 127 | // clear error bits |
123 | | fRegs->serr = fRegs->serr; |
| 128 | _ClearErrorRegister(); |
124 | 129 | |
125 | 130 | // power up device |
126 | 131 | fRegs->cmd |= PORT_CMD_POD; |
… |
… |
AHCIPort::Init1()
|
131 | 136 | // activate link |
132 | 137 | fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE; |
133 | 138 | |
134 | | // enable FIS receive |
135 | | fRegs->cmd |= PORT_CMD_FER; |
| 139 | // enable FIS receive (enabled when fb set, only to be disabled when unset) |
| 140 | fRegs->cmd |= PORT_CMD_FRE; |
136 | 141 | |
137 | 142 | FlushPostedWrites(); |
138 | 143 | |
… |
… |
AHCIPort::Init2()
|
146 | 151 | { |
147 | 152 | TRACE("AHCIPort::Init2 port %d\n", fIndex); |
148 | 153 | |
149 | | // start DMA engine |
150 | | fRegs->cmd |= PORT_CMD_ST; |
| 154 | // enable port |
| 155 | Enable(); |
151 | 156 | |
152 | 157 | // enable interrupts |
153 | 158 | fRegs->ie = PORT_INT_MASK; |
154 | 159 | |
155 | 160 | FlushPostedWrites(); |
156 | 161 | |
157 | | ResetPort(true); |
| 162 | // reset port and probe info |
| 163 | SoftReset(); |
158 | 164 | |
159 | 165 | TRACE("ie 0x%08" B_PRIx32 "\n", fRegs->ie); |
160 | 166 | TRACE("is 0x%08" B_PRIx32 "\n", fRegs->is); |
… |
… |
AHCIPort::Init2()
|
172 | 178 | |
173 | 179 | fDevicePresent = (fRegs->ssts & 0xf) == 0x3; |
174 | 180 | |
| 181 | TRACE("%s: port %d, device %s\n", __func__, fIndex, |
| 182 | fDevicePresent ? "present" : "absent"); |
| 183 | |
175 | 184 | return B_OK; |
176 | 185 | } |
177 | 186 | |
… |
… |
AHCIPort::Uninit()
|
181 | 190 | { |
182 | 191 | TRACE("AHCIPort::Uninit port %d\n", fIndex); |
183 | 192 | |
184 | | // disable FIS receive |
185 | | fRegs->cmd &= ~PORT_CMD_FER; |
| 193 | // Spec v1.3.1, §10.3.2 - Shut down port before unsetting FRE |
186 | 194 | |
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); |
| 195 | // shutdown the port |
| 196 | if (!Disable()) { |
| 197 | ERROR("%s: port %d error, unable to shutdown before FRE clear!\n", |
| 198 | __func__, fIndex); |
| 199 | return; |
190 | 200 | } |
191 | 201 | |
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 | | } |
| 202 | // Clear FRE and wait for completion |
| 203 | fRegs->cmd &= ~PORT_CMD_FRE; |
| 204 | if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) |
| 205 | ERROR("%s: port %d error FIS rx still running\n", __func__, fIndex); |
200 | 206 | |
201 | 207 | // disable interrupts |
202 | 208 | fRegs->ie = 0; |
… |
… |
void
|
218 | 224 | AHCIPort::ResetDevice() |
219 | 225 | { |
220 | 226 | // perform a hard reset |
221 | | if (!_HardReset()) |
| 227 | if (PortReset() != B_OK) { |
| 228 | ERROR("%s: port %d unable to hard reset device\n", __func__, fIndex); |
222 | 229 | return; |
| 230 | } |
223 | 231 | |
224 | 232 | if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) |
225 | 233 | TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex); |
… |
… |
AHCIPort::ResetDevice()
|
238 | 246 | |
239 | 247 | |
240 | 248 | status_t |
241 | | AHCIPort::ResetPort(bool forceDeviceReset) |
| 249 | AHCIPort::SoftReset() |
242 | 250 | { |
243 | | if (!fTestUnitReadyActive) |
244 | | TRACE("AHCIPort::ResetPort port %d\n", fIndex); |
| 251 | TRACE("AHCIPort::SoftReset port %d\n", fIndex); |
245 | 252 | |
246 | | // stop DMA engine |
247 | | fRegs->cmd &= ~PORT_CMD_ST; |
248 | | FlushPostedWrites(); |
| 253 | // Spec v1.3.1, §10.4.1 Software Reset |
| 254 | // A single device on one port is reset, HBA and phy comm remain intact. |
249 | 255 | |
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); |
| 256 | // stop port, flush transactions |
| 257 | if (!Disable()) { |
| 258 | // If the port doesn't power off, move on to a stronger reset. |
| 259 | ERROR("%s: port %d soft reset failed. Moving on to port reset.\n", |
| 260 | __func__, fIndex); |
| 261 | return PortReset(); |
253 | 262 | } |
254 | 263 | |
255 | | bool deviceBusy = fRegs->tfd & (ATA_BSY | ATA_DRQ); |
| 264 | // start port |
| 265 | Enable(); |
256 | 266 | |
257 | | if (!fTestUnitReadyActive) { |
258 | | TRACE("AHCIPort::ResetPort port %d, deviceBusy %d, " |
259 | | "forceDeviceReset %d\n", fIndex, deviceBusy, forceDeviceReset); |
| 267 | // TODO: If FBS Enable, clear PxFBS.EN prior to issuing sw reset |
| 268 | |
| 269 | if (wait_until_clear(&fRegs->tfd, ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST, |
| 270 | 1000000) < B_OK) { |
| 271 | ERROR("%s: port %d still busy. Moving on to port reset.\n", |
| 272 | __func__, fIndex); |
| 273 | return PortReset(); |
260 | 274 | } |
261 | 275 | |
262 | | if (deviceBusy || forceDeviceReset) |
263 | | ResetDevice(); |
| 276 | // set command table soft reset bit |
| 277 | fCommandTable->cfis[2] |= ATA_DEVICE_CONTROL_SOFT_RESET; |
264 | 278 | |
265 | | // start DMA engine |
266 | | fRegs->cmd |= PORT_CMD_ST; |
267 | | FlushPostedWrites(); |
| 279 | // TODO: We could use a low level ahci command call (~ahci_exec_polled_cmd) |
| 280 | cpu_status cpu = disable_interrupts(); |
| 281 | acquire_spinlock(&fSpinlock); |
| 282 | |
| 283 | // FIS ATA set Reset + clear busy |
| 284 | fCommandList[0].r = 1; |
| 285 | fCommandList[0].c = 1; |
| 286 | // FIS ATA clear Reset + clear busy |
| 287 | fCommandList[1].r = 0; |
| 288 | fCommandList[1].c = 0; |
| 289 | |
| 290 | // Issue Command |
| 291 | fRegs->ci = 1; |
| 292 | FlushPostedWrites(); |
| 293 | release_spinlock(&fSpinlock); |
| 294 | restore_interrupts(cpu); |
| 295 | |
| 296 | if (wait_until_clear(&fRegs->ci, 0, 1000000) < B_OK) { |
| 297 | TRACE("%s: port %d: device is busy\n", __func__, fIndex); |
| 298 | return PortReset(); |
| 299 | } |
| 300 | |
| 301 | fCommandTable->cfis[2] &= ~ATA_DEVICE_CONTROL_SOFT_RESET; |
| 302 | |
| 303 | if (wait_until_clear(&fRegs->tfd, ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST, |
| 304 | 1000000) < B_OK) { |
| 305 | ERROR("%s: port %d software reset failed. Doing port reset...\n", |
| 306 | __func__, fIndex); |
| 307 | return PortReset(); |
| 308 | } |
| 309 | //#endif |
268 | 310 | |
269 | 311 | return PostReset(); |
270 | 312 | } |
271 | 313 | |
272 | 314 | |
273 | 315 | status_t |
274 | | AHCIPort::PostReset() |
| 316 | AHCIPort::PortReset() |
275 | 317 | { |
276 | | if (!fTestUnitReadyActive) |
277 | | TRACE("AHCIPort::PostReset port %d\n", fIndex); |
| 318 | TRACE("AHCIPort::PortReset port %d\n", fIndex); |
| 319 | |
| 320 | // Spec v1.3.1, §10.4.2 Port Reset |
| 321 | // Physical comm between HBA and port disabled. More Intrusive |
| 322 | if (!Disable()) { |
| 323 | ERROR("%s: port %d unable to reset!\n", __func__, fIndex); |
| 324 | return B_ERROR; |
| 325 | } |
| 326 | |
| 327 | fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK) |
| 328 | | DET_INITIALIZATION << SATA_CONTROL_DET_SHIFT; |
| 329 | FlushPostedWrites(); |
| 330 | spin(1100); |
| 331 | // You must wait 1ms at minimum |
| 332 | fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK) |
| 333 | | DET_NO_INITIALIZATION << SATA_CONTROL_DET_SHIFT; |
| 334 | FlushPostedWrites(); |
278 | 335 | |
279 | | if ((fRegs->ssts & 0xf) != 0x3 || (fRegs->tfd & 0xff) == 0x7f) { |
280 | | TRACE("AHCIPort::PostReset port %d: no device\n", fIndex); |
281 | | return B_OK; |
| 336 | if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) { |
| 337 | TRACE("AHCIPort::PortReset port %d device present but no phy " |
| 338 | "communication\n", fIndex); |
| 339 | return B_ERROR; |
282 | 340 | } |
283 | 341 | |
| 342 | //if ((fRegs->tfd & 0xff) == 0x7f) { |
| 343 | // TRACE("AHCIPort::PostReset port %d: no device\n", fIndex); |
| 344 | // return B_OK; |
| 345 | //} |
| 346 | |
| 347 | Enable(); |
| 348 | |
| 349 | return PostReset(); |
| 350 | } |
| 351 | |
| 352 | |
| 353 | status_t |
| 354 | AHCIPort::PostReset() |
| 355 | { |
284 | 356 | if ((fRegs->tfd & 0xff) == 0xff) |
285 | 357 | snooze(200000); |
286 | 358 | |
… |
… |
AHCIPort::PostReset()
|
290 | 362 | return B_ERROR; |
291 | 363 | } |
292 | 364 | |
293 | | wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000); |
294 | | |
295 | | fIsATAPI = fRegs->sig == 0xeb140101; |
| 365 | wait_until_clear(&fRegs->tfd, ATA_STATUS_BUSY, 31000000); |
296 | 366 | |
| 367 | fIsATAPI = fRegs->sig == SATA_SIG_ATAPI; |
297 | 368 | if (fIsATAPI) |
298 | 369 | fRegs->cmd |= PORT_CMD_ATAPI; |
299 | 370 | else |
… |
… |
AHCIPort::PostReset()
|
302 | 373 | |
303 | 374 | if (!fTestUnitReadyActive) { |
304 | 375 | TRACE("device signature 0x%08" B_PRIx32 " (%s)\n", fRegs->sig, |
305 | | fRegs->sig == 0xeb140101 ? "ATAPI" : fRegs->sig == 0x00000101 |
| 376 | fRegs->sig == SATA_SIG_ATAPI ? "ATAPI" : fRegs->sig == SATA_SIG_ATA |
306 | 377 | ? "ATA" : "unknown"); |
307 | 378 | } |
308 | 379 | |
| 380 | _ClearErrorRegister(); |
| 381 | |
309 | 382 | return B_OK; |
310 | 383 | } |
311 | 384 | |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
374 | 447 | } |
375 | 448 | |
376 | 449 | // read and clear SError |
377 | | uint32 serr = fRegs->serr; |
378 | | fRegs->serr = serr; |
| 450 | _ClearErrorRegister(); |
379 | 451 | |
380 | 452 | if (is & PORT_INT_TFE) { |
381 | 453 | if (!fTestUnitReadyActive) |
382 | 454 | TRACE("Task File Error\n"); |
383 | 455 | |
384 | | fResetPort = true; |
| 456 | fSoftReset = true; |
385 | 457 | fError = true; |
386 | 458 | } |
387 | 459 | if (is & PORT_INT_HBF) { |
388 | 460 | TRACE("Host Bus Fatal Error\n"); |
389 | | fResetPort = true; |
| 461 | fSoftReset = true; |
390 | 462 | fError = true; |
391 | 463 | } |
392 | 464 | if (is & PORT_INT_HBD) { |
393 | 465 | TRACE("Host Bus Data Error\n"); |
394 | | fResetPort = true; |
| 466 | fSoftReset = true; |
395 | 467 | fError = true; |
396 | 468 | } |
397 | 469 | if (is & PORT_INT_IF) { |
398 | 470 | TRACE("Interface Fatal Error\n"); |
399 | | fResetPort = true; |
| 471 | fSoftReset = true; |
400 | 472 | fError = true; |
401 | 473 | } |
402 | 474 | if (is & PORT_INT_INF) { |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
404 | 476 | } |
405 | 477 | if (is & PORT_INT_OF) { |
406 | 478 | TRACE("Overflow\n"); |
407 | | fResetPort = true; |
| 479 | fSoftReset = true; |
408 | 480 | fError = true; |
409 | 481 | } |
410 | 482 | if (is & PORT_INT_IPM) { |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
412 | 484 | } |
413 | 485 | if (is & PORT_INT_PRC) { |
414 | 486 | TRACE("PhyReady Change\n"); |
415 | | // fResetPort = true; |
| 487 | // fSoftReset = true; |
416 | 488 | } |
417 | 489 | if (is & PORT_INT_PC) { |
418 | 490 | TRACE("Port Connect Change\n"); |
419 | 491 | // TODO: check if the COMINIT is actually unsolicited! |
420 | 492 | // Spec v1.3, §6.2.2.3 Recovery of Unsolicited COMINIT |
| 493 | // Spec v1.3.1, §7.4 Interaction of command list and port change status |
| 494 | // TODO: Issue COMRESET ? |
| 495 | //PortReset(); |
| 496 | //HBAReset(); ??? |
421 | 497 | |
422 | | // perform a hard reset |
423 | | // if (!_HardReset()) |
424 | | // return; |
425 | | // |
426 | | // // clear error bits to clear PxSERR.DIAG.X |
427 | | // _ClearErrorRegister(); |
| 498 | // clear error bits to clear PxSERR.DIAG.X |
| 499 | _ClearErrorRegister(); |
428 | 500 | } |
429 | 501 | if (is & PORT_INT_UF) { |
430 | 502 | TRACE("Unknown FIS\n"); |
431 | | fResetPort = true; |
| 503 | fSoftReset = true; |
432 | 504 | } |
433 | 505 | |
434 | 506 | if (fError) { |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
453 | 525 | status_t status = get_memory_map_etc(B_CURRENT_TEAM, data, dataSize, |
454 | 526 | entries, &entriesUsed); |
455 | 527 | if (status != B_OK) { |
456 | | TRACE("AHCIPort::FillPrdTable get_memory_map() failed: %s\n", |
457 | | strerror(status)); |
| 528 | TRACE("%s: get_memory_map() failed: %s\n", __func__, strerror(status)); |
458 | 529 | return B_ERROR; |
459 | 530 | } |
460 | 531 | |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
501 | 572 | sgCount--; |
502 | 573 | } |
503 | 574 | if (*prdCount == 0) { |
504 | | TRACE("AHCIPort::FillPrdTable: count is 0\n"); |
| 575 | TRACE("%s: count is 0\n", __func__); |
505 | 576 | return B_ERROR; |
506 | 577 | } |
507 | 578 | if (dataSize > 0) { |
… |
… |
AHCIPort::ScsiInquiry(scsi_ccb* request)
|
664 | 735 | ExecuteSataRequest(&sreq); |
665 | 736 | sreq.WaitForCompletion(); |
666 | 737 | |
667 | | if ((sreq.CompletionStatus() & ATA_ERR) != 0) { |
| 738 | if ((sreq.CompletionStatus() & ATA_STATUS_ERROR) != 0) { |
668 | 739 | ERROR("identify device failed\n"); |
669 | 740 | request->subsys_status = SCSI_REQ_CMP_ERR; |
670 | 741 | gSCSI->finished(request, 1); |
… |
… |
for (uint32 i = 0; i < lbaRangeCount; i++) {
|
1006 | 1077 | ExecuteSataRequest(&sreq); |
1007 | 1078 | sreq.WaitForCompletion(); |
1008 | 1079 | |
1009 | | if ((sreq.CompletionStatus() & ATA_ERR) != 0) { |
| 1080 | if ((sreq.CompletionStatus() & ATA_STATUS_ERROR) != 0) { |
1010 | 1081 | TRACE("trim failed (%" B_PRIu32 " ranges)!\n", lbaRangeCount); |
1011 | 1082 | request->subsys_status = SCSI_REQ_CMP_ERR; |
1012 | 1083 | } else |
… |
… |
AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)
|
1058 | 1129 | fCommandList->prdtl = prdEntrys; |
1059 | 1130 | fCommandList->prdbc = 0; |
1060 | 1131 | |
1061 | | if (wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 1000000) < B_OK) { |
| 1132 | if (wait_until_clear(&fRegs->tfd, ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST, |
| 1133 | 1000000) < B_OK) { |
1062 | 1134 | TRACE("ExecuteAtaRequest port %d: device is busy\n", fIndex); |
1063 | | ResetPort(); |
| 1135 | SoftReset(); |
1064 | 1136 | FinishTransfer(); |
1065 | 1137 | request->Abort(); |
1066 | 1138 | return; |
… |
… |
AHCIPort::ExecuteSataRequest(sata_request* request, bool isWrite)
|
1095 | 1167 | TRACE("tfd 0x%08" B_PRIx32 "\n", fRegs->tfd); |
1096 | 1168 | */ |
1097 | 1169 | |
1098 | | if (fResetPort || status == B_TIMED_OUT) { |
1099 | | fResetPort = false; |
1100 | | ResetPort(); |
| 1170 | if (fSoftReset || status == B_TIMED_OUT) { |
| 1171 | fSoftReset = false; |
| 1172 | SoftReset(); |
1101 | 1173 | } |
1102 | 1174 | |
1103 | 1175 | size_t bytesTransfered = fCommandList->prdbc; |
… |
… |
AHCIPort::ScsiGetRestrictions(bool* isATAPI, bool* noAutoSense,
|
1323 | 1395 | |
1324 | 1396 | |
1325 | 1397 | bool |
1326 | | AHCIPort::_HardReset() |
| 1398 | AHCIPort::Enable() |
1327 | 1399 | { |
| 1400 | // Spec v1.3.1, §10.3.1 Start (PxCMD.ST) |
| 1401 | TRACE("%s: port %d\n", __func__, fIndex); |
1328 | 1402 | if ((fRegs->cmd & PORT_CMD_ST) != 0) { |
1329 | | // We shouldn't perform a reset, but at least document it |
1330 | | TRACE("AHCIPort::_HardReset() PORT_CMD_ST set, behaviour undefined\n"); |
| 1403 | TRACE("%s: Starting port already running!\n", __func__); |
1331 | 1404 | return false; |
1332 | 1405 | } |
1333 | 1406 | |
1334 | | fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK) |
1335 | | | DET_INITIALIZATION << SATA_CONTROL_DET_SHIFT; |
1336 | | FlushPostedWrites(); |
1337 | | spin(1100); |
1338 | | // You must wait 1ms at minimum |
1339 | | fRegs->sctl = (fRegs->sctl & ~SATA_CONTROL_DET_MASK) |
1340 | | | DET_NO_INITIALIZATION << SATA_CONTROL_DET_SHIFT; |
| 1407 | if ((fRegs->cmd & PORT_CMD_FRE) == 0) { |
| 1408 | TRACE("%s: Unable to start port without FRE enabled!\n", __func__); |
| 1409 | return false; |
| 1410 | } |
| 1411 | |
| 1412 | // Clear DMA engine and wait for completion |
| 1413 | if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) { |
| 1414 | TRACE("%s: port %d error DMA engine still running\n", __func__, |
| 1415 | fIndex); |
| 1416 | return false; |
| 1417 | } |
| 1418 | // Start port |
| 1419 | fRegs->cmd |= PORT_CMD_ST; |
1341 | 1420 | FlushPostedWrites(); |
| 1421 | return true; |
| 1422 | } |
| 1423 | |
| 1424 | |
| 1425 | bool |
| 1426 | AHCIPort::Disable() |
| 1427 | { |
| 1428 | TRACE("%s: port %d\n", __func__, fIndex); |
| 1429 | |
| 1430 | if ((fRegs->cmd & PORT_CMD_ST) == 0) { |
| 1431 | // Port already disabled, carry on. |
| 1432 | TRACE("%s: port %d attempting to disable stopped port.\n", |
| 1433 | __func__, fIndex); |
| 1434 | } else { |
| 1435 | // Disable port |
| 1436 | fRegs->cmd &= ~PORT_CMD_ST; |
| 1437 | FlushPostedWrites(); |
| 1438 | } |
| 1439 | |
| 1440 | // Spec v1.3.1, §10.4.2 Port Reset - assume hung after 500 mil. |
| 1441 | // Clear DMA engine and wait for completion |
| 1442 | if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) { |
| 1443 | TRACE("%s: port %d error DMA engine still running\n", __func__, |
| 1444 | fIndex); |
| 1445 | return false; |
| 1446 | } |
1342 | 1447 | |
1343 | 1448 | return true; |
1344 | 1449 | } |
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; |
diff --git a/src/add-ons/kernel/busses/scsi/ahci/sata_request.cpp b/src/add-ons/kernel/busses/scsi/ahci/sata_request.cpp
index 6a53fcf..9e06dce 100644
a
|
b
|
sata_request::SetATAPICommand(size_t transferLength)
|
121 | 121 | void |
122 | 122 | sata_request::Finish(int tfd, size_t bytesTransfered) |
123 | 123 | { |
124 | | if ((tfd & (ATA_ERR | ATA_DF)) != 0) { |
| 124 | if ((tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) != 0) { |
125 | 125 | uint8 status = tfd & 0xff; |
126 | 126 | uint8 error = (tfd >> 8) & 0xff; |
127 | 127 | |
… |
… |
sata_request::Finish(int tfd, size_t bytesTransfered)
|
135 | 135 | fCcb->data_resid = fCcb->data_length - bytesTransfered; |
136 | 136 | fCcb->device_status = SCSI_STATUS_GOOD; |
137 | 137 | fCcb->subsys_status = SCSI_REQ_CMP; |
138 | | if (tfd & (ATA_ERR | ATA_DF)) { |
| 138 | if (tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) { |
139 | 139 | fCcb->subsys_status = SCSI_REQ_CMP_ERR; |
140 | 140 | if (fIsATAPI) { |
141 | 141 | if (!IsTestUnitReady()) { |
… |
… |
sata_request::Abort()
|
187 | 187 | gSCSI->finished(fCcb, 1); |
188 | 188 | delete this; |
189 | 189 | } else { |
190 | | fCompletionStatus = ATA_ERR; |
| 190 | fCompletionStatus = ATA_STATUS_ERROR; |
191 | 191 | release_sem(fCompletionSem); |
192 | 192 | } |
193 | 193 | } |