patch-2.0.36 linux/drivers/char/specialix.c

Next file: linux/drivers/char/specialix_io8.h
Previous file: linux/drivers/char/random.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.35/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
@@ -56,10 +56,14 @@
  *                Changed the specialix=... format to include interrupt.
  * Revision 1.7:  May 27 1997
  *                Made many more debug printk's a compile time option.
+ * Revision 1.8:  Port to 2.1 kernel. -- Not here.
+ *
+ * Revision 1.9:  October 1 1998
+ *                Support for the PCI version of the card.
  * 
  */
 
-#define VERSION "1.7"
+#define VERSION "1.9"
 
 
 /*
@@ -84,7 +88,8 @@
 #include <linux/delay.h>
 #include <linux/tqueue.h>
 #include <linux/version.h>
-
+#include <linux/pci.h>
+#include <linux/bios32.h>
 
 /* ************************************************************** */
 /* * This section can be removed when 2.0 becomes outdated....  * */
@@ -170,7 +175,6 @@
 #define SPECIALIX_TYPE_NORMAL	1
 #define SPECIALIX_TYPE_CALLOUT	2
 
-static struct specialix_board * IRQ_to_board[16] = { NULL, } ;
 static struct tty_driver specialix_driver, specialix_callout_driver;
 static int    specialix_refcount = 0;
 static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, };
@@ -322,23 +326,28 @@
 
 extern inline int sx_check_io_range(struct specialix_board * bp)
 {
-	return check_region (bp->base, SX_IO_SPACE);
+	return check_region (bp->base, 
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
 }
 
 
 extern inline void sx_request_io_range(struct specialix_board * bp)
 {
-	request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+	request_region(bp->base,
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE, 
+	               "specialix IO8+" );
 }
 
 
 extern inline void sx_release_io_range(struct specialix_board * bp)
 {
-	release_region(bp->base, SX_IO_SPACE);
+	release_region(bp->base,
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
 }
 
 	
 /* Must be called with enabled interrupts */
+/* Ugly. Don't use this for anything else than initialization code */
 extern inline void sx_long_delay(unsigned long delay)
 {
 	unsigned long i;
@@ -354,6 +363,8 @@
 	int virq;
 	int i;
 
+	if (bp->flags & SX_BOARD_IS_PCI) 
+		return 1;
 	switch (bp->irq) {
 	/* In the same order as in the docs... */
 	case 15: virq = 0;break;
@@ -481,9 +492,14 @@
 	printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", 
 	        board_No(bp),  val1, val2);
 #endif
-	if (val1 != 0xb2) {
-		printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n",
-		       board_No(bp), bp->base);
+	/* They managed to switch the bit order between the docs and
+	   the IO8+ card. The new PCI card now conforms to old docs.
+	   They changed the PCI docs to reflect the situation on the
+	   old card. */
+	val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
+	if (val1 != val2) {
+		printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
+		       board_No(bp), val2, bp->base, val1);
 		return 1;
 	}
 
@@ -865,7 +881,7 @@
 	unsigned long loop = 0;
 	int saved_reg;
 
-	bp = IRQ_to_board[irq];
+	bp = dev_id;
 	
 	if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
 #ifdef SPECIALIX_DEBUG 
@@ -921,6 +937,25 @@
  *  Routines for open & close processing.
  */
 
+void turn_ints_off (struct specialix_board *bp)
+{
+	if (bp->flags & SX_BOARD_IS_PCI) {
+		/* This was intended for enabeling the interrupt on the
+		 * PCI card. However it seems that it's already enabled
+		 * and as PCI interrupts can be shared, there is no real
+		 * reason to have to turn it off. */
+	}
+	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+}
+
+void turn_ints_on (struct specialix_board *bp)
+{
+	if (bp->flags & SX_BOARD_IS_PCI) {
+		/* play with the PCI chip. See comment above */
+	}
+	(void) sx_in (bp, 0); /* Turn ON interrupts. */
+}
+
 
 /* Called with disabled interrupts */
 extern inline int sx_setup_board(struct specialix_board * bp)
@@ -930,14 +965,12 @@
 	if (bp->flags & SX_BOARD_ACTIVE) 
 		return 0;
 
-	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);
 
 	if (error) 
 		return error;
 
-	IRQ_to_board[bp->irq] = bp;
-	(void) sx_in (bp, 0); /* Turn ON interrupts. */
-
+	turn_ints_on (bp);
 	bp->flags |= SX_BOARD_ACTIVE;
 
 	MOD_INC_USE_COUNT;
@@ -953,11 +986,13 @@
 	
 	bp->flags &= ~SX_BOARD_ACTIVE;
 	
-	free_irq(bp->irq, NULL);
-	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+#if SPECIALIX_DEBUG > 2
+	printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp));
+#endif
+	free_irq(bp->irq, bp);
+
+	turn_ints_off (bp);
 
-	IRQ_to_board[bp->irq] = NULL;
-	
 	MOD_DEC_USE_COUNT;
 }
 
@@ -1042,12 +1077,14 @@
 		/* Page 48 of version 2.0 of the CL-CD1865 databook */
 		if (tmp >= 12) {
 			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
-			        "Performance degradation is possible.\n",
+			        "Performance degradation is possible.\n"
+			        "Read specialix.txt for more info.\n",
 			        port_No (port), tmp);
 		} else {
 			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
 			        "Warning: overstressing Cirrus chip. "
-                                "This might not work.\n", 
+			        "This might not work.\n"
+			        "Read specialix.txt for more info.\n", 
 			        port_No (port), tmp);
 		}
 	}
@@ -2150,7 +2187,6 @@
 		return 1;
 	}
 	init_bh(SPECIALIX_BH, do_specialix_bh);
-	memset(IRQ_to_board, 0, sizeof(IRQ_to_board));
 	memset(&specialix_driver, 0, sizeof(specialix_driver));
 	specialix_driver.magic = TTY_DRIVER_MAGIC;
 	specialix_driver.name = "ttyW";
@@ -2240,7 +2276,7 @@
 void specialix_setup(char *str, int * ints)
 {
 	int i;
-        
+	
 	for (i=0;i<SX_NBOARD;i++) {
 		sx_board[i].base = 0;
 	}
@@ -2261,8 +2297,12 @@
 {
 	int i;
 	int found = 0;
+#ifdef CONFIG_PCI
+	int pci_index;
+	unsigned char pci_bus, pci_device_fn;
+#endif
 
-	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
 	printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
 #ifdef CONFIG_SPECIALIX_RTSCTS
 	printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
@@ -2277,6 +2317,36 @@
 		if (sx_board[i].base && !sx_probe(&sx_board[i]))
 			found++;
 
+#ifdef CONFIG_PCI
+	i=0;
+	pci_index = 0;
+	while ((i < SX_NBOARD) && (pci_index < 0xff)) {
+		unsigned char tbyte;
+		unsigned int tint;
+		if (sx_board[i].flags & SX_BOARD_PRESENT) {
+			i++;
+			continue;
+		}
+		if (pcibios_find_device (PCI_VENDOR_ID_SPECIALIX, 
+		                         PCI_DEVICE_ID_SPECIALIX_IO8,
+		                         pci_index, &pci_bus, &pci_device_fn)
+		    != PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+		                         PCI_INTERRUPT_LINE, &tbyte);
+		sx_board[i].irq = tbyte;
+
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+		                          PCI_BASE_ADDRESS_2, &tint);
+		/* Mask out the fact that it's IO-space */
+		sx_board[i].base = tint & PCI_BASE_ADDRESS_IO_MASK; 
+		sx_board[i].flags |= SX_BOARD_IS_PCI;
+		if (!sx_probe(&sx_board[i]))
+			found ++;
+		pci_index++;
+	}
+#endif
+
 	if (!found) {
 		sx_release_drivers();
 		printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
@@ -2293,7 +2363,8 @@
 
 /*
  * You can setup up to 4 boards.
- * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
+ * You should specify the IRQs too in that case "irq=....,...". 
  * 
  * More than 4 boards in one computer is not possible, as the card can
  * only use 4 different interrupts. 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov