diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 12:53:28 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:46 +0200 |
commit | fc79b268eb1d0588588c529888ce585f5cacf7c0 (patch) | |
tree | 1f219271db6d15f164fc5158851ad30d33490e91 /bsps | |
parent | bsps: Move PCI drivers to bsps (diff) | |
download | rtems-fc79b268eb1d0588588c529888ce585f5cacf7c0.tar.bz2 |
bsps: Move ATA drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
-rw-r--r-- | bsps/i386/pc386/ata/ide.c | 725 | ||||
-rw-r--r-- | bsps/i386/pc386/ata/idecfg.c | 145 | ||||
-rw-r--r-- | bsps/powerpc/gen5200/ata/ata-dma-pio-single.c | 189 | ||||
-rw-r--r-- | bsps/powerpc/gen5200/ata/ata-instance.c | 41 | ||||
-rw-r--r-- | bsps/powerpc/gen5200/ata/idecfg.c | 100 | ||||
-rw-r--r-- | bsps/powerpc/gen5200/ata/pcmcia_ide.c | 682 | ||||
-rw-r--r-- | bsps/powerpc/gen5200/ata/pcmcia_ide.h | 98 |
7 files changed, 1980 insertions, 0 deletions
diff --git a/bsps/i386/pc386/ata/ide.c b/bsps/i386/pc386/ata/ide.c new file mode 100644 index 0000000000..52877cf60b --- /dev/null +++ b/bsps/i386/pc386/ata/ide.c @@ -0,0 +1,725 @@ +/*===============================================================*\ +| Project: RTEMS PC386 IDE harddisc driver | ++-----------------------------------------------------------------+ +| File: ide.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the BSP layer for IDE access below the | +| libchip IDE harddisc driver | +| based on a board specific driver from | +| Eugeny S. Mints, Oktet | +| | +| 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. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 01.14.03 creation doe | +\*===============================================================*/ + +#include <inttypes.h> + +#include <rtems.h> + +#include <bsp.h> +#include <libchip/ide_ctrl.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ide_ctrl_io.h> + +#define ATA_SECTOR_SIZE (512) + +/* + * Use during initialisation. + */ +extern void Wait_X_ms(unsigned int msecs); + +bool pc386_ide_show; +uint32_t pc386_ide_timeout; + +#define PC386_IDE_DEBUG_OUT 0 + +#if PC386_IDE_DEBUG_OUT +bool pc386_ide_trace; +#define pc386_ide_printk if (pc386_ide_trace) printk +#endif + +#define PC386_IDE_PROBE_TIMEOUT (500) +#define PC386_IDE_PRESTART_TIMEOUT (1000) +#define PC386_IDE_TASKING_TIMEOUT (2000) + +/* + * Prestart sleep using a calibrated timing loop. + */ +static void pc386_ide_prestart_sleep (void) +{ + Wait_X_ms (10); +} + +/* + * Once tasking has started we use a task sleep. + */ +static void pc386_ide_tasking_sleep (void) +{ + rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (10000) ? + RTEMS_MICROSECONDS_TO_TICKS (10000) : 1); +} + +typedef void (*pc386_ide_sleeper)(void); + +static void pc386_ide_sleep (pc386_ide_sleeper sleeper) +{ + sleeper (); +} + +static void wait(volatile uint32_t loops) +{ + while (loops) + loops--; +} + +static bool pc386_ide_status_busy (uint32_t port, + volatile uint32_t timeout, + uint8_t* status_val, + pc386_ide_sleeper sleeper) +{ + volatile uint8_t status; + int polls; + + do + { + polls = 500; + while (polls) + { + inport_byte (port + IDE_REGISTER_STATUS, status); + if ((status & IDE_REGISTER_STATUS_BSY) == 0) + { + *status_val = status; + return true; + } + polls--; + } + + if (timeout) + { + timeout--; + pc386_ide_sleep (sleeper); + } + } + while (timeout); + + *status_val = status; + return false; +} + +static bool pc386_ide_status_data_ready (uint32_t port, + volatile uint32_t timeout, + uint8_t* status_val, + pc386_ide_sleeper sleeper) +{ + volatile uint8_t status; + int polls; + + do + { + polls = 1000; + while (polls) + { + inport_byte (port + IDE_REGISTER_STATUS, status); + + if (((status & IDE_REGISTER_STATUS_BSY) == 0) && + (status & IDE_REGISTER_STATUS_DRQ)) + { + *status_val = status; + return true; + } + + polls--; + } + + if (timeout) + { + timeout--; + pc386_ide_sleep (sleeper); + } + } + while (timeout); + + *status_val = status; + return false; +} + +/* + * support functions for IDE harddisk IF + */ +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static bool pc386_ide_probe +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| This function should probe, whether a IDE disk is available | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| true, when flash disk available | +\*=========================================================================*/ +{ + bool ide_card_plugged = true; /* assume: we have a disk here */ + + return ide_card_plugged; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void pc386_ide_initialize +( +/*-------------------------------------------------------------------------*\ + | Purpose: | + | initialize IDE access | + +---------------------------------------------------------------------------+ + | Input Parameters: | + \*-------------------------------------------------------------------------*/ + int minor /* controller minor number */ + ) +/*-------------------------------------------------------------------------*\ + | Return Value: | + | <none> | + \*=========================================================================*/ +{ + uint32_t port = IDE_Controller_Table[minor].port1; + uint8_t dev = 0; + + if (pc386_ide_show) + printk("IDE%d: port base: %04" PRIu32 "\n", minor, port); + + outport_byte(port+IDE_REGISTER_DEVICE_HEAD, + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | 0xE0); + wait(10000); + outport_byte(port+IDE_REGISTER_DEVICE_CONTROL, + IDE_REGISTER_DEVICE_CONTROL_SRST | IDE_REGISTER_DEVICE_CONTROL_nIEN); + wait(10000); + outport_byte(port+IDE_REGISTER_DEVICE_CONTROL, + IDE_REGISTER_DEVICE_CONTROL_nIEN); + wait(10000); + + for (dev = 0; dev < 2; dev++) + { + uint16_t capabilities = 0; + uint32_t byte; + uint8_t status; + uint8_t error; + uint8_t cyllsb; + uint8_t cylmsb; + const char* label = dev ? " slave" : "master"; + int max_multiple_sectors = 0; + int cur_multiple_sectors = 0; + uint32_t cylinders = 0; + uint32_t heads = 0; + uint32_t sectors = 0; + uint32_t lba_sectors = 0; + char model_number[41]; + char* p = &model_number[0]; + bool data_ready; + + (void) cur_multiple_sectors; /* avoid set but not used warning */ + + memset(model_number, 0, sizeof(model_number)); + + outport_byte(port+IDE_REGISTER_DEVICE_HEAD, + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | 0xE0); + /* + outport_byte(port+IDE_REGISTER_SECTOR_NUMBER, + (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | IDE_REGISTER_LBA3_L); + */ + + outport_byte(port+IDE_REGISTER_COMMAND, 0x00); + + if (!pc386_ide_status_busy (port, PC386_IDE_PROBE_TIMEOUT, + &status, pc386_ide_prestart_sleep)) + continue; + + inport_byte(port+IDE_REGISTER_STATUS, status); + inport_byte(port+IDE_REGISTER_ERROR, error); + inport_byte(port+IDE_REGISTER_CYLINDER_LOW, cyllsb); + inport_byte(port+IDE_REGISTER_CYLINDER_HIGH, cylmsb); + + if (pc386_ide_show) + { + printk("IDE%d:%s: status=%02x\n", minor, label, status); + printk("IDE%d:%s: error=%02x\n", minor, label, error); + printk("IDE%d:%s: cylinder-low=%02x\n", minor, label, cyllsb); + printk("IDE%d:%s: cylinder-high=%02x\n", minor, label, cylmsb); + } + + outport_byte(port+IDE_REGISTER_COMMAND, 0xec); + + if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT, + &status, pc386_ide_prestart_sleep)) + { + if (pc386_ide_show) + printk("IDE%d:%s: device busy: %02x\n", minor, label, status); + continue; + } + + data_ready = pc386_ide_status_data_ready (port, + 250, + &status, + pc386_ide_prestart_sleep); + + if (status & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, error); + if (error != 4) + { + if (pc386_ide_show) + printk("IDE%d:%s: error=%04x\n", minor, label, error); + continue; + } + /* + * The device is an ATAPI device. + */ + outport_byte(port+IDE_REGISTER_COMMAND, 0xa1); + data_ready = pc386_ide_status_data_ready (port, + 250, + &status, + pc386_ide_prestart_sleep); + } + + if (!data_ready) + continue; + + byte = 0; + while (byte < 512) + { + uint16_t word; + + if (pc386_ide_show && ((byte % 16) == 0)) + printk("\n %04" PRIx32 " : ", byte); + + inport_word(port+IDE_REGISTER_DATA, word); + + if (pc386_ide_show) + printk ("%04x ", word); + + if (byte == 2) + cylinders = word; + if (byte == 6) + heads = word; + if (byte == 12) + sectors = word; + + if (byte >= 54 && byte < (54 + 40)) + { + *p = word >> 8; + p++; + *p = word; + p++; + } + + if (byte == (47 * 2)) + max_multiple_sectors = word & 0xff; + + if (byte == (49 * 2)) + capabilities = word; + + if (byte == (59 * 2)) + { + if (word & (1 << 8)) + cur_multiple_sectors = word & 0xff; + } + + if (byte == (60 * 2)) + lba_sectors = word; + if (byte == (61 * 2)) + lba_sectors |= word << 16; + + byte += 2; + } + + if (pc386_ide_show) + printk("\nbytes read = %" PRIu32 "\n", byte); + + if (p != &model_number[0]) + { + uint32_t size; + uint32_t left; + uint32_t right; + char units; + + if (capabilities & (1 << 9)) + size = lba_sectors; + else + size = cylinders * heads * sectors; + + size /= 2; + + if (size > (1024 * 1024)) + { + size = (size * 10) / (1000 * 1000); + units = 'G'; + } + else if (size > 1024) + { + size = (size * 10) / 1000; + units = 'M'; + } + else + { + size = size * 10; + units = 'K'; + } + + left = size / 10; + right = size % 10; + + p--; + while (*p == ' ') + { + *p = '\0'; + p--; + } + + printk("IDE%d:%s:%s, %" PRIu32 ".%" PRIu32 "%c (%" PRIu32 "/%" PRIu32 "/%" PRIu32 "), max blk size:%d\n", + minor, label, model_number, left, right, units, + heads, cylinders, sectors, max_multiple_sectors * 512); + } + +#if IDE_CLEAR_MULTI_SECTOR_COUNT + if (max_multiple_sectors) + { + outport_byte(port+IDE_REGISTER_SECTOR_COUNT, 0); + outport_byte(port+IDE_REGISTER_COMMAND, 0xc6); + + if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT, + &status, pc386_ide_prestart_sleep)) + { + if (pc386_ide_show) + printk("IDE%d:%s: device busy: %02x\n", minor, label, status); + continue; + } + + inport_byte(port+IDE_REGISTER_STATUS, status); + if (status & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, error); + if (error & IDE_REGISTER_ERROR_ABRT) + printk("IDE%d:%s: disable multiple failed\n", minor, label); + else + printk("IDE%d:%s: unknown error on disable multiple: %02x\n", + minor, label, error); + } + } +#endif + + outport_byte(port+IDE_REGISTER_DEVICE_CONTROL, + IDE_REGISTER_DEVICE_CONTROL_nIEN); + wait(10000); + } + + pc386_ide_timeout = PC386_IDE_TASKING_TIMEOUT; + + /* + * FIXME: enable interrupts, if needed + */ +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void pc386_ide_read_reg +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| read a IDE controller register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, /* controller minor number */ + int reg, /* register index to access */ + uint16_t *value /* ptr to return value location */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + uint32_t port = IDE_Controller_Table[minor].port1; + uint8_t bval1,bval2; + + if (reg == IDE_REGISTER_DATA_WORD) { + inport_byte(port+reg, bval1); + inport_byte(port+reg+1, bval2); + *value = bval1 + (bval2 << 8); + } + else { + inport_byte(port+reg, bval1); + *value = bval1; + } +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("pc386_ide_read_reg (0x%x)=0x%x\r\n",reg,*value & 0xff); +#endif +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void pc386_ide_write_reg +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| write a IDE controller register | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, /* controller minor number */ + int reg, /* register index to access */ + uint16_t value /* value to write */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + uint32_t port = IDE_Controller_Table[minor].port1; + +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("pc386_ide_write_reg(0x%x,0x%x)\r\n",reg,value & 0xff); +#endif + if (reg == IDE_REGISTER_DATA_WORD) { + outport_word(port+reg,value); + } + else { + outport_byte(port+reg,value); + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void pc386_ide_read_block +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| read a IDE controller data block | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, + uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, + uint32_t *cbuf, + uint32_t *pos + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + uint32_t port = IDE_Controller_Table[minor].port1; + uint32_t cnt = 0; +#if PC386_IDE_DEBUG_OUT + int i32 = 0; + pc386_ide_printk("pc386_ide_read_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n", + block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos); +#endif + + while (cnt < block_size) + { + uint16_t *lbuf; + uint8_t status_val; + int b; + + if (!pc386_ide_status_data_ready (port, pc386_ide_timeout, + &status_val, pc386_ide_tasking_sleep)) + { + printk ("pc386_ide_read_block: block=%" PRIu32 \ + " cbuf=%" PRIu32 " status=%02x, cnt=%" PRIu32 " bs=%" PRIu32 "\n", + bufs[*cbuf].block, *cbuf, status_val, cnt, block_size); + /* FIXME: add an error here. */ + return; + } + + if (status_val & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, status_val); + printk("pc386_ide_read_block: error: %02x\n", status_val); + return; + } + + lbuf = (uint16_t*)((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos)); + + for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++) + { + inport_word(port+IDE_REGISTER_DATA,*lbuf); + +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("%04x ",*lbuf); + i32++; + if (i32 >= 16) + { + pc386_ide_printk("\n"); + i32 = 0; + } +#endif + lbuf++; + } + cnt += ATA_SECTOR_SIZE; + (*pos) += ATA_SECTOR_SIZE; + if ((*pos) == bufs[(*cbuf)].length) { + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + } + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static void pc386_ide_write_block +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| write a IDE controller data block | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, + uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, + uint32_t *cbuf, + uint32_t *pos + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + uint32_t port = IDE_Controller_Table[minor].port1; + uint32_t cnt = 0; +#if PC386_IDE_DEBUG_OUT + int i32 = 0; + pc386_ide_printk("pc386_ide_write_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n", + block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos); +#endif + + while (cnt < block_size) + { + uint16_t *lbuf; + uint8_t status_val; + int b; + + if (!pc386_ide_status_data_ready (port, pc386_ide_timeout, + &status_val, pc386_ide_tasking_sleep)) + { + printk ("pc386_ide_write_block: block=%" PRIu32 " status=%02x, cnt=%" PRIu32 " bs=%" PRIu32 "\n", + bufs[*cbuf].block, status_val, cnt, block_size); + /* FIXME: add an error here. */ + return; + } + + if (status_val & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, status_val); + printk ("pc386_ide_write_block: error: %02x\n", status_val); + return; + } + + lbuf = (uint16_t*)(((uint8_t*)bufs[*cbuf].buffer) + (*pos)); + + for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++) + { +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("%04x ",*lbuf); + i32++; + if (i32 >= 16) + { + pc386_ide_printk("\n"); + i32 = 0; + } +#endif + outport_word(port+IDE_REGISTER_DATA,*lbuf); + lbuf++; + } + cnt += ATA_SECTOR_SIZE; + (*pos) += ATA_SECTOR_SIZE; + if ((*pos) == bufs[(*cbuf)].length) { + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + } + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static int pc386_ide_control +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| control interface for controller | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, /* controller minor number */ + uint32_t cmd, /* command to send */ + void * arg /* optional argument */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + return 0; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +static rtems_status_code pc386_ide_config_io_speed +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| set up transfer speed, if possible | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int minor, /* controller minor number */ + uint16_t modes_avail /* optional argument */ + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ +{ + return RTEMS_SUCCESSFUL; +} + +/* + * The following table configures the functions used for IDE drivers + * in this BSP. + */ + +ide_ctrl_fns_t pc386_ide_ctrl_fns = { + pc386_ide_probe, + pc386_ide_initialize, + pc386_ide_control, + pc386_ide_read_reg, + pc386_ide_write_reg, + pc386_ide_read_block, + pc386_ide_write_block, + pc386_ide_config_io_speed +}; diff --git a/bsps/i386/pc386/ata/idecfg.c b/bsps/i386/pc386/ata/idecfg.c new file mode 100644 index 0000000000..371a6eea53 --- /dev/null +++ b/bsps/i386/pc386/ata/idecfg.c @@ -0,0 +1,145 @@ +/*===============================================================*\ +| Project: RTEMS PC386 IDE harddisc driver tables | ++-----------------------------------------------------------------+ +| File: idecfg.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the table of functions for the BSP layer | +| for IDE access below the libchip IDE harddisc driver | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 01.14.03 creation doe | +\*===============================================================*/ + +#include <rtems.h> +#include <bsp.h> +#include <libchip/ide_ctrl.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ide_ctrl_io.h> + +extern bool pc386_ide_show; + +/* + * The following table configures the functions used for IDE drivers + * in this BSP. + */ + +/* + * The following table configures the IDE drivers used in this BSP. + */ +extern ide_ctrl_fns_t pc386_ide_ctrl_fns; + +/* IDE controllers Table */ +ide_controller_bsp_table_t IDE_Controller_Table[] = { + {"/dev/ide0", + IDE_STD, /* standard IDE controller */ + &pc386_ide_ctrl_fns, + NULL, /* probe for IDE standard registers */ + FALSE, /* not (yet) initialized */ + 0x1f0, /* base I/O address for first IDE controller */ + FALSE,0, /* not (yet) interrupt driven */ + NULL + } + , /* colon only needed when both interfaces present */ + {"/dev/ide1", + IDE_STD, /* standard IDE controller */ + &pc386_ide_ctrl_fns, + NULL, /* probe for IDE standard registers */ + FALSE, /* not (yet) initialized */ + 0x170, /* base I/O address for second IDE controller */ + FALSE,0, /* not (yet) interrupt driven */ + NULL + } +}; + +/* Number of rows in IDE_Controller_Table. Default is 0. */ +unsigned long IDE_Controller_Count; + +#if IDE_USE_PRIMARY_INTERFACE +#define IDE1_DEFAULT true +#else +#define IDE1_DEFAULT false +#endif +#if IDE_USE_SECONDARY_INTERFACE +#define IDE2_DEFAULT true +#else +#define IDE2_DEFAULT false +#endif + +void bsp_ide_cmdline_init(void) +{ + const char* ide_disable; + + ide_disable = bsp_cmdline_arg ("--ide-disable"); + + if (ide_disable == NULL) { + bool ide1 = IDE1_DEFAULT; + bool ide2 = IDE2_DEFAULT; + const char* ide; + + /* + * Can have: + * --ide=0,1 + */ + ide = bsp_cmdline_arg ("--ide="); + + if (ide) + { + int i; + /* + * If a command line option exists remove the defaults. + */ + ide1 = ide2 = false; + + ide += sizeof ("--ide=") - 1; + + for (i = 0; i < 3; i++) + { + switch (ide[i]) + { + case '0': + ide1 = true; + break; + case '1': + ide2 = true; + break; + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case ',': + break; + default: + break; + } + } + } + + if (ide2 && !ide1) + IDE_Controller_Table[0] = IDE_Controller_Table[1]; + + if (ide1) + IDE_Controller_Count++; + if (ide2) + IDE_Controller_Count++; + + /* + * Allow the user to get the initialise to print probing + * type information. + */ + ide = bsp_cmdline_arg ("--ide-show"); + + if (ide) + pc386_ide_show = true; + } +} diff --git a/bsps/powerpc/gen5200/ata/ata-dma-pio-single.c b/bsps/powerpc/gen5200/ata/ata-dma-pio-single.c new file mode 100644 index 0000000000..e621febbb0 --- /dev/null +++ b/bsps/powerpc/gen5200/ata/ata-dma-pio-single.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2011-2013 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 NDEBUG + +#include <bsp/ata.h> + +#include <libcpu/powerpc-utility.h> + +#include <bsp.h> +#include <bsp/fatal.h> +#include <bsp/irq.h> + +typedef enum { + DATA_CURRENT = 0, + DATA_END, + DATA_REG +} variables; + +typedef enum { + INC_0_NE = 0, + INC_2_NE +} increments; + +/* + * for + * idx0 = DATA_CURRENT, idx1 = DATA_REG + * idx0 != DATA_END + * idx0 += 2, idx1 += 0 + * do + * *idx0 = *idx1 [INT, 16 bit] OR *idx1 = *idx0 [INT, 16 bit] + */ +static const uint32_t ops[] = { + LCD(0, VAR(DATA_CURRENT), 0, VAR(DATA_REG), TERM_FIRST, VAR(DATA_END), INC_2_NE, INC_0_NE), + 0, /* Transfer opcode, see transfer() */ +}; + +static bool is_last_transfer(const ata_driver_dma_pio_single *self) +{ + return self->transfer_current + 1 == self->transfer_end; +} + +static void start_sector_transfer(ata_driver_dma_pio_single *self) +{ + uint16_t *current = ata_sg_get_sector_data_begin(&self->sg_context, self->transfer_current); + bestcomm_task_set_variable(&self->task, DATA_CURRENT, (uint32_t) current); + bestcomm_task_set_variable(&self->task, DATA_END, (uint32_t) ata_sg_get_sector_data_end(&self->sg_context, current)); + bestcomm_task_start(&self->task); + + bool last = is_last_transfer(self); + ++self->transfer_current; + + if (!last) { + ata_flush_sector(ata_sg_get_sector_data_begin(&self->sg_context, self->transfer_current)); + } +} + +static void dma_pio_single_interrupt_handler(void *arg) +{ + ata_driver_dma_pio_single *self = arg; + bool ok = ata_check_status(); + bool send_event = false; + if (ok && self->transfer_current != self->transfer_end) { + bool enable_dma_interrupt = self->read && is_last_transfer(self); + if (enable_dma_interrupt) { + bestcomm_task_irq_clear(&self->task); + bestcomm_task_irq_enable(&self->task); + } + + start_sector_transfer(self); + } else { + send_event = true; + } + + if (send_event) { + bestcomm_task_wakeup_event_task(&self->task); + } +} + +static bool transfer_dma_pio_single(ata_driver *super, bool read, rtems_blkdev_sg_buffer *sg, size_t sg_count) +{ + bool ok = true; + ata_driver_dma_pio_single *self = (ata_driver_dma_pio_single *) super; + + self->read = read; + ata_sg_reset(&self->sg_context, sg, sg_count); + rtems_blkdev_bnum start_sector = ata_sg_get_start_sector(&self->sg_context); + rtems_blkdev_bnum sector_count = ata_sg_get_sector_count(&self->sg_context); + rtems_blkdev_bnum relative_sector = 0; + + ata_flush_sector(ata_sg_get_sector_data_begin(&self->sg_context, relative_sector)); + + uint8_t command = ata_read_or_write_sectors_command(read); + + uint32_t opcode; + if (read) { + opcode = DRD1A(INT, INIT_ALWAYS, DEST_DEREF_IDX(0), SZ_16, SRC_DEREF_IDX(1), SZ_16); + } else { + opcode = DRD1A(INT, INIT_ALWAYS, DEST_DEREF_IDX(1), SZ_16, SRC_DEREF_IDX(0), SZ_16); + } + + bestcomm_task_irq_disable(&self->task); + bestcomm_task_associate_with_current_task(&self->task); + + size_t transfer_opcode_index = 1; /* See ops */ + bestcomm_task_set_opcode(&self->task, transfer_opcode_index, opcode); + + 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); + + self->transfer_current = relative_sector; + self->transfer_end = relative_sector + transfer_count; + + ok = ata_execute_io_command(command, start_sector + relative_sector, transfer_count); + if (ok) { + if (!read) { + ok = ata_wait_for_data_request(); + assert(ok); + + rtems_interrupt_level level; + rtems_interrupt_disable(level); + start_sector_transfer(self); + rtems_interrupt_enable(level); + } + + bestcomm_task_wait(&self->task); + + ok = ata_check_status(); + + relative_sector += ATA_PER_TRANSFER_SECTOR_COUNT_MAX; + } + } + + return ok; +} + +static int io_control_dma_pio_single( + rtems_disk_device *dd, + uint32_t cmd, + void *arg +) +{ + return ata_driver_io_control(dd, cmd, arg, transfer_dma_pio_single); +} + +void ata_driver_dma_pio_single_create(ata_driver_dma_pio_single *self, const char *device_file_path, TaskId task_index) +{ + ata_driver_create(&self->super, device_file_path, io_control_dma_pio_single); + + self->read = false; + + if (ata_driver_is_card_present(&self->super)) { + bestcomm_task_create_and_load(&self->task, task_index, ops, sizeof(ops)); + + bestcomm_task_set_variable(&self->task, DATA_REG, (uint32_t) &ATA->write.data); + + bestcomm_task_set_increment_and_condition(&self->task, INC_0_NE, 0, COND_NE); + bestcomm_task_set_increment_and_condition(&self->task, INC_2_NE, 2, COND_NE); + + bestcomm_task_enable_combined_write(&self->task, true); + bestcomm_task_enable_read_buffer(&self->task, true); + bestcomm_task_enable_speculative_read(&self->task, true); + + ata_clear_interrupts(); + + rtems_status_code sc = rtems_interrupt_handler_install( + BSP_SIU_IRQ_ATA, + "ATA", + RTEMS_INTERRUPT_UNIQUE, + dma_pio_single_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC5200_FATAL_ATA_DMA_SINGLE_IRQ_INSTALL); + } + } +} diff --git a/bsps/powerpc/gen5200/ata/ata-instance.c b/bsps/powerpc/gen5200/ata/ata-instance.c new file mode 100644 index 0000000000..b2b26341f6 --- /dev/null +++ b/bsps/powerpc/gen5200/ata/ata-instance.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011-2013 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 <bsp/ata.h> +#include <bsp/fatal.h> + +#include <libchip/ata.h> + +#include <bsp.h> + +static ata_driver_dma_pio_single ata_driver_instance; + +rtems_status_code rtems_ata_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *arg +) +{ + rtems_status_code sc = rtems_disk_io_initialize(); + + if (sc == RTEMS_SUCCESSFUL) { + bestcomm_glue_init(); + + ata_driver_dma_pio_single_create(&ata_driver_instance, "/dev/hda", TASK_PCI_TX); + } else { + bsp_fatal(MPC5200_FATAL_ATA_DISK_IO_INIT); + } + + return sc; +} diff --git a/bsps/powerpc/gen5200/ata/idecfg.c b/bsps/powerpc/gen5200/ata/idecfg.c new file mode 100644 index 0000000000..8cd26beef3 --- /dev/null +++ b/bsps/powerpc/gen5200/ata/idecfg.c @@ -0,0 +1,100 @@ +/*===============================================================*\ +| Project: RTEMS generic MPC5200 BSP | ++-----------------------------------------------------------------+ +| Copyright (c) 2005 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| 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. | +| | ++-----------------------------------------------------------------+ +| this file contains the IDE configuration | +\*===============================================================*/ +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/mpc5200.h> +#include "./pcmcia_ide.h" + +#include <libchip/ide_ctrl.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ide_ctrl_io.h> + +/* + * The following table configures the IDE driver used in this BSP. + */ +extern ide_ctrl_fns_t mpc5200_pcmciaide_ctrl_fns; + +volatile uint32_t * mpc5200_ata_drive_regs[] = + { + (uint32_t *)&(mpc5200.ata_ddr), /* data (offset 0x00) */ + (uint32_t *)&(mpc5200.ata_dfr_der), /* features / error (offset 0x01) */ + (uint32_t *)&(mpc5200.ata_dscr), /* sector count (offset 0x02) */ + (uint32_t *)&(mpc5200.ata_dsnr), /* sector no. / lba0 (offset 0x03) */ + (uint32_t *)&(mpc5200.ata_dclr), /* cylinder low / lba1 (offset 0x04) */ + (uint32_t *)&(mpc5200.ata_dchr), /* cylinder high/ lba2 (offset 0x05) */ + (uint32_t *)&(mpc5200.ata_ddhr), /* device head / lba3 (offset 0x06) */ + (uint32_t *)&(mpc5200.ata_dcr_dsr), /* command /status (offset 0x07) */ + + (uint32_t *)&(mpc5200.ata_dctr_dasr), /* device control / alternate status (offset 0x08) */ + (uint32_t *)&(mpc5200.ata_ddr), /* (offset 0x09) */ + (uint32_t *)&(mpc5200.ata_ddr), /* (offset 0x0A) */ + NULL, /* (offset 0x0B) */ + NULL, /* (offset 0x0C) */ + NULL, /* (offset 0x0D) */ + NULL, /* (offset 0x0E) */ + NULL /* (offset 0x0F) */ + }; + +/* IDE controllers Table */ +ide_controller_bsp_table_t IDE_Controller_Table[] = + { + { + "/dev/idepcmcia", + IDE_CUSTOM, /* PCMCIA Flash cards emulate custom IDE controller */ + &mpc5200_pcmciaide_ctrl_fns, /* pointer to function set used for IDE drivers in this BSP */ + NULL, /* no BSP dependent probe needed */ + FALSE, /* not (yet) initialized */ + (uint32_t)0, /* no port address but custom reg.set in params is used */ +#ifdef ATA_USE_INT + TRUE, /* interrupt driven */ +#else + FALSE, /* non interrupt driven */ +#endif + BSP_SIU_IRQ_ATA, /* interrupt vector */ + NULL /* no additional parameters */ + } +}; + +/* Number of rows in IDE_Controller_Table */ +unsigned long IDE_Controller_Count = sizeof(IDE_Controller_Table)/sizeof(IDE_Controller_Table[0]); + +uint32_t ata_pio_timings[2][6] = + { + /* PIO3 timings in nanosconds */ + { + 180, /* t0 cycle time */ + 80, /* t2 DIOR-/DIOW pulse width 8 bit */ + 80, /* t1 DIOR-/DIOW pulse width 16 bit */ + 10, /* t4 DIOW- data hold */ + 30, /* t1 Addr.valid to DIOR-/DIOW setup */ + 35, /* ta IORDY setup time */ + }, + /* PIO4 timings in nanosconds */ + { + 120, /* t0 cycle time */ + 70, /* t1 DIOR-/DIOW pulse width 8 bit */ + 70, /* t1 DIOR-/DIOW pulse width 16 bit */ + 10, /* t4 DIOW- data hold */ + 25, /* t1 Addr.valid to DIOR-/DIOW setup */ + 35, /* ta IORDY setup time */ + } + }; + + diff --git a/bsps/powerpc/gen5200/ata/pcmcia_ide.c b/bsps/powerpc/gen5200/ata/pcmcia_ide.c new file mode 100644 index 0000000000..9683ae383c --- /dev/null +++ b/bsps/powerpc/gen5200/ata/pcmcia_ide.c @@ -0,0 +1,682 @@ +/*===============================================================*\ +| Project: RTEMS generic MPC5200 BSP | ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2005 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| 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. | +| | ++-----------------------------------------------------------------+ +| this file contains the PCMCIA IDE access functions | +\*===============================================================*/ +/***********************************************************************/ +/* */ +/* Module: pcmcia_ide.c */ +/* Date: 07/17/2003 */ +/* Purpose: RTEMS MPC5x00 PCMCIA IDE harddisk driver */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Description: */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Code */ +/* References: RTEMS MBX8xx PCMCIA IDE harddisc driver */ +/* Module: pcmcia_ide.c */ +/* Project: RTEMS 4.6.0pre1 / Mbx8xx BSP */ +/* Version */ +/* Date: 01/14/2003 */ +/* */ +/* Author(s) / Copyright(s): */ +/* */ +/* Copyright (c) 2003 IMD */ +/* Ingenieurbuero fuer Microcomputertechnik Th. Doerfler */ +/* <Thomas.Doerfler@imd-systems.de> */ +/* all rights reserved */ +/* */ +/* this file contains the BSP layer for PCMCIA IDE access below the */ +/* libchip IDE harddisc driver based on a board specific driver from */ +/* Eugeny S. Mints, Oktet */ +/* */ +/* 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. */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Partially based on the code references which are named above. */ +/* Adaptions, modifications, enhancements and any recent parts of */ +/* the code are under the right of */ +/* */ +/* IPR Engineering, Dachauer Straße 38, D-80335 München */ +/* Copyright(C) 2003 */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* IPR Engineering makes no representation or warranties with */ +/* respect to the performance of this computer program, and */ +/* specifically disclaims any responsibility for any damages, */ +/* special or consequential, connected with the use of this program. */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Version history: 1.0 */ +/* */ +/***********************************************************************/ + +#include <sys/param.h> +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/score/sysstate.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/mpc5200.h> +#include "./pcmcia_ide.h" + +#include <libchip/ide_ctrl.h> +#include <libchip/ide_ctrl_cfg.h> +#include <libchip/ide_ctrl_io.h> +#include <string.h> + +#define IDE_DMA_TEST FALSE + +/* DMA supported PIO mode is broken */ +#define IDE_USE_INT TRUE +#define IDE_READ_USE_DMA FALSE +#define IDE_USE_READ_PIO_OPT FALSE +#define IDE_WRITE_USE_DMA FALSE +#define IDE_USE_WRITE_PIO_OPT FALSE +#define IDE_USE_DMA (IDE_READ_USE_DMA || IDE_WRITE_USE_DMA) + +#define IDE_USE_STATISTICS TRUE + +#if IDE_USE_DMA +#define PCMCIA_IDE_DMA_WR_BD_CNT 2 +#define PCMCIA_IDE_DMA_RD_BD_CNT 2 +#define PCMCIA_IDE_INTERRUPT_EVENT RTEMS_EVENT_2 +/* Task number assignment */ +#include "../bestcomm/bestcomm_glue.h" +#include "../bestcomm/bestcomm_api.h" +#include "../bestcomm/task_api/bestcomm_cntrl.h" +#include "../bestcomm/task_api/tasksetup_bdtable.h" + +#define IDE_RX_TASK_NO TASK_GEN_DP_BD_0 +#define IDE_TX_TASK_NO TASK_GEN_DP_BD_1 +static TaskId pcmcia_ide_rxTaskId; /* SDMA RX task ID */ +static TaskId pcmcia_ide_txTaskId; /* SDMA TX task ID */ +#define PCMCIA_IDE_RD_SECTOR_SIZE 512 /* FIXME: make this better... */ +#define PCMCIA_IDE_WR_SECTOR_SIZE 512 /* FIXME: make this better... */ + +bool mpc5200_dma_task_started[2] = {false,false}; +#endif /* IDE_USE_DMA */ + +#if IDE_USE_STATISTICS +uint32_t mpc5200_pcmciaide_write_block_call_cnt = 0; +uint32_t mpc5200_pcmciaide_write_block_block_cnt = 0; +uint32_t mpc5200_pcmciaide_read_block_call_cnt = 0; +uint32_t mpc5200_pcmciaide_read_block_block_cnt = 0; +#endif + +extern volatile uint32_t * mpc5200_ata_drive_regs[]; +extern uint32_t ata_pio_timings[2][6]; + +void mpc5200_pcmciaide_dma_blockop( + bool, int, uint16_t, rtems_blkdev_sg_buffer *, uint32_t *, uint32_t *); +/* + * support functions for PCMCIA IDE IF + */ +static bool mpc5200_pcmciaide_probe(int minor) + { + bool ide_card_plugged = false; /* assume: we don't have a card plugged in */ + struct mpc5200_gpt *gpt = (struct mpc5200_gpt *)(&mpc5200.gpt[GPT2]); + + #ifdef MPC5200_BOARD_DP2 + /* Deactivate RESET signal */ + rtems_interrupt_level level; + rtems_interrupt_disable(level); + mpc5200.gpiowe |= GPIO_W_PIN_PSC1_4; + mpc5200.gpiowod &= ~GPIO_W_PIN_PSC1_4; + mpc5200.gpiowdd |= GPIO_W_PIN_PSC1_4; + mpc5200.gpiowdo |= GPIO_W_PIN_PSC1_4; + rtems_interrupt_enable(level); + /* FIXME */ + volatile int i = 0; + while (++i < 20000000); + #endif + + /* enable card detection on GPT2 */ + gpt->emsel = (GPT_EMSEL_GPIO_IN | GPT_EMSEL_TIMER_MS_GPIO); + +#if defined (MPC5200_BOARD_BRS5L) + /* Check for card detection (-CD0) */ + if((gpt->status) & GPT_STATUS_PIN) + ide_card_plugged = false; + else +#endif + ide_card_plugged = true; + + return ide_card_plugged; + + } + +#define DMA1_T0(val) BSP_BFLD32(COUNT_VAL(val), 0, 7) +#define DMA1_TD(val) BSP_BFLD32(COUNT_VAL(val), 8, 15) +#define DMA1_TK(val) BSP_BFLD32(COUNT_VAL(val), 16, 23) +#define DMA1_TM(val) BSP_BFLD32(COUNT_VAL(val), 24, 31) + +#define DMA2_TH(val) BSP_BFLD32(COUNT_VAL(val), 0, 7) +#define DMA2_TJ(val) BSP_BFLD32(COUNT_VAL(val), 8, 15) +#define DMA2_TN(val) BSP_BFLD32(COUNT_VAL(val), 16, 23) + +static rtems_status_code mpc5200_pcmciaide_config_io_speed(int minor, uint16_t modes_avail) + { + uint8_t pio_t0, pio_t2_8, pio_t2_16, pio_t4, pio_t1, pio_ta; + + if((modes_avail & ATA_MODES_PIO4) != 0) + { + + pio_t0 = ata_pio_timings[PIO_4][T0]; + pio_t2_8 = ata_pio_timings[PIO_4][T2_8]; + pio_t2_16 = ata_pio_timings[PIO_4][T2_16]; + pio_t4 = ata_pio_timings[PIO_4][T4]; + pio_t1 = ata_pio_timings[PIO_4][T1]; + pio_ta = ata_pio_timings[PIO_4][TA]; + + } + else + { + + pio_t0 = ata_pio_timings[PIO_3][T0]; + pio_t2_8 = ata_pio_timings[PIO_3][T2_8]; + pio_t2_16 = ata_pio_timings[PIO_3][T2_16]; + pio_t4 = ata_pio_timings[PIO_3][T4]; + pio_t1 = ata_pio_timings[PIO_3][T1]; + pio_ta = ata_pio_timings[PIO_3][TA]; + + } + + /* set timings according according to selected ATA mode */ + mpc5200.ata_pio1 = ATA_PIO_TIMING_1(pio_t0, pio_t2_8, pio_t2_16); + mpc5200.ata_pio2 = ATA_PIO_TIMING_2(pio_t4, pio_t1, pio_ta); + + mpc5200.ata_dma1 = DMA1_T0(120) | DMA1_TD(70) | DMA1_TK(25) | DMA1_TM(25); + mpc5200.ata_dma2 = DMA2_TH(10) | DMA2_TJ(5) | DMA2_TN(10); + + return RTEMS_SUCCESSFUL; + + } + + + +static void mpc5200_pcmciaide_read_reg(int minor, int reg, uint16_t *value) + { + volatile uint32_t *ata_reg = mpc5200_ata_drive_regs[reg]; + + if(reg == IDE_REGISTER_DATA_WORD) + *value = *(volatile uint16_t *)(ata_reg); + else + *value = *(volatile uint8_t *)(ata_reg); + } + + +static void mpc5200_pcmciaide_write_reg(int minor, int reg, uint16_t value) + { + volatile uint32_t *ata_reg = mpc5200_ata_drive_regs[reg]; + + if(reg == IDE_REGISTER_DATA_WORD) + *(volatile uint16_t *)(ata_reg) = value; + else + *(volatile uint8_t *)(ata_reg) = value; + } + +#if IDE_USE_DMA + + +uint32_t pcmcia_ide_rxInterrupts; +uint32_t pcmcia_ide_txInterrupts; +volatile rtems_id pcmcia_ide_hdl_task = 0; +/* + * MPC5200 BestComm interrupt handlers + */ +static void pcmcia_ide_recv_dmairq_hdl(rtems_irq_hdl_param unused) +{ + SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend,IDE_RX_TASK_NO); + +/*Disable receive ints*/ + bestcomm_glue_irq_disable(IDE_RX_TASK_NO); + + pcmcia_ide_rxInterrupts++; /* Rx int has occurred */ + + if (pcmcia_ide_hdl_task != 0) { + rtems_event_send(pcmcia_ide_hdl_task,PCMCIA_IDE_INTERRUPT_EVENT); + } +} + +static void pcmcia_ide_xmit_dmairq_hdl(rtems_irq_hdl_param unused) +{ + + SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend,IDE_TX_TASK_NO); + + /*Disable transmit ints*/ + bestcomm_glue_irq_disable(IDE_TX_TASK_NO); + + pcmcia_ide_txInterrupts++; /* Tx int has occurred */ + + if (pcmcia_ide_hdl_task != 0) { + rtems_event_send(pcmcia_ide_hdl_task,PCMCIA_IDE_INTERRUPT_EVENT); + } +} + + +void mpc5200_pcmciaide_dma_init(int minor) +{ + TaskSetupParamSet_t rxParam; /* RX task setup parameters */ + TaskSetupParamSet_t txParam; /* TX task setup parameters */ + /* + * Init Bestcomm system + */ + bestcomm_glue_init(); + /* + * Setup the SDMA RX task. + */ + rxParam.NumBD = PCMCIA_IDE_DMA_RD_BD_CNT; + rxParam.Size.MaxBuf = PCMCIA_IDE_RD_SECTOR_SIZE; + rxParam.Initiator = INITIATOR_ALWAYS; + rxParam.StartAddrSrc = + (uint32)mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD]; + rxParam.IncrSrc = 0; + rxParam.SzSrc = sizeof(uint16_t); + rxParam.StartAddrDst = (uint32)NULL; + rxParam.IncrDst = sizeof(uint16_t); + rxParam.SzDst = sizeof(uint16_t); /* XXX: set this to 32 bit? */ + + pcmcia_ide_rxTaskId = TaskSetup(IDE_RX_TASK_NO,&rxParam ); + + /* + * Setup the TX task. + */ + txParam.NumBD = PCMCIA_IDE_DMA_WR_BD_CNT; + txParam.Size.MaxBuf = PCMCIA_IDE_WR_SECTOR_SIZE; + txParam.Initiator = INITIATOR_ALWAYS; + txParam.StartAddrSrc = (uint32)NULL; + txParam.IncrSrc = sizeof(uint16_t); + txParam.SzSrc = sizeof(uint16_t); /* do not set this to 32 bit! */ + txParam.StartAddrDst = + (uint32)mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD]; + txParam.IncrDst = 0; + txParam.SzDst = sizeof(uint16_t); + + pcmcia_ide_txTaskId = TaskSetup( IDE_TX_TASK_NO, &txParam ); + /* + * FIXME: Init BD rings + */ + /* + * Enable the SmartDMA transmit/receive task. + * do not enable interrupts to CPU + */ + /* + * connect interrupt handlers + */ + bestcomm_glue_irq_install(IDE_TX_TASK_NO,pcmcia_ide_xmit_dmairq_hdl,NULL); + bestcomm_glue_irq_install(IDE_RX_TASK_NO,pcmcia_ide_recv_dmairq_hdl,NULL); +} +#endif /* IDE_USE_DMA */ + +void mpc5200_pcmciaide_dma_blockop(bool is_write, + int minor, + uint16_t block_size, + rtems_blkdev_sg_buffer *bufs, + uint32_t *cbuf, + uint32_t *pos) + +{ +#if IDE_USE_DMA + /* + * Nameing: + * - a block is one unit of data on disk (multiple sectors) + * - a buffer is a contignuous chunk of data in memory + * a block on disk may be filled with data from several buffers + */ + uint32_t buf_idx,bufs_from_dma, bufs_to_dma,bufs_total; + uint32_t bds_free; + uint32_t llength; + rtems_status_code rc = RTEMS_SUCCESSFUL; + rtems_event_set events; + BDIdx nxt_bd_idx; + bool use_irq = (_System_state_Current == SYSTEM_STATE_UP); + /* + * determine number of blocks + */ + llength = 0; + buf_idx = 0; + bufs += *cbuf; /* *cbuf is the index of the next buffer to send in this transaction */ + while (llength < block_size) { + llength += bufs[buf_idx++].length; + } + bufs_from_dma = 0; + bufs_to_dma = 0; + bufs_total = buf_idx; + /* + * here all BDs should be unused + */ + bds_free = is_write ? PCMCIA_IDE_DMA_WR_BD_CNT : PCMCIA_IDE_DMA_RD_BD_CNT; + /* + * repeat, until all bufs are transferred + */ + while ((rc == RTEMS_SUCCESSFUL) && + (bufs_from_dma < bufs_total)) { + + while ((rc == RTEMS_SUCCESSFUL) && + (bufs_to_dma < bufs_total) && + (bds_free > 0)) { + /* + * fill in BD, set interrupt if needed + */ + SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend,(is_write + ? IDE_TX_TASK_NO + : IDE_RX_TASK_NO)); + if (is_write) { + TaskBDAssign(pcmcia_ide_txTaskId , + (void *)bufs[bufs_to_dma].buffer, + (void *)mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD], + bufs[bufs_to_dma].length, + 0/* flags */); +#if IDE_USE_STATISTICS + mpc5200_pcmciaide_write_block_block_cnt++; +#endif + } + else { + TaskBDAssign(pcmcia_ide_rxTaskId , + (void *)mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD], + (void *)bufs[bufs_to_dma].buffer, + bufs[bufs_to_dma].length, + 0/* flags */); +#if IDE_USE_STATISTICS + mpc5200_pcmciaide_read_block_block_cnt++; +#endif + } + bufs_to_dma ++; + bds_free --; + } + if (is_write) { + TaskStart( pcmcia_ide_txTaskId, TASK_AUTOSTART_DISABLE, + pcmcia_ide_txTaskId, TASK_INTERRUPT_DISABLE ); + } + else { + TaskStart( pcmcia_ide_rxTaskId, TASK_AUTOSTART_DISABLE, + pcmcia_ide_rxTaskId, TASK_INTERRUPT_DISABLE ); + } + if (use_irq) { + + /* + * enable interrupts, wait for interrupt event + */ + pcmcia_ide_hdl_task = rtems_task_self(); + bestcomm_glue_irq_enable((is_write + ? IDE_TX_TASK_NO + : IDE_RX_TASK_NO)); + + rtems_event_receive(PCMCIA_IDE_INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + + pcmcia_ide_hdl_task = 0; + } + else { + /* + * HACK: just wait some time... + */ + /* + * FIXME: poll, until SDMA is finished + */ + volatile int32_t i; + for (i = 0;i < 10000;i++) {}; + } + + do { + nxt_bd_idx = TaskBDRelease(is_write + ? pcmcia_ide_txTaskId + : pcmcia_ide_rxTaskId); + if ((nxt_bd_idx != TASK_ERR_BD_RING_EMPTY) && + (nxt_bd_idx != TASK_ERR_BD_BUSY)) { + (*cbuf)++; + (*pos) += bufs[bufs_from_dma].length; + bufs_from_dma++; + bds_free++; + } + } while ((nxt_bd_idx != TASK_ERR_BD_RING_EMPTY) && + (nxt_bd_idx != TASK_ERR_BD_BUSY) && + (bufs_from_dma < bufs_to_dma)); + } +#endif /* IDE_USE_DMA */ +} + + +static void mpc5200_pcmciaide_read_block(int minor, uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, uint32_t *cbuf, uint32_t *pos) +{ + + volatile uint32_t *ata_reg=mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD]; + uint16_t cnt = 0; + uint16_t *lbuf = (uint16_t*)((uint8_t*)(bufs[(*cbuf)].buffer)+(*pos)); + uint32_t llength = bufs[(*cbuf)].length; + bool use_dma; + +#if IDE_USE_STATISTICS + mpc5200_pcmciaide_read_block_call_cnt++; +#endif +#if IDE_READ_USE_DMA + /* + * FIXME: walk through buffer list. If any buffer has other size than default, + * then do not use DMA + * Is this needed? + */ + use_dma = true; + /* use_dma = false; */ +#else + use_dma = false; +#endif + if (use_dma) { + /* + * FIXME: wait for DRQ ready + * check, that once DRQ is ready, we really can send ALL data for this + * type of transfer mode + */ + while ((GET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG((volatile uint32_t) + (mpc5200.ata_dctr_dasr)) & + IDE_REGISTER_STATUS_DRQ) == 0); + /* + * translate (part of) buffer list into DMA BDs + * only last (available) DMA BD sends interrupt + * DMA BDs may get ready as soon as possible + */ + mpc5200_pcmciaide_dma_blockop(FALSE, /* read operation */ + minor, + block_size,bufs,cbuf,pos); + } + else { +#if IDE_USE_READ_PIO_OPT + while(cnt < block_size) { + + *lbuf++ = GET_UP_WORD_OF_MPC5200_ATA_DRIVE_REG(*(volatile uint32_t *)(ata_reg)); /* only 16 bit data port */ + cnt += 2; + (*pos) += 2; + + if((*pos) == llength) { + + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + llength = bufs[(*cbuf)].length; + + } + } +#else + + while((GET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG((volatile uint32_t)(mpc5200.ata_dctr_dasr)) & IDE_REGISTER_STATUS_DRQ) && (cnt < block_size)) { + + *lbuf++ = *(volatile uint16_t *)(ata_reg); /* only 16 bit data port */ + cnt += 2; + (*pos) += 2; + + if((*pos) == llength) { + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + llength = bufs[(*cbuf)].length; + } + } +#endif + } +} + +static void mpc5200_pcmciaide_write_block(int minor, uint32_t block_size, + rtems_blkdev_sg_buffer *bufs, uint32_t *cbuf, uint32_t *pos) +{ + + + volatile uint32_t *ata_reg = mpc5200_ata_drive_regs[IDE_REGISTER_DATA_WORD]; + uint16_t cnt = 0; + uint16_t *lbuf = (uint16_t *)((uint8_t *)(bufs[(*cbuf)].buffer) + (*pos)); + uint32_t llength = bufs[(*cbuf)].length; + bool use_dma; + +#if IDE_USE_STATISTICS + mpc5200_pcmciaide_write_block_call_cnt++; +#endif +#if IDE_WRITE_USE_DMA + /* + * FIXME: walk through buffer list. If any buffer has other size than default, + * then do not use DMA + * Is this needed? + */ + use_dma = true; +#else + use_dma = false; +#endif + + if (use_dma) { + /* + * wait for DRQ ready + * FIXME: check, that once DRQ is ready, we really can send ALL data for this + * type of transfer mode + */ + while ((GET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG((volatile uint32_t) + (mpc5200.ata_dctr_dasr)) & + IDE_REGISTER_STATUS_DRQ) == 0); + /* + * translate (part of) buffer list into DMA BDs + * only last (available) DMA BD sends interrupt + * DMA BDs may get ready as soon as possible + */ + mpc5200_pcmciaide_dma_blockop(true, /* write opeartion */ + minor, + block_size,bufs,cbuf,pos); + } + else { +#if IDE_USE_WRITE_PIO_OPT + while(cnt < block_size) { + int32_t loop_cnt,loop_max; + +#if IDE_USE_STATISTICS + mpc5200_pcmciaide_write_block_block_cnt++; +#endif + + loop_max = llength - (*pos) ; + if (loop_max > (block_size - cnt)) { + loop_max = (block_size - cnt); + } + for (loop_cnt = loop_max/2;loop_cnt > 0;loop_cnt--) { + *(volatile uint32_t *)(ata_reg) = + SET_UP_WORD_OF_MPC5200_ATA_DRIVE_REG(*lbuf++); /* only 16 bit data port */ + } + cnt += loop_max; + (*pos) += loop_max; + + if((*pos) == llength) { + + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + llength = bufs[(*cbuf)].length; + } + } +#else + while((GET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG((volatile uint32_t)(mpc5200.ata_dctr_dasr)) + & IDE_REGISTER_STATUS_DRQ) + && (cnt < block_size)) { + *(volatile uint16_t *)(ata_reg) = *lbuf++; /* only 16 bit data port */ + cnt += 2; + (*pos) += 2; + + if((*pos) == llength) { + (*pos) = 0; + (*cbuf)++; + lbuf = bufs[(*cbuf)].buffer; + llength = bufs[(*cbuf)].length; + } + } +#endif + } +} + +static int mpc5200_pcmciaide_control(int minor, uint32_t cmd, void * arg) + { + return RTEMS_SUCCESSFUL; + } + +static void mpc5200_pcmciaide_initialize(int minor) + { +#if defined (MPC5200_BOARD_BRS5L) + struct mpc5200_gpt *gpt = (struct mpc5200_gpt *)(&mpc5200.gpt[GPT7]); + + /* invert ATA reset on GPT7 */ + gpt->emsel = (GPT_EMSEL_GPIO_OUT_HIGH | GPT_EMSEL_TIMER_MS_GPIO); +#endif + /* reset ata host contr. and FIFO */ + mpc5200.ata_hcfg |= (ATA_HCFG_SMR | ATA_HCFG_FR); + mpc5200.ata_hcfg &= ~(ATA_HCFG_SMR | ATA_HCFG_FR); + + /* for the first access set lowest performance transfer mode to PIO3 */ + mpc5200_pcmciaide_config_io_speed(minor, ATA_MODES_PIO3); + + /* enable PIO operations (PIO 3/4) */ + mpc5200.ata_hcfg |= ATA_HCFG_IORDY; + +#ifdef IDE_USE_INT + mpc5200.ata_hcfg |= ATA_HCFG_IE ; +#endif + +#if IDE_USE_DMA + mpc5200_pcmciaide_dma_init(minor); +#endif + } + + +/* + * The following table configures the functions used for IDE drivers + * in this BSP. + */ +ide_ctrl_fns_t mpc5200_pcmciaide_ctrl_fns = + { + mpc5200_pcmciaide_probe, + mpc5200_pcmciaide_initialize, + mpc5200_pcmciaide_control, + mpc5200_pcmciaide_read_reg, + mpc5200_pcmciaide_write_reg, + mpc5200_pcmciaide_read_block, + mpc5200_pcmciaide_write_block, + mpc5200_pcmciaide_config_io_speed + }; + diff --git a/bsps/powerpc/gen5200/ata/pcmcia_ide.h b/bsps/powerpc/gen5200/ata/pcmcia_ide.h new file mode 100644 index 0000000000..8cd74729a5 --- /dev/null +++ b/bsps/powerpc/gen5200/ata/pcmcia_ide.h @@ -0,0 +1,98 @@ +/*===============================================================*\ +| Project: RTEMS generic MPC5200 BSP | ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2005 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| 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. | +| | ++-----------------------------------------------------------------+ +| this file contains declarations for the PCMCIA IDE Interface | +\*===============================================================*/ +/***********************************************************************/ +/* */ +/* Module: pcmcia_ide.h */ +/* Date: 17/07/2003 */ +/* Purpose: RTEMS MPC5x00 PCMCIA IDE harddisk header file */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Description: */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Code */ +/* References: none */ +/* Module: */ +/* Project: */ +/* Version */ +/* Date: */ +/* */ +/* Author(s) / Copyright(s): */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Partially based on the code references which are named above. */ +/* Adaptions, modifications, enhancements and any recent parts of */ +/* the code are under the right of */ +/* */ +/* IPR Engineering, Dachauer Straße 38, D-80335 München */ +/* Copyright(C) 2003 */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* IPR Engineering makes no representation or warranties with */ +/* respect to the performance of this computer program, and */ +/* specifically disclaims any responsibility for any damages, */ +/* special or consequential, connected with the use of this program. */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Version history: 1.0 */ +/* */ +/***********************************************************************/ + +#ifndef __PCMCIA_IDE_h +#define __PCMCIA_IDE_h + +#include <bsp.h> + +#define GPIOPCR_ATA_CS_4_5 (1 << 24) + +/*#define DEBUG_OUT*/ +#define GET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG(val32) ((uint16_t)((val32) >> 24)) +#define SET_UP_BYTE_OF_MPC5200_ATA_DRIVE_REG(val8) ((uint32_t)((val8) << 24)) +#define GET_UP_WORD_OF_MPC5200_ATA_DRIVE_REG(val32) ((uint16_t)((val32) >> 16)) +#define SET_UP_WORD_OF_MPC5200_ATA_DRIVE_REG(val16) ((uint32_t)((val16) << 16)) + +#define ATA_HCFG_SMR (1 << 31) +#define ATA_HCFG_FR (1 << 30) +#define ATA_HCFG_IORDY (1 << 24) +#define ATA_HCFG_IE (1 << 25) + +#define COUNT_VAL(nsec) (((nsec) * (IPB_CLOCK / 1000000) + 999) / 1000) + +#define PIO_3 0 +#define PIO_4 1 + +#define T0 0 +#define T2_8 1 +#define T2_16 2 +#define T4 3 +#define T1 4 +#define TA 5 + +#define ATA_PIO_TIMING_1(t0,t2_8,t2_16) (((COUNT_VAL(t0)) << 24) | ((COUNT_VAL(t2_8)) << 16) | ((COUNT_VAL(t2_16)) << 8)) +#define ATA_PIO_TIMING_2(t4,t1,ta) (((COUNT_VAL(t4)) << 24) | ((COUNT_VAL(t1)) << 16) | ((COUNT_VAL(ta)) << 8)) + +#endif |