pnx8xxx_uart.c
来自「linux 内核源代码」· C语言 代码 · 共 853 行 · 第 1/2 页
C
853 行
/* * UART driver for PNX8XXX SoCs * * Author: Per Hallsmark per.hallsmark@mvista.com * Ported to 2.6 kernel by EmbeddedAlley * Reworked by Vitaly Wool <vitalywool@gmail.com> * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * Copyright (C) 2000 Deep Blue Solutions Ltd. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of * any kind, whether express or implied. * */#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/module.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial_core.h>#include <linux/serial.h>#include <linux/serial_pnx8xxx.h>#include <asm/io.h>#include <asm/irq.h>/* We'll be using StrongARM sa1100 serial port major/minor */#define SERIAL_PNX8XXX_MAJOR 204#define MINOR_START 5#define NR_PORTS 2#define PNX8XXX_ISR_PASS_LIMIT 256/* * Convert from ignore_status_mask or read_status_mask to FIFO * and interrupt status bits */#define SM_TO_FIFO(x) ((x) >> 10)#define SM_TO_ISTAT(x) ((x) & 0x000001ff)#define FIFO_TO_SM(x) ((x) << 10)#define ISTAT_TO_SM(x) ((x) & 0x000001ff)/* * This is the size of our serial port register set. */#define UART_PORT_SIZE 0x1000/* * This determines how often we check the modem status signals * for any change. They generally aren't connected to an IRQ * so we have to poll them. We also check immediately before * filling the TX fifo incase CTS has been dropped. */#define MCTRL_TIMEOUT (250*HZ/1000)extern struct pnx8xxx_port pnx8xxx_ports[];static inline int serial_in(struct pnx8xxx_port *sport, int offset){ return (__raw_readl(sport->port.membase + offset));}static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value){ __raw_writel(value, sport->port.membase + offset);}/* * Handle any change of modem status signal since we were last called. */static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport){ unsigned int status, changed; status = sport->port.ops->get_mctrl(&sport->port); changed = status ^ sport->old_status; if (changed == 0) return; sport->old_status = status; if (changed & TIOCM_RI) sport->port.icount.rng++; if (changed & TIOCM_DSR) sport->port.icount.dsr++; if (changed & TIOCM_CAR) uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); wake_up_interruptible(&sport->port.info->delta_msr_wait);}/* * This is our per-port timeout handler, for checking the * modem status signals. */static void pnx8xxx_timeout(unsigned long data){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data; unsigned long flags; if (sport->port.info) { spin_lock_irqsave(&sport->port.lock, flags); pnx8xxx_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); }}/* * interrupts disabled on entry */static void pnx8xxx_stop_tx(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; u32 ien; /* Disable TX intr */ ien = serial_in(sport, PNX8XXX_IEN); serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX); /* Clear all pending TX intr */ serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX);}/* * interrupts may not be disabled on entry */static void pnx8xxx_start_tx(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; u32 ien; /* Clear all pending TX intr */ serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); /* Enable TX intr */ ien = serial_in(sport, PNX8XXX_IEN); serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX);}/* * Interrupts enabled */static void pnx8xxx_stop_rx(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; u32 ien; /* Disable RX intr */ ien = serial_in(sport, PNX8XXX_IEN); serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX); /* Clear all pending RX intr */ serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX);}/* * Set the modem control timer to fire immediately. */static void pnx8xxx_enable_ms(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; mod_timer(&sport->timer, jiffies);}static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport){ struct tty_struct *tty = sport->port.info->tty; unsigned int status, ch, flg; status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) { ch = serial_in(sport, PNX8XXX_FIFO); sport->port.icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE | PNX8XXX_UART_FIFO_RXPAR) | ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) { if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) sport->port.icount.parity++; else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) sport->port.icount.frame++; if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN)) sport->port.icount.overrun++; status &= sport->port.read_status_mask; if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) flg = TTY_PARITY; else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) flg = TTY_FRAME;#ifdef SUPPORT_SYSRQ sport->port.sysrq = 0;#endif } if (uart_handle_sysrq_char(&sport->port, ch)) goto ignore_char; uart_insert_char(&sport->port, status, ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg); ignore_char: serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) | PNX8XXX_UART_LCR_RX_NEXT); status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); } tty_flip_buffer_push(tty);}static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport){ struct circ_buf *xmit = &sport->port.info->xmit; if (sport->port.x_char) { serial_out(sport, PNX8XXX_FIFO, sport->port.x_char); sport->port.icount.tx++; sport->port.x_char = 0; return; } /* * Check the modem control lines before * transmitting anything. */ pnx8xxx_mctrl_check(sport); if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { pnx8xxx_stop_tx(&sport->port); return; } /* * TX while bytes available */ while (((serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) { serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); if (uart_circ_empty(xmit)) pnx8xxx_stop_tx(&sport->port);}static irqreturn_t pnx8xxx_int(int irq, void *dev_id){ struct pnx8xxx_port *sport = dev_id; unsigned int status; spin_lock(&sport->port.lock); /* Get the interrupts */ status = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN); /* Break signal received */ if (status & PNX8XXX_UART_INT_BREAK) { sport->port.icount.brk++; uart_handle_break(&sport->port); } /* Byte received */ if (status & PNX8XXX_UART_INT_RX) pnx8xxx_rx_chars(sport); /* TX holding register empty - transmit a byte */ if (status & PNX8XXX_UART_INT_TX) pnx8xxx_tx_chars(sport); /* Clear the ISTAT register */ serial_out(sport, PNX8XXX_ICLR, status); spin_unlock(&sport->port.lock); return IRQ_HANDLED;}/* * Return TIOCSER_TEMT when transmitter is not busy. */static unsigned int pnx8xxx_tx_empty(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT;}static unsigned int pnx8xxx_get_mctrl(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; unsigned int mctrl = TIOCM_DSR; unsigned int msr; /* REVISIT */ msr = serial_in(sport, PNX8XXX_MCR); mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0; mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0; return mctrl;}static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl){#if 0 /* FIXME */ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; unsigned int msr;#endif}/* * Interrupts always disabled. */static void pnx8xxx_break_ctl(struct uart_port *port, int break_state){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; unsigned long flags; unsigned int lcr; spin_lock_irqsave(&sport->port.lock, flags); lcr = serial_in(sport, PNX8XXX_LCR); if (break_state == -1) lcr |= PNX8XXX_UART_LCR_TXBREAK; else lcr &= ~PNX8XXX_UART_LCR_TXBREAK; serial_out(sport, PNX8XXX_LCR, lcr); spin_unlock_irqrestore(&sport->port.lock, flags);}static int pnx8xxx_startup(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; int retval; /* * Allocate the IRQ */ retval = request_irq(sport->port.irq, pnx8xxx_int, 0, "pnx8xxx-uart", sport); if (retval) return retval; /* * Finally, clear and enable interrupts */ serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | PNX8XXX_UART_INT_ALLTX); serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) | PNX8XXX_UART_INT_ALLRX | PNX8XXX_UART_INT_ALLTX); /* * Enable modem status interrupts */ spin_lock_irq(&sport->port.lock); pnx8xxx_enable_ms(&sport->port); spin_unlock_irq(&sport->port.lock); return 0;}static void pnx8xxx_shutdown(struct uart_port *port){ struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; int lcr; /* * Stop our timer. */ del_timer_sync(&sport->timer); /* * Disable all interrupts */ serial_out(sport, PNX8XXX_IEN, 0); /* * Reset the Tx and Rx FIFOS, disable the break condition */ lcr = serial_in(sport, PNX8XXX_LCR); lcr &= ~PNX8XXX_UART_LCR_TXBREAK; lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST; serial_out(sport, PNX8XXX_LCR, lcr); /* * Clear all interrupts */ serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | PNX8XXX_UART_INT_ALLTX); /* * Free the interrupt */ free_irq(sport->port.irq, sport);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?