/* * Copyright (c) 2010-2013 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. */ #define NDEBUG #include #include #include #include #include bool ata_execute_io_command(uint8_t command, uint32_t lba, uint32_t sector_count_32) { assert(sector_count_32 >= 1); assert(sector_count_32 <= 256); assert(ata_is_drive_ready_for_selection()); ATA->write.head = (uint8_t) (0xe0 | ((lba >> 24) & 0x0f)); ata_wait_400_nano_seconds(); ata_wait_for_drive_ready(); /* * It seems that the write to the ATA device registers is sometimes not * successful. The write is retried until the read back values are all right * or a timeout is reached. */ bool ok = false; uint8_t sector = (uint8_t) lba; uint8_t cylinder_low = (uint8_t) (lba >> 8); uint8_t cylinder_high = (uint8_t) (lba >> 16); uint8_t sector_count = (uint8_t) sector_count_32; int i; for (i = 0; !ok && i < 100; ++i) { ATA->write.sector = sector; ATA->write.cylinder_low = cylinder_low; ATA->write.cylinder_high = cylinder_high; ATA->write.sector_count = sector_count; uint8_t actual_sector = ATA->read.sector; uint8_t actual_cylinder_low = ATA->read.cylinder_low; uint8_t actual_cylinder_high = ATA->read.cylinder_high; uint8_t actual_sector_count = ATA->read.sector_count; ok = actual_cylinder_high == cylinder_high && actual_cylinder_low == cylinder_low && actual_sector == sector && actual_sector_count == sector_count; } if (ok) { ATA->write.command = command; } return ok; } void ata_reset_device(void) { /* ATA/ATAPI-7 V2, 11.2 Software reset protocol */ ATA->write.control = DCTRL_SRST; rtems_bsp_delay(5); ATA->write.control = 0; rtems_bsp_delay(2000); ata_wait_for_not_busy(); } bool ata_set_transfer_mode(uint8_t mode) { assert(ata_is_drive_ready_for_selection()); ATA->write.head = 0xe0; ata_wait_400_nano_seconds(); ata_wait_for_drive_ready(); ATA->write.feature = 0x3; ATA->write.sector_count = mode; ATA->write.command = 0xef; ata_wait_for_not_busy(); return ata_check_status(); } static bool probe(void) { bool card_present = true; #ifdef MPC5200_BOARD_BRS5L volatile struct mpc5200_gpt *gpt = &mpc5200.gpt[GPT2]; /* Enable card detection on GPT2 */ gpt->emsel = (GPT_EMSEL_GPIO_IN | GPT_EMSEL_TIMER_MS_GPIO); /* Check for card detection (-CD0) */ if ((gpt->status & GPT_STATUS_PIN) != 0) { card_present = false; } #endif return card_present; } static void create_lock(ata_driver *self) { rtems_status_code sc = rtems_semaphore_create( rtems_build_name('A', 'T', 'A', ' '), 1, RTEMS_LOCAL | RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY, 0, &self->lock ); if (sc != RTEMS_SUCCESSFUL) { bsp_fatal(MPC5200_FATAL_ATA_LOCK_CREATE); } } static void destroy_lock(const ata_driver *self) { rtems_status_code sc = rtems_semaphore_delete(self->lock); if (sc != RTEMS_SUCCESSFUL) { bsp_fatal(MPC5200_FATAL_ATA_LOCK_DESTROY); } } void ata_driver_create(ata_driver *self, const char *device_file_path, rtems_block_device_ioctl io_control) { self->card_present = probe(); if (ata_driver_is_card_present(self)) { create_lock(self); ata_reset_device(); uint16_t sector_buffer[256]; ata_dev_t ata_device; rtems_status_code sc = ata_identify_device(0, 0, sector_buffer, &ata_device); if (sc == RTEMS_SUCCESSFUL && ata_device.lba_avaible) { sc = ide_controller_config_io_speed(0, ata_device.modes_available); if (sc == RTEMS_SUCCESSFUL) { sc = rtems_blkdev_create( device_file_path, ATA_SECTOR_SIZE, ata_device.lba_sectors, io_control, self ); if (sc != RTEMS_SUCCESSFUL) { bsp_fatal(MPC5200_FATAL_ATA_DISK_CREATE); } } } } } void ata_driver_destroy(ata_driver *self) { destroy_lock(self); /* TODO */ assert(0); } static bool transfer_pio_polled(ata_driver *self, bool read, rtems_blkdev_sg_buffer *sg, size_t sg_count) { bool ok = true; ata_sg_context sg_context; ata_sg_create(&sg_context, sg, sg_count); rtems_blkdev_bnum start_sector = ata_sg_get_start_sector(&sg_context); rtems_blkdev_bnum sector_count = ata_sg_get_sector_count(&sg_context); rtems_blkdev_bnum relative_sector = 0; uint8_t command = ata_read_or_write_sectors_command(read); while (ok && relative_sector < sector_count) { rtems_blkdev_bnum remaining_sectors = sector_count - relative_sector; rtems_blkdev_bnum transfer_count = ata_max_transfer_count(remaining_sectors); rtems_blkdev_bnum transfer_end = relative_sector + transfer_count; ok = ata_execute_io_command(command, start_sector + relative_sector, transfer_count); rtems_blkdev_bnum transfer; for (transfer = relative_sector; ok && transfer < transfer_end; ++transfer) { uint16_t *current = ata_sg_get_sector_data_begin(&sg_context, transfer); uint16_t *end = ata_sg_get_sector_data_end(&sg_context, current); ok = ata_wait_for_data_request(); if (ok) { if (read) { while (current != end) { *current = ATA->read.data; ++current; } } else { while (current != end) { ATA->write.data = *current; ++current; } } } } if (ok) { ata_wait_for_not_busy(); ok = ata_check_status(); } relative_sector += ATA_PER_TRANSFER_SECTOR_COUNT_MAX; } return ok; } int ata_driver_io_control_pio_polled( rtems_disk_device *dd, uint32_t cmd, void *arg ) { return ata_driver_io_control(dd, cmd, arg, transfer_pio_polled); }