//==========================================================================
|
//==========================================================================
|
//
|
//
|
// io/pcmcia/pcmcia.c
|
// io/pcmcia/pcmcia.c
|
//
|
//
|
// PCMCIA support (Card Services)
|
// PCMCIA support (Card Services)
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos is free software; you can redistribute it and/or modify it under
|
// the terms of the GNU General Public License as published by the Free
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas
|
// Contributors: gthomas
|
// Date: 2000-07-06
|
// Date: 2000-07-06
|
// Purpose: PCMCIA support
|
// Purpose: PCMCIA support
|
// Description:
|
// Description:
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
#include <pkgconf/io_pcmcia.h>
|
#include <pkgconf/io_pcmcia.h>
|
|
|
#include <cyg/io/pcmcia.h>
|
#include <cyg/io/pcmcia.h>
|
#include <cyg/infra/diag.h>
|
#include <cyg/infra/diag.h>
|
|
|
#if CYGHWR_IO_PCMCIA_DEVICE == 0
|
#if CYGHWR_IO_PCMCIA_DEVICE == 0
|
#error Need hardware package for PCMCIA support
|
#error Need hardware package for PCMCIA support
|
#endif
|
#endif
|
|
|
#define CF_NUM_SLOTS CYGHWR_IO_PCMCIA_DEVICE
|
#define CF_NUM_SLOTS CYGHWR_IO_PCMCIA_DEVICE
|
static struct cf_slot cf_slots[CF_NUM_SLOTS];
|
static struct cf_slot cf_slots[CF_NUM_SLOTS];
|
|
|
// Implementation routines
|
// Implementation routines
|
void cf_hwr_init(struct cf_slot *slot);
|
void cf_hwr_init(struct cf_slot *slot);
|
void cf_hwr_change_state(struct cf_slot *slot, int desired_state);
|
void cf_hwr_change_state(struct cf_slot *slot, int desired_state);
|
void cf_hwr_clear_interrupt(struct cf_slot *slot);
|
void cf_hwr_clear_interrupt(struct cf_slot *slot);
|
|
|
bool
|
bool
|
cf_get_CIS(struct cf_slot *slot, unsigned char id,
|
cf_get_CIS(struct cf_slot *slot, unsigned char id,
|
unsigned char *buf, int *len, int *ptr)
|
unsigned char *buf, int *len, int *ptr)
|
{
|
{
|
int i, size;
|
int i, size;
|
unsigned char *cis = slot->attr;
|
unsigned char *cis = slot->attr;
|
unsigned char *cis_end = cis + slot->attr_length;
|
unsigned char *cis_end = cis + slot->attr_length;
|
cis += *ptr;
|
cis += *ptr;
|
while (cis < cis_end) {
|
while (cis < cis_end) {
|
if (*cis == 0xFF) {
|
if (*cis == 0xFF) {
|
break;
|
break;
|
}
|
}
|
if (*cis == id) {
|
if (*cis == id) {
|
size = *(cis+2) + 2;
|
size = *(cis+2) + 2;
|
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
*buf++ = *cis;
|
*buf++ = *cis;
|
cis += 2;
|
cis += 2;
|
}
|
}
|
*len = size;
|
*len = size;
|
*ptr = (unsigned long)(cis - slot->attr);
|
*ptr = (unsigned long)(cis - slot->attr);
|
return true;
|
return true;
|
} else {
|
} else {
|
// Skip to next entry
|
// Skip to next entry
|
cis += (*(cis+2) * 2) + 4;
|
cis += (*(cis+2) * 2) + 4;
|
}
|
}
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
void
|
void
|
cf_set_COR(struct cf_slot *slot, unsigned long cor, unsigned char val)
|
cf_set_COR(struct cf_slot *slot, unsigned long cor, unsigned char val)
|
{
|
{
|
volatile unsigned char *cfg = slot->attr;
|
volatile unsigned char *cfg = slot->attr;
|
cfg[cor] = val;
|
cfg[cor] = val;
|
}
|
}
|
|
|
static void
|
static void
|
cf_parse_power_structure(unsigned char **buf)
|
cf_parse_power_structure(unsigned char **buf)
|
{
|
{
|
unsigned char *bp = *buf;
|
unsigned char *bp = *buf;
|
unsigned char tpce_pd = *bp++;
|
unsigned char tpce_pd = *bp++;
|
unsigned char settings;
|
unsigned char settings;
|
int indx;
|
int indx;
|
for (indx = 6; indx >= 0; indx--) {
|
for (indx = 6; indx >= 0; indx--) {
|
if (tpce_pd & (1<<indx)) {
|
if (tpce_pd & (1<<indx)) {
|
settings = *bp++; // main value
|
settings = *bp++; // main value
|
if (settings & 0x80) {
|
if (settings & 0x80) {
|
bp++; // extension byte - FIXME
|
bp++; // extension byte - FIXME
|
}
|
}
|
}
|
}
|
}
|
}
|
*buf = bp;
|
*buf = bp;
|
}
|
}
|
|
|
static void
|
static void
|
cf_parse_timing_structure(unsigned char **buf)
|
cf_parse_timing_structure(unsigned char **buf)
|
{
|
{
|
unsigned char *bp = *buf;
|
unsigned char *bp = *buf;
|
unsigned char tpce_td = *bp++;
|
unsigned char tpce_td = *bp++;
|
if ((tpce_td & 0x1C) != 0x1C) {
|
if ((tpce_td & 0x1C) != 0x1C) {
|
// diag_printf("READY = %x.%x\n",(tpce_td & 0x1C)>>2, *bp);
|
// diag_printf("READY = %x.%x\n",(tpce_td & 0x1C)>>2, *bp);
|
bp++;
|
bp++;
|
}
|
}
|
if ((tpce_td & 0x03) != 0x03) {
|
if ((tpce_td & 0x03) != 0x03) {
|
// diag_printf("WAIT = %x.%x\n",(tpce_td & 0x03)>>0, *bp);
|
// diag_printf("WAIT = %x.%x\n",(tpce_td & 0x03)>>0, *bp);
|
bp++;
|
bp++;
|
}
|
}
|
*buf = bp;
|
*buf = bp;
|
}
|
}
|
|
|
static void
|
static void
|
cf_parse_IO_space_structure(unsigned char **buf, struct cf_io_space *io_space)
|
cf_parse_IO_space_structure(unsigned char **buf, struct cf_io_space *io_space)
|
{
|
{
|
unsigned char *bp = *buf;
|
unsigned char *bp = *buf;
|
unsigned char tpce_io = *bp++;
|
unsigned char tpce_io = *bp++;
|
unsigned char rd;
|
unsigned char rd;
|
unsigned long base = 0, length = 0;
|
unsigned long base = 0, length = 0;
|
int i;
|
int i;
|
io_space->mode = (tpce_io & 0x60) >> 5;
|
io_space->mode = (tpce_io & 0x60) >> 5;
|
if (tpce_io & 0x80) {
|
if (tpce_io & 0x80) {
|
rd = *bp++;
|
rd = *bp++;
|
io_space->num_addrs = (rd & 0x0F) + 1;
|
io_space->num_addrs = (rd & 0x0F) + 1;
|
for (i = 0; i < io_space->num_addrs; i++) {
|
for (i = 0; i < io_space->num_addrs; i++) {
|
// Address
|
// Address
|
switch ((rd & 0x30) >> 4) {
|
switch ((rd & 0x30) >> 4) {
|
case 0:
|
case 0:
|
break; // Not present (shouldn't happen)
|
break; // Not present (shouldn't happen)
|
case 1:
|
case 1:
|
base = *bp++;
|
base = *bp++;
|
break;
|
break;
|
case 2:
|
case 2:
|
base = (bp[1] << 8) | bp[0];
|
base = (bp[1] << 8) | bp[0];
|
bp += 2;
|
bp += 2;
|
break;
|
break;
|
case 3:
|
case 3:
|
base = (bp[3] << 24) | (bp[2] << 16) | (bp[1] << 8) | bp[0];
|
base = (bp[3] << 24) | (bp[2] << 16) | (bp[1] << 8) | bp[0];
|
bp += 4;
|
bp += 4;
|
break;
|
break;
|
}
|
}
|
io_space->base[i] = base;
|
io_space->base[i] = base;
|
// Length
|
// Length
|
switch ((rd & 0xC0) >> 6) {
|
switch ((rd & 0xC0) >> 6) {
|
case 0:
|
case 0:
|
break; // Not present (shouldn't happen)
|
break; // Not present (shouldn't happen)
|
case 1:
|
case 1:
|
length = *bp++;
|
length = *bp++;
|
break;
|
break;
|
case 2:
|
case 2:
|
length = (bp[1] << 8) | bp[0];
|
length = (bp[1] << 8) | bp[0];
|
bp += 2;
|
bp += 2;
|
break;
|
break;
|
case 3:
|
case 3:
|
length = (bp[3] << 24) | (bp[2] << 16) | (bp[1] << 8) | bp[0];
|
length = (bp[3] << 24) | (bp[2] << 16) | (bp[1] << 8) | bp[0];
|
bp += 4;
|
bp += 4;
|
break;
|
break;
|
}
|
}
|
length++;
|
length++;
|
io_space->size[i] = length;
|
io_space->size[i] = length;
|
// diag_printf("IO addr %d - base: %x, length: %x\n", i, base, length);
|
// diag_printf("IO addr %d - base: %x, length: %x\n", i, base, length);
|
}
|
}
|
}
|
}
|
*buf = bp;
|
*buf = bp;
|
}
|
}
|
|
|
bool
|
bool
|
cf_parse_cftable(unsigned char *buf, int len, struct cf_cftable *cftable)
|
cf_parse_cftable(unsigned char *buf, int len, struct cf_cftable *cftable)
|
{
|
{
|
unsigned char tpce_indx, tpce_fs;
|
unsigned char tpce_indx, tpce_fs;
|
if (*buf++ != CF_CISTPL_CFTABLE_ENTRY) {
|
if (*buf++ != CF_CISTPL_CFTABLE_ENTRY) {
|
diag_printf("%s - called with invalid CIS: %x\n", __FUNCTION__, *--buf);
|
diag_printf("%s - called with invalid CIS: %x\n", __FUNCTION__, *--buf);
|
return false;
|
return false;
|
}
|
}
|
buf++; // Skip length/link
|
buf++; // Skip length/link
|
tpce_indx = *buf++;
|
tpce_indx = *buf++;
|
cftable->cor = tpce_indx & 0x3F;
|
cftable->cor = tpce_indx & 0x3F;
|
if (tpce_indx & 0x80) {
|
if (tpce_indx & 0x80) {
|
cftable->interface = *buf++;
|
cftable->interface = *buf++;
|
}
|
}
|
cftable->feature_select = tpce_fs = *buf++;
|
cftable->feature_select = tpce_fs = *buf++;
|
if (tpce_fs & 0x01) {
|
if (tpce_fs & 0x01) {
|
cf_parse_power_structure(&buf);
|
cf_parse_power_structure(&buf);
|
}
|
}
|
if (tpce_fs & 0x02) {
|
if (tpce_fs & 0x02) {
|
cf_parse_power_structure(&buf);
|
cf_parse_power_structure(&buf);
|
}
|
}
|
if (tpce_fs & 0x04) {
|
if (tpce_fs & 0x04) {
|
cf_parse_timing_structure(&buf);
|
cf_parse_timing_structure(&buf);
|
}
|
}
|
if (tpce_fs & 0x08) {
|
if (tpce_fs & 0x08) {
|
cf_parse_IO_space_structure(&buf, &cftable->io_space);
|
cf_parse_IO_space_structure(&buf, &cftable->io_space);
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
bool
|
bool
|
cf_parse_config(unsigned char *buf, int len, struct cf_config *config)
|
cf_parse_config(unsigned char *buf, int len, struct cf_config *config)
|
{
|
{
|
unsigned char tpcc_sz;
|
unsigned char tpcc_sz;
|
int i;
|
int i;
|
if (*buf++ != CF_CISTPL_CONFIG) {
|
if (*buf++ != CF_CISTPL_CONFIG) {
|
diag_printf("%s - called with invalid CIS: %x\n", __FUNCTION__, *--buf);
|
diag_printf("%s - called with invalid CIS: %x\n", __FUNCTION__, *--buf);
|
return false;
|
return false;
|
}
|
}
|
buf++; // Skip length/link
|
buf++; // Skip length/link
|
tpcc_sz = *buf++;
|
tpcc_sz = *buf++;
|
buf++; // Skip 'last' pointer
|
buf++; // Skip 'last' pointer
|
config->base = 0;
|
config->base = 0;
|
for (i = (tpcc_sz & 0x03); i >= 0; i--) {
|
for (i = (tpcc_sz & 0x03); i >= 0; i--) {
|
config->base = (config->base << 8) | buf[i];
|
config->base = (config->base << 8) | buf[i];
|
}
|
}
|
buf += (tpcc_sz & 0x03) + 1;
|
buf += (tpcc_sz & 0x03) + 1;
|
config->mask_length = ((tpcc_sz & 0x3C) >> 2) + 1;
|
config->mask_length = ((tpcc_sz & 0x3C) >> 2) + 1;
|
for (i = 0; i < config->mask_length; i++) {
|
for (i = 0; i < config->mask_length; i++) {
|
config->mask[i] = *buf++;
|
config->mask[i] = *buf++;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
//
|
//
|
// Return a pointer to the slot descriptor for a given slot
|
// Return a pointer to the slot descriptor for a given slot
|
//
|
//
|
struct cf_slot *
|
struct cf_slot *
|
cf_get_slot(int indx)
|
cf_get_slot(int indx)
|
{
|
{
|
if ((indx >= 0) && (indx < CF_NUM_SLOTS)) {
|
if ((indx >= 0) && (indx < CF_NUM_SLOTS)) {
|
return &cf_slots[indx];
|
return &cf_slots[indx];
|
} else {
|
} else {
|
diag_printf("PCMCIA: Invalid slot %d\n", indx);
|
diag_printf("PCMCIA: Invalid slot %d\n", indx);
|
return (struct cf_slot *)0;
|
return (struct cf_slot *)0;
|
}
|
}
|
}
|
}
|
|
|
//
|
//
|
// Initialize all PCMCIA (Compact Flash) slots
|
// Initialize all PCMCIA (Compact Flash) slots
|
//
|
//
|
void
|
void
|
cf_init(void)
|
cf_init(void)
|
{
|
{
|
int i;
|
int i;
|
for (i = 0; i < CF_NUM_SLOTS; i++) {
|
for (i = 0; i < CF_NUM_SLOTS; i++) {
|
cf_slots[i].index = i;
|
cf_slots[i].index = i;
|
cf_hwr_init(&cf_slots[i]);
|
cf_hwr_init(&cf_slots[i]);
|
}
|
}
|
}
|
}
|
|
|
//
|
//
|
// Transition a card/slot
|
// Transition a card/slot
|
//
|
//
|
void
|
void
|
cf_change_state(struct cf_slot *slot, int desired_state)
|
cf_change_state(struct cf_slot *slot, int desired_state)
|
{
|
{
|
cf_hwr_change_state(slot, desired_state);
|
cf_hwr_change_state(slot, desired_state);
|
}
|
}
|
|
|
//
|
//
|
// Register an interrupt handler
|
// Register an interrupt handler
|
//
|
//
|
void
|
void
|
cf_register_handler(struct cf_slot *slot,
|
cf_register_handler(struct cf_slot *slot,
|
void (*handler)(int, int, void *),
|
void (*handler)(int, int, void *),
|
void *param)
|
void *param)
|
{
|
{
|
slot->irq_handler.handler = handler;
|
slot->irq_handler.handler = handler;
|
slot->irq_handler.param = param;
|
slot->irq_handler.param = param;
|
}
|
}
|
|
|
//
|
//
|
// Allow interrupt function to acknowledge interrupt
|
// Allow interrupt function to acknowledge interrupt
|
//
|
//
|
void
|
void
|
cf_clear_interrupt(struct cf_slot *slot)
|
cf_clear_interrupt(struct cf_slot *slot)
|
{
|
{
|
cf_hwr_clear_interrupt(slot);
|
cf_hwr_clear_interrupt(slot);
|
}
|
}
|
|
|
|
|