44c10138fd
Instead of all drivers reading pci config space to get the revision ID, they can now use the pci_device->revision member. This exposes some issues where drivers where reading a word or a dword for the revision number, and adding useless error-handling around the read. Some drivers even just read it for no purpose of all. In devices where the revision ID is being copied over and used in what appears to be the equivalent of hotpath, I have left the copy code and the cached copy as not to influence the driver's performance. Compile tested with make all{yes,mod}config on x86_64 and i386. Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com> Acked-by: Dave Jones <davej@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
449 lines
12 KiB
C
449 lines
12 KiB
C
/* $Id: bkm_a8.c,v 1.22.2.4 2004/01/15 14:02:34 keil Exp $
|
|
*
|
|
* low level stuff for Scitel Quadro (4*S0, passive)
|
|
*
|
|
* Author Roland Klabunde
|
|
* Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
|
|
*
|
|
* This software may be used and distributed according to the terms
|
|
* of the GNU General Public License, incorporated herein by reference.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <linux/init.h>
|
|
#include "hisax.h"
|
|
#include "isac.h"
|
|
#include "ipac.h"
|
|
#include "hscx.h"
|
|
#include "isdnl1.h"
|
|
#include <linux/pci.h>
|
|
#include "bkm_ax.h"
|
|
|
|
#ifdef CONFIG_PCI
|
|
|
|
#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */
|
|
|
|
extern const char *CardType[];
|
|
|
|
static const char sct_quadro_revision[] = "$Revision: 1.22.2.4 $";
|
|
|
|
static const char *sct_quadro_subtypes[] =
|
|
{
|
|
"",
|
|
"#1",
|
|
"#2",
|
|
"#3",
|
|
"#4"
|
|
};
|
|
|
|
|
|
#define wordout(addr,val) outw(val,addr)
|
|
#define wordin(addr) inw(addr)
|
|
|
|
static inline u_char
|
|
readreg(unsigned int ale, unsigned int adr, u_char off)
|
|
{
|
|
register u_char ret;
|
|
wordout(ale, off);
|
|
ret = wordin(adr) & 0xFF;
|
|
return (ret);
|
|
}
|
|
|
|
static inline void
|
|
readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
|
|
{
|
|
int i;
|
|
wordout(ale, off);
|
|
for (i = 0; i < size; i++)
|
|
data[i] = wordin(adr) & 0xFF;
|
|
}
|
|
|
|
|
|
static inline void
|
|
writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
|
|
{
|
|
wordout(ale, off);
|
|
wordout(adr, data);
|
|
}
|
|
|
|
static inline void
|
|
writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
|
|
{
|
|
int i;
|
|
wordout(ale, off);
|
|
for (i = 0; i < size; i++)
|
|
wordout(adr, data[i]);
|
|
}
|
|
|
|
/* Interface functions */
|
|
|
|
static u_char
|
|
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
|
{
|
|
return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80));
|
|
}
|
|
|
|
static void
|
|
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
|
{
|
|
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value);
|
|
}
|
|
|
|
static void
|
|
ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
|
|
{
|
|
readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
|
|
}
|
|
|
|
static void
|
|
WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
|
|
{
|
|
writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
|
|
}
|
|
|
|
|
|
static u_char
|
|
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
|
{
|
|
return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0)));
|
|
}
|
|
|
|
static void
|
|
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
|
{
|
|
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
|
|
}
|
|
|
|
/* Set the specific ipac to active */
|
|
static void
|
|
set_ipac_active(struct IsdnCardState *cs, u_int active)
|
|
{
|
|
/* set irq mask */
|
|
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
|
|
active ? 0xc0 : 0xff);
|
|
}
|
|
|
|
/*
|
|
* fast interrupt HSCX stuff goes here
|
|
*/
|
|
|
|
#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \
|
|
cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0))
|
|
#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \
|
|
cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data)
|
|
#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \
|
|
cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
|
|
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \
|
|
cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
|
|
|
|
#include "hscx_irq.c"
|
|
|
|
static irqreturn_t
|
|
bkm_interrupt_ipac(int intno, void *dev_id)
|
|
{
|
|
struct IsdnCardState *cs = dev_id;
|
|
u_char ista, val, icnt = 5;
|
|
u_long flags;
|
|
|
|
spin_lock_irqsave(&cs->lock, flags);
|
|
ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
|
|
if (!(ista & 0x3f)) { /* not this IPAC */
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
return IRQ_NONE;
|
|
}
|
|
Start_IPAC:
|
|
if (cs->debug & L1_DEB_IPAC)
|
|
debugl1(cs, "IPAC ISTA %02X", ista);
|
|
if (ista & 0x0f) {
|
|
val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40);
|
|
if (ista & 0x01)
|
|
val |= 0x01;
|
|
if (ista & 0x04)
|
|
val |= 0x02;
|
|
if (ista & 0x08)
|
|
val |= 0x04;
|
|
if (val) {
|
|
hscx_int_main(cs, val);
|
|
}
|
|
}
|
|
if (ista & 0x20) {
|
|
val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80);
|
|
if (val) {
|
|
isac_interrupt(cs, val);
|
|
}
|
|
}
|
|
if (ista & 0x10) {
|
|
val = 0x01;
|
|
isac_interrupt(cs, val);
|
|
}
|
|
ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
|
|
if ((ista & 0x3f) && icnt) {
|
|
icnt--;
|
|
goto Start_IPAC;
|
|
}
|
|
if (!icnt)
|
|
printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n",
|
|
CardType[cs->typ],
|
|
sct_quadro_subtypes[cs->subtyp]);
|
|
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
|
|
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void
|
|
release_io_sct_quadro(struct IsdnCardState *cs)
|
|
{
|
|
release_region(cs->hw.ax.base & 0xffffffc0, 128);
|
|
if (cs->subtyp == SCT_1)
|
|
release_region(cs->hw.ax.plx_adr, 64);
|
|
}
|
|
|
|
static void
|
|
enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
|
|
{
|
|
if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
|
|
if (bEnable)
|
|
wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
|
|
else
|
|
wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
|
|
}
|
|
}
|
|
|
|
static void
|
|
reset_bkm(struct IsdnCardState *cs)
|
|
{
|
|
if (cs->subtyp == SCT_1) {
|
|
wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
|
|
mdelay(10);
|
|
/* Remove the soft reset */
|
|
wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
|
|
mdelay(10);
|
|
}
|
|
}
|
|
|
|
static int
|
|
BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
|
{
|
|
u_long flags;
|
|
|
|
switch (mt) {
|
|
case CARD_RESET:
|
|
spin_lock_irqsave(&cs->lock, flags);
|
|
/* Disable ints */
|
|
set_ipac_active(cs, 0);
|
|
enable_bkm_int(cs, 0);
|
|
reset_bkm(cs);
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
return (0);
|
|
case CARD_RELEASE:
|
|
/* Sanity */
|
|
spin_lock_irqsave(&cs->lock, flags);
|
|
set_ipac_active(cs, 0);
|
|
enable_bkm_int(cs, 0);
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
release_io_sct_quadro(cs);
|
|
return (0);
|
|
case CARD_INIT:
|
|
spin_lock_irqsave(&cs->lock, flags);
|
|
cs->debug |= L1_DEB_IPAC;
|
|
set_ipac_active(cs, 1);
|
|
inithscxisac(cs, 3);
|
|
/* Enable ints */
|
|
enable_bkm_int(cs, 1);
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
return (0);
|
|
case CARD_TEST:
|
|
return (0);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int __devinit
|
|
sct_alloc_io(u_int adr, u_int len)
|
|
{
|
|
if (!request_region(adr, len, "scitel")) {
|
|
printk(KERN_WARNING
|
|
"HiSax: Scitel port %#x-%#x already in use\n",
|
|
adr, adr + len);
|
|
return (1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static struct pci_dev *dev_a8 __devinitdata = NULL;
|
|
static u16 sub_vendor_id __devinitdata = 0;
|
|
static u16 sub_sys_id __devinitdata = 0;
|
|
static u_char pci_bus __devinitdata = 0;
|
|
static u_char pci_device_fn __devinitdata = 0;
|
|
static u_char pci_irq __devinitdata = 0;
|
|
|
|
#endif /* CONFIG_PCI */
|
|
|
|
int __devinit
|
|
setup_sct_quadro(struct IsdnCard *card)
|
|
{
|
|
#ifdef CONFIG_PCI
|
|
struct IsdnCardState *cs = card->cs;
|
|
char tmp[64];
|
|
u_int found = 0;
|
|
u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
|
|
|
|
strcpy(tmp, sct_quadro_revision);
|
|
printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
|
|
if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
|
|
cs->subtyp = SCT_1; /* Preset */
|
|
} else
|
|
return (0);
|
|
|
|
/* Identify subtype by para[0] */
|
|
if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
|
|
cs->subtyp = card->para[0];
|
|
else {
|
|
printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n",
|
|
CardType[card->typ]);
|
|
return (0);
|
|
}
|
|
if ((cs->subtyp != SCT_1) && ((sub_sys_id != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
|
|
(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
|
|
return (0);
|
|
if (cs->subtyp == SCT_1) {
|
|
while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
|
|
PCI_DEVICE_ID_PLX_9050, dev_a8))) {
|
|
|
|
sub_vendor_id = dev_a8->subsystem_vendor;
|
|
sub_sys_id = dev_a8->subsystem_device;
|
|
if ((sub_sys_id == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
|
|
(sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
|
|
if (pci_enable_device(dev_a8))
|
|
return(0);
|
|
pci_ioaddr1 = pci_resource_start(dev_a8, 1);
|
|
pci_irq = dev_a8->irq;
|
|
pci_bus = dev_a8->bus->number;
|
|
pci_device_fn = dev_a8->devfn;
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
printk(KERN_WARNING "HiSax: %s (%s): Card not found\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp]);
|
|
return (0);
|
|
}
|
|
#ifdef ATTEMPT_PCI_REMAPPING
|
|
/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
|
|
if ((pci_ioaddr1 & 0x80) && (dev_a8->revision == 1)) {
|
|
printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp]);
|
|
/* Restart PCI negotiation */
|
|
pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, (u_int) - 1);
|
|
/* Move up by 0x80 byte */
|
|
pci_ioaddr1 += 0x80;
|
|
pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, pci_ioaddr1);
|
|
dev_a8->resource[ 1].start = pci_ioaddr1;
|
|
}
|
|
#endif /* End HACK */
|
|
}
|
|
if (!pci_irq) { /* IRQ range check ?? */
|
|
printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp]);
|
|
return (0);
|
|
}
|
|
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
|
|
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
|
|
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
|
|
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_4, &pci_ioaddr4);
|
|
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_5, &pci_ioaddr5);
|
|
if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) {
|
|
printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp]);
|
|
return (0);
|
|
}
|
|
pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK;
|
|
/* Take over */
|
|
cs->irq = pci_irq;
|
|
cs->irq_flags |= IRQF_SHARED;
|
|
/* pci_ioaddr1 is unique to all subdevices */
|
|
/* pci_ioaddr2 is for the fourth subdevice only */
|
|
/* pci_ioaddr3 is for the third subdevice only */
|
|
/* pci_ioaddr4 is for the second subdevice only */
|
|
/* pci_ioaddr5 is for the first subdevice only */
|
|
cs->hw.ax.plx_adr = pci_ioaddr1;
|
|
/* Enter all ipac_base addresses */
|
|
switch(cs->subtyp) {
|
|
case 1:
|
|
cs->hw.ax.base = pci_ioaddr5 + 0x00;
|
|
if (sct_alloc_io(pci_ioaddr1, 128))
|
|
return(0);
|
|
if (sct_alloc_io(pci_ioaddr5, 64))
|
|
return(0);
|
|
/* disable all IPAC */
|
|
writereg(pci_ioaddr5, pci_ioaddr5 + 4,
|
|
IPAC_MASK, 0xFF);
|
|
writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
|
|
IPAC_MASK, 0xFF);
|
|
writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
|
|
IPAC_MASK, 0xFF);
|
|
writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
|
|
IPAC_MASK, 0xFF);
|
|
break;
|
|
case 2:
|
|
cs->hw.ax.base = pci_ioaddr4 + 0x08;
|
|
if (sct_alloc_io(pci_ioaddr4, 64))
|
|
return(0);
|
|
break;
|
|
case 3:
|
|
cs->hw.ax.base = pci_ioaddr3 + 0x10;
|
|
if (sct_alloc_io(pci_ioaddr3, 64))
|
|
return(0);
|
|
break;
|
|
case 4:
|
|
cs->hw.ax.base = pci_ioaddr2 + 0x20;
|
|
if (sct_alloc_io(pci_ioaddr2, 64))
|
|
return(0);
|
|
break;
|
|
}
|
|
/* For isac and hscx data path */
|
|
cs->hw.ax.data_adr = cs->hw.ax.base + 4;
|
|
|
|
printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4lX, 0x%.4lX, 0x%.4lX and IRQ %d\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp],
|
|
cs->hw.ax.plx_adr,
|
|
cs->hw.ax.base,
|
|
cs->hw.ax.data_adr,
|
|
cs->irq);
|
|
|
|
test_and_set_bit(HW_IPAC, &cs->HW_Flags);
|
|
|
|
cs->readisac = &ReadISAC;
|
|
cs->writeisac = &WriteISAC;
|
|
cs->readisacfifo = &ReadISACfifo;
|
|
cs->writeisacfifo = &WriteISACfifo;
|
|
|
|
cs->BC_Read_Reg = &ReadHSCX;
|
|
cs->BC_Write_Reg = &WriteHSCX;
|
|
cs->BC_Send_Data = &hscx_fill_fifo;
|
|
cs->cardmsg = &BKM_card_msg;
|
|
cs->irq_func = &bkm_interrupt_ipac;
|
|
|
|
printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n",
|
|
CardType[card->typ],
|
|
sct_quadro_subtypes[cs->subtyp],
|
|
readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
|
|
return (1);
|
|
#else
|
|
printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n");
|
|
#endif /* CONFIG_PCI */
|
|
}
|