3 * Copyright (C) 2012 secunet Security Networks AG
4 * Copyright (C) 2013 Edward O'Callaghan <eocallaghan@alterapraxis.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <libpayload.h>
35 #include <storage/ata.h>
36 #include <storage/ahci.h>
38 #include "ahci_private.h"
41 static inline u32
_ahci_clear_status(volatile u32
*const reg
,
45 const u32 bits
= *reg
;
48 printf("ahci: %s: %s == 0x%08x\n", f
, r
, bits
);
52 #define ahci_clear_status(p, r) _ahci_clear_status(&(p)->r, #r, __func__)
54 static inline u32
_ahci_clear_status(volatile u32
*const reg
)
56 const u32 bits
= *reg
;
61 #define ahci_clear_status(p, r) _ahci_clear_status(&(p)->r)
64 /** Give a buffer with even address. */
65 static u8
*ahci_prdbuf_init(ahci_dev_t
*const dev
,
66 u8
*const user_buf
, const size_t len
,
69 if ((uintptr_t)user_buf
& 1) {
70 printf("ahci: Odd buffer pointer (%p).\n", user_buf
);
71 if (dev
->buf
) /* orphaned buffer */
72 free(dev
->buf
- *(dev
->buf
- 1));
73 dev
->buf
= malloc(len
+ 2);
76 dev
->user_buf
= user_buf
;
77 dev
->write_back
= !out
;
79 if ((uintptr_t)dev
->buf
& 1) {
88 memcpy(dev
->buf
, user_buf
, len
);
95 static void ahci_prdbuf_finalize(ahci_dev_t
*const dev
)
99 memcpy(dev
->user_buf
, dev
->buf
, dev
->buflen
);
100 free(dev
->buf
- *(dev
->buf
- 1));
103 dev
->user_buf
= NULL
;
108 int ahci_cmdengine_start(hba_port_t
*const port
)
110 /* CR has to be clear before starting the command engine.
111 This shouldn't take too long, but we should time out nevertheless. */
112 int timeout
= 1000; /* Time out after 1000 * 1us == 1ms. */
113 while ((port
->cmd_stat
& HBA_PxCMD_CR
) && timeout
--)
116 printf("ahci: Timeout during start of command engine.\n");
120 port
->cmd_stat
|= HBA_PxCMD_FRE
;
121 port
->cmd_stat
|= HBA_PxCMD_ST
;
125 int ahci_cmdengine_stop(hba_port_t
*const port
)
127 port
->cmd_stat
&= ~HBA_PxCMD_ST
;
129 /* Wait for the controller to clear CR.
130 This shouldn't take too long, but we should time out nevertheless. */
131 int timeout
= 1000; /* Time out after 1000 * 1us == 1ms. */
132 while ((port
->cmd_stat
& HBA_PxCMD_CR
) && timeout
--)
135 printf("ahci: Timeout during stopping of command engine.\n");
139 port
->cmd_stat
&= ~HBA_PxCMD_FRE
;
141 /* Wait for the controller to clear FR.
142 This shouldn't take too long, but we should time out nevertheless. */
143 timeout
= 1000; /* Time out after 1000 * 1us == 1ms. */
144 while ((port
->cmd_stat
& HBA_PxCMD_FR
) && timeout
--)
147 printf("ahci: Timeout during stopping of command engine.\n");
154 ssize_t
ahci_cmdslot_exec(ahci_dev_t
*const dev
)
156 const int slotnum
= 0; /* We always use the first slot. */
158 if (!(dev
->port
->cmd_stat
& HBA_PxCMD_CR
))
161 /* Trigger command execution. */
162 dev
->port
->cmd_issue
|= (1 << slotnum
);
164 /* Wait for the controller to finish command execution. */
165 int timeout
= 50000; /* Time out after 50000 * 100us == 5s. */
166 while ((dev
->port
->cmd_issue
& (1 << slotnum
)) &&
167 !(dev
->port
->intr_status
& HBA_PxIS_TFES
) &&
171 printf("ahci: Timeout during command execution.\n");
175 ahci_prdbuf_finalize(dev
);
177 const u32 intr_status
= ahci_clear_status(dev
->port
, intr_status
);
178 if (intr_status
& (HBA_PxIS_FATAL
| HBA_PxIS_PCS
)) {
179 ahci_error_recovery(dev
, intr_status
);
182 return dev
->cmdlist
[slotnum
].prd_bytes
;
186 size_t ahci_cmdslot_prepare(ahci_dev_t
*const dev
,
187 u8
*const user_buf
, size_t buf_len
,
190 const int slotnum
= 0; /* We always use the first slot. */
192 size_t read_count
= 0;
194 memset((void *)&dev
->cmdlist
[slotnum
],
195 '\0', sizeof(dev
->cmdlist
[slotnum
]));
196 memset((void *)dev
->cmdtable
,
197 '\0', sizeof(*dev
->cmdtable
));
198 dev
->cmdlist
[slotnum
].cmd
= CMD_CFL(FIS_H2D_FIS_LEN
);
199 dev
->cmdlist
[slotnum
].cmdtable_base
= virt_to_phys(dev
->cmdtable
);
206 prdt_len
= ((buf_len
- 1) >> BYTES_PER_PRD_SHIFT
) + 1;
207 const size_t max_prdt_len
= ARRAY_SIZE(dev
->cmdtable
->prdt
);
208 if (prdt_len
> max_prdt_len
) {
209 prdt_len
= max_prdt_len
;
210 buf_len
= prdt_len
<< BYTES_PER_PRD_SHIFT
;
213 dev
->cmdlist
[slotnum
].prdt_length
= prdt_len
;
214 read_count
= buf_len
;
216 buf
= ahci_prdbuf_init(dev
, user_buf
, buf_len
, out
);
219 for (i
= 0; i
< prdt_len
; ++i
) {
221 (buf_len
< BYTES_PER_PRD
)
222 ? buf_len
: BYTES_PER_PRD
;
223 dev
->cmdtable
->prdt
[i
].data_base
= virt_to_phys(buf
);
224 dev
->cmdtable
->prdt
[i
].flags
= PRD_TABLE_BYTES(bytes
);
233 int ahci_identify_device(ata_dev_t
*const ata_dev
, u8
*const buf
)
235 ahci_dev_t
*const dev
= (ahci_dev_t
*)ata_dev
;
237 ahci_cmdslot_prepare(dev
, buf
, 512, 0);
239 dev
->cmdtable
->fis
[0] = FIS_HOST_TO_DEVICE
;
240 dev
->cmdtable
->fis
[1] = FIS_H2D_CMD
;
241 dev
->cmdtable
->fis
[2] = ata_dev
->identify_cmd
;
243 if ((ahci_cmdslot_exec(dev
) < 0) || (dev
->cmdlist
->prd_bytes
!= 512))