1 | /*
|
---|
2 | * Copyright (c) 2012 Petr Jerman
|
---|
3 | * All rights reserved.
|
---|
4 | *
|
---|
5 | * Redistribution and use in source and binary forms, with or without
|
---|
6 | * modification, are permitted provided that the following conditions
|
---|
7 | * are met:
|
---|
8 | *
|
---|
9 | * - Redistributions of source code must retain the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer.
|
---|
11 | * - 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 | * - The name of the author may not be used to endorse or promote products
|
---|
15 | * derived from this software without specific prior written permission.
|
---|
16 | *
|
---|
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
27 | */
|
---|
28 |
|
---|
29 | /** @file
|
---|
30 | * AHCI SATA driver implementation.
|
---|
31 | */
|
---|
32 |
|
---|
33 | #include <as.h>
|
---|
34 | #include <errno.h>
|
---|
35 | #include <stdio.h>
|
---|
36 | #include <devman.h>
|
---|
37 | #include <ddf/interrupt.h>
|
---|
38 | #include <ddf/log.h>
|
---|
39 | #include <device/hw_res_parsed.h>
|
---|
40 | #include <device/pci.h>
|
---|
41 | #include <sysinfo.h>
|
---|
42 | #include <ipc/irc.h>
|
---|
43 | #include <ns.h>
|
---|
44 | #include <ahci_iface.h>
|
---|
45 | #include "ahci.h"
|
---|
46 | #include "ahci_hw.h"
|
---|
47 | #include "ahci_sata.h"
|
---|
48 |
|
---|
49 | #define NAME "ahci"
|
---|
50 |
|
---|
51 | #define LO(ptr) \
|
---|
52 | ((uint32_t) (((uint64_t) ((uintptr_t) (ptr))) & 0xffffffff))
|
---|
53 |
|
---|
54 | #define HI(ptr) \
|
---|
55 | ((uint32_t) (((uint64_t) ((uintptr_t) (ptr))) >> 32))
|
---|
56 |
|
---|
57 | /** Interrupt pseudocode for a single port
|
---|
58 | *
|
---|
59 | * The interrupt handling works as follows:
|
---|
60 | *
|
---|
61 | * 1. Port interrupt status register is read
|
---|
62 | * (stored as arg2).
|
---|
63 | * 2. If port interrupt is indicated, then:
|
---|
64 | * 3. Port interrupt status register is cleared.
|
---|
65 | * 4. Global interrupt status register is read
|
---|
66 | * and cleared (any potential interrupts from
|
---|
67 | * other ports are reasserted automatically).
|
---|
68 | * 5. Port number is stored as arg1.
|
---|
69 | * 6. The interrupt is accepted.
|
---|
70 | *
|
---|
71 | */
|
---|
72 | #define AHCI_PORT_CMDS(port) \
|
---|
73 | { \
|
---|
74 | /* Read port interrupt status register */ \
|
---|
75 | .cmd = CMD_PIO_READ_32, \
|
---|
76 | .addr = NULL, \
|
---|
77 | .dstarg = 2 \
|
---|
78 | }, \
|
---|
79 | { \
|
---|
80 | /* Check if port asserted interrupt */ \
|
---|
81 | .cmd = CMD_PREDICATE, \
|
---|
82 | .value = 5, \
|
---|
83 | .srcarg = 2, \
|
---|
84 | }, \
|
---|
85 | { \
|
---|
86 | /* Clear port interrupt status register */ \
|
---|
87 | .cmd = CMD_PIO_WRITE_A_32, \
|
---|
88 | .addr = NULL, \
|
---|
89 | .srcarg = 2 \
|
---|
90 | }, \
|
---|
91 | { \
|
---|
92 | /* Read global interrupt status register */ \
|
---|
93 | .cmd = CMD_PIO_READ_32, \
|
---|
94 | .addr = NULL, \
|
---|
95 | .dstarg = 0 \
|
---|
96 | }, \
|
---|
97 | { \
|
---|
98 | /* Clear global interrupt status register */ \
|
---|
99 | .cmd = CMD_PIO_WRITE_A_32, \
|
---|
100 | .addr = NULL, \
|
---|
101 | .srcarg = 0 \
|
---|
102 | }, \
|
---|
103 | { \
|
---|
104 | /* Indicate port interrupt assertion */ \
|
---|
105 | .cmd = CMD_LOAD, \
|
---|
106 | .value = (port), \
|
---|
107 | .dstarg = 1 \
|
---|
108 | }, \
|
---|
109 | { \
|
---|
110 | /* Accept the interrupt */ \
|
---|
111 | .cmd = CMD_ACCEPT \
|
---|
112 | }
|
---|
113 |
|
---|
114 | static int ahci_get_sata_device_name(ddf_fun_t *, size_t, char *);
|
---|
115 | static int ahci_get_num_blocks(ddf_fun_t *, uint64_t *);
|
---|
116 | static int ahci_get_block_size(ddf_fun_t *, size_t *);
|
---|
117 | static int ahci_read_blocks(ddf_fun_t *, uint64_t, size_t, void *);
|
---|
118 | static int ahci_write_blocks(ddf_fun_t *, uint64_t, size_t, void *);
|
---|
119 |
|
---|
120 | static int ahci_identify_device(sata_dev_t *);
|
---|
121 | static int ahci_set_highest_ultra_dma_mode(sata_dev_t *);
|
---|
122 | static int ahci_rb_fpdma(sata_dev_t *, void *, uint64_t);
|
---|
123 | static int ahci_wb_fpdma(sata_dev_t *, void *, uint64_t);
|
---|
124 |
|
---|
125 | static void ahci_sata_devices_create(ahci_dev_t *, ddf_dev_t *);
|
---|
126 | static ahci_dev_t *ahci_ahci_create(ddf_dev_t *);
|
---|
127 | static void ahci_ahci_hw_start(ahci_dev_t *);
|
---|
128 |
|
---|
129 | static int ahci_dev_add(ddf_dev_t *);
|
---|
130 |
|
---|
131 | static void ahci_get_model_name(uint16_t *, char *);
|
---|
132 | static int ahci_enable_interrupt(int);
|
---|
133 |
|
---|
134 | static fibril_mutex_t sata_devices_count_lock;
|
---|
135 | static int sata_devices_count = 0;
|
---|
136 |
|
---|
137 | /*----------------------------------------------------------------------------*/
|
---|
138 | /*-- AHCI Interface ----------------------------------------------------------*/
|
---|
139 | /*----------------------------------------------------------------------------*/
|
---|
140 |
|
---|
141 | static ahci_iface_t ahci_interface = {
|
---|
142 | .get_sata_device_name = &ahci_get_sata_device_name,
|
---|
143 | .get_num_blocks = &ahci_get_num_blocks,
|
---|
144 | .get_block_size = &ahci_get_block_size,
|
---|
145 | .read_blocks = &ahci_read_blocks,
|
---|
146 | .write_blocks = &ahci_write_blocks
|
---|
147 | };
|
---|
148 |
|
---|
149 | static ddf_dev_ops_t ahci_ops = {
|
---|
150 | .interfaces[AHCI_DEV_IFACE] = &ahci_interface
|
---|
151 | };
|
---|
152 |
|
---|
153 | static driver_ops_t driver_ops = {
|
---|
154 | .dev_add = &ahci_dev_add
|
---|
155 | };
|
---|
156 |
|
---|
157 | static driver_t ahci_driver = {
|
---|
158 | .name = NAME,
|
---|
159 | .driver_ops = &driver_ops
|
---|
160 | };
|
---|
161 |
|
---|
162 | /** Get SATA device name.
|
---|
163 | *
|
---|
164 | * @param fun Device function handling the call.
|
---|
165 | * @param sata_dev_name_length Length of the sata_dev_name buffer.
|
---|
166 | * @param sata_dev_name Buffer for SATA device name.
|
---|
167 | *
|
---|
168 | * @return EOK.
|
---|
169 | *
|
---|
170 | */
|
---|
171 | static int ahci_get_sata_device_name(ddf_fun_t *fun,
|
---|
172 | size_t sata_dev_name_length, char *sata_dev_name)
|
---|
173 | {
|
---|
174 | sata_dev_t *sata = (sata_dev_t *) fun->driver_data;
|
---|
175 | str_cpy(sata_dev_name, sata_dev_name_length, sata->model);
|
---|
176 | return EOK;
|
---|
177 | }
|
---|
178 |
|
---|
179 | /** Get Number of blocks in SATA device.
|
---|
180 | *
|
---|
181 | * @param fun Device function handling the call.
|
---|
182 | * @param blocks Return number of blocks in SATA device.
|
---|
183 | *
|
---|
184 | * @return EOK.
|
---|
185 | *
|
---|
186 | */
|
---|
187 | static int ahci_get_num_blocks(ddf_fun_t *fun, uint64_t *num_blocks)
|
---|
188 | {
|
---|
189 | sata_dev_t *sata = (sata_dev_t *) fun->driver_data;
|
---|
190 | *num_blocks = sata->blocks;
|
---|
191 | return EOK;
|
---|
192 | }
|
---|
193 |
|
---|
194 | /** Get SATA device block size.
|
---|
195 | *
|
---|
196 | * @param fun Device function handling the call.
|
---|
197 | * @param block_size Return block size.
|
---|
198 | *
|
---|
199 | * @return EOK.
|
---|
200 | *
|
---|
201 | */
|
---|
202 | static int ahci_get_block_size(ddf_fun_t *fun, size_t *block_size)
|
---|
203 | {
|
---|
204 | sata_dev_t *sata = (sata_dev_t *) fun->driver_data;
|
---|
205 | *block_size = sata->block_size;
|
---|
206 | return EOK;
|
---|
207 | }
|
---|
208 |
|
---|
209 | /** Read data blocks into SATA device.
|
---|
210 | *
|
---|
211 | * @param fun Device function handling the call.
|
---|
212 | * @param blocknum Number of first block.
|
---|
213 | * @param count Number of blocks to read.
|
---|
214 | * @param buf Buffer for data.
|
---|
215 | *
|
---|
216 | * @return EOK if succeed, error code otherwise
|
---|
217 | *
|
---|
218 | */
|
---|
219 | static int ahci_read_blocks(ddf_fun_t *fun, uint64_t blocknum,
|
---|
220 | size_t count, void *buf)
|
---|
221 | {
|
---|
222 | sata_dev_t *sata = (sata_dev_t *) fun->driver_data;
|
---|
223 |
|
---|
224 | void *phys;
|
---|
225 | void *ibuf;
|
---|
226 | int rc = dmamem_map_anonymous(sata->block_size, AS_AREA_READ | AS_AREA_WRITE,
|
---|
227 | 0, &phys, (void **) &ibuf);
|
---|
228 | if (rc != EOK) {
|
---|
229 | ddf_msg(LVL_ERROR, "Cannot allocate read buffer.");
|
---|
230 | return rc;
|
---|
231 | }
|
---|
232 |
|
---|
233 | bzero(buf, sata->block_size);
|
---|
234 |
|
---|
235 | fibril_mutex_lock(&sata->lock);
|
---|
236 |
|
---|
237 | for (size_t cur = 0; cur < count; cur++) {
|
---|
238 | rc = ahci_rb_fpdma(sata, phys, blocknum + cur);
|
---|
239 | if (rc != EOK)
|
---|
240 | break;
|
---|
241 |
|
---|
242 | memcpy((void *) (((uint8_t *) buf) + (sata->block_size * cur)),
|
---|
243 | ibuf, sata->block_size);
|
---|
244 | }
|
---|
245 |
|
---|
246 | fibril_mutex_unlock(&sata->lock);
|
---|
247 | dmamem_unmap_anonymous(ibuf);
|
---|
248 |
|
---|
249 | return rc;
|
---|
250 | }
|
---|
251 |
|
---|
252 | /** Write data blocks into SATA device.
|
---|
253 | *
|
---|
254 | * @param fun Device function handling the call.
|
---|
255 | * @param blocknum Number of first block.
|
---|
256 | * @param count Number of blocks to write.
|
---|
257 | * @param buf Buffer with data.
|
---|
258 | *
|
---|
259 | * @return EOK if succeed, error code otherwise
|
---|
260 | *
|
---|
261 | */
|
---|
262 | static int ahci_write_blocks(ddf_fun_t *fun, uint64_t blocknum,
|
---|
263 | size_t count, void *buf)
|
---|
264 | {
|
---|
265 | sata_dev_t *sata = (sata_dev_t *) fun->driver_data;
|
---|
266 |
|
---|
267 | void *phys;
|
---|
268 | void *ibuf;
|
---|
269 | int rc = dmamem_map_anonymous(sata->block_size, AS_AREA_READ | AS_AREA_WRITE,
|
---|
270 | 0, &phys, (void **) &ibuf);
|
---|
271 | if (rc != EOK) {
|
---|
272 | ddf_msg(LVL_ERROR, "Cannot allocate write buffer.");
|
---|
273 | return rc;
|
---|
274 | }
|
---|
275 |
|
---|
276 | fibril_mutex_lock(&sata->lock);
|
---|
277 |
|
---|
278 | for (size_t cur = 0; cur < count; cur++) {
|
---|
279 | memcpy(ibuf, (void *) (((uint8_t *) buf) + (sata->block_size * cur)),
|
---|
280 | sata->block_size);
|
---|
281 | rc = ahci_wb_fpdma(sata, phys, blocknum + cur);
|
---|
282 | if (rc != EOK)
|
---|
283 | break;
|
---|
284 | }
|
---|
285 |
|
---|
286 | fibril_mutex_unlock(&sata->lock);
|
---|
287 | dmamem_unmap_anonymous(ibuf);
|
---|
288 |
|
---|
289 | return rc;
|
---|
290 | }
|
---|
291 |
|
---|
292 | /*----------------------------------------------------------------------------*/
|
---|
293 | /*-- AHCI Commands -----------------------------------------------------------*/
|
---|
294 | /*----------------------------------------------------------------------------*/
|
---|
295 |
|
---|
296 | /** Wait for interrupt event.
|
---|
297 | *
|
---|
298 | * @param sata SATA device structure.
|
---|
299 | *
|
---|
300 | * @return Value of interrupt state register.
|
---|
301 | *
|
---|
302 | */
|
---|
303 | static ahci_port_is_t ahci_wait_event(sata_dev_t *sata)
|
---|
304 | {
|
---|
305 | fibril_mutex_lock(&sata->event_lock);
|
---|
306 |
|
---|
307 | sata->event_pxis = 0;
|
---|
308 | while (sata->event_pxis == 0)
|
---|
309 | fibril_condvar_wait(&sata->event_condvar, &sata->event_lock);
|
---|
310 |
|
---|
311 | ahci_port_is_t pxis = sata->event_pxis;
|
---|
312 |
|
---|
313 | if (ahci_port_is_permanent_error(pxis))
|
---|
314 | sata->is_invalid_device = true;
|
---|
315 |
|
---|
316 | fibril_mutex_unlock(&sata->event_lock);
|
---|
317 |
|
---|
318 | return pxis;
|
---|
319 | }
|
---|
320 |
|
---|
321 | /** Set AHCI registers for identifying SATA device.
|
---|
322 | *
|
---|
323 | * @param sata SATA device structure.
|
---|
324 | * @param phys Physical address of working buffer.
|
---|
325 | *
|
---|
326 | */
|
---|
327 | static void ahci_identify_device_cmd(sata_dev_t *sata, void *phys)
|
---|
328 | {
|
---|
329 | volatile sata_std_command_frame_t *cmd =
|
---|
330 | (sata_std_command_frame_t *) sata->cmd_table;
|
---|
331 |
|
---|
332 | cmd->fis_type = SATA_CMD_FIS_TYPE;
|
---|
333 | cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
|
---|
334 | cmd->command = 0xec;
|
---|
335 | cmd->features = 0;
|
---|
336 | cmd->lba_lower = 0;
|
---|
337 | cmd->device = 0;
|
---|
338 | cmd->lba_upper = 0;
|
---|
339 | cmd->features_upper = 0;
|
---|
340 | cmd->count = 0;
|
---|
341 | cmd->reserved1 = 0;
|
---|
342 | cmd->control = 0;
|
---|
343 | cmd->reserved2 = 0;
|
---|
344 |
|
---|
345 | volatile ahci_cmd_prdt_t *prdt =
|
---|
346 | (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
|
---|
347 |
|
---|
348 | prdt->data_address_low = LO(phys);
|
---|
349 | prdt->data_address_upper = HI(phys);
|
---|
350 | prdt->reserved1 = 0;
|
---|
351 | prdt->dbc = SATA_IDENTIFY_DEVICE_BUFFER_LENGTH - 1;
|
---|
352 | prdt->reserved2 = 0;
|
---|
353 | prdt->ioc = 0;
|
---|
354 |
|
---|
355 | sata->cmd_header->prdtl = 1;
|
---|
356 | sata->cmd_header->flags =
|
---|
357 | AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
|
---|
358 | AHCI_CMDHDR_FLAGS_2DWCMD;
|
---|
359 | sata->cmd_header->bytesprocessed = 0;
|
---|
360 |
|
---|
361 | /* Run command. */
|
---|
362 | sata->port->pxsact |= 1;
|
---|
363 | sata->port->pxci |= 1;
|
---|
364 | }
|
---|
365 |
|
---|
366 | /** Set AHCI registers for identifying packet SATA device.
|
---|
367 | *
|
---|
368 | * @param sata SATA device structure.
|
---|
369 | * @param phys Physical address of working buffer.
|
---|
370 | *
|
---|
371 | */
|
---|
372 | static void ahci_identify_packet_device_cmd(sata_dev_t *sata, void *phys)
|
---|
373 | {
|
---|
374 | volatile sata_std_command_frame_t *cmd =
|
---|
375 | (sata_std_command_frame_t *) sata->cmd_table;
|
---|
376 |
|
---|
377 | cmd->fis_type = SATA_CMD_FIS_TYPE;
|
---|
378 | cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
|
---|
379 | cmd->command = 0xa1;
|
---|
380 | cmd->features = 0;
|
---|
381 | cmd->lba_lower = 0;
|
---|
382 | cmd->device = 0;
|
---|
383 | cmd->lba_upper = 0;
|
---|
384 | cmd->features_upper = 0;
|
---|
385 | cmd->count = 0;
|
---|
386 | cmd->reserved1 = 0;
|
---|
387 | cmd->control = 0;
|
---|
388 | cmd->reserved2 = 0;
|
---|
389 |
|
---|
390 | volatile ahci_cmd_prdt_t *prdt =
|
---|
391 | (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
|
---|
392 |
|
---|
393 | prdt->data_address_low = LO(phys);
|
---|
394 | prdt->data_address_upper = HI(phys);
|
---|
395 | prdt->reserved1 = 0;
|
---|
396 | prdt->dbc = SATA_IDENTIFY_DEVICE_BUFFER_LENGTH - 1;
|
---|
397 | prdt->reserved2 = 0;
|
---|
398 | prdt->ioc = 0;
|
---|
399 |
|
---|
400 | sata->cmd_header->prdtl = 1;
|
---|
401 | sata->cmd_header->flags =
|
---|
402 | AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
|
---|
403 | AHCI_CMDHDR_FLAGS_2DWCMD;
|
---|
404 | sata->cmd_header->bytesprocessed = 0;
|
---|
405 |
|
---|
406 | /* Run command. */
|
---|
407 | sata->port->pxsact |= 1;
|
---|
408 | sata->port->pxci |= 1;
|
---|
409 | }
|
---|
410 |
|
---|
411 | /** Fill device identification in SATA device structure.
|
---|
412 | *
|
---|
413 | * @param sata SATA device structure.
|
---|
414 | *
|
---|
415 | * @return EOK if succeed, error code otherwise.
|
---|
416 | *
|
---|
417 | */
|
---|
418 | static int ahci_identify_device(sata_dev_t *sata)
|
---|
419 | {
|
---|
420 | if (sata->is_invalid_device) {
|
---|
421 | ddf_msg(LVL_ERROR,
|
---|
422 | "Identify command device on invalid device");
|
---|
423 | return EINTR;
|
---|
424 | }
|
---|
425 |
|
---|
426 | void *phys;
|
---|
427 | sata_identify_data_t *idata;
|
---|
428 | int rc = dmamem_map_anonymous(SATA_IDENTIFY_DEVICE_BUFFER_LENGTH,
|
---|
429 | AS_AREA_READ | AS_AREA_WRITE, 0, &phys, (void **) &idata);
|
---|
430 | if (rc != EOK) {
|
---|
431 | ddf_msg(LVL_ERROR, "Cannot allocate buffer to identify device.");
|
---|
432 | return rc;
|
---|
433 | }
|
---|
434 |
|
---|
435 | bzero(idata, SATA_IDENTIFY_DEVICE_BUFFER_LENGTH);
|
---|
436 |
|
---|
437 | fibril_mutex_lock(&sata->lock);
|
---|
438 |
|
---|
439 | ahci_identify_device_cmd(sata, phys);
|
---|
440 | ahci_port_is_t pxis = ahci_wait_event(sata);
|
---|
441 |
|
---|
442 | if (sata->is_invalid_device) {
|
---|
443 | ddf_msg(LVL_ERROR,
|
---|
444 | "Unrecoverable error during ata identify device");
|
---|
445 | goto error;
|
---|
446 | }
|
---|
447 |
|
---|
448 | if (ahci_port_is_tfes(pxis)) {
|
---|
449 | ahci_identify_packet_device_cmd(sata, phys);
|
---|
450 | pxis = ahci_wait_event(sata);
|
---|
451 |
|
---|
452 | if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
|
---|
453 | ddf_msg(LVL_ERROR,
|
---|
454 | "Unrecoverable error during ata identify packet device");
|
---|
455 | goto error;
|
---|
456 | }
|
---|
457 |
|
---|
458 | sata->is_packet_device = true;
|
---|
459 | }
|
---|
460 |
|
---|
461 | ahci_get_model_name(idata->model_name, sata->model);
|
---|
462 |
|
---|
463 | /*
|
---|
464 | * Due to QEMU limitation (as of 2012-06-22),
|
---|
465 | * only NCQ FPDMA mode is supported.
|
---|
466 | */
|
---|
467 | if ((idata->sata_cap & sata_np_cap_ncq) == 0) {
|
---|
468 | ddf_msg(LVL_ERROR, "%s: NCQ must be supported", sata->model);
|
---|
469 | goto error;
|
---|
470 | }
|
---|
471 |
|
---|
472 | uint16_t logsec = idata->physical_logic_sector_size;
|
---|
473 | if ((logsec & 0xc000) == 0x4000) {
|
---|
474 | /* Length of sector may be larger than 512 B */
|
---|
475 | if (logsec & 0x0100) {
|
---|
476 | /* Size of sector is larger than 512 B */
|
---|
477 | ddf_msg(LVL_ERROR,
|
---|
478 | "%s: Sector length other than 512 B not supported",
|
---|
479 | sata->model);
|
---|
480 | goto error;
|
---|
481 | }
|
---|
482 |
|
---|
483 | if ((logsec & 0x0200) && ((logsec & 0x000f) != 0)) {
|
---|
484 | /* Physical sectors per logical sector is greather than 1 */
|
---|
485 | ddf_msg(LVL_ERROR,
|
---|
486 | "%s: Sector length other than 512 B not supported",
|
---|
487 | sata->model);
|
---|
488 | goto error;
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | if (sata->is_packet_device) {
|
---|
493 | /*
|
---|
494 | * Due to QEMU limitation (as of 2012-06-22),
|
---|
495 | * only NCQ FPDMA mode supported - block size is
|
---|
496 | * 512 B, not 2048 B!
|
---|
497 | */
|
---|
498 | sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
|
---|
499 | sata->blocks = 0;
|
---|
500 | } else {
|
---|
501 | sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
|
---|
502 |
|
---|
503 | if ((idata->caps & sata_rd_cap_lba) == 0) {
|
---|
504 | ddf_msg(LVL_ERROR, "%s: LBA for NCQ must be supported",
|
---|
505 | sata->model);
|
---|
506 | goto error;
|
---|
507 | } else if ((idata->cmd_set1 & sata_cs1_addr48) == 0) {
|
---|
508 | sata->blocks = (uint32_t) idata->total_lba28_0 |
|
---|
509 | ((uint32_t) idata->total_lba28_1 << 16);
|
---|
510 | } else {
|
---|
511 | /* Device supports LBA-48 addressing. */
|
---|
512 | sata->blocks = (uint64_t) idata->total_lba48_0 |
|
---|
513 | ((uint64_t) idata->total_lba48_1 << 16) |
|
---|
514 | ((uint64_t) idata->total_lba48_2 << 32) |
|
---|
515 | ((uint64_t) idata->total_lba48_3 << 48);
|
---|
516 | }
|
---|
517 | }
|
---|
518 |
|
---|
519 | uint8_t udma_mask = idata->udma & 0x007f;
|
---|
520 | sata->highest_udma_mode = (uint8_t) -1;
|
---|
521 | if (udma_mask == 0) {
|
---|
522 | ddf_msg(LVL_ERROR,
|
---|
523 | "%s: UDMA mode for NCQ FPDMA mode must be supported",
|
---|
524 | sata->model);
|
---|
525 | goto error;
|
---|
526 | } else {
|
---|
527 | for (uint8_t i = 0; i < 7; i++) {
|
---|
528 | if (udma_mask & (1 << i))
|
---|
529 | sata->highest_udma_mode = i;
|
---|
530 | }
|
---|
531 | }
|
---|
532 |
|
---|
533 | fibril_mutex_unlock(&sata->lock);
|
---|
534 | dmamem_unmap_anonymous(idata);
|
---|
535 |
|
---|
536 | return EOK;
|
---|
537 |
|
---|
538 | error:
|
---|
539 | fibril_mutex_unlock(&sata->lock);
|
---|
540 | dmamem_unmap_anonymous(idata);
|
---|
541 |
|
---|
542 | return EINTR;
|
---|
543 | }
|
---|
544 |
|
---|
545 | /** Set AHCI registers for setting SATA device transfer mode.
|
---|
546 | *
|
---|
547 | * @param sata SATA device structure.
|
---|
548 | * @param phys Physical address of working buffer.
|
---|
549 | * @param mode Required mode.
|
---|
550 | *
|
---|
551 | */
|
---|
552 | static void ahci_set_mode_cmd(sata_dev_t *sata, void* phys, uint8_t mode)
|
---|
553 | {
|
---|
554 | volatile sata_std_command_frame_t *cmd =
|
---|
555 | (sata_std_command_frame_t *) sata->cmd_table;
|
---|
556 |
|
---|
557 | cmd->fis_type = SATA_CMD_FIS_TYPE;
|
---|
558 | cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
|
---|
559 | cmd->command = 0xef;
|
---|
560 | cmd->features = 0x03;
|
---|
561 | cmd->lba_lower = 0;
|
---|
562 | cmd->device = 0;
|
---|
563 | cmd->lba_upper = 0;
|
---|
564 | cmd->features_upper = 0;
|
---|
565 | cmd->count = mode;
|
---|
566 | cmd->reserved1 = 0;
|
---|
567 | cmd->control = 0;
|
---|
568 | cmd->reserved2 = 0;
|
---|
569 |
|
---|
570 | volatile ahci_cmd_prdt_t *prdt =
|
---|
571 | (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
|
---|
572 |
|
---|
573 | prdt->data_address_low = LO(phys);
|
---|
574 | prdt->data_address_upper = HI(phys);
|
---|
575 | prdt->reserved1 = 0;
|
---|
576 | prdt->dbc = SATA_SET_FEATURE_BUFFER_LENGTH - 1;
|
---|
577 | prdt->reserved2 = 0;
|
---|
578 | prdt->ioc = 0;
|
---|
579 |
|
---|
580 | sata->cmd_header->prdtl = 1;
|
---|
581 | sata->cmd_header->flags =
|
---|
582 | AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
|
---|
583 | AHCI_CMDHDR_FLAGS_2DWCMD;
|
---|
584 | sata->cmd_header->bytesprocessed = 0;
|
---|
585 |
|
---|
586 | /* Run command. */
|
---|
587 | sata->port->pxsact |= 1;
|
---|
588 | sata->port->pxci |= 1;
|
---|
589 | }
|
---|
590 |
|
---|
591 | /** Set highest ultra DMA mode supported by SATA device.
|
---|
592 | *
|
---|
593 | * @param sata SATA device structure.
|
---|
594 | *
|
---|
595 | * @return EOK if succeed, error code otherwise
|
---|
596 | *
|
---|
597 | */
|
---|
598 | static int ahci_set_highest_ultra_dma_mode(sata_dev_t *sata)
|
---|
599 | {
|
---|
600 | if (sata->is_invalid_device) {
|
---|
601 | ddf_msg(LVL_ERROR,
|
---|
602 | "%s: Setting highest UDMA mode on invalid device",
|
---|
603 | sata->model);
|
---|
604 | return EINTR;
|
---|
605 | }
|
---|
606 |
|
---|
607 | if (sata->highest_udma_mode == (uint8_t) -1) {
|
---|
608 | ddf_msg(LVL_ERROR,
|
---|
609 | "%s: No AHCI UDMA support.", sata->model);
|
---|
610 | return EINTR;
|
---|
611 | }
|
---|
612 |
|
---|
613 | if (sata->highest_udma_mode > 6) {
|
---|
614 | ddf_msg(LVL_ERROR,
|
---|
615 | "%s: Unknown AHCI UDMA mode.", sata->model);
|
---|
616 | return EINTR;
|
---|
617 | }
|
---|
618 |
|
---|
619 | void *phys;
|
---|
620 | sata_identify_data_t *idata;
|
---|
621 | int rc = dmamem_map_anonymous(SATA_SET_FEATURE_BUFFER_LENGTH,
|
---|
622 | AS_AREA_READ | AS_AREA_WRITE, 0, &phys, (void **) &idata);
|
---|
623 | if (rc != EOK) {
|
---|
624 | ddf_msg(LVL_ERROR, "Cannot allocate buffer for device set mode.");
|
---|
625 | return rc;
|
---|
626 | }
|
---|
627 |
|
---|
628 | bzero(idata, SATA_SET_FEATURE_BUFFER_LENGTH);
|
---|
629 |
|
---|
630 | fibril_mutex_lock(&sata->lock);
|
---|
631 |
|
---|
632 | uint8_t mode = 0x40 | (sata->highest_udma_mode & 0x07);
|
---|
633 | ahci_set_mode_cmd(sata, phys, mode);
|
---|
634 | ahci_port_is_t pxis = ahci_wait_event(sata);
|
---|
635 |
|
---|
636 | if (sata->is_invalid_device) {
|
---|
637 | ddf_msg(LVL_ERROR,
|
---|
638 | "%s: Unrecoverable error during set highest UDMA mode",
|
---|
639 | sata->model);
|
---|
640 | goto error;
|
---|
641 | }
|
---|
642 |
|
---|
643 | if (ahci_port_is_error(pxis)) {
|
---|
644 | ddf_msg(LVL_ERROR,
|
---|
645 | "%s: Error during set highest UDMA mode", sata->model);
|
---|
646 | goto error;
|
---|
647 | }
|
---|
648 |
|
---|
649 | fibril_mutex_unlock(&sata->lock);
|
---|
650 | dmamem_unmap_anonymous(idata);
|
---|
651 |
|
---|
652 | return EOK;
|
---|
653 |
|
---|
654 | error:
|
---|
655 | fibril_mutex_unlock(&sata->lock);
|
---|
656 | dmamem_unmap_anonymous(idata);
|
---|
657 |
|
---|
658 | return EINTR;
|
---|
659 | }
|
---|
660 |
|
---|
661 | /** Set AHCI registers for reading one sector from the SATA device using FPDMA.
|
---|
662 | *
|
---|
663 | * @param sata SATA device structure.
|
---|
664 | * @param phys Physical address of buffer for sector data.
|
---|
665 | * @param blocknum Block number to read.
|
---|
666 | *
|
---|
667 | */
|
---|
668 | static void ahci_rb_fpdma_cmd(sata_dev_t *sata, void *phys, uint64_t blocknum)
|
---|
669 | {
|
---|
670 | volatile sata_ncq_command_frame_t *cmd =
|
---|
671 | (sata_ncq_command_frame_t *) sata->cmd_table;
|
---|
672 |
|
---|
673 | cmd->fis_type = SATA_CMD_FIS_TYPE;
|
---|
674 | cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
|
---|
675 | cmd->command = 0x60;
|
---|
676 | cmd->tag = 0;
|
---|
677 | cmd->control = 0;
|
---|
678 |
|
---|
679 | cmd->reserved1 = 0;
|
---|
680 | cmd->reserved2 = 0;
|
---|
681 | cmd->reserved3 = 0;
|
---|
682 | cmd->reserved4 = 0;
|
---|
683 | cmd->reserved5 = 0;
|
---|
684 | cmd->reserved6 = 0;
|
---|
685 |
|
---|
686 | cmd->sector_count_low = 1;
|
---|
687 | cmd->sector_count_high = 0;
|
---|
688 |
|
---|
689 | cmd->lba0 = blocknum & 0xff;
|
---|
690 | cmd->lba1 = (blocknum >> 8) & 0xff;
|
---|
691 | cmd->lba2 = (blocknum >> 16) & 0xff;
|
---|
692 | cmd->lba3 = (blocknum >> 24) & 0xff;
|
---|
693 | cmd->lba4 = (blocknum >> 32) & 0xff;
|
---|
694 | cmd->lba5 = (blocknum >> 40) & 0xff;
|
---|
695 |
|
---|
696 | volatile ahci_cmd_prdt_t *prdt =
|
---|
697 | (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
|
---|
698 |
|
---|
699 | prdt->data_address_low = LO(phys);
|
---|
700 | prdt->data_address_upper = HI(phys);
|
---|
701 | prdt->reserved1 = 0;
|
---|
702 | prdt->dbc = sata->block_size - 1;
|
---|
703 | prdt->reserved2 = 0;
|
---|
704 | prdt->ioc = 0;
|
---|
705 |
|
---|
706 | sata->cmd_header->prdtl = 1;
|
---|
707 | sata->cmd_header->flags =
|
---|
708 | AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
|
---|
709 | AHCI_CMDHDR_FLAGS_5DWCMD;
|
---|
710 | sata->cmd_header->bytesprocessed = 0;
|
---|
711 |
|
---|
712 | sata->port->pxsact |= 1;
|
---|
713 | sata->port->pxci |= 1;
|
---|
714 | }
|
---|
715 |
|
---|
716 | /** Read one sector from the SATA device using FPDMA.
|
---|
717 | *
|
---|
718 | * @param sata SATA device structure.
|
---|
719 | * @param phys Physical address of buffer for sector data.
|
---|
720 | * @param blocknum Block number to read.
|
---|
721 | *
|
---|
722 | * @return EOK if succeed, error code otherwise
|
---|
723 | *
|
---|
724 | */
|
---|
725 | static int ahci_rb_fpdma(sata_dev_t *sata, void *phys, uint64_t blocknum)
|
---|
726 | {
|
---|
727 | if (sata->is_invalid_device) {
|
---|
728 | ddf_msg(LVL_ERROR,
|
---|
729 | "%s: FPDMA read from invalid device", sata->model);
|
---|
730 | return EINTR;
|
---|
731 | }
|
---|
732 |
|
---|
733 | ahci_rb_fpdma_cmd(sata, phys, blocknum);
|
---|
734 | ahci_port_is_t pxis = ahci_wait_event(sata);
|
---|
735 |
|
---|
736 | if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
|
---|
737 | ddf_msg(LVL_ERROR,
|
---|
738 | "%s: Unrecoverable error during FPDMA read", sata->model);
|
---|
739 | return EINTR;
|
---|
740 | }
|
---|
741 |
|
---|
742 | return EOK;
|
---|
743 | }
|
---|
744 |
|
---|
745 | /** Set AHCI registers for writing one sector to the SATA device, use FPDMA.
|
---|
746 | *
|
---|
747 | * @param sata SATA device structure.
|
---|
748 | * @param phys Physical address of buffer with sector data.
|
---|
749 | * @param blocknum Block number to write.
|
---|
750 | *
|
---|
751 | * @return EOK if succeed, error code otherwise
|
---|
752 | *
|
---|
753 | */
|
---|
754 | static void ahci_wb_fpdma_cmd(sata_dev_t *sata, void *phys, uint64_t blocknum)
|
---|
755 | {
|
---|
756 | volatile sata_ncq_command_frame_t *cmd =
|
---|
757 | (sata_ncq_command_frame_t *) sata->cmd_table;
|
---|
758 |
|
---|
759 | cmd->fis_type = SATA_CMD_FIS_TYPE;
|
---|
760 | cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
|
---|
761 | cmd->command = 0x61;
|
---|
762 | cmd->tag = 0;
|
---|
763 | cmd->control = 0;
|
---|
764 |
|
---|
765 | cmd->reserved1 = 0;
|
---|
766 | cmd->reserved2 = 0;
|
---|
767 | cmd->reserved3 = 0;
|
---|
768 | cmd->reserved4 = 0;
|
---|
769 | cmd->reserved5 = 0;
|
---|
770 | cmd->reserved6 = 0;
|
---|
771 |
|
---|
772 | cmd->sector_count_low = 1;
|
---|
773 | cmd->sector_count_high = 0;
|
---|
774 |
|
---|
775 | cmd->lba0 = blocknum & 0xff;
|
---|
776 | cmd->lba1 = (blocknum >> 8) & 0xff;
|
---|
777 | cmd->lba2 = (blocknum >> 16) & 0xff;
|
---|
778 | cmd->lba3 = (blocknum >> 24) & 0xff;
|
---|
779 | cmd->lba4 = (blocknum >> 32) & 0xff;
|
---|
780 | cmd->lba5 = (blocknum >> 40) & 0xff;
|
---|
781 |
|
---|
782 | volatile ahci_cmd_prdt_t *prdt =
|
---|
783 | (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
|
---|
784 |
|
---|
785 | prdt->data_address_low = LO(phys);
|
---|
786 | prdt->data_address_upper = HI(phys);
|
---|
787 | prdt->reserved1 = 0;
|
---|
788 | prdt->dbc = sata->block_size - 1;
|
---|
789 | prdt->reserved2 = 0;
|
---|
790 | prdt->ioc = 0;
|
---|
791 |
|
---|
792 | sata->cmd_header->prdtl = 1;
|
---|
793 | sata->cmd_header->flags =
|
---|
794 | AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
|
---|
795 | AHCI_CMDHDR_FLAGS_WRITE |
|
---|
796 | AHCI_CMDHDR_FLAGS_5DWCMD;
|
---|
797 | sata->cmd_header->bytesprocessed = 0;
|
---|
798 |
|
---|
799 | sata->port->pxsact |= 1;
|
---|
800 | sata->port->pxci |= 1;
|
---|
801 | }
|
---|
802 |
|
---|
803 | /** Write one sector into the SATA device, use FPDMA.
|
---|
804 | *
|
---|
805 | * @param sata SATA device structure.
|
---|
806 | * @param phys Physical addres of buffer with sector data.
|
---|
807 | * @param blocknum Block number to write.
|
---|
808 | *
|
---|
809 | * @return EOK if succeed, error code otherwise
|
---|
810 | *
|
---|
811 | */
|
---|
812 | static int ahci_wb_fpdma(sata_dev_t *sata, void *phys, uint64_t blocknum)
|
---|
813 | {
|
---|
814 | if (sata->is_invalid_device) {
|
---|
815 | ddf_msg(LVL_ERROR,
|
---|
816 | "%s: FPDMA write to invalid device", sata->model);
|
---|
817 | return EINTR;
|
---|
818 | }
|
---|
819 |
|
---|
820 | ahci_wb_fpdma_cmd(sata, phys, blocknum);
|
---|
821 | ahci_port_is_t pxis = ahci_wait_event(sata);
|
---|
822 |
|
---|
823 | if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
|
---|
824 | ddf_msg(LVL_ERROR,
|
---|
825 | "%s: Unrecoverable error during FPDMA write", sata->model);
|
---|
826 | return EINTR;
|
---|
827 | }
|
---|
828 |
|
---|
829 | return EOK;
|
---|
830 | }
|
---|
831 |
|
---|
832 | /*----------------------------------------------------------------------------*/
|
---|
833 | /*-- Interrupts handling -----------------------------------------------------*/
|
---|
834 | /*----------------------------------------------------------------------------*/
|
---|
835 |
|
---|
836 | static irq_pio_range_t ahci_ranges[] = {
|
---|
837 | {
|
---|
838 | .base = 0,
|
---|
839 | .size = 0,
|
---|
840 | }
|
---|
841 | };
|
---|
842 |
|
---|
843 | static irq_cmd_t ahci_cmds[] = {
|
---|
844 | AHCI_PORT_CMDS(0),
|
---|
845 | AHCI_PORT_CMDS(1),
|
---|
846 | AHCI_PORT_CMDS(2),
|
---|
847 | AHCI_PORT_CMDS(3),
|
---|
848 | AHCI_PORT_CMDS(4),
|
---|
849 | AHCI_PORT_CMDS(5),
|
---|
850 | AHCI_PORT_CMDS(6),
|
---|
851 | AHCI_PORT_CMDS(7),
|
---|
852 | AHCI_PORT_CMDS(8),
|
---|
853 | AHCI_PORT_CMDS(9),
|
---|
854 | AHCI_PORT_CMDS(10),
|
---|
855 | AHCI_PORT_CMDS(11),
|
---|
856 | AHCI_PORT_CMDS(12),
|
---|
857 | AHCI_PORT_CMDS(13),
|
---|
858 | AHCI_PORT_CMDS(14),
|
---|
859 | AHCI_PORT_CMDS(15),
|
---|
860 | AHCI_PORT_CMDS(16),
|
---|
861 | AHCI_PORT_CMDS(17),
|
---|
862 | AHCI_PORT_CMDS(18),
|
---|
863 | AHCI_PORT_CMDS(19),
|
---|
864 | AHCI_PORT_CMDS(20),
|
---|
865 | AHCI_PORT_CMDS(21),
|
---|
866 | AHCI_PORT_CMDS(22),
|
---|
867 | AHCI_PORT_CMDS(23),
|
---|
868 | AHCI_PORT_CMDS(24),
|
---|
869 | AHCI_PORT_CMDS(25),
|
---|
870 | AHCI_PORT_CMDS(26),
|
---|
871 | AHCI_PORT_CMDS(27),
|
---|
872 | AHCI_PORT_CMDS(28),
|
---|
873 | AHCI_PORT_CMDS(29),
|
---|
874 | AHCI_PORT_CMDS(30),
|
---|
875 | AHCI_PORT_CMDS(31)
|
---|
876 | };
|
---|
877 |
|
---|
878 | /** AHCI interrupt handler.
|
---|
879 | *
|
---|
880 | * @param dev DDF device structure.
|
---|
881 | * @param iid The IPC call id.
|
---|
882 | * @param icall The IPC call structure.
|
---|
883 | *
|
---|
884 | */
|
---|
885 | static void ahci_interrupt(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *icall)
|
---|
886 | {
|
---|
887 | ahci_dev_t *ahci = (ahci_dev_t *) dev->driver_data;
|
---|
888 | unsigned int port = IPC_GET_ARG1(*icall);
|
---|
889 | ahci_port_is_t pxis = IPC_GET_ARG2(*icall);
|
---|
890 |
|
---|
891 | if (port >= AHCI_MAX_PORTS)
|
---|
892 | return;
|
---|
893 |
|
---|
894 | sata_dev_t *sata = (sata_dev_t *) ahci->sata_devs[port];
|
---|
895 | if (sata == NULL)
|
---|
896 | return;
|
---|
897 |
|
---|
898 | /* Evaluate port event */
|
---|
899 | if ((ahci_port_is_end_of_operation(pxis)) ||
|
---|
900 | (ahci_port_is_error(pxis))) {
|
---|
901 | fibril_mutex_lock(&sata->event_lock);
|
---|
902 |
|
---|
903 | sata->event_pxis = pxis;
|
---|
904 | fibril_condvar_signal(&sata->event_condvar);
|
---|
905 |
|
---|
906 | fibril_mutex_unlock(&sata->event_lock);
|
---|
907 | }
|
---|
908 | }
|
---|
909 |
|
---|
910 | /*----------------------------------------------------------------------------*/
|
---|
911 | /*-- AHCI and SATA device creating and initializing routines -----------------*/
|
---|
912 | /*----------------------------------------------------------------------------*/
|
---|
913 |
|
---|
914 | /** Allocate SATA device structure with buffers for hardware.
|
---|
915 | *
|
---|
916 | * @param port AHCI port structure
|
---|
917 | *
|
---|
918 | * @return SATA device structure if succeed, NULL otherwise.
|
---|
919 | *
|
---|
920 | */
|
---|
921 | static sata_dev_t *ahci_sata_allocate(volatile ahci_port_t *port)
|
---|
922 | {
|
---|
923 | size_t size = 4096;
|
---|
924 | void *phys = NULL;
|
---|
925 | void *virt_fb = NULL;
|
---|
926 | void *virt_cmd = NULL;
|
---|
927 | void *virt_table = NULL;
|
---|
928 |
|
---|
929 | sata_dev_t *sata = malloc(sizeof(sata_dev_t));
|
---|
930 | if (sata == NULL)
|
---|
931 | return NULL;
|
---|
932 |
|
---|
933 | bzero(sata, sizeof(sata_dev_t));
|
---|
934 |
|
---|
935 | sata->port = port;
|
---|
936 |
|
---|
937 | /* Allocate and init retfis structure. */
|
---|
938 | int rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
|
---|
939 | &phys, &virt_fb);
|
---|
940 | if (rc != EOK)
|
---|
941 | goto error_retfis;
|
---|
942 |
|
---|
943 | bzero(virt_fb, size);
|
---|
944 | sata->port->pxfbu = HI(phys);
|
---|
945 | sata->port->pxfb = LO(phys);
|
---|
946 |
|
---|
947 | /* Allocate and init command header structure. */
|
---|
948 | rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
|
---|
949 | &phys, &virt_cmd);
|
---|
950 | if (rc != EOK)
|
---|
951 | goto error_cmd;
|
---|
952 |
|
---|
953 | bzero(virt_cmd, size);
|
---|
954 | sata->port->pxclbu = HI(phys);
|
---|
955 | sata->port->pxclb = LO(phys);
|
---|
956 | sata->cmd_header = (ahci_cmdhdr_t *) virt_cmd;
|
---|
957 |
|
---|
958 | /* Allocate and init command table structure. */
|
---|
959 | rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
|
---|
960 | &phys, &virt_table);
|
---|
961 | if (rc != EOK)
|
---|
962 | goto error_table;
|
---|
963 |
|
---|
964 | bzero(virt_table, size);
|
---|
965 | sata->cmd_header->cmdtableu = HI(phys);
|
---|
966 | sata->cmd_header->cmdtable = LO(phys);
|
---|
967 | sata->cmd_table = (uint32_t*) virt_table;
|
---|
968 |
|
---|
969 | return sata;
|
---|
970 |
|
---|
971 | error_table:
|
---|
972 | dmamem_unmap(virt_cmd, size);
|
---|
973 | error_cmd:
|
---|
974 | dmamem_unmap(virt_fb, size);
|
---|
975 | error_retfis:
|
---|
976 | free(sata);
|
---|
977 | return NULL;
|
---|
978 | }
|
---|
979 |
|
---|
980 | /** Initialize and start SATA hardware device.
|
---|
981 | *
|
---|
982 | * @param sata SATA device structure.
|
---|
983 | *
|
---|
984 | */
|
---|
985 | static void ahci_sata_hw_start(sata_dev_t *sata)
|
---|
986 | {
|
---|
987 | ahci_port_cmd_t pxcmd;
|
---|
988 |
|
---|
989 | pxcmd.u32 = sata->port->pxcmd;
|
---|
990 |
|
---|
991 | /* Frame receiver disabled. */
|
---|
992 | pxcmd.fre = 0;
|
---|
993 |
|
---|
994 | /* Disable process the command list. */
|
---|
995 | pxcmd.st = 0;
|
---|
996 |
|
---|
997 | sata->port->pxcmd = pxcmd.u32;
|
---|
998 |
|
---|
999 | /* Clear interrupt status. */
|
---|
1000 | sata->port->pxis = 0xffffffff;
|
---|
1001 |
|
---|
1002 | /* Clear error status. */
|
---|
1003 | sata->port->pxserr = 0xffffffff;
|
---|
1004 |
|
---|
1005 | /* Enable all interrupts. */
|
---|
1006 | sata->port->pxie = 0xffffffff;
|
---|
1007 |
|
---|
1008 | /* Frame receiver enabled. */
|
---|
1009 | pxcmd.fre = 1;
|
---|
1010 |
|
---|
1011 | /* Enable process the command list. */
|
---|
1012 | pxcmd.st = 1;
|
---|
1013 |
|
---|
1014 | sata->port->pxcmd = pxcmd.u32;
|
---|
1015 | }
|
---|
1016 |
|
---|
1017 | /** Create and initialize connected SATA structure device
|
---|
1018 | *
|
---|
1019 | * @param ahci AHCI device structure.
|
---|
1020 | * @param dev DDF device structure.
|
---|
1021 | * @param port AHCI port structure.
|
---|
1022 | * @param port_num Number of AHCI port with existing SATA device.
|
---|
1023 | *
|
---|
1024 | * @return EOK if succeed, error code otherwise.
|
---|
1025 | *
|
---|
1026 | */
|
---|
1027 | static int ahci_sata_create(ahci_dev_t *ahci, ddf_dev_t *dev,
|
---|
1028 | volatile ahci_port_t *port, unsigned int port_num)
|
---|
1029 | {
|
---|
1030 | ddf_fun_t *fun = NULL;
|
---|
1031 | sata_dev_t *sata = ahci_sata_allocate(port);
|
---|
1032 | if (sata == NULL)
|
---|
1033 | return EINTR;
|
---|
1034 |
|
---|
1035 | /* Set pointers between SATA and AHCI structures. */
|
---|
1036 | sata->ahci = ahci;
|
---|
1037 | sata->port_num = port_num;
|
---|
1038 | ahci->sata_devs[port_num] = sata;
|
---|
1039 |
|
---|
1040 | /* Initialize synchronization structures */
|
---|
1041 | fibril_mutex_initialize(&sata->lock);
|
---|
1042 | fibril_mutex_initialize(&sata->event_lock);
|
---|
1043 | fibril_condvar_initialize(&sata->event_condvar);
|
---|
1044 |
|
---|
1045 | ahci_sata_hw_start(sata);
|
---|
1046 |
|
---|
1047 | /* Identify device. */
|
---|
1048 | if (ahci_identify_device(sata) != EOK)
|
---|
1049 | goto error;
|
---|
1050 |
|
---|
1051 | /* Set required UDMA mode */
|
---|
1052 | if (ahci_set_highest_ultra_dma_mode(sata) != EOK)
|
---|
1053 | goto error;
|
---|
1054 |
|
---|
1055 | /* Add device to the system */
|
---|
1056 | char sata_dev_name[16];
|
---|
1057 | snprintf(sata_dev_name, 16, "ahci_%u", sata_devices_count);
|
---|
1058 |
|
---|
1059 | fibril_mutex_lock(&sata_devices_count_lock);
|
---|
1060 | sata_devices_count++;
|
---|
1061 | fibril_mutex_unlock(&sata_devices_count_lock);
|
---|
1062 |
|
---|
1063 | fun = ddf_fun_create(dev, fun_exposed, sata_dev_name);
|
---|
1064 | if (fun == NULL) {
|
---|
1065 | ddf_msg(LVL_ERROR, "Failed creating function.");
|
---|
1066 | goto error;
|
---|
1067 | }
|
---|
1068 |
|
---|
1069 | fun->ops = &ahci_ops;
|
---|
1070 | fun->driver_data = sata;
|
---|
1071 | int rc = ddf_fun_bind(fun);
|
---|
1072 | if (rc != EOK) {
|
---|
1073 | ddf_msg(LVL_ERROR, "Failed binding function.");
|
---|
1074 | goto error;
|
---|
1075 | }
|
---|
1076 |
|
---|
1077 | return EOK;
|
---|
1078 |
|
---|
1079 | error:
|
---|
1080 | sata->is_invalid_device = true;
|
---|
1081 | if (fun != NULL)
|
---|
1082 | ddf_fun_destroy(fun);
|
---|
1083 |
|
---|
1084 | return EINTR;
|
---|
1085 | }
|
---|
1086 |
|
---|
1087 | /** Create and initialize all SATA structure devices for connected SATA drives.
|
---|
1088 | *
|
---|
1089 | * @param ahci AHCI device structure.
|
---|
1090 | * @param dev DDF device structure.
|
---|
1091 | *
|
---|
1092 | */
|
---|
1093 | static void ahci_sata_devices_create(ahci_dev_t *ahci, ddf_dev_t *dev)
|
---|
1094 | {
|
---|
1095 | for (unsigned int port_num = 0; port_num < AHCI_MAX_PORTS; port_num++) {
|
---|
1096 | /* Active ports only */
|
---|
1097 | if (!(ahci->memregs->ghc.pi & (1 << port_num)))
|
---|
1098 | continue;
|
---|
1099 |
|
---|
1100 | volatile ahci_port_t *port = ahci->memregs->ports + port_num;
|
---|
1101 |
|
---|
1102 | /* Active devices only */
|
---|
1103 | ahci_port_ssts_t pxssts;
|
---|
1104 | pxssts.u32 = port->pxssts;
|
---|
1105 | if (pxssts.det != AHCI_PORT_SSTS_DET_ACTIVE)
|
---|
1106 | continue;
|
---|
1107 |
|
---|
1108 | ahci_sata_create(ahci, dev, port, port_num);
|
---|
1109 | }
|
---|
1110 | }
|
---|
1111 |
|
---|
1112 | /** Create AHCI device structure, intialize it and register interrupt routine.
|
---|
1113 | *
|
---|
1114 | * @param dev DDF device structure.
|
---|
1115 | *
|
---|
1116 | * @return AHCI device structure if succeed, NULL otherwise.
|
---|
1117 | *
|
---|
1118 | */
|
---|
1119 | static ahci_dev_t *ahci_ahci_create(ddf_dev_t *dev)
|
---|
1120 | {
|
---|
1121 | ahci_dev_t *ahci = malloc(sizeof(ahci_dev_t));
|
---|
1122 | if (!ahci)
|
---|
1123 | return NULL;
|
---|
1124 |
|
---|
1125 | bzero(ahci, sizeof(ahci_dev_t));
|
---|
1126 |
|
---|
1127 | ahci->dev = dev;
|
---|
1128 |
|
---|
1129 | hw_res_list_parsed_t hw_res_parsed;
|
---|
1130 | hw_res_list_parsed_init(&hw_res_parsed);
|
---|
1131 | if (hw_res_get_list_parsed(dev->parent_sess, &hw_res_parsed, 0) != EOK)
|
---|
1132 | goto error_get_res_parsed;
|
---|
1133 |
|
---|
1134 | /* Map AHCI registers. */
|
---|
1135 | ahci->memregs = NULL;
|
---|
1136 |
|
---|
1137 | physmem_map((void *) (size_t) (hw_res_parsed.mem_ranges.ranges[0].address),
|
---|
1138 | AHCI_MEMREGS_PAGES_COUNT, AS_AREA_READ | AS_AREA_WRITE,
|
---|
1139 | (void **) &ahci->memregs);
|
---|
1140 | if (ahci->memregs == NULL)
|
---|
1141 | goto error_map_registers;
|
---|
1142 |
|
---|
1143 | /* Register interrupt handler */
|
---|
1144 | ahci_ranges[0].base = (size_t) hw_res_parsed.mem_ranges.ranges[0].address;
|
---|
1145 | ahci_ranges[0].size = sizeof(ahci_memregs_t);
|
---|
1146 |
|
---|
1147 | for (unsigned int port = 0; port < AHCI_MAX_PORTS; port++) {
|
---|
1148 | size_t base = port * 7;
|
---|
1149 |
|
---|
1150 | ahci_cmds[base].addr =
|
---|
1151 | ((uint32_t *) (size_t) hw_res_parsed.mem_ranges.ranges[0].address) +
|
---|
1152 | AHCI_PORTS_REGISTERS_OFFSET + port * AHCI_PORT_REGISTERS_SIZE +
|
---|
1153 | AHCI_PORT_IS_REGISTER_OFFSET;
|
---|
1154 | ahci_cmds[base + 2].addr = ahci_cmds[base].addr;
|
---|
1155 |
|
---|
1156 | ahci_cmds[base + 3].addr =
|
---|
1157 | ((uint32_t *) (size_t) hw_res_parsed.mem_ranges.ranges[0].address) +
|
---|
1158 | AHCI_GHC_IS_REGISTER_OFFSET;
|
---|
1159 | ahci_cmds[base + 4].addr = ahci_cmds[base + 3].addr;
|
---|
1160 | }
|
---|
1161 |
|
---|
1162 | irq_code_t ct;
|
---|
1163 | ct.cmdcount = sizeof(ahci_cmds) / sizeof(irq_cmd_t);
|
---|
1164 | ct.cmds = ahci_cmds;
|
---|
1165 | ct.rangecount = sizeof(ahci_ranges) / sizeof(irq_pio_range_t);
|
---|
1166 | ct.ranges = ahci_ranges;
|
---|
1167 |
|
---|
1168 | int rc = register_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0],
|
---|
1169 | ahci_interrupt, &ct);
|
---|
1170 | if (rc != EOK) {
|
---|
1171 | ddf_msg(LVL_ERROR, "Failed registering interrupt handler.");
|
---|
1172 | goto error_register_interrupt_handler;
|
---|
1173 | }
|
---|
1174 |
|
---|
1175 | rc = ahci_enable_interrupt(hw_res_parsed.irqs.irqs[0]);
|
---|
1176 | if (rc != EOK) {
|
---|
1177 | ddf_msg(LVL_ERROR, "Failed enable interupt.");
|
---|
1178 | goto error_enable_interrupt;
|
---|
1179 | }
|
---|
1180 |
|
---|
1181 | hw_res_list_parsed_clean(&hw_res_parsed);
|
---|
1182 | return ahci;
|
---|
1183 |
|
---|
1184 | error_enable_interrupt:
|
---|
1185 | unregister_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0]);
|
---|
1186 |
|
---|
1187 | error_register_interrupt_handler:
|
---|
1188 | // FIXME: unmap physical memory
|
---|
1189 |
|
---|
1190 | error_map_registers:
|
---|
1191 | hw_res_list_parsed_clean(&hw_res_parsed);
|
---|
1192 |
|
---|
1193 | error_get_res_parsed:
|
---|
1194 | free(ahci);
|
---|
1195 | return NULL;
|
---|
1196 | }
|
---|
1197 |
|
---|
1198 | /** Initialize and start AHCI hardware device.
|
---|
1199 | *
|
---|
1200 | * @param ahci AHCI device.
|
---|
1201 | *
|
---|
1202 | */
|
---|
1203 | static void ahci_ahci_hw_start(ahci_dev_t *ahci)
|
---|
1204 | {
|
---|
1205 | /* Disable command completion coalescing feature */
|
---|
1206 | ahci_ghc_ccc_ctl_t ccc;
|
---|
1207 |
|
---|
1208 | ccc.u32 = ahci->memregs->ghc.ccc_ctl;
|
---|
1209 | ccc.en = 0;
|
---|
1210 | ahci->memregs->ghc.ccc_ctl = ccc.u32;
|
---|
1211 |
|
---|
1212 | /* Set master latency timer. */
|
---|
1213 | pci_config_space_write_8(ahci->dev->parent_sess, AHCI_PCI_MLT, 32);
|
---|
1214 |
|
---|
1215 | /* Enable PCI interrupt and bus mastering */
|
---|
1216 | ahci_pcireg_cmd_t cmd;
|
---|
1217 |
|
---|
1218 | pci_config_space_read_16(ahci->dev->parent_sess, AHCI_PCI_CMD, &cmd.u16);
|
---|
1219 | cmd.id = 0;
|
---|
1220 | cmd.bme = 1;
|
---|
1221 | pci_config_space_write_16(ahci->dev->parent_sess, AHCI_PCI_CMD, cmd.u16);
|
---|
1222 |
|
---|
1223 | /* Enable AHCI and interrupt. */
|
---|
1224 | ahci->memregs->ghc.ghc = AHCI_GHC_GHC_AE | AHCI_GHC_GHC_IE;
|
---|
1225 | }
|
---|
1226 |
|
---|
1227 | /** AHCI device driver initialization
|
---|
1228 | *
|
---|
1229 | * Create and initialize all SATA structure devices for connected
|
---|
1230 | * SATA drives.
|
---|
1231 | *
|
---|
1232 | * @param dev DDF device structure.
|
---|
1233 | *
|
---|
1234 | * @return EOK if succeed, error code otherwise.
|
---|
1235 | *
|
---|
1236 | */
|
---|
1237 | static int ahci_dev_add(ddf_dev_t *dev)
|
---|
1238 | {
|
---|
1239 | /* Connect to parent device */
|
---|
1240 | dev->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
|
---|
1241 | dev->handle, IPC_FLAG_BLOCKING);
|
---|
1242 | if (dev->parent_sess == NULL)
|
---|
1243 | return EINTR;
|
---|
1244 |
|
---|
1245 | ahci_dev_t *ahci = ahci_ahci_create(dev);
|
---|
1246 | if (ahci == NULL)
|
---|
1247 | goto error;
|
---|
1248 |
|
---|
1249 | dev->driver_data = ahci;
|
---|
1250 |
|
---|
1251 | /* Start AHCI hardware. */
|
---|
1252 | ahci_ahci_hw_start(ahci);
|
---|
1253 |
|
---|
1254 | /* Create device structures for sata devices attached to AHCI. */
|
---|
1255 | ahci_sata_devices_create(ahci, dev);
|
---|
1256 |
|
---|
1257 | return EOK;
|
---|
1258 |
|
---|
1259 | error:
|
---|
1260 | async_hangup(dev->parent_sess);
|
---|
1261 | return EINTR;
|
---|
1262 | }
|
---|
1263 |
|
---|
1264 | /*----------------------------------------------------------------------------*/
|
---|
1265 | /*-- Helpers and utilities ---------------------------------------------------*/
|
---|
1266 | /*----------------------------------------------------------------------------*/
|
---|
1267 |
|
---|
1268 | /** Convert SATA model name
|
---|
1269 | *
|
---|
1270 | * Convert SATA model name from machine format returned by
|
---|
1271 | * identify device command to human readable form.
|
---|
1272 | *
|
---|
1273 | * @param src Source buffer with device name in machine format.
|
---|
1274 | * @param dst Buffer for human readable string, minimum size is 41 chars.
|
---|
1275 | *
|
---|
1276 | */
|
---|
1277 | static void ahci_get_model_name(uint16_t *src, char *dst)
|
---|
1278 | {
|
---|
1279 | uint8_t model[40];
|
---|
1280 | bzero(model, 40);
|
---|
1281 |
|
---|
1282 | for (unsigned int i = 0; i < 20; i++) {
|
---|
1283 | uint16_t w = src[i];
|
---|
1284 | model[2 * i] = w >> 8;
|
---|
1285 | model[2 * i + 1] = w & 0x00ff;
|
---|
1286 | }
|
---|
1287 |
|
---|
1288 | uint32_t len = 40;
|
---|
1289 | while ((len > 0) && (model[len - 1] == 0x20))
|
---|
1290 | len--;
|
---|
1291 |
|
---|
1292 | size_t pos = 0;
|
---|
1293 | for (unsigned int i = 0; i < len; i++) {
|
---|
1294 | uint8_t c = model[i];
|
---|
1295 | if (c >= 0x80)
|
---|
1296 | c = '?';
|
---|
1297 |
|
---|
1298 | chr_encode(c, dst, &pos, 40);
|
---|
1299 | }
|
---|
1300 |
|
---|
1301 | dst[pos] = '\0';
|
---|
1302 | }
|
---|
1303 |
|
---|
1304 | /** Enable interrupt using SERVICE_IRC.
|
---|
1305 | *
|
---|
1306 | * @param irq Requested irq number.
|
---|
1307 | *
|
---|
1308 | * @return EOK if succeed, error code otherwise.
|
---|
1309 | *
|
---|
1310 | */
|
---|
1311 | static int ahci_enable_interrupt(int irq)
|
---|
1312 | {
|
---|
1313 | async_sess_t *irc_sess = NULL;
|
---|
1314 | irc_sess = service_connect_blocking(EXCHANGE_SERIALIZE, SERVICE_IRC, 0, 0);
|
---|
1315 | if (!irc_sess)
|
---|
1316 | return EINTR;
|
---|
1317 |
|
---|
1318 | async_exch_t *exch = async_exchange_begin(irc_sess);
|
---|
1319 | const int rc = async_req_1_0(exch, IRC_ENABLE_INTERRUPT, irq);
|
---|
1320 | async_exchange_end(exch);
|
---|
1321 |
|
---|
1322 | async_hangup(irc_sess);
|
---|
1323 | return rc;
|
---|
1324 | }
|
---|
1325 |
|
---|
1326 | /*----------------------------------------------------------------------------*/
|
---|
1327 | /*-- AHCI Main routine -------------------------------------------------------*/
|
---|
1328 | /*----------------------------------------------------------------------------*/
|
---|
1329 |
|
---|
1330 | int main(int argc, char *argv[])
|
---|
1331 | {
|
---|
1332 | printf("%s: HelenOS AHCI device driver\n", NAME);
|
---|
1333 | ddf_log_init(NAME, LVL_ERROR);
|
---|
1334 | fibril_mutex_initialize(&sata_devices_count_lock);
|
---|
1335 | return ddf_driver_main(&ahci_driver);
|
---|
1336 | }
|
---|