ioc4_serial.c
来自「linux 内核源代码」· C语言 代码 · 共 2,250 行 · 第 1/5 页
C
2,250 行
/* Local port info for each IOC4 serial ports */struct ioc4_port { struct uart_port *ip_port; /* current active port ptr */ /* Ptrs for all ports */ struct uart_port *ip_all_ports[UART_PORT_COUNT]; /* Back ptrs for this port */ struct ioc4_control *ip_control; struct pci_dev *ip_pdev; struct ioc4_soft *ip_ioc4_soft; /* pci mem addresses */ struct ioc4_misc_regs __iomem *ip_mem; struct ioc4_serial __iomem *ip_serial; struct ioc4_serialregs __iomem *ip_serial_regs; struct ioc4_uartregs __iomem *ip_uart_regs; /* Ring buffer page for this port */ dma_addr_t ip_dma_ringbuf; /* vaddr of ring buffer */ struct ring_buffer *ip_cpu_ringbuf; /* Rings for this port */ struct ring *ip_inring; struct ring *ip_outring; /* Hook to port specific values */ struct hooks *ip_hooks; spinlock_t ip_lock; /* Various rx/tx parameters */ int ip_baud; int ip_tx_lowat; int ip_rx_timeout; /* Copy of notification bits */ int ip_notify; /* Shadow copies of various registers so we don't need to PIO * read them constantly */ uint32_t ip_ienb; /* Enabled interrupts */ uint32_t ip_sscr; uint32_t ip_tx_prod; uint32_t ip_rx_cons; int ip_pci_bus_speed; unsigned char ip_flags;};/* tx low water mark. We need to notify the driver whenever tx is getting * close to empty so it can refill the tx buffer and keep things going. * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll * have no trouble getting in more chars in time (I certainly hope so). */#define TX_LOWAT_LATENCY 1000#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY)#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)/* Flags per port */#define INPUT_HIGH 0x01#define DCD_ON 0x02#define LOWAT_WRITTEN 0x04#define READ_ABORTED 0x08#define PORT_ACTIVE 0x10#define PORT_INACTIVE 0 /* This is the value when "off" *//* Since each port has different register offsets and bitmasks * for everything, we'll store those that we need in tables so we * don't have to be constantly checking the port we are dealing with. */struct hooks { uint32_t intr_delta_dcd; uint32_t intr_delta_cts; uint32_t intr_tx_mt; uint32_t intr_rx_timer; uint32_t intr_rx_high; uint32_t intr_tx_explicit; uint32_t intr_dma_error; uint32_t intr_clear; uint32_t intr_all; int rs422_select_pin;};static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = { /* Values for port 0 */ { IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS, IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER, IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT, IOC4_OTHER_IR_S0_MEMERR, (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL | IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER | IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS | IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT), IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN, }, /* Values for port 1 */ { IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS, IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER, IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT, IOC4_OTHER_IR_S1_MEMERR, (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL | IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER | IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS | IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT), IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN, }, /* Values for port 2 */ { IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS, IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER, IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT, IOC4_OTHER_IR_S2_MEMERR, (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL | IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER | IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS | IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT), IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN, }, /* Values for port 3 */ { IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS, IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER, IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT, IOC4_OTHER_IR_S3_MEMERR, (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL | IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER | IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS | IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT), IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN, }};/* A ring buffer entry */struct ring_entry { union { struct { uint32_t alldata; uint32_t allsc; } all; struct { char data[4]; /* data bytes */ char sc[4]; /* status/control */ } s; } u;};/* Test the valid bits in any of the 4 sc chars using "allsc" member */#define RING_ANY_VALID \ ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101)#define ring_sc u.s.sc#define ring_data u.s.data#define ring_allsc u.all.allsc/* Number of entries per ring buffer. */#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))/* An individual ring */struct ring { struct ring_entry entries[ENTRIES_PER_RING];};/* The whole enchilada */struct ring_buffer { struct ring TX_0_OR_2; struct ring RX_0_OR_2; struct ring TX_1_OR_3; struct ring RX_1_OR_3;};/* Get a ring from a port struct */#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)/* Infinite loop detection. */#define MAXITER 10000000/* Prototypes */static void receive_chars(struct uart_port *);static void handle_intr(void *arg, uint32_t sio_ir);/* * port_is_active - determines if this port is currently active * @port: ptr to soft struct for this port * @uart_port: uart port to test for */static inline int port_is_active(struct ioc4_port *port, struct uart_port *uart_port){ if (port) { if ((port->ip_flags & PORT_ACTIVE) && (port->ip_port == uart_port)) return 1; } return 0;}/** * write_ireg - write the interrupt regs * @ioc4_soft: ptr to soft struct for this port * @val: value to write * @which: which register * @type: which ireg set */static inline voidwrite_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type){ struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr; unsigned long flags; spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags); switch (type) { case IOC4_SIO_INTR_TYPE: switch (which) { case IOC4_W_IES: writel(val, &mem->sio_ies.raw); break; case IOC4_W_IEC: writel(val, &mem->sio_iec.raw); break; } break; case IOC4_OTHER_INTR_TYPE: switch (which) { case IOC4_W_IES: writel(val, &mem->other_ies.raw); break; case IOC4_W_IEC: writel(val, &mem->other_iec.raw); break; } break; default: break; } spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags);}/** * set_baud - Baud rate setting code * @port: port to set * @baud: baud rate to use */static int set_baud(struct ioc4_port *port, int baud){ int actual_baud; int diff; int lcr; unsigned short divisor; struct ioc4_uartregs __iomem *uart; divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed); if (!divisor) return 1; actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed); diff = actual_baud - baud; if (diff < 0) diff = -diff; /* If we're within 1%, we've found a match */ if (diff * 100 > actual_baud) return 1; uart = port->ip_uart_regs; lcr = readb(&uart->i4u_lcr); writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr); writeb((unsigned char)divisor, &uart->i4u_dll); writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm); writeb(lcr, &uart->i4u_lcr); return 0;}/** * get_ioc4_port - given a uart port, return the control structure * @port: uart port * @set: set this port as current */static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set){ struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev); struct ioc4_control *control = idd->idd_serial_data; struct ioc4_port *port; int port_num, port_type; if (control) { for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++ ) { port = control->ic_port[port_num].icp_port; if (!port) continue; for (port_type = UART_PORT_MIN; port_type < UART_PORT_COUNT; port_type++) { if (the_port == port->ip_all_ports [port_type]) { /* set local copy */ if (set) { port->ip_port = the_port; } return port; } } } } return NULL;}/* The IOC4 hardware provides no atomic way to determine if interrupts * are pending since two reads are required to do so. The handler must * read the SIO_IR and the SIO_IES, and take the logical and of the * two. When this value is zero, all interrupts have been serviced and * the handler may return. * * This has the unfortunate "hole" that, if some other CPU or * some other thread or some higher level interrupt manages to * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may * think we have observed SIO_IR&SIO_IE==0 when in fact this * condition never really occurred. * * To solve this, we use a simple spinlock that must be held * whenever modifying SIO_IE; holding this lock while observing * both SIO_IR and SIO_IE guarantees that we do not falsely * conclude that no enabled interrupts are pending. */static inline uint32_tpending_intrs(struct ioc4_soft *soft, int type){ struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; unsigned long flag; uint32_t intrs = 0; BUG_ON(!((type == IOC4_SIO_INTR_TYPE) || (type == IOC4_OTHER_INTR_TYPE))); spin_lock_irqsave(&soft->is_ir_lock, flag); switch (type) { case IOC4_SIO_INTR_TYPE: intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw); break; case IOC4_OTHER_INTR_TYPE: intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw); /* Don't process any ATA interrupte */ intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); break; default: break; } spin_unlock_irqrestore(&soft->is_ir_lock, flag); return intrs;}/** * port_init - Initialize the sio and ioc4 hardware for a given port * called per port from attach... * @port: port to initialize */static int inline port_init(struct ioc4_port *port){ uint32_t sio_cr; struct hooks *hooks = port->ip_hooks; struct ioc4_uartregs __iomem *uart; /* Idle the IOC4 serial interface */ writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr); /* Wait until any pending bus activity for this port has ceased */ do sio_cr = readl(&port->ip_mem->sio_cr.raw); while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); /* Finish reset sequence */ writel(0, &port->ip_serial_regs->sscr); /* Once RESET is done, reload cached tx_prod and rx_cons values * and set rings to empty by making prod == cons */ port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); /* Disable interrupts for this 16550 */ uart = port->ip_uart_regs; writeb(0, &uart->i4u_lcr); writeb(0, &uart->i4u_ier); /* Set the default baud */ set_baud(port, port->ip_baud); /* Set line control to 8 bits no parity */ writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr); /* UART_LCR_STOP == 1 stop */ /* Enable the FIFOs */ writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr); /* then reset 16550 FIFOs */ writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, &uart->i4u_fcr); /* Clear modem control register */ writeb(0, &uart->i4u_mcr); /* Clear deltas in modem status register */ readb(&uart->i4u_msr); /* Only do this once per port pair */ if (port->ip_hooks == &hooks_array[0] || port->ip_hooks == &hooks_array[2]) { unsigned long ring_pci_addr; uint32_t __iomem *sbbr_l; uint32_t __iomem *sbbr_h; if (port->ip_hooks == &hooks_array[0]) { sbbr_l = &port->ip_serial->sbbr01_l; sbbr_h = &port->ip_serial->sbbr01_h; } else { sbbr_l = &port->ip_serial->sbbr23_l; sbbr_h = &port->ip_serial->sbbr23_h; } ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", __FUNCTION__, ring_pci_addr)); writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); } /* Set the receive timeout value to 10 msec */ writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?