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..b1bae91 100644
a
|
b
|
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()
|
132 | 132 | fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE; |
133 | 133 | |
134 | 134 | // enable FIS receive |
135 | | fRegs->cmd |= PORT_CMD_FER; |
| 135 | fRegs->cmd |= PORT_CMD_FRE; |
136 | 136 | |
137 | 137 | FlushPostedWrites(); |
138 | 138 | |
… |
… |
AHCIPort::Init1()
|
144 | 144 | status_t |
145 | 145 | AHCIPort::Init2() |
146 | 146 | { |
147 | | TRACE("AHCIPort::Init2 port %d\n", fIndex); |
| 147 | TRACE("%s: port %d\n", __func__, 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 | // reset port and probe info |
157 | 158 | ResetPort(true); |
158 | 159 | |
159 | 160 | TRACE("ie 0x%08" B_PRIx32 "\n", fRegs->ie); |
… |
… |
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; |
186 | | |
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 | | } |
191 | | |
192 | | // stop DMA engine |
193 | | fRegs->cmd &= ~PORT_CMD_ST; |
| 188 | // Clear FRE and wait for completion |
| 189 | fRegs->cmd &= ~PORT_CMD_FRE; |
| 190 | if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) |
| 191 | TRACE("%s: port %d error FIS rx still running\n", __func__, fIndex); |
194 | 192 | |
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 | | } |
| 193 | // shutdown the port |
| 194 | Disable(); |
200 | 195 | |
201 | 196 | // disable interrupts |
202 | 197 | fRegs->ie = 0; |
… |
… |
void
|
218 | 213 | AHCIPort::ResetDevice() |
219 | 214 | { |
220 | 215 | // perform a hard reset |
221 | | if (!_HardReset()) |
| 216 | if (!_HardReset()) { |
| 217 | ERROR("%s: port %d unable to hard reset device\n", __func__, fIndex); |
222 | 218 | return; |
| 219 | } |
223 | 220 | |
224 | 221 | if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) |
225 | 222 | TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex); |
… |
… |
AHCIPort::ResetDevice()
|
228 | 225 | |
229 | 226 | if (fRegs->ssts & 1) { |
230 | 227 | if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) { |
231 | | TRACE("AHCIPort::ResetDevice port %d device present but no phy " |
| 228 | TRACE("AHCIPort::ResetDevice port %d device present but no phy " |
232 | 229 | "communication\n", fIndex); |
233 | 230 | } |
234 | 231 | } |
… |
… |
status_t
|
241 | 238 | AHCIPort::ResetPort(bool forceDeviceReset) |
242 | 239 | { |
243 | 240 | if (!fTestUnitReadyActive) |
244 | | TRACE("AHCIPort::ResetPort port %d\n", fIndex); |
| 241 | TRACE("%s: port %d\n", __func__, fIndex); |
245 | 242 | |
246 | | // stop DMA engine |
247 | | fRegs->cmd &= ~PORT_CMD_ST; |
248 | | FlushPostedWrites(); |
249 | | |
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); |
253 | | } |
| 243 | // stop port, flush transactions |
| 244 | Disable(); |
254 | 245 | |
255 | 246 | bool deviceBusy = fRegs->tfd & (ATA_BSY | ATA_DRQ); |
256 | 247 | |
257 | 248 | if (!fTestUnitReadyActive) { |
258 | | TRACE("AHCIPort::ResetPort port %d, deviceBusy %d, " |
259 | | "forceDeviceReset %d\n", fIndex, deviceBusy, forceDeviceReset); |
| 249 | TRACE("AHCIPort::ResetPort port %d, deviceBusy %d, " |
| 250 | "forceDeviceReset %d\n", fIndex, deviceBusy, forceDeviceReset); |
260 | 251 | } |
261 | 252 | |
262 | 253 | if (deviceBusy || forceDeviceReset) |
263 | 254 | ResetDevice(); |
264 | 255 | |
265 | | // start DMA engine |
266 | | fRegs->cmd |= PORT_CMD_ST; |
267 | | FlushPostedWrites(); |
| 256 | // start port |
| 257 | Enable(); |
268 | 258 | |
269 | 259 | return PostReset(); |
270 | 260 | } |
… |
… |
status_t
|
274 | 264 | AHCIPort::PostReset() |
275 | 265 | { |
276 | 266 | if (!fTestUnitReadyActive) |
277 | | TRACE("AHCIPort::PostReset port %d\n", fIndex); |
| 267 | TRACE("AHCIPort::PostReset port %d\n", fIndex); |
278 | 268 | |
279 | 269 | if ((fRegs->ssts & 0xf) != 0x3 || (fRegs->tfd & 0xff) == 0x7f) { |
280 | 270 | TRACE("AHCIPort::PostReset port %d: no device\n", fIndex); |
… |
… |
AHCIPort::PostReset()
|
292 | 282 | |
293 | 283 | wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000); |
294 | 284 | |
295 | | fIsATAPI = fRegs->sig == 0xeb140101; |
296 | | |
| 285 | fIsATAPI = fRegs->sig == SATA_SIG_ATAPI; |
297 | 286 | if (fIsATAPI) |
298 | 287 | fRegs->cmd |= PORT_CMD_ATAPI; |
299 | 288 | else |
… |
… |
AHCIPort::PostReset()
|
302 | 291 | |
303 | 292 | if (!fTestUnitReadyActive) { |
304 | 293 | TRACE("device signature 0x%08" B_PRIx32 " (%s)\n", fRegs->sig, |
305 | | fRegs->sig == 0xeb140101 ? "ATAPI" : fRegs->sig == 0x00000101 |
| 294 | fRegs->sig == SATA_SIG_ATAPI ? "ATAPI" : fRegs->sig == SATA_SIG_ATA |
306 | 295 | ? "ATA" : "unknown"); |
307 | 296 | } |
308 | 297 | |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
374 | 363 | } |
375 | 364 | |
376 | 365 | // read and clear SError |
377 | | uint32 serr = fRegs->serr; |
378 | | fRegs->serr = serr; |
| 366 | _ClearErrorRegister(); |
379 | 367 | |
380 | 368 | if (is & PORT_INT_TFE) { |
381 | 369 | if (!fTestUnitReadyActive) |
… |
… |
AHCIPort::InterruptErrorHandler(uint32 is)
|
417 | 405 | if (is & PORT_INT_PC) { |
418 | 406 | TRACE("Port Connect Change\n"); |
419 | 407 | // 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 |
| 408 | // Spec v1.3.1, §7.4 Interaction of command list and port change status |
| 409 | ResetPort(true); |
426 | 410 | _ClearErrorRegister(); |
427 | 411 | } |
428 | 412 | if (is & PORT_INT_UF) { |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
452 | 436 | status_t status = get_memory_map_etc(B_CURRENT_TEAM, data, dataSize, |
453 | 437 | entries, &entriesUsed); |
454 | 438 | if (status != B_OK) { |
455 | | TRACE("AHCIPort::FillPrdTable get_memory_map() failed: %s\n", |
456 | | strerror(status)); |
| 439 | TRACE("%s: get_memory_map() failed: %s\n", __func__, strerror(status)); |
457 | 440 | return B_ERROR; |
458 | 441 | } |
459 | 442 | |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
474 | 457 | FLOW("FillPrdTable: sg-entry addr %#" B_PRIxPHYSADDR ", size %lu\n", |
475 | 458 | address, size); |
476 | 459 | if (address & 1) { |
477 | | TRACE("AHCIPort::FillPrdTable: data alignment error\n"); |
| 460 | TRACE("%s: data alignment error\n", __func__); |
478 | 461 | return B_ERROR; |
479 | 462 | } |
480 | 463 | dataSize -= size; |
… |
… |
AHCIPort::FillPrdTable(volatile prd* prdTable, int* prdCount, int prdMax,
|
500 | 483 | sgCount--; |
501 | 484 | } |
502 | 485 | if (*prdCount == 0) { |
503 | | TRACE("AHCIPort::FillPrdTable: count is 0\n"); |
| 486 | TRACE("%s: count is 0\n", __func__); |
504 | 487 | return B_ERROR; |
505 | 488 | } |
506 | 489 | if (dataSize > 0) { |
507 | | TRACE("AHCIPort::FillPrdTable: sg table %ld bytes too small\n", |
508 | | dataSize); |
| 490 | TRACE("%s: sg table %ld bytes too small\n", __func__, dataSize); |
509 | 491 | return B_ERROR; |
510 | 492 | } |
511 | 493 | return B_OK; |
… |
… |
AHCIPort::ScsiGetRestrictions(bool* isATAPI, bool* noAutoSense,
|
1322 | 1304 | |
1323 | 1305 | |
1324 | 1306 | bool |
| 1307 | AHCIPort::Enable() |
| 1308 | { |
| 1309 | TRACE("%s: port %d\n", __func__, fIndex); |
| 1310 | if ((fRegs->cmd & PORT_CMD_ST) != 0) { |
| 1311 | TRACE("%s: Starting port already running!\n", __func__); |
| 1312 | return false; |
| 1313 | } |
| 1314 | // Start port |
| 1315 | fRegs->cmd |= PORT_CMD_ST; |
| 1316 | FlushPostedWrites(); |
| 1317 | return true; |
| 1318 | } |
| 1319 | |
| 1320 | |
| 1321 | bool |
| 1322 | AHCIPort::Disable() |
| 1323 | { |
| 1324 | TRACE("%s: port %d\n", __func__, fIndex); |
| 1325 | |
| 1326 | if ((fRegs->cmd & PORT_CMD_ST) == 0) { |
| 1327 | TRACE("%s: port %d attempting to disable stopped port.\n", |
| 1328 | __func__, fIndex); |
| 1329 | } |
| 1330 | |
| 1331 | // Disable port |
| 1332 | fRegs->cmd &= ~PORT_CMD_ST; |
| 1333 | FlushPostedWrites(); |
| 1334 | |
| 1335 | // Clear DMA engine and wait for completion |
| 1336 | if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) { |
| 1337 | TRACE("%s: port %d error DMA engine still running\n", __func__, |
| 1338 | fIndex); |
| 1339 | return false; |
| 1340 | } |
| 1341 | |
| 1342 | return true; |
| 1343 | } |
| 1344 | |
| 1345 | |
| 1346 | bool |
1325 | 1347 | AHCIPort::_HardReset() |
1326 | 1348 | { |
1327 | 1349 | if ((fRegs->cmd & PORT_CMD_ST) != 0) { |
1328 | 1350 | // We shouldn't perform a reset, but at least document it |
1329 | | TRACE("AHCIPort::_HardReset() PORT_CMD_ST set, behaviour undefined\n"); |
| 1351 | TRACE("%s: port %d warning: ST set, bypassing hard reset\n", __func__, |
| 1352 | fIndex); |
1330 | 1353 | return false; |
1331 | 1354 | } |
1332 | 1355 | |
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..2127478 100644
a
|
b
|
private:
|
50 | 50 | void ResetDevice(); |
51 | 51 | status_t ResetPort(bool forceDeviceReset = false); |
52 | 52 | status_t PostReset(); |
| 53 | bool Enable(); |
| 54 | bool Disable(); |
| 55 | |
53 | 56 | void FlushPostedWrites(); |
54 | 57 | void DumpD2HFis(); |
55 | 58 | |