root / hw / ide / ahci.c @ 1fddfba1
History | View | Annotate | Download (32.6 kB)
1 | f6ad2e32 | Alexander Graf | /*
|
---|---|---|---|
2 | f6ad2e32 | Alexander Graf | * QEMU AHCI Emulation
|
3 | f6ad2e32 | Alexander Graf | *
|
4 | f6ad2e32 | Alexander Graf | * Copyright (c) 2010 qiaochong@loongson.cn
|
5 | f6ad2e32 | Alexander Graf | * Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
|
6 | f6ad2e32 | Alexander Graf | * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
|
7 | f6ad2e32 | Alexander Graf | * Copyright (c) 2010 Alexander Graf <agraf@suse.de>
|
8 | f6ad2e32 | Alexander Graf | *
|
9 | f6ad2e32 | Alexander Graf | * This library is free software; you can redistribute it and/or
|
10 | f6ad2e32 | Alexander Graf | * modify it under the terms of the GNU Lesser General Public
|
11 | f6ad2e32 | Alexander Graf | * License as published by the Free Software Foundation; either
|
12 | f6ad2e32 | Alexander Graf | * version 2 of the License, or (at your option) any later version.
|
13 | f6ad2e32 | Alexander Graf | *
|
14 | f6ad2e32 | Alexander Graf | * This library is distributed in the hope that it will be useful,
|
15 | f6ad2e32 | Alexander Graf | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16 | f6ad2e32 | Alexander Graf | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
17 | f6ad2e32 | Alexander Graf | * Lesser General Public License for more details.
|
18 | f6ad2e32 | Alexander Graf | *
|
19 | f6ad2e32 | Alexander Graf | * You should have received a copy of the GNU Lesser General Public
|
20 | f6ad2e32 | Alexander Graf | * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
21 | f6ad2e32 | Alexander Graf | *
|
22 | f6ad2e32 | Alexander Graf | */
|
23 | f6ad2e32 | Alexander Graf | |
24 | f6ad2e32 | Alexander Graf | #include <hw/hw.h> |
25 | f6ad2e32 | Alexander Graf | #include <hw/msi.h> |
26 | f6ad2e32 | Alexander Graf | #include <hw/pc.h> |
27 | f6ad2e32 | Alexander Graf | #include <hw/pci.h> |
28 | f6ad2e32 | Alexander Graf | |
29 | f6ad2e32 | Alexander Graf | #include "monitor.h" |
30 | f6ad2e32 | Alexander Graf | #include "dma.h" |
31 | f6ad2e32 | Alexander Graf | #include "cpu-common.h" |
32 | f6ad2e32 | Alexander Graf | #include "internal.h" |
33 | f6ad2e32 | Alexander Graf | #include <hw/ide/pci.h> |
34 | 03c7a6a8 | Sebastian Herbszt | #include <hw/ide/ahci.h> |
35 | f6ad2e32 | Alexander Graf | |
36 | f6ad2e32 | Alexander Graf | /* #define DEBUG_AHCI */
|
37 | f6ad2e32 | Alexander Graf | |
38 | f6ad2e32 | Alexander Graf | #ifdef DEBUG_AHCI
|
39 | f6ad2e32 | Alexander Graf | #define DPRINTF(port, fmt, ...) \
|
40 | f6ad2e32 | Alexander Graf | do { fprintf(stderr, "ahci: %s: [%d] ", __FUNCTION__, port); \ |
41 | f6ad2e32 | Alexander Graf | fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) |
42 | f6ad2e32 | Alexander Graf | #else
|
43 | f6ad2e32 | Alexander Graf | #define DPRINTF(port, fmt, ...) do {} while(0) |
44 | f6ad2e32 | Alexander Graf | #endif
|
45 | f6ad2e32 | Alexander Graf | |
46 | f6ad2e32 | Alexander Graf | static void check_cmd(AHCIState *s, int port); |
47 | f6ad2e32 | Alexander Graf | static int handle_cmd(AHCIState *s,int port,int slot); |
48 | f6ad2e32 | Alexander Graf | static void ahci_reset_port(AHCIState *s, int port); |
49 | f6ad2e32 | Alexander Graf | static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); |
50 | 87e62065 | Alexander Graf | static void ahci_init_d2h(AHCIDevice *ad); |
51 | f6ad2e32 | Alexander Graf | |
52 | f6ad2e32 | Alexander Graf | static uint32_t ahci_port_read(AHCIState *s, int port, int offset) |
53 | f6ad2e32 | Alexander Graf | { |
54 | f6ad2e32 | Alexander Graf | uint32_t val; |
55 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr; |
56 | f6ad2e32 | Alexander Graf | pr = &s->dev[port].port_regs; |
57 | f6ad2e32 | Alexander Graf | |
58 | f6ad2e32 | Alexander Graf | switch (offset) {
|
59 | f6ad2e32 | Alexander Graf | case PORT_LST_ADDR:
|
60 | f6ad2e32 | Alexander Graf | val = pr->lst_addr; |
61 | f6ad2e32 | Alexander Graf | break;
|
62 | f6ad2e32 | Alexander Graf | case PORT_LST_ADDR_HI:
|
63 | f6ad2e32 | Alexander Graf | val = pr->lst_addr_hi; |
64 | f6ad2e32 | Alexander Graf | break;
|
65 | f6ad2e32 | Alexander Graf | case PORT_FIS_ADDR:
|
66 | f6ad2e32 | Alexander Graf | val = pr->fis_addr; |
67 | f6ad2e32 | Alexander Graf | break;
|
68 | f6ad2e32 | Alexander Graf | case PORT_FIS_ADDR_HI:
|
69 | f6ad2e32 | Alexander Graf | val = pr->fis_addr_hi; |
70 | f6ad2e32 | Alexander Graf | break;
|
71 | f6ad2e32 | Alexander Graf | case PORT_IRQ_STAT:
|
72 | f6ad2e32 | Alexander Graf | val = pr->irq_stat; |
73 | f6ad2e32 | Alexander Graf | break;
|
74 | f6ad2e32 | Alexander Graf | case PORT_IRQ_MASK:
|
75 | f6ad2e32 | Alexander Graf | val = pr->irq_mask; |
76 | f6ad2e32 | Alexander Graf | break;
|
77 | f6ad2e32 | Alexander Graf | case PORT_CMD:
|
78 | f6ad2e32 | Alexander Graf | val = pr->cmd; |
79 | f6ad2e32 | Alexander Graf | break;
|
80 | f6ad2e32 | Alexander Graf | case PORT_TFDATA:
|
81 | f6ad2e32 | Alexander Graf | val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) | |
82 | f6ad2e32 | Alexander Graf | s->dev[port].port.ifs[0].status;
|
83 | f6ad2e32 | Alexander Graf | break;
|
84 | f6ad2e32 | Alexander Graf | case PORT_SIG:
|
85 | f6ad2e32 | Alexander Graf | val = pr->sig; |
86 | f6ad2e32 | Alexander Graf | break;
|
87 | f6ad2e32 | Alexander Graf | case PORT_SCR_STAT:
|
88 | f6ad2e32 | Alexander Graf | if (s->dev[port].port.ifs[0].bs) { |
89 | f6ad2e32 | Alexander Graf | val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | |
90 | f6ad2e32 | Alexander Graf | SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; |
91 | f6ad2e32 | Alexander Graf | } else {
|
92 | f6ad2e32 | Alexander Graf | val = SATA_SCR_SSTATUS_DET_NODEV; |
93 | f6ad2e32 | Alexander Graf | } |
94 | f6ad2e32 | Alexander Graf | break;
|
95 | f6ad2e32 | Alexander Graf | case PORT_SCR_CTL:
|
96 | f6ad2e32 | Alexander Graf | val = pr->scr_ctl; |
97 | f6ad2e32 | Alexander Graf | break;
|
98 | f6ad2e32 | Alexander Graf | case PORT_SCR_ERR:
|
99 | f6ad2e32 | Alexander Graf | val = pr->scr_err; |
100 | f6ad2e32 | Alexander Graf | break;
|
101 | f6ad2e32 | Alexander Graf | case PORT_SCR_ACT:
|
102 | f6ad2e32 | Alexander Graf | pr->scr_act &= ~s->dev[port].finished; |
103 | f6ad2e32 | Alexander Graf | s->dev[port].finished = 0;
|
104 | f6ad2e32 | Alexander Graf | val = pr->scr_act; |
105 | f6ad2e32 | Alexander Graf | break;
|
106 | f6ad2e32 | Alexander Graf | case PORT_CMD_ISSUE:
|
107 | f6ad2e32 | Alexander Graf | val = pr->cmd_issue; |
108 | f6ad2e32 | Alexander Graf | break;
|
109 | f6ad2e32 | Alexander Graf | case PORT_RESERVED:
|
110 | f6ad2e32 | Alexander Graf | default:
|
111 | f6ad2e32 | Alexander Graf | val = 0;
|
112 | f6ad2e32 | Alexander Graf | } |
113 | f6ad2e32 | Alexander Graf | DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val);
|
114 | f6ad2e32 | Alexander Graf | return val;
|
115 | f6ad2e32 | Alexander Graf | |
116 | f6ad2e32 | Alexander Graf | } |
117 | f6ad2e32 | Alexander Graf | |
118 | f6ad2e32 | Alexander Graf | static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) |
119 | f6ad2e32 | Alexander Graf | { |
120 | f6ad2e32 | Alexander Graf | struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
|
121 | f6ad2e32 | Alexander Graf | |
122 | f6ad2e32 | Alexander Graf | DPRINTF(0, "raise irq\n"); |
123 | f6ad2e32 | Alexander Graf | |
124 | f6ad2e32 | Alexander Graf | if (msi_enabled(&d->card)) {
|
125 | f6ad2e32 | Alexander Graf | msi_notify(&d->card, 0);
|
126 | f6ad2e32 | Alexander Graf | } else {
|
127 | f6ad2e32 | Alexander Graf | qemu_irq_raise(s->irq); |
128 | f6ad2e32 | Alexander Graf | } |
129 | f6ad2e32 | Alexander Graf | } |
130 | f6ad2e32 | Alexander Graf | |
131 | f6ad2e32 | Alexander Graf | static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) |
132 | f6ad2e32 | Alexander Graf | { |
133 | f6ad2e32 | Alexander Graf | struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
|
134 | f6ad2e32 | Alexander Graf | |
135 | f6ad2e32 | Alexander Graf | DPRINTF(0, "lower irq\n"); |
136 | f6ad2e32 | Alexander Graf | |
137 | f6ad2e32 | Alexander Graf | if (!msi_enabled(&d->card)) {
|
138 | f6ad2e32 | Alexander Graf | qemu_irq_lower(s->irq); |
139 | f6ad2e32 | Alexander Graf | } |
140 | f6ad2e32 | Alexander Graf | } |
141 | f6ad2e32 | Alexander Graf | |
142 | f6ad2e32 | Alexander Graf | static void ahci_check_irq(AHCIState *s) |
143 | f6ad2e32 | Alexander Graf | { |
144 | f6ad2e32 | Alexander Graf | int i;
|
145 | f6ad2e32 | Alexander Graf | |
146 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus); |
147 | f6ad2e32 | Alexander Graf | |
148 | 2c4b9d0e | Alexander Graf | for (i = 0; i < s->ports; i++) { |
149 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &s->dev[i].port_regs; |
150 | f6ad2e32 | Alexander Graf | if (pr->irq_stat & pr->irq_mask) {
|
151 | f6ad2e32 | Alexander Graf | s->control_regs.irqstatus |= (1 << i);
|
152 | f6ad2e32 | Alexander Graf | } |
153 | f6ad2e32 | Alexander Graf | } |
154 | f6ad2e32 | Alexander Graf | |
155 | f6ad2e32 | Alexander Graf | if (s->control_regs.irqstatus &&
|
156 | f6ad2e32 | Alexander Graf | (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { |
157 | f6ad2e32 | Alexander Graf | ahci_irq_raise(s, NULL);
|
158 | f6ad2e32 | Alexander Graf | } else {
|
159 | f6ad2e32 | Alexander Graf | ahci_irq_lower(s, NULL);
|
160 | f6ad2e32 | Alexander Graf | } |
161 | f6ad2e32 | Alexander Graf | } |
162 | f6ad2e32 | Alexander Graf | |
163 | f6ad2e32 | Alexander Graf | static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d, |
164 | f6ad2e32 | Alexander Graf | int irq_type)
|
165 | f6ad2e32 | Alexander Graf | { |
166 | f6ad2e32 | Alexander Graf | DPRINTF(d->port_no, "trigger irq %#x -> %x\n",
|
167 | f6ad2e32 | Alexander Graf | irq_type, d->port_regs.irq_mask & irq_type); |
168 | f6ad2e32 | Alexander Graf | |
169 | f6ad2e32 | Alexander Graf | d->port_regs.irq_stat |= irq_type; |
170 | f6ad2e32 | Alexander Graf | ahci_check_irq(s); |
171 | f6ad2e32 | Alexander Graf | } |
172 | f6ad2e32 | Alexander Graf | |
173 | f6ad2e32 | Alexander Graf | static void map_page(uint8_t **ptr, uint64_t addr, uint32_t wanted) |
174 | f6ad2e32 | Alexander Graf | { |
175 | f6ad2e32 | Alexander Graf | target_phys_addr_t len = wanted; |
176 | f6ad2e32 | Alexander Graf | |
177 | f6ad2e32 | Alexander Graf | if (*ptr) {
|
178 | fe6ceac8 | Stefan Hajnoczi | cpu_physical_memory_unmap(*ptr, len, 1, len);
|
179 | f6ad2e32 | Alexander Graf | } |
180 | f6ad2e32 | Alexander Graf | |
181 | f6ad2e32 | Alexander Graf | *ptr = cpu_physical_memory_map(addr, &len, 1);
|
182 | f6ad2e32 | Alexander Graf | if (len < wanted) {
|
183 | fe6ceac8 | Stefan Hajnoczi | cpu_physical_memory_unmap(*ptr, len, 1, len);
|
184 | f6ad2e32 | Alexander Graf | *ptr = NULL;
|
185 | f6ad2e32 | Alexander Graf | } |
186 | f6ad2e32 | Alexander Graf | } |
187 | f6ad2e32 | Alexander Graf | |
188 | f6ad2e32 | Alexander Graf | static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) |
189 | f6ad2e32 | Alexander Graf | { |
190 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &s->dev[port].port_regs; |
191 | f6ad2e32 | Alexander Graf | |
192 | f6ad2e32 | Alexander Graf | DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val);
|
193 | f6ad2e32 | Alexander Graf | switch (offset) {
|
194 | f6ad2e32 | Alexander Graf | case PORT_LST_ADDR:
|
195 | f6ad2e32 | Alexander Graf | pr->lst_addr = val; |
196 | f6ad2e32 | Alexander Graf | map_page(&s->dev[port].lst, |
197 | f6ad2e32 | Alexander Graf | ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); |
198 | f6ad2e32 | Alexander Graf | s->dev[port].cur_cmd = NULL;
|
199 | f6ad2e32 | Alexander Graf | break;
|
200 | f6ad2e32 | Alexander Graf | case PORT_LST_ADDR_HI:
|
201 | f6ad2e32 | Alexander Graf | pr->lst_addr_hi = val; |
202 | f6ad2e32 | Alexander Graf | map_page(&s->dev[port].lst, |
203 | f6ad2e32 | Alexander Graf | ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); |
204 | f6ad2e32 | Alexander Graf | s->dev[port].cur_cmd = NULL;
|
205 | f6ad2e32 | Alexander Graf | break;
|
206 | f6ad2e32 | Alexander Graf | case PORT_FIS_ADDR:
|
207 | f6ad2e32 | Alexander Graf | pr->fis_addr = val; |
208 | f6ad2e32 | Alexander Graf | map_page(&s->dev[port].res_fis, |
209 | f6ad2e32 | Alexander Graf | ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); |
210 | f6ad2e32 | Alexander Graf | break;
|
211 | f6ad2e32 | Alexander Graf | case PORT_FIS_ADDR_HI:
|
212 | f6ad2e32 | Alexander Graf | pr->fis_addr_hi = val; |
213 | f6ad2e32 | Alexander Graf | map_page(&s->dev[port].res_fis, |
214 | f6ad2e32 | Alexander Graf | ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); |
215 | f6ad2e32 | Alexander Graf | break;
|
216 | f6ad2e32 | Alexander Graf | case PORT_IRQ_STAT:
|
217 | f6ad2e32 | Alexander Graf | pr->irq_stat &= ~val; |
218 | f6ad2e32 | Alexander Graf | break;
|
219 | f6ad2e32 | Alexander Graf | case PORT_IRQ_MASK:
|
220 | f6ad2e32 | Alexander Graf | pr->irq_mask = val & 0xfdc000ff;
|
221 | f6ad2e32 | Alexander Graf | ahci_check_irq(s); |
222 | f6ad2e32 | Alexander Graf | break;
|
223 | f6ad2e32 | Alexander Graf | case PORT_CMD:
|
224 | f6ad2e32 | Alexander Graf | pr->cmd = val & ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); |
225 | f6ad2e32 | Alexander Graf | |
226 | f6ad2e32 | Alexander Graf | if (pr->cmd & PORT_CMD_START) {
|
227 | f6ad2e32 | Alexander Graf | pr->cmd |= PORT_CMD_LIST_ON; |
228 | f6ad2e32 | Alexander Graf | } |
229 | f6ad2e32 | Alexander Graf | |
230 | f6ad2e32 | Alexander Graf | if (pr->cmd & PORT_CMD_FIS_RX) {
|
231 | f6ad2e32 | Alexander Graf | pr->cmd |= PORT_CMD_FIS_ON; |
232 | f6ad2e32 | Alexander Graf | } |
233 | f6ad2e32 | Alexander Graf | |
234 | 87e62065 | Alexander Graf | /* XXX usually the FIS would be pending on the bus here and
|
235 | 87e62065 | Alexander Graf | issuing deferred until the OS enables FIS receival.
|
236 | 87e62065 | Alexander Graf | Instead, we only submit it once - which works in most
|
237 | 87e62065 | Alexander Graf | cases, but is a hack. */
|
238 | 87e62065 | Alexander Graf | if ((pr->cmd & PORT_CMD_FIS_ON) &&
|
239 | 87e62065 | Alexander Graf | !s->dev[port].init_d2h_sent) { |
240 | 87e62065 | Alexander Graf | ahci_init_d2h(&s->dev[port]); |
241 | 87e62065 | Alexander Graf | s->dev[port].init_d2h_sent = 1;
|
242 | 87e62065 | Alexander Graf | } |
243 | 87e62065 | Alexander Graf | |
244 | f6ad2e32 | Alexander Graf | check_cmd(s, port); |
245 | f6ad2e32 | Alexander Graf | break;
|
246 | f6ad2e32 | Alexander Graf | case PORT_TFDATA:
|
247 | f6ad2e32 | Alexander Graf | s->dev[port].port.ifs[0].error = (val >> 8) & 0xff; |
248 | f6ad2e32 | Alexander Graf | s->dev[port].port.ifs[0].status = val & 0xff; |
249 | f6ad2e32 | Alexander Graf | break;
|
250 | f6ad2e32 | Alexander Graf | case PORT_SIG:
|
251 | f6ad2e32 | Alexander Graf | pr->sig = val; |
252 | f6ad2e32 | Alexander Graf | break;
|
253 | f6ad2e32 | Alexander Graf | case PORT_SCR_STAT:
|
254 | f6ad2e32 | Alexander Graf | pr->scr_stat = val; |
255 | f6ad2e32 | Alexander Graf | break;
|
256 | f6ad2e32 | Alexander Graf | case PORT_SCR_CTL:
|
257 | f6ad2e32 | Alexander Graf | if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && |
258 | f6ad2e32 | Alexander Graf | ((val & AHCI_SCR_SCTL_DET) == 0)) {
|
259 | f6ad2e32 | Alexander Graf | ahci_reset_port(s, port); |
260 | f6ad2e32 | Alexander Graf | } |
261 | f6ad2e32 | Alexander Graf | pr->scr_ctl = val; |
262 | f6ad2e32 | Alexander Graf | break;
|
263 | f6ad2e32 | Alexander Graf | case PORT_SCR_ERR:
|
264 | f6ad2e32 | Alexander Graf | pr->scr_err &= ~val; |
265 | f6ad2e32 | Alexander Graf | break;
|
266 | f6ad2e32 | Alexander Graf | case PORT_SCR_ACT:
|
267 | f6ad2e32 | Alexander Graf | /* RW1 */
|
268 | f6ad2e32 | Alexander Graf | pr->scr_act |= val; |
269 | f6ad2e32 | Alexander Graf | break;
|
270 | f6ad2e32 | Alexander Graf | case PORT_CMD_ISSUE:
|
271 | f6ad2e32 | Alexander Graf | pr->cmd_issue |= val; |
272 | f6ad2e32 | Alexander Graf | check_cmd(s, port); |
273 | f6ad2e32 | Alexander Graf | break;
|
274 | f6ad2e32 | Alexander Graf | default:
|
275 | f6ad2e32 | Alexander Graf | break;
|
276 | f6ad2e32 | Alexander Graf | } |
277 | f6ad2e32 | Alexander Graf | } |
278 | f6ad2e32 | Alexander Graf | |
279 | f6ad2e32 | Alexander Graf | static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr) |
280 | f6ad2e32 | Alexander Graf | { |
281 | f6ad2e32 | Alexander Graf | AHCIState *s = ptr; |
282 | f6ad2e32 | Alexander Graf | uint32_t val = 0;
|
283 | f6ad2e32 | Alexander Graf | |
284 | f6ad2e32 | Alexander Graf | addr = addr & 0xfff;
|
285 | f6ad2e32 | Alexander Graf | if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
|
286 | f6ad2e32 | Alexander Graf | switch (addr) {
|
287 | f6ad2e32 | Alexander Graf | case HOST_CAP:
|
288 | f6ad2e32 | Alexander Graf | val = s->control_regs.cap; |
289 | f6ad2e32 | Alexander Graf | break;
|
290 | f6ad2e32 | Alexander Graf | case HOST_CTL:
|
291 | f6ad2e32 | Alexander Graf | val = s->control_regs.ghc; |
292 | f6ad2e32 | Alexander Graf | break;
|
293 | f6ad2e32 | Alexander Graf | case HOST_IRQ_STAT:
|
294 | f6ad2e32 | Alexander Graf | val = s->control_regs.irqstatus; |
295 | f6ad2e32 | Alexander Graf | break;
|
296 | f6ad2e32 | Alexander Graf | case HOST_PORTS_IMPL:
|
297 | f6ad2e32 | Alexander Graf | val = s->control_regs.impl; |
298 | f6ad2e32 | Alexander Graf | break;
|
299 | f6ad2e32 | Alexander Graf | case HOST_VERSION:
|
300 | f6ad2e32 | Alexander Graf | val = s->control_regs.version; |
301 | f6ad2e32 | Alexander Graf | break;
|
302 | f6ad2e32 | Alexander Graf | } |
303 | f6ad2e32 | Alexander Graf | |
304 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); |
305 | f6ad2e32 | Alexander Graf | } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && |
306 | 2c4b9d0e | Alexander Graf | (addr < (AHCI_PORT_REGS_START_ADDR + |
307 | 2c4b9d0e | Alexander Graf | (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { |
308 | f6ad2e32 | Alexander Graf | val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
309 | f6ad2e32 | Alexander Graf | addr & AHCI_PORT_ADDR_OFFSET_MASK); |
310 | f6ad2e32 | Alexander Graf | } |
311 | f6ad2e32 | Alexander Graf | |
312 | f6ad2e32 | Alexander Graf | return val;
|
313 | f6ad2e32 | Alexander Graf | } |
314 | f6ad2e32 | Alexander Graf | |
315 | f6ad2e32 | Alexander Graf | |
316 | f6ad2e32 | Alexander Graf | |
317 | f6ad2e32 | Alexander Graf | static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) |
318 | f6ad2e32 | Alexander Graf | { |
319 | f6ad2e32 | Alexander Graf | AHCIState *s = ptr; |
320 | f6ad2e32 | Alexander Graf | addr = addr & 0xfff;
|
321 | f6ad2e32 | Alexander Graf | |
322 | f6ad2e32 | Alexander Graf | /* Only aligned reads are allowed on AHCI */
|
323 | f6ad2e32 | Alexander Graf | if (addr & 3) { |
324 | f6ad2e32 | Alexander Graf | fprintf(stderr, "ahci: Mis-aligned write to addr 0x"
|
325 | f6ad2e32 | Alexander Graf | TARGET_FMT_plx "\n", addr);
|
326 | f6ad2e32 | Alexander Graf | return;
|
327 | f6ad2e32 | Alexander Graf | } |
328 | f6ad2e32 | Alexander Graf | |
329 | f6ad2e32 | Alexander Graf | if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
|
330 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); |
331 | f6ad2e32 | Alexander Graf | |
332 | f6ad2e32 | Alexander Graf | switch (addr) {
|
333 | f6ad2e32 | Alexander Graf | case HOST_CAP: /* R/WO, RO */ |
334 | f6ad2e32 | Alexander Graf | /* FIXME handle R/WO */
|
335 | f6ad2e32 | Alexander Graf | break;
|
336 | f6ad2e32 | Alexander Graf | case HOST_CTL: /* R/W */ |
337 | f6ad2e32 | Alexander Graf | if (val & HOST_CTL_RESET) {
|
338 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "HBA Reset\n"); |
339 | 760c3e44 | Alexander Graf | ahci_reset(container_of(s, AHCIPCIState, ahci)); |
340 | f6ad2e32 | Alexander Graf | } else {
|
341 | f6ad2e32 | Alexander Graf | s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
|
342 | f6ad2e32 | Alexander Graf | ahci_check_irq(s); |
343 | f6ad2e32 | Alexander Graf | } |
344 | f6ad2e32 | Alexander Graf | break;
|
345 | f6ad2e32 | Alexander Graf | case HOST_IRQ_STAT: /* R/WC, RO */ |
346 | f6ad2e32 | Alexander Graf | s->control_regs.irqstatus &= ~val; |
347 | f6ad2e32 | Alexander Graf | ahci_check_irq(s); |
348 | f6ad2e32 | Alexander Graf | break;
|
349 | f6ad2e32 | Alexander Graf | case HOST_PORTS_IMPL: /* R/WO, RO */ |
350 | f6ad2e32 | Alexander Graf | /* FIXME handle R/WO */
|
351 | f6ad2e32 | Alexander Graf | break;
|
352 | f6ad2e32 | Alexander Graf | case HOST_VERSION: /* RO */ |
353 | f6ad2e32 | Alexander Graf | /* FIXME report write? */
|
354 | f6ad2e32 | Alexander Graf | break;
|
355 | f6ad2e32 | Alexander Graf | default:
|
356 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr); |
357 | f6ad2e32 | Alexander Graf | } |
358 | f6ad2e32 | Alexander Graf | } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && |
359 | 2c4b9d0e | Alexander Graf | (addr < (AHCI_PORT_REGS_START_ADDR + |
360 | 2c4b9d0e | Alexander Graf | (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { |
361 | f6ad2e32 | Alexander Graf | ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
362 | f6ad2e32 | Alexander Graf | addr & AHCI_PORT_ADDR_OFFSET_MASK, val); |
363 | f6ad2e32 | Alexander Graf | } |
364 | f6ad2e32 | Alexander Graf | |
365 | f6ad2e32 | Alexander Graf | } |
366 | f6ad2e32 | Alexander Graf | |
367 | f6ad2e32 | Alexander Graf | static CPUReadMemoryFunc * const ahci_readfn[3]={ |
368 | f6ad2e32 | Alexander Graf | ahci_mem_readl, |
369 | f6ad2e32 | Alexander Graf | ahci_mem_readl, |
370 | f6ad2e32 | Alexander Graf | ahci_mem_readl |
371 | f6ad2e32 | Alexander Graf | }; |
372 | f6ad2e32 | Alexander Graf | |
373 | f6ad2e32 | Alexander Graf | static CPUWriteMemoryFunc * const ahci_writefn[3]={ |
374 | f6ad2e32 | Alexander Graf | ahci_mem_writel, |
375 | f6ad2e32 | Alexander Graf | ahci_mem_writel, |
376 | f6ad2e32 | Alexander Graf | ahci_mem_writel |
377 | f6ad2e32 | Alexander Graf | }; |
378 | f6ad2e32 | Alexander Graf | |
379 | f6ad2e32 | Alexander Graf | static void ahci_reg_init(AHCIState *s) |
380 | f6ad2e32 | Alexander Graf | { |
381 | f6ad2e32 | Alexander Graf | int i;
|
382 | f6ad2e32 | Alexander Graf | |
383 | 2c4b9d0e | Alexander Graf | s->control_regs.cap = (s->ports - 1) |
|
384 | f6ad2e32 | Alexander Graf | (AHCI_NUM_COMMAND_SLOTS << 8) |
|
385 | f6ad2e32 | Alexander Graf | (AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) | |
386 | f6ad2e32 | Alexander Graf | HOST_CAP_NCQ | HOST_CAP_AHCI; |
387 | f6ad2e32 | Alexander Graf | |
388 | 2c4b9d0e | Alexander Graf | s->control_regs.impl = (1 << s->ports) - 1; |
389 | f6ad2e32 | Alexander Graf | |
390 | f6ad2e32 | Alexander Graf | s->control_regs.version = AHCI_VERSION_1_0; |
391 | f6ad2e32 | Alexander Graf | |
392 | 2c4b9d0e | Alexander Graf | for (i = 0; i < s->ports; i++) { |
393 | f6ad2e32 | Alexander Graf | s->dev[i].port_state = STATE_RUN; |
394 | f6ad2e32 | Alexander Graf | } |
395 | f6ad2e32 | Alexander Graf | } |
396 | f6ad2e32 | Alexander Graf | |
397 | f6ad2e32 | Alexander Graf | static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len,
|
398 | f6ad2e32 | Alexander Graf | QEMUSGList *sglist) |
399 | f6ad2e32 | Alexander Graf | { |
400 | f6ad2e32 | Alexander Graf | uint32_t i = 0;
|
401 | f6ad2e32 | Alexander Graf | uint32_t total = 0, once;
|
402 | f6ad2e32 | Alexander Graf | ScatterGatherEntry *cur_prd; |
403 | f6ad2e32 | Alexander Graf | uint32_t sgcount; |
404 | f6ad2e32 | Alexander Graf | |
405 | f6ad2e32 | Alexander Graf | cur_prd = sglist->sg; |
406 | f6ad2e32 | Alexander Graf | sgcount = sglist->nsg; |
407 | f6ad2e32 | Alexander Graf | for (i = 0; len && sgcount; i++) { |
408 | f6ad2e32 | Alexander Graf | once = MIN(cur_prd->len, len); |
409 | f6ad2e32 | Alexander Graf | cpu_physical_memory_read(cur_prd->base, buffer, once); |
410 | f6ad2e32 | Alexander Graf | cur_prd++; |
411 | f6ad2e32 | Alexander Graf | sgcount--; |
412 | f6ad2e32 | Alexander Graf | len -= once; |
413 | f6ad2e32 | Alexander Graf | buffer += once; |
414 | f6ad2e32 | Alexander Graf | total += once; |
415 | f6ad2e32 | Alexander Graf | } |
416 | f6ad2e32 | Alexander Graf | |
417 | f6ad2e32 | Alexander Graf | return total;
|
418 | f6ad2e32 | Alexander Graf | } |
419 | f6ad2e32 | Alexander Graf | |
420 | f6ad2e32 | Alexander Graf | static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len,
|
421 | f6ad2e32 | Alexander Graf | QEMUSGList *sglist) |
422 | f6ad2e32 | Alexander Graf | { |
423 | f6ad2e32 | Alexander Graf | uint32_t i = 0;
|
424 | f6ad2e32 | Alexander Graf | uint32_t total = 0, once;
|
425 | f6ad2e32 | Alexander Graf | ScatterGatherEntry *cur_prd; |
426 | f6ad2e32 | Alexander Graf | uint32_t sgcount; |
427 | f6ad2e32 | Alexander Graf | |
428 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "total: 0x%x bytes\n", len); |
429 | f6ad2e32 | Alexander Graf | |
430 | f6ad2e32 | Alexander Graf | cur_prd = sglist->sg; |
431 | f6ad2e32 | Alexander Graf | sgcount = sglist->nsg; |
432 | f6ad2e32 | Alexander Graf | for (i = 0; len && sgcount; i++) { |
433 | f6ad2e32 | Alexander Graf | once = MIN(cur_prd->len, len); |
434 | f6ad2e32 | Alexander Graf | DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base); |
435 | f6ad2e32 | Alexander Graf | cpu_physical_memory_write(cur_prd->base, buffer, once); |
436 | f6ad2e32 | Alexander Graf | cur_prd++; |
437 | f6ad2e32 | Alexander Graf | sgcount--; |
438 | f6ad2e32 | Alexander Graf | len -= once; |
439 | f6ad2e32 | Alexander Graf | buffer += once; |
440 | f6ad2e32 | Alexander Graf | total += once; |
441 | f6ad2e32 | Alexander Graf | } |
442 | f6ad2e32 | Alexander Graf | |
443 | f6ad2e32 | Alexander Graf | return total;
|
444 | f6ad2e32 | Alexander Graf | } |
445 | f6ad2e32 | Alexander Graf | |
446 | f6ad2e32 | Alexander Graf | static void check_cmd(AHCIState *s, int port) |
447 | f6ad2e32 | Alexander Graf | { |
448 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &s->dev[port].port_regs; |
449 | f6ad2e32 | Alexander Graf | int slot;
|
450 | f6ad2e32 | Alexander Graf | |
451 | f6ad2e32 | Alexander Graf | if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
|
452 | f6ad2e32 | Alexander Graf | for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) { |
453 | f6ad2e32 | Alexander Graf | if ((pr->cmd_issue & (1 << slot)) && |
454 | f6ad2e32 | Alexander Graf | !handle_cmd(s, port, slot)) { |
455 | f6ad2e32 | Alexander Graf | pr->cmd_issue &= ~(1 << slot);
|
456 | f6ad2e32 | Alexander Graf | } |
457 | f6ad2e32 | Alexander Graf | } |
458 | f6ad2e32 | Alexander Graf | } |
459 | f6ad2e32 | Alexander Graf | } |
460 | f6ad2e32 | Alexander Graf | |
461 | f6ad2e32 | Alexander Graf | static void ahci_check_cmd_bh(void *opaque) |
462 | f6ad2e32 | Alexander Graf | { |
463 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = opaque; |
464 | f6ad2e32 | Alexander Graf | |
465 | f6ad2e32 | Alexander Graf | qemu_bh_delete(ad->check_bh); |
466 | f6ad2e32 | Alexander Graf | ad->check_bh = NULL;
|
467 | f6ad2e32 | Alexander Graf | |
468 | f6ad2e32 | Alexander Graf | if ((ad->busy_slot != -1) && |
469 | f6ad2e32 | Alexander Graf | !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
|
470 | f6ad2e32 | Alexander Graf | /* no longer busy */
|
471 | f6ad2e32 | Alexander Graf | ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
|
472 | f6ad2e32 | Alexander Graf | ad->busy_slot = -1;
|
473 | f6ad2e32 | Alexander Graf | } |
474 | f6ad2e32 | Alexander Graf | |
475 | f6ad2e32 | Alexander Graf | check_cmd(ad->hba, ad->port_no); |
476 | f6ad2e32 | Alexander Graf | } |
477 | f6ad2e32 | Alexander Graf | |
478 | 87e62065 | Alexander Graf | static void ahci_init_d2h(AHCIDevice *ad) |
479 | 87e62065 | Alexander Graf | { |
480 | 87e62065 | Alexander Graf | uint8_t init_fis[0x20];
|
481 | 87e62065 | Alexander Graf | IDEState *ide_state = &ad->port.ifs[0];
|
482 | 87e62065 | Alexander Graf | |
483 | 87e62065 | Alexander Graf | memset(init_fis, 0, sizeof(init_fis)); |
484 | 87e62065 | Alexander Graf | |
485 | 87e62065 | Alexander Graf | init_fis[4] = 1; |
486 | 87e62065 | Alexander Graf | init_fis[12] = 1; |
487 | 87e62065 | Alexander Graf | |
488 | 87e62065 | Alexander Graf | if (ide_state->drive_kind == IDE_CD) {
|
489 | 87e62065 | Alexander Graf | init_fis[5] = ide_state->lcyl;
|
490 | 87e62065 | Alexander Graf | init_fis[6] = ide_state->hcyl;
|
491 | 87e62065 | Alexander Graf | } |
492 | 87e62065 | Alexander Graf | |
493 | 87e62065 | Alexander Graf | ahci_write_fis_d2h(ad, init_fis); |
494 | 87e62065 | Alexander Graf | } |
495 | 87e62065 | Alexander Graf | |
496 | f6ad2e32 | Alexander Graf | static void ahci_reset_port(AHCIState *s, int port) |
497 | f6ad2e32 | Alexander Graf | { |
498 | f6ad2e32 | Alexander Graf | AHCIDevice *d = &s->dev[port]; |
499 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &d->port_regs; |
500 | f6ad2e32 | Alexander Graf | IDEState *ide_state = &d->port.ifs[0];
|
501 | f6ad2e32 | Alexander Graf | int i;
|
502 | f6ad2e32 | Alexander Graf | |
503 | f6ad2e32 | Alexander Graf | DPRINTF(port, "reset port\n");
|
504 | f6ad2e32 | Alexander Graf | |
505 | f6ad2e32 | Alexander Graf | ide_bus_reset(&d->port); |
506 | f6ad2e32 | Alexander Graf | ide_state->ncq_queues = AHCI_MAX_CMDS; |
507 | f6ad2e32 | Alexander Graf | |
508 | f6ad2e32 | Alexander Graf | pr->irq_stat = 0;
|
509 | f6ad2e32 | Alexander Graf | pr->irq_mask = 0;
|
510 | f6ad2e32 | Alexander Graf | pr->scr_stat = 0;
|
511 | f6ad2e32 | Alexander Graf | pr->scr_ctl = 0;
|
512 | f6ad2e32 | Alexander Graf | pr->scr_err = 0;
|
513 | f6ad2e32 | Alexander Graf | pr->scr_act = 0;
|
514 | f6ad2e32 | Alexander Graf | d->busy_slot = -1;
|
515 | 87e62065 | Alexander Graf | d->init_d2h_sent = 0;
|
516 | f6ad2e32 | Alexander Graf | |
517 | f6ad2e32 | Alexander Graf | ide_state = &s->dev[port].port.ifs[0];
|
518 | f6ad2e32 | Alexander Graf | if (!ide_state->bs) {
|
519 | f6ad2e32 | Alexander Graf | return;
|
520 | f6ad2e32 | Alexander Graf | } |
521 | f6ad2e32 | Alexander Graf | |
522 | f6ad2e32 | Alexander Graf | /* reset ncq queue */
|
523 | f6ad2e32 | Alexander Graf | for (i = 0; i < AHCI_MAX_CMDS; i++) { |
524 | f6ad2e32 | Alexander Graf | NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; |
525 | f6ad2e32 | Alexander Graf | if (!ncq_tfs->used) {
|
526 | f6ad2e32 | Alexander Graf | continue;
|
527 | f6ad2e32 | Alexander Graf | } |
528 | f6ad2e32 | Alexander Graf | |
529 | f6ad2e32 | Alexander Graf | if (ncq_tfs->aiocb) {
|
530 | f6ad2e32 | Alexander Graf | bdrv_aio_cancel(ncq_tfs->aiocb); |
531 | f6ad2e32 | Alexander Graf | ncq_tfs->aiocb = NULL;
|
532 | f6ad2e32 | Alexander Graf | } |
533 | f6ad2e32 | Alexander Graf | |
534 | f6ad2e32 | Alexander Graf | qemu_sglist_destroy(&ncq_tfs->sglist); |
535 | f6ad2e32 | Alexander Graf | ncq_tfs->used = 0;
|
536 | f6ad2e32 | Alexander Graf | } |
537 | f6ad2e32 | Alexander Graf | |
538 | f6ad2e32 | Alexander Graf | s->dev[port].port_state = STATE_RUN; |
539 | f6ad2e32 | Alexander Graf | if (!ide_state->bs) {
|
540 | f6ad2e32 | Alexander Graf | s->dev[port].port_regs.sig = 0;
|
541 | cdfe17df | Blue Swirl | ide_state->status = SEEK_STAT | WRERR_STAT; |
542 | f6ad2e32 | Alexander Graf | } else if (ide_state->drive_kind == IDE_CD) { |
543 | f6ad2e32 | Alexander Graf | s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM; |
544 | f6ad2e32 | Alexander Graf | ide_state->lcyl = 0x14;
|
545 | f6ad2e32 | Alexander Graf | ide_state->hcyl = 0xeb;
|
546 | f6ad2e32 | Alexander Graf | DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
|
547 | f6ad2e32 | Alexander Graf | ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; |
548 | f6ad2e32 | Alexander Graf | } else {
|
549 | f6ad2e32 | Alexander Graf | s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK; |
550 | f6ad2e32 | Alexander Graf | ide_state->status = SEEK_STAT | WRERR_STAT; |
551 | f6ad2e32 | Alexander Graf | } |
552 | f6ad2e32 | Alexander Graf | |
553 | f6ad2e32 | Alexander Graf | ide_state->error = 1;
|
554 | 87e62065 | Alexander Graf | ahci_init_d2h(d); |
555 | f6ad2e32 | Alexander Graf | } |
556 | f6ad2e32 | Alexander Graf | |
557 | f6ad2e32 | Alexander Graf | static void debug_print_fis(uint8_t *fis, int cmd_len) |
558 | f6ad2e32 | Alexander Graf | { |
559 | f6ad2e32 | Alexander Graf | #ifdef DEBUG_AHCI
|
560 | f6ad2e32 | Alexander Graf | int i;
|
561 | f6ad2e32 | Alexander Graf | |
562 | f6ad2e32 | Alexander Graf | fprintf(stderr, "fis:");
|
563 | f6ad2e32 | Alexander Graf | for (i = 0; i < cmd_len; i++) { |
564 | f6ad2e32 | Alexander Graf | if ((i & 0xf) == 0) { |
565 | f6ad2e32 | Alexander Graf | fprintf(stderr, "\n%02x:",i);
|
566 | f6ad2e32 | Alexander Graf | } |
567 | f6ad2e32 | Alexander Graf | fprintf(stderr, "%02x ",fis[i]);
|
568 | f6ad2e32 | Alexander Graf | } |
569 | f6ad2e32 | Alexander Graf | fprintf(stderr, "\n");
|
570 | f6ad2e32 | Alexander Graf | #endif
|
571 | f6ad2e32 | Alexander Graf | } |
572 | f6ad2e32 | Alexander Graf | |
573 | f6ad2e32 | Alexander Graf | static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) |
574 | f6ad2e32 | Alexander Graf | { |
575 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &s->dev[port].port_regs; |
576 | f6ad2e32 | Alexander Graf | IDEState *ide_state; |
577 | f6ad2e32 | Alexander Graf | uint8_t *sdb_fis; |
578 | f6ad2e32 | Alexander Graf | |
579 | f6ad2e32 | Alexander Graf | if (!s->dev[port].res_fis ||
|
580 | f6ad2e32 | Alexander Graf | !(pr->cmd & PORT_CMD_FIS_RX)) { |
581 | f6ad2e32 | Alexander Graf | return;
|
582 | f6ad2e32 | Alexander Graf | } |
583 | f6ad2e32 | Alexander Graf | |
584 | f6ad2e32 | Alexander Graf | sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS]; |
585 | f6ad2e32 | Alexander Graf | ide_state = &s->dev[port].port.ifs[0];
|
586 | f6ad2e32 | Alexander Graf | |
587 | f6ad2e32 | Alexander Graf | /* clear memory */
|
588 | f6ad2e32 | Alexander Graf | *(uint32_t*)sdb_fis = 0;
|
589 | f6ad2e32 | Alexander Graf | |
590 | f6ad2e32 | Alexander Graf | /* write values */
|
591 | f6ad2e32 | Alexander Graf | sdb_fis[0] = ide_state->error;
|
592 | f6ad2e32 | Alexander Graf | sdb_fis[2] = ide_state->status & 0x77; |
593 | f6ad2e32 | Alexander Graf | s->dev[port].finished |= finished; |
594 | f6ad2e32 | Alexander Graf | *(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished);
|
595 | f6ad2e32 | Alexander Graf | |
596 | f6ad2e32 | Alexander Graf | ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_STAT_SDBS); |
597 | f6ad2e32 | Alexander Graf | } |
598 | f6ad2e32 | Alexander Graf | |
599 | f6ad2e32 | Alexander Graf | static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) |
600 | f6ad2e32 | Alexander Graf | { |
601 | f6ad2e32 | Alexander Graf | AHCIPortRegs *pr = &ad->port_regs; |
602 | f6ad2e32 | Alexander Graf | uint8_t *d2h_fis; |
603 | f6ad2e32 | Alexander Graf | int i;
|
604 | f6ad2e32 | Alexander Graf | target_phys_addr_t cmd_len = 0x80;
|
605 | f6ad2e32 | Alexander Graf | int cmd_mapped = 0; |
606 | f6ad2e32 | Alexander Graf | |
607 | f6ad2e32 | Alexander Graf | if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
|
608 | f6ad2e32 | Alexander Graf | return;
|
609 | f6ad2e32 | Alexander Graf | } |
610 | f6ad2e32 | Alexander Graf | |
611 | f6ad2e32 | Alexander Graf | if (!cmd_fis) {
|
612 | f6ad2e32 | Alexander Graf | /* map cmd_fis */
|
613 | f6ad2e32 | Alexander Graf | uint64_t tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr); |
614 | f6ad2e32 | Alexander Graf | cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 0);
|
615 | f6ad2e32 | Alexander Graf | cmd_mapped = 1;
|
616 | f6ad2e32 | Alexander Graf | } |
617 | f6ad2e32 | Alexander Graf | |
618 | f6ad2e32 | Alexander Graf | d2h_fis = &ad->res_fis[RES_FIS_RFIS]; |
619 | f6ad2e32 | Alexander Graf | |
620 | f6ad2e32 | Alexander Graf | d2h_fis[0] = 0x34; |
621 | f6ad2e32 | Alexander Graf | d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); |
622 | f6ad2e32 | Alexander Graf | d2h_fis[2] = ad->port.ifs[0].status; |
623 | f6ad2e32 | Alexander Graf | d2h_fis[3] = ad->port.ifs[0].error; |
624 | f6ad2e32 | Alexander Graf | |
625 | f6ad2e32 | Alexander Graf | d2h_fis[4] = cmd_fis[4]; |
626 | f6ad2e32 | Alexander Graf | d2h_fis[5] = cmd_fis[5]; |
627 | f6ad2e32 | Alexander Graf | d2h_fis[6] = cmd_fis[6]; |
628 | f6ad2e32 | Alexander Graf | d2h_fis[7] = cmd_fis[7]; |
629 | f6ad2e32 | Alexander Graf | d2h_fis[8] = cmd_fis[8]; |
630 | f6ad2e32 | Alexander Graf | d2h_fis[9] = cmd_fis[9]; |
631 | f6ad2e32 | Alexander Graf | d2h_fis[10] = cmd_fis[10]; |
632 | f6ad2e32 | Alexander Graf | d2h_fis[11] = cmd_fis[11]; |
633 | f6ad2e32 | Alexander Graf | d2h_fis[12] = cmd_fis[12]; |
634 | f6ad2e32 | Alexander Graf | d2h_fis[13] = cmd_fis[13]; |
635 | f6ad2e32 | Alexander Graf | for (i = 14; i < 0x20; i++) { |
636 | f6ad2e32 | Alexander Graf | d2h_fis[i] = 0;
|
637 | f6ad2e32 | Alexander Graf | } |
638 | f6ad2e32 | Alexander Graf | |
639 | f6ad2e32 | Alexander Graf | if (d2h_fis[2] & ERR_STAT) { |
640 | f6ad2e32 | Alexander Graf | ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_TFES); |
641 | f6ad2e32 | Alexander Graf | } |
642 | f6ad2e32 | Alexander Graf | |
643 | f6ad2e32 | Alexander Graf | ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); |
644 | f6ad2e32 | Alexander Graf | |
645 | f6ad2e32 | Alexander Graf | if (cmd_mapped) {
|
646 | fe6ceac8 | Stefan Hajnoczi | cpu_physical_memory_unmap(cmd_fis, cmd_len, 0, cmd_len);
|
647 | f6ad2e32 | Alexander Graf | } |
648 | f6ad2e32 | Alexander Graf | } |
649 | f6ad2e32 | Alexander Graf | |
650 | f6ad2e32 | Alexander Graf | static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) |
651 | f6ad2e32 | Alexander Graf | { |
652 | f6ad2e32 | Alexander Graf | AHCICmdHdr *cmd = ad->cur_cmd; |
653 | f6ad2e32 | Alexander Graf | uint32_t opts = le32_to_cpu(cmd->opts); |
654 | f6ad2e32 | Alexander Graf | uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80;
|
655 | f6ad2e32 | Alexander Graf | int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN;
|
656 | f6ad2e32 | Alexander Graf | target_phys_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG));
|
657 | f6ad2e32 | Alexander Graf | target_phys_addr_t real_prdt_len = prdt_len; |
658 | f6ad2e32 | Alexander Graf | uint8_t *prdt; |
659 | f6ad2e32 | Alexander Graf | int i;
|
660 | f6ad2e32 | Alexander Graf | int r = 0; |
661 | f6ad2e32 | Alexander Graf | |
662 | f6ad2e32 | Alexander Graf | if (!sglist_alloc_hint) {
|
663 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
|
664 | f6ad2e32 | Alexander Graf | return -1; |
665 | f6ad2e32 | Alexander Graf | } |
666 | f6ad2e32 | Alexander Graf | |
667 | f6ad2e32 | Alexander Graf | /* map PRDT */
|
668 | f6ad2e32 | Alexander Graf | if (!(prdt = cpu_physical_memory_map(prdt_addr, &prdt_len, 0))){ |
669 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "map failed\n");
|
670 | f6ad2e32 | Alexander Graf | return -1; |
671 | f6ad2e32 | Alexander Graf | } |
672 | f6ad2e32 | Alexander Graf | |
673 | f6ad2e32 | Alexander Graf | if (prdt_len < real_prdt_len) {
|
674 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "mapped less than expected\n");
|
675 | f6ad2e32 | Alexander Graf | r = -1;
|
676 | f6ad2e32 | Alexander Graf | goto out;
|
677 | f6ad2e32 | Alexander Graf | } |
678 | f6ad2e32 | Alexander Graf | |
679 | f6ad2e32 | Alexander Graf | /* Get entries in the PRDT, init a qemu sglist accordingly */
|
680 | f6ad2e32 | Alexander Graf | if (sglist_alloc_hint > 0) { |
681 | f6ad2e32 | Alexander Graf | AHCI_SG *tbl = (AHCI_SG *)prdt; |
682 | f6ad2e32 | Alexander Graf | |
683 | f6ad2e32 | Alexander Graf | qemu_sglist_init(sglist, sglist_alloc_hint); |
684 | f6ad2e32 | Alexander Graf | for (i = 0; i < sglist_alloc_hint; i++) { |
685 | f6ad2e32 | Alexander Graf | /* flags_size is zero-based */
|
686 | f6ad2e32 | Alexander Graf | qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), |
687 | f6ad2e32 | Alexander Graf | le32_to_cpu(tbl[i].flags_size) + 1);
|
688 | f6ad2e32 | Alexander Graf | } |
689 | f6ad2e32 | Alexander Graf | } |
690 | f6ad2e32 | Alexander Graf | |
691 | f6ad2e32 | Alexander Graf | out:
|
692 | fe6ceac8 | Stefan Hajnoczi | cpu_physical_memory_unmap(prdt, prdt_len, 0, prdt_len);
|
693 | f6ad2e32 | Alexander Graf | return r;
|
694 | f6ad2e32 | Alexander Graf | } |
695 | f6ad2e32 | Alexander Graf | |
696 | f6ad2e32 | Alexander Graf | static void ncq_cb(void *opaque, int ret) |
697 | f6ad2e32 | Alexander Graf | { |
698 | f6ad2e32 | Alexander Graf | NCQTransferState *ncq_tfs = (NCQTransferState *)opaque; |
699 | f6ad2e32 | Alexander Graf | IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
|
700 | f6ad2e32 | Alexander Graf | |
701 | f6ad2e32 | Alexander Graf | /* Clear bit for this tag in SActive */
|
702 | f6ad2e32 | Alexander Graf | ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag);
|
703 | f6ad2e32 | Alexander Graf | |
704 | f6ad2e32 | Alexander Graf | if (ret < 0) { |
705 | f6ad2e32 | Alexander Graf | /* error */
|
706 | f6ad2e32 | Alexander Graf | ide_state->error = ABRT_ERR; |
707 | f6ad2e32 | Alexander Graf | ide_state->status = READY_STAT | ERR_STAT; |
708 | f6ad2e32 | Alexander Graf | ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
|
709 | f6ad2e32 | Alexander Graf | } else {
|
710 | f6ad2e32 | Alexander Graf | ide_state->status = READY_STAT | SEEK_STAT; |
711 | f6ad2e32 | Alexander Graf | } |
712 | f6ad2e32 | Alexander Graf | |
713 | f6ad2e32 | Alexander Graf | ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs->drive->port_no, |
714 | f6ad2e32 | Alexander Graf | (1 << ncq_tfs->tag));
|
715 | f6ad2e32 | Alexander Graf | |
716 | f6ad2e32 | Alexander Graf | DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n",
|
717 | f6ad2e32 | Alexander Graf | ncq_tfs->tag); |
718 | f6ad2e32 | Alexander Graf | |
719 | f6ad2e32 | Alexander Graf | qemu_sglist_destroy(&ncq_tfs->sglist); |
720 | f6ad2e32 | Alexander Graf | ncq_tfs->used = 0;
|
721 | f6ad2e32 | Alexander Graf | } |
722 | f6ad2e32 | Alexander Graf | |
723 | f6ad2e32 | Alexander Graf | static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, |
724 | f6ad2e32 | Alexander Graf | int slot)
|
725 | f6ad2e32 | Alexander Graf | { |
726 | f6ad2e32 | Alexander Graf | NCQFrame *ncq_fis = (NCQFrame*)cmd_fis; |
727 | f6ad2e32 | Alexander Graf | uint8_t tag = ncq_fis->tag >> 3;
|
728 | f6ad2e32 | Alexander Graf | NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[tag]; |
729 | f6ad2e32 | Alexander Graf | |
730 | f6ad2e32 | Alexander Graf | if (ncq_tfs->used) {
|
731 | f6ad2e32 | Alexander Graf | /* error - already in use */
|
732 | f6ad2e32 | Alexander Graf | fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag);
|
733 | f6ad2e32 | Alexander Graf | return;
|
734 | f6ad2e32 | Alexander Graf | } |
735 | f6ad2e32 | Alexander Graf | |
736 | f6ad2e32 | Alexander Graf | ncq_tfs->used = 1;
|
737 | f6ad2e32 | Alexander Graf | ncq_tfs->drive = &s->dev[port]; |
738 | f6ad2e32 | Alexander Graf | ncq_tfs->slot = slot; |
739 | f6ad2e32 | Alexander Graf | ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) |
|
740 | f6ad2e32 | Alexander Graf | ((uint64_t)ncq_fis->lba4 << 32) |
|
741 | f6ad2e32 | Alexander Graf | ((uint64_t)ncq_fis->lba3 << 24) |
|
742 | f6ad2e32 | Alexander Graf | ((uint64_t)ncq_fis->lba2 << 16) |
|
743 | f6ad2e32 | Alexander Graf | ((uint64_t)ncq_fis->lba1 << 8) |
|
744 | f6ad2e32 | Alexander Graf | (uint64_t)ncq_fis->lba0; |
745 | f6ad2e32 | Alexander Graf | |
746 | f6ad2e32 | Alexander Graf | /* Note: We calculate the sector count, but don't currently rely on it.
|
747 | f6ad2e32 | Alexander Graf | * The total size of the DMA buffer tells us the transfer size instead. */
|
748 | f6ad2e32 | Alexander Graf | ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
|
749 | f6ad2e32 | Alexander Graf | ncq_fis->sector_count_low; |
750 | f6ad2e32 | Alexander Graf | |
751 | f6ad2e32 | Alexander Graf | DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n",
|
752 | f6ad2e32 | Alexander Graf | ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
|
753 | f6ad2e32 | Alexander Graf | s->dev[port].port.ifs[0].nb_sectors - 1); |
754 | f6ad2e32 | Alexander Graf | |
755 | f6ad2e32 | Alexander Graf | ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist); |
756 | f6ad2e32 | Alexander Graf | ncq_tfs->tag = tag; |
757 | f6ad2e32 | Alexander Graf | |
758 | f6ad2e32 | Alexander Graf | switch(ncq_fis->command) {
|
759 | f6ad2e32 | Alexander Graf | case READ_FPDMA_QUEUED:
|
760 | f6ad2e32 | Alexander Graf | DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n",
|
761 | f6ad2e32 | Alexander Graf | ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
|
762 | f6ad2e32 | Alexander Graf | ncq_tfs->is_read = 1;
|
763 | f6ad2e32 | Alexander Graf | |
764 | f6ad2e32 | Alexander Graf | DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba);
|
765 | f6ad2e32 | Alexander Graf | ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
|
766 | f6ad2e32 | Alexander Graf | &ncq_tfs->sglist, ncq_tfs->lba, |
767 | f6ad2e32 | Alexander Graf | ncq_cb, ncq_tfs); |
768 | f6ad2e32 | Alexander Graf | break;
|
769 | f6ad2e32 | Alexander Graf | case WRITE_FPDMA_QUEUED:
|
770 | f6ad2e32 | Alexander Graf | DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n",
|
771 | f6ad2e32 | Alexander Graf | ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
|
772 | f6ad2e32 | Alexander Graf | ncq_tfs->is_read = 0;
|
773 | f6ad2e32 | Alexander Graf | |
774 | f6ad2e32 | Alexander Graf | DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba);
|
775 | f6ad2e32 | Alexander Graf | ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
|
776 | f6ad2e32 | Alexander Graf | &ncq_tfs->sglist, ncq_tfs->lba, |
777 | f6ad2e32 | Alexander Graf | ncq_cb, ncq_tfs); |
778 | f6ad2e32 | Alexander Graf | break;
|
779 | f6ad2e32 | Alexander Graf | default:
|
780 | f6ad2e32 | Alexander Graf | DPRINTF(port, "error: tried to process non-NCQ command as NCQ\n");
|
781 | f6ad2e32 | Alexander Graf | qemu_sglist_destroy(&ncq_tfs->sglist); |
782 | f6ad2e32 | Alexander Graf | break;
|
783 | f6ad2e32 | Alexander Graf | } |
784 | f6ad2e32 | Alexander Graf | } |
785 | f6ad2e32 | Alexander Graf | |
786 | f6ad2e32 | Alexander Graf | static int handle_cmd(AHCIState *s, int port, int slot) |
787 | f6ad2e32 | Alexander Graf | { |
788 | f6ad2e32 | Alexander Graf | IDEState *ide_state; |
789 | f6ad2e32 | Alexander Graf | uint32_t opts; |
790 | f6ad2e32 | Alexander Graf | uint64_t tbl_addr; |
791 | f6ad2e32 | Alexander Graf | AHCICmdHdr *cmd; |
792 | f6ad2e32 | Alexander Graf | uint8_t *cmd_fis; |
793 | f6ad2e32 | Alexander Graf | target_phys_addr_t cmd_len; |
794 | f6ad2e32 | Alexander Graf | |
795 | f6ad2e32 | Alexander Graf | if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { |
796 | f6ad2e32 | Alexander Graf | /* Engine currently busy, try again later */
|
797 | f6ad2e32 | Alexander Graf | DPRINTF(port, "engine busy\n");
|
798 | f6ad2e32 | Alexander Graf | return -1; |
799 | f6ad2e32 | Alexander Graf | } |
800 | f6ad2e32 | Alexander Graf | |
801 | f6ad2e32 | Alexander Graf | cmd = &((AHCICmdHdr *)s->dev[port].lst)[slot]; |
802 | f6ad2e32 | Alexander Graf | |
803 | f6ad2e32 | Alexander Graf | if (!s->dev[port].lst) {
|
804 | f6ad2e32 | Alexander Graf | DPRINTF(port, "error: lst not given but cmd handled");
|
805 | f6ad2e32 | Alexander Graf | return -1; |
806 | f6ad2e32 | Alexander Graf | } |
807 | f6ad2e32 | Alexander Graf | |
808 | f6ad2e32 | Alexander Graf | /* remember current slot handle for later */
|
809 | f6ad2e32 | Alexander Graf | s->dev[port].cur_cmd = cmd; |
810 | f6ad2e32 | Alexander Graf | |
811 | f6ad2e32 | Alexander Graf | opts = le32_to_cpu(cmd->opts); |
812 | f6ad2e32 | Alexander Graf | tbl_addr = le64_to_cpu(cmd->tbl_addr); |
813 | f6ad2e32 | Alexander Graf | |
814 | f6ad2e32 | Alexander Graf | cmd_len = 0x80;
|
815 | f6ad2e32 | Alexander Graf | cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 1);
|
816 | f6ad2e32 | Alexander Graf | |
817 | f6ad2e32 | Alexander Graf | if (!cmd_fis) {
|
818 | f6ad2e32 | Alexander Graf | DPRINTF(port, "error: guest passed us an invalid cmd fis\n");
|
819 | f6ad2e32 | Alexander Graf | return -1; |
820 | f6ad2e32 | Alexander Graf | } |
821 | f6ad2e32 | Alexander Graf | |
822 | f6ad2e32 | Alexander Graf | /* The device we are working for */
|
823 | f6ad2e32 | Alexander Graf | ide_state = &s->dev[port].port.ifs[0];
|
824 | f6ad2e32 | Alexander Graf | |
825 | f6ad2e32 | Alexander Graf | if (!ide_state->bs) {
|
826 | f6ad2e32 | Alexander Graf | DPRINTF(port, "error: guest accessed unused port");
|
827 | f6ad2e32 | Alexander Graf | goto out;
|
828 | f6ad2e32 | Alexander Graf | } |
829 | f6ad2e32 | Alexander Graf | |
830 | f6ad2e32 | Alexander Graf | debug_print_fis(cmd_fis, 0x90);
|
831 | f6ad2e32 | Alexander Graf | //debug_print_fis(cmd_fis, (opts & AHCI_CMD_HDR_CMD_FIS_LEN) * 4);
|
832 | f6ad2e32 | Alexander Graf | |
833 | f6ad2e32 | Alexander Graf | switch (cmd_fis[0]) { |
834 | f6ad2e32 | Alexander Graf | case SATA_FIS_TYPE_REGISTER_H2D:
|
835 | f6ad2e32 | Alexander Graf | break;
|
836 | f6ad2e32 | Alexander Graf | default:
|
837 | f6ad2e32 | Alexander Graf | DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x "
|
838 | f6ad2e32 | Alexander Graf | "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1], |
839 | f6ad2e32 | Alexander Graf | cmd_fis[2]);
|
840 | f6ad2e32 | Alexander Graf | goto out;
|
841 | f6ad2e32 | Alexander Graf | break;
|
842 | f6ad2e32 | Alexander Graf | } |
843 | f6ad2e32 | Alexander Graf | |
844 | f6ad2e32 | Alexander Graf | switch (cmd_fis[1]) { |
845 | f6ad2e32 | Alexander Graf | case SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER:
|
846 | f6ad2e32 | Alexander Graf | break;
|
847 | f6ad2e32 | Alexander Graf | case 0: |
848 | f6ad2e32 | Alexander Graf | break;
|
849 | f6ad2e32 | Alexander Graf | default:
|
850 | f6ad2e32 | Alexander Graf | DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x "
|
851 | f6ad2e32 | Alexander Graf | "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1], |
852 | f6ad2e32 | Alexander Graf | cmd_fis[2]);
|
853 | f6ad2e32 | Alexander Graf | goto out;
|
854 | f6ad2e32 | Alexander Graf | break;
|
855 | f6ad2e32 | Alexander Graf | } |
856 | f6ad2e32 | Alexander Graf | |
857 | f6ad2e32 | Alexander Graf | switch (s->dev[port].port_state) {
|
858 | f6ad2e32 | Alexander Graf | case STATE_RUN:
|
859 | f6ad2e32 | Alexander Graf | if (cmd_fis[15] & ATA_SRST) { |
860 | f6ad2e32 | Alexander Graf | s->dev[port].port_state = STATE_RESET; |
861 | f6ad2e32 | Alexander Graf | } |
862 | f6ad2e32 | Alexander Graf | break;
|
863 | f6ad2e32 | Alexander Graf | case STATE_RESET:
|
864 | f6ad2e32 | Alexander Graf | if (!(cmd_fis[15] & ATA_SRST)) { |
865 | f6ad2e32 | Alexander Graf | ahci_reset_port(s, port); |
866 | f6ad2e32 | Alexander Graf | } |
867 | f6ad2e32 | Alexander Graf | break;
|
868 | f6ad2e32 | Alexander Graf | } |
869 | f6ad2e32 | Alexander Graf | |
870 | f6ad2e32 | Alexander Graf | if (cmd_fis[1] == SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER) { |
871 | f6ad2e32 | Alexander Graf | |
872 | f6ad2e32 | Alexander Graf | /* Check for NCQ command */
|
873 | f6ad2e32 | Alexander Graf | if ((cmd_fis[2] == READ_FPDMA_QUEUED) || |
874 | f6ad2e32 | Alexander Graf | (cmd_fis[2] == WRITE_FPDMA_QUEUED)) {
|
875 | f6ad2e32 | Alexander Graf | process_ncq_command(s, port, cmd_fis, slot); |
876 | f6ad2e32 | Alexander Graf | goto out;
|
877 | f6ad2e32 | Alexander Graf | } |
878 | f6ad2e32 | Alexander Graf | |
879 | f6ad2e32 | Alexander Graf | /* Decompose the FIS */
|
880 | f6ad2e32 | Alexander Graf | ide_state->nsector = (int64_t)((cmd_fis[13] << 8) | cmd_fis[12]); |
881 | f6ad2e32 | Alexander Graf | ide_state->feature = cmd_fis[3];
|
882 | f6ad2e32 | Alexander Graf | if (!ide_state->nsector) {
|
883 | f6ad2e32 | Alexander Graf | ide_state->nsector = 256;
|
884 | f6ad2e32 | Alexander Graf | } |
885 | f6ad2e32 | Alexander Graf | |
886 | f6ad2e32 | Alexander Graf | if (ide_state->drive_kind != IDE_CD) {
|
887 | 1fddfba1 | Alexander Graf | /*
|
888 | 1fddfba1 | Alexander Graf | * We set the sector depending on the sector defined in the FIS.
|
889 | 1fddfba1 | Alexander Graf | * Unfortunately, the spec isn't exactly obvious on this one.
|
890 | 1fddfba1 | Alexander Graf | *
|
891 | 1fddfba1 | Alexander Graf | * Apparently LBA48 commands set fis bytes 10,9,8,6,5,4 to the
|
892 | 1fddfba1 | Alexander Graf | * 48 bit sector number. ATA_CMD_READ_DMA_EXT is an example for
|
893 | 1fddfba1 | Alexander Graf | * such a command.
|
894 | 1fddfba1 | Alexander Graf | *
|
895 | 1fddfba1 | Alexander Graf | * Non-LBA48 commands however use 7[lower 4 bits],6,5,4 to define a
|
896 | 1fddfba1 | Alexander Graf | * 28-bit sector number. ATA_CMD_READ_DMA is an example for such
|
897 | 1fddfba1 | Alexander Graf | * a command.
|
898 | 1fddfba1 | Alexander Graf | *
|
899 | 1fddfba1 | Alexander Graf | * Since the spec doesn't explicitly state what each field should
|
900 | 1fddfba1 | Alexander Graf | * do, I simply assume non-used fields as reserved and OR everything
|
901 | 1fddfba1 | Alexander Graf | * together, independent of the command.
|
902 | 1fddfba1 | Alexander Graf | */
|
903 | 1fddfba1 | Alexander Graf | ide_set_sector(ide_state, ((uint64_t)cmd_fis[10] << 40) |
904 | 1fddfba1 | Alexander Graf | | ((uint64_t)cmd_fis[9] << 32) |
905 | 1fddfba1 | Alexander Graf | /* This is used for LBA48 commands */
|
906 | 1fddfba1 | Alexander Graf | | ((uint64_t)cmd_fis[8] << 24) |
907 | 1fddfba1 | Alexander Graf | /* This is used for non-LBA48 commands */
|
908 | 1fddfba1 | Alexander Graf | | ((uint64_t)(cmd_fis[7] & 0xf) << 24) |
909 | 1fddfba1 | Alexander Graf | | ((uint64_t)cmd_fis[6] << 16) |
910 | 1fddfba1 | Alexander Graf | | ((uint64_t)cmd_fis[5] << 8) |
911 | 1fddfba1 | Alexander Graf | | cmd_fis[4]);
|
912 | f6ad2e32 | Alexander Graf | } |
913 | f6ad2e32 | Alexander Graf | |
914 | f6ad2e32 | Alexander Graf | /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command
|
915 | f6ad2e32 | Alexander Graf | * table to ide_state->io_buffer
|
916 | f6ad2e32 | Alexander Graf | */
|
917 | f6ad2e32 | Alexander Graf | if (opts & AHCI_CMD_ATAPI) {
|
918 | f6ad2e32 | Alexander Graf | memcpy(ide_state->io_buffer, &cmd_fis[AHCI_COMMAND_TABLE_ACMD], 0x10);
|
919 | f6ad2e32 | Alexander Graf | ide_state->lcyl = 0x14;
|
920 | f6ad2e32 | Alexander Graf | ide_state->hcyl = 0xeb;
|
921 | f6ad2e32 | Alexander Graf | debug_print_fis(ide_state->io_buffer, 0x10);
|
922 | f6ad2e32 | Alexander Graf | ide_state->feature = IDE_FEATURE_DMA; |
923 | f6ad2e32 | Alexander Graf | s->dev[port].done_atapi_packet = 0;
|
924 | f6ad2e32 | Alexander Graf | /* XXX send PIO setup FIS */
|
925 | f6ad2e32 | Alexander Graf | } |
926 | f6ad2e32 | Alexander Graf | |
927 | f6ad2e32 | Alexander Graf | ide_state->error = 0;
|
928 | f6ad2e32 | Alexander Graf | |
929 | f6ad2e32 | Alexander Graf | /* Reset transferred byte counter */
|
930 | f6ad2e32 | Alexander Graf | cmd->status = 0;
|
931 | f6ad2e32 | Alexander Graf | |
932 | f6ad2e32 | Alexander Graf | /* We're ready to process the command in FIS byte 2. */
|
933 | f6ad2e32 | Alexander Graf | ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
|
934 | f6ad2e32 | Alexander Graf | |
935 | f6ad2e32 | Alexander Graf | if (s->dev[port].port.ifs[0].status & READY_STAT) { |
936 | f6ad2e32 | Alexander Graf | ahci_write_fis_d2h(&s->dev[port], cmd_fis); |
937 | f6ad2e32 | Alexander Graf | } |
938 | f6ad2e32 | Alexander Graf | } |
939 | f6ad2e32 | Alexander Graf | |
940 | f6ad2e32 | Alexander Graf | out:
|
941 | fe6ceac8 | Stefan Hajnoczi | cpu_physical_memory_unmap(cmd_fis, cmd_len, 1, cmd_len);
|
942 | f6ad2e32 | Alexander Graf | |
943 | f6ad2e32 | Alexander Graf | if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { |
944 | f6ad2e32 | Alexander Graf | /* async command, complete later */
|
945 | f6ad2e32 | Alexander Graf | s->dev[port].busy_slot = slot; |
946 | f6ad2e32 | Alexander Graf | return -1; |
947 | f6ad2e32 | Alexander Graf | } |
948 | f6ad2e32 | Alexander Graf | |
949 | f6ad2e32 | Alexander Graf | /* done handling the command */
|
950 | f6ad2e32 | Alexander Graf | return 0; |
951 | f6ad2e32 | Alexander Graf | } |
952 | f6ad2e32 | Alexander Graf | |
953 | f6ad2e32 | Alexander Graf | /* DMA dev <-> ram */
|
954 | f6ad2e32 | Alexander Graf | static int ahci_start_transfer(IDEDMA *dma) |
955 | f6ad2e32 | Alexander Graf | { |
956 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
957 | f6ad2e32 | Alexander Graf | IDEState *s = &ad->port.ifs[0];
|
958 | f6ad2e32 | Alexander Graf | uint32_t size = (uint32_t)(s->data_end - s->data_ptr); |
959 | f6ad2e32 | Alexander Graf | /* write == ram -> device */
|
960 | f6ad2e32 | Alexander Graf | uint32_t opts = le32_to_cpu(ad->cur_cmd->opts); |
961 | f6ad2e32 | Alexander Graf | int is_write = opts & AHCI_CMD_WRITE;
|
962 | f6ad2e32 | Alexander Graf | int is_atapi = opts & AHCI_CMD_ATAPI;
|
963 | f6ad2e32 | Alexander Graf | int has_sglist = 0; |
964 | f6ad2e32 | Alexander Graf | |
965 | f6ad2e32 | Alexander Graf | if (is_atapi && !ad->done_atapi_packet) {
|
966 | f6ad2e32 | Alexander Graf | /* already prepopulated iobuffer */
|
967 | f6ad2e32 | Alexander Graf | ad->done_atapi_packet = 1;
|
968 | f6ad2e32 | Alexander Graf | goto out;
|
969 | f6ad2e32 | Alexander Graf | } |
970 | f6ad2e32 | Alexander Graf | |
971 | f6ad2e32 | Alexander Graf | if (!ahci_populate_sglist(ad, &s->sg)) {
|
972 | f6ad2e32 | Alexander Graf | has_sglist = 1;
|
973 | f6ad2e32 | Alexander Graf | } |
974 | f6ad2e32 | Alexander Graf | |
975 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "%sing %d bytes on %s w/%s sglist\n",
|
976 | f6ad2e32 | Alexander Graf | is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata", |
977 | f6ad2e32 | Alexander Graf | has_sglist ? "" : "o"); |
978 | f6ad2e32 | Alexander Graf | |
979 | f6ad2e32 | Alexander Graf | if (is_write && has_sglist && (s->data_ptr < s->data_end)) {
|
980 | f6ad2e32 | Alexander Graf | read_from_sglist(s->data_ptr, size, &s->sg); |
981 | f6ad2e32 | Alexander Graf | } |
982 | f6ad2e32 | Alexander Graf | |
983 | f6ad2e32 | Alexander Graf | if (!is_write && has_sglist && (s->data_ptr < s->data_end)) {
|
984 | f6ad2e32 | Alexander Graf | write_to_sglist(s->data_ptr, size, &s->sg); |
985 | f6ad2e32 | Alexander Graf | } |
986 | f6ad2e32 | Alexander Graf | |
987 | f6ad2e32 | Alexander Graf | /* update number of transferred bytes */
|
988 | f6ad2e32 | Alexander Graf | ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size); |
989 | f6ad2e32 | Alexander Graf | |
990 | f6ad2e32 | Alexander Graf | out:
|
991 | f6ad2e32 | Alexander Graf | /* declare that we processed everything */
|
992 | f6ad2e32 | Alexander Graf | s->data_ptr = s->data_end; |
993 | f6ad2e32 | Alexander Graf | |
994 | f6ad2e32 | Alexander Graf | if (has_sglist) {
|
995 | f6ad2e32 | Alexander Graf | qemu_sglist_destroy(&s->sg); |
996 | f6ad2e32 | Alexander Graf | } |
997 | f6ad2e32 | Alexander Graf | |
998 | f6ad2e32 | Alexander Graf | s->end_transfer_func(s); |
999 | f6ad2e32 | Alexander Graf | |
1000 | f6ad2e32 | Alexander Graf | if (!(s->status & DRQ_STAT)) {
|
1001 | f6ad2e32 | Alexander Graf | /* done with DMA */
|
1002 | f6ad2e32 | Alexander Graf | ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS); |
1003 | f6ad2e32 | Alexander Graf | } |
1004 | f6ad2e32 | Alexander Graf | |
1005 | f6ad2e32 | Alexander Graf | return 0; |
1006 | f6ad2e32 | Alexander Graf | } |
1007 | f6ad2e32 | Alexander Graf | |
1008 | f6ad2e32 | Alexander Graf | static void ahci_start_dma(IDEDMA *dma, IDEState *s, |
1009 | f6ad2e32 | Alexander Graf | BlockDriverCompletionFunc *dma_cb) |
1010 | f6ad2e32 | Alexander Graf | { |
1011 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
1012 | f6ad2e32 | Alexander Graf | |
1013 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "\n");
|
1014 | f6ad2e32 | Alexander Graf | ad->dma_cb = dma_cb; |
1015 | f6ad2e32 | Alexander Graf | ad->dma_status |= BM_STATUS_DMAING; |
1016 | f6ad2e32 | Alexander Graf | dma_cb(s, 0);
|
1017 | f6ad2e32 | Alexander Graf | } |
1018 | f6ad2e32 | Alexander Graf | |
1019 | f6ad2e32 | Alexander Graf | static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) |
1020 | f6ad2e32 | Alexander Graf | { |
1021 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
1022 | f6ad2e32 | Alexander Graf | IDEState *s = &ad->port.ifs[0];
|
1023 | f6ad2e32 | Alexander Graf | int i;
|
1024 | f6ad2e32 | Alexander Graf | |
1025 | f6ad2e32 | Alexander Graf | ahci_populate_sglist(ad, &s->sg); |
1026 | f6ad2e32 | Alexander Graf | |
1027 | f6ad2e32 | Alexander Graf | s->io_buffer_size = 0;
|
1028 | f6ad2e32 | Alexander Graf | for (i = 0; i < s->sg.nsg; i++) { |
1029 | f6ad2e32 | Alexander Graf | s->io_buffer_size += s->sg.sg[i].len; |
1030 | f6ad2e32 | Alexander Graf | } |
1031 | f6ad2e32 | Alexander Graf | |
1032 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
|
1033 | f6ad2e32 | Alexander Graf | return s->io_buffer_size != 0; |
1034 | f6ad2e32 | Alexander Graf | } |
1035 | f6ad2e32 | Alexander Graf | |
1036 | f6ad2e32 | Alexander Graf | static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) |
1037 | f6ad2e32 | Alexander Graf | { |
1038 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
1039 | f6ad2e32 | Alexander Graf | IDEState *s = &ad->port.ifs[0];
|
1040 | f6ad2e32 | Alexander Graf | uint8_t *p = s->io_buffer + s->io_buffer_index; |
1041 | f6ad2e32 | Alexander Graf | int l = s->io_buffer_size - s->io_buffer_index;
|
1042 | f6ad2e32 | Alexander Graf | |
1043 | f6ad2e32 | Alexander Graf | if (ahci_populate_sglist(ad, &s->sg)) {
|
1044 | f6ad2e32 | Alexander Graf | return 0; |
1045 | f6ad2e32 | Alexander Graf | } |
1046 | f6ad2e32 | Alexander Graf | |
1047 | f6ad2e32 | Alexander Graf | if (is_write) {
|
1048 | f6ad2e32 | Alexander Graf | write_to_sglist(p, l, &s->sg); |
1049 | f6ad2e32 | Alexander Graf | } else {
|
1050 | f6ad2e32 | Alexander Graf | read_from_sglist(p, l, &s->sg); |
1051 | f6ad2e32 | Alexander Graf | } |
1052 | f6ad2e32 | Alexander Graf | |
1053 | f6ad2e32 | Alexander Graf | /* update number of transferred bytes */
|
1054 | f6ad2e32 | Alexander Graf | ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); |
1055 | f6ad2e32 | Alexander Graf | s->io_buffer_index += l; |
1056 | f6ad2e32 | Alexander Graf | |
1057 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "len=%#x\n", l);
|
1058 | f6ad2e32 | Alexander Graf | |
1059 | f6ad2e32 | Alexander Graf | return 1; |
1060 | f6ad2e32 | Alexander Graf | } |
1061 | f6ad2e32 | Alexander Graf | |
1062 | f6ad2e32 | Alexander Graf | static int ahci_dma_set_unit(IDEDMA *dma, int unit) |
1063 | f6ad2e32 | Alexander Graf | { |
1064 | f6ad2e32 | Alexander Graf | /* only a single unit per link */
|
1065 | f6ad2e32 | Alexander Graf | return 0; |
1066 | f6ad2e32 | Alexander Graf | } |
1067 | f6ad2e32 | Alexander Graf | |
1068 | f6ad2e32 | Alexander Graf | static int ahci_dma_add_status(IDEDMA *dma, int status) |
1069 | f6ad2e32 | Alexander Graf | { |
1070 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
1071 | f6ad2e32 | Alexander Graf | ad->dma_status |= status; |
1072 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "set status: %x\n", status);
|
1073 | f6ad2e32 | Alexander Graf | |
1074 | f6ad2e32 | Alexander Graf | if (status & BM_STATUS_INT) {
|
1075 | f6ad2e32 | Alexander Graf | ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS); |
1076 | f6ad2e32 | Alexander Graf | } |
1077 | f6ad2e32 | Alexander Graf | |
1078 | f6ad2e32 | Alexander Graf | return 0; |
1079 | f6ad2e32 | Alexander Graf | } |
1080 | f6ad2e32 | Alexander Graf | |
1081 | f6ad2e32 | Alexander Graf | static int ahci_dma_set_inactive(IDEDMA *dma) |
1082 | f6ad2e32 | Alexander Graf | { |
1083 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); |
1084 | f6ad2e32 | Alexander Graf | |
1085 | f6ad2e32 | Alexander Graf | DPRINTF(ad->port_no, "dma done\n");
|
1086 | f6ad2e32 | Alexander Graf | |
1087 | f6ad2e32 | Alexander Graf | /* update d2h status */
|
1088 | f6ad2e32 | Alexander Graf | ahci_write_fis_d2h(ad, NULL);
|
1089 | f6ad2e32 | Alexander Graf | |
1090 | f6ad2e32 | Alexander Graf | ad->dma_cb = NULL;
|
1091 | f6ad2e32 | Alexander Graf | |
1092 | 4d29b50a | Jan Kiszka | if (!ad->check_bh) {
|
1093 | 4d29b50a | Jan Kiszka | /* maybe we still have something to process, check later */
|
1094 | 4d29b50a | Jan Kiszka | ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); |
1095 | 4d29b50a | Jan Kiszka | qemu_bh_schedule(ad->check_bh); |
1096 | 4d29b50a | Jan Kiszka | } |
1097 | f6ad2e32 | Alexander Graf | |
1098 | f6ad2e32 | Alexander Graf | return 0; |
1099 | f6ad2e32 | Alexander Graf | } |
1100 | f6ad2e32 | Alexander Graf | |
1101 | f6ad2e32 | Alexander Graf | static void ahci_irq_set(void *opaque, int n, int level) |
1102 | f6ad2e32 | Alexander Graf | { |
1103 | f6ad2e32 | Alexander Graf | } |
1104 | f6ad2e32 | Alexander Graf | |
1105 | f6ad2e32 | Alexander Graf | static void ahci_dma_restart_cb(void *opaque, int running, int reason) |
1106 | f6ad2e32 | Alexander Graf | { |
1107 | f6ad2e32 | Alexander Graf | } |
1108 | f6ad2e32 | Alexander Graf | |
1109 | f6ad2e32 | Alexander Graf | static int ahci_dma_reset(IDEDMA *dma) |
1110 | f6ad2e32 | Alexander Graf | { |
1111 | f6ad2e32 | Alexander Graf | return 0; |
1112 | f6ad2e32 | Alexander Graf | } |
1113 | f6ad2e32 | Alexander Graf | |
1114 | f6ad2e32 | Alexander Graf | static const IDEDMAOps ahci_dma_ops = { |
1115 | f6ad2e32 | Alexander Graf | .start_dma = ahci_start_dma, |
1116 | f6ad2e32 | Alexander Graf | .start_transfer = ahci_start_transfer, |
1117 | f6ad2e32 | Alexander Graf | .prepare_buf = ahci_dma_prepare_buf, |
1118 | f6ad2e32 | Alexander Graf | .rw_buf = ahci_dma_rw_buf, |
1119 | f6ad2e32 | Alexander Graf | .set_unit = ahci_dma_set_unit, |
1120 | f6ad2e32 | Alexander Graf | .add_status = ahci_dma_add_status, |
1121 | f6ad2e32 | Alexander Graf | .set_inactive = ahci_dma_set_inactive, |
1122 | f6ad2e32 | Alexander Graf | .restart_cb = ahci_dma_restart_cb, |
1123 | f6ad2e32 | Alexander Graf | .reset = ahci_dma_reset, |
1124 | f6ad2e32 | Alexander Graf | }; |
1125 | f6ad2e32 | Alexander Graf | |
1126 | 2c4b9d0e | Alexander Graf | void ahci_init(AHCIState *s, DeviceState *qdev, int ports) |
1127 | f6ad2e32 | Alexander Graf | { |
1128 | f6ad2e32 | Alexander Graf | qemu_irq *irqs; |
1129 | f6ad2e32 | Alexander Graf | int i;
|
1130 | f6ad2e32 | Alexander Graf | |
1131 | 2c4b9d0e | Alexander Graf | s->ports = ports; |
1132 | 2c4b9d0e | Alexander Graf | s->dev = qemu_mallocz(sizeof(AHCIDevice) * ports);
|
1133 | f6ad2e32 | Alexander Graf | ahci_reg_init(s); |
1134 | f6ad2e32 | Alexander Graf | s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s, |
1135 | f6ad2e32 | Alexander Graf | DEVICE_LITTLE_ENDIAN); |
1136 | 2c4b9d0e | Alexander Graf | irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports); |
1137 | f6ad2e32 | Alexander Graf | |
1138 | 2c4b9d0e | Alexander Graf | for (i = 0; i < s->ports; i++) { |
1139 | f6ad2e32 | Alexander Graf | AHCIDevice *ad = &s->dev[i]; |
1140 | f6ad2e32 | Alexander Graf | |
1141 | f6ad2e32 | Alexander Graf | ide_bus_new(&ad->port, qdev, i); |
1142 | f6ad2e32 | Alexander Graf | ide_init2(&ad->port, irqs[i]); |
1143 | f6ad2e32 | Alexander Graf | |
1144 | f6ad2e32 | Alexander Graf | ad->hba = s; |
1145 | f6ad2e32 | Alexander Graf | ad->port_no = i; |
1146 | f6ad2e32 | Alexander Graf | ad->port.dma = &ad->dma; |
1147 | f6ad2e32 | Alexander Graf | ad->port.dma->ops = &ahci_dma_ops; |
1148 | f6ad2e32 | Alexander Graf | ad->port_regs.cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON; |
1149 | f6ad2e32 | Alexander Graf | } |
1150 | f6ad2e32 | Alexander Graf | } |
1151 | f6ad2e32 | Alexander Graf | |
1152 | 2c4b9d0e | Alexander Graf | void ahci_uninit(AHCIState *s)
|
1153 | 2c4b9d0e | Alexander Graf | { |
1154 | 2c4b9d0e | Alexander Graf | qemu_free(s->dev); |
1155 | 2c4b9d0e | Alexander Graf | } |
1156 | 2c4b9d0e | Alexander Graf | |
1157 | 03c7a6a8 | Sebastian Herbszt | void ahci_reset(void *opaque) |
1158 | f6ad2e32 | Alexander Graf | { |
1159 | f6ad2e32 | Alexander Graf | struct AHCIPCIState *d = opaque;
|
1160 | f6ad2e32 | Alexander Graf | int i;
|
1161 | f6ad2e32 | Alexander Graf | |
1162 | 760c3e44 | Alexander Graf | d->ahci.control_regs.irqstatus = 0;
|
1163 | 760c3e44 | Alexander Graf | d->ahci.control_regs.ghc = 0;
|
1164 | 760c3e44 | Alexander Graf | |
1165 | 2c4b9d0e | Alexander Graf | for (i = 0; i < d->ahci.ports; i++) { |
1166 | f6ad2e32 | Alexander Graf | ahci_reset_port(&d->ahci, i); |
1167 | f6ad2e32 | Alexander Graf | } |
1168 | f6ad2e32 | Alexander Graf | } |