mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-05 15:15:44 +00:00
1332 lines
40 KiB
C
1332 lines
40 KiB
C
/* SPDX-License-Identifier: GPL-2.0+-with-RTEMS-exception */
|
|
|
|
/*
|
|
* 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/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(ata_ide_dev_t *ata_dev, rtems_blkdev_request *req)
|
|
{
|
|
ata_req_t *areq; /* ATA request */
|
|
rtems_device_minor_number ctrl_minor;
|
|
uint8_t dev;
|
|
|
|
/* get controller which serves the ATA device */
|
|
ctrl_minor = ata_dev->ctrl_minor;
|
|
|
|
/* get ATA device identifier (0 or 1) */
|
|
dev = ata_dev->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 */
|
|
rtems_chain_set_off_chain(&areq->link);
|
|
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(ata_ide_dev_t *ata_dev, uint32_t cmd, void *argp)
|
|
{
|
|
rtems_status_code rc;
|
|
ata_req_t *areq; /* ATA request */
|
|
rtems_device_minor_number ctrl_minor;
|
|
uint8_t dev;
|
|
ata_queue_msg_t msg;
|
|
|
|
/* get controller which serves the ATA device */
|
|
ctrl_minor = ata_dev->ctrl_minor;
|
|
|
|
/* get ATA device identifier (0 or 1) */
|
|
dev = ata_dev->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_exit();
|
|
#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)
|
|
{
|
|
ata_ide_dev_t *ata_dev = rtems_disk_get_driver_data(dd);
|
|
rtems_status_code status;
|
|
|
|
/*
|
|
* in most cases this means that device 'device' is not an registred ATA
|
|
* device
|
|
*/
|
|
if (ata_dev->device == ATA_UNDEFINED_VALUE)
|
|
{
|
|
errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case RTEMS_BLKIO_REQUEST:
|
|
status = ata_io_data_request(ata_dev, (rtems_blkdev_request *)argp);
|
|
break;
|
|
|
|
case ATAIO_SET_MULTIPLE_MODE:
|
|
status = ata_non_data_request(ata_dev, 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];
|
|
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;
|
|
|
|
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)
|
|
{
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
return status;
|
|
}
|
|
rtems_chain_set_off_chain(&int_st->link);
|
|
#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;
|
|
|
|
status = rtems_blkdev_create(name, 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, &ata_devs[ata_devs_number]);
|
|
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;
|
|
}
|