mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
369 lines
9.7 KiB
C
369 lines
9.7 KiB
C
![]() |
#include <linux/wait.h>
|
||
|
|
||
|
#define UART_CONFIG_TYPE (1 << 0)
|
||
|
#define UART_CONFIG_IRQ (1 << 1)
|
||
|
#define UPIO_PORT (0)
|
||
|
#define UPIO_HUB6 (1)
|
||
|
#define UPIO_MEM (2)
|
||
|
#define UPIO_MEM32 (3)
|
||
|
#define UPIO_AU (4) /* Au1x00 type IO */
|
||
|
#define UPIO_TSI (5) /* Tsi108/109 type IO */
|
||
|
#define UPF_FOURPORT (1 << 1)
|
||
|
#define UPF_SAK (1 << 2)
|
||
|
#define UPF_SPD_MASK (0x1030)
|
||
|
#define UPF_SPD_HI (0x0010)
|
||
|
#define UPF_SPD_VHI (0x0020)
|
||
|
#define UPF_SPD_CUST (0x0030)
|
||
|
#define UPF_SPD_SHI (0x1000)
|
||
|
#define UPF_SPD_WARP (0x1010)
|
||
|
#define UPF_SKIP_TEST (1 << 6)
|
||
|
#define UPF_AUTO_IRQ (1 << 7)
|
||
|
#define UPF_HARDPPS_CD (1 << 11)
|
||
|
#define UPF_LOW_LATENCY (1 << 13)
|
||
|
#define UPF_BUGGY_UART (1 << 14)
|
||
|
#define UPF_MAGIC_MULTIPLIER (1 << 16)
|
||
|
#define UPF_CONS_FLOW (1 << 23)
|
||
|
#define UPF_SHARE_IRQ (1 << 24)
|
||
|
#define UPF_BOOT_AUTOCONF (1 << 28)
|
||
|
#define UPF_DEAD (1 << 30)
|
||
|
#define UPF_IOREMAP (1 << 31)
|
||
|
#define UPF_CHANGE_MASK (0x17fff)
|
||
|
#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY)
|
||
|
#define USF_CLOSING_WAIT_INF (0)
|
||
|
#define USF_CLOSING_WAIT_NONE (~0U)
|
||
|
|
||
|
#define UART_XMIT_SIZE PAGE_SIZE
|
||
|
|
||
|
#define UIF_CHECK_CD (1 << 25)
|
||
|
#define UIF_CTS_FLOW (1 << 26)
|
||
|
#define UIF_NORMAL_ACTIVE (1 << 29)
|
||
|
#define UIF_INITIALIZED (1 << 31)
|
||
|
#define UIF_SUSPENDED (1 << 30)
|
||
|
|
||
|
#define WAKEUP_CHARS 256
|
||
|
|
||
|
#define uart_circ_empty(circ) ((circ)->head == (circ)->tail)
|
||
|
#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
|
||
|
|
||
|
#define uart_circ_chars_pending(circ) \
|
||
|
(CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
|
||
|
|
||
|
#define uart_circ_chars_free(circ) \
|
||
|
(CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
|
||
|
|
||
|
#define uart_tx_stopped(port) \
|
||
|
((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
|
||
|
|
||
|
#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \
|
||
|
(cflag) & CRTSCTS || \
|
||
|
!((cflag) & CLOCAL))
|
||
|
|
||
|
|
||
|
struct sb_uart_port;
|
||
|
struct sb_uart_info;
|
||
|
struct serial_struct;
|
||
|
struct device;
|
||
|
|
||
|
struct sb_uart_ops {
|
||
|
unsigned int (*tx_empty)(struct sb_uart_port *);
|
||
|
void (*set_mctrl)(struct sb_uart_port *, unsigned int mctrl);
|
||
|
unsigned int (*get_mctrl)(struct sb_uart_port *);
|
||
|
void (*stop_tx)(struct sb_uart_port *);
|
||
|
void (*start_tx)(struct sb_uart_port *);
|
||
|
void (*send_xchar)(struct sb_uart_port *, char ch);
|
||
|
void (*stop_rx)(struct sb_uart_port *);
|
||
|
void (*enable_ms)(struct sb_uart_port *);
|
||
|
void (*break_ctl)(struct sb_uart_port *, int ctl);
|
||
|
int (*startup)(struct sb_uart_port *);
|
||
|
void (*shutdown)(struct sb_uart_port *);
|
||
|
void (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new,
|
||
|
struct MP_TERMIOS *old);
|
||
|
void (*pm)(struct sb_uart_port *, unsigned int state,
|
||
|
unsigned int oldstate);
|
||
|
int (*set_wake)(struct sb_uart_port *, unsigned int state);
|
||
|
|
||
|
const char *(*type)(struct sb_uart_port *);
|
||
|
|
||
|
void (*release_port)(struct sb_uart_port *);
|
||
|
|
||
|
int (*request_port)(struct sb_uart_port *);
|
||
|
void (*config_port)(struct sb_uart_port *, int);
|
||
|
int (*verify_port)(struct sb_uart_port *, struct serial_struct *);
|
||
|
int (*ioctl)(struct sb_uart_port *, unsigned int, unsigned long);
|
||
|
};
|
||
|
|
||
|
|
||
|
struct sb_uart_icount {
|
||
|
__u32 cts;
|
||
|
__u32 dsr;
|
||
|
__u32 rng;
|
||
|
__u32 dcd;
|
||
|
__u32 rx;
|
||
|
__u32 tx;
|
||
|
__u32 frame;
|
||
|
__u32 overrun;
|
||
|
__u32 parity;
|
||
|
__u32 brk;
|
||
|
__u32 buf_overrun;
|
||
|
};
|
||
|
typedef unsigned int upf_t;
|
||
|
|
||
|
struct sb_uart_port {
|
||
|
spinlock_t lock; /* port lock */
|
||
|
unsigned int iobase; /* in/out[bwl] */
|
||
|
unsigned char __iomem *membase; /* read/write[bwl] */
|
||
|
unsigned int irq; /* irq number */
|
||
|
unsigned int uartclk; /* base uart clock */
|
||
|
unsigned int fifosize; /* tx fifo size */
|
||
|
unsigned char x_char; /* xon/xoff char */
|
||
|
unsigned char regshift; /* reg offset shift */
|
||
|
unsigned char iotype; /* io access style */
|
||
|
unsigned char unused1;
|
||
|
|
||
|
|
||
|
unsigned int read_status_mask; /* driver specific */
|
||
|
unsigned int ignore_status_mask; /* driver specific */
|
||
|
struct sb_uart_info *info; /* pointer to parent info */
|
||
|
struct sb_uart_icount icount; /* statistics */
|
||
|
|
||
|
struct console *cons; /* struct console, if any */
|
||
|
#ifdef CONFIG_SERIAL_CORE_CONSOLE
|
||
|
unsigned long sysrq; /* sysrq timeout */
|
||
|
#endif
|
||
|
|
||
|
upf_t flags;
|
||
|
|
||
|
unsigned int mctrl; /* current modem ctrl settings */
|
||
|
unsigned int timeout; /* character-based timeout */
|
||
|
unsigned int type; /* port type */
|
||
|
const struct sb_uart_ops *ops;
|
||
|
unsigned int custom_divisor;
|
||
|
unsigned int line; /* port index */
|
||
|
unsigned long mapbase; /* for ioremap */
|
||
|
struct device *dev; /* parent device */
|
||
|
unsigned char hub6; /* this should be in the 8250 driver */
|
||
|
unsigned char unused[3];
|
||
|
};
|
||
|
|
||
|
#define mdmode unused[2]
|
||
|
#define MDMODE_ADDR 0x1
|
||
|
#define MDMODE_ENABLE 0x2
|
||
|
#define MDMODE_AUTO 0x4
|
||
|
#define MDMODE_ADDRSEND 0x8
|
||
|
|
||
|
struct sb_uart_state {
|
||
|
unsigned int close_delay; /* msec */
|
||
|
unsigned int closing_wait; /* msec */
|
||
|
|
||
|
|
||
|
int count;
|
||
|
int pm_state;
|
||
|
struct sb_uart_info *info;
|
||
|
struct sb_uart_port *port;
|
||
|
|
||
|
struct mutex mutex;
|
||
|
};
|
||
|
|
||
|
typedef unsigned int uif_t;
|
||
|
|
||
|
struct sb_uart_info {
|
||
|
struct tty_struct *tty;
|
||
|
struct circ_buf xmit;
|
||
|
uif_t flags;
|
||
|
|
||
|
int blocked_open;
|
||
|
|
||
|
struct tasklet_struct tlet;
|
||
|
|
||
|
wait_queue_head_t open_wait;
|
||
|
wait_queue_head_t delta_msr_wait;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct module;
|
||
|
struct tty_driver;
|
||
|
|
||
|
struct uart_driver {
|
||
|
struct module *owner;
|
||
|
const char *driver_name;
|
||
|
const char *dev_name;
|
||
|
int major;
|
||
|
int minor;
|
||
|
int nr;
|
||
|
struct console *cons;
|
||
|
|
||
|
struct sb_uart_state *state;
|
||
|
struct tty_driver *tty_driver;
|
||
|
};
|
||
|
|
||
|
void sb_uart_write_wakeup(struct sb_uart_port *port)
|
||
|
{
|
||
|
struct sb_uart_info *info = port->info;
|
||
|
tasklet_schedule(&info->tlet);
|
||
|
}
|
||
|
|
||
|
void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag,
|
||
|
unsigned int baud)
|
||
|
{
|
||
|
unsigned int bits;
|
||
|
|
||
|
switch (cflag & CSIZE)
|
||
|
{
|
||
|
case CS5:
|
||
|
bits = 7;
|
||
|
break;
|
||
|
|
||
|
case CS6:
|
||
|
bits = 8;
|
||
|
break;
|
||
|
|
||
|
case CS7:
|
||
|
bits = 9;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
bits = 10;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (cflag & CSTOPB)
|
||
|
{
|
||
|
bits++;
|
||
|
}
|
||
|
|
||
|
if (cflag & PARENB)
|
||
|
{
|
||
|
bits++;
|
||
|
}
|
||
|
|
||
|
bits = bits * port->fifosize;
|
||
|
|
||
|
port->timeout = (HZ * bits) / baud + HZ/50;
|
||
|
}
|
||
|
unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios,
|
||
|
struct MP_TERMIOS *old, unsigned int min,
|
||
|
unsigned int max)
|
||
|
{
|
||
|
unsigned int try, baud, altbaud = 38400;
|
||
|
upf_t flags = port->flags & UPF_SPD_MASK;
|
||
|
|
||
|
if (flags == UPF_SPD_HI)
|
||
|
altbaud = 57600;
|
||
|
if (flags == UPF_SPD_VHI)
|
||
|
altbaud = 115200;
|
||
|
if (flags == UPF_SPD_SHI)
|
||
|
altbaud = 230400;
|
||
|
if (flags == UPF_SPD_WARP)
|
||
|
altbaud = 460800;
|
||
|
|
||
|
for (try = 0; try < 2; try++) {
|
||
|
|
||
|
switch (termios->c_cflag & (CBAUD | CBAUDEX))
|
||
|
{
|
||
|
case B921600 : baud = 921600; break;
|
||
|
case B460800 : baud = 460800; break;
|
||
|
case B230400 : baud = 230400; break;
|
||
|
case B115200 : baud = 115200; break;
|
||
|
case B57600 : baud = 57600; break;
|
||
|
case B38400 : baud = 38400; break;
|
||
|
case B19200 : baud = 19200; break;
|
||
|
case B9600 : baud = 9600; break;
|
||
|
case B4800 : baud = 4800; break;
|
||
|
case B2400 : baud = 2400; break;
|
||
|
case B1800 : baud = 1800; break;
|
||
|
case B1200 : baud = 1200; break;
|
||
|
case B600 : baud = 600; break;
|
||
|
case B300 : baud = 300; break;
|
||
|
case B200 : baud = 200; break;
|
||
|
case B150 : baud = 150; break;
|
||
|
case B134 : baud = 134; break;
|
||
|
case B110 : baud = 110; break;
|
||
|
case B75 : baud = 75; break;
|
||
|
case B50 : baud = 50; break;
|
||
|
default : baud = 9600; break;
|
||
|
}
|
||
|
|
||
|
if (baud == 38400)
|
||
|
baud = altbaud;
|
||
|
|
||
|
if (baud == 0)
|
||
|
baud = 9600;
|
||
|
|
||
|
if (baud >= min && baud <= max)
|
||
|
return baud;
|
||
|
|
||
|
termios->c_cflag &= ~CBAUD;
|
||
|
if (old) {
|
||
|
termios->c_cflag |= old->c_cflag & CBAUD;
|
||
|
old = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
termios->c_cflag |= B9600;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud)
|
||
|
{
|
||
|
unsigned int quot;
|
||
|
|
||
|
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
|
||
|
quot = port->custom_divisor;
|
||
|
else
|
||
|
quot = (port->uartclk + (8 * baud)) / (16 * baud);
|
||
|
|
||
|
return quot;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static inline int sb_uart_handle_break(struct sb_uart_port *port)
|
||
|
{
|
||
|
struct sb_uart_info *info = port->info;
|
||
|
|
||
|
if (port->flags & UPF_SAK)
|
||
|
do_SAK(info->tty);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static inline void sb_uart_handle_dcd_change(struct sb_uart_port *port, unsigned int status)
|
||
|
{
|
||
|
struct sb_uart_info *info = port->info;
|
||
|
|
||
|
port->icount.dcd++;
|
||
|
|
||
|
if (info->flags & UIF_CHECK_CD) {
|
||
|
if (status)
|
||
|
wake_up_interruptible(&info->open_wait);
|
||
|
else if (info->tty)
|
||
|
tty_hangup(info->tty);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline void sb_uart_handle_cts_change(struct sb_uart_port *port, unsigned int status)
|
||
|
{
|
||
|
struct sb_uart_info *info = port->info;
|
||
|
struct tty_struct *tty = info->tty;
|
||
|
|
||
|
port->icount.cts++;
|
||
|
|
||
|
if (info->flags & UIF_CTS_FLOW) {
|
||
|
if (tty->hw_stopped) {
|
||
|
if (status) {
|
||
|
tty->hw_stopped = 0;
|
||
|
port->ops->start_tx(port);
|
||
|
sb_uart_write_wakeup(port);
|
||
|
}
|
||
|
} else {
|
||
|
if (!status) {
|
||
|
tty->hw_stopped = 1;
|
||
|
port->ops->stop_tx(port);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|