📄 serial_16550a.c
字号:
#include "config.h"#include "machine.h"#include "serial_reg.h"#include "serial.h"#include "processor.h"#include <types.h>/* * This converts from our new CONFIG_ symbols to the symbols * that asm/serial.h expects. You _NEED_ to comment out the * linux/config.h include contained inside asm/serial.h for * this to work. */#undef CONFIG_SERIAL_MANY_PORTS#undef CONFIG_SERIAL_DETECT_IRQ#undef CONFIG_SERIAL_MULTIPORT#undef CONFIG_HUB6#ifdef CONFIG_SERIAL_8250_MANY_PORTS#define CONFIG_SERIAL_MANY_PORTS 1#endif#ifdef CONFIG_SERIAL_8250_DETECT_IRQ#define CONFIG_SERIAL_DETECT_IRQ 1#endif#ifdef CONFIG_SERIAL_8250_MULTIPORT#define CONFIG_SERIAL_MULTIPORT 1#endif#ifdef CONFIG_SERIAL_8250_HUB6#define CONFIG_HUB6 1#endif#define SERIAL_DEBUG_AUTOCONF#include <asm/serial.h>/* UART16550A */#define EUART_CTL_BASE 0x10000000 //nGCS5//#define EUART_CTL_BASE pSC16550A_IO_BASE //nGCS2//#define EUART_CTL_BASE 0xd8000000//#define EUART0_CTL_BASE EUART_CTL_BASE//#define EUART1_CTL_BASE EUART_CTL_BASE + 0x08//#define EUART2_CTL_BASE EUART_CTL_BASE + 0x10//#define EUART3_CTL_BASE EUART_CTL_BASE + 0x18#define EUART0_CTL_BASE 0x10000000 //nGCS2#define EUART1_CTL_BASE 0x18000000 //nGCS3#define EUART2_CTL_BASE 0x20000000 //nGCS4#define EUART3_CTL_BASE 0x28000000 //nGCS5#define UART_NR 4 //4#define port_acr unused[0] /* 8bit */#define port_ier unused[1] /* 8bit */#define port_rev unused[2] /* 8bit */#define port_lcr unused[3] /* 8bit *//* * Here we define the default xmit fifo size used for each type of UART. */static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { { "unknown", 1, 0 }, { "8250", 1, 0 }, { "16450", 1, 0 }, { "16550", 1, 0 }, { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, { "Cirrus", 1, 0 }, { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, { "Startech", 1, 0 }, { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }};//#define GPIO_SERIAL#ifdef GPIO_SERIALstatic _INLINE_ unsigned intserial_in(struct uart_port *port, int offset){ //unsigned int ret; unsigned char scratch, scratch1; unsigned int addr; unsigned long flags; unsigned int mask; addr = offset&7; switch(port->iobase) { case EUART0_CTL_BASE: mask = G16550_NGCS0_MASK; break; case EUART1_CTL_BASE: //printk("Serial port 1 input,ioport=0x%08x\n",(port->iobase|0xC8000000) + offset); mask = G16550_NGCS1_MASK; break; case EUART2_CTL_BASE: mask = G16550_NGCS2_MASK; break; case EUART3_CTL_BASE: default: //mask = 0; return 0; } spin_lock_irqsave(sio_lock,flags); /* atomic IO */ GPCON(PORTD_OFS) = G16550_DBUS_DIR_IN | G16550_CBUS_DIR | G16550_ABUS_DIR; GPDAT(PORTD_OFS) = (GPDAT(PORTD_OFS) & ~G16550_ADDR_MASK) | (addr << G16550_ADDR_SHIFT); GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) & ~(mask|G16550_NREAD_MASK); //GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) & ~G16550_NREAD_MASK; scratch1 = GPDAT(PORTD_OFS) & G16550_DATA_MASK; scratch = GPDAT(PORTD_OFS) & G16550_DATA_MASK; //GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) | G16550_NREAD_MASK; GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) | (mask|G16550_NREAD_MASK); spin_unlock_irqrestore(sio_lock, flags); //if (scratch == scratch1) return scratch; //else while(1);}static _INLINE_ voidserial_out(struct uart_port *port, int offset, unsigned int value){ //unsigned int ret; int addr; unsigned long flags; unsigned int mask; addr = offset&7; switch(port->iobase) { case EUART0_CTL_BASE: mask = G16550_NGCS0_MASK; break; case EUART1_CTL_BASE: //printk("Serial port 1 output,ioport=0x%08x\n",(port->iobase|0xC8000000) + offset); mask = G16550_NGCS1_MASK; break; case EUART2_CTL_BASE: mask = G16550_NGCS2_MASK; break; case EUART3_CTL_BASE: default: //mask = 0; return; } spin_lock_irqsave(sio_lock, flags); /* atomic IO */ GPCON(PORTD_OFS) = G16550_DBUS_DIR_OUT | G16550_CBUS_DIR | G16550_ABUS_DIR; GPDAT(PORTD_OFS) = (GPDAT(PORTD_OFS) & (~G16550_DATA_MASK)) | (value&255); GPDAT(PORTD_OFS) = (GPDAT(PORTD_OFS) & (~G16550_ADDR_MASK)) | (addr << G16550_ADDR_SHIFT); GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) & ~(mask|G16550_NWRITE_MASK); GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) & ~G16550_NWRITE_MASK; //GPCON(PORTD_OFS) = G16550_DBUS_DIR_OUT | G16550_CBUS_DIR | G16550_ABUS_DIR; //GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) | G16550_NWRITE_MASK; GPDAT(PORTD_OFS) = GPDAT(PORTD_OFS) | (mask|G16550_NWRITE_MASK); GPCON(PORTD_OFS) = G16550_DBUS_DIR_IN | G16550_CBUS_DIR | G16550_ABUS_DIR; spin_unlock_irqrestore(sio_lock, flags);}#elsestatic inline unsigned charserial_in(struct uart_port *port, int reg){ //udelay(10); return *(volatile unsigned char *)(port->iobase + reg);}static inline voidserial_out(struct uart_port *port, int reg, unsigned int value){ //udelay(10); *(volatile unsigned char *)(port->iobase + reg) = value;}#endif/* * We used to support using pause I/O for certain machines. We * haven't supported this for a while, but just in case it's badly * needed for certain old 386 machines, I've left these #define's * in.... */#define serial_inp(port, reg) serial_in(port, reg)#define serial_outp(port, reg, value) serial_out(port, reg, value)/* * This is a quickie test to see how big the FIFO is. * It doesn't work at all the time, more's the pity. */static int size_fifo(struct uart_port *port){ unsigned char old_fcr, old_mcr, old_dll, old_dlm; int count; old_fcr = serial_inp(port, UART_FCR); old_mcr = serial_inp(port, UART_MCR); //serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(port, UART_MCR, UART_MCR_LOOP); serial_outp(port, UART_LCR, UART_LCR_DLAB); old_dll = serial_inp(port, UART_DLL); old_dlm = serial_inp(port, UART_DLM); serial_outp(port, UART_DLL, 0x01); serial_outp(port, UART_DLM, 0x00); serial_outp(port, UART_LCR, UART_LCR_WLEN8); for (count = 0; count < 256; count++) serial_outp(port, UART_TX, count); do{ mdelay(20); }while((!(serial_inp(port, UART_LSR) & UART_LSR_THRE)) && count--); for (count = 0; (serial_inp(port, UART_LSR) & UART_LSR_DR) && (count < 256); count++) serial_inp(port, UART_RX); serial_outp(port, UART_FCR, old_fcr); serial_outp(port, UART_MCR, old_mcr); serial_outp(port, UART_LCR, UART_LCR_DLAB); serial_outp(port, UART_DLL, old_dll); serial_outp(port, UART_DLM, old_dlm); printk("autoconfig: fifosize is %d byte(s).\n",count); return count;}/* * This routine is called by rs_init() to initialize a specific serial * port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is * whether or not this UART is a 16550A or not, since this will * determine whether or not we can use its FIFO features or not. */static int autoconfig(struct uart_port *port, unsigned int probeflags){ unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags;#ifdef SERIAL_DEBUG_AUTOCONF printk("Testing ttyS%d (0x%04x, 0x%08lx)...\n", port->line, port->iobase, port->membase);#endif if (!port->iobase) return -1; if (!(port->flags & ASYNC_BUGGY_UART)) { /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. * * 0x80 is used as a nonsense port to prevent against * false positives due to ISA bus float. The * assumption is that 0x80 is a non-existent port; * which should be safe since include/asm/io.h also * makes this assumption. */ scratch = serial_inp(port, UART_IER); serial_outp(port, UART_IER, 0); scratch2 = serial_inp(port, UART_IER); serial_outp(port, UART_IER, 0x0F); scratch3 = serial_inp(port, UART_IER); serial_outp(port, UART_IER, scratch); if (scratch2 || scratch3 != 0x0F) {#ifdef SERIAL_DEBUG_AUTOCONF printk("serial: ttyS%d: simple autoconfig failed " "(%02x, %02x)\n", port->line, scratch2, scratch3);#endif return -1; /* We failed; there's nothing here */ } } save_mcr = serial_in(port, UART_MCR); save_lcr = serial_in(port, UART_LCR); /* * Check to see if a UART is really there. Certain broken * internal modems based on the Rockwell chipset fail this * test, because they apparently don't implement the loopback * test mode. So this test is skipped on the COM 1 through * COM 4 ports. This *should* be safe, since no board * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope! */ if (!(port->flags & ASYNC_SKIP_TEST)) { serial_outp(port, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(port, UART_MSR) & 0xF0; serial_outp(port, UART_MCR, save_mcr); if (status1 != 0x90) {#ifdef SERIAL_DEBUG_AUTOCONF printk("serial: ttyS%d: no UART loopback failed\n", port->line);#endif return -1; } } serial_outp(port, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(port, UART_EFR, 0); /* EFR is the same as FCR */ serial_outp(port, UART_LCR, 0); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(port, UART_IIR) >> 6; switch (scratch) { case 0: port->type = PORT_16450; printk("Found Serial Port PORT_16450!\n"); break; case 1: port->type = PORT_UNKNOWN; printk("Found Serial Port PORT_UNKNOWN!\n"); break; case 2: port->type = PORT_16550; printk("Found Serial Port PORT_16550!\n"); break; case 3: port->type = PORT_16550A; printk("Found Serial Port PORT_16550A!\n"); break; } if (port->type == PORT_16550A) { /* Check for Startech UART's */ serial_outp(port, UART_LCR, UART_LCR_DLAB); if (serial_in(port, UART_EFR) == 0) { port->type = PORT_16650; } else { serial_outp(port, UART_LCR, 0xBF); if (serial_in(port, UART_EFR) == 0) ; } }#if 0 if (port->type == PORT_16550A) { /* Check for TI 16750 */ serial_outp(port, UART_LCR, save_lcr | UART_LCR_DLAB); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(port, UART_IIR) >> 5; if (scratch == 7) { /* * If this is a 16750, and not a cheap UART * clone, then it should only go into 64 byte * mode if the UART_FCR7_64BYTE bit was set * while UART_LCR_DLAB was latched. */ serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(port, UART_LCR, 0); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(port, UART_IIR) >> 5; if (scratch == 6) port->type = PORT_16750; } serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); }#endif serial_outp(port, UART_LCR, save_lcr); if (port->type == PORT_16450) { scratch = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, 0xa5); status1 = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, 0x5a); status2 = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) port->type = PORT_8250; } port->fifosize = size_fifo(port); //uart_config[port->type].dfl_xmit_fifo_size; if (port->type == PORT_UNKNOWN) { return -1; } /* * Reset the UART. */ serial_outp(port, UART_MCR, save_mcr); serial_outp(port, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(port, UART_FCR, 0); (void)serial_in(port, UART_RX); serial_outp(port, UART_IER, 0); return 0;}//static struct uart_port serial8250_ports[UART_NR];static struct uart_port serial8250_ports[4] = { { iobase: (unsigned long)(EUART0_CTL_BASE), iotype: SERIAL_IO_PORT, uartclk: 1843200, fifosize: 16, type: PORT_16550A, flags: ASYNC_BOOT_AUTOCONF, }, { iobase: (unsigned long)(EUART1_CTL_BASE), iotype: SERIAL_IO_PORT, uartclk: 1843200, fifosize: 16, type: PORT_16550A, flags: ASYNC_BOOT_AUTOCONF, }, { iobase: (unsigned long)(EUART2_CTL_BASE), iotype: SERIAL_IO_PORT, uartclk: 1843200, fifosize: 16, type: PORT_16550A, flags: ASYNC_BOOT_AUTOCONF, }, { iobase: (unsigned long)(EUART3_CTL_BASE), iotype: SERIAL_IO_PORT, uartclk: 1843200, fifosize: 16, type: PORT_16550A, flags: ASYNC_BOOT_AUTOCONF, }};int Serial16550A_init(void){ int i; if (autoconfig(&serial8250_ports[0], 0)<0) printk("no 16C550 serial port found!\n"); autoconfig(&serial8250_ports[1], 0); autoconfig(&serial8250_ports[2], 0); autoconfig(&serial8250_ports[3], 0); /*for (i=0;i<6;i++) { printk("%d",i+1); mdelay(1000); }printk("\n");*/ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -