diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-03 07:20:11 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-04 10:13:28 +0200 |
commit | 27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch) | |
tree | def0664dcddc53fd5d599b455c64f76ca2293606 /bsps/shared/dev/ide | |
parent | bsps: Move config macros to RTEMS_BSP_CONFIGURE (diff) | |
download | rtems-27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a.tar.bz2 |
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/shared/dev/ide')
-rw-r--r-- | bsps/shared/dev/ide/ata.c | 1360 | ||||
-rw-r--r-- | bsps/shared/dev/ide/ata_util.c | 215 | ||||
-rw-r--r-- | bsps/shared/dev/ide/ide_controller.c | 200 |
3 files changed, 1775 insertions, 0 deletions
diff --git a/bsps/shared/dev/ide/ata.c b/bsps/shared/dev/ide/ata.c new file mode 100644 index 0000000000..7bb3f6ec73 --- /dev/null +++ b/bsps/shared/dev/ide/ata.c @@ -0,0 +1,1360 @@ +/* + * ata.c + * + * ATA RTEMS driver. ATA driver is hardware independant implementation of + * ATA-2 standard, working draft X3T10/0948D, revision 4c. ATA driver bases + * on RTEMS IDE controller driver. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Authors: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + */ +#include <errno.h> +#include <rtems/chain.h> +#include <assert.h> +#include <string.h> /* for "memset" declaration */ + +#include <rtems/diskdevs.h> +#include <rtems/blkdev.h> +#include <libchip/ide_ctrl_io.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ata_internal.h> +#include <libchip/ata.h> + +#define ATA_DEBUG 0 + +#if ATA_DEBUG +#include <stdio.h> +bool ata_trace; +#define ata_printf if (ata_trace) printf +#endif + +#if CPU_SIMPLE_VECTORED_INTERRUPTS != TRUE +#include <rtems/irq.h> +#define ATA_IRQ_CHAIN_MAX_CNT 4 /* support up to 4 ATA devices */ +typedef struct { + rtems_irq_number name; + rtems_chain_control irq_chain; +} ata_irq_chain_t; + +ata_irq_chain_t ata_irq_chain[ATA_IRQ_CHAIN_MAX_CNT]; +int ata_irq_chain_cnt = 0; +#endif + +static rtems_id ata_lock; +static void +rtems_ata_lock (void) +{ + rtems_status_code sc = rtems_semaphore_obtain (ata_lock, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR); +} + +static void +rtems_ata_unlock (void) +{ + rtems_status_code sc = rtems_semaphore_release (ata_lock); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR); +} + +#define RTEMS_ATA_LOCK_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \ + RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) + +/* FIXME: case if ATA device is FLASH device need more attention */ +#undef ATA_DEV_IS_FLASH_DISK + +/* Array indexed by controllers minor number */ +static ata_ide_ctrl_t ata_ide_ctrls[IDE_CTRL_MAX_MINOR_NUMBER]; + +/* + * Mapping from ATA-minor numbers to + * controller-minor and device on this controller. + */ +static ata_ide_dev_t ata_devs[2 * IDE_CTRL_MAX_MINOR_NUMBER]; +static int ata_devs_number; + +/* Flag meaning that ATA driver has already been initialized */ +static bool ata_initialized = false; + + +/* task and queue used for asynchronous I/O operations */ +static rtems_id ata_task_id; +static rtems_id ata_queue_id; + +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE +/* Mapping of interrupt vectors to devices */ +static rtems_chain_control ata_int_vec[ATA_MAX_RTEMS_INT_VEC_NUMBER + 1]; +#endif + +static void +ata_process_request(rtems_device_minor_number ctrl_minor); + +static void +ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor, + ata_req_t *areq); + +/* + * read/write, open/close and ioctl are provided by general block device + * driver. Only initialization and ata-specific ioctl are here. + */ + +/* ata_io_data_request -- + * Form read/write request for an ATA device and enqueue it to + * IDE controller. + * + * PARAMETERS: + * device - device identifier + * req - read/write request from block device driver + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +static rtems_status_code +ata_io_data_request(dev_t device, rtems_blkdev_request *req) +{ + ata_req_t *areq; /* ATA request */ + rtems_device_minor_number rel_minor; /* relative minor which indexes + * ata_devs array + */ + rtems_device_minor_number ctrl_minor; + uint8_t dev; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* get controller which serves the ATA device */ + ctrl_minor = ata_devs[rel_minor].ctrl_minor; + + /* get ATA device identifier (0 or 1) */ + dev = ata_devs[rel_minor].device; + + areq = malloc(sizeof(ata_req_t)); + if (areq == NULL) + { + rtems_blkdev_request_done(req, RTEMS_NO_MEMORY); + return RTEMS_SUCCESSFUL; + } + + areq->breq = req; + areq->cnt = req->bufnum; + areq->cbuf = 0; + areq->pos = 0; + + /* set up registers masks */ + areq->regs.to_write = ATA_REGISTERS_POSITION; + areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS); + + /* choose device on the controller for which the command will be issued */ + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] = + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); + + /* Find ATA command and its type */ + if (ATA_DEV_INFO(ctrl_minor, dev).mode_active & ATA_MODES_DMA) + { + /* XXX: never has been tested */ + areq->type = ATA_COMMAND_TYPE_DMA; + if (req->req == RTEMS_BLKDEV_REQ_READ) + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_DMA; + else + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_DMA; + } + else + { + if (req->req == RTEMS_BLKDEV_REQ_READ) + { + areq->type = ATA_COMMAND_TYPE_PIO_IN; + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_SECTORS; +#if ATA_DEBUG + ata_printf("ata_io_data_request: type: READ: %lu, %lu cmd:%02x\n", + req->bufs[0].block, req->bufnum, + areq->regs.regs[IDE_REGISTER_COMMAND]); +#endif + } + else + { + areq->type = ATA_COMMAND_TYPE_PIO_OUT; + areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_SECTORS; +#if ATA_DEBUG + ata_printf("ata_io_data_request: type: WRITE: %lu, %lu cmd:%02x\n", + req->bufs[0].block, req->bufnum, + areq->regs.regs[IDE_REGISTER_COMMAND]); +#endif + } + } + + /* + * Fill position registers + */ + if (ATA_DEV_INFO(ctrl_minor, dev).lba_avaible) + { + uint32_t start = req->bufs[0].block; + areq->regs.regs[IDE_REGISTER_LBA0] = (uint8_t)start; + areq->regs.regs[IDE_REGISTER_LBA1] = (uint8_t)(start >> 8); + areq->regs.regs[IDE_REGISTER_LBA2] = (uint8_t)(start >> 16); + /* Set as the head register write above */ + areq->regs.regs[IDE_REGISTER_LBA3] |= (uint8_t) (start >> 24); + areq->regs.regs[IDE_REGISTER_LBA3] |= IDE_REGISTER_LBA3_L; + } + else + { + uint32_t count = req->bufs[0].block; + + areq->regs.regs[IDE_REGISTER_SECTOR_NUMBER] = + (count % ATA_DEV_INFO(ctrl_minor, dev).sectors) + 1; + + /* now count = number of tracks: */ + count /= ATA_DEV_INFO(ctrl_minor, dev).sectors; + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= + (count / ATA_DEV_INFO(ctrl_minor, dev).cylinders); + + /* now count = number of cylinders */ + count %= ATA_DEV_INFO(ctrl_minor, dev).cylinders; + areq->regs.regs[IDE_REGISTER_CYLINDER_LOW] = (uint8_t)count; + areq->regs.regs[IDE_REGISTER_CYLINDER_HIGH] = (uint8_t)(count >> 8); + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &= + ~IDE_REGISTER_DEVICE_HEAD_L; + } + + /* + * Fill sector count register. We have a number of buffers (bufnum) which + * can be of a specific length (bufs[0].length / ATA_SECTOR_SIZE). + */ + areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = + areq->breq->bufnum * (areq->breq->bufs[0].length / ATA_SECTOR_SIZE); + + /* add request to the queue of awaiting requests to the controller */ + ata_add_to_controller_queue(ctrl_minor, areq); + + return RTEMS_SUCCESSFUL; +} + +/* ata_non_data_request -- + * Form and serve request of NON DATA type for an ATA device. + * Processing of NON DATA request is SYNChronous operation. + * + * PARAMETERS: + * device - device identifier + * cmd - command + * argp - arguments for command + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +static rtems_status_code +ata_non_data_request(dev_t device, uint32_t cmd, void *argp) +{ + rtems_status_code rc; + ata_req_t *areq; /* ATA request */ + rtems_device_minor_number rel_minor; /* relative minor which indexes + * ata_devs array + */ + rtems_device_minor_number ctrl_minor; + uint8_t dev; + ata_queue_msg_t msg; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* get controller which serves the ATA device */ + ctrl_minor = ata_devs[rel_minor].ctrl_minor; + + /* get ATA device identifier (0 or 1) */ + dev = ata_devs[rel_minor].device; + + /* form the request */ + areq = malloc(sizeof(ata_req_t)); + if (areq == NULL) + { + return RTEMS_NO_MEMORY; + } + memset(areq, 0, sizeof(ata_req_t)); + + areq->type = ATA_COMMAND_TYPE_NON_DATA; + areq->regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); + areq->breq = NULL; + areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR); + + /* + * depending on command fill command register and additional registers + * which are needed for command execution + */ + switch(cmd) + { + case ATAIO_SET_MULTIPLE_MODE: + areq->regs.regs[IDE_REGISTER_COMMAND] = + ATA_COMMAND_SET_MULTIPLE_MODE; + areq->regs.to_write |= + ATA_REGISTERS_VALUE(IDE_REGISTER_SECTOR_COUNT); + areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = *(uint8_t*)argp; + break; + + default: + free(areq); + return RTEMS_INVALID_NUMBER; + break; + } + + rc = rtems_semaphore_create(rtems_build_name('I', 'D', 'E', 'S'), + 0, + RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &(areq->sema)); + if (rc != RTEMS_SUCCESSFUL) + { + free(areq); + return rc; + } + + ata_add_to_controller_queue(ctrl_minor, areq); + + /* wait for request processing... */ + rc = rtems_semaphore_obtain(areq->sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + { + free(areq); + return rc; + } + + rtems_semaphore_delete(areq->sema); + + /* + * if no error occurred and if necessary, update internal ata driver data + * structures to reflect changes (in device configuration, for example) + */ + if (areq->status == RTEMS_SUCCESSFUL) + { + switch(cmd) + { + case ATAIO_SET_MULTIPLE_MODE: + /* invalid operation now */ + default: + rc = RTEMS_INVALID_NUMBER; + break; + } + } + else + { + /* XXX: should be correct error processing: for ex, may be + * ABRT and then we should return RTEMS_NOT_IMPLEMENTED + */ + rc = RTEMS_IO_ERROR; + } + + /* tell ata driver that controller ready to serve next request */ + ATA_SEND_EVT(msg, ATA_MSG_SUCCESS_EVT, ctrl_minor, 0); + + return rc; +} + +/* ata_process_request -- + * Get first request from controller's queue and process it. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * + * RETURNS: + * NONE + */ +static void +ata_process_request(rtems_device_minor_number ctrl_minor) +{ + ata_req_t *areq; + uint16_t byte; /* emphasize that only 8 low bits is meaningful */ + ata_queue_msg_t msg; + uint8_t i; +#if 0 + uint8_t dev; +#endif + uint16_t val; + ISR_Level level; + + /* if no requests to controller then do nothing */ + if (rtems_chain_is_empty(&ata_ide_ctrls[ctrl_minor].reqs)) + return; + + /* get first request in the controller's queue */ + _ISR_Local_disable(level); + areq = (ata_req_t *)rtems_chain_first(&ata_ide_ctrls[ctrl_minor].reqs); + _ISR_Local_enable(level); + +#if 0 + /* get ATA device identifier (0 or 1) */ + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; +#endif + + /* execute device select protocol */ + ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD, + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]); + + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + } while ((byte & IDE_REGISTER_STATUS_BSY) || + (!(byte & IDE_REGISTER_STATUS_DRDY))); + + /* fill in all necessary registers on the controller */ + for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++) + { + uint32_t reg = (1 << i); + if (areq->regs.to_write & reg) + ide_controller_write_register(ctrl_minor, i, areq->regs.regs[i]); + } + +#if ATA_DEBUG + ata_printf("ata_process_request: type: %d\n", areq->type); +#endif + + /* continue to execute ATA protocols depending on type of request */ + if (areq->type == ATA_COMMAND_TYPE_PIO_OUT) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + if (byte & IDE_REGISTER_STATUS_DRQ) + { + if (areq->cnt) + { + int ccbuf = areq->cbuf; + ide_controller_write_data_block(ctrl_minor, + areq->breq->bufs[0].length * areq->cnt, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + ccbuf = areq->cbuf - ccbuf; + areq->cnt -= ccbuf; + } + } + else + { + if (IDE_Controller_Table[ctrl_minor].int_driven == false) + { + ide_controller_read_register( + ctrl_minor, + IDE_REGISTER_ALTERNATE_STATUS_OFFSET, + &val); + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + + ATA_SEND_EVT(msg, ATA_MSG_ERROR_EVT, ctrl_minor, + RTEMS_IO_ERROR); + } + } + } + + if (IDE_Controller_Table[ctrl_minor].int_driven == false) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } +} + +/* ata_request_done -- + * Extract request from controller queue, execute callback if necessary + * and process next request for the controller. + * + * PARAMETERS: + * areq - ATA request + * ctrl_minor - controller identifier + * status - status with which request has been done + * error - error, if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * NONE + */ +static inline void +ata_request_done(ata_req_t *areq, rtems_device_minor_number ctrl_minor, + rtems_status_code status) +{ + assert(areq); + +#if ATA_DEBUG + ata_printf("ata_request_done: entry\n"); +#endif + + ATA_EXEC_CALLBACK(areq, status); + rtems_chain_extract(&areq->link); + + if (!rtems_chain_is_empty(&ata_ide_ctrls[ctrl_minor].reqs)) + { + free(areq); + ata_process_request(ctrl_minor); + return; + } + + free(areq); + +#if ATA_DEBUG + ata_printf("ata_request_done: exit\n"); +#endif +} + +/* ata_non_data_request_done -- + * Set up request status and release request's semaphore. + * + * PARAMETERS: + * areq - ATA request + * ctrl_minor - controller identifier + * status - status with which request has been done + * error - error, if status != RTEMS_SUCCESSFUL + * + * RETURNS: + * NONE + */ +static inline void +ata_non_data_request_done(ata_req_t *areq, + rtems_device_minor_number ctrl_minor, + rtems_status_code status, int info) +{ +#if ATA_DEBUG + ata_printf("ata_non_data_request_done: entry\n"); +#endif + + areq->status = status; + areq->info = info; + rtems_semaphore_release(areq->sema); +} + + +/* ata_add_to_controller_queue -- + * Add request to the controller's queue. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static void +ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor, + ata_req_t *areq) +{ + rtems_ata_lock(); + + rtems_chain_append(&ata_ide_ctrls[ctrl_minor].reqs, &areq->link); + if (rtems_chain_has_only_one_node(&ata_ide_ctrls[ctrl_minor].reqs)) + { + + ata_queue_msg_t msg; + +#if ATA_DEBUG_DOES_NOT_WORK_WITH_QEMU + uint16_t val; + /* + * read IDE_REGISTER_ALTERNATE_STATUS instead IDE_REGISTER_STATUS + * to prevent clearing of pending interrupt + */ + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ALTERNATE_STATUS, + &val); + if (val & IDE_REGISTER_STATUS_BSY) + return; +#endif + ATA_SEND_EVT(msg, ATA_MSG_PROCESS_NEXT_EVT, ctrl_minor, 0); + } + + rtems_ata_unlock(); +} + + +/* ata_interrupt_handler -- + * ATA driver interrrupt handler. If interrrupt happend it mapped it to + * controller (controllerS, if a number of controllers share one int line) + * and generates ATA event(s). + * + * PARAMETERS: + * vec - interrupt vector + * + * RETURNS: + * NONE + */ +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE +static rtems_isr ata_interrupt_handler(rtems_vector_number vec) +{ + rtems_chain_node *the_node = rtems_chain_first(&ata_int_vec[vec]); + ata_queue_msg_t msg; + uint16_t byte; /* emphasize that only 8 low bits is meaningful */ + + for ( ; !rtems_chain_is_tail(&ata_int_vec[vec], the_node) ; ) + { + /* if (1) - is temporary hack - currently I don't know how to identify + * controller which asserted interrupt if few controllers share one + * interrupt line + */ + if (1) + { + msg.ctrl_minor = ((ata_int_st_t *)the_node)->ctrl_minor; + ide_controller_read_register(msg.ctrl_minor, IDE_REGISTER_STATUS, + &byte); + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, msg.ctrl_minor, 0); + } + the_node = the_node->next; + } +} +#else +static void ata_interrupt_handler(rtems_irq_hdl_param handle) +{ + uintptr_t ata_irq_chain_index = (uintptr_t) handle; + rtems_chain_node *the_node = + rtems_chain_last(&ata_irq_chain[ata_irq_chain_index].irq_chain); + ata_queue_msg_t msg; + uint16_t byte; /* emphasize that only 8 low bits is meaningful */ + + + for ( ; !rtems_chain_is_tail(&ata_irq_chain[ata_irq_chain_index].irq_chain, + the_node) ; ) + { + /* if (1) - is temporary hack - currently I don't know how to identify + * controller which asserted interrupt if few controllers share one + * interrupt line + */ + if (1) + { + msg.ctrl_minor = ((ata_int_st_t *)the_node)->ctrl_minor; + ide_controller_read_register(msg.ctrl_minor, IDE_REGISTER_STATUS, + &byte); + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, msg.ctrl_minor, 0); + } + the_node = the_node->next; + } +} + +static void ata_interrupt_on(const rtems_irq_connect_data *ptr) + { + + /* enable ATA device interrupt */ + ide_controller_write_register(0, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + 0x00 + ); + } + + +static void ata_interrupt_off(const rtems_irq_connect_data *ptr) + { + + /* disable ATA device interrupt */ + ide_controller_write_register(0, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + IDE_REGISTER_DEVICE_CONTROL_nIEN + ); + } + + +static int ata_interrupt_isOn(const rtems_irq_connect_data *ptr) + { + uint16_t byte; /* emphasize that only 8 low bits is meaningful */ + + /* return int. status od ATA device */ + ide_controller_read_register(0, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + &byte + ); + + return !(byte & IDE_REGISTER_DEVICE_CONTROL_nIEN); + } + + +static rtems_irq_connect_data ata_irq_data = + { + + 0, /* filled out before use... */ + ata_interrupt_handler,/* filled out before use... */ + NULL, + ata_interrupt_on, + ata_interrupt_off, + ata_interrupt_isOn + }; +#endif + +/* ata_pio_in_protocol -- + * ATA PIO_IN protocol implementation, see specification + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static inline void +ata_pio_in_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq) +{ + uint16_t val; +#if 0 + uint8_t dev; +#endif + ata_queue_msg_t msg; + +#if 0 + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; +#endif + + if (areq->cnt) + { + int ccbuf = areq->cbuf; + ide_controller_read_data_block(ctrl_minor, + areq->breq->bufs[0].length * areq->cnt, + areq->breq->bufs, &areq->cbuf, &areq->pos); + ccbuf = areq->cbuf - ccbuf; + areq->cnt -= ccbuf; + } + + if (areq->cnt == 0) + { + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL); + } + else if (IDE_Controller_Table[ctrl_minor].int_driven == false) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val); + } while (val & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } +} + +/* ata_pio_out_protocol -- + * ATA PIO_OUT protocol implementation, see specification + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +static inline void +ata_pio_out_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq) +{ + uint16_t val; +#if 0 + uint8_t dev; +#endif + ata_queue_msg_t msg; + +#if ATA_DEBUG + ata_printf("ata_pio_out_protocol:\n"); +#endif + +#if 0 + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; +#endif + + if (areq->cnt == 0) + { + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL); + } + else + { + if (areq->cnt) + { + int ccbuf = areq->cbuf; + ide_controller_write_data_block(ctrl_minor, + areq->breq->bufs[0].length * areq->cnt, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + ccbuf = areq->cbuf - ccbuf; + areq->cnt -= ccbuf; + } + if (IDE_Controller_Table[ctrl_minor].int_driven == false) + { + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + } while (val & IDE_REGISTER_STATUS_BSY); + + ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0); + } + } +} + +/* ata_queue_task -- + * Task which manages ATA driver events queue. + * + * PARAMETERS: + * arg - ignored + * + * RETURNS: + * NONE + * + * NOTES: + * should be non-preemptive + */ +static rtems_task +ata_queue_task(rtems_task_argument arg) +{ + ata_queue_msg_t msg; + size_t size; + ata_req_t *areq; + rtems_device_minor_number ctrl_minor; + uint16_t val; + uint16_t val1; + rtems_status_code rc; + ISR_Level level; + + rtems_ata_lock(); + + while (1) + { + rtems_ata_unlock(); + + /* get event which has happend */ + rc = rtems_message_queue_receive(ata_queue_id, &msg, &size, RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (rc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + + /* get controller on which event has happend */ + ctrl_minor = msg.ctrl_minor; + + rtems_ata_lock(); + + /* get current request to the controller */ + _ISR_Local_disable(level); + areq = (ata_req_t *)rtems_chain_first(&ata_ide_ctrls[ctrl_minor].reqs); + _ISR_Local_enable(level); + + switch(msg.type) + { + case ATA_MSG_PROCESS_NEXT_EVT: + /* process next request in the controller queue */ + ata_process_request(ctrl_minor); + break; + + case ATA_MSG_SUCCESS_EVT: + /* + * finish processing of current request with successful + * status and start processing of the next request in the + * controller queue + */ + ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL); + break; + + case ATA_MSG_ERROR_EVT: + /* + * finish processing of current request with error + * status and start processing of the next request in the + * controller queue + */ + ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR); + break; + + case ATA_MSG_GEN_EVT: + /* + * continue processing of the current request to the + * controller according to current request state and + * ATA protocol + */ + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, + &val); + /* process error case */ + if (val & IDE_REGISTER_STATUS_ERR) + { + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ERROR, + &val); + if (val & (IDE_REGISTER_ERROR_UNC | + IDE_REGISTER_ERROR_ICRC | + IDE_REGISTER_ERROR_IDNF | + IDE_REGISTER_ERROR_NM | + IDE_REGISTER_ERROR_MED)) + { + if (areq->type == ATA_COMMAND_TYPE_NON_DATA) + ata_non_data_request_done(areq, ctrl_minor, + RTEMS_UNSATISFIED, + RTEMS_IO_ERROR); + else + ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR); + break; + } + } + + switch(areq->type) + { + case ATA_COMMAND_TYPE_PIO_IN: + ata_pio_in_protocol(ctrl_minor, areq); + break; + + case ATA_COMMAND_TYPE_PIO_OUT: + ata_pio_out_protocol(ctrl_minor, areq); + break; + + case ATA_COMMAND_TYPE_NON_DATA: + ide_controller_read_register(ctrl_minor, + IDE_REGISTER_ERROR, + &val1); + ata_non_data_request_done(areq, ctrl_minor, + RTEMS_SUCCESSFUL, + val1); + break; + + default: +#if ATA_DEBUG + ata_printf("ata_queue_task: non-supported command type\n"); +#endif + ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR); + break; + } + break; + + default: +#if ATA_DEBUG + ata_printf("ata_queue_task: internal error\n"); + rtems_task_delete (RTEMS_SELF); +#endif + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + break; + } + } +} + +/* ata_ioctl -- + * ATA driver ioctl interface. + * + * PARAMETERS: + * device - device identifier + * cmd - command + * argp - arguments + * + * RETURNS: + * depend on 'cmd' + */ +static int +ata_ioctl(rtems_disk_device *dd, uint32_t cmd, void *argp) +{ + dev_t device = rtems_disk_get_device_identifier(dd); + rtems_status_code status; + rtems_device_minor_number rel_minor; + + rel_minor = (rtems_filesystem_dev_minor_t(device)) / + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; + + /* + * in most cases this means that device 'device' is not an registred ATA + * device + */ + if (ata_devs[rel_minor].device == ATA_UNDEFINED_VALUE) + { + errno = ENODEV; + return -1; + } + + switch (cmd) + { + case RTEMS_BLKIO_REQUEST: + status = ata_io_data_request(device, (rtems_blkdev_request *)argp); + break; + + case ATAIO_SET_MULTIPLE_MODE: + status = ata_non_data_request(device, cmd, argp); + break; + + case RTEMS_BLKIO_CAPABILITIES: + *((uint32_t*) argp) = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT; + status = RTEMS_SUCCESSFUL; + break; + + default: + return rtems_blkdev_ioctl (dd, cmd, argp); + break; + } + + if (status != RTEMS_SUCCESSFUL) + { + errno = EIO; + return -1; + } + return 0; +} + +static void ata_execute_device_diagnostic( + rtems_device_minor_number ctrl_minor, + uint16_t *sector_buffer +) +{ +#if ATA_EXEC_DEVICE_DIAGNOSTIC + ata_req_t areq; + blkdev_request1 breq; + + ata_breq_init(&breq, sector_buffer); + + /* + * Issue EXECUTE DEVICE DIAGNOSTIC ATA command for explore is + * there any ATA device on the controller. + * + * This command may fail and it assumes we have a master device and may + * be a slave device. I think the identify command will handle + * detection better than this method. + */ + memset(&areq, 0, sizeof(ata_req_t)); + areq.type = ATA_COMMAND_TYPE_NON_DATA; + areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq.regs.regs[IDE_REGISTER_COMMAND] = + ATA_COMMAND_EXECUTE_DEVICE_DIAGNOSTIC; + areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR); + + areq.breq = (rtems_blkdev_request *)&breq; + + /* + * Process the request. Special processing of requests on + * initialization phase is needed because at this moment there + * is no multitasking enviroment + */ + ata_process_request_on_init_phase(ctrl_minor, &areq); + + /* + * check status of I/O operation + */ + if (breq.req.status == RTEMS_SUCCESSFUL) + { + /* disassemble returned diagnostic codes */ + if (areq.info == ATA_DEV0_PASSED_DEV1_PASSED_OR_NOT_PRSNT) + { + printk("ATA: ctrl:%d: primary, secondary\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor,0).present = true; + ATA_DEV_INFO(ctrl_minor,1).present = true; + } + else if (areq.info == ATA_DEV0_PASSED_DEV1_FAILED) + { + printk("ATA: ctrl:%d: primary\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor,0).present = true; + ATA_DEV_INFO(ctrl_minor,1).present = false; + } + else if (areq.info < ATA_DEV1_PASSED_DEV0_FAILED) + { + printk("ATA: ctrl:%d: secondary\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor,0).present = false; + ATA_DEV_INFO(ctrl_minor,1).present = true; + } + else + { + printk("ATA: ctrl:%d: none\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor, 0).present = false; + ATA_DEV_INFO(ctrl_minor, 1).present = false; + } + + /* refine the returned codes */ + if (ATA_DEV_INFO(ctrl_minor, 1).present) + { + uint16_t ec = 0; + ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &ec); + if (ec & ATA_DEV1_PASSED_DEV0_FAILED) + { + printk("ATA: ctrl:%d: secondary inforced\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor, 1).present = true; + } + else + { + printk("ATA: ctrl:%d: secondary removed\n", ctrl_minor); + ATA_DEV_INFO(ctrl_minor, 1).present = false; + } + } + } + else +#endif + { + ATA_DEV_INFO(ctrl_minor, 0).present = true; + ATA_DEV_INFO(ctrl_minor,1).present = true; + } +} + +/* + * ata_initialize -- + * Initializes all ATA devices found on initialized IDE controllers. + * + * PARAMETERS: + * major - device major number + * minor - device minor number + * args - arguments + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +rtems_device_driver +rtems_ata_initialize(rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *args) +{ + uint32_t ctrl_minor; + rtems_status_code status; + uint16_t *buffer; + int i, dev = 0; + char name[ATA_MAX_NAME_LENGTH]; + dev_t device; + ata_int_st_t *int_st; + +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE + rtems_isr_entry old_isr; +#else + int ata_irq_chain_use; +#endif + + if (ata_initialized) + return RTEMS_SUCCESSFUL; + + /* initialization of disk devices library */ + status = rtems_disk_io_initialize(); + if (status != RTEMS_SUCCESSFUL) + return status; + + status = rtems_semaphore_create (rtems_build_name ('A', 'T', 'A', 'L'), + 1, RTEMS_ATA_LOCK_ATTRIBS, 0, + &ata_lock); + if (status != RTEMS_SUCCESSFUL) + return status; + + /* create queue for asynchronous requests handling */ + status = rtems_message_queue_create( + rtems_build_name('A', 'T', 'A', 'Q'), + ATA_DRIVER_MESSAGE_QUEUE_SIZE, + sizeof(ata_queue_msg_t), + RTEMS_FIFO | RTEMS_LOCAL, + &ata_queue_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_disk_io_done(); + return status; + } + + /* + * create ATA driver task, see comments for task implementation for + * details + */ + status = rtems_task_create( + rtems_build_name ('A', 'T', 'A', 'T'), + ((rtems_ata_driver_task_priority > 0) + ? rtems_ata_driver_task_priority + : ATA_DRIVER_TASK_DEFAULT_PRIORITY), + ATA_DRIVER_TASK_STACK_SIZE, + RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR | + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, + &ata_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } + + /* + * start ATA driver task. Actually the task will not start immediately - + * it will start only after multitasking support will be started + */ + status = rtems_task_start(ata_task_id, ata_queue_task, 0); + if (status != RTEMS_SUCCESSFUL) + { + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } + + buffer = (uint16_t*)malloc(ATA_SECTOR_SIZE); + if (buffer == NULL) + { + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return RTEMS_NO_MEMORY; + } + + ata_devs_number = 0; + + for (i = 0; i < (2 * IDE_CTRL_MAX_MINOR_NUMBER); i++) + ata_devs[i].device = ATA_UNDEFINED_VALUE; + +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE + /* prepare ATA driver for handling interrupt driven devices */ + for (i = 0; i < ATA_MAX_RTEMS_INT_VEC_NUMBER; i++) + rtems_chain_initialize_empty(&ata_int_vec[i]); +#else + for (i = 0; i < ATA_IRQ_CHAIN_MAX_CNT; i++) { + rtems_chain_initialize_empty(&(ata_irq_chain[i].irq_chain)); + } +#endif + + /* + * during ATA driver initialization EXECUTE DEVICE DIAGNOSTIC and + * IDENTIFY DEVICE ATA command should be issued; for these purposes ATA + * requests should be formed; ATA requests contain block device request, + * so form block device request first + */ + + /* + * for each presented IDE controller execute EXECUTE DEVICE DIAGNOSTIC + * ATA command; for each found device execute IDENTIFY DEVICE ATA + * command + */ + for (ctrl_minor = 0; ctrl_minor < IDE_Controller_Count; ctrl_minor++) + if (IDE_Controller_Table[ctrl_minor].status == IDE_CTRL_INITIALIZED) + { + rtems_chain_initialize_empty(&ata_ide_ctrls[ctrl_minor].reqs); + + if (IDE_Controller_Table[ctrl_minor].int_driven == true) + { + int_st = malloc(sizeof(ata_int_st_t)); + if (int_st == NULL) + { + free(buffer); + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return RTEMS_NO_MEMORY; + } + + int_st->ctrl_minor = ctrl_minor; +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE + status = rtems_interrupt_catch( + ata_interrupt_handler, + IDE_Controller_Table[ctrl_minor].int_vec, + &old_isr); +#else + /* + * FIXME: check existing entries. if they use the same + * IRQ name, then append int_st to respective chain + * otherwise, use new ata_irq_chain entry + */ + ata_irq_chain_use = -1; + for (i = 0; + ((i < ata_irq_chain_cnt) && + (ata_irq_chain_use < 0));i++) { + if (ata_irq_chain[i].name == + IDE_Controller_Table[ctrl_minor].int_vec) { + ata_irq_chain_use = i; + } + } + if (ata_irq_chain_use < 0) { + /* + * no match found, try to use new channel entry + */ + if (ata_irq_chain_cnt < ATA_IRQ_CHAIN_MAX_CNT) { + ata_irq_chain_use = ata_irq_chain_cnt++; + + ata_irq_chain[ata_irq_chain_use].name = + IDE_Controller_Table[ctrl_minor].int_vec; + ata_irq_data.name = + IDE_Controller_Table[ctrl_minor].int_vec; + ata_irq_data.hdl = ata_interrupt_handler; + ata_irq_data.handle = (rtems_irq_hdl_param) (uintptr_t) ctrl_minor; + + status = ((0 == BSP_install_rtems_irq_handler(&ata_irq_data)) + ? RTEMS_INVALID_NUMBER + : RTEMS_SUCCESSFUL); + } + else { + status = RTEMS_TOO_MANY; + } + } +#endif + if (status != RTEMS_SUCCESSFUL) + { + free(int_st); + free(buffer); + rtems_task_delete(ata_task_id); + rtems_message_queue_delete(ata_queue_id); + rtems_disk_io_done(); + return status; + } +#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE + rtems_chain_append( + &ata_int_vec[IDE_Controller_Table[ctrl_minor].int_vec], + &int_st->link); +#else + rtems_chain_append( + &(ata_irq_chain[ata_irq_chain_use].irq_chain), + &int_st->link); +#endif + + /* disable interrupts */ + ide_controller_write_register(ctrl_minor, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + IDE_REGISTER_DEVICE_CONTROL_nIEN); + } + + ata_execute_device_diagnostic(ctrl_minor, buffer); + + /* for each found ATA device obtain it configuration */ + for (dev = 0; dev < 2; dev++) + if (ATA_DEV_INFO(ctrl_minor, dev).present) + { + status = ata_identify_device( + ctrl_minor, + dev, + buffer, + &ATA_DEV_INFO(ctrl_minor, dev)); + if (status != RTEMS_SUCCESSFUL) + continue; + + /* + * choose most appropriate ATA device data I/O speed supported + * by the controller + */ + status = ide_controller_config_io_speed( + ctrl_minor, + ATA_DEV_INFO(ctrl_minor, dev).modes_available); + if (status != RTEMS_SUCCESSFUL) + continue; + + /* + * Ok, let register new ATA device in the system + */ + ata_devs[ata_devs_number].ctrl_minor = ctrl_minor; + ata_devs[ata_devs_number].device = dev; + + /* The space leaves a hole for the character. */ + strcpy(name, "/dev/hd "); + name[7] = 'a' + 2 * ctrl_minor + dev; + + device = rtems_filesystem_make_dev_t( + major, + (ata_devs_number * + ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE)); + status = rtems_disk_create_phys(device, ATA_SECTOR_SIZE, + ATA_DEV_INFO(ctrl_minor, dev).lba_avaible ? + ATA_DEV_INFO(ctrl_minor, dev).lba_sectors : + (ATA_DEV_INFO(ctrl_minor, dev).heads * + ATA_DEV_INFO(ctrl_minor, dev).cylinders * + ATA_DEV_INFO(ctrl_minor, dev).sectors), + ata_ioctl, NULL, name); + if (status != RTEMS_SUCCESSFUL) + { + ata_devs[ata_devs_number].device = ATA_UNDEFINED_VALUE; + continue; + } + ata_devs_number++; + } + if (IDE_Controller_Table[ctrl_minor].int_driven == true) + { + ide_controller_write_register(ctrl_minor, + IDE_REGISTER_DEVICE_CONTROL_OFFSET, + 0x00); + } + } + + free(buffer); + ata_initialized = true; + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/shared/dev/ide/ata_util.c b/bsps/shared/dev/ide/ata_util.c new file mode 100644 index 0000000000..68e0f0bbe5 --- /dev/null +++ b/bsps/shared/dev/ide/ata_util.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2010 embedded brains GmbH. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Authors: Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <assert.h> +#include <string.h> + +#include <libchip/ide_ctrl_io.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ata_internal.h> + +/* ata_process_request_on_init_phase -- + * Process the ATA request during system initialization. Request + * processing is syncronous and doesn't use multiprocessing enviroment. + * + * PARAMETERS: + * ctrl_minor - controller identifier + * areq - ATA request + * + * RETURNS: + * NONE + */ +void +ata_process_request_on_init_phase(rtems_device_minor_number ctrl_minor, + ata_req_t *areq) +{ + uint16_t byte;/* emphasize that only 8 low bits is meaningful */ + uint8_t i; +#if 0 + uint8_t dev; +#endif + uint16_t val, val1; + volatile unsigned retries; + + assert(areq); + +#if 0 + dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & + IDE_REGISTER_DEVICE_HEAD_DEV; +#endif + + ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD, + areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]); + + retries = 0; + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + /* If device (on INIT, i.e. it should be idle) is neither + * busy nor ready something's fishy, i.e., there is probably + * no device present. + * I'd like to do a proper timeout but don't know of a portable + * timeout routine (w/o using multitasking / rtems_task_wake_after()) + */ + if ( ! (byte & (IDE_REGISTER_STATUS_BSY | IDE_REGISTER_STATUS_DRDY))) { + retries++; + if ( 10000 == retries ) { + /* probably no drive connected */ + areq->breq->status = RTEMS_UNSATISFIED; + return; + } + } + } while ((byte & IDE_REGISTER_STATUS_BSY) || + (!(byte & IDE_REGISTER_STATUS_DRDY))); + + for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++) + { + uint32_t reg = (1 << i); + if (areq->regs.to_write & reg) + ide_controller_write_register(ctrl_minor, i, + areq->regs.regs[i]); + } + + do { + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); + } while (byte & IDE_REGISTER_STATUS_BSY); + + ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val); + ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &val1); + + if (val & IDE_REGISTER_STATUS_ERR) + { + areq->breq->status = RTEMS_IO_ERROR; + return; + } + + switch(areq->type) + { + case ATA_COMMAND_TYPE_PIO_IN: + if (areq->cnt) + { + int ccbuf = areq->cbuf; + ide_controller_read_data_block(ctrl_minor, + areq->breq->bufs[0].length * areq->cnt, + areq->breq->bufs, &areq->cbuf, + &areq->pos); + ccbuf = areq->cbuf - ccbuf; + areq->cnt -= ccbuf; + } + if (areq->cnt == 0) + { + areq->breq->status = RTEMS_SUCCESSFUL; + } + else + { + /* + * this shouldn't happend on the initialization + * phase! + */ + rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); + } + break; + + case ATA_COMMAND_TYPE_NON_DATA: + areq->breq->status = RTEMS_SUCCESSFUL; + areq->info = val1; + break; + + default: + areq->breq->status = RTEMS_IO_ERROR; + break; + } +} + +void ata_breq_init(blkdev_request1 *breq, uint16_t *sector_buffer) +{ + memset(breq, 0, sizeof(*breq)); + + breq->req.done_arg = breq; + breq->req.bufnum = 1; + breq->req.bufs [0].length = ATA_SECTOR_SIZE; + breq->req.bufs [0].buffer = sector_buffer; +} + +rtems_status_code ata_identify_device( + rtems_device_minor_number ctrl_minor, + int dev, + uint16_t *sector_buffer, + ata_dev_t *device_entry +) +{ + ata_req_t areq; + blkdev_request1 breq; + + ata_breq_init(&breq, sector_buffer); + + /* + * Issue DEVICE IDENTIFY ATA command and get device + * configuration + */ + memset(&areq, 0, sizeof(ata_req_t)); + areq.type = ATA_COMMAND_TYPE_PIO_IN; + areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); + areq.regs.regs [IDE_REGISTER_COMMAND] = ATA_COMMAND_IDENTIFY_DEVICE; + areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS); + areq.breq = (rtems_blkdev_request *)&breq; + areq.cnt = breq.req.bufnum; + areq.regs.regs [IDE_REGISTER_DEVICE_HEAD] |= + dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS; + + /* + * Process the request. Special processing of requests on + * initialization phase is needed because at this moment there + * is no multitasking enviroment + */ + ata_process_request_on_init_phase(ctrl_minor, &areq); + + /* check status of I/O operation */ + if (breq.req.status != RTEMS_SUCCESSFUL) { + return RTEMS_IO_ERROR; + } + + /* + * Parse returned device configuration and fill in ATA internal + * device info structure + */ + device_entry->cylinders = + CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_CLNDS]); + device_entry->heads = + CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_HEADS]); + device_entry->sectors = + CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_SECS]); + device_entry->lba_sectors = + CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS1]); + device_entry->lba_sectors <<= 16; + device_entry->lba_sectors += CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS0]); + device_entry->lba_avaible = + (CF_LE_W(sector_buffer[ATA_IDENT_WORD_CAPABILITIES]) >> 9) & 0x1; + + if ((CF_LE_W(sector_buffer[ATA_IDENT_WORD_FIELD_VALIDITY]) & + ATA_IDENT_BIT_VALID) == 0) { + /* no "supported modes" info -> use default */ + device_entry->mode_active = ATA_MODES_PIO3; + } else { + device_entry->modes_available = + ((CF_LE_W(sector_buffer[64]) & 0x1) ? ATA_MODES_PIO3 : 0) | + ((CF_LE_W(sector_buffer[64]) & 0x2) ? ATA_MODES_PIO4 : 0) | + ((CF_LE_W(sector_buffer[63]) & 0x1) ? ATA_MODES_DMA0 : 0) | + ((CF_LE_W(sector_buffer[63]) & 0x2) ? + ATA_MODES_DMA0 | ATA_MODES_DMA1 : 0) | + ((CF_LE_W(sector_buffer[63]) & 0x4) ? + ATA_MODES_DMA0 | ATA_MODES_DMA1 | ATA_MODES_DMA2 : 0); + if (device_entry->modes_available == 0) { + return RTEMS_IO_ERROR; + } + } + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/shared/dev/ide/ide_controller.c b/bsps/shared/dev/ide/ide_controller.c new file mode 100644 index 0000000000..912f9e3157 --- /dev/null +++ b/bsps/shared/dev/ide/ide_controller.c @@ -0,0 +1,200 @@ +/* + * ide_controller.c + * + * This is generic rtems driver for IDE controllers. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Authors: Alexandra Kossovsky <sasha@oktet.ru> + * Eugeny S. Mints <Eugeny.Mints@oktet.ru> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + */ + +#define IDE_CONTROLLER_TRACE 0 + +#include <rtems/chain.h> +#include <errno.h> +#include <rtems/blkdev.h> + +#include <libchip/ide_ctrl.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ide_ctrl_io.h> + +#if IDE_CONTROLLER_TRACE +int ide_controller_trace = 1; +#endif + +/* + * ide_controller_initialize -- + * Initializes all configured IDE controllers. Controllers configuration + * table is provided by BSP + * + * PARAMETERS: + * major - device major number + * minor_arg - device minor number + * args - arguments + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +rtems_device_driver +ide_controller_initialize(rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *args) +{ + unsigned long minor; + + /* FIXME: may be it should be done on compilation phase */ + if (IDE_Controller_Count > IDE_CTRL_MAX_MINOR_NUMBER) + rtems_fatal_error_occurred(RTEMS_TOO_MANY); + + for (minor=0; minor < IDE_Controller_Count; minor++) + { + IDE_Controller_Table[minor].status = IDE_CTRL_NON_INITIALIZED; + + if ((IDE_Controller_Table[minor].probe == NULL || + IDE_Controller_Table[minor].probe(minor)) && + (IDE_Controller_Table[minor].fns->ctrl_probe == NULL || + IDE_Controller_Table[minor].fns->ctrl_probe(minor))) + { + dev_t dev; + dev = rtems_filesystem_make_dev_t( major, minor ); + if (mknod(IDE_Controller_Table[minor].name, + 0777 | S_IFBLK, dev ) < 0) + rtems_fatal_error_occurred(errno); + IDE_Controller_Table[minor].fns->ctrl_initialize(minor); + IDE_Controller_Table[minor].status = IDE_CTRL_INITIALIZED; + } + } + return RTEMS_SUCCESSFUL; +} + +/* + * ide_controller_read_data_block -- + * Read data block via controller's data register + * + * PARAMETERS: + * minor - minor number of controller + * block_size - number of bytes to read + * bufs - set of buffers to store data + * cbuf - number of current buffer from the set + * pos - position inside current buffer 'cbuf' + * + * RETURNS: + * NONE + */ +void +ide_controller_read_data_block(rtems_device_minor_number minor, + uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, + uint32_t *cbuf, + uint32_t *pos) +{ +#if IDE_CONTROLLER_TRACE + if (ide_controller_trace) + printk ("IDE data block read: %d:%d\n", *cbuf, bufs[*cbuf].block); +#endif + IDE_Controller_Table[minor].fns->ctrl_read_block(minor, block_size, bufs, + cbuf, pos); +} + +/* + * ide_controller_write_data_block -- + * Write data block via controller's data register + * + * PARAMETERS: + * minor - minor number of controller + * block_size - number of bytes to write + * bufs - set of buffers which store data + * cbuf - number of current buffer from the set + * pos - position inside current buffer 'cbuf' + * + * RETURNS: + * NONE + */ +void +ide_controller_write_data_block(rtems_device_minor_number minor, + uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, + uint32_t *cbuf, + uint32_t *pos) + +{ +#if IDE_CONTROLLER_TRACE + if (ide_controller_trace) + printk ("IDE data block write: %d:%d\n", *cbuf, bufs[*cbuf].block); +#endif + IDE_Controller_Table[minor].fns->ctrl_write_block(minor, block_size, bufs, + cbuf, pos); +} + +/* + * ide_controller_read_register -- + * Read controller's register + * + * PARAMETERS: + * minor - minor number of controller + * reg - register to read + * value - placeholder for result + * + * RETURNS + * NONE + */ +void +ide_controller_read_register(rtems_device_minor_number minor, + int reg, + uint16_t *value) +{ + IDE_Controller_Table[minor].fns->ctrl_reg_read(minor, reg, value); +#if IDE_CONTROLLER_TRACE + if (ide_controller_trace) + printk ("IDE read reg: %d => %04x\n", reg, *value); +#endif +} + +/* + * ide_controller_write_register -- + * Write controller's register + * + * PARAMETERS: + * minor - minor number of controller + * reg - register to write + * value - value to write + * + * RETURNS: + * NONE + */ +void +ide_controller_write_register(rtems_device_minor_number minor, int reg, + uint16_t value) +{ +#if IDE_CONTROLLER_TRACE + if (ide_controller_trace) + printk ("IDE write reg: %d => %04x\n", reg, value); +#endif + IDE_Controller_Table[minor].fns->ctrl_reg_write(minor, reg, value); +} + +/* + * ide_controller_config_io_speed -- + * Set controller's speed of IO operations + * + * PARAMETERS: + * minor - minor number of controller + * modes_available - speeds available + * + * RETURNS: + * RTEMS_SUCCESSFUL on success, or error code if + * error occured + */ +rtems_status_code +ide_controller_config_io_speed(int minor, uint16_t modes_available) +{ + return IDE_Controller_Table[minor].fns->ctrl_config_io_speed( + minor, + modes_available); +} |