source: mainline/uspace/drv/block/ahci/ahci.c@ 3be9d10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was eadaeae8, checked in by Jakub Jermar <jakub@…>, 8 years ago

Make capability handles type-safe

Define distinct pointer types for the handles of the supported
capability types and use them instead of integer handles. This makes it
virtually impossible to pass a non-handle or a handle of different type
instead of the proper handle. Also turn cap_handle_t into an "untyped"
capability handle that can be assigned to and from the "typed" handles.

This commit also fixes a bug in msim-con driver, which wrongly used the
IRQ number instead of the IRQ capability handle to unregister the IRQ.

This commit also fixes the wrong use of the capability handle instead
of error code in libusbhost.

  • Property mode set to 100644
File size: 33.3 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.h>
39#include <device/hw_res_parsed.h>
40#include <pci_dev_iface.h>
41#include <ahci_iface.h>
42#include "ahci.h"
43#include "ahci_hw.h"
44#include "ahci_sata.h"
45
46#define NAME "ahci"
47
48#define LO(ptr) \
49 ((uint32_t) (((uint64_t) ((uintptr_t) (ptr))) & 0xffffffff))
50
51#define HI(ptr) \
52 ((uint32_t) (((uint64_t) ((uintptr_t) (ptr))) >> 32))
53
54/** Interrupt pseudocode for a single port
55 *
56 * The interrupt handling works as follows:
57 *
58 * 1. Port interrupt status register is read
59 * (stored as arg2).
60 * 2. If port interrupt is indicated, then:
61 * 3. Port interrupt status register is cleared.
62 * 4. Global interrupt status register is read
63 * and cleared (any potential interrupts from
64 * other ports are reasserted automatically).
65 * 5. Port number is stored as arg1.
66 * 6. The interrupt is accepted.
67 *
68 */
69#define AHCI_PORT_CMDS(port) \
70 { \
71 /* Read port interrupt status register */ \
72 .cmd = CMD_PIO_READ_32, \
73 .addr = NULL, \
74 .dstarg = 2 \
75 }, \
76 { \
77 /* Check if port asserted interrupt */ \
78 .cmd = CMD_PREDICATE, \
79 .value = 5, \
80 .srcarg = 2, \
81 }, \
82 { \
83 /* Clear port interrupt status register */ \
84 .cmd = CMD_PIO_WRITE_A_32, \
85 .addr = NULL, \
86 .srcarg = 2 \
87 }, \
88 { \
89 /* Read global interrupt status register */ \
90 .cmd = CMD_PIO_READ_32, \
91 .addr = NULL, \
92 .dstarg = 0 \
93 }, \
94 { \
95 /* Clear global interrupt status register */ \
96 .cmd = CMD_PIO_WRITE_A_32, \
97 .addr = NULL, \
98 .srcarg = 0 \
99 }, \
100 { \
101 /* Indicate port interrupt assertion */ \
102 .cmd = CMD_LOAD, \
103 .value = (port), \
104 .dstarg = 1 \
105 }, \
106 { \
107 /* Accept the interrupt */ \
108 .cmd = CMD_ACCEPT \
109 }
110
111static errno_t get_sata_device_name(ddf_fun_t *, size_t, char *);
112static errno_t get_num_blocks(ddf_fun_t *, uint64_t *);
113static errno_t get_block_size(ddf_fun_t *, size_t *);
114static errno_t read_blocks(ddf_fun_t *, uint64_t, size_t, void *);
115static errno_t write_blocks(ddf_fun_t *, uint64_t, size_t, void *);
116
117static errno_t ahci_identify_device(sata_dev_t *);
118static errno_t ahci_set_highest_ultra_dma_mode(sata_dev_t *);
119static errno_t ahci_rb_fpdma(sata_dev_t *, uintptr_t, uint64_t);
120static errno_t ahci_wb_fpdma(sata_dev_t *, uintptr_t, uint64_t);
121
122static void ahci_sata_devices_create(ahci_dev_t *, ddf_dev_t *);
123static ahci_dev_t *ahci_ahci_create(ddf_dev_t *);
124static void ahci_ahci_hw_start(ahci_dev_t *);
125
126static errno_t ahci_dev_add(ddf_dev_t *);
127
128static void ahci_get_model_name(uint16_t *, char *);
129
130static fibril_mutex_t sata_devices_count_lock;
131static int sata_devices_count = 0;
132
133/*----------------------------------------------------------------------------*/
134/*-- AHCI Interface ----------------------------------------------------------*/
135/*----------------------------------------------------------------------------*/
136
137static ahci_iface_t ahci_interface = {
138 .get_sata_device_name = &get_sata_device_name,
139 .get_num_blocks = &get_num_blocks,
140 .get_block_size = &get_block_size,
141 .read_blocks = &read_blocks,
142 .write_blocks = &write_blocks
143};
144
145static ddf_dev_ops_t ahci_ops = {
146 .interfaces[AHCI_DEV_IFACE] = &ahci_interface
147};
148
149static driver_ops_t driver_ops = {
150 .dev_add = &ahci_dev_add
151};
152
153static driver_t ahci_driver = {
154 .name = NAME,
155 .driver_ops = &driver_ops
156};
157
158/** Get SATA structure from DDF function. */
159static sata_dev_t *fun_sata_dev(ddf_fun_t *fun)
160{
161 return ddf_fun_data_get(fun);
162}
163
164/** Get AHCI structure from DDF device. */
165static ahci_dev_t *dev_ahci_dev(ddf_dev_t *dev)
166{
167 return ddf_dev_data_get(dev);
168}
169
170/** Get SATA device name.
171 *
172 * @param fun Device function handling the call.
173 * @param sata_dev_name_length Length of the sata_dev_name buffer.
174 * @param sata_dev_name Buffer for SATA device name.
175 *
176 * @return EOK.
177 *
178 */
179static errno_t get_sata_device_name(ddf_fun_t *fun,
180 size_t sata_dev_name_length, char *sata_dev_name)
181{
182 sata_dev_t *sata = fun_sata_dev(fun);
183 str_cpy(sata_dev_name, sata_dev_name_length, sata->model);
184 return EOK;
185}
186
187/** Get Number of blocks in SATA device.
188 *
189 * @param fun Device function handling the call.
190 * @param blocks Return number of blocks in SATA device.
191 *
192 * @return EOK.
193 *
194 */
195static errno_t get_num_blocks(ddf_fun_t *fun, uint64_t *num_blocks)
196{
197 sata_dev_t *sata = fun_sata_dev(fun);
198 *num_blocks = sata->blocks;
199 return EOK;
200}
201
202/** Get SATA device block size.
203 *
204 * @param fun Device function handling the call.
205 * @param block_size Return block size.
206 *
207 * @return EOK.
208 *
209 */
210static errno_t get_block_size(ddf_fun_t *fun, size_t *block_size)
211{
212 sata_dev_t *sata = fun_sata_dev(fun);
213 *block_size = sata->block_size;
214 return EOK;
215}
216
217/** Read data blocks into SATA device.
218 *
219 * @param fun Device function handling the call.
220 * @param blocknum Number of first block.
221 * @param count Number of blocks to read.
222 * @param buf Buffer for data.
223 *
224 * @return EOK if succeed, error code otherwise
225 *
226 */
227static errno_t read_blocks(ddf_fun_t *fun, uint64_t blocknum,
228 size_t count, void *buf)
229{
230 sata_dev_t *sata = fun_sata_dev(fun);
231
232 uintptr_t phys;
233 void *ibuf = AS_AREA_ANY;
234 errno_t rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
235 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &ibuf);
236 if (rc != EOK) {
237 ddf_msg(LVL_ERROR, "Cannot allocate read buffer.");
238 return rc;
239 }
240
241 memset(buf, 0, sata->block_size);
242
243 fibril_mutex_lock(&sata->lock);
244
245 for (size_t cur = 0; cur < count; cur++) {
246 rc = ahci_rb_fpdma(sata, phys, blocknum + cur);
247 if (rc != EOK)
248 break;
249
250 memcpy((void *) (((uint8_t *) buf) + (sata->block_size * cur)),
251 ibuf, sata->block_size);
252 }
253
254 fibril_mutex_unlock(&sata->lock);
255 dmamem_unmap_anonymous(ibuf);
256
257 return rc;
258}
259
260/** Write data blocks into SATA device.
261 *
262 * @param fun Device function handling the call.
263 * @param blocknum Number of first block.
264 * @param count Number of blocks to write.
265 * @param buf Buffer with data.
266 *
267 * @return EOK if succeed, error code otherwise
268 *
269 */
270static errno_t write_blocks(ddf_fun_t *fun, uint64_t blocknum,
271 size_t count, void *buf)
272{
273 sata_dev_t *sata = fun_sata_dev(fun);
274
275 uintptr_t phys;
276 void *ibuf = AS_AREA_ANY;
277 errno_t rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
278 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &ibuf);
279 if (rc != EOK) {
280 ddf_msg(LVL_ERROR, "Cannot allocate write buffer.");
281 return rc;
282 }
283
284 fibril_mutex_lock(&sata->lock);
285
286 for (size_t cur = 0; cur < count; cur++) {
287 memcpy(ibuf, (void *) (((uint8_t *) buf) + (sata->block_size * cur)),
288 sata->block_size);
289 rc = ahci_wb_fpdma(sata, phys, blocknum + cur);
290 if (rc != EOK)
291 break;
292 }
293
294 fibril_mutex_unlock(&sata->lock);
295 dmamem_unmap_anonymous(ibuf);
296
297 return rc;
298}
299
300/*----------------------------------------------------------------------------*/
301/*-- AHCI Commands -----------------------------------------------------------*/
302/*----------------------------------------------------------------------------*/
303
304/** Wait for interrupt event.
305 *
306 * @param sata SATA device structure.
307 *
308 * @return Value of interrupt state register.
309 *
310 */
311static ahci_port_is_t ahci_wait_event(sata_dev_t *sata)
312{
313 fibril_mutex_lock(&sata->event_lock);
314
315 sata->event_pxis = 0;
316 while (sata->event_pxis == 0)
317 fibril_condvar_wait(&sata->event_condvar, &sata->event_lock);
318
319 ahci_port_is_t pxis = sata->event_pxis;
320
321 if (ahci_port_is_permanent_error(pxis))
322 sata->is_invalid_device = true;
323
324 fibril_mutex_unlock(&sata->event_lock);
325
326 return pxis;
327}
328
329/** Set AHCI registers for identifying SATA device.
330 *
331 * @param sata SATA device structure.
332 * @param phys Physical address of working buffer.
333 *
334 */
335static void ahci_identify_device_cmd(sata_dev_t *sata, uintptr_t phys)
336{
337 volatile sata_std_command_frame_t *cmd =
338 (sata_std_command_frame_t *) sata->cmd_table;
339
340 cmd->fis_type = SATA_CMD_FIS_TYPE;
341 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
342 cmd->command = 0xec;
343 cmd->features = 0;
344 cmd->lba_lower = 0;
345 cmd->device = 0;
346 cmd->lba_upper = 0;
347 cmd->features_upper = 0;
348 cmd->count = 0;
349 cmd->reserved1 = 0;
350 cmd->control = 0;
351 cmd->reserved2 = 0;
352
353 volatile ahci_cmd_prdt_t *prdt =
354 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
355
356 prdt->data_address_low = LO(phys);
357 prdt->data_address_upper = HI(phys);
358 prdt->reserved1 = 0;
359 prdt->dbc = SATA_IDENTIFY_DEVICE_BUFFER_LENGTH - 1;
360 prdt->reserved2 = 0;
361 prdt->ioc = 0;
362
363 sata->cmd_header->prdtl = 1;
364 sata->cmd_header->flags =
365 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
366 AHCI_CMDHDR_FLAGS_2DWCMD;
367 sata->cmd_header->bytesprocessed = 0;
368
369 /* Run command. */
370 sata->port->pxsact |= 1;
371 sata->port->pxci |= 1;
372}
373
374/** Set AHCI registers for identifying packet SATA device.
375 *
376 * @param sata SATA device structure.
377 * @param phys Physical address of working buffer.
378 *
379 */
380static void ahci_identify_packet_device_cmd(sata_dev_t *sata, uintptr_t phys)
381{
382 volatile sata_std_command_frame_t *cmd =
383 (sata_std_command_frame_t *) sata->cmd_table;
384
385 cmd->fis_type = SATA_CMD_FIS_TYPE;
386 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
387 cmd->command = 0xa1;
388 cmd->features = 0;
389 cmd->lba_lower = 0;
390 cmd->device = 0;
391 cmd->lba_upper = 0;
392 cmd->features_upper = 0;
393 cmd->count = 0;
394 cmd->reserved1 = 0;
395 cmd->control = 0;
396 cmd->reserved2 = 0;
397
398 volatile ahci_cmd_prdt_t *prdt =
399 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
400
401 prdt->data_address_low = LO(phys);
402 prdt->data_address_upper = HI(phys);
403 prdt->reserved1 = 0;
404 prdt->dbc = SATA_IDENTIFY_DEVICE_BUFFER_LENGTH - 1;
405 prdt->reserved2 = 0;
406 prdt->ioc = 0;
407
408 sata->cmd_header->prdtl = 1;
409 sata->cmd_header->flags =
410 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
411 AHCI_CMDHDR_FLAGS_2DWCMD;
412 sata->cmd_header->bytesprocessed = 0;
413
414 /* Run command. */
415 sata->port->pxsact |= 1;
416 sata->port->pxci |= 1;
417}
418
419/** Fill device identification in SATA device structure.
420 *
421 * @param sata SATA device structure.
422 *
423 * @return EOK if succeed, error code otherwise.
424 *
425 */
426static errno_t ahci_identify_device(sata_dev_t *sata)
427{
428 if (sata->is_invalid_device) {
429 ddf_msg(LVL_ERROR,
430 "Identify command device on invalid device");
431 return EINTR;
432 }
433
434 uintptr_t phys;
435 sata_identify_data_t *idata = AS_AREA_ANY;
436 errno_t rc = dmamem_map_anonymous(SATA_IDENTIFY_DEVICE_BUFFER_LENGTH,
437 DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
438 (void *) &idata);
439 if (rc != EOK) {
440 ddf_msg(LVL_ERROR, "Cannot allocate buffer to identify device.");
441 return rc;
442 }
443
444 memset(idata, 0, SATA_IDENTIFY_DEVICE_BUFFER_LENGTH);
445
446 fibril_mutex_lock(&sata->lock);
447
448 ahci_identify_device_cmd(sata, phys);
449 ahci_port_is_t pxis = ahci_wait_event(sata);
450
451 if (sata->is_invalid_device) {
452 ddf_msg(LVL_ERROR,
453 "Unrecoverable error during ata identify device");
454 goto error;
455 }
456
457 if (ahci_port_is_tfes(pxis)) {
458 ahci_identify_packet_device_cmd(sata, phys);
459 pxis = ahci_wait_event(sata);
460
461 if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
462 ddf_msg(LVL_ERROR,
463 "Unrecoverable error during ata identify packet device");
464 goto error;
465 }
466
467 sata->is_packet_device = true;
468 }
469
470 ahci_get_model_name(idata->model_name, sata->model);
471
472 /*
473 * Due to QEMU limitation (as of 2012-06-22),
474 * only NCQ FPDMA mode is supported.
475 */
476 if ((idata->sata_cap & sata_np_cap_ncq) == 0) {
477 ddf_msg(LVL_ERROR, "%s: NCQ must be supported", sata->model);
478 goto error;
479 }
480
481 uint16_t logsec = idata->physical_logic_sector_size;
482 if ((logsec & 0xc000) == 0x4000) {
483 /* Length of sector may be larger than 512 B */
484 if (logsec & 0x0100) {
485 /* Size of sector is larger than 512 B */
486 ddf_msg(LVL_ERROR,
487 "%s: Sector length other than 512 B not supported",
488 sata->model);
489 goto error;
490 }
491
492 if ((logsec & 0x0200) && ((logsec & 0x000f) != 0)) {
493 /* Physical sectors per logical sector is greather than 1 */
494 ddf_msg(LVL_ERROR,
495 "%s: Sector length other than 512 B not supported",
496 sata->model);
497 goto error;
498 }
499 }
500
501 if (sata->is_packet_device) {
502 /*
503 * Due to QEMU limitation (as of 2012-06-22),
504 * only NCQ FPDMA mode supported - block size is
505 * 512 B, not 2048 B!
506 */
507 sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
508 sata->blocks = 0;
509 } else {
510 sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
511
512 if ((idata->caps & sata_rd_cap_lba) == 0) {
513 ddf_msg(LVL_ERROR, "%s: LBA for NCQ must be supported",
514 sata->model);
515 goto error;
516 } else if ((idata->cmd_set1 & sata_cs1_addr48) == 0) {
517 sata->blocks = (uint32_t) idata->total_lba28_0 |
518 ((uint32_t) idata->total_lba28_1 << 16);
519 } else {
520 /* Device supports LBA-48 addressing. */
521 sata->blocks = (uint64_t) idata->total_lba48_0 |
522 ((uint64_t) idata->total_lba48_1 << 16) |
523 ((uint64_t) idata->total_lba48_2 << 32) |
524 ((uint64_t) idata->total_lba48_3 << 48);
525 }
526 }
527
528 uint8_t udma_mask = idata->udma & 0x007f;
529 sata->highest_udma_mode = (uint8_t) -1;
530 if (udma_mask == 0) {
531 ddf_msg(LVL_ERROR,
532 "%s: UDMA mode for NCQ FPDMA mode must be supported",
533 sata->model);
534 goto error;
535 } else {
536 for (uint8_t i = 0; i < 7; i++) {
537 if (udma_mask & (1 << i))
538 sata->highest_udma_mode = i;
539 }
540 }
541
542 fibril_mutex_unlock(&sata->lock);
543 dmamem_unmap_anonymous(idata);
544
545 return EOK;
546
547error:
548 fibril_mutex_unlock(&sata->lock);
549 dmamem_unmap_anonymous(idata);
550
551 return EINTR;
552}
553
554/** Set AHCI registers for setting SATA device transfer mode.
555 *
556 * @param sata SATA device structure.
557 * @param phys Physical address of working buffer.
558 * @param mode Required mode.
559 *
560 */
561static void ahci_set_mode_cmd(sata_dev_t *sata, uintptr_t phys, uint8_t mode)
562{
563 volatile sata_std_command_frame_t *cmd =
564 (sata_std_command_frame_t *) sata->cmd_table;
565
566 cmd->fis_type = SATA_CMD_FIS_TYPE;
567 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
568 cmd->command = 0xef;
569 cmd->features = 0x03;
570 cmd->lba_lower = 0;
571 cmd->device = 0;
572 cmd->lba_upper = 0;
573 cmd->features_upper = 0;
574 cmd->count = mode;
575 cmd->reserved1 = 0;
576 cmd->control = 0;
577 cmd->reserved2 = 0;
578
579 volatile ahci_cmd_prdt_t *prdt =
580 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
581
582 prdt->data_address_low = LO(phys);
583 prdt->data_address_upper = HI(phys);
584 prdt->reserved1 = 0;
585 prdt->dbc = SATA_SET_FEATURE_BUFFER_LENGTH - 1;
586 prdt->reserved2 = 0;
587 prdt->ioc = 0;
588
589 sata->cmd_header->prdtl = 1;
590 sata->cmd_header->flags =
591 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
592 AHCI_CMDHDR_FLAGS_2DWCMD;
593 sata->cmd_header->bytesprocessed = 0;
594
595 /* Run command. */
596 sata->port->pxsact |= 1;
597 sata->port->pxci |= 1;
598}
599
600/** Set highest ultra DMA mode supported by SATA device.
601 *
602 * @param sata SATA device structure.
603 *
604 * @return EOK if succeed, error code otherwise
605 *
606 */
607static errno_t ahci_set_highest_ultra_dma_mode(sata_dev_t *sata)
608{
609 if (sata->is_invalid_device) {
610 ddf_msg(LVL_ERROR,
611 "%s: Setting highest UDMA mode on invalid device",
612 sata->model);
613 return EINTR;
614 }
615
616 if (sata->highest_udma_mode == (uint8_t) -1) {
617 ddf_msg(LVL_ERROR,
618 "%s: No AHCI UDMA support.", sata->model);
619 return EINTR;
620 }
621
622 if (sata->highest_udma_mode > 6) {
623 ddf_msg(LVL_ERROR,
624 "%s: Unknown AHCI UDMA mode.", sata->model);
625 return EINTR;
626 }
627
628 uintptr_t phys;
629 sata_identify_data_t *idata = AS_AREA_ANY;
630 errno_t rc = dmamem_map_anonymous(SATA_SET_FEATURE_BUFFER_LENGTH,
631 DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
632 (void *) &idata);
633 if (rc != EOK) {
634 ddf_msg(LVL_ERROR, "Cannot allocate buffer for device set mode.");
635 return rc;
636 }
637
638 memset(idata, 0, SATA_SET_FEATURE_BUFFER_LENGTH);
639
640 fibril_mutex_lock(&sata->lock);
641
642 uint8_t mode = 0x40 | (sata->highest_udma_mode & 0x07);
643 ahci_set_mode_cmd(sata, phys, mode);
644 ahci_port_is_t pxis = ahci_wait_event(sata);
645
646 if (sata->is_invalid_device) {
647 ddf_msg(LVL_ERROR,
648 "%s: Unrecoverable error during set highest UDMA mode",
649 sata->model);
650 goto error;
651 }
652
653 if (ahci_port_is_error(pxis)) {
654 ddf_msg(LVL_ERROR,
655 "%s: Error during set highest UDMA mode", sata->model);
656 goto error;
657 }
658
659 fibril_mutex_unlock(&sata->lock);
660 dmamem_unmap_anonymous(idata);
661
662 return EOK;
663
664error:
665 fibril_mutex_unlock(&sata->lock);
666 dmamem_unmap_anonymous(idata);
667
668 return EINTR;
669}
670
671/** Set AHCI registers for reading one sector from the SATA device using FPDMA.
672 *
673 * @param sata SATA device structure.
674 * @param phys Physical address of buffer for sector data.
675 * @param blocknum Block number to read.
676 *
677 */
678static void ahci_rb_fpdma_cmd(sata_dev_t *sata, uintptr_t phys,
679 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 errno_t ahci_rb_fpdma(sata_dev_t *sata, uintptr_t 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, uintptr_t phys,
766 uint64_t blocknum)
767{
768 volatile sata_ncq_command_frame_t *cmd =
769 (sata_ncq_command_frame_t *) sata->cmd_table;
770
771 cmd->fis_type = SATA_CMD_FIS_TYPE;
772 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
773 cmd->command = 0x61;
774 cmd->tag = 0;
775 cmd->control = 0;
776
777 cmd->reserved1 = 0;
778 cmd->reserved2 = 0;
779 cmd->reserved3 = 0;
780 cmd->reserved4 = 0;
781 cmd->reserved5 = 0;
782 cmd->reserved6 = 0;
783
784 cmd->sector_count_low = 1;
785 cmd->sector_count_high = 0;
786
787 cmd->lba0 = blocknum & 0xff;
788 cmd->lba1 = (blocknum >> 8) & 0xff;
789 cmd->lba2 = (blocknum >> 16) & 0xff;
790 cmd->lba3 = (blocknum >> 24) & 0xff;
791 cmd->lba4 = (blocknum >> 32) & 0xff;
792 cmd->lba5 = (blocknum >> 40) & 0xff;
793
794 volatile ahci_cmd_prdt_t *prdt =
795 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
796
797 prdt->data_address_low = LO(phys);
798 prdt->data_address_upper = HI(phys);
799 prdt->reserved1 = 0;
800 prdt->dbc = sata->block_size - 1;
801 prdt->reserved2 = 0;
802 prdt->ioc = 0;
803
804 sata->cmd_header->prdtl = 1;
805 sata->cmd_header->flags =
806 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
807 AHCI_CMDHDR_FLAGS_WRITE |
808 AHCI_CMDHDR_FLAGS_5DWCMD;
809 sata->cmd_header->bytesprocessed = 0;
810
811 sata->port->pxsact |= 1;
812 sata->port->pxci |= 1;
813}
814
815/** Write one sector into the SATA device, use FPDMA.
816 *
817 * @param sata SATA device structure.
818 * @param phys Physical addres of buffer with sector data.
819 * @param blocknum Block number to write.
820 *
821 * @return EOK if succeed, error code otherwise
822 *
823 */
824static errno_t ahci_wb_fpdma(sata_dev_t *sata, uintptr_t phys, uint64_t blocknum)
825{
826 if (sata->is_invalid_device) {
827 ddf_msg(LVL_ERROR,
828 "%s: FPDMA write to invalid device", sata->model);
829 return EINTR;
830 }
831
832 ahci_wb_fpdma_cmd(sata, phys, blocknum);
833 ahci_port_is_t pxis = ahci_wait_event(sata);
834
835 if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
836 ddf_msg(LVL_ERROR,
837 "%s: Unrecoverable error during FPDMA write", sata->model);
838 return EINTR;
839 }
840
841 return EOK;
842}
843
844/*----------------------------------------------------------------------------*/
845/*-- Interrupts handling -----------------------------------------------------*/
846/*----------------------------------------------------------------------------*/
847
848static irq_pio_range_t ahci_ranges[] = {
849 {
850 .base = 0,
851 .size = 0,
852 }
853};
854
855static irq_cmd_t ahci_cmds[] = {
856 AHCI_PORT_CMDS(0),
857 AHCI_PORT_CMDS(1),
858 AHCI_PORT_CMDS(2),
859 AHCI_PORT_CMDS(3),
860 AHCI_PORT_CMDS(4),
861 AHCI_PORT_CMDS(5),
862 AHCI_PORT_CMDS(6),
863 AHCI_PORT_CMDS(7),
864 AHCI_PORT_CMDS(8),
865 AHCI_PORT_CMDS(9),
866 AHCI_PORT_CMDS(10),
867 AHCI_PORT_CMDS(11),
868 AHCI_PORT_CMDS(12),
869 AHCI_PORT_CMDS(13),
870 AHCI_PORT_CMDS(14),
871 AHCI_PORT_CMDS(15),
872 AHCI_PORT_CMDS(16),
873 AHCI_PORT_CMDS(17),
874 AHCI_PORT_CMDS(18),
875 AHCI_PORT_CMDS(19),
876 AHCI_PORT_CMDS(20),
877 AHCI_PORT_CMDS(21),
878 AHCI_PORT_CMDS(22),
879 AHCI_PORT_CMDS(23),
880 AHCI_PORT_CMDS(24),
881 AHCI_PORT_CMDS(25),
882 AHCI_PORT_CMDS(26),
883 AHCI_PORT_CMDS(27),
884 AHCI_PORT_CMDS(28),
885 AHCI_PORT_CMDS(29),
886 AHCI_PORT_CMDS(30),
887 AHCI_PORT_CMDS(31)
888};
889
890/** AHCI interrupt handler.
891 *
892 * @param icall The IPC call structure.
893 * @param dev DDF device structure.
894 *
895 */
896static void ahci_interrupt(ipc_call_t *icall, ddf_dev_t *dev)
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 uintptr_t phys = 0;
936 void *virt_fb = AS_AREA_ANY;
937 void *virt_cmd = AS_AREA_ANY;
938 void *virt_table = AS_AREA_ANY;
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 errno_t rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
952 AS_AREA_READ | AS_AREA_WRITE, 0, &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, DMAMEM_4GiB,
962 AS_AREA_READ | AS_AREA_WRITE, 0, &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, DMAMEM_4GiB,
973 AS_AREA_READ | AS_AREA_WRITE, 0, &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 errno_t 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 errno_t 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_get(dev);
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 = AS_AREA_ANY;
1154
1155 physmem_map(RNGABS(hw_res_parsed.mem_ranges.ranges[0]),
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 = RNGABS(hw_res_parsed.mem_ranges.ranges[0]);
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 *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
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 *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
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 cap_irq_handle_t irq_cap;
1187 errno_t rc = register_interrupt_handler(dev,
1188 hw_res_parsed.irqs.irqs[0], ahci_interrupt, &ct, &irq_cap);
1189 if (rc != EOK) {
1190 ddf_msg(LVL_ERROR, "Failed registering interrupt handler.");
1191 goto error_register_interrupt_handler;
1192 }
1193
1194 rc = hw_res_enable_interrupt(ahci->parent_sess,
1195 hw_res_parsed.irqs.irqs[0]);
1196 if (rc != EOK) {
1197 ddf_msg(LVL_ERROR, "Failed enable interupt.");
1198 goto error_enable_interrupt;
1199 }
1200
1201 hw_res_list_parsed_clean(&hw_res_parsed);
1202 return ahci;
1203
1204error_enable_interrupt:
1205 unregister_interrupt_handler(dev, irq_cap);
1206
1207error_register_interrupt_handler:
1208 // FIXME: unmap physical memory
1209
1210error_map_registers:
1211 hw_res_list_parsed_clean(&hw_res_parsed);
1212
1213error_get_res_parsed:
1214 free(ahci);
1215 return NULL;
1216}
1217
1218/** Initialize and start AHCI hardware device.
1219 *
1220 * @param ahci AHCI device.
1221 *
1222 */
1223static void ahci_ahci_hw_start(ahci_dev_t *ahci)
1224{
1225 /* Disable command completion coalescing feature */
1226 ahci_ghc_ccc_ctl_t ccc;
1227
1228 ccc.u32 = ahci->memregs->ghc.ccc_ctl;
1229 ccc.en = 0;
1230 ahci->memregs->ghc.ccc_ctl = ccc.u32;
1231
1232 /* Set master latency timer. */
1233 pci_config_space_write_8(ahci->parent_sess, AHCI_PCI_MLT, 32);
1234
1235 /* Enable PCI interrupt and bus mastering */
1236 ahci_pcireg_cmd_t cmd;
1237
1238 pci_config_space_read_16(ahci->parent_sess, AHCI_PCI_CMD, &cmd.u16);
1239 cmd.id = 0;
1240 cmd.bme = 1;
1241 pci_config_space_write_16(ahci->parent_sess, AHCI_PCI_CMD, cmd.u16);
1242
1243 /* Enable AHCI and interrupt. */
1244 ahci->memregs->ghc.ghc = AHCI_GHC_GHC_AE | AHCI_GHC_GHC_IE;
1245}
1246
1247/** AHCI device driver initialization
1248 *
1249 * Create and initialize all SATA structure devices for connected
1250 * SATA drives.
1251 *
1252 * @param dev DDF device structure.
1253 *
1254 * @return EOK if succeed, error code otherwise.
1255 *
1256 */
1257static errno_t ahci_dev_add(ddf_dev_t *dev)
1258{
1259 ahci_dev_t *ahci = ahci_ahci_create(dev);
1260 if (ahci == NULL)
1261 goto error;
1262
1263 /* Start AHCI hardware. */
1264 ahci_ahci_hw_start(ahci);
1265
1266 /* Create device structures for sata devices attached to AHCI. */
1267 ahci_sata_devices_create(ahci, dev);
1268
1269 return EOK;
1270
1271error:
1272 return EINTR;
1273}
1274
1275/*----------------------------------------------------------------------------*/
1276/*-- Helpers and utilities ---------------------------------------------------*/
1277/*----------------------------------------------------------------------------*/
1278
1279/** Convert SATA model name
1280 *
1281 * Convert SATA model name from machine format returned by
1282 * identify device command to human readable form.
1283 *
1284 * @param src Source buffer with device name in machine format.
1285 * @param dst Buffer for human readable string, minimum size is 41 chars.
1286 *
1287 */
1288static void ahci_get_model_name(uint16_t *src, char *dst)
1289{
1290 uint8_t model[40];
1291 memset(model, 0, 40);
1292
1293 for (unsigned int i = 0; i < 20; i++) {
1294 uint16_t w = src[i];
1295 model[2 * i] = w >> 8;
1296 model[2 * i + 1] = w & 0x00ff;
1297 }
1298
1299 uint32_t len = 40;
1300 while ((len > 0) && (model[len - 1] == 0x20))
1301 len--;
1302
1303 size_t pos = 0;
1304 for (unsigned int i = 0; i < len; i++) {
1305 uint8_t c = model[i];
1306 if (c >= 0x80)
1307 c = '?';
1308
1309 chr_encode(c, dst, &pos, 40);
1310 }
1311
1312 dst[pos] = '\0';
1313}
1314
1315/*----------------------------------------------------------------------------*/
1316/*-- AHCI Main routine -------------------------------------------------------*/
1317/*----------------------------------------------------------------------------*/
1318
1319int main(int argc, char *argv[])
1320{
1321 printf("%s: HelenOS AHCI device driver\n", NAME);
1322 ddf_log_init(NAME);
1323 fibril_mutex_initialize(&sata_devices_count_lock);
1324 return ddf_driver_main(&ahci_driver);
1325}
Note: See TracBrowser for help on using the repository browser.