source: mainline/uspace/drv/block/ahci/ahci.c@ 95d5dca

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 95d5dca was 7f620e8, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

libdrv, libc: Move AHCI to libdrv.

  • 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 <pci_dev_iface.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 get_sata_device_name(ddf_fun_t *, size_t, char *);
114static int get_num_blocks(ddf_fun_t *, uint64_t *);
115static int get_block_size(ddf_fun_t *, size_t *);
116static int read_blocks(ddf_fun_t *, uint64_t, size_t, void *);
117static int 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 *, uintptr_t, uint64_t);
122static int ahci_wb_fpdma(sata_dev_t *, uintptr_t, 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 = &get_sata_device_name,
142 .get_num_blocks = &get_num_blocks,
143 .get_block_size = &get_block_size,
144 .read_blocks = &read_blocks,
145 .write_blocks = &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 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 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 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 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 uintptr_t phys;
236 void *ibuf;
237 int rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
238 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &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 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 uintptr_t phys;
279 void *ibuf;
280 int rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
281 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &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, uintptr_t 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, uintptr_t 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 uintptr_t phys;
438 sata_identify_data_t *idata;
439 int rc = dmamem_map_anonymous(SATA_IDENTIFY_DEVICE_BUFFER_LENGTH,
440 DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
441 (void **) &idata);
442 if (rc != EOK) {
443 ddf_msg(LVL_ERROR, "Cannot allocate buffer to identify device.");
444 return rc;
445 }
446
447 memset(idata, 0, SATA_IDENTIFY_DEVICE_BUFFER_LENGTH);
448
449 fibril_mutex_lock(&sata->lock);
450
451 ahci_identify_device_cmd(sata, phys);
452 ahci_port_is_t pxis = ahci_wait_event(sata);
453
454 if (sata->is_invalid_device) {
455 ddf_msg(LVL_ERROR,
456 "Unrecoverable error during ata identify device");
457 goto error;
458 }
459
460 if (ahci_port_is_tfes(pxis)) {
461 ahci_identify_packet_device_cmd(sata, phys);
462 pxis = ahci_wait_event(sata);
463
464 if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
465 ddf_msg(LVL_ERROR,
466 "Unrecoverable error during ata identify packet device");
467 goto error;
468 }
469
470 sata->is_packet_device = true;
471 }
472
473 ahci_get_model_name(idata->model_name, sata->model);
474
475 /*
476 * Due to QEMU limitation (as of 2012-06-22),
477 * only NCQ FPDMA mode is supported.
478 */
479 if ((idata->sata_cap & sata_np_cap_ncq) == 0) {
480 ddf_msg(LVL_ERROR, "%s: NCQ must be supported", sata->model);
481 goto error;
482 }
483
484 uint16_t logsec = idata->physical_logic_sector_size;
485 if ((logsec & 0xc000) == 0x4000) {
486 /* Length of sector may be larger than 512 B */
487 if (logsec & 0x0100) {
488 /* Size of sector is larger than 512 B */
489 ddf_msg(LVL_ERROR,
490 "%s: Sector length other than 512 B not supported",
491 sata->model);
492 goto error;
493 }
494
495 if ((logsec & 0x0200) && ((logsec & 0x000f) != 0)) {
496 /* Physical sectors per logical sector is greather than 1 */
497 ddf_msg(LVL_ERROR,
498 "%s: Sector length other than 512 B not supported",
499 sata->model);
500 goto error;
501 }
502 }
503
504 if (sata->is_packet_device) {
505 /*
506 * Due to QEMU limitation (as of 2012-06-22),
507 * only NCQ FPDMA mode supported - block size is
508 * 512 B, not 2048 B!
509 */
510 sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
511 sata->blocks = 0;
512 } else {
513 sata->block_size = SATA_DEFAULT_SECTOR_SIZE;
514
515 if ((idata->caps & sata_rd_cap_lba) == 0) {
516 ddf_msg(LVL_ERROR, "%s: LBA for NCQ must be supported",
517 sata->model);
518 goto error;
519 } else if ((idata->cmd_set1 & sata_cs1_addr48) == 0) {
520 sata->blocks = (uint32_t) idata->total_lba28_0 |
521 ((uint32_t) idata->total_lba28_1 << 16);
522 } else {
523 /* Device supports LBA-48 addressing. */
524 sata->blocks = (uint64_t) idata->total_lba48_0 |
525 ((uint64_t) idata->total_lba48_1 << 16) |
526 ((uint64_t) idata->total_lba48_2 << 32) |
527 ((uint64_t) idata->total_lba48_3 << 48);
528 }
529 }
530
531 uint8_t udma_mask = idata->udma & 0x007f;
532 sata->highest_udma_mode = (uint8_t) -1;
533 if (udma_mask == 0) {
534 ddf_msg(LVL_ERROR,
535 "%s: UDMA mode for NCQ FPDMA mode must be supported",
536 sata->model);
537 goto error;
538 } else {
539 for (uint8_t i = 0; i < 7; i++) {
540 if (udma_mask & (1 << i))
541 sata->highest_udma_mode = i;
542 }
543 }
544
545 fibril_mutex_unlock(&sata->lock);
546 dmamem_unmap_anonymous(idata);
547
548 return EOK;
549
550error:
551 fibril_mutex_unlock(&sata->lock);
552 dmamem_unmap_anonymous(idata);
553
554 return EINTR;
555}
556
557/** Set AHCI registers for setting SATA device transfer mode.
558 *
559 * @param sata SATA device structure.
560 * @param phys Physical address of working buffer.
561 * @param mode Required mode.
562 *
563 */
564static void ahci_set_mode_cmd(sata_dev_t *sata, uintptr_t phys, uint8_t mode)
565{
566 volatile sata_std_command_frame_t *cmd =
567 (sata_std_command_frame_t *) sata->cmd_table;
568
569 cmd->fis_type = SATA_CMD_FIS_TYPE;
570 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
571 cmd->command = 0xef;
572 cmd->features = 0x03;
573 cmd->lba_lower = 0;
574 cmd->device = 0;
575 cmd->lba_upper = 0;
576 cmd->features_upper = 0;
577 cmd->count = mode;
578 cmd->reserved1 = 0;
579 cmd->control = 0;
580 cmd->reserved2 = 0;
581
582 volatile ahci_cmd_prdt_t *prdt =
583 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
584
585 prdt->data_address_low = LO(phys);
586 prdt->data_address_upper = HI(phys);
587 prdt->reserved1 = 0;
588 prdt->dbc = SATA_SET_FEATURE_BUFFER_LENGTH - 1;
589 prdt->reserved2 = 0;
590 prdt->ioc = 0;
591
592 sata->cmd_header->prdtl = 1;
593 sata->cmd_header->flags =
594 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
595 AHCI_CMDHDR_FLAGS_2DWCMD;
596 sata->cmd_header->bytesprocessed = 0;
597
598 /* Run command. */
599 sata->port->pxsact |= 1;
600 sata->port->pxci |= 1;
601}
602
603/** Set highest ultra DMA mode supported by SATA device.
604 *
605 * @param sata SATA device structure.
606 *
607 * @return EOK if succeed, error code otherwise
608 *
609 */
610static int ahci_set_highest_ultra_dma_mode(sata_dev_t *sata)
611{
612 if (sata->is_invalid_device) {
613 ddf_msg(LVL_ERROR,
614 "%s: Setting highest UDMA mode on invalid device",
615 sata->model);
616 return EINTR;
617 }
618
619 if (sata->highest_udma_mode == (uint8_t) -1) {
620 ddf_msg(LVL_ERROR,
621 "%s: No AHCI UDMA support.", sata->model);
622 return EINTR;
623 }
624
625 if (sata->highest_udma_mode > 6) {
626 ddf_msg(LVL_ERROR,
627 "%s: Unknown AHCI UDMA mode.", sata->model);
628 return EINTR;
629 }
630
631 uintptr_t phys;
632 sata_identify_data_t *idata;
633 int rc = dmamem_map_anonymous(SATA_SET_FEATURE_BUFFER_LENGTH,
634 DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
635 (void **) &idata);
636 if (rc != EOK) {
637 ddf_msg(LVL_ERROR, "Cannot allocate buffer for device set mode.");
638 return rc;
639 }
640
641 memset(idata, 0, SATA_SET_FEATURE_BUFFER_LENGTH);
642
643 fibril_mutex_lock(&sata->lock);
644
645 uint8_t mode = 0x40 | (sata->highest_udma_mode & 0x07);
646 ahci_set_mode_cmd(sata, phys, mode);
647 ahci_port_is_t pxis = ahci_wait_event(sata);
648
649 if (sata->is_invalid_device) {
650 ddf_msg(LVL_ERROR,
651 "%s: Unrecoverable error during set highest UDMA mode",
652 sata->model);
653 goto error;
654 }
655
656 if (ahci_port_is_error(pxis)) {
657 ddf_msg(LVL_ERROR,
658 "%s: Error during set highest UDMA mode", sata->model);
659 goto error;
660 }
661
662 fibril_mutex_unlock(&sata->lock);
663 dmamem_unmap_anonymous(idata);
664
665 return EOK;
666
667error:
668 fibril_mutex_unlock(&sata->lock);
669 dmamem_unmap_anonymous(idata);
670
671 return EINTR;
672}
673
674/** Set AHCI registers for reading one sector from the SATA device using FPDMA.
675 *
676 * @param sata SATA device structure.
677 * @param phys Physical address of buffer for sector data.
678 * @param blocknum Block number to read.
679 *
680 */
681static void ahci_rb_fpdma_cmd(sata_dev_t *sata, uintptr_t phys,
682 uint64_t blocknum)
683{
684 volatile sata_ncq_command_frame_t *cmd =
685 (sata_ncq_command_frame_t *) sata->cmd_table;
686
687 cmd->fis_type = SATA_CMD_FIS_TYPE;
688 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
689 cmd->command = 0x60;
690 cmd->tag = 0;
691 cmd->control = 0;
692
693 cmd->reserved1 = 0;
694 cmd->reserved2 = 0;
695 cmd->reserved3 = 0;
696 cmd->reserved4 = 0;
697 cmd->reserved5 = 0;
698 cmd->reserved6 = 0;
699
700 cmd->sector_count_low = 1;
701 cmd->sector_count_high = 0;
702
703 cmd->lba0 = blocknum & 0xff;
704 cmd->lba1 = (blocknum >> 8) & 0xff;
705 cmd->lba2 = (blocknum >> 16) & 0xff;
706 cmd->lba3 = (blocknum >> 24) & 0xff;
707 cmd->lba4 = (blocknum >> 32) & 0xff;
708 cmd->lba5 = (blocknum >> 40) & 0xff;
709
710 volatile ahci_cmd_prdt_t *prdt =
711 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
712
713 prdt->data_address_low = LO(phys);
714 prdt->data_address_upper = HI(phys);
715 prdt->reserved1 = 0;
716 prdt->dbc = sata->block_size - 1;
717 prdt->reserved2 = 0;
718 prdt->ioc = 0;
719
720 sata->cmd_header->prdtl = 1;
721 sata->cmd_header->flags =
722 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
723 AHCI_CMDHDR_FLAGS_5DWCMD;
724 sata->cmd_header->bytesprocessed = 0;
725
726 sata->port->pxsact |= 1;
727 sata->port->pxci |= 1;
728}
729
730/** Read one sector from the SATA device using FPDMA.
731 *
732 * @param sata SATA device structure.
733 * @param phys Physical address of buffer for sector data.
734 * @param blocknum Block number to read.
735 *
736 * @return EOK if succeed, error code otherwise
737 *
738 */
739static int ahci_rb_fpdma(sata_dev_t *sata, uintptr_t phys, uint64_t blocknum)
740{
741 if (sata->is_invalid_device) {
742 ddf_msg(LVL_ERROR,
743 "%s: FPDMA read from invalid device", sata->model);
744 return EINTR;
745 }
746
747 ahci_rb_fpdma_cmd(sata, phys, blocknum);
748 ahci_port_is_t pxis = ahci_wait_event(sata);
749
750 if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
751 ddf_msg(LVL_ERROR,
752 "%s: Unrecoverable error during FPDMA read", sata->model);
753 return EINTR;
754 }
755
756 return EOK;
757}
758
759/** Set AHCI registers for writing one sector to the SATA device, use FPDMA.
760 *
761 * @param sata SATA device structure.
762 * @param phys Physical address of buffer with sector data.
763 * @param blocknum Block number to write.
764 *
765 * @return EOK if succeed, error code otherwise
766 *
767 */
768static void ahci_wb_fpdma_cmd(sata_dev_t *sata, uintptr_t phys,
769 uint64_t blocknum)
770{
771 volatile sata_ncq_command_frame_t *cmd =
772 (sata_ncq_command_frame_t *) sata->cmd_table;
773
774 cmd->fis_type = SATA_CMD_FIS_TYPE;
775 cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
776 cmd->command = 0x61;
777 cmd->tag = 0;
778 cmd->control = 0;
779
780 cmd->reserved1 = 0;
781 cmd->reserved2 = 0;
782 cmd->reserved3 = 0;
783 cmd->reserved4 = 0;
784 cmd->reserved5 = 0;
785 cmd->reserved6 = 0;
786
787 cmd->sector_count_low = 1;
788 cmd->sector_count_high = 0;
789
790 cmd->lba0 = blocknum & 0xff;
791 cmd->lba1 = (blocknum >> 8) & 0xff;
792 cmd->lba2 = (blocknum >> 16) & 0xff;
793 cmd->lba3 = (blocknum >> 24) & 0xff;
794 cmd->lba4 = (blocknum >> 32) & 0xff;
795 cmd->lba5 = (blocknum >> 40) & 0xff;
796
797 volatile ahci_cmd_prdt_t *prdt =
798 (ahci_cmd_prdt_t *) (&sata->cmd_table[0x20]);
799
800 prdt->data_address_low = LO(phys);
801 prdt->data_address_upper = HI(phys);
802 prdt->reserved1 = 0;
803 prdt->dbc = sata->block_size - 1;
804 prdt->reserved2 = 0;
805 prdt->ioc = 0;
806
807 sata->cmd_header->prdtl = 1;
808 sata->cmd_header->flags =
809 AHCI_CMDHDR_FLAGS_CLEAR_BUSY_UPON_OK |
810 AHCI_CMDHDR_FLAGS_WRITE |
811 AHCI_CMDHDR_FLAGS_5DWCMD;
812 sata->cmd_header->bytesprocessed = 0;
813
814 sata->port->pxsact |= 1;
815 sata->port->pxci |= 1;
816}
817
818/** Write one sector into the SATA device, use FPDMA.
819 *
820 * @param sata SATA device structure.
821 * @param phys Physical addres of buffer with sector data.
822 * @param blocknum Block number to write.
823 *
824 * @return EOK if succeed, error code otherwise
825 *
826 */
827static int ahci_wb_fpdma(sata_dev_t *sata, uintptr_t phys, uint64_t blocknum)
828{
829 if (sata->is_invalid_device) {
830 ddf_msg(LVL_ERROR,
831 "%s: FPDMA write to invalid device", sata->model);
832 return EINTR;
833 }
834
835 ahci_wb_fpdma_cmd(sata, phys, blocknum);
836 ahci_port_is_t pxis = ahci_wait_event(sata);
837
838 if ((sata->is_invalid_device) || (ahci_port_is_error(pxis))) {
839 ddf_msg(LVL_ERROR,
840 "%s: Unrecoverable error during FPDMA write", sata->model);
841 return EINTR;
842 }
843
844 return EOK;
845}
846
847/*----------------------------------------------------------------------------*/
848/*-- Interrupts handling -----------------------------------------------------*/
849/*----------------------------------------------------------------------------*/
850
851static irq_pio_range_t ahci_ranges[] = {
852 {
853 .base = 0,
854 .size = 0,
855 }
856};
857
858static irq_cmd_t ahci_cmds[] = {
859 AHCI_PORT_CMDS(0),
860 AHCI_PORT_CMDS(1),
861 AHCI_PORT_CMDS(2),
862 AHCI_PORT_CMDS(3),
863 AHCI_PORT_CMDS(4),
864 AHCI_PORT_CMDS(5),
865 AHCI_PORT_CMDS(6),
866 AHCI_PORT_CMDS(7),
867 AHCI_PORT_CMDS(8),
868 AHCI_PORT_CMDS(9),
869 AHCI_PORT_CMDS(10),
870 AHCI_PORT_CMDS(11),
871 AHCI_PORT_CMDS(12),
872 AHCI_PORT_CMDS(13),
873 AHCI_PORT_CMDS(14),
874 AHCI_PORT_CMDS(15),
875 AHCI_PORT_CMDS(16),
876 AHCI_PORT_CMDS(17),
877 AHCI_PORT_CMDS(18),
878 AHCI_PORT_CMDS(19),
879 AHCI_PORT_CMDS(20),
880 AHCI_PORT_CMDS(21),
881 AHCI_PORT_CMDS(22),
882 AHCI_PORT_CMDS(23),
883 AHCI_PORT_CMDS(24),
884 AHCI_PORT_CMDS(25),
885 AHCI_PORT_CMDS(26),
886 AHCI_PORT_CMDS(27),
887 AHCI_PORT_CMDS(28),
888 AHCI_PORT_CMDS(29),
889 AHCI_PORT_CMDS(30),
890 AHCI_PORT_CMDS(31)
891};
892
893/** AHCI interrupt handler.
894 *
895 * @param dev DDF device structure.
896 * @param iid The IPC call id.
897 * @param icall The IPC call structure.
898 *
899 */
900static void ahci_interrupt(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *icall)
901{
902 ahci_dev_t *ahci = dev_ahci_dev(dev);
903 unsigned int port = IPC_GET_ARG1(*icall);
904 ahci_port_is_t pxis = IPC_GET_ARG2(*icall);
905
906 if (port >= AHCI_MAX_PORTS)
907 return;
908
909 sata_dev_t *sata = (sata_dev_t *) ahci->sata_devs[port];
910 if (sata == NULL)
911 return;
912
913 /* Evaluate port event */
914 if ((ahci_port_is_end_of_operation(pxis)) ||
915 (ahci_port_is_error(pxis))) {
916 fibril_mutex_lock(&sata->event_lock);
917
918 sata->event_pxis = pxis;
919 fibril_condvar_signal(&sata->event_condvar);
920
921 fibril_mutex_unlock(&sata->event_lock);
922 }
923}
924
925/*----------------------------------------------------------------------------*/
926/*-- AHCI and SATA device creating and initializing routines -----------------*/
927/*----------------------------------------------------------------------------*/
928
929/** Allocate SATA device structure with buffers for hardware.
930 *
931 * @param port AHCI port structure
932 *
933 * @return SATA device structure if succeed, NULL otherwise.
934 *
935 */
936static sata_dev_t *ahci_sata_allocate(ahci_dev_t *ahci, volatile ahci_port_t *port)
937{
938 size_t size = 4096;
939 uintptr_t phys = 0;
940 void *virt_fb = NULL;
941 void *virt_cmd = NULL;
942 void *virt_table = NULL;
943 ddf_fun_t *fun;
944
945 fun = ddf_fun_create(ahci->dev, fun_exposed, NULL);
946
947 sata_dev_t *sata = ddf_fun_data_alloc(fun, sizeof(sata_dev_t));
948 if (sata == NULL)
949 return NULL;
950
951 sata->fun = fun;
952 sata->port = port;
953
954 /* Allocate and init retfis structure. */
955 int rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
956 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_fb);
957 if (rc != EOK)
958 goto error_retfis;
959
960 memset(virt_fb, 0, size);
961 sata->port->pxfbu = HI(phys);
962 sata->port->pxfb = LO(phys);
963
964 /* Allocate and init command header structure. */
965 rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
966 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_cmd);
967 if (rc != EOK)
968 goto error_cmd;
969
970 memset(virt_cmd, 0, size);
971 sata->port->pxclbu = HI(phys);
972 sata->port->pxclb = LO(phys);
973 sata->cmd_header = (ahci_cmdhdr_t *) virt_cmd;
974
975 /* Allocate and init command table structure. */
976 rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
977 AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_table);
978 if (rc != EOK)
979 goto error_table;
980
981 memset(virt_table, 0, size);
982 sata->cmd_header->cmdtableu = HI(phys);
983 sata->cmd_header->cmdtable = LO(phys);
984 sata->cmd_table = (uint32_t*) virt_table;
985
986 return sata;
987
988error_table:
989 dmamem_unmap(virt_cmd, size);
990error_cmd:
991 dmamem_unmap(virt_fb, size);
992error_retfis:
993 free(sata);
994 return NULL;
995}
996
997/** Initialize and start SATA hardware device.
998 *
999 * @param sata SATA device structure.
1000 *
1001 */
1002static void ahci_sata_hw_start(sata_dev_t *sata)
1003{
1004 ahci_port_cmd_t pxcmd;
1005
1006 pxcmd.u32 = sata->port->pxcmd;
1007
1008 /* Frame receiver disabled. */
1009 pxcmd.fre = 0;
1010
1011 /* Disable process the command list. */
1012 pxcmd.st = 0;
1013
1014 sata->port->pxcmd = pxcmd.u32;
1015
1016 /* Clear interrupt status. */
1017 sata->port->pxis = 0xffffffff;
1018
1019 /* Clear error status. */
1020 sata->port->pxserr = 0xffffffff;
1021
1022 /* Enable all interrupts. */
1023 sata->port->pxie = 0xffffffff;
1024
1025 /* Frame receiver enabled. */
1026 pxcmd.fre = 1;
1027
1028 /* Enable process the command list. */
1029 pxcmd.st = 1;
1030
1031 sata->port->pxcmd = pxcmd.u32;
1032}
1033
1034/** Create and initialize connected SATA structure device
1035 *
1036 * @param ahci AHCI device structure.
1037 * @param dev DDF device structure.
1038 * @param port AHCI port structure.
1039 * @param port_num Number of AHCI port with existing SATA device.
1040 *
1041 * @return EOK if succeed, error code otherwise.
1042 *
1043 */
1044static int ahci_sata_create(ahci_dev_t *ahci, ddf_dev_t *dev,
1045 volatile ahci_port_t *port, unsigned int port_num)
1046{
1047 ddf_fun_t *fun = NULL;
1048 int rc;
1049
1050 sata_dev_t *sata = ahci_sata_allocate(ahci, port);
1051 if (sata == NULL)
1052 return EINTR;
1053
1054 /* Set pointers between SATA and AHCI structures. */
1055 sata->ahci = ahci;
1056 sata->port_num = port_num;
1057 ahci->sata_devs[port_num] = sata;
1058
1059 /* Initialize synchronization structures */
1060 fibril_mutex_initialize(&sata->lock);
1061 fibril_mutex_initialize(&sata->event_lock);
1062 fibril_condvar_initialize(&sata->event_condvar);
1063
1064 ahci_sata_hw_start(sata);
1065
1066 /* Identify device. */
1067 if (ahci_identify_device(sata) != EOK)
1068 goto error;
1069
1070 /* Set required UDMA mode */
1071 if (ahci_set_highest_ultra_dma_mode(sata) != EOK)
1072 goto error;
1073
1074 /* Add device to the system */
1075 char sata_dev_name[16];
1076 snprintf(sata_dev_name, 16, "ahci_%u", sata_devices_count);
1077
1078 fibril_mutex_lock(&sata_devices_count_lock);
1079 sata_devices_count++;
1080 fibril_mutex_unlock(&sata_devices_count_lock);
1081
1082 rc= ddf_fun_set_name(sata->fun, sata_dev_name);
1083 if (rc != EOK) {
1084 ddf_msg(LVL_ERROR, "Failed setting function name.");
1085 goto error;
1086 }
1087
1088 ddf_fun_set_ops(fun, &ahci_ops);
1089
1090 rc = ddf_fun_bind(fun);
1091 if (rc != EOK) {
1092 ddf_msg(LVL_ERROR, "Failed binding function.");
1093 goto error;
1094 }
1095
1096 return EOK;
1097
1098error:
1099 sata->is_invalid_device = true;
1100 if (fun != NULL)
1101 ddf_fun_destroy(fun);
1102
1103 return EINTR;
1104}
1105
1106/** Create and initialize all SATA structure devices for connected SATA drives.
1107 *
1108 * @param ahci AHCI device structure.
1109 * @param dev DDF device structure.
1110 *
1111 */
1112static void ahci_sata_devices_create(ahci_dev_t *ahci, ddf_dev_t *dev)
1113{
1114 for (unsigned int port_num = 0; port_num < AHCI_MAX_PORTS; port_num++) {
1115 /* Active ports only */
1116 if (!(ahci->memregs->ghc.pi & (1 << port_num)))
1117 continue;
1118
1119 volatile ahci_port_t *port = ahci->memregs->ports + port_num;
1120
1121 /* Active devices only */
1122 ahci_port_ssts_t pxssts;
1123 pxssts.u32 = port->pxssts;
1124 if (pxssts.det != AHCI_PORT_SSTS_DET_ACTIVE)
1125 continue;
1126
1127 ahci_sata_create(ahci, dev, port, port_num);
1128 }
1129}
1130
1131/** Create AHCI device structure, intialize it and register interrupt routine.
1132 *
1133 * @param dev DDF device structure.
1134 *
1135 * @return AHCI device structure if succeed, NULL otherwise.
1136 *
1137 */
1138static ahci_dev_t *ahci_ahci_create(ddf_dev_t *dev)
1139{
1140 ahci_dev_t *ahci = ddf_dev_data_alloc(dev, sizeof(ahci_dev_t));
1141 if (!ahci)
1142 return NULL;
1143
1144 /* Connect to parent device */
1145 ahci->parent_sess = ddf_dev_parent_sess_create(dev, EXCHANGE_SERIALIZE);
1146 if (ahci->parent_sess == NULL)
1147 return NULL;
1148
1149 ahci->dev = dev;
1150
1151 hw_res_list_parsed_t hw_res_parsed;
1152 hw_res_list_parsed_init(&hw_res_parsed);
1153 if (hw_res_get_list_parsed(ahci->parent_sess, &hw_res_parsed, 0) != EOK)
1154 goto error_get_res_parsed;
1155
1156 /* Map AHCI registers. */
1157 ahci->memregs = NULL;
1158
1159 physmem_map(RNGABS(hw_res_parsed.mem_ranges.ranges[0]),
1160 AHCI_MEMREGS_PAGES_COUNT, AS_AREA_READ | AS_AREA_WRITE,
1161 (void **) &ahci->memregs);
1162 if (ahci->memregs == NULL)
1163 goto error_map_registers;
1164
1165 /* Register interrupt handler */
1166 ahci_ranges[0].base = RNGABS(hw_res_parsed.mem_ranges.ranges[0]);
1167 ahci_ranges[0].size = sizeof(ahci_memregs_t);
1168
1169 for (unsigned int port = 0; port < AHCI_MAX_PORTS; port++) {
1170 size_t base = port * 7;
1171
1172 ahci_cmds[base].addr =
1173 ((uint32_t *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
1174 AHCI_PORTS_REGISTERS_OFFSET + port * AHCI_PORT_REGISTERS_SIZE +
1175 AHCI_PORT_IS_REGISTER_OFFSET;
1176 ahci_cmds[base + 2].addr = ahci_cmds[base].addr;
1177
1178 ahci_cmds[base + 3].addr =
1179 ((uint32_t *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
1180 AHCI_GHC_IS_REGISTER_OFFSET;
1181 ahci_cmds[base + 4].addr = ahci_cmds[base + 3].addr;
1182 }
1183
1184 irq_code_t ct;
1185 ct.cmdcount = sizeof(ahci_cmds) / sizeof(irq_cmd_t);
1186 ct.cmds = ahci_cmds;
1187 ct.rangecount = sizeof(ahci_ranges) / sizeof(irq_pio_range_t);
1188 ct.ranges = ahci_ranges;
1189
1190 int rc = register_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0],
1191 ahci_interrupt, &ct);
1192 if (rc != EOK) {
1193 ddf_msg(LVL_ERROR, "Failed registering interrupt handler.");
1194 goto error_register_interrupt_handler;
1195 }
1196
1197 rc = ahci_enable_interrupt(hw_res_parsed.irqs.irqs[0]);
1198 if (rc != EOK) {
1199 ddf_msg(LVL_ERROR, "Failed enable interupt.");
1200 goto error_enable_interrupt;
1201 }
1202
1203 hw_res_list_parsed_clean(&hw_res_parsed);
1204 return ahci;
1205
1206error_enable_interrupt:
1207 unregister_interrupt_handler(dev, hw_res_parsed.irqs.irqs[0]);
1208
1209error_register_interrupt_handler:
1210 // FIXME: unmap physical memory
1211
1212error_map_registers:
1213 hw_res_list_parsed_clean(&hw_res_parsed);
1214
1215error_get_res_parsed:
1216 free(ahci);
1217 return NULL;
1218}
1219
1220/** Initialize and start AHCI hardware device.
1221 *
1222 * @param ahci AHCI device.
1223 *
1224 */
1225static void ahci_ahci_hw_start(ahci_dev_t *ahci)
1226{
1227 /* Disable command completion coalescing feature */
1228 ahci_ghc_ccc_ctl_t ccc;
1229
1230 ccc.u32 = ahci->memregs->ghc.ccc_ctl;
1231 ccc.en = 0;
1232 ahci->memregs->ghc.ccc_ctl = ccc.u32;
1233
1234 /* Set master latency timer. */
1235 pci_config_space_write_8(ahci->parent_sess, AHCI_PCI_MLT, 32);
1236
1237 /* Enable PCI interrupt and bus mastering */
1238 ahci_pcireg_cmd_t cmd;
1239
1240 pci_config_space_read_16(ahci->parent_sess, AHCI_PCI_CMD, &cmd.u16);
1241 cmd.id = 0;
1242 cmd.bme = 1;
1243 pci_config_space_write_16(ahci->parent_sess, AHCI_PCI_CMD, cmd.u16);
1244
1245 /* Enable AHCI and interrupt. */
1246 ahci->memregs->ghc.ghc = AHCI_GHC_GHC_AE | AHCI_GHC_GHC_IE;
1247}
1248
1249/** AHCI device driver initialization
1250 *
1251 * Create and initialize all SATA structure devices for connected
1252 * SATA drives.
1253 *
1254 * @param dev DDF device structure.
1255 *
1256 * @return EOK if succeed, error code otherwise.
1257 *
1258 */
1259static int ahci_dev_add(ddf_dev_t *dev)
1260{
1261 ahci_dev_t *ahci = ahci_ahci_create(dev);
1262 if (ahci == NULL)
1263 goto error;
1264
1265 /* Start AHCI hardware. */
1266 ahci_ahci_hw_start(ahci);
1267
1268 /* Create device structures for sata devices attached to AHCI. */
1269 ahci_sata_devices_create(ahci, dev);
1270
1271 return EOK;
1272
1273error:
1274 return EINTR;
1275}
1276
1277/*----------------------------------------------------------------------------*/
1278/*-- Helpers and utilities ---------------------------------------------------*/
1279/*----------------------------------------------------------------------------*/
1280
1281/** Convert SATA model name
1282 *
1283 * Convert SATA model name from machine format returned by
1284 * identify device command to human readable form.
1285 *
1286 * @param src Source buffer with device name in machine format.
1287 * @param dst Buffer for human readable string, minimum size is 41 chars.
1288 *
1289 */
1290static void ahci_get_model_name(uint16_t *src, char *dst)
1291{
1292 uint8_t model[40];
1293 memset(model, 0, 40);
1294
1295 for (unsigned int i = 0; i < 20; i++) {
1296 uint16_t w = src[i];
1297 model[2 * i] = w >> 8;
1298 model[2 * i + 1] = w & 0x00ff;
1299 }
1300
1301 uint32_t len = 40;
1302 while ((len > 0) && (model[len - 1] == 0x20))
1303 len--;
1304
1305 size_t pos = 0;
1306 for (unsigned int i = 0; i < len; i++) {
1307 uint8_t c = model[i];
1308 if (c >= 0x80)
1309 c = '?';
1310
1311 chr_encode(c, dst, &pos, 40);
1312 }
1313
1314 dst[pos] = '\0';
1315}
1316
1317/** Enable interrupt using SERVICE_IRC.
1318 *
1319 * @param irq Requested irq number.
1320 *
1321 * @return EOK if succeed, error code otherwise.
1322 *
1323 */
1324static int ahci_enable_interrupt(int irq)
1325{
1326 async_sess_t *irc_sess = NULL;
1327 irc_sess = service_connect_blocking(EXCHANGE_SERIALIZE, SERVICE_IRC, 0, 0);
1328 if (!irc_sess)
1329 return EINTR;
1330
1331 async_exch_t *exch = async_exchange_begin(irc_sess);
1332 const int rc = async_req_1_0(exch, IRC_ENABLE_INTERRUPT, irq);
1333 async_exchange_end(exch);
1334
1335 async_hangup(irc_sess);
1336 return rc;
1337}
1338
1339/*----------------------------------------------------------------------------*/
1340/*-- AHCI Main routine -------------------------------------------------------*/
1341/*----------------------------------------------------------------------------*/
1342
1343int main(int argc, char *argv[])
1344{
1345 printf("%s: HelenOS AHCI device driver\n", NAME);
1346 ddf_log_init(NAME);
1347 fibril_mutex_initialize(&sata_devices_count_lock);
1348 return ddf_driver_main(&ahci_driver);
1349}
Note: See TracBrowser for help on using the repository browser.