/*
|
/*
|
* linux/arch/arm/drivers/block/ide-ics.c
|
* linux/arch/arm/drivers/block/ide-ics.c
|
*
|
*
|
* Copyright (c) 1996,1997 Russell King.
|
* Copyright (c) 1996,1997 Russell King.
|
*
|
*
|
* Changelog:
|
* Changelog:
|
* 08-06-1996 RMK Created
|
* 08-06-1996 RMK Created
|
* 12-09-1997 RMK Added interrupt enable/disable
|
* 12-09-1997 RMK Added interrupt enable/disable
|
*/
|
*/
|
|
|
#include <linux/module.h>
|
#include <linux/module.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
|
|
#include <asm/dma.h>
|
#include <asm/dma.h>
|
#include <asm/ecard.h>
|
#include <asm/ecard.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
|
|
#include "ide.h"
|
#include "ide.h"
|
|
|
/*
|
/*
|
* Maximum number of interfaces per card
|
* Maximum number of interfaces per card
|
*/
|
*/
|
#define MAX_IFS 2
|
#define MAX_IFS 2
|
|
|
#define ICS_IDENT_OFFSET 0x8a0
|
#define ICS_IDENT_OFFSET 0x8a0
|
|
|
#define ICS_ARCIN_V5_INTRSTAT 0x000
|
#define ICS_ARCIN_V5_INTRSTAT 0x000
|
#define ICS_ARCIN_V5_INTROFFSET 0x001
|
#define ICS_ARCIN_V5_INTROFFSET 0x001
|
#define ICS_ARCIN_V5_IDEOFFSET 0xa00
|
#define ICS_ARCIN_V5_IDEOFFSET 0xa00
|
#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
|
#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
|
#define ICS_ARCIN_V5_IDESTEPPING 4
|
#define ICS_ARCIN_V5_IDESTEPPING 4
|
|
|
#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
|
#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
|
#define ICS_ARCIN_V6_INTROFFSET_1 0x880
|
#define ICS_ARCIN_V6_INTROFFSET_1 0x880
|
#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
|
#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
|
#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
|
#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
|
#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
|
#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
|
#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
|
#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
|
#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
|
#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
|
#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
|
#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
|
#define ICS_ARCIN_V6_IDESTEPPING 4
|
#define ICS_ARCIN_V6_IDESTEPPING 4
|
|
|
static const card_ids icside_cids[] = {
|
static const card_ids icside_cids[] = {
|
{ MANU_ICS, PROD_ICS_IDE },
|
{ MANU_ICS, PROD_ICS_IDE },
|
{ MANU_ICS2, PROD_ICS2_IDE },
|
{ MANU_ICS2, PROD_ICS2_IDE },
|
{ 0xffff, 0xffff }
|
{ 0xffff, 0xffff }
|
};
|
};
|
|
|
typedef enum {
|
typedef enum {
|
ics_if_unknown,
|
ics_if_unknown,
|
ics_if_arcin_v5,
|
ics_if_arcin_v5,
|
ics_if_arcin_v6
|
ics_if_arcin_v6
|
} iftype_t;
|
} iftype_t;
|
|
|
static struct expansion_card *ec[MAX_ECARDS];
|
static struct expansion_card *ec[MAX_ECARDS];
|
static int result[MAX_ECARDS][MAX_IFS];
|
static int result[MAX_ECARDS][MAX_IFS];
|
|
|
|
|
/* ---------------- Version 5 PCB Support Functions --------------------- */
|
/* ---------------- Version 5 PCB Support Functions --------------------- */
|
/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
* Purpose : enable interrupts from card
|
* Purpose : enable interrupts from card
|
*/
|
*/
|
static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
{
|
{
|
unsigned int memc_port = (unsigned int)ec->irq_data;
|
unsigned int memc_port = (unsigned int)ec->irq_data;
|
outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
|
outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
|
}
|
}
|
|
|
/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
* Purpose : disable interrupts from card
|
* Purpose : disable interrupts from card
|
*/
|
*/
|
static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
|
{
|
{
|
unsigned int memc_port = (unsigned int)ec->irq_data;
|
unsigned int memc_port = (unsigned int)ec->irq_data;
|
inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
|
inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
|
}
|
}
|
|
|
static const expansioncard_ops_t icside_ops_arcin_v5 = {
|
static const expansioncard_ops_t icside_ops_arcin_v5 = {
|
icside_irqenable_arcin_v5,
|
icside_irqenable_arcin_v5,
|
icside_irqdisable_arcin_v5,
|
icside_irqdisable_arcin_v5,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL
|
NULL
|
};
|
};
|
|
|
|
|
/* ---------------- Version 6 PCB Support Functions --------------------- */
|
/* ---------------- Version 6 PCB Support Functions --------------------- */
|
/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
* Purpose : enable interrupts from card
|
* Purpose : enable interrupts from card
|
*/
|
*/
|
static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
{
|
{
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
|
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
|
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
|
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
|
}
|
}
|
|
|
/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
* Purpose : disable interrupts from card
|
* Purpose : disable interrupts from card
|
*/
|
*/
|
static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
|
{
|
{
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
|
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
|
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
|
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
|
}
|
}
|
|
|
/* Prototype: icside_irqprobe(struct expansion_card *ec)
|
/* Prototype: icside_irqprobe(struct expansion_card *ec)
|
* Purpose : detect an active interrupt from card
|
* Purpose : detect an active interrupt from card
|
*/
|
*/
|
static int icside_irqprobe_arcin_v6(struct expansion_card *ec)
|
static int icside_irqprobe_arcin_v6(struct expansion_card *ec)
|
{
|
{
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
unsigned int ide_base_port = (unsigned int)ec->irq_data;
|
|
|
return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
|
return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
|
inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
|
inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
|
}
|
}
|
|
|
static const expansioncard_ops_t icside_ops_arcin_v6 = {
|
static const expansioncard_ops_t icside_ops_arcin_v6 = {
|
icside_irqenable_arcin_v6,
|
icside_irqenable_arcin_v6,
|
icside_irqdisable_arcin_v6,
|
icside_irqdisable_arcin_v6,
|
icside_irqprobe_arcin_v6,
|
icside_irqprobe_arcin_v6,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL
|
NULL
|
};
|
};
|
|
|
|
|
|
|
/* Prototype: icside_identifyif (struct expansion_card *ec)
|
/* Prototype: icside_identifyif (struct expansion_card *ec)
|
* Purpose : identify IDE interface type
|
* Purpose : identify IDE interface type
|
* Notes : checks the description string
|
* Notes : checks the description string
|
*/
|
*/
|
static iftype_t icside_identifyif (struct expansion_card *ec)
|
static iftype_t icside_identifyif (struct expansion_card *ec)
|
{
|
{
|
unsigned int addr;
|
unsigned int addr;
|
iftype_t iftype;
|
iftype_t iftype;
|
int id = 0;
|
int id = 0;
|
|
|
iftype = ics_if_unknown;
|
iftype = ics_if_unknown;
|
|
|
addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
|
addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
|
|
|
id = inb (addr) & 1;
|
id = inb (addr) & 1;
|
id |= (inb (addr + 1) & 1) << 1;
|
id |= (inb (addr + 1) & 1) << 1;
|
id |= (inb (addr + 2) & 1) << 2;
|
id |= (inb (addr + 2) & 1) << 2;
|
id |= (inb (addr + 3) & 1) << 3;
|
id |= (inb (addr + 3) & 1) << 3;
|
|
|
switch (id) {
|
switch (id) {
|
case 0: /* A3IN */
|
case 0: /* A3IN */
|
printk ("icside: A3IN unsupported\n");
|
printk ("icside: A3IN unsupported\n");
|
break;
|
break;
|
|
|
case 1: /* A3USER */
|
case 1: /* A3USER */
|
printk ("icside: A3USER unsupported\n");
|
printk ("icside: A3USER unsupported\n");
|
break;
|
break;
|
|
|
case 3: /* ARCIN V6 */
|
case 3: /* ARCIN V6 */
|
printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
|
printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
|
iftype = ics_if_arcin_v6;
|
iftype = ics_if_arcin_v6;
|
break;
|
break;
|
|
|
case 15:/* ARCIN V5 (no id) */
|
case 15:/* ARCIN V5 (no id) */
|
printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
|
printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
|
iftype = ics_if_arcin_v5;
|
iftype = ics_if_arcin_v5;
|
break;
|
break;
|
|
|
default:/* we don't know - complain very loudly */
|
default:/* we don't know - complain very loudly */
|
printk ("icside: ***********************************\n");
|
printk ("icside: ***********************************\n");
|
printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
|
printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
|
printk ("icside: ***********************************\n");
|
printk ("icside: ***********************************\n");
|
printk ("icside: please report this to: linux@arm.linux.org.uk\n");
|
printk ("icside: please report this to: linux@arm.linux.org.uk\n");
|
break;
|
break;
|
}
|
}
|
|
|
return iftype;
|
return iftype;
|
}
|
}
|
|
|
/* Prototype: icside_register (struct expansion_card *ec)
|
/* Prototype: icside_register (struct expansion_card *ec)
|
* Purpose : register an ICS IDE card with the IDE driver
|
* Purpose : register an ICS IDE card with the IDE driver
|
* Notes : we make sure that interrupts are disabled from the card
|
* Notes : we make sure that interrupts are disabled from the card
|
*/
|
*/
|
static inline void icside_register (struct expansion_card *ec, int index)
|
static inline void icside_register (struct expansion_card *ec, int index)
|
{
|
{
|
unsigned long port = 0, latch;
|
unsigned long port = 0, latch;
|
|
|
result[index][0] = -1;
|
result[index][0] = -1;
|
result[index][1] = -1;
|
result[index][1] = -1;
|
|
|
switch (icside_identifyif (ec)) {
|
switch (icside_identifyif (ec)) {
|
case ics_if_unknown:
|
case ics_if_unknown:
|
default:
|
default:
|
printk ("icside: *** Warning: ICS IDE Interface unrecognised! ***\n");
|
printk ("icside: *** Warning: ICS IDE Interface unrecognised! ***\n");
|
break;
|
break;
|
|
|
case ics_if_arcin_v5:
|
case ics_if_arcin_v5:
|
port = ecard_address (ec, ECARD_MEMC, 0);
|
port = ecard_address (ec, ECARD_MEMC, 0);
|
ec->irqaddr = (unsigned char *)ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
|
ec->irqaddr = (unsigned char *)ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
|
ec->irqmask = 1;
|
ec->irqmask = 1;
|
ec->irq_data = (void *)port;
|
ec->irq_data = (void *)port;
|
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
|
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
|
|
|
/*
|
/*
|
* Be on the safe side - disable interrupts
|
* Be on the safe side - disable interrupts
|
*/
|
*/
|
inb (port + ICS_ARCIN_V5_INTROFFSET);
|
inb (port + ICS_ARCIN_V5_INTROFFSET);
|
result[index][0] =
|
result[index][0] =
|
ide_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
|
ide_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
|
port + ICS_ARCIN_V5_IDEALTOFFSET,
|
port + ICS_ARCIN_V5_IDEALTOFFSET,
|
ICS_ARCIN_V5_IDESTEPPING,
|
ICS_ARCIN_V5_IDESTEPPING,
|
ec->irq);
|
ec->irq);
|
break;
|
break;
|
|
|
case ics_if_arcin_v6:
|
case ics_if_arcin_v6:
|
latch = ecard_address (ec, ECARD_IOC, ECARD_FAST);
|
latch = ecard_address (ec, ECARD_IOC, ECARD_FAST);
|
if (ec->dma == NO_DMA)
|
if (ec->dma == NO_DMA)
|
port = ecard_address (ec, ECARD_EASI, ECARD_FAST);
|
port = ecard_address (ec, ECARD_EASI, ECARD_FAST);
|
if (port)
|
if (port)
|
outb(0x20, latch);
|
outb(0x20, latch);
|
else {
|
else {
|
port = latch;
|
port = latch;
|
outb(0, port);
|
outb(0, port);
|
}
|
}
|
ec->irq_data = (void *)port;
|
ec->irq_data = (void *)port;
|
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
|
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
|
|
|
/*
|
/*
|
* Be on the safe side - disable interrupts
|
* Be on the safe side - disable interrupts
|
*/
|
*/
|
inb (port + ICS_ARCIN_V6_INTROFFSET_1);
|
inb (port + ICS_ARCIN_V6_INTROFFSET_1);
|
inb (port + ICS_ARCIN_V6_INTROFFSET_2);
|
inb (port + ICS_ARCIN_V6_INTROFFSET_2);
|
result[index][0] =
|
result[index][0] =
|
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
|
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
|
port + ICS_ARCIN_V6_IDEALTOFFSET_1,
|
port + ICS_ARCIN_V6_IDEALTOFFSET_1,
|
ICS_ARCIN_V6_IDESTEPPING,
|
ICS_ARCIN_V6_IDESTEPPING,
|
ec->irq);
|
ec->irq);
|
result[index][1] =
|
result[index][1] =
|
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
|
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
|
port + ICS_ARCIN_V6_IDEALTOFFSET_2,
|
port + ICS_ARCIN_V6_IDEALTOFFSET_2,
|
ICS_ARCIN_V6_IDESTEPPING,
|
ICS_ARCIN_V6_IDESTEPPING,
|
ec->irq);
|
ec->irq);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
int icside_init (void)
|
int icside_init (void)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < MAX_ECARDS; i++)
|
for (i = 0; i < MAX_ECARDS; i++)
|
ec[i] = NULL;
|
ec[i] = NULL;
|
|
|
ecard_startfind ();
|
ecard_startfind ();
|
|
|
for (i = 0; ; i++) {
|
for (i = 0; ; i++) {
|
if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
|
if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
|
break;
|
break;
|
|
|
ecard_claim (ec[i]);
|
ecard_claim (ec[i]);
|
icside_register (ec[i], i);
|
icside_register (ec[i], i);
|
}
|
}
|
|
|
for (i = 0; i < MAX_ECARDS; i++)
|
for (i = 0; i < MAX_ECARDS; i++)
|
if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
|
if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
|
ecard_release (ec[i]);
|
ecard_release (ec[i]);
|
ec[i] = NULL;
|
ec[i] = NULL;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#ifdef MODULE
|
#ifdef MODULE
|
int init_module (void)
|
int init_module (void)
|
{
|
{
|
return icside_init();
|
return icside_init();
|
}
|
}
|
|
|
void cleanup_module (void)
|
void cleanup_module (void)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < MAX_ECARDS; i++)
|
for (i = 0; i < MAX_ECARDS; i++)
|
if (ec[i]) {
|
if (ec[i]) {
|
if (result[i][0] >= 0)
|
if (result[i][0] >= 0)
|
ide_unregister (result[i][0]);
|
ide_unregister (result[i][0]);
|
|
|
if (result[i][1] >= 0)
|
if (result[i][1] >= 0)
|
ide_unregister (result[i][1]);
|
ide_unregister (result[i][1]);
|
|
|
ecard_release (ec[i]);
|
ecard_release (ec[i]);
|
ec[i] = NULL;
|
ec[i] = NULL;
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
|
|
|
|