blob: bcb0ce70bcde121aa86075a06cf226c15e513beb [file] [log] [blame]
Edward O'Callaghanefc58412014-01-23 08:30:42 +11001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2012 secunet Security Networks AG
5 * Copyright (C) 2013 Edward O'Callaghan <eocallaghan@alterapraxis.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdlib.h>
32#include <stdint.h>
33#include <string.h>
34#include <libpayload.h>
35#include <pci.h>
36#include <storage/ata.h>
37#include <storage/ahci.h>
38
39#include "ahci_private.h"
40
41
42#ifdef DEBUG_STATUS
43static inline u32 _ahci_clear_status(volatile u32 *const reg,
44 const char *const r,
45 const char *const f)
46{
47 const u32 bits = *reg;
48 if (bits)
49 *reg = bits;
50 printf("ahci: %s: %s == 0x%08x\n", f, r, bits);
51
52 return bits;
53}
54#define ahci_clear_status(p, r) _ahci_clear_status(&(p)->r, #r, __func__)
55#else
56static inline u32 _ahci_clear_status(volatile u32 *const reg)
57{
58 const u32 bits = *reg;
59 if (bits)
60 *reg = bits;
61 return bits;
62}
63#define ahci_clear_status(p, r) _ahci_clear_status(&(p)->r)
64#endif
65
66
67/** Give a buffer with even address. */
68static u8 *ahci_prdbuf_init(ahci_dev_t *const dev,
69 u8 *const user_buf, const size_t len,
70 const int out)
71{
72 if ((u32)user_buf & 1) {
73 printf("ahci: Odd buffer pointer (%p).\n", user_buf);
74 if (dev->buf) /* orphaned buffer */
75 free((void *)dev->buf - *(dev->buf - 1));
76 dev->buf = malloc(len + 2);
77 if (!dev->buf)
78 return NULL;
79 dev->user_buf = user_buf;
80 dev->write_back = !out;
81 dev->buflen = len;
82 if ((u32)dev->buf & 1) {
83 dev->buf[0] = 1;
84 dev->buf += 1;
85 } else {
86 dev->buf[0] = 1;
87 dev->buf[1] = 2;
88 dev->buf += 2;
89 }
90 if (out)
91 memcpy(dev->buf, user_buf, len);
92 return dev->buf;
93 } else {
94 return user_buf;
95 }
96}
97
98static void ahci_prdbuf_finalize(ahci_dev_t *const dev)
99{
100 if (dev->buf) {
101 if (dev->write_back)
102 memcpy(dev->user_buf, dev->buf, dev->buflen);
103 free((void *)dev->buf - *(dev->buf - 1));
104 }
105 dev->buf = NULL;
106 dev->user_buf = NULL;
107 dev->write_back = 0;
108 dev->buflen = 0;
109}
110
111int ahci_cmdengine_start(hba_port_t *const port)
112{
113 /* CR has to be clear before starting the command engine.
114 This shouldn't take too long, but we should time out nevertheless. */
115 int timeout = 1000; /* Time out after 1000 * 1us == 1ms. */
116 while ((port->cmd_stat & HBA_PxCMD_CR) && timeout--)
117 udelay(1);
118 if (timeout < 0) {
119 printf("ahci: Timeout during start of command engine.\n");
120 return 1;
121 }
122
123 port->cmd_stat |= HBA_PxCMD_FRE;
124 port->cmd_stat |= HBA_PxCMD_ST;
125 return 0;
126}
127
128int ahci_cmdengine_stop(hba_port_t *const port)
129{
130 port->cmd_stat &= ~HBA_PxCMD_ST;
131
132 /* Wait for the controller to clear CR.
133 This shouldn't take too long, but we should time out nevertheless. */
134 int timeout = 1000; /* Time out after 1000 * 1us == 1ms. */
135 while ((port->cmd_stat & HBA_PxCMD_CR) && timeout--)
136 udelay(1);
137 if (timeout < 0) {
138 printf("ahci: Timeout during stopping of command engine.\n");
139 return 1;
140 }
141
142 port->cmd_stat &= ~HBA_PxCMD_FRE;
143
144 /* Wait for the controller to clear FR.
145 This shouldn't take too long, but we should time out nevertheless. */
146 timeout = 1000; /* Time out after 1000 * 1us == 1ms. */
147 while ((port->cmd_stat & HBA_PxCMD_FR) && timeout--)
148 udelay(1);
149 if (timeout < 0) {
150 printf("ahci: Timeout during stopping of command engine.\n");
151 return 1;
152 }
153
154 return 0;
155}
156
157ssize_t ahci_cmdslot_exec(ahci_dev_t *const dev)
158{
159 const int slotnum = 0; /* We always use the first slot. */
160
161 if (!(dev->port->cmd_stat & HBA_PxCMD_CR))
162 return -1;
163
164 /* Trigger command execution. */
165 dev->port->cmd_issue |= (1 << slotnum);
166
167 /* Wait for the controller to finish command execution. */
168 int timeout = 50000; /* Time out after 50000 * 100us == 5s. */
169 while ((dev->port->cmd_issue & (1 << slotnum)) &&
170 !(dev->port->intr_status & HBA_PxIS_TFES) &&
171 timeout--)
172 udelay(100);
173 if (timeout < 0) {
174 printf("ahci: Timeout during command execution.\n");
175 return -1;
176 }
177
178 ahci_prdbuf_finalize(dev);
179
180 const u32 intr_status = ahci_clear_status(dev->port, intr_status);
181 if (intr_status & (HBA_PxIS_FATAL | HBA_PxIS_PCS)) {
182 ahci_error_recovery(dev, intr_status);
183 return -1;
184 } else {
185 return dev->cmdlist[slotnum].prd_bytes;
186 }
187}
188
189size_t ahci_cmdslot_prepare(ahci_dev_t *const dev,
190 u8 *const user_buf, size_t buf_len,
191 const int out)
192{
193 const int slotnum = 0; /* We always use the first slot. */
194
195 size_t read_count = 0;
196
197 memset((void *)&dev->cmdlist[slotnum],
198 '\0', sizeof(dev->cmdlist[slotnum]));
199 memset((void *)dev->cmdtable,
200 '\0', sizeof(*dev->cmdtable));
201 dev->cmdlist[slotnum].cmd = CMD_CFL(FIS_H2D_FIS_LEN);
202 dev->cmdlist[slotnum].cmdtable_base = virt_to_phys(dev->cmdtable);
203
204 if (buf_len > 0) {
205 size_t prdt_len;
206 u8 *buf;
207 int i;
208
209 prdt_len = ((buf_len - 1) >> BYTES_PER_PRD_SHIFT) + 1;
210 const size_t max_prdt_len = ARRAY_SIZE(dev->cmdtable->prdt);
211 if (prdt_len > max_prdt_len) {
212 prdt_len = max_prdt_len;
213 buf_len = prdt_len << BYTES_PER_PRD_SHIFT;
214 }
215
216 dev->cmdlist[slotnum].prdt_length = prdt_len;
217 read_count = buf_len;
218
219 buf = ahci_prdbuf_init(dev, user_buf, buf_len, out);
220 if (!buf)
221 return 0;
222 for (i = 0; i < prdt_len; ++i) {
223 const size_t bytes =
224 (buf_len < BYTES_PER_PRD)
225 ? buf_len : BYTES_PER_PRD;
226 dev->cmdtable->prdt[i].data_base = virt_to_phys(buf);
227 dev->cmdtable->prdt[i].flags = PRD_TABLE_BYTES(bytes);
228 buf_len -= bytes;
229 buf += bytes;
230 }
231 }
232
233 return read_count;
234}
235
236int ahci_identify_device(ata_dev_t *const ata_dev, u8 *const buf)
237{
238 ahci_dev_t *const dev = (ahci_dev_t *)ata_dev;
239
240 ahci_cmdslot_prepare(dev, buf, 512, 0);
241
242 dev->cmdtable->fis[0] = FIS_HOST_TO_DEVICE;
243 dev->cmdtable->fis[1] = FIS_H2D_CMD;
244 dev->cmdtable->fis[2] = ata_dev->identify_cmd;
245
246 if ((ahci_cmdslot_exec(dev) < 0) || (dev->cmdlist->prd_bytes != 512))
247 return -1;
248 else
249 return 0;
250}