blob: 05a80b3774e5e3f7a4da0515a5f6897668e8ef77 [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
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04007#include "ahci.h" // CDB_CMD_READ_10
8#include "ata.h" // ATA_CB_STAT
Kevin O'Connor4bc49972012-05-13 22:58:08 -04009#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "blockcmd.h" // CDB_CMD_READ_10
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "ioport.h" // inb
12#include "malloc.h" // free
13#include "output.h" // dprintf
Kevin O'Connor9cb49922011-06-20 22:22:42 -040014#include "pci.h" // foreachpci
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010015#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
16#include "pci_regs.h" // PCI_INTERRUPT_LINE
Kevin O'Connor3df600b2013-09-14 19:28:55 -040017#include "stacks.h" // yield
Kevin O'Connor135f3f62013-09-14 23:57:26 -040018#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040019#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040020#include "util.h" // timer_calc
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010021
Gerd Hoffmanne1041192011-07-14 16:24:04 +020022#define AHCI_REQUEST_TIMEOUT 32000 // 32 seconds max for IDE ops
23#define AHCI_RESET_TIMEOUT 500 // 500 miliseconds
24#define AHCI_LINK_TIMEOUT 10 // 10 miliseconds
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010025
26/****************************************************************
27 * these bits must run in both 16bit and 32bit modes
28 ****************************************************************/
29
30// prepare sata command fis
31static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command)
32{
33 memset_fl(fis, 0, sizeof(*fis));
Kevin O'Connor890c0852012-05-24 23:55:00 -040034 SET_LOWFLAT(fis->command, command);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010035}
36
37static void sata_prep_readwrite(struct sata_cmd_fis *fis,
38 struct disk_op_s *op, int iswrite)
39{
40 u64 lba = op->lba;
41 u8 command;
42
43 memset_fl(fis, 0, sizeof(*fis));
44
45 if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
Kevin O'Connor890c0852012-05-24 23:55:00 -040046 SET_LOWFLAT(fis->sector_count2, op->count >> 8);
47 SET_LOWFLAT(fis->lba_low2, lba >> 24);
48 SET_LOWFLAT(fis->lba_mid2, lba >> 32);
49 SET_LOWFLAT(fis->lba_high2, lba >> 40);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010050 lba &= 0xffffff;
51 command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
52 : ATA_CMD_READ_DMA_EXT);
53 } else {
54 command = (iswrite ? ATA_CMD_WRITE_DMA
55 : ATA_CMD_READ_DMA);
56 }
Kevin O'Connor890c0852012-05-24 23:55:00 -040057 SET_LOWFLAT(fis->feature, 1); /* dma */
58 SET_LOWFLAT(fis->command, command);
59 SET_LOWFLAT(fis->sector_count, op->count);
60 SET_LOWFLAT(fis->lba_low, lba);
61 SET_LOWFLAT(fis->lba_mid, lba >> 8);
62 SET_LOWFLAT(fis->lba_high, lba >> 16);
63 SET_LOWFLAT(fis->device, ((lba >> 24) & 0xf) | ATA_CB_DH_LBA);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010064}
65
66static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize)
67{
68 memset_fl(fis, 0, sizeof(*fis));
Kevin O'Connor890c0852012-05-24 23:55:00 -040069 SET_LOWFLAT(fis->command, ATA_CMD_PACKET);
70 SET_LOWFLAT(fis->feature, 1); /* dma */
71 SET_LOWFLAT(fis->lba_mid, blocksize);
72 SET_LOWFLAT(fis->lba_high, blocksize >> 8);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010073}
74
75// ahci register access helpers
76static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg)
77{
78 u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
79 return pci_readl(addr);
80}
81
82static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val)
83{
84 u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
85 pci_writel(addr, val);
86}
87
88static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg)
89{
90 u32 ctrl_reg = 0x100;
91 ctrl_reg += pnr * 0x80;
92 ctrl_reg += port_reg;
93 return ctrl_reg;
94}
95
96static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg)
97{
98 u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
99 return ahci_ctrl_readl(ctrl, ctrl_reg);
100}
101
102static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val)
103{
104 u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
105 ahci_ctrl_writel(ctrl, ctrl_reg, val);
106}
107
108// submit ahci command + wait for result
109static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
110 void *buffer, u32 bsize)
111{
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200112 u32 val, status, success, flags, intbits, error;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100113 struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl);
114 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
115 struct ahci_fis_s *fis = GET_GLOBAL(port->fis);
116 struct ahci_list_s *list = GET_GLOBAL(port->list);
117 u32 pnr = GET_GLOBAL(port->pnr);
118
Kevin O'Connor890c0852012-05-24 23:55:00 -0400119 SET_LOWFLAT(cmd->fis.reg, 0x27);
120 SET_LOWFLAT(cmd->fis.pmp_type, (1 << 7)); /* cmd fis */
121 SET_LOWFLAT(cmd->prdt[0].base, ((u32)buffer));
122 SET_LOWFLAT(cmd->prdt[0].baseu, 0);
123 SET_LOWFLAT(cmd->prdt[0].flags, bsize-1);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100124
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100125 flags = ((1 << 16) | /* one prd entry */
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100126 (iswrite ? (1 << 6) : 0) |
127 (isatapi ? (1 << 5) : 0) |
Gerd Hoffmanna8c6a4e2011-07-14 16:23:59 +0200128 (5 << 0)); /* fis length (dwords) */
Kevin O'Connor890c0852012-05-24 23:55:00 -0400129 SET_LOWFLAT(list[0].flags, flags);
130 SET_LOWFLAT(list[0].bytes, 0);
131 SET_LOWFLAT(list[0].base, ((u32)(cmd)));
132 SET_LOWFLAT(list[0].baseu, 0);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100133
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400134 dprintf(8, "AHCI/%d: send cmd ...\n", pnr);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200135 intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
136 if (intbits)
137 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100138 ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
139 ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200140
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400141 u32 end = timer_calc(AHCI_REQUEST_TIMEOUT);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200142 do {
143 for (;;) {
144 intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
145 if (intbits) {
146 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
147 if (intbits & 0x02) {
Kevin O'Connor890c0852012-05-24 23:55:00 -0400148 status = GET_LOWFLAT(fis->psfis[2]);
149 error = GET_LOWFLAT(fis->psfis[3]);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200150 break;
151 }
152 if (intbits & 0x01) {
Kevin O'Connor890c0852012-05-24 23:55:00 -0400153 status = GET_LOWFLAT(fis->rfis[2]);
154 error = GET_LOWFLAT(fis->rfis[3]);
Gerd Hoffmann07532972011-07-14 16:24:00 +0200155 break;
156 }
157 }
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400158 if (timer_check(end)) {
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200159 warn_timeout();
160 return -1;
161 }
Gerd Hoffmann07532972011-07-14 16:24:00 +0200162 yield();
163 }
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400164 dprintf(8, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n",
Gerd Hoffmann07532972011-07-14 16:24:00 +0200165 pnr, intbits, status);
166 } while (status & ATA_CB_STAT_BSY);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100167
168 success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
Gerd Hoffmanncbda7952011-07-14 16:24:03 +0200169 ATA_CB_STAT_ERR)) &&
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100170 ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY)));
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200171 if (success) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400172 dprintf(8, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr,
Gerd Hoffmann6f850492011-07-14 16:24:01 +0200173 status);
174 } else {
175 dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr,
176 status, error);
177
178 // non-queued error recovery (AHCI 1.3 section 6.2.2.1)
179 // Clears PxCMD.ST to 0 to reset the PxCI register
180 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
181 ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START);
182
183 // waits for PxCMD.CR to clear to 0
184 while (1) {
185 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
186 if ((val & PORT_CMD_LIST_ON) == 0)
187 break;
188 yield();
189 }
190
191 // Clears any error bits in PxSERR to enable capturing new errors
192 val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
193 ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
194
195 // Clears status bits in PxIS as appropriate
196 val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
197 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
198
199 // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue
200 // a COMRESET to the device to put it in an idle state
201 val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
202 if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) {
203 dprintf(2, "AHCI/%d: issue comreset\n", pnr);
204 val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL);
205 // set Device Detection Initialization (DET) to 1 for 1 ms for comreset
206 ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1);
207 mdelay (1);
208 ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val);
209 }
210
211 // Sets PxCMD.ST to 1 to enable issuing new commands
212 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
213 ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
214 }
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100215 return success ? 0 : -1;
216}
217
218#define CDROM_CDB_SIZE 12
219
220int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
221{
Kevin O'Connor80c2b6e2010-12-05 12:52:02 -0500222 if (! CONFIG_AHCI)
223 return 0;
224
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100225 struct ahci_port_s *port = container_of(
226 op->drive_g, struct ahci_port_s, drive);
227 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
228 u8 *atapi = cdbcmd;
229 int i, rc;
230
231 sata_prep_atapi(&cmd->fis, blocksize);
232 for (i = 0; i < CDROM_CDB_SIZE; i++) {
Kevin O'Connor890c0852012-05-24 23:55:00 -0400233 SET_LOWFLAT(cmd->atapi[i], atapi[i]);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100234 }
235 rc = ahci_command(port, 0, 1, op->buf_fl,
236 op->count * blocksize);
237 if (rc < 0)
238 return DISK_RET_EBADTRACK;
239 return DISK_RET_SUCCESS;
240}
241
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200242// read/write count blocks from a harddrive, op->buf_fl must be word aligned
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100243static int
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200244ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100245{
246 struct ahci_port_s *port = container_of(
247 op->drive_g, struct ahci_port_s, drive);
248 struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
249 int rc;
250
251 sata_prep_readwrite(&cmd->fis, op, iswrite);
252 rc = ahci_command(port, iswrite, 0, op->buf_fl,
253 op->count * DISK_SECTOR_SIZE);
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400254 dprintf(8, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n",
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100255 iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc);
256 if (rc < 0)
257 return DISK_RET_EBADTRACK;
258 return DISK_RET_SUCCESS;
259}
260
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200261// read/write count blocks from a harddrive.
262static int
263ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
264{
265 // if caller's buffer is word aligned, use it directly
266 if (((u32) op->buf_fl & 1) == 0)
267 return ahci_disk_readwrite_aligned(op, iswrite);
268
269 // Use a word aligned buffer for AHCI I/O
270 int rc;
271 struct disk_op_s localop = *op;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200272 u8 *alignedbuf_fl = GET_GLOBAL(bounce_buf_fl);
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200273 u8 *position = op->buf_fl;
274
275 localop.buf_fl = alignedbuf_fl;
276 localop.count = 1;
277
278 if (iswrite) {
279 u16 block;
280 for (block = 0; block < op->count; block++) {
281 memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE);
282 rc = ahci_disk_readwrite_aligned (&localop, 1);
283 if (rc)
284 return rc;
285 position += DISK_SECTOR_SIZE;
286 localop.lba++;
287 }
288 } else { // read
289 u16 block;
290 for (block = 0; block < op->count; block++) {
291 rc = ahci_disk_readwrite_aligned (&localop, 0);
292 if (rc)
293 return rc;
294 memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE);
295 position += DISK_SECTOR_SIZE;
296 localop.lba++;
297 }
298 }
299 return DISK_RET_SUCCESS;
300}
301
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100302// command demuxer
303int process_ahci_op(struct disk_op_s *op)
304{
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100305 if (!CONFIG_AHCI)
306 return 0;
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400307 switch (op->command) {
308 case CMD_READ:
309 return ahci_disk_readwrite(op, 0);
310 case CMD_WRITE:
311 return ahci_disk_readwrite(op, 1);
312 case CMD_FORMAT:
313 case CMD_RESET:
314 case CMD_ISREADY:
315 case CMD_VERIFY:
316 case CMD_SEEK:
317 return DISK_RET_SUCCESS;
318 default:
319 dprintf(1, "AHCI: unknown disk command %d\n", op->command);
320 op->count = 0;
321 return DISK_RET_EPARAM;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100322 }
323}
324
325/****************************************************************
326 * everything below is pure 32bit code
327 ****************************************************************/
328
329static void
330ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
331{
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200332 u32 val;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100333
334 /* disable FIS + CMD */
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400335 u32 end = timer_calc(AHCI_RESET_TIMEOUT);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200336 for (;;) {
337 val = ahci_port_readl(ctrl, pnr, PORT_CMD);
338 if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START |
339 PORT_CMD_FIS_ON | PORT_CMD_LIST_ON)))
340 break;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100341 val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START);
342 ahci_port_writel(ctrl, pnr, PORT_CMD, val);
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400343 if (timer_check(end)) {
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200344 warn_timeout();
345 break;
346 }
347 yield();
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100348 }
349
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100350 /* disable + clear IRQs */
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200351 ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, 0);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100352 val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
353 if (val)
354 ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
355}
356
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100357static struct ahci_port_s*
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200358ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100359{
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200360 struct ahci_port_s *port = malloc_tmp(sizeof(*port));
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100361
362 if (!port) {
363 warn_noalloc();
364 return NULL;
365 }
366 port->pnr = pnr;
367 port->ctrl = ctrl;
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200368 port->list = memalign_tmp(1024, 1024);
369 port->fis = memalign_tmp(256, 256);
370 port->cmd = memalign_tmp(256, 256);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100371 if (port->list == NULL || port->fis == NULL || port->cmd == NULL) {
372 warn_noalloc();
373 return NULL;
374 }
375 memset(port->list, 0, 1024);
376 memset(port->fis, 0, 256);
377 memset(port->cmd, 0, 256);
378
379 ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
380 ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200381 return port;
382}
383
Gerd Hoffmannce12eaf2013-09-09 16:33:06 +0200384static void ahci_port_release(struct ahci_port_s *port)
385{
386 ahci_port_reset(port->ctrl, port->pnr);
387 free(port->list);
388 free(port->fis);
389 free(port->cmd);
390 free(port);
391}
392
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200393static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port)
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200394{
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200395 struct ahci_port_s *tmp;
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200396 u32 cmd;
397
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200398 tmp = malloc_fseg(sizeof(*port));
Gerd Hoffmannce12eaf2013-09-09 16:33:06 +0200399 if (!tmp) {
400 warn_noalloc();
401 ahci_port_release(port);
402 return NULL;
403 }
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200404 *tmp = *port;
405 free(port);
406 port = tmp;
407
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200408 ahci_port_reset(port->ctrl, port->pnr);
409
410 free(port->list);
411 free(port->fis);
412 free(port->cmd);
413 port->list = memalign_low(1024, 1024);
414 port->fis = memalign_low(256, 256);
415 port->cmd = memalign_low(256, 256);
416
417 ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list);
418 ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis);
419
420 cmd = ahci_port_readl(port->ctrl, port->pnr, PORT_CMD);
421 cmd |= (PORT_CMD_FIS_RX|PORT_CMD_START);
422 ahci_port_writel(port->ctrl, port->pnr, PORT_CMD, cmd);
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200423
424 return port;
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200425}
426
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200427#define MAXMODEL 40
428
429/* See ahci spec chapter 10.1 "Software Initialization of HBA" */
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500430static int ahci_port_setup(struct ahci_port_s *port)
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200431{
432 struct ahci_ctrl_s *ctrl = port->ctrl;
433 u32 pnr = port->pnr;
434 char model[MAXMODEL+1];
435 u16 buffer[256];
436 u32 cmd, stat, err, tf;
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200437 int rc;
438
439 /* enable FIS recv */
440 cmd = ahci_port_readl(ctrl, pnr, PORT_CMD);
441 cmd |= PORT_CMD_FIS_RX;
442 ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
443
444 /* spin up */
445 cmd |= PORT_CMD_SPIN_UP;
446 ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400447 u32 end = timer_calc(AHCI_LINK_TIMEOUT);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200448 for (;;) {
449 stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
450 if ((stat & 0x07) == 0x03) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400451 dprintf(2, "AHCI/%d: link up\n", port->pnr);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200452 break;
453 }
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400454 if (timer_check(end)) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400455 dprintf(2, "AHCI/%d: link down\n", port->pnr);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200456 return -1;
457 }
458 yield();
459 }
460
461 /* clear error status */
462 err = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
463 if (err)
464 ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err);
465
466 /* wait for device becoming ready */
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400467 end = timer_calc(AHCI_REQUEST_TIMEOUT);
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200468 for (;;) {
469 tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
470 if (!(tf & (ATA_CB_STAT_BSY |
471 ATA_CB_STAT_DRQ)))
472 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400473 if (timer_check(end)) {
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200474 warn_timeout();
475 dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf);
476 return -1;
477 }
478 yield();
479 }
480
481 /* start device */
482 cmd |= PORT_CMD_START;
483 ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100484
485 sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
486 rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
487 if (rc == 0) {
488 port->atapi = 1;
489 } else {
490 port->atapi = 0;
491 sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE);
492 rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
493 if (rc < 0)
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200494 return -1;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100495 }
496
Gerd Hoffmann0e6f6362010-12-09 08:39:48 +0100497 port->drive.cntl_id = pnr;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100498 port->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100499
500 if (!port->atapi) {
501 // found disk (ata)
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400502 port->drive.type = DTYPE_AHCI;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100503 port->drive.blksize = DISK_SECTOR_SIZE;
504 port->drive.pchs.cylinders = buffer[1];
505 port->drive.pchs.heads = buffer[3];
506 port->drive.pchs.spt = buffer[6];
507
508 u64 sectors;
509 if (buffer[83] & (1 << 10)) // word 83 - lba48 support
510 sectors = *(u64*)&buffer[100]; // word 100-103
511 else
512 sectors = *(u32*)&buffer[60]; // word 60 and word 61
513 port->drive.sectors = sectors;
514 u64 adjsize = sectors >> 11;
515 char adjprefix = 'M';
516 if (adjsize >= (1 << 16)) {
517 adjsize >>= 10;
518 adjprefix = 'G';
519 }
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200520 port->desc = znprintf(MAXDESCSIZE
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500521 , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
522 , port->pnr
523 , ata_extract_model(model, MAXMODEL, buffer)
524 , ata_extract_version(buffer)
525 , (u32)adjsize, adjprefix);
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200526 port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100527 } else {
528 // found cdrom (atapi)
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400529 port->drive.type = DTYPE_AHCI_ATAPI;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100530 port->drive.blksize = CDROM_SECTOR_SIZE;
531 port->drive.sectors = (u64)-1;
532 u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
Gerd Hoffmann263ea2f2011-08-04 19:36:29 +0200533 if (!iscd) {
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400534 dprintf(1, "AHCI/%d: atapi device isn't a cdrom\n", port->pnr);
Gerd Hoffmann263ea2f2011-08-04 19:36:29 +0200535 return -1;
536 }
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200537 port->desc = znprintf(MAXDESCSIZE
Gerd Hoffmann263ea2f2011-08-04 19:36:29 +0200538 , "DVD/CD [AHCI/%d: %s ATAPI-%d DVD/CD]"
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500539 , port->pnr
540 , ata_extract_model(model, MAXMODEL, buffer)
Gerd Hoffmann263ea2f2011-08-04 19:36:29 +0200541 , ata_extract_version(buffer));
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200542 port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100543 }
Gerd Hoffmanne1041192011-07-14 16:24:04 +0200544 return 0;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100545}
546
547// Detect any drives attached to a given controller.
548static void
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200549ahci_port_detect(void *data)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100550{
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200551 struct ahci_port_s *port = data;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100552 int rc;
553
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200554 dprintf(2, "AHCI/%d: probing\n", port->pnr);
555 ahci_port_reset(port->ctrl, port->pnr);
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500556 rc = ahci_port_setup(port);
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200557 if (rc < 0)
558 ahci_port_release(port);
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200559 else {
Gerd Hoffmannef8adc02011-08-04 19:36:31 +0200560 port = ahci_port_realloc(port);
Gerd Hoffmannce12eaf2013-09-09 16:33:06 +0200561 if (port == NULL)
562 return;
Gerd Hoffmann2dcbf7f2011-08-04 19:36:30 +0200563 dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc);
564 if (!port->atapi) {
565 // Register with bcv system.
566 boot_add_hd(&port->drive, port->desc, port->prio);
567 } else {
568 // fill cdidmap
569 boot_add_cd(&port->drive, port->desc, port->prio);
570 }
571 }
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100572}
573
574// Initialize an ata controller and detect its drives.
575static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500576ahci_controller_setup(struct pci_device *pci)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100577{
578 struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl));
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200579 struct ahci_port_s *port;
Gerd Hoffmann9c869922011-07-14 16:24:05 +0200580 u16 bdf = pci->bdf;
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200581 u32 val, pnr, max;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100582
583 if (!ctrl) {
584 warn_noalloc();
585 return;
586 }
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200587
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500588 if (create_bounce_buf() < 0) {
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200589 warn_noalloc();
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +0200590 free(ctrl);
Scott Duplichan9c48aab2011-07-14 16:24:02 +0200591 return;
592 }
593
Gerd Hoffmann9c869922011-07-14 16:24:05 +0200594 ctrl->pci_tmp = pci;
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100595 ctrl->pci_bdf = bdf;
596 ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5);
597 ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
598 dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n",
599 bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq);
600
Kevin O'Connor7eb02222010-12-12 14:01:47 -0500601 pci_config_maskw(bdf, PCI_COMMAND, 0,
602 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
603
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100604 val = ahci_ctrl_readl(ctrl, HOST_CTL);
605 ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN);
606
607 ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP);
608 ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL);
609 dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n",
610 ctrl->caps, ctrl->ports);
611
Gerd Hoffmann9713f242011-08-04 19:36:28 +0200612 max = ctrl->caps & 0x1f;
613 for (pnr = 0; pnr <= max; pnr++) {
614 if (!(ctrl->ports & (1 << pnr)))
615 continue;
616 port = ahci_port_alloc(ctrl, pnr);
617 if (port == NULL)
618 continue;
619 run_thread(ahci_port_detect, port);
620 }
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100621}
622
623// Locate and init ahci controllers.
624static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500625ahci_scan(void)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100626{
627 // Scan PCI bus for ATA adapters
Kevin O'Connor9cb49922011-06-20 22:22:42 -0400628 struct pci_device *pci;
629 foreachpci(pci) {
630 if (pci->class != PCI_CLASS_STORAGE_SATA)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100631 continue;
Kevin O'Connor9cb49922011-06-20 22:22:42 -0400632 if (pci->prog_if != 1 /* AHCI rev 1 */)
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100633 continue;
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500634 ahci_controller_setup(pci);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100635 }
636}
637
638void
639ahci_setup(void)
640{
641 ASSERT32FLAT();
642 if (!CONFIG_AHCI)
643 return;
644
645 dprintf(3, "init ahci\n");
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500646 ahci_scan();
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100647}