#include #include #include #include #include #include #include #include #include static const char *sx_name = "Keyspan SX driver"; static const char *sx_sname = "keyspan_sx"; static const char *sx_version = "0.0"; #define KEYSPAN_SX_SUBSYSTEM_VENDOR 0x11a9 #define KEYSPAN_SX_SUBSYSTEM_DEVID 0x5334 #define KEYSPAN_SX_MAX_BOARDS 4 #define KEYSPAN_SX_MAX_UARTS 16 #define DEBUG 1 struct sx_uart { u8 *base; int type; }; static struct sx_uart sx_uart[KEYSPAN_SX_MAX_UARTS]; static struct pci_dev *sx_board[KEYSPAN_SX_MAX_BOARDS]; static void print_pci_details(struct pci_dev *dev) { printk("%s: irq=%u, addresses=[0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx]\n", sx_sname, dev->irq, dev->base_address[0], dev->base_address[1], dev->base_address[2], dev->base_address[3]); } static inline u8 serial_inp(struct sx_uart *info, u16 offset) { return readb(info->base + (offset<<7)); } static inline u8 serial_in(struct sx_uart *info, u16 offset) { return readb(info->base + (offset<<7)); } static inline void serial_outp(struct sx_uart *info, u16 offset, u8 data) { writeb(data, info->base + (offset<<7)); } static int probe_uart(u8 *baseptr) { unsigned long flags; u8 status1, status2, scratch, scratch2; struct sx_uart uart, *info = &uart; struct serial_state state_struct, *state = &state_struct; info->base = baseptr; save_flags(flags); cli(); #if 0 /* We KNOW there's something there, so switch off this test *OM**/ /* * Do a simple existence test first; if we fail this, there's * no point trying anything else. */ scratch = serial_inp(info, UART_IER); serial_outp(info, UART_IER, 0); outb(0xff, 0x080); scratch2 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, scratch); if (scratch2) { restore_flags(flags); return 0; } #endif /* * Check to see if a UART is really there. */ scratch = serial_inp(info, UART_MCR); serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch); serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); if (status1 != 0x90) { restore_flags(flags); return 0; } scratch2 = serial_in(info, UART_LCR); serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ serial_outp(info, UART_LCR, 0); serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(info, UART_IIR) >> 6; switch (scratch) { case 0: state->type = PORT_16450; break; case 1: state->type = PORT_UNKNOWN; break; case 2: state->type = PORT_16550; break; case 3: state->type = PORT_16550A; break; } if (state->type == PORT_16550A) { /* Check for Startech UART's */ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); if (serial_in(info, UART_EFR) == 0) { state->type = PORT_16650; } else { u8 old_fctr, old_emsr, old_fcr, old_mcr; u8 old_dll, old_dlm; int count; unsigned long last_jiff; serial_outp(info, UART_LCR, 0xBF); if (serial_in(info, UART_EFR) == 0) { old_fctr = serial_inp(info, UART_FCTR); serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP); old_emsr = serial_inp(info, UART_EMSR); serial_outp(info, UART_EMSR, 0x00); serial_outp(info, UART_LCR, scratch2); scratch = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, 0xa5); status1 = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, 0x5a); status2 = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) { serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP); serial_outp(info, UART_EMSR, old_emsr); serial_outp(info, UART_FCTR, old_fctr); state->type = PORT_16850; } else { old_fcr = serial_inp(info, UART_FCR); old_mcr = serial_inp(info, UART_MCR); /* * Since this is a 16650 type uart, the * attempted write to the 850 FCTR has * trashed the IER. Restore it here. */ serial_outp(info, UART_IER, old_fctr); serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(info, UART_MCR, UART_MCR_LOOP); serial_outp(info, UART_LCR, UART_LCR_DLAB); old_dll = serial_inp(info, UART_DLL); old_dlm = serial_inp(info, UART_DLM); serial_outp(info, UART_DLL, 0x01); serial_outp(info, UART_DLM, 0x00); serial_outp(info, UART_LCR, 0x03); for (count = 0; count < 64; count++) serial_outp(info, UART_TX, count); restore_flags(flags); last_jiff = jiffies; while (jiffies - last_jiff < 2); save_flags(flags); cli(); for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && (count < 64); count++) serial_inp(info, UART_RX); serial_outp(info, UART_FCR, old_fcr); serial_outp(info, UART_MCR, old_mcr); serial_outp(info, UART_LCR, UART_LCR_DLAB); serial_outp(info, UART_DLL, old_dll); serial_outp(info, UART_DLM, old_dlm); if (count == 64) state->type = PORT_16654; else state->type = PORT_16650V2; } } } } if (state->type == PORT_16550A) { /* Check for TI 16750 */ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 7) { serial_outp(info, UART_LCR, 0); scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 6) state->type = PORT_16750; } serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); } serial_outp(info, UART_LCR, scratch2); if (state->type == PORT_16450) { scratch = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, 0xa5); status1 = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, 0x5a); status2 = serial_in(info, UART_SCR); serial_outp(info, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) state->type = PORT_8250; } if (state->type == PORT_UNKNOWN) { restore_flags(flags); return 0; } serial_outp(info, UART_MCR, 0x00); serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_in(info, UART_RX); serial_outp(info, UART_IER, 0); restore_flags(flags); return state->type; } static void enable_board(struct pci_dev *dev) { u8 data; pci_read_config_byte(dev, PCI_COMMAND, &data); pci_write_config_byte(dev, PCI_COMMAND, data | PCI_COMMAND_MEMORY); } static void disable_board(struct pci_dev *dev) { u8 data; pci_read_config_byte(dev, PCI_COMMAND, &data); pci_write_config_byte(dev, PCI_COMMAND, data & ~PCI_COMMAND_MEMORY); } static int filter_keyspan_board(u16 subvendor, u16 subdevice) { if (subvendor == KEYSPAN_SX_SUBSYSTEM_VENDOR && subdevice == KEYSPAN_SX_SUBSYSTEM_DEVID) return 4; return 0; } static int probe_9050(int board_index, struct pci_dev *dev) { u16 subvendor, subdevice; int nr_expected_uarts; pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &subdevice); nr_expected_uarts = filter_keyspan_board(subvendor, subdevice); #if DEBUG printk("%s: found subdevice(%x,%x) with %d uarts (0 signals board not reconignized as a Keyspan board)", sx_sname, subvendor, subdevice, nr_expected_uarts); #endif if (nr_expected_uarts) { u8 *p; int i, ret, uart_index = 0; sx_board[board_index] = dev; print_pci_details(dev); enable_board(dev); for (i = 0; i < nr_expected_uarts; i++) { p = ioremap(dev->base_address[2] + i*0x400, 0x400); if ((ret = probe_uart(p))) { sx_uart[uart_index].base = p; printk("%s: probe_uart(0x%p) %d type %d\n", sx_sname, p, uart_index, ret); uart_index++; } else { iounmap(p); printk("%s: probe_uart(0x%p) returns failure\n", sx_sname, p); } } board_index++; } return board_index; } static void register_device(void) { int board_index = 0; struct pci_dev *dev = NULL; if (!pci_present()) return; while ((board_index < KEYSPAN_SX_MAX_BOARDS) && (dev = pci_find_device(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, dev))) { board_index = probe_9050(board_index, dev); } } static void unregister_device(void) { struct pci_dev **p = sx_board; struct sx_uart *q = sx_uart; int i; for (i = 0; i < KEYSPAN_SX_MAX_BOARDS; i++, p++) { if (*p) disable_board(*p); } for (i = 0; i < KEYSPAN_SX_MAX_UARTS; i++, q++) { if (q->base) iounmap(q->base); } } __initfunc(int sx_init(void)) { printk("%s(%s): init\n", sx_name, sx_version); register_device(); return 0; } #ifdef MODULE int init_module(void) { #if DEBUG printk("%s: init_module\n", sx_sname); #endif return sx_init(); } void cleanup_module(void) { #if DEBUG printk("%s: cleanup module\n", sx_sname); #endif unregister_device(); } #endif /* MODULE */