patch-2.3.4 linux/drivers/isdn/eicon/eicon_pci.c
Next file: linux/drivers/isdn/eicon/eicon_pci.h
Previous file: linux/drivers/isdn/eicon/eicon_mod.c
Back to the patch index
Back to the overall index
- Lines: 953
- Date:
Wed May 26 16:55:40 1999
- Orig file:
v2.3.3/linux/drivers/isdn/eicon/eicon_pci.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_pci.c linux/drivers/isdn/eicon/eicon_pci.c
@@ -0,0 +1,952 @@
+/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for PCI cards.
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * Deutsche Telekom AG for S2M support.
+ *
+ * This program 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 Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_pci.c,v $
+ * Revision 1.6 1999/04/01 12:48:37 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.5 1999/03/29 11:19:49 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4 1999/03/02 12:37:48 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3 1999/01/24 20:14:24 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2 1999/01/10 18:46:06 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1 1999/01/01 18:09:45 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/pci.h>
+
+#include "eicon.h"
+#include "eicon_pci.h"
+
+
+char *eicon_pci_revision = "$Revision: 1.6 $";
+
+#if CONFIG_PCI /* intire stuff is only for PCI */
+
+#undef EICON_PCI_DEBUG
+
+int eicon_pci_find_card(char *ID)
+{
+ if (pci_present()) {
+ struct pci_dev *pdev = NULL;
+ int pci_nextindex=0, pci_cards=0, pci_akt=0;
+ int pci_type = PCI_MAESTRA;
+ int NoMorePCICards = FALSE;
+ char *ram, *reg, *cfg;
+ unsigned int pram=0, preg=0, pcfg=0;
+ char did[12];
+ eicon_pci_card *aparms;
+
+ if (!(aparms = (eicon_pci_card *) kmalloc(sizeof(eicon_pci_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon_pci: Could not allocate card-struct.\n");
+ return 0;
+ }
+
+ for (pci_cards = 0; pci_cards < 0x0f; pci_cards++)
+ {
+ do {
+ if ((pdev = pci_find_device(PCI_VENDOR_EICON,
+ pci_type,
+ pdev)))
+ {
+ pci_nextindex++;
+ break;
+ }
+ else {
+ pci_nextindex = 0;
+ switch (pci_type) /* switch to next card type */
+ {
+ case PCI_MAESTRA:
+ pci_type = PCI_MAESTRAQ; break;
+ case PCI_MAESTRAQ:
+ pci_type = PCI_MAESTRAQ_U; break;
+ case PCI_MAESTRAQ_U:
+ pci_type = PCI_MAESTRAP; break;
+ default:
+ case PCI_MAESTRAP:
+ NoMorePCICards = TRUE;
+ }
+ }
+ }
+ while (!NoMorePCICards);
+ if (NoMorePCICards)
+ {
+ if (pci_cards < 1) {
+ printk(KERN_INFO "Eicon: No supported PCI cards found.\n");
+ kfree(aparms);
+ return 0;
+ }
+ else
+ {
+ printk(KERN_INFO "Eicon: %d PCI card%s registered.\n",
+ pci_cards, (pci_cards > 1) ? "s":"");
+ kfree(aparms);
+ return (pci_cards);
+ }
+ }
+
+ pci_akt = 0;
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ printk(KERN_INFO "Eicon: DIVA Server BRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRA;
+
+ aparms->irq = pdev->irq;
+ preg = pdev->base_address[2] & 0xfffffffc;
+ pcfg = pdev->base_address[1] & 0xffffff80;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n", preg);
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n", pcfg);
+#endif
+ pci_akt = 1;
+ break;
+
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ printk(KERN_ERR "Eicon: DIVA Server 4BRI/PCI detected but not supported !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+
+ case PCI_MAESTRAP:
+ printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/
+ aparms->irq = pdev->irq;
+ pram = pdev->base_address[0] & 0xfffff000;
+ preg = pdev->base_address[2] & 0xfffff000;
+ pcfg = pdev->base_address[4] & 0xfffff000;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: ram=0x%x\n",
+ (pram));
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n",
+ (preg));
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n",
+ (pcfg));
+#endif
+ pci_akt = 1;
+ break;
+ default:
+ printk(KERN_ERR "eicon_pci: Unknown PCI card detected !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+ }
+
+ if (pci_akt) {
+ /* remapping memory */
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ aparms->PCIreg = (unsigned int) preg;
+ aparms->PCIcfg = (unsigned int) pcfg;
+ if (check_region((aparms->PCIreg), 0x20)) {
+ printk(KERN_WARNING "eicon_pci: reg port already in use !\n");
+ aparms->PCIreg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIreg, 0x20, "eicon reg");
+ }
+ if (check_region((aparms->PCIcfg), 0x100)) {
+ printk(KERN_WARNING "eicon_pci: cfg port already in use !\n");
+ aparms->PCIcfg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIcfg, 0x100, "eicon cfg");
+ }
+ break;
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ case PCI_MAESTRAP:
+ aparms->shmem = (eicon_pci_shmem *) ioremap(pram, 0x10000);
+ ram = (u8 *) ((u32)aparms->shmem + MP_SHARED_RAM_OFFSET);
+ reg = ioremap(preg, 0x4000);
+ cfg = ioremap(pcfg, 0x1000);
+ aparms->PCIram = (unsigned int) ram;
+ aparms->PCIreg = (unsigned int) reg;
+ aparms->PCIcfg = (unsigned int) cfg;
+ break;
+ }
+ if ((!aparms->PCIreg) || (!aparms->PCIcfg)) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ } else {
+ aparms->mvalid = 1;
+
+ sprintf(did, "%s%d", (strlen(ID) < 1) ? "eicon":ID, pci_cards);
+
+ printk(KERN_INFO "%s: DriverID: '%s'\n",eicon_ctype_name[aparms->type] , did);
+
+ if (!(eicon_addcard(aparms->type, (int) aparms, aparms->irq, did))) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ }
+ }
+ }
+
+ }
+ } else
+ printk(KERN_ERR "eicon_pci: Kernel compiled with PCI but no PCI-bios found !\n");
+ return 0;
+}
+
+/*
+ * Checks protocol file id for "F#xxxx" string fragment to
+ * extract the features, supported by this protocol version.
+ * binary representation of the feature string value is returned
+ * in *value. The function returns 0 if feature string was not
+ * found or has a wrong format, else 1.
+ */
+static int GetProtFeatureValue(char *sw_id, int *value)
+{
+ __u8 i, offset;
+
+ while (*sw_id)
+ {
+ if ((sw_id[0] == 'F') && (sw_id[1] == '#'))
+ {
+ sw_id = &sw_id[2];
+ for (i=0, *value=0; i<4; i++, sw_id++)
+ {
+ if ((*sw_id >= '0') && (*sw_id <= '9'))
+ {
+ offset = '0';
+ }
+ else if ((*sw_id >= 'A') && (*sw_id <= 'F'))
+ {
+ offset = 'A' + 10;
+ }
+ else if ((*sw_id >= 'a') && (*sw_id <= 'f'))
+ {
+ offset = 'a' + 10;
+ }
+ else
+ {
+ return 0;
+ }
+ *value |= (*sw_id - offset) << (4*(3-i));
+ }
+ return 1;
+ }
+ else
+ {
+ sw_id++;
+ }
+ }
+ return 0;
+}
+
+
+void
+eicon_pci_printpar(eicon_pci_card *card) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ printk(KERN_INFO "%s at 0x%x / 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->PCIreg,
+ (unsigned int)card->PCIcfg,
+ card->irq);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ printk(KERN_INFO "%s at 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->shmem,
+ card->irq);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_INFO "eicon_pci: remapped ram= 0x%x\n",(unsigned int)card->PCIram);
+ printk(KERN_INFO "eicon_pci: remapped reg= 0x%x\n",(unsigned int)card->PCIreg);
+ printk(KERN_INFO "eicon_pci: remapped cfg= 0x%x\n",(unsigned int)card->PCIcfg);
+#endif
+ break;
+ }
+}
+
+
+static void
+eicon_pci_release_shmem(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->mvalid) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ /* reset board */
+ outb(0, card->PCIcfg + 0x4c); /* disable interrupts from PLX */
+ outb(0, card->PCIreg + M_RESET);
+ SLEEP(20);
+ outb(0, card->PCIreg + M_ADDRH);
+ outw(0, card->PCIreg + M_ADDR);
+ outw(0, card->PCIreg + M_DATA);
+
+ release_region(card->PCIreg, 0x20);
+ release_region(card->PCIcfg, 0x100);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ iounmap((void *)card->shmem);
+ iounmap((void *)card->PCIreg);
+ iounmap((void *)card->PCIcfg);
+ break;
+ }
+ }
+ card->mvalid = 0;
+}
+
+static void
+eicon_pci_release_irq(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->ivalid)
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+}
+
+void
+eicon_pci_release(eicon_pci_card *card) {
+ eicon_pci_release_irq(card);
+ eicon_pci_release_shmem(card);
+}
+
+/*
+ * Upload buffer content to adapters shared memory
+ * on verify error, 1 is returned and a message is printed on screen
+ * else 0 is returned
+ * Can serve IO-Type and Memory type adapters
+ */
+int eicon_upload(t_dsp_download_space *p_para,
+ __u16 length, /* byte count */
+ __u8 *buffer,
+ int verify)
+{
+ __u32 i, dwdata = 0, val = 0, timeout;
+ __u16 data;
+ eicon_pci_boot *boot = 0;
+
+ switch (p_para->type) /* actions depend on type of union */
+ {
+ case DL_PARA_IO_TYPE:
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ /* outw (((u16 *)code)[i >> 1], p_para->dat.io.ioDATA); */
+ outw (*(u16 *)&buffer[i], p_para->dat.io.ioDATA);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ data = inw(p_para->dat.io.ioDATA);
+ if (data != *(u16 *)&buffer[i])
+ {
+ p_para->dat.io.r3addr += i;
+ p_para->dat.io.BadData = data;
+ p_para->dat.io.GoodData = *(u16 *)&buffer[i];
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case DL_PARA_MEM_TYPE:
+ boot = p_para->dat.mem.boot;
+ writel(p_para->dat.mem.r3addr, &boot->addr);
+ for (i=0; i<length; i+=4)
+ {
+ writel(((u32 *)buffer)[i >> 2], &boot->data[i]);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=4)
+ {
+ dwdata = readl(&boot->data[i]);
+ if (((u32 *)buffer)[i >> 2] != dwdata)
+ {
+ p_para->dat.mem.r3addr += i;
+ p_para->dat.mem.BadData = dwdata;
+ p_para->dat.mem.GoodData = ((u32 *)buffer)[i >> 2];
+ return 1;
+ }
+ }
+ }
+ writel(((length + 3) / 4), &boot->len); /* len in dwords */
+ writel(2, &boot->cmd);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ val = readl(&boot->cmd);
+ if (!val) break;
+ SLEEP(2);
+ }
+ if (val)
+ {
+ p_para->dat.mem.timeout = 1;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/* show header information of code file */
+static
+int eicon_pci_print_hdr(unsigned char *code, int offset)
+{
+ unsigned char hdr[80];
+ int i, fvalue = 0;
+
+ i = 0;
+ while ((i < (sizeof(hdr) -1))
+ && (code[offset + i] != '\0')
+ && (code[offset + i] != '\r')
+ && (code[offset + i] != '\n'))
+ {
+ hdr[i] = code[offset + i];
+ i++;
+ }
+ hdr[i] = '\0';
+ printk(KERN_DEBUG "Eicon: loading %s\n", hdr);
+ if (GetProtFeatureValue(hdr, &fvalue)) return(fvalue);
+ else return(0);
+}
+
+
+/*
+ * Configure a card, download code into BRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ int i,j;
+ int timeout;
+ unsigned int offset, offp=0, size, length;
+ int signature = 0;
+ int FeatureValue = 0;
+ eicon_pci_codebuf cbuf;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ unsigned char *code;
+ unsigned int reg;
+ unsigned int cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ reg = card->PCIreg;
+ cfg = card->PCIcfg;
+
+ /* reset board */
+ outb(0, reg + M_RESET);
+ SLEEP(10);
+ outb(0, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card\n");
+#endif
+
+ /* clear shared memory */
+ outb(0xff, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ for(i = 0; i < 0xffff; i++) outw(0, reg + M_DATA);
+ SLEEP(10);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: clear shared memory\n");
+#endif
+
+ /* download protocol and dsp file */
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_IO_TYPE;
+ dl_para.dat.io.ioADDR = reg + M_ADDR;
+ dl_para.dat.io.ioADDRH = reg + M_ADDRH;
+ dl_para.dat.io.ioDATA = reg + M_DATA;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j == 0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ offset = 0;
+
+ if (j == 0) dl_para.dat.io.r3addr = 0;
+ if (j == 1) dl_para.dat.io.r3addr = M_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = M_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = M_DSP_CODE_BASE + sizeof(__u32);
+
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%04x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ kfree(code);
+ return -EIO;
+ }
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.io.r3addr += length;
+ } while (offset < size);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "Eicon: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* clear signature */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x1e, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* copy configuration data into shared memory */
+ outw(8, reg + M_ADDR); outb(cbuf.tei, reg + M_DATA);
+ outw(9, reg + M_ADDR); outb(cbuf.nt2, reg + M_DATA);
+ outw(10,reg + M_ADDR); outb(0, reg + M_DATA);
+ outw(11,reg + M_ADDR); outb(cbuf.WatchDog, reg + M_DATA);
+ outw(12,reg + M_ADDR); outb(cbuf.Permanent, reg + M_DATA);
+ outw(13,reg + M_ADDR); outb(0, reg + M_DATA); /* XInterface */
+ outw(14,reg + M_ADDR); outb(cbuf.StableL2, reg + M_DATA);
+ outw(15,reg + M_ADDR); outb(cbuf.NoOrderCheck, reg + M_DATA);
+ outw(16,reg + M_ADDR); outb(0, reg + M_DATA); /* HandsetType */
+ outw(17,reg + M_ADDR); outb(0, reg + M_DATA); /* SigFlags */
+ outw(18,reg + M_ADDR); outb(cbuf.LowChannel, reg + M_DATA);
+ outw(19,reg + M_ADDR); outb(cbuf.ProtVersion, reg + M_DATA);
+ outw(20,reg + M_ADDR); outb(cbuf.Crc4, reg + M_DATA);
+ outw(21,reg + M_ADDR); outb((cbuf.Loopback) ? 2:0, reg + M_DATA);
+
+ for (i=0;i<32;i++)
+ {
+ outw( 32+i, reg + M_ADDR); outb(cbuf.l[0].oad[i], reg + M_DATA);
+ outw( 64+i, reg + M_ADDR); outb(cbuf.l[0].osa[i], reg + M_DATA);
+ outw( 96+i, reg + M_ADDR); outb(cbuf.l[0].spid[i], reg + M_DATA);
+ outw(128+i, reg + M_ADDR); outb(cbuf.l[1].oad[i], reg + M_DATA);
+ outw(160+i, reg + M_ADDR); outb(cbuf.l[1].osa[i], reg + M_DATA);
+ outw(192+i, reg + M_ADDR); outb(cbuf.l[1].spid[i], reg + M_DATA);
+ }
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: starting CPU...\n");
+#endif
+ /* let the CPU run */
+ outw(0x08, reg + M_RESET);
+
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ outw(0x1e, reg + M_ADDR);
+ signature = inw(reg + M_DATA);
+ if (signature == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if (signature != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%x expected 0x%x\n",signature,DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: Timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x3f6, reg + M_ADDR);
+ card->channels = inw(reg + M_DATA);
+ card->serial = (u32)inw(cfg + 0x22) << 16 | (u32)inw(cfg + 0x26);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ /* Trigger an interrupt and check if it is delivered */
+ outb(0x41, cfg + 0x4c); /* enable PLX for interrupts */
+ outb(0x89, reg + M_RESET); /* place int request */
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(5);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+
+/*
+ * Configure a card, download code into PRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ eicon_pci_boot *boot;
+ eicon_pr_ram *prram;
+ int i,j;
+ int timeout;
+ int FeatureValue = 0;
+ unsigned int offset, offp=0, size, length;
+ unsigned long int signature = 0;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ eicon_pci_codebuf cbuf;
+ unsigned char *code;
+ unsigned char req_int;
+ char *ram, *reg, *cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ boot = &card->shmem->boot;
+ ram = (char *)card->PCIram;
+ reg = (char *)card->PCIreg;
+ cfg = (char *)card->PCIcfg;
+ prram = (eicon_pr_ram *)ram;
+
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ /* set command count to 0 */
+ writel(0, &boot->reserved);
+
+ /* check if CPU increments the life word */
+ i = readw(&boot->live);
+ SLEEP(20);
+ if (i == readw(&boot->live)) {
+ printk(KERN_ERR "eicon_pci: card is reset, but CPU not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card OK (CPU running)\n");
+#endif
+
+ /* download firmware : DSP and Protocol */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_MEM_TYPE;
+ dl_para.dat.mem.boot = boot;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j==0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ if (j==1) writel(MP_DSP_ADDR, &boot->addr); /* DSP code entry point */
+
+ if (j == 0) dl_para.dat.io.r3addr = MP_PROTOCOL_ADDR;
+ if (j == 1) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE + sizeof(__u32);
+
+ offset = 0;
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ if (dl_para.dat.mem.timeout == 0)
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ else
+ printk(KERN_ERR "eicon_pci: timeout, no ACK to load !\n");
+ kfree(code);
+ return -EIO;
+ }
+
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.mem.r3addr += length;
+ } while (offset < size);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* initialize the adapter data structure */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* clear out config space */
+ for (i = 0; i < 256; i++) writeb(0, &ram[i]);
+
+ /* copy configuration down to the card */
+ writeb(cbuf.tei, &ram[8]);
+ writeb(cbuf.nt2, &ram[9]);
+ writeb(0, &ram[10]);
+ writeb(cbuf.WatchDog, &ram[11]);
+ writeb(cbuf.Permanent, &ram[12]);
+ writeb(cbuf.XInterface, &ram[13]);
+ writeb(cbuf.StableL2, &ram[14]);
+ writeb(cbuf.NoOrderCheck, &ram[15]);
+ writeb(cbuf.HandsetType, &ram[16]);
+ writeb(0, &ram[17]);
+ writeb(cbuf.LowChannel, &ram[18]);
+ writeb(cbuf.ProtVersion, &ram[19]);
+ writeb(cbuf.Crc4, &ram[20]);
+ for (i = 0; i < 32; i++)
+ {
+ writeb(cbuf.l[0].oad[i], &ram[32 + i]);
+ writeb(cbuf.l[0].osa[i], &ram[64 + i]);
+ writeb(cbuf.l[0].spid[i], &ram[96 + i]);
+ writeb(cbuf.l[1].oad[i], &ram[128 + i]);
+ writeb(cbuf.l[1].osa[i], &ram[160 + i]);
+ writeb(cbuf.l[1].spid[i], &ram[192 + i]);
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: configured card OK\n");
+#endif
+
+ /* start adapter */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: tell card to start...\n");
+#endif
+ writel(MP_PROTOCOL_ADDR, &boot->addr); /* RISC code entry point */
+ writel(3, &boot->cmd); /* DIVAS_START_CMD */
+
+ /* wait till card ACKs */
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ signature = readl(&boot->signature);
+ if ((signature >> 16) == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if ((signature >> 16) != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%lx expected 0x%x\n",(signature >> 16),DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ card->channels = readb(&ram[0x3f6]);
+ card->serial = readl(&ram[0x3f0]);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ readb(&ram[0x3fe]);
+ writeb(0, &ram[0x3fe]); /* reset any pending interrupt */
+ readb(&ram[0x3fe]);
+
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+ req_int = readb(&prram->ReadyInt);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ req_int++;
+ /* Trigger an interrupt and check if it is delivered */
+ writeb(req_int, &prram->ReadyInt);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(2);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PCI */
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)