blob: 7279199ad427833c847ac20bf9017ca7e9625a39 [file] [log] [blame]
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +01001// Low level AHCI disk access
2//
3// Copyright (C) 2010 Gerd Hoffmann <kraxel@redhat.com>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "types.h" // u8
8#include "ioport.h" // inb
9#include "util.h" // dprintf
10#include "biosvar.h" // GET_EBDA
Kevin O'Connor9cb49922011-06-20 22:22:42 -040011#include "pci.h" // foreachpci
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010012#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
13#include "pci_regs.h" // PCI_INTERRUPT_LINE
14#include "boot.h" // add_bcv_hd
15#include "disk.h" // struct ata_s
16#include "ata.h" // ATA_CB_STAT
17#include "ahci.h" // CDB_CMD_READ_10
18#include "blockcmd.h" // CDB_CMD_READ_10
19
20#define AHCI_MAX_RETRIES 5
21
22/****************************************************************
23 * these bits must run in both 16bit and 32bit modes
24 ****************************************************************/
Scott Duplichan9c48aab2011-07-14 16:24:02 +020025u8 *ahci_buf_fl VAR16VISIBLE;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010026
27// prepare sata command fis
28static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command)
29{
30 memset_fl(fis, 0, sizeof(*fis));
31 SET_FLATPTR(fis->command, command);
32}
33
34static void sata_prep_readwrite(struct sata_cmd_fis *fis,
35 struct disk_op_s *op, int iswrite)
36{
37 u64 lba = op->lba;
38 u8 command;
39
40 memset_fl(fis, 0, sizeof(*fis));
41
42 if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
43 SET_FLATPTR(fis->sector_count2, op->count >> 8);
44 SET_FLATPTR(fis->lba_low2, lba >> 24);
45 SET_FLATPTR(fis->lba_mid2, lba >> 32);
46 SET_FLATPTR(fis->lba_high2, lba >> 40);
47 lba &= 0xffffff;
48 command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
49 : ATA_CMD_READ_DMA_EXT);
50 } else {
51 command = (iswrite ? ATA_CMD_WRITE_DMA
52 : ATA_CMD_READ_DMA);
53 }
Gerd Hoffmannc19fc712010-12-09 08:39:45 +010054 SET_FLATPTR(fis->feature, 1); /* dma */
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010055 SET_FLATPTR(fis->command, command);
56 SET_FLATPTR(fis->sector_count, op->count);
57 SET_FLATPTR(fis->lba_low, lba);
58 SET_FLATPTR(fis->lba_mid, lba >> 8);
59 SET_FLATPTR(fis->lba_high, lba >> 16);
60 SET_FLATPTR(fis->device, ((lba >> 24) & 0xf) | ATA_CB_DH_LBA);
61}
62
63static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize)
64{
65 memset_fl(fis, 0, sizeof(*fis));
66 SET_FLATPTR(fis->command, ATA_CMD_PACKET);
Gerd Hoffmannc19fc712010-12-09 08:39:45 +010067 SET_FLATPTR(fis->feature, 1); /* dma */
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010068 SET_FLATPTR(fis->lba_mid, blocksize);
69 SET_FLATPTR(fis->lba_high, blocksize >> 8);
70}
71
72// ahci register access helpers
73static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg)
74{
75 u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
76 return pci_readl(addr);
77}
78
79static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val)
80{
81 u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
82 pci_writel(addr, val);
83}
84
85static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg)
86{
87 u32 ctrl_reg = 0x100;
88 ctrl_reg += pnr * 0x80;
89 ctrl_reg += port_reg;
90 return ctrl_reg;
91}
92
93static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg)
94{
95 u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
96 return ahci_ctrl_readl(ctrl, ctrl_reg);
97}
98
99static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val)
100{
101 u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
102 ahci_ctrl_writel(ctrl, ctrl_reg, val);
103}
104
105// submit ahci command + wait for result
106static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
107 void *buffer, u32 bsize)
108{
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200109 u32 val, status, success, flags, intbits, error;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100110 struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl);
111 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
112 struct ahci_fis_s *fis = GET_GLOBAL(port->fis);
113 struct ahci_list_s *list = GET_GLOBAL(port->list);
114 u32 pnr = GET_GLOBAL(port->pnr);
115
116 SET_FLATPTR(cmd->fis.reg, 0x27);
117 SET_FLATPTR(cmd->fis.pmp_type, (1 << 7)); /* cmd fis */
118 SET_FLATPTR(cmd->prdt[0].base, ((u32)buffer));
119 SET_FLATPTR(cmd->prdt[0].baseu, 0);
120 SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
121
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100122 flags = ((1 << 16) | /* one prd entry */
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100123 (iswrite ? (1 << 6) : 0) |
124 (isatapi ? (1 << 5) : 0) |
Gerd Hoffmanna8c6a4e2011-07-14 16:23:59 +0200125 (5 << 0)); /* fis length (dwords) */
126 SET_FLATPTR(list[0].flags, flags);
127 SET_FLATPTR(list[0].bytes, 0);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100128 SET_FLATPTR(list[0].base, ((u32)(cmd)));
129 SET_FLATPTR(list[0].baseu, 0);
130
131 dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200132 intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
133 if (intbits)
134 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100135 ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
136 ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200137
138 do {
139 for (;;) {
140 intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
141 if (intbits) {
142 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
143 if (intbits & 0x02) {
144 status = GET_FLATPTR(fis->psfis[2]);
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200145 error = GET_FLATPTR(fis->psfis[3]);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200146 break;
147 }
148 if (intbits & 0x01) {
149 status = GET_FLATPTR(fis->rfis[2]);
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200150 error = GET_FLATPTR(fis->rfis[3]);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200151 break;
152 }
153 }
154 yield();
155 }
156 dprintf(2, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n",
157 pnr, intbits, status);
158 } while (status & ATA_CB_STAT_BSY);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100159
160 success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
Gerd Hoffmanncbda7952011-07-14 16:24:03 +0200161 ATA_CB_STAT_ERR)) &&
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100162 ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY)));
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200163 if (success) {
164 dprintf(2, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr,
165 status);
166 } else {
167 dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr,
168 status, error);
169
170 // non-queued error recovery (AHCI 1.3 section 6.2.2.1)
171 // Clears PxCMD.ST to 0 to reset the PxCI register
172 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
173 ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START);
174
175 // waits for PxCMD.CR to clear to 0
176 while (1) {
177 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
178 if ((val & PORT_CMD_LIST_ON) == 0)
179 break;
180 yield();
181 }
182
183 // Clears any error bits in PxSERR to enable capturing new errors
184 val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
185 ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
186
187 // Clears status bits in PxIS as appropriate
188 val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
189 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
190
191 // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue
192 // a COMRESET to the device to put it in an idle state
193 val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
194 if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) {
195 dprintf(2, "AHCI/%d: issue comreset\n", pnr);
196 val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL);
197 // set Device Detection Initialization (DET) to 1 for 1 ms for comreset
198 ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1);
199 mdelay (1);
200 ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val);
201 }
202
203 // Sets PxCMD.ST to 1 to enable issuing new commands
204 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
205 ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
206 }
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100207 return success ? 0 : -1;
208}
209
210#define CDROM_CDB_SIZE 12
211
212int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
213{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -0500214 if (! CONFIG_AHCI)
215 return 0;
216
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100217 struct ahci_port_s *port = container_of(
218 op->drive_g, struct ahci_port_s, drive);
219 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
220 u8 *atapi = cdbcmd;
221 int i, rc;
222
223 sata_prep_atapi(&cmd->fis, blocksize);
224 for (i = 0; i < CDROM_CDB_SIZE; i++) {
225 SET_FLATPTR(cmd->atapi[i], atapi[i]);
226 }
227 rc = ahci_command(port, 0, 1, op->buf_fl,
228 op->count * blocksize);
229 if (rc < 0)
230 return DISK_RET_EBADTRACK;
231 return DISK_RET_SUCCESS;
232}
233
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200234// read/write count blocks from a harddrive, op->buf_fl must be word aligned
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100235static int
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200236ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100237{
238 struct ahci_port_s *port = container_of(
239 op->drive_g, struct ahci_port_s, drive);
240 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
241 int rc;
242
243 sata_prep_readwrite(&cmd->fis, op, iswrite);
244 rc = ahci_command(port, iswrite, 0, op->buf_fl,
245 op->count * DISK_SECTOR_SIZE);
246 dprintf(2, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n",
247 iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc);
248 if (rc < 0)
249 return DISK_RET_EBADTRACK;
250 return DISK_RET_SUCCESS;
251}
252
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200253// read/write count blocks from a harddrive.
254static int
255ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
256{
257 // if caller's buffer is word aligned, use it directly
258 if (((u32) op->buf_fl & 1) == 0)
259 return ahci_disk_readwrite_aligned(op, iswrite);
260
261 // Use a word aligned buffer for AHCI I/O
262 int rc;
263 struct disk_op_s localop = *op;
264 u8 *alignedbuf_fl = GET_GLOBAL(ahci_buf_fl);
265 u8 *position = op->buf_fl;
266
267 localop.buf_fl = alignedbuf_fl;
268 localop.count = 1;
269
270 if (iswrite) {
271 u16 block;
272 for (block = 0; block < op->count; block++) {
273 memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE);
274 rc = ahci_disk_readwrite_aligned (&localop, 1);
275 if (rc)
276 return rc;
277 position += DISK_SECTOR_SIZE;
278 localop.lba++;
279 }
280 } else { // read
281 u16 block;
282 for (block = 0; block < op->count; block++) {
283 rc = ahci_disk_readwrite_aligned (&localop, 0);
284 if (rc)
285 return rc;
286 memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE);
287 position += DISK_SECTOR_SIZE;
288 localop.lba++;
289 }
290 }
291 return DISK_RET_SUCCESS;
292}
293
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100294// command demuxer
295int process_ahci_op(struct disk_op_s *op)
296{
297 struct ahci_port_s *port;
298 u32 atapi;
299
300 if (!CONFIG_AHCI)
301 return 0;
302
303 port = container_of(op->drive_g, struct ahci_port_s, drive);
304 atapi = GET_GLOBAL(port->atapi);
305
306 if (atapi) {
307 switch (op->command) {
308 case CMD_READ:
309 return cdb_read(op);
310 case CMD_WRITE:
311 case CMD_FORMAT:
312 return DISK_RET_EWRITEPROTECT;
313 case CMD_RESET:
314 /* FIXME: what should we do here? */
315 case CMD_VERIFY:
316 case CMD_SEEK:
317 return DISK_RET_SUCCESS;
318 default:
319 dprintf(1, "AHCI: unknown cdrom command %d\n", op->command);
320 op->count = 0;
321 return DISK_RET_EPARAM;
322 }
323 } else {
324 switch (op->command) {
325 case CMD_READ:
326 return ahci_disk_readwrite(op, 0);
327 case CMD_WRITE:
328 return ahci_disk_readwrite(op, 1);
329 case CMD_RESET:
330 /* FIXME: what should we do here? */
331 case CMD_FORMAT:
332 case CMD_VERIFY:
333 case CMD_SEEK:
334 return DISK_RET_SUCCESS;
335 default:
336 dprintf(1, "AHCI: unknown disk command %d\n", op->command);
337 op->count = 0;
338 return DISK_RET_EPARAM;
339 }
340 }
341}
342
343/****************************************************************
344 * everything below is pure 32bit code
345 ****************************************************************/
346
347static void
348ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
349{
350 u32 val, count = 0;
351
352 /* disable FIS + CMD */
353 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
354 while (val & (PORT_CMD_FIS_RX | PORT_CMD_START |
355 PORT_CMD_FIS_ON | PORT_CMD_LIST_ON) &&
356 count < AHCI_MAX_RETRIES) {
357 val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START);
358 ahci_port_writel(ctrl, pnr, PORT_CMD, val);
359 ndelay(500);
360 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
361 count++;
362 }
363
364 /* clear status */
365 val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
366 if (val)
367 ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
368
369 /* disable + clear IRQs */
370 ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, val);
371 val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
372 if (val)
373 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
374}
375
376static int
377ahci_port_probe(struct ahci_ctrl_s *ctrl, u32 pnr)
378{
379 u32 val, count = 0;
380
381 val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
Gerd Hoffmanncbda7952011-07-14 16:24:03 +0200382 while (val & ATA_CB_STAT_BSY) {
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100383 ndelay(500);
384 val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
385 count++;
386 if (count >= AHCI_MAX_RETRIES)
387 return -1;
388 }
389
390 val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
391 if ((val & 0x07) != 0x03)
392 return -1;
393 return 0;
394}
395
396#define MAXMODEL 40
397
398static struct ahci_port_s*
399ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
400{
401 struct ahci_port_s *port = malloc_fseg(sizeof(*port));
402 char model[MAXMODEL+1];
403 u16 buffer[256];
404 u32 val;
405 int rc;
406
407 if (!port) {
408 warn_noalloc();
409 return NULL;
410 }
411 port->pnr = pnr;
412 port->ctrl = ctrl;
413 port->list = memalign_low(1024, 1024);
414 port->fis = memalign_low(256, 256);
415 port->cmd = memalign_low(256, 256);
416 if (port->list == NULL || port->fis == NULL || port->cmd == NULL) {
417 warn_noalloc();
418 return NULL;
419 }
420 memset(port->list, 0, 1024);
421 memset(port->fis, 0, 256);
422 memset(port->cmd, 0, 256);
423
424 ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
425 ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
426 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200427 val |= PORT_CMD_FIS_RX;
428 ahci_port_writel(ctrl, pnr, PORT_CMD, val);
429 val |= PORT_CMD_START;
430 ahci_port_writel(ctrl, pnr, PORT_CMD, val);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100431
432 sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
433 rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
434 if (rc == 0) {
435 port->atapi = 1;
436 } else {
437 port->atapi = 0;
438 sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE);
439 rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
440 if (rc < 0)
441 goto err;
442 }
443
444 port->drive.type = DTYPE_AHCI;
Gerd Hoffmann0e6f6362010-12-09 08:39:48 +0100445 port->drive.cntl_id = pnr;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100446 port->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100447
448 if (!port->atapi) {
449 // found disk (ata)
450 port->drive.blksize = DISK_SECTOR_SIZE;
451 port->drive.pchs.cylinders = buffer[1];
452 port->drive.pchs.heads = buffer[3];
453 port->drive.pchs.spt = buffer[6];
454
455 u64 sectors;
456 if (buffer[83] & (1 << 10)) // word 83 - lba48 support
457 sectors = *(u64*)&buffer[100]; // word 100-103
458 else
459 sectors = *(u32*)&buffer[60]; // word 60 and word 61
460 port->drive.sectors = sectors;
461 u64 adjsize = sectors >> 11;
462 char adjprefix = 'M';
463 if (adjsize >= (1 << 16)) {
464 adjsize >>= 10;
465 adjprefix = 'G';
466 }
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500467 char *desc = znprintf(MAXDESCSIZE
468 , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
469 , port->pnr
470 , ata_extract_model(model, MAXMODEL, buffer)
471 , ata_extract_version(buffer)
472 , (u32)adjsize, adjprefix);
473 dprintf(1, "%s\n", desc);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100474
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100475 // Register with bcv system.
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500476 boot_add_hd(&port->drive, desc, -1);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100477 } else {
478 // found cdrom (atapi)
479 port->drive.blksize = CDROM_SECTOR_SIZE;
480 port->drive.sectors = (u64)-1;
481 u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500482 char *desc = znprintf(MAXDESCSIZE
483 , "DVD/CD [AHCI/%d: %s ATAPI-%d %s]"
484 , port->pnr
485 , ata_extract_model(model, MAXMODEL, buffer)
486 , ata_extract_version(buffer)
487 , (iscd ? "DVD/CD" : "Device"));
488 dprintf(1, "%s\n", desc);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100489
490 // fill cdidmap
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500491 if (iscd)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500492 boot_add_cd(&port->drive, desc, -1);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100493 }
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100494
495 return port;
496
497err:
498 dprintf(1, "AHCI/%d: init failure, reset\n", port->pnr);
499 ahci_port_reset(ctrl, pnr);
500 return NULL;
501}
502
503// Detect any drives attached to a given controller.
504static void
505ahci_detect(void *data)
506{
507 struct ahci_ctrl_s *ctrl = data;
508 struct ahci_port_s *port;
509 u32 pnr, max;
510 int rc;
511
512 max = ctrl->caps & 0x1f;
Gerd Hoffmann1e924bb2010-12-09 08:39:47 +0100513 for (pnr = 0; pnr <= max; pnr++) {
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100514 if (!(ctrl->ports & (1 << pnr)))
515 continue;
516 dprintf(2, "AHCI/%d: probing\n", pnr);
517 ahci_port_reset(ctrl, pnr);
518 rc = ahci_port_probe(ctrl, pnr);
519 dprintf(1, "AHCI/%d: link %s\n", pnr, rc == 0 ? "up" : "down");
520 if (rc != 0)
521 continue;
522 port = ahci_port_init(ctrl, pnr);
523 }
524}
525
526// Initialize an ata controller and detect its drives.
527static void
528ahci_init_controller(int bdf)
529{
530 struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl));
531 u32 val;
532
533 if (!ctrl) {
534 warn_noalloc();
535 return;
536 }
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200537
538 ahci_buf_fl = malloc_low(DISK_SECTOR_SIZE);
539 if (!ahci_buf_fl) {
540 warn_noalloc();
541 return;
542 }
543
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100544 ctrl->pci_bdf = bdf;
545 ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5);
546 ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
547 dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n",
548 bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq);
549
Kevin O'Connor7eb02222010-12-12 14:01:47 -0500550 pci_config_maskw(bdf, PCI_COMMAND, 0,
551 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
552
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100553 val = ahci_ctrl_readl(ctrl, HOST_CTL);
554 ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN);
555
556 ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP);
557 ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL);
558 dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n",
559 ctrl->caps, ctrl->ports);
560
561 run_thread(ahci_detect, ctrl);
562}
563
564// Locate and init ahci controllers.
565static void
566ahci_init(void)
567{
568 // Scan PCI bus for ATA adapters
Kevin O'Connor9cb49922011-06-20 22:22:42 -0400569 struct pci_device *pci;
570 foreachpci(pci) {
571 if (pci->class != PCI_CLASS_STORAGE_SATA)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100572 continue;
Kevin O'Connor9cb49922011-06-20 22:22:42 -0400573 if (pci->prog_if != 1 /* AHCI rev 1 */)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100574 continue;
Kevin O'Connor9cb49922011-06-20 22:22:42 -0400575 ahci_init_controller(pci->bdf);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100576 }
577}
578
579void
580ahci_setup(void)
581{
582 ASSERT32FLAT();
583 if (!CONFIG_AHCI)
584 return;
585
586 dprintf(3, "init ahci\n");
587 ahci_init();
588}