source: mainline/uspace/drv/block/ahci/ahci.c@ ff03f79

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ff03f79 was acdb5bac, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Replace usage of bzero() with C standard memset().

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