Commit aa98b772 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

staging: sb105x: delete the driver

It's been marked BROKEN for over a year, and no one has stepped up to do
anything with the code, and no one has complained.  So just delete it.
If someone wants to fix it up and merge it "properly", they can revert
this commit.

Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent aee0bda8
...@@ -124,8 +124,6 @@ source "drivers/staging/imx-drm/Kconfig" ...@@ -124,8 +124,6 @@ source "drivers/staging/imx-drm/Kconfig"
source "drivers/staging/dgrp/Kconfig" source "drivers/staging/dgrp/Kconfig"
source "drivers/staging/sb105x/Kconfig"
source "drivers/staging/fwserial/Kconfig" source "drivers/staging/fwserial/Kconfig"
source "drivers/staging/goldfish/Kconfig" source "drivers/staging/goldfish/Kconfig"
......
...@@ -55,7 +55,6 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ ...@@ -55,7 +55,6 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_CED1401) += ced1401/
obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_DRM_IMX) += imx-drm/
obj-$(CONFIG_DGRP) += dgrp/ obj-$(CONFIG_DGRP) += dgrp/
obj-$(CONFIG_SB105X) += sb105x/
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_LUSTRE_FS) += lustre/ obj-$(CONFIG_LUSTRE_FS) += lustre/
......
config SB105X
tristate "SystemBase PCI Multiport UART"
select SERIAL_CORE
depends on PCI && X86 && TTY && BROKEN
help
A driver for the SystemBase Multi-2/PCI serial card
To compile this driver a module, choose M here: the module
will be called "sb105x".
obj-$(CONFIG_SB105X) += sb105x.o
sb105x-y := sb_pci_mp.o
/*
* SB105X_UART.h
*
* Copyright (C) 2008 systembase
*
* UART registers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef UART_SB105X_H
#define UART_SB105X_H
/*
* option register
*/
/* Device Information Register */
#define MP_OPTR_DIR0 0x04 /* port0 ~ port8 */
#define MP_OPTR_DIR1 0x05 /* port8 ~ port15 */
#define MP_OPTR_DIR2 0x06 /* port16 ~ port23 */
#define MP_OPTR_DIR3 0x07 /* port24 ~ port31 */
#define DIR_UART_16C550 0
#define DIR_UART_16C1050 1
#define DIR_UART_16C1050A 2
#define DIR_CLK_1843200 0x0 /* input clock 1843200 Hz */
#define DIR_CLK_3686400 0x1 /* input clock 3686400 Hz */
#define DIR_CLK_7372800 0x2 /* input clock 7372800 Hz */
#define DIR_CLK_14745600 0x3 /* input clock 14745600 Hz */
#define DIR_CLK_29491200 0x4 /* input clock 29491200 Hz */
#define DIR_CLK_58985400 0x5 /* input clock 58985400 Hz */
/* Interface Information Register */
#define MP_OPTR_IIR0 0x08 /* port0 ~ port8 */
#define MP_OPTR_IIR1 0x09 /* port8 ~ port15 */
#define MP_OPTR_IIR2 0x0A /* port16 ~ port23 */
#define MP_OPTR_IIR3 0x0B /* port24 ~ port31 */
#define IIR_RS232 0x00 /* RS232 type */
#define IIR_RS422 0x10 /* RS422 type */
#define IIR_RS485 0x20 /* RS485 type */
#define IIR_TYPE_MASK 0x30
/* Interrupt Mask Register */
#define MP_OPTR_IMR0 0x0C /* port0 ~ port8 */
#define MP_OPTR_IMR1 0x0D /* port8 ~ port15 */
#define MP_OPTR_IMR2 0x0E /* port16 ~ port23 */
#define MP_OPTR_IMR3 0x0F /* port24 ~ port31 */
/* Interrupt Poll Register */
#define MP_OPTR_IPR0 0x10 /* port0 ~ port8 */
#define MP_OPTR_IPR1 0x11 /* port8 ~ port15 */
#define MP_OPTR_IPR2 0x12 /* port16 ~ port23 */
#define MP_OPTR_IPR3 0x13 /* port24 ~ port31 */
/* General Purpose Output Control Register */
#define MP_OPTR_GPOCR 0x20
/* General Purpose Output Data Register */
#define MP_OPTR_GPODR 0x21
/* Parallel Additional Function Register */
#define MP_OPTR_PAFR 0x23
/*
* systembase 16c105x UART register
*/
#define PAGE_0 0
#define PAGE_1 1
#define PAGE_2 2
#define PAGE_3 3
#define PAGE_4 4
/*
* ******************************************************************
* * DLAB=0 =============== Page 0 Registers *
* ******************************************************************
*/
#define SB105X_RX 0 /* In: Receive buffer */
#define SB105X_TX 0 /* Out: Transmit buffer */
#define SB105X_IER 1 /* Out: Interrupt Enable Register */
#define SB105X_IER_CTSI 0x80 /* CTS# Interrupt Enable (Requires EFR[4] = 1) */
#define SB105X_IER_RTSI 0x40 /* RTS# Interrupt Enable (Requires EFR[4] = 1) */
#define SB105X_IER_XOI 0x20 /* Xoff Interrupt Enable (Requires EFR[4] = 1) */
#define SB105X_IER_SME 0x10 /* Sleep Mode Enable (Requires EFR[4] = 1) */
#define SB105X_IER_MSI 0x08 /* Enable Modem status interrupt */
#define SB105X_IER_RLSI 0x04 /* Enable receiver line status interrupt */
#define SB105X_IER_THRI 0x02 /* Enable Transmitter holding register int. */
#define SB105X_IER_RDI 0x01 /* Enable receiver data interrupt */
#define SB105X_ISR 2 /* In: Interrupt ID Register */
#define SB105X_ISR_NOINT 0x01 /* No interrupts pending */
#define SB105X_ISR_RLSI 0x06 /* Receiver line status interrupt (Priority = 1)*/
#define SB105X_ISR_RDAI 0x0c /* Receive Data Available interrupt */
#define SB105X_ISR_CTII 0x04 /* Character Timeout Indication interrupt */
#define SB105X_ISR_THRI 0x02 /* Transmitter holding register empty */
#define SB105X_ISR_MSI 0x00 /* Modem status interrupt */
#define SB105X_ISR_RXCI 0x10 /* Receive Xoff or Special Character interrupt */
#define SB105X_ISR_RCSI 0x20 /* RTS#, CTS# status interrupt during Auto RTS/CTS flow control */
#define SB105X_FCR 2 /* Out: FIFO Control Register */
#define SB105X_FCR_FEN 0x01 /* FIFO Enable */
#define SB105X_FCR_RXFR 0x02 /* RX FIFO Reset */
#define SB105X_FCR_TXFR 0x04 /* TX FIFO Reset */
#define SB105X_FCR_DMS 0x08 /* DMA Mode Select */
#define SB105X_FCR_RTR08 0x00 /* Receive Trigger Level set at 8 */
#define SB105X_FCR_RTR16 0x40 /* Receive Trigger Level set at 16 */
#define SB105X_FCR_RTR56 0x80 /* Receive Trigger Level set at 56 */
#define SB105X_FCR_RTR60 0xc0 /* Receive Trigger Level set at 60 */
#define SB105X_FCR_TTR08 0x00 /* Transmit Trigger Level set at 8 */
#define SB105X_FCR_TTR16 0x10 /* Transmit Trigger Level set at 16 */
#define SB105X_FCR_TTR32 0x20 /* Transmit Trigger Level set at 32 */
#define SB105X_FCR_TTR56 0x30 /* Transmit Trigger Level set at 56 */
#define SB105X_LCR 3 /* Out: Line Control Register */
/*
* * Note: if the word length is 5 bits (SB105X_LCR_WLEN5), then setting
* * SB105X_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
*/
#define SB105X_LCR_DLAB 0x80 /* Divisor Latch Enable */
#define SB105X_LCR_SBC 0x40 /* Break Enable*/
#define SB105X_LCR_SPAR 0x20 /* Set Stick parity */
#define SB105X_LCR_EPAR 0x10 /* Even parity select */
#define SB105X_LCR_PAREN 0x08 /* Parity Enable */
#define SB105X_LCR_STOP 0x04 /* Stop bits: 0->1 bit, 1->2 bits, 1 and SB105X_LCR_WLEN5 -> 1.5 bit */
#define SB105X_LCR_WLEN5 0x00 /* Wordlength: 5 bits */
#define SB105X_LCR_WLEN6 0x01 /* Wordlength: 6 bits */
#define SB105X_LCR_WLEN7 0x02 /* Wordlength: 7 bits */
#define SB105X_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
#define SB105X_LCR_BF 0xBF
#define SB105X_MCR 4 /* Out: Modem Control Register */
#define SB105X_MCR_CPS 0x80 /* Clock Prescaler Select */
#define SB105X_MCR_P2S 0x40 /* Page 2 Select /Xoff Re-Transmit Access Enable */
#define SB105X_MCR_XOA 0x20 /* Xon Any Enable */
#define SB105X_MCR_ILB 0x10 /* Internal Loopback Enable */
#define SB105X_MCR_OUT2 0x08 /* Out2/Interrupt Output Enable*/
#define SB105X_MCR_OUT1 0x04 /* Out1/Interrupt Output Enable */
#define SB105X_MCR_RTS 0x02 /* RTS# Output */
#define SB105X_MCR_DTR 0x01 /* DTR# Output */
#define SB105X_LSR 5 /* In: Line Status Register */
#define SB105X_LSR_RFEI 0x80 /* Receive FIFO data error Indicator */
#define SB105X_LSR_TEMI 0x40 /* THR and TSR Empty Indicator */
#define SB105X_LSR_THRE 0x20 /* THR Empty Indicator */
#define SB105X_LSR_BII 0x10 /* Break interrupt indicator */
#define SB105X_LSR_FEI 0x08 /* Frame error indicator */
#define SB105X_LSR_PEI 0x04 /* Parity error indicator */
#define SB105X_LSR_OEI 0x02 /* Overrun error indicator */
#define SB105X_LSR_RDRI 0x01 /* Receive data ready Indicator*/
#define SB105X_MSR 6 /* In: Modem Status Register */
#define SB105X_MSR_DCD 0x80 /* Data Carrier Detect */
#define SB105X_MSR_RI 0x40 /* Ring Indicator */
#define SB105X_MSR_DSR 0x20 /* Data Set Ready */
#define SB105X_MSR_CTS 0x10 /* Clear to Send */
#define SB105X_MSR_DDCD 0x08 /* Delta DCD */
#define SB105X_MSR_DRI 0x04 /* Delta ring indicator */
#define SB105X_MSR_DDSR 0x02 /* Delta DSR */
#define SB105X_MSR_DCTS 0x01 /* Delta CTS */
#define SB105XA_MDR 6 /* Out: Multi Drop mode Register */
#define SB105XA_MDR_NPS 0x08 /* 9th Bit Polarity Select */
#define SB105XA_MDR_AME 0x02 /* Auto Multi-drop Enable */
#define SB105XA_MDR_MDE 0x01 /* Multi Drop Enable */
#define SB105X_SPR 7 /* I/O: Scratch Register */
/*
* DLAB=1
*/
#define SB105X_DLL 0 /* Out: Divisor Latch Low */
#define SB105X_DLM 1 /* Out: Divisor Latch High */
/*
* ******************************************************************
* * DLAB(LCR[7]) = 0 , MCR[6] = 1 ============= Page 2 Registers *
* ******************************************************************
*/
#define SB105X_GICR 1 /* Global Interrupt Control Register */
#define SB105X_GICR_GIM 0x01 /* Global Interrupt Mask */
#define SB105X_GISR 2 /* Global Interrupt Status Register */
#define SB105X_GISR_MGICR0 0x80 /* Mirror the content of GICR[0] */
#define SB105X_GISR_CS3IS 0x08 /* SB105X of CS3# Interrupt Status */
#define SB105X_GISR_CS2IS 0x04 /* SB105X of CS2# Interrupt Status */
#define SB105X_GISR_CS1IS 0x02 /* SB105X of CS1# Interrupt Status */
#define SB105X_GISR_CS0IS 0x01 /* SB105X of CS0# Interrupt Status */
#define SB105X_TFCR 5 /* Transmit FIFO Count Register */
#define SB105X_RFCR 6 /* Receive FIFO Count Register */
#define SB105X_FSR 7 /* Flow Control Status Register */
#define SB105X_FSR_THFS 0x20 /* Transmit Hardware Flow Control Status */
#define SB105X_FSR_TSFS 0x10 /* Transmit Software Flow Control Status */
#define SB105X_FSR_RHFS 0x02 /* Receive Hardware Flow Control Status */
#define SB105X_FSR_RSFS 0x01 /* Receive Software Flow Control Status */
/*
* ******************************************************************
* * LCR = 0xBF, PSR[0] = 0 ============= Page 3 Registers *
* ******************************************************************
*/
#define SB105X_PSR 0 /* Page Select Register */
#define SB105X_PSR_P3KEY 0xA4 /* Page 3 Select Key */
#define SB105X_PSR_P4KEY 0xA5 /* Page 5 Select Key */
#define SB105X_ATR 1 /* Auto Toggle Control Register */
#define SB105X_ATR_RPS 0x80 /* RXEN Polarity Select */
#define SB105X_ATR_RCMS 0x40 /* RXEN Control Mode Select */
#define SB105X_ATR_TPS 0x20 /* TXEN Polarity Select */
#define SB105X_ATR_TCMS 0x10 /* TXEN Control Mode Select */
#define SB105X_ATR_ATDIS 0x00 /* Auto Toggle is disabled */
#define SB105X_ATR_ART 0x01 /* RTS#/TXEN pin operates as TXEN */
#define SB105X_ATR_ADT 0x02 /* DTR#/TXEN pin operates as TXEN */
#define SB105X_ATR_A80 0x03 /* only in 80 pin use */
#define SB105X_EFR 2 /* (Auto) Enhanced Feature Register */
#define SB105X_EFR_ACTS 0x80 /* Auto-CTS Flow Control Enable */
#define SB105X_EFR_ARTS 0x40 /* Auto-RTS Flow Control Enable */
#define SB105X_EFR_SCD 0x20 /* Special Character Detect */
#define SB105X_EFR_EFBEN 0x10 /* Enhanced Function Bits Enable */
#define SB105X_XON1 4 /* Xon1 Character Register */
#define SB105X_XON2 5 /* Xon2 Character Register */
#define SB105X_XOFF1 6 /* Xoff1 Character Register */
#define SB105X_XOFF2 7 /* Xoff2 Character Register */
/*
* ******************************************************************
* * LCR = 0xBF, PSR[0] = 1 ============ Page 4 Registers *
* ******************************************************************
*/
#define SB105X_AFR 1 /* Additional Feature Register */
#define SB105X_AFR_GIPS 0x20 /* Global Interrupt Polarity Select */
#define SB105X_AFR_GIEN 0x10 /* Global Interrupt Enable */
#define SB105X_AFR_AFEN 0x01 /* 256-byte FIFO Enable */
#define SB105X_XRCR 2 /* Xoff Re-transmit Count Register */
#define SB105X_XRCR_NRC1 0x00 /* Transmits Xoff Character whenever the number of received data is 1 during XOFF status */
#define SB105X_XRCR_NRC4 0x01 /* Transmits Xoff Character whenever the number of received data is 4 during XOFF status */
#define SB105X_XRCR_NRC8 0x02 /* Transmits Xoff Character whenever the number of received data is 8 during XOFF status */
#define SB105X_XRCR_NRC16 0x03 /* Transmits Xoff Character whenever the number of received data is 16 during XOFF status */
#define SB105X_TTR 4 /* Transmit FIFO Trigger Level Register */
#define SB105X_RTR 5 /* Receive FIFO Trigger Level Register */
#define SB105X_FUR 6 /* Flow Control Upper Threshold Register */
#define SB105X_FLR 7 /* Flow Control Lower Threshold Register */
/* page 0 */
#define SB105X_GET_CHAR(port) inb((port)->iobase + SB105X_RX)
#define SB105X_GET_IER(port) inb((port)->iobase + SB105X_IER)
#define SB105X_GET_ISR(port) inb((port)->iobase + SB105X_ISR)
#define SB105X_GET_LCR(port) inb((port)->iobase + SB105X_LCR)
#define SB105X_GET_MCR(port) inb((port)->iobase + SB105X_MCR)
#define SB105X_GET_LSR(port) inb((port)->iobase + SB105X_LSR)
#define SB105X_GET_MSR(port) inb((port)->iobase + SB105X_MSR)
#define SB105X_GET_SPR(port) inb((port)->iobase + SB105X_SPR)
#define SB105X_PUT_CHAR(port,v) outb((v),(port)->iobase + SB105X_TX )
#define SB105X_PUT_IER(port,v) outb((v),(port)->iobase + SB105X_IER )
#define SB105X_PUT_FCR(port,v) outb((v),(port)->iobase + SB105X_FCR )
#define SB105X_PUT_LCR(port,v) outb((v),(port)->iobase + SB105X_LCR )
#define SB105X_PUT_MCR(port,v) outb((v),(port)->iobase + SB105X_MCR )
#define SB105X_PUT_SPR(port,v) outb((v),(port)->iobase + SB105X_SPR )
/* page 1 */
#define SB105X_GET_REG(port,reg) inb((port)->iobase + (reg))
#define SB105X_PUT_REG(port,reg,v) outb((v),(port)->iobase + (reg))
/* page 2 */
#define SB105X_PUT_PSR(port,v) outb((v),(port)->iobase + SB105X_PSR )
#endif
#include "sb_pci_mp.h"
#include <linux/module.h>
#include <linux/parport.h>
extern struct parport *parport_pc_probe_port(unsigned long base_lo,
unsigned long base_hi,
int irq, int dma,
struct device *dev,
int irqflags);
static struct mp_device_t mp_devs[MAX_MP_DEV];
static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t);
static int NR_BOARD=0;
static int NR_PORTS=0;
static struct mp_port multi_ports[MAX_MP_PORT];
static struct irq_info irq_lists[NR_IRQS];
static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset);
static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value);
static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset);
static int sb1054_get_register(struct sb_uart_port *port, int page, int reg);
static int sb1054_set_register(struct sb_uart_port *port, int page, int reg, int value);
static void SendATCommand(struct mp_port *mtpt);
static int set_deep_fifo(struct sb_uart_port *port, int status);
static int get_deep_fifo(struct sb_uart_port *port);
static int get_device_type(int arg);
static int set_auto_rts(struct sb_uart_port *port, int status);
static void mp_stop(struct tty_struct *tty);
static void __mp_start(struct tty_struct *tty);
static void mp_start(struct tty_struct *tty);
static void mp_tasklet_action(unsigned long data);
static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear);
static int mp_startup(struct sb_uart_state *state, int init_hw);
static void mp_shutdown(struct sb_uart_state *state);
static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios);
static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c);
static int mp_put_char(struct tty_struct *tty, unsigned char ch);
static void mp_put_chars(struct tty_struct *tty);
static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count);
static int mp_write_room(struct tty_struct *tty);
static int mp_chars_in_buffer(struct tty_struct *tty);
static void mp_flush_buffer(struct tty_struct *tty);
static void mp_send_xchar(struct tty_struct *tty, char ch);
static void mp_throttle(struct tty_struct *tty);
static void mp_unthrottle(struct tty_struct *tty);
static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo);
static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo);
static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value);
static int mp_tiocmget(struct tty_struct *tty);
static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear);
static int mp_break_ctl(struct tty_struct *tty, int break_state);
static int mp_do_autoconfig(struct sb_uart_state *state);
static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg);
static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt);
static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios);
static void mp_close(struct tty_struct *tty, struct file *filp);
static void mp_wait_until_sent(struct tty_struct *tty, int timeout);
static void mp_hangup(struct tty_struct *tty);
static void mp_update_termios(struct sb_uart_state *state);
static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state);
static struct sb_uart_state *uart_get(struct uart_driver *drv, int line);
static int mp_open(struct tty_struct *tty, struct file *filp);
static const char *mp_type(struct sb_uart_port *port);
static void mp_change_pm(struct sb_uart_state *state, int pm_state);
static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port);
static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port);
static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state);
static int mp_register_driver(struct uart_driver *drv);
static void mp_unregister_driver(struct uart_driver *drv);
static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port);
static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port);
static void autoconfig(struct mp_port *mtpt, unsigned int probeflags);
static void autoconfig_irq(struct mp_port *mtpt);
static void multi_stop_tx(struct sb_uart_port *port);
static void multi_start_tx(struct sb_uart_port *port);
static void multi_stop_rx(struct sb_uart_port *port);
static void multi_enable_ms(struct sb_uart_port *port);
static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status );
static _INLINE_ void transmit_chars(struct mp_port *mtpt);
static _INLINE_ void check_modem_status(struct mp_port *mtpt);
static inline void multi_handle_port(struct mp_port *mtpt);
static irqreturn_t multi_interrupt(int irq, void *dev_id);
static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt);
static int serial_link_irq_chain(struct mp_port *mtpt);
static void serial_unlink_irq_chain(struct mp_port *mtpt);
static void multi_timeout(unsigned long data);
static unsigned int multi_tx_empty(struct sb_uart_port *port);
static unsigned int multi_get_mctrl(struct sb_uart_port *port);
static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl);
static void multi_break_ctl(struct sb_uart_port *port, int break_state);
static int multi_startup(struct sb_uart_port *port);
static void multi_shutdown(struct sb_uart_port *port);
static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud);
static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old);
static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate);
static void multi_release_std_resource(struct mp_port *mtpt);
static void multi_release_port(struct sb_uart_port *port);
static int multi_request_port(struct sb_uart_port *port);
static void multi_config_port(struct sb_uart_port *port, int flags);
static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser);
static const char *multi_type(struct sb_uart_port *port);
static void __init multi_init_ports(void);
static void __init multi_register_ports(struct uart_driver *drv);
static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd);
static int deep[256];
static int deep_count;
static int fcr_arr[256];
static int fcr_count;
static int ttr[256];
static int ttr_count;
static int rtr[256];
static int rtr_count;
module_param_array(deep,int,&deep_count,0);
module_param_array(fcr_arr,int,&fcr_count,0);
module_param_array(ttr,int,&ttr_count,0);
module_param_array(rtr,int,&rtr_count,0);
static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset)
{
return inb(mtpt->port.iobase + offset);
}
static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value)
{
outb(value, mtpt->port.iobase + offset);
}
static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset)
{
return inb(mtpt->option_base_addr + offset);
}
static int sb1053a_get_interface(struct mp_port *mtpt, int port_num)
{
unsigned long option_base_addr = mtpt->option_base_addr;
unsigned int interface = 0;
switch (port_num)
{
case 0:
case 1:
/* set GPO[1:0] = 00 */
outb(0x00, option_base_addr + MP_OPTR_GPODR);
break;
case 2:
case 3:
/* set GPO[1:0] = 01 */
outb(0x01, option_base_addr + MP_OPTR_GPODR);
break;
case 4:
case 5:
/* set GPO[1:0] = 10 */
outb(0x02, option_base_addr + MP_OPTR_GPODR);
break;
default:
break;
}
port_num &= 0x1;
/* get interface */
interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num);
/* set GPO[1:0] = 11 */
outb(0x03, option_base_addr + MP_OPTR_GPODR);
return (interface);
}
static int sb1054_get_register(struct sb_uart_port *port, int page, int reg)
{
int ret = 0;
unsigned int lcr = 0;
unsigned int mcr = 0;
unsigned int tmp = 0;
if( page <= 0)
{
printk(" page 0 can not use this function\n");
return -1;
}
switch(page)
{
case 1:
lcr = SB105X_GET_LCR(port);
tmp = lcr | SB105X_LCR_DLAB;
SB105X_PUT_LCR(port, tmp);
tmp = SB105X_GET_LCR(port);
ret = SB105X_GET_REG(port,reg);
SB105X_PUT_LCR(port,lcr);
break;
case 2:
mcr = SB105X_GET_MCR(port);
tmp = mcr | SB105X_MCR_P2S;
SB105X_PUT_MCR(port,tmp);
ret = SB105X_GET_REG(port,reg);
SB105X_PUT_MCR(port,mcr);
break;
case 3:
lcr = SB105X_GET_LCR(port);
tmp = lcr | SB105X_LCR_BF;
SB105X_PUT_LCR(port,tmp);
SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY);
ret = SB105X_GET_REG(port,reg);
SB105X_PUT_LCR(port,lcr);
break;
case 4:
lcr = SB105X_GET_LCR(port);
tmp = lcr | SB105X_LCR_BF;
SB105X_PUT_LCR(port,tmp);
SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY);
ret = SB105X_GET_REG(port,reg);
SB105X_PUT_LCR(port,lcr);
break;
default:
printk(" error invalid page number \n");
return -1;
}
return ret;
}
static int sb1054_set_register(struct sb_uart_port *port, int page, int reg, int value)
{
int lcr = 0;
int mcr = 0;
int ret = 0;
if( page <= 0)
{
printk(" page 0 can not use this function\n");
return -1;
}
switch(page)
{
case 1:
lcr = SB105X_GET_LCR(port);
SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB);
SB105X_PUT_REG(port,reg,value);
SB105X_PUT_LCR(port, lcr);
ret = 1;
break;
case 2:
mcr = SB105X_GET_MCR(port);
SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S);
SB105X_PUT_REG(port,reg,value);
SB105X_PUT_MCR(port, mcr);
ret = 1;
break;
case 3:
lcr = SB105X_GET_LCR(port);
SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF);
SB105X_PUT_PSR(port, SB105X_PSR_P3KEY);
SB105X_PUT_REG(port,reg,value);
SB105X_PUT_LCR(port, lcr);
ret = 1;
break;
case 4:
lcr = SB105X_GET_LCR(port);
SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF);
SB105X_PUT_PSR(port, SB105X_PSR_P4KEY);
SB105X_PUT_REG(port,reg,value);
SB105X_PUT_LCR(port, lcr);
ret = 1;
break;
default:
printk(" error invalid page number \n");
return -1;
}
return ret;
}
static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode)
{
int mdr = SB105XA_MDR_NPS;
if (mode & MDMODE_ENABLE)
{
mdr |= SB105XA_MDR_MDE;
}
if (1) //(mode & MDMODE_AUTO)
{
int efr = 0;
mdr |= SB105XA_MDR_AME;
efr = sb1054_get_register(port, PAGE_3, SB105X_EFR);
efr |= SB105X_EFR_SCD;
sb1054_set_register(port, PAGE_3, SB105X_EFR, efr);
}
sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr);
port->mdmode &= ~0x6;
port->mdmode |= mode;
printk("[%d] multidrop init: %x\n", port->line, port->mdmode);
return 0;
}
static int get_multidrop_addr(struct sb_uart_port *port)
{
return sb1054_get_register(port, PAGE_3, SB105X_XOFF2);
}
static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr)
{
sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr);
return 0;
}
static void SendATCommand(struct mp_port *mtpt)
{
// a t cr lf
unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0};
unsigned char lineControl;
unsigned char i=0;
unsigned char Divisor = 0xc;
lineControl = serial_inp(mtpt,UART_LCR);
serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB));
serial_outp(mtpt,UART_DLL,(Divisor & 0xff));
serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800
serial_outp(mtpt,UART_LCR,lineControl);
serial_outp(mtpt,UART_LCR,0x03); // N-8-1
serial_outp(mtpt,UART_FCR,7);
serial_outp(mtpt,UART_MCR,0x3);
while(ch[i]){
while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){
;
}
serial_outp(mtpt,0,ch[i++]);
}
}// end of SendATCommand()
static int set_deep_fifo(struct sb_uart_port *port, int status)
{
int afr_status = 0;
afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
if(status == ENABLE)
{
afr_status |= SB105X_AFR_AFEN;
}
else
{
afr_status &= ~SB105X_AFR_AFEN;
}
sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status);
sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]);
sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]);
afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
return afr_status;
}
static int get_device_type(int arg)
{
int ret;
ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0);
ret = (ret & 0xf0) >> 4;
switch (ret)
{
case DIR_UART_16C550:
return PORT_16C55X;
case DIR_UART_16C1050:
return PORT_16C105X;
case DIR_UART_16C1050A:
/*
if (mtpt->port.line < 2)
{
return PORT_16C105XA;
}
else
{
if (mtpt->device->device_id & 0x50)
{
return PORT_16C55X;
}
else
{
return PORT_16C105X;
}
}*/
return PORT_16C105XA;
default:
return PORT_UNKNOWN;
}
}
static int get_deep_fifo(struct sb_uart_port *port)
{
int afr_status = 0;
afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
return afr_status;
}
static int set_auto_rts(struct sb_uart_port *port, int status)
{
int atr_status = 0;
#if 0
int efr_status = 0;
efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR);
if(status == ENABLE)
efr_status |= SB105X_EFR_ARTS;
else
efr_status &= ~SB105X_EFR_ARTS;
sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status);
efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR);
#endif
//ATR
atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR);
switch(status)
{
case RS422PTP:
atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80);
break;
case RS422MD:
atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
break;
case RS485NE:
atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
break;
case RS485ECHO:
atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
break;
}
sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status);
atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR);
return atr_status;
}
static void mp_stop(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
port->ops->stop_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void __mp_start(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
}
static void mp_start(struct tty_struct *tty)
{
__mp_start(tty);
}
static void mp_tasklet_action(unsigned long data)
{
struct sb_uart_state *state = (struct sb_uart_state *)data;
struct tty_struct *tty;
printk("tasklet is called!\n");
tty = state->info->tty;
tty_wakeup(tty);
}
static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear)
{
unsigned int old;
old = port->mctrl;
port->mctrl = (old & ~clear) | set;
if (old != port->mctrl)
port->ops->set_mctrl(port, port->mctrl);
}
#define uart_set_mctrl(port,set) mp_update_mctrl(port,set,0)
#define uart_clear_mctrl(port,clear) mp_update_mctrl(port,0,clear)
static int mp_startup(struct sb_uart_state *state, int init_hw)
{
struct sb_uart_info *info = state->info;
struct sb_uart_port *port = state->port;
unsigned long page;
int retval = 0;
if (info->flags & UIF_INITIALIZED)
return 0;
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
if (port->type == PORT_UNKNOWN)
return 0;
if (!info->xmit.buf) {
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
info->xmit.buf = (unsigned char *) page;
uart_circ_clear(&info->xmit);
}
retval = port->ops->startup(port);
if (retval == 0) {
if (init_hw) {
mp_change_speed(state, NULL);
if (info->tty && (info->tty->termios.c_cflag & CBAUD))
uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
}
info->flags |= UIF_INITIALIZED;
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
retval = 0;
return retval;
}
static void mp_shutdown(struct sb_uart_state *state)
{
struct sb_uart_info *info = state->info;
struct sb_uart_port *port = state->port;
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
if (info->flags & UIF_INITIALIZED) {
info->flags &= ~UIF_INITIALIZED;
if (!info->tty || (info->tty->termios.c_cflag & HUPCL))
uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
wake_up_interruptible(&info->delta_msr_wait);
port->ops->shutdown(port);
synchronize_irq(port->irq);
}
tasklet_kill(&info->tlet);
if (info->xmit.buf) {
free_page((unsigned long)info->xmit.buf);
info->xmit.buf = NULL;
}
}
static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios)
{
struct tty_struct *tty = state->info->tty;
struct sb_uart_port *port = state->port;
if (!tty || port->type == PORT_UNKNOWN)
return;
if (tty->termios.c_cflag & CRTSCTS)
state->info->flags |= UIF_CTS_FLOW;
else
state->info->flags &= ~UIF_CTS_FLOW;
if (tty->termios.c_cflag & CLOCAL)
state->info->flags &= ~UIF_CHECK_CD;
else
state->info->flags |= UIF_CHECK_CD;
port->ops->set_termios(port, &tty->termios, old_termios);
}
static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c)
{
unsigned long flags;
int ret = 0;
if (!circ->buf)
return 0;
spin_lock_irqsave(&port->lock, flags);
if (uart_circ_chars_free(circ) != 0) {
circ->buf[circ->head] = c;
circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
ret = 1;
}
spin_unlock_irqrestore(&port->lock, flags);
return ret;
}
static int mp_put_char(struct tty_struct *tty, unsigned char ch)
{
struct sb_uart_state *state = tty->driver_data;
return __mp_put_char(state->port, &state->info->xmit, ch);
}
static void mp_put_chars(struct tty_struct *tty)
{
mp_start(tty);
}
static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port;
struct circ_buf *circ;
int c, ret = 0;
if (!state || !state->info) {
return -EL3HLT;
}
port = state->port;
circ = &state->info->xmit;
if (!circ->buf)
return 0;
while (1) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
mp_start(tty);
return ret;
}
static int mp_write_room(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
return uart_circ_chars_free(&state->info->xmit);
}
static int mp_chars_in_buffer(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
return uart_circ_chars_pending(&state->info->xmit);
}
static void mp_flush_buffer(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port;
unsigned long flags;
if (!state || !state->info) {
return;
}
port = state->port;
spin_lock_irqsave(&port->lock, flags);
uart_circ_clear(&state->info->xmit);
spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&tty->write_wait);
tty_wakeup(tty);
}
static void mp_send_xchar(struct tty_struct *tty, char ch)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
unsigned long flags;
if (port->ops->send_xchar)
port->ops->send_xchar(port, ch);
else {
port->x_char = ch;
if (ch) {
spin_lock_irqsave(&port->lock, flags);
port->ops->start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
}
}
static void mp_throttle(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
if (I_IXOFF(tty))
mp_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios.c_cflag & CRTSCTS)
uart_clear_mctrl(state->port, TIOCM_RTS);
}
static void mp_unthrottle(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
if (I_IXOFF(tty)) {
if (port->x_char)
port->x_char = 0;
else
mp_send_xchar(tty, START_CHAR(tty));
}
if (tty->termios.c_cflag & CRTSCTS)
uart_set_mctrl(port, TIOCM_RTS);
}
static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo)
{
struct sb_uart_port *port = state->port;
struct serial_struct tmp;
memset(&tmp, 0, sizeof(tmp));
tmp.type = port->type;
tmp.line = port->line;
tmp.port = port->iobase;
if (HIGH_BITS_OFFSET)
tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET;
tmp.irq = port->irq;
tmp.flags = port->flags;
tmp.xmit_fifo_size = port->fifosize;
tmp.baud_base = port->uartclk / 16;
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
state->closing_wait;
tmp.custom_divisor = port->custom_divisor;
tmp.hub6 = port->hub6;
tmp.io_type = port->iotype;
tmp.iomem_reg_shift = port->regshift;
tmp.iomem_base = (void *)port->mapbase;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo)
{
struct serial_struct new_serial;
struct sb_uart_port *port = state->port;
unsigned long new_port;
unsigned int change_irq, change_port, closing_wait;
unsigned int old_custom_divisor;
unsigned int old_flags, new_flags;
int retval = 0;
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
return -EFAULT;
new_port = new_serial.port;
if (HIGH_BITS_OFFSET)
new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
new_serial.irq = irq_canonicalize(new_serial.irq);
closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
USF_CLOSING_WAIT_NONE : new_serial.closing_wait;
MP_STATE_LOCK(state);
change_irq = new_serial.irq != port->irq;
change_port = new_port != port->iobase ||
(unsigned long)new_serial.iomem_base != port->mapbase ||
new_serial.hub6 != port->hub6 ||
new_serial.io_type != port->iotype ||
new_serial.iomem_reg_shift != port->regshift ||
new_serial.type != port->type;
old_flags = port->flags;
new_flags = new_serial.flags;
old_custom_divisor = port->custom_divisor;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EPERM;
if (change_irq || change_port ||
(new_serial.baud_base != port->uartclk / 16) ||
(new_serial.close_delay != state->close_delay) ||
(closing_wait != state->closing_wait) ||
(new_serial.xmit_fifo_size != port->fifosize) ||
(((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))
goto exit;
port->flags = ((port->flags & ~UPF_USR_MASK) |
(new_flags & UPF_USR_MASK));
port->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
if (port->ops->verify_port)
retval = port->ops->verify_port(port, &new_serial);
if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
(new_serial.baud_base < 9600))
retval = -EINVAL;
if (retval)
goto exit;
if (change_port || change_irq) {
retval = -EBUSY;
if (uart_users(state) > 1)
goto exit;
mp_shutdown(state);
}
if (change_port) {
unsigned long old_iobase, old_mapbase;
unsigned int old_type, old_iotype, old_hub6, old_shift;
old_iobase = port->iobase;
old_mapbase = port->mapbase;
old_type = port->type;
old_hub6 = port->hub6;
old_iotype = port->iotype;
old_shift = port->regshift;
if (old_type != PORT_UNKNOWN)
port->ops->release_port(port);
port->iobase = new_port;
port->type = new_serial.type;
port->hub6 = new_serial.hub6;
port->iotype = new_serial.io_type;
port->regshift = new_serial.iomem_reg_shift;
port->mapbase = (unsigned long)new_serial.iomem_base;
if (port->type != PORT_UNKNOWN) {
retval = port->ops->request_port(port);
} else {
retval = 0;
}
if (retval && old_type != PORT_UNKNOWN) {
port->iobase = old_iobase;
port->type = old_type;
port->hub6 = old_hub6;
port->iotype = old_iotype;
port->regshift = old_shift;
port->mapbase = old_mapbase;
retval = port->ops->request_port(port);
if (retval)
port->type = PORT_UNKNOWN;
retval = -EBUSY;
}
}
port->irq = new_serial.irq;
port->uartclk = new_serial.baud_base * 16;
port->flags = (port->flags & ~UPF_CHANGE_MASK) |
(new_flags & UPF_CHANGE_MASK);
port->custom_divisor = new_serial.custom_divisor;
state->close_delay = new_serial.close_delay;
state->closing_wait = closing_wait;
port->fifosize = new_serial.xmit_fifo_size;
if (state->info->tty)
state->info->tty->low_latency =
(port->flags & UPF_LOW_LATENCY) ? 1 : 0;
check_and_exit:
retval = 0;
if (port->type == PORT_UNKNOWN)
goto exit;
if (state->info->flags & UIF_INITIALIZED) {
if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
old_custom_divisor != port->custom_divisor) {
if (port->flags & UPF_SPD_MASK) {
printk(KERN_NOTICE
"%s sets custom speed on ttyMP%d. This "
"is deprecated.\n", current->comm,
port->line);
}
mp_change_speed(state, NULL);
}
} else
retval = mp_startup(state, 1);
exit:
MP_STATE_UNLOCK(state);
return retval;
}
static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value)
{
struct sb_uart_port *port = state->port;
unsigned int result;
result = port->ops->tx_empty(port);
if (port->x_char ||
((uart_circ_chars_pending(&state->info->xmit) > 0) &&
!state->info->tty->stopped && !state->info->tty->hw_stopped))
result &= ~TIOCSER_TEMT;
return put_user(result, value);
}
static int mp_tiocmget(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
int result = -EIO;
MP_STATE_LOCK(state);
if (!(tty->flags & (1 << TTY_IO_ERROR))) {
result = port->mctrl;
spin_lock_irq(&port->lock);
result |= port->ops->get_mctrl(port);
spin_unlock_irq(&port->lock);
}
MP_STATE_UNLOCK(state);
return result;
}
static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
int ret = -EIO;
MP_STATE_LOCK(state);
if (!(tty->flags & (1 << TTY_IO_ERROR))) {
mp_update_mctrl(port, set, clear);
ret = 0;
}
MP_STATE_UNLOCK(state);
return ret;
}
static int mp_break_ctl(struct tty_struct *tty, int break_state)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
MP_STATE_LOCK(state);
if (port->type != PORT_UNKNOWN)
port->ops->break_ctl(port, break_state);
MP_STATE_UNLOCK(state);
return 0;
}
static int mp_do_autoconfig(struct sb_uart_state *state)
{
struct sb_uart_port *port = state->port;
int flags, ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (mutex_lock_interruptible(&state->mutex))
return -ERESTARTSYS;
ret = -EBUSY;
if (uart_users(state) == 1) {
mp_shutdown(state);
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
flags = UART_CONFIG_TYPE;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
port->ops->config_port(port, flags);
ret = mp_startup(state, 1);
}
MP_STATE_UNLOCK(state);
return ret;
}
static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg)
{
struct sb_uart_port *port = state->port;
DECLARE_WAITQUEUE(wait, current);
struct sb_uart_icount cprev, cnow;
int ret;
spin_lock_irq(&port->lock);
memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount));
port->ops->enable_ms(port);
spin_unlock_irq(&port->lock);
add_wait_queue(&state->info->delta_msr_wait, &wait);
for (;;) {
spin_lock_irq(&port->lock);
memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount));
spin_unlock_irq(&port->lock);
set_current_state(TASK_INTERRUPTIBLE);
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
ret = 0;
break;
}
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
cprev = cnow;
}
current->state = TASK_RUNNING;
remove_wait_queue(&state->info->delta_msr_wait, &wait);
return ret;
}
static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt)
{
struct serial_icounter_struct icount = {};
struct sb_uart_icount cnow;
struct sb_uart_port *port = state->port;
spin_lock_irq(&port->lock);
memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount));
spin_unlock_irq(&port->lock);
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
}
static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
struct sb_uart_state *state = tty->driver_data;
struct mp_port *info = (struct mp_port *)state->port;
int ret = -ENOIOCTLCMD;
switch (cmd) {
case TIOCSMULTIDROP:
/* set multi-drop mode enable or disable, and default operation mode is H/W mode */
if (info->port.type == PORT_16C105XA)
{
//arg &= ~0x6;
//state->port->mdmode = 0;
return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg);
}
ret = -ENOTSUPP;
break;
case GETDEEPFIFO:
ret = get_deep_fifo(state->port);
return ret;
case SETDEEPFIFO:
ret = set_deep_fifo(state->port,arg);
deep[state->port->line] = arg;
return ret;
case SETTTR:
if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg);
ttr[state->port->line] = arg;
}
return ret;
case SETRTR:
if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg);
rtr[state->port->line] = arg;
}
return ret;
case GETTTR:
if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR);
}
return ret;
case GETRTR:
if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR);
}
return ret;
case SETFCR:
if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg);
}
else{
serial_out(info,2,arg);
}
return ret;
case TIOCSMDADDR:
/* set multi-drop address */
if (info->port.type == PORT_16C105XA)
{
state->port->mdmode |= MDMODE_ADDR;
return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg);
}
ret = -ENOTSUPP;
break;
case TIOCGMDADDR:
/* set multi-drop address */
if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR))
{
return get_multidrop_addr((struct sb_uart_port *)info);
}
ret = -ENOTSUPP;
break;
case TIOCSENDADDR:
/* send address in multi-drop mode */
if ((info->port.type == PORT_16C105XA)
&& (state->port->mdmode & (MDMODE_ENABLE)))
{
if (mp_chars_in_buffer(tty) > 0)
{
tty_wait_until_sent(tty, 0);
}
//while ((serial_in(info, UART_LSR) & 0x60) != 0x60);
//while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0);
while ((serial_in(info, UART_LSR) & 0x60) != 0x60);
serial_out(info, UART_SCR, (int)arg);
}
break;
case TIOCGSERIAL:
ret = mp_get_info(state, (struct serial_struct *)arg);
break;
case TIOCSSERIAL:
ret = mp_set_info(state, (struct serial_struct *)arg);
break;
case TIOCSERCONFIG:
ret = mp_do_autoconfig(state);
break;
case TIOCSERGWILD: /* obsolete */
case TIOCSERSWILD: /* obsolete */
ret = 0;
break;
/* for Multiport */
case TIOCGNUMOFPORT: /* Get number of ports */
return NR_PORTS;
case TIOCGGETDEVID:
return mp_devs[arg].device_id;
case TIOCGGETREV:
return mp_devs[arg].revision;
case TIOCGGETNRPORTS:
return mp_devs[arg].nr_ports;
case TIOCGGETBDNO:
return NR_BOARD;
case TIOCGGETINTERFACE:
if (mp_devs[arg].revision == 0xc0)
{
/* for SB16C1053APCI */
return (sb1053a_get_interface(info, info->port.line));
}
else
{
return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8)));
}
case TIOCGGETPORTTYPE:
ret = get_device_type(arg);
return ret;
case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/
outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 ,
info->interface_config_addr);
return 0;
case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */
outb( ( inb(info->interface_config_addr) & ~0x03 ) ,
info->interface_config_addr);
return 0;
}
if (ret != -ENOIOCTLCMD)
goto out;
if (tty->flags & (1 << TTY_IO_ERROR)) {
ret = -EIO;
goto out;
}
switch (cmd) {
case TIOCMIWAIT:
ret = mp_wait_modem_status(state, arg);
break;
case TIOCGICOUNT:
ret = mp_get_count(state, (struct serial_icounter_struct *)arg);
break;
}
if (ret != -ENOIOCTLCMD)
goto out;
MP_STATE_LOCK(state);
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
ret = mp_get_lsr_info(state, (unsigned int *)arg);
break;
default: {
struct sb_uart_port *port = state->port;
if (port->ops->ioctl)
ret = port->ops->ioctl(port, cmd, arg);
break;
}
}
MP_STATE_UNLOCK(state);
out:
return ret;
}
static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios)
{
struct sb_uart_state *state = tty->driver_data;
unsigned long flags;
unsigned int cflag = tty->termios.c_cflag;
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
if ((cflag ^ old_termios->c_cflag) == 0 &&
RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0)
return;
mp_change_speed(state, old_termios);
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
uart_set_mctrl(state->port, mask);
}
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
spin_lock_irqsave(&state->port->lock, flags);
tty->hw_stopped = 0;
__mp_start(tty);
spin_unlock_irqrestore(&state->port->lock, flags);
}
if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
spin_lock_irqsave(&state->port->lock, flags);
if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {
tty->hw_stopped = 1;
state->port->ops->stop_tx(state->port);
}
spin_unlock_irqrestore(&state->port->lock, flags);
}
}
static void mp_close(struct tty_struct *tty, struct file *filp)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port;
printk("mp_close!\n");
if (!state || !state->port)
return;
port = state->port;
printk("close1 %d\n", __LINE__);
MP_STATE_LOCK(state);
printk("close2 %d\n", __LINE__);
if (tty_hung_up_p(filp))
goto done;
printk("close3 %d\n", __LINE__);
if ((tty->count == 1) && (state->count != 1)) {
printk("mp_close: bad serial port count; tty->count is 1, "
"state->count is %d\n", state->count);
state->count = 1;
}
printk("close4 %d\n", __LINE__);
if (--state->count < 0) {
printk("rs_close: bad serial port count for ttyMP%d: %d\n",
port->line, state->count);
state->count = 0;
}
if (state->count)
goto done;
tty->closing = 1;
printk("close5 %d\n", __LINE__);
if (state->closing_wait != USF_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, state->closing_wait);
printk("close6 %d\n", __LINE__);
if (state->info->flags & UIF_INITIALIZED) {
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
port->ops->stop_rx(port);
spin_unlock_irqrestore(&port->lock, flags);
mp_wait_until_sent(tty, port->timeout);
}
printk("close7 %d\n", __LINE__);
mp_shutdown(state);
printk("close8 %d\n", __LINE__);
mp_flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
state->info->tty = NULL;
if (state->info->blocked_open)
{
if (state->close_delay)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(state->close_delay);
}
}
else
{
mp_change_pm(state, 3);
}
printk("close8 %d\n", __LINE__);
state->info->flags &= ~UIF_NORMAL_ACTIVE;
wake_up_interruptible(&state->info->open_wait);
done:
printk("close done\n");
MP_STATE_UNLOCK(state);
module_put(THIS_MODULE);
}
static void mp_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct sb_uart_state *state = tty->driver_data;
struct sb_uart_port *port = state->port;
unsigned long char_time, expire;
if (port->type == PORT_UNKNOWN || port->fifosize == 0)
return;
char_time = (port->timeout - HZ/50) / port->fifosize;
char_time = char_time / 5;
if (char_time == 0)
char_time = 1;
if (timeout && timeout < char_time)
char_time = timeout;
if (timeout == 0 || timeout > 2 * port->timeout)
timeout = 2 * port->timeout;
expire = jiffies + timeout;
while (!port->ops->tx_empty(port)) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (time_after(jiffies, expire))
break;
}
set_current_state(TASK_RUNNING); /* might not be needed */
}
static void mp_hangup(struct tty_struct *tty)
{
struct sb_uart_state *state = tty->driver_data;
MP_STATE_LOCK(state);
if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) {
mp_flush_buffer(tty);
mp_shutdown(state);
state->count = 0;
state->info->flags &= ~UIF_NORMAL_ACTIVE;
state->info->tty = NULL;
wake_up_interruptible(&state->info->open_wait);
wake_up_interruptible(&state->info->delta_msr_wait);
}
MP_STATE_UNLOCK(state);
}
static void mp_update_termios(struct sb_uart_state *state)
{
struct tty_struct *tty = state->info->tty;
struct sb_uart_port *port = state->port;
if (!(tty->flags & (1 << TTY_IO_ERROR))) {
mp_change_speed(state, NULL);
if (tty->termios.c_cflag & CBAUD)
uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
}
static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state)
{
DECLARE_WAITQUEUE(wait, current);
struct sb_uart_info *info = state->info;
struct sb_uart_port *port = state->port;
unsigned int mctrl;
info->blocked_open++;
state->count--;
add_wait_queue(&info->open_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) || info->tty == NULL)
break;
if (!(info->flags & UIF_INITIALIZED))
break;
if ((filp->f_flags & O_NONBLOCK) ||
(info->tty->termios.c_cflag & CLOCAL) ||
(info->tty->flags & (1 << TTY_IO_ERROR))) {
break;
}
if (info->tty->termios.c_cflag & CBAUD)
uart_set_mctrl(port, TIOCM_DTR);
spin_lock_irq(&port->lock);
port->ops->enable_ms(port);
mctrl = port->ops->get_mctrl(port);
spin_unlock_irq(&port->lock);
if (mctrl & TIOCM_CAR)
break;
MP_STATE_UNLOCK(state);
schedule();
MP_STATE_LOCK(state);
if (signal_pending(current))
break;
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
state->count++;
info->blocked_open--;
if (signal_pending(current))
return -ERESTARTSYS;
if (!info->tty || tty_hung_up_p(filp))
return -EAGAIN;
return 0;
}
static struct sb_uart_state *uart_get(struct uart_driver *drv, int line)
{
struct sb_uart_state *state;
MP_MUTEX_LOCK(mp_mutex);
state = drv->state + line;
if (mutex_lock_interruptible(&state->mutex)) {
state = ERR_PTR(-ERESTARTSYS);
goto out;
}
state->count++;
if (!state->port) {
state->count--;
MP_STATE_UNLOCK(state);
state = ERR_PTR(-ENXIO);
goto out;
}
if (!state->info) {
state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL);
if (state->info) {
memset(state->info, 0, sizeof(struct sb_uart_info));
init_waitqueue_head(&state->info->open_wait);
init_waitqueue_head(&state->info->delta_msr_wait);
state->port->info = state->info;
tasklet_init(&state->info->tlet, mp_tasklet_action,
(unsigned long)state);
} else {
state->count--;
MP_STATE_UNLOCK(state);
state = ERR_PTR(-ENOMEM);
}
}
out:
MP_MUTEX_UNLOCK(mp_mutex);
return state;
}
static int mp_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
struct sb_uart_state *state;
int retval;
int line = tty->index;
struct mp_port *mtpt;
retval = -ENODEV;
if (line >= tty->driver->num)
goto fail;
state = uart_get(drv, line);
if (IS_ERR(state)) {
retval = PTR_ERR(state);
goto fail;
}
mtpt = (struct mp_port *)state->port;
tty->driver_data = state;
tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
state->info->tty = tty;
if (tty_hung_up_p(filp)) {
retval = -EAGAIN;
state->count--;
MP_STATE_UNLOCK(state);
goto fail;
}
if (state->count == 1)
mp_change_pm(state, 0);
retval = mp_startup(state, 0);
if (retval == 0)
retval = mp_block_til_ready(filp, state);
MP_STATE_UNLOCK(state);
if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {
state->info->flags |= UIF_NORMAL_ACTIVE;
mp_update_termios(state);
}
uart_clear_mctrl(state->port, TIOCM_RTS);
try_module_get(THIS_MODULE);
fail:
return retval;
}
static const char *mp_type(struct sb_uart_port *port)
{
const char *str = NULL;
if (port->ops->type)
str = port->ops->type(port);
if (!str)
str = "unknown";
return str;
}
static void mp_change_pm(struct sb_uart_state *state, int pm_state)
{
struct sb_uart_port *port = state->port;
if (port->ops->pm)
port->ops->pm(port, pm_state, state->pm_state);
state->pm_state = pm_state;
}
static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port)
{
char address[64];
switch (port->iotype) {
case UPIO_PORT:
snprintf(address, sizeof(address),"I/O 0x%x", port->iobase);
break;
case UPIO_HUB6:
snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6);
break;
case UPIO_MEM:
snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase);
break;
default:
snprintf(address, sizeof(address),"*unknown*" );
strlcpy(address, "*unknown*", sizeof(address));
break;
}
printk( "%s%d at %s (irq = %d) is a %s\n",
drv->dev_name, port->line, address, port->irq, mp_type(port));
}
static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port)
{
unsigned int flags;
if (!port->iobase && !port->mapbase && !port->membase)
{
DPRINTK("%s error \n",__FUNCTION__);
return;
}
flags = UART_CONFIG_TYPE;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
if (port->flags & UPF_BOOT_AUTOCONF) {
port->type = PORT_UNKNOWN;
port->ops->config_port(port, flags);
}
if (port->type != PORT_UNKNOWN) {
unsigned long flags;
mp_report_port(drv, port);
spin_lock_irqsave(&port->lock, flags);
port->ops->set_mctrl(port, 0);
spin_unlock_irqrestore(&port->lock, flags);
mp_change_pm(state, 3);
}
}
static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state)
{
struct sb_uart_port *port = state->port;
struct sb_uart_info *info = state->info;
if (info && info->tty)
tty_hangup(info->tty);
MP_STATE_LOCK(state);
state->info = NULL;
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port);
port->type = PORT_UNKNOWN;
if (info) {
tasklet_kill(&info->tlet);
kfree(info);
}
MP_STATE_UNLOCK(state);
}
static struct tty_operations mp_ops = {
.open = mp_open,
.close = mp_close,
.write = mp_write,
.put_char = mp_put_char,
.flush_chars = mp_put_chars,
.write_room = mp_write_room,
.chars_in_buffer= mp_chars_in_buffer,
.flush_buffer = mp_flush_buffer,
.ioctl = mp_ioctl,
.throttle = mp_throttle,
.unthrottle = mp_unthrottle,
.send_xchar = mp_send_xchar,
.set_termios = mp_set_termios,
.stop = mp_stop,
.start = mp_start,
.hangup = mp_hangup,
.break_ctl = mp_break_ctl,
.wait_until_sent= mp_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = NULL,
#endif
.tiocmget = mp_tiocmget,
.tiocmset = mp_tiocmset,
};
static int mp_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
int i, retval;
drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL);
retval = -ENOMEM;
if (!drv->state)
{
printk("SB PCI Error: Kernel memory allocation error!\n");
goto out;
}
memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr);
normal = alloc_tty_driver(drv->nr);
if (!normal)
{
printk("SB PCI Error: tty allocation error!\n");
goto out;
}
drv->tty_driver = normal;
normal->owner = drv->owner;
normal->magic = TTY_DRIVER_MAGIC;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->num = MAX_MP_PORT ;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &mp_ops);
for (i = 0; i < drv->nr; i++) {
struct sb_uart_state *state = drv->state + i;
state->close_delay = 500;
state->closing_wait = 30000;
mutex_init(&state->mutex);
}
retval = tty_register_driver(normal);
out:
if (retval < 0) {
printk("Register tty driver Fail!\n");
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
void mp_unregister_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
normal = drv->tty_driver;
if (!normal)
{
return;
}
tty_unregister_driver(normal);
put_tty_driver(normal);
drv->tty_driver = NULL;
kfree(drv->state);
}
static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port)
{
struct sb_uart_state *state;
int ret = 0;
if (port->line >= drv->nr)
return -EINVAL;
state = drv->state + port->line;
MP_MUTEX_LOCK(mp_mutex);
if (state->port) {
ret = -EINVAL;
goto out;
}
state->port = port;
spin_lock_init(&port->lock);
port->cons = drv->cons;
port->info = state->info;
mp_configure_port(drv, state, port);
tty_register_device(drv->tty_driver, port->line, port->dev);
out:
MP_MUTEX_UNLOCK(mp_mutex);
return ret;
}
static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port)
{
struct sb_uart_state *state = drv->state + port->line;
if (state->port != port)
printk(KERN_ALERT "Removing wrong port: %p != %p\n",
state->port, port);
MP_MUTEX_LOCK(mp_mutex);
tty_unregister_device(drv->tty_driver, port->line);
mp_unconfigure_port(drv, state);
state->port = NULL;
MP_MUTEX_UNLOCK(mp_mutex);
return 0;
}
static void autoconfig(struct mp_port *mtpt, unsigned int probeflags)
{
unsigned char status1, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr;
unsigned long flags;
unsigned char u_type;
unsigned char b_ret = 0;
if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase)
return;
DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ",
mtpt->port.line, mtpt->port.iobase, mtpt->port.membase);
spin_lock_irqsave(&mtpt->port.lock, flags);
if (!(mtpt->port.flags & UPF_BUGGY_UART)) {
scratch = serial_inp(mtpt, UART_IER);
serial_outp(mtpt, UART_IER, 0);
#ifdef __i386__
outb(0xff, 0x080);
#endif
scratch2 = serial_inp(mtpt, UART_IER) & 0x0f;
serial_outp(mtpt, UART_IER, 0x0F);
#ifdef __i386__
outb(0, 0x080);
#endif
scratch3 = serial_inp(mtpt, UART_IER) & 0x0F;
serial_outp(mtpt, UART_IER, scratch);
if (scratch2 != 0 || scratch3 != 0x0F) {
DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
scratch2, scratch3);
goto out;
}
}
save_mcr = serial_in(mtpt, UART_MCR);
save_lcr = serial_in(mtpt, UART_LCR);
if (!(mtpt->port.flags & UPF_SKIP_TEST)) {
serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(mtpt, UART_MSR) & 0xF0;
serial_outp(mtpt, UART_MCR, save_mcr);
if (status1 != 0x90) {
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
status1);
goto out;
}
}
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR, 0);
serial_outp(mtpt, UART_LCR, 0);
serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(mtpt, UART_IIR) >> 6;
DEBUG_AUTOCONF("iir=%d ", scratch);
if(mtpt->device->nr_ports >= 8)
b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8)));
else
b_ret = read_option_register(mtpt,MP_OPTR_DIR0);
u_type = (b_ret & 0xf0) >> 4;
if(mtpt->port.type == PORT_UNKNOWN )
{
switch (u_type)
{
case DIR_UART_16C550:
mtpt->port.type = PORT_16C55X;
break;
case DIR_UART_16C1050:
mtpt->port.type = PORT_16C105X;
break;
case DIR_UART_16C1050A:
if (mtpt->port.line < 2)
{
mtpt->port.type = PORT_16C105XA;
}
else
{
if (mtpt->device->device_id & 0x50)
{
mtpt->port.type = PORT_16C55X;
}
else
{
mtpt->port.type = PORT_16C105X;
}
}
break;
default:
mtpt->port.type = PORT_UNKNOWN;
break;
}
}
if(mtpt->port.type == PORT_UNKNOWN )
{
printk("unknow2\n");
switch (scratch) {
case 0:
case 1:
mtpt->port.type = PORT_UNKNOWN;
break;
case 2:
case 3:
mtpt->port.type = PORT_16C55X;
break;
}
}
serial_outp(mtpt, UART_LCR, save_lcr);
mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size;
mtpt->capabilities = uart_config[mtpt->port.type].flags;
if (mtpt->port.type == PORT_UNKNOWN)
goto out;
serial_outp(mtpt, UART_MCR, save_mcr);
serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
serial_outp(mtpt, UART_FCR, 0);
(void)serial_in(mtpt, UART_RX);
serial_outp(mtpt, UART_IER, 0);
out:
spin_unlock_irqrestore(&mtpt->port.lock, flags);
DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name);
}
static void autoconfig_irq(struct mp_port *mtpt)
{
unsigned char save_mcr, save_ier;
unsigned long irqs;
int irq;
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial_inp(mtpt, UART_MCR);
save_ier = serial_inp(mtpt, UART_IER);
serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
serial_outp(mtpt, UART_MCR, 0);
serial_outp(mtpt, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(mtpt, UART_IER, 0x0f); /* enable all intrs */
(void)serial_inp(mtpt, UART_LSR);
(void)serial_inp(mtpt, UART_RX);
(void)serial_inp(mtpt, UART_IIR);
(void)serial_inp(mtpt, UART_MSR);
serial_outp(mtpt, UART_TX, 0xFF);
irq = probe_irq_off(irqs);
serial_outp(mtpt, UART_MCR, save_mcr);
serial_outp(mtpt, UART_IER, save_ier);
mtpt->port.irq = (irq > 0) ? irq : 0;
}
static void multi_stop_tx(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
if (mtpt->ier & UART_IER_THRI) {
mtpt->ier &= ~UART_IER_THRI;
serial_out(mtpt, UART_IER, mtpt->ier);
}
tasklet_schedule(&port->info->tlet);
}
static void multi_start_tx(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
if (!(mtpt->ier & UART_IER_THRI)) {
mtpt->ier |= UART_IER_THRI;
serial_out(mtpt, UART_IER, mtpt->ier);
}
}
static void multi_stop_rx(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
mtpt->ier &= ~UART_IER_RLSI;
mtpt->port.read_status_mask &= ~UART_LSR_DR;
serial_out(mtpt, UART_IER, mtpt->ier);
}
static void multi_enable_ms(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
mtpt->ier |= UART_IER_MSI;
serial_out(mtpt, UART_IER, mtpt->ier);
}
static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status )
{
struct tty_struct *tty = mtpt->port.info->tty;
unsigned char lsr = *status;
int max_count = 256;
unsigned char ch;
char flag;
//lsr &= mtpt->port.read_status_mask;
do {
if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE))
{
ch = serial_inp(mtpt, UART_RX);
}
else if (lsr & UART_LSR_SPECIAL)
{
flag = 0;
ch = serial_inp(mtpt, UART_RX);
if (lsr & UART_LSR_BI)
{
mtpt->port.icount.brk++;
flag = TTY_BREAK;
if (sb_uart_handle_break(&mtpt->port))
goto ignore_char;
}
if (lsr & UART_LSR_PE)
{
mtpt->port.icount.parity++;
flag = TTY_PARITY;
}
if (lsr & UART_LSR_FE)
{
mtpt->port.icount.frame++;
flag = TTY_FRAME;
}
if (lsr & UART_LSR_OE)
{
mtpt->port.icount.overrun++;
flag = TTY_OVERRUN;
}
tty_insert_flip_char(tty, ch, flag);
}
else
{
ch = serial_inp(mtpt, UART_RX);
tty_insert_flip_char(tty, ch, 0);
}
ignore_char:
lsr = serial_inp(mtpt, UART_LSR);
} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
tty_flip_buffer_push(tty);
}
static _INLINE_ void transmit_chars(struct mp_port *mtpt)
{
struct circ_buf *xmit = &mtpt->port.info->xmit;
int count;
if (mtpt->port.x_char) {
serial_outp(mtpt, UART_TX, mtpt->port.x_char);
mtpt->port.icount.tx++;
mtpt->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) {
multi_stop_tx(&mtpt->port);
return;
}
count = uart_circ_chars_pending(xmit);
if(count > mtpt->port.fifosize)
{
count = mtpt->port.fifosize;
}
printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode);
do {
#if 0
/* check multi-drop mode */
if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR))
{
printk("send address\n");
/* send multi-drop address */
serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]);
}
else
#endif
{
serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]);
}
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
mtpt->port.icount.tx++;
} while (--count > 0);
}
static _INLINE_ void check_modem_status(struct mp_port *mtpt)
{
int status;
status = serial_in(mtpt, UART_MSR);
if ((status & UART_MSR_ANY_DELTA) == 0)
return;
if (status & UART_MSR_TERI)
mtpt->port.icount.rng++;
if (status & UART_MSR_DDSR)
mtpt->port.icount.dsr++;
if (status & UART_MSR_DDCD)
sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD);
if (status & UART_MSR_DCTS)
sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS);
wake_up_interruptible(&mtpt->port.info->delta_msr_wait);
}
static inline void multi_handle_port(struct mp_port *mtpt)
{
unsigned int status = serial_inp(mtpt, UART_LSR);
//printk("lsr: %x\n", status);
if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL))
receive_chars(mtpt, &status);
check_modem_status(mtpt);
if (status & UART_LSR_THRE)
{
if ((mtpt->port.type == PORT_16C105X)
|| (mtpt->port.type == PORT_16C105XA))
transmit_chars(mtpt);
else
{
if (mtpt->interface >= RS485NE)
uart_set_mctrl(&mtpt->port, TIOCM_RTS);
transmit_chars(mtpt);
if (mtpt->interface >= RS485NE)
{
while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60);
uart_clear_mctrl(&mtpt->port, TIOCM_RTS);
}
}
}
}
static irqreturn_t multi_interrupt(int irq, void *dev_id)
{
struct irq_info *iinfo = dev_id;
struct list_head *lhead, *end = NULL;
int pass_counter = 0;
spin_lock(&iinfo->lock);
lhead = iinfo->head;
do {
struct mp_port *mtpt;
unsigned int iir;
mtpt = list_entry(lhead, struct mp_port, list);
iir = serial_in(mtpt, UART_IIR);
printk("interrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee
if (!(iir & UART_IIR_NO_INT))
{
printk("interrupt handle\n");
spin_lock(&mtpt->port.lock);
multi_handle_port(mtpt);
spin_unlock(&mtpt->port.lock);
end = NULL;
} else if (end == NULL)
end = lhead;
lhead = lhead->next;
if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT)
{
printk(KERN_ERR "multi: too much work for "
"irq%d\n", irq);
printk( "multi: too much work for "
"irq%d\n", irq);
break;
}
} while (lhead != end);
spin_unlock(&iinfo->lock);
return IRQ_HANDLED;
}
static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt)
{
spin_lock_irq(&i->lock);
if (!list_empty(i->head)) {
if (i->head == &mtpt->list)
i->head = i->head->next;
list_del(&mtpt->list);
} else {
i->head = NULL;
}
spin_unlock_irq(&i->lock);
}
static int serial_link_irq_chain(struct mp_port *mtpt)
{
struct irq_info *i = irq_lists + mtpt->port.irq;
int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
spin_lock_irq(&i->lock);
if (i->head) {
list_add(&mtpt->list, i->head);
spin_unlock_irq(&i->lock);
ret = 0;
} else {
INIT_LIST_HEAD(&mtpt->list);
i->head = &mtpt->list;
spin_unlock_irq(&i->lock);
ret = request_irq(mtpt->port.irq, multi_interrupt,
irq_flags, "serial", i);
if (ret < 0)
serial_do_unlink(i, mtpt);
}
return ret;
}
static void serial_unlink_irq_chain(struct mp_port *mtpt)
{
struct irq_info *i = irq_lists + mtpt->port.irq;
if (list_empty(i->head))
{
free_irq(mtpt->port.irq, i);
}
serial_do_unlink(i, mtpt);
}
static void multi_timeout(unsigned long data)
{
struct mp_port *mtpt = (struct mp_port *)data;
spin_lock(&mtpt->port.lock);
multi_handle_port(mtpt);
spin_unlock(&mtpt->port.lock);
mod_timer(&mtpt->timer, jiffies+1 );
}
static unsigned int multi_tx_empty(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(&mtpt->port.lock, flags);
ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
spin_unlock_irqrestore(&mtpt->port.lock, flags);
return ret;
}
static unsigned int multi_get_mctrl(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned char status;
unsigned int ret;
status = serial_in(mtpt, UART_MSR);
ret = 0;
if (status & UART_MSR_DCD)
ret |= TIOCM_CAR;
if (status & UART_MSR_RI)
ret |= TIOCM_RNG;
if (status & UART_MSR_DSR)
ret |= TIOCM_DSR;
if (status & UART_MSR_CTS)
ret |= TIOCM_CTS;
return ret;
}
static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned char mcr = 0;
mctrl &= 0xff;
if (mctrl & TIOCM_RTS)
mcr |= UART_MCR_RTS;
if (mctrl & TIOCM_DTR)
mcr |= UART_MCR_DTR;
if (mctrl & TIOCM_OUT1)
mcr |= UART_MCR_OUT1;
if (mctrl & TIOCM_OUT2)
mcr |= UART_MCR_OUT2;
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;
serial_out(mtpt, UART_MCR, mcr);
}
static void multi_break_ctl(struct sb_uart_port *port, int break_state)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned long flags;
spin_lock_irqsave(&mtpt->port.lock, flags);
if (break_state == -1)
mtpt->lcr |= UART_LCR_SBC;
else
mtpt->lcr &= ~UART_LCR_SBC;
serial_out(mtpt, UART_LCR, mtpt->lcr);
spin_unlock_irqrestore(&mtpt->port.lock, flags);
}
static int multi_startup(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned long flags;
int retval;
mtpt->capabilities = uart_config[mtpt->port.type].flags;
mtpt->mcr = 0;
if (mtpt->capabilities & UART_CLEAR_FIFO) {
serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
serial_outp(mtpt, UART_FCR, 0);
}
(void) serial_inp(mtpt, UART_LSR);
(void) serial_inp(mtpt, UART_RX);
(void) serial_inp(mtpt, UART_IIR);
(void) serial_inp(mtpt, UART_MSR);
//test-wlee 9-bit disable
serial_outp(mtpt, UART_MSR, 0);
if (!(mtpt->port.flags & UPF_BUGGY_UART) &&
(serial_inp(mtpt, UART_LSR) == 0xff)) {
printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line);
//return -ENODEV;
}
if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) {
unsigned int timeout = mtpt->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
mtpt->timer.data = (unsigned long)mtpt;
mod_timer(&mtpt->timer, jiffies + timeout);
}
else
{
retval = serial_link_irq_chain(mtpt);
if (retval)
return retval;
}
serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8);
spin_lock_irqsave(&mtpt->port.lock, flags);
if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT))
mtpt->port.mctrl |= TIOCM_OUT2;
multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
spin_unlock_irqrestore(&mtpt->port.lock, flags);
mtpt->ier = UART_IER_RLSI | UART_IER_RDI;
serial_outp(mtpt, UART_IER, mtpt->ier);
(void) serial_inp(mtpt, UART_LSR);
(void) serial_inp(mtpt, UART_RX);
(void) serial_inp(mtpt, UART_IIR);
(void) serial_inp(mtpt, UART_MSR);
return 0;
}
static void multi_shutdown(struct sb_uart_port *port)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned long flags;
mtpt->ier = 0;
serial_outp(mtpt, UART_IER, 0);
spin_lock_irqsave(&mtpt->port.lock, flags);
mtpt->port.mctrl &= ~TIOCM_OUT2;
multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
spin_unlock_irqrestore(&mtpt->port.lock, flags);
serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC);
serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT);
serial_outp(mtpt, UART_FCR, 0);
(void) serial_in(mtpt, UART_RX);
if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL))
{
del_timer_sync(&mtpt->timer);
}
else
{
serial_unlink_irq_chain(mtpt);
}
}
static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud)
{
unsigned int quot;
if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
baud == (port->uartclk/4))
quot = 0x8001;
else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
baud == (port->uartclk/8))
quot = 0x8002;
else
quot = sb_uart_get_divisor(port, baud);
return quot;
}
static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old)
{
struct mp_port *mtpt = (struct mp_port *)port;
unsigned char cval, fcr = 0;
unsigned long flags;
unsigned int baud, quot;
switch (termios->c_cflag & CSIZE) {
case CS5:
cval = 0x00;
break;
case CS6:
cval = 0x01;
break;
case CS7:
cval = 0x02;
break;
default:
case CS8:
cval = 0x03;
break;
}
if (termios->c_cflag & CSTOPB)
cval |= 0x04;
if (termios->c_cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(termios->c_cflag & PARODD))
cval |= UART_LCR_EPAR;
#ifdef CMSPAR
if (termios->c_cflag & CMSPAR)
cval |= UART_LCR_SPAR;
#endif
baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = multi_get_divisor(port, baud);
if (mtpt->capabilities & UART_USE_FIFO) {
//if (baud < 2400)
// fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
//else
// fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
// fcr = UART_FCR_ENABLE_FIFO | 0x90;
fcr = fcr_arr[mtpt->port.line];
}
spin_lock_irqsave(&mtpt->port.lock, flags);
sb_uart_update_timeout(port, termios->c_cflag, baud);
mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (termios->c_iflag & (BRKINT | PARMRK))
mtpt->port.read_status_mask |= UART_LSR_BI;
mtpt->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
if (termios->c_iflag & IGNBRK) {
mtpt->port.ignore_status_mask |= UART_LSR_BI;
if (termios->c_iflag & IGNPAR)
mtpt->port.ignore_status_mask |= UART_LSR_OE;
}
if ((termios->c_cflag & CREAD) == 0)
mtpt->port.ignore_status_mask |= UART_LSR_DR;
mtpt->ier &= ~UART_IER_MSI;
if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag))
mtpt->ier |= UART_IER_MSI;
serial_out(mtpt, UART_IER, mtpt->ier);
if (mtpt->capabilities & UART_STARTECH) {
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR,
termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
}
serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
serial_outp(mtpt, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(mtpt, UART_DLM, quot >> 8); /* MS of divisor */
serial_outp(mtpt, UART_LCR, cval); /* reset DLAB */
mtpt->lcr = cval; /* Save LCR */
if (fcr & UART_FCR_ENABLE_FIFO) {
/* emulated UARTs (Lucent Venus 167x) need two steps */
serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
}
serial_outp(mtpt, UART_FCR, fcr); /* set fcr */
if ((mtpt->port.type == PORT_16C105X)
|| (mtpt->port.type == PORT_16C105XA))
{
if(deep[mtpt->port.line]!=0)
set_deep_fifo(port, ENABLE);
if (mtpt->interface != RS232)
set_auto_rts(port,mtpt->interface);
}
else
{
if (mtpt->interface >= RS485NE)
{
uart_clear_mctrl(&mtpt->port, TIOCM_RTS);
}
}
if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M)
{
SendATCommand(mtpt);
printk("SendATCommand\n");
}
multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
spin_unlock_irqrestore(&mtpt->port.lock, flags);
}
static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate)
{
struct mp_port *mtpt = (struct mp_port *)port;
if (state) {
if (mtpt->capabilities & UART_STARTECH) {
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR, UART_EFR_ECB);
serial_outp(mtpt, UART_LCR, 0);
serial_outp(mtpt, UART_IER, UART_IERX_SLEEP);
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR, 0);
serial_outp(mtpt, UART_LCR, 0);
}
if (mtpt->pm)
mtpt->pm(port, state, oldstate);
}
else
{
if (mtpt->capabilities & UART_STARTECH) {
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR, UART_EFR_ECB);
serial_outp(mtpt, UART_LCR, 0);
serial_outp(mtpt, UART_IER, 0);
serial_outp(mtpt, UART_LCR, 0xBF);
serial_outp(mtpt, UART_EFR, 0);
serial_outp(mtpt, UART_LCR, 0);
}
if (mtpt->pm)
mtpt->pm(port, state, oldstate);
}
}
static void multi_release_std_resource(struct mp_port *mtpt)
{
unsigned int size = 8 << mtpt->port.regshift;
switch (mtpt->port.iotype) {
case UPIO_MEM:
if (!mtpt->port.mapbase)
break;
if (mtpt->port.flags & UPF_IOREMAP) {
iounmap(mtpt->port.membase);
mtpt->port.membase = NULL;
}
release_mem_region(mtpt->port.mapbase, size);
break;
case UPIO_HUB6:
case UPIO_PORT:
release_region(mtpt->port.iobase,size);
break;
}
}
static void multi_release_port(struct sb_uart_port *port)
{
}
static int multi_request_port(struct sb_uart_port *port)
{
return 0;
}
static void multi_config_port(struct sb_uart_port *port, int flags)
{
struct mp_port *mtpt = (struct mp_port *)port;
int probeflags = PROBE_ANY;
if (flags & UART_CONFIG_TYPE)
autoconfig(mtpt, probeflags);
if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(mtpt);
if (mtpt->port.type == PORT_UNKNOWN)
multi_release_std_resource(mtpt);
}
static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser)
{
if (ser->irq >= NR_IRQS || ser->irq < 0 ||
ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
ser->type == PORT_STARTECH)
return -EINVAL;
return 0;
}
static const char *multi_type(struct sb_uart_port *port)
{
int type = port->type;
if (type >= ARRAY_SIZE(uart_config))
type = 0;
return uart_config[type].name;
}
static struct sb_uart_ops multi_pops = {
.tx_empty = multi_tx_empty,
.set_mctrl = multi_set_mctrl,
.get_mctrl = multi_get_mctrl,
.stop_tx = multi_stop_tx,
.start_tx = multi_start_tx,
.stop_rx = multi_stop_rx,
.enable_ms = multi_enable_ms,
.break_ctl = multi_break_ctl,
.startup = multi_startup,
.shutdown = multi_shutdown,
.set_termios = multi_set_termios,
.pm = multi_pm,
.type = multi_type,
.release_port = multi_release_port,
.request_port = multi_request_port,
.config_port = multi_config_port,
.verify_port = multi_verify_port,
};
static struct uart_driver multi_reg = {
.owner = THIS_MODULE,
.driver_name = "goldel_tulip",
.dev_name = "ttyMP",
.major = SB_TTY_MP_MAJOR,
.minor = 0,
.nr = MAX_MP_PORT,
.cons = NULL,
};
static void __init multi_init_ports(void)
{
struct mp_port *mtpt;
static int first = 1;
int i,j,k;
unsigned char osc;
unsigned char b_ret = 0;
static struct mp_device_t *sbdev;
if (!first)
return;
first = 0;
mtpt = multi_ports;
for (k=0;k<NR_BOARD;k++)
{
sbdev = &mp_devs[k];
for (i = 0; i < sbdev->nr_ports; i++, mtpt++)
{
mtpt->device = sbdev;
mtpt->port.iobase = sbdev->uart_access_addr + 8*i;
mtpt->port.irq = sbdev->irq;
if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91)))
mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i;
else if (sbdev->revision == 0xc0)
mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1);
else
mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8;
mtpt->option_base_addr = sbdev->option_reg_addr;
mtpt->poll_type = sbdev->poll_type;
mtpt->port.uartclk = BASE_BAUD * 16;
/* get input clock information */
osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F;
if (osc==0x0f)
osc = 0;
for(j=0;j<osc;j++)
mtpt->port.uartclk *= 2;
mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ;
mtpt->port.iotype = UPIO_PORT;
mtpt->port.ops = &multi_pops;
if (sbdev->revision == 0xc0)
{
/* for SB16C1053APCI */
b_ret = sb1053a_get_interface(mtpt, i);
}
else
{
b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8));
printk("IIR_RET = %x\n",b_ret);
}
/* default to RS232 */
mtpt->interface = RS232;
if (IIR_RS422 == (b_ret & IIR_TYPE_MASK))
mtpt->interface = RS422PTP;
if (IIR_RS485 == (b_ret & IIR_TYPE_MASK))
mtpt->interface = RS485NE;
}
}
}
static void __init multi_register_ports(struct uart_driver *drv)
{
int i;
multi_init_ports();
for (i = 0; i < NR_PORTS; i++) {
struct mp_port *mtpt = &multi_ports[i];
mtpt->port.line = i;
mtpt->port.ops = &multi_pops;
init_timer(&mtpt->timer);
mtpt->timer.function = multi_timeout;
mp_add_one_port(drv, &mtpt->port);
}
}
/**
* pci_remap_base - remap BAR value of pci device
*
* PARAMETERS
* pcidev - pci_dev structure address
* offset - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4
* address - address to be changed BAR value
* size - size of address space
*
* RETURNS
* If this function performs successful, it returns 0. Otherwise, It returns -1.
*/
static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset,
unsigned int address, unsigned int size)
{
#if 0
struct resource *root;
unsigned index = (offset - 0x10) >> 2;
#endif
pci_write_config_dword(pcidev, offset, address);
#if 0
root = pcidev->resource[index].parent;
release_resource(&pcidev->resource[index]);
address &= ~0x1;
pcidev->resource[index].start = address;
pcidev->resource[index].end = address + size - 1;
if (request_resource(root, &pcidev->resource[index]) != NULL)
{
printk(KERN_ERR "pci remap conflict!! 0x%x\n", address);
return (-1);
}
#endif
return (0);
}
static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
{
static struct mp_device_t *sbdev = mp_devs;
unsigned long addr = 0;
int j;
struct resource *ret = NULL;
sbdev->device_id = brd.device_id;
pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision));
sbdev->name = brd.name;
sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK;
/* check revision. The SB16C1053APCI's option i/o address is BAR4 */
if (sbdev->revision == 0xc0)
{
/* SB16C1053APCI */
sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
}
else
{
sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
}
#if 1
if (sbdev->revision == 0xc0)
{
outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR);
inb(sbdev->option_reg_addr + MP_OPTR_GPOCR);
outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR);
}
#endif
sbdev->irq = pcidev->irq;
if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00))
{
sbdev->poll_type = TYPE_INTERRUPT;
}
else
{
sbdev->poll_type = TYPE_POLL;
}
/* codes which is specific to each board*/
switch(brd.device_id){
case PCI_DEVICE_ID_MP1 :
case PCIE_DEVICE_ID_MP1 :
case PCIE_DEVICE_ID_MP1E :
case PCIE_DEVICE_ID_GT_MP1 :
sbdev->nr_ports = 1;
break;
case PCI_DEVICE_ID_MP2 :
case PCIE_DEVICE_ID_MP2 :
case PCIE_DEVICE_ID_GT_MP2 :
case PCIE_DEVICE_ID_MP2B :
case PCIE_DEVICE_ID_MP2E :
sbdev->nr_ports = 2;
/* serial base address remap */
if (sbdev->revision == 0xc0)
{
int prev_port_addr = 0;
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
}
break;
case PCI_DEVICE_ID_MP4 :
case PCI_DEVICE_ID_MP4A :
case PCIE_DEVICE_ID_MP4 :
case PCI_DEVICE_ID_GT_MP4 :
case PCI_DEVICE_ID_GT_MP4A :
case PCIE_DEVICE_ID_GT_MP4 :
case PCI_DEVICE_ID_MP4M :
case PCIE_DEVICE_ID_MP4B :
sbdev->nr_ports = 4;
if(sbdev->revision == 0x91){
sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK;
outb(0x03 , sbdev->reserved_addr[0] + 0x01);
outb(0x03 , sbdev->reserved_addr[0] + 0x02);
outb(0x01 , sbdev->reserved_addr[0] + 0x20);
outb(0x00 , sbdev->reserved_addr[0] + 0x21);
request_region(sbdev->reserved_addr[0], 32, sbdev->name);
sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK;
}
/* SB16C1053APCI */
if (sbdev->revision == 0xc0)
{
int prev_port_addr = 0;
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8);
}
break;
case PCI_DEVICE_ID_MP6 :
case PCI_DEVICE_ID_MP6A :
case PCI_DEVICE_ID_GT_MP6 :
case PCI_DEVICE_ID_GT_MP6A :
sbdev->nr_ports = 6;
/* SB16C1053APCI */
if (sbdev->revision == 0xc0)
{
int prev_port_addr = 0;
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16);
}
break;
case PCI_DEVICE_ID_MP8 :
case PCIE_DEVICE_ID_MP8 :
case PCI_DEVICE_ID_GT_MP8 :
case PCIE_DEVICE_ID_GT_MP8 :
case PCIE_DEVICE_ID_MP8B :
sbdev->nr_ports = 8;
break;
case PCI_DEVICE_ID_MP32 :
case PCIE_DEVICE_ID_MP32 :
case PCI_DEVICE_ID_GT_MP32 :
case PCIE_DEVICE_ID_GT_MP32 :
{
int portnum_hex=0;
portnum_hex = inb(sbdev->option_reg_addr);
sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16);
}
break;
#ifdef CONFIG_PARPORT_PC
case PCI_DEVICE_ID_MP2S1P :
sbdev->nr_ports = 2;
/* SB16C1053APCI */
if (sbdev->revision == 0xc0)
{
int prev_port_addr = 0;
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
}
/* add PC compatible parallel port */
parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
break;
case PCI_DEVICE_ID_MP1P :
/* add PC compatible parallel port */
parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
break;
#endif
}
ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name);
if (sbdev->revision == 0xc0)
{
ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name);
}
else
{
ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name);
}
NR_BOARD++;
NR_PORTS += sbdev->nr_ports;
/* Enable PCI interrupt */
addr = sbdev->option_reg_addr + MP_OPTR_IMR0;
for(j=0; j < (sbdev->nr_ports/8)+1; j++)
{
if (sbdev->poll_type == TYPE_INTERRUPT)
{
outb(0xff,addr +j);
}
}
sbdev++;
return 0;
}
static int __init multi_init(void)
{
int ret, i;
struct pci_dev *dev = NULL;
if(fcr_count==0)
{
for(i=0;i<256;i++)
{
fcr_arr[i] = 0x01;
}
}
if(deep_count==0)
{
for(i=0;i<256;i++)
{
deep[i] = 1;
}
}
if(rtr_count==0)
{
for(i=0;i<256;i++)
{
rtr[i] = 0x10;
}
}
if(ttr_count==0)
{
for(i=0;i<256;i++)
{
ttr[i] = 0x38;
}
}
printk("MULTI INIT\n");
for( i=0; i< mp_nrpcibrds; i++)
{
while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) )
{
printk("FOUND~~~\n");
// Cent OS bug fix
// if (mp_pciboards[i].device_id & 0x0800)
{
int status;
pci_disable_device(dev);
status = pci_enable_device(dev);
if (status != 0)
{
printk("Multiport Board Enable Fail !\n\n");
status = -ENXIO;
return status;
}
}
init_mp_dev(dev, mp_pciboards[i]);
}
}
for (i = 0; i < NR_IRQS; i++)
spin_lock_init(&irq_lists[i].lock);
ret = mp_register_driver(&multi_reg);
if (ret >= 0)
multi_register_ports(&multi_reg);
return ret;
}
static void __exit multi_exit(void)
{
int i;
for (i = 0; i < NR_PORTS; i++)
mp_remove_one_port(&multi_reg, &multi_ports[i].port);
mp_unregister_driver(&multi_reg);
}
module_init(multi_init);
module_exit(multi_exit);
MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE");
MODULE_LICENSE("GPL");
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty_driver.h>
#include <linux/pci.h>
#include <linux/circ_buf.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/serial.h>
#include <linux/interrupt.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#define MP_TERMIOS ktermios
#include "sb_mp_register.h"
#include "sb_ser_core.h"
#define DRIVER_VERSION "1.1"
#define DRIVER_DATE "2012/01/05"
#define DRIVER_AUTHOR "SYSTEMBASE<tech@sysbas.com>"
#define DRIVER_DESC "SystemBase PCI/PCIe Multiport Core"
#define SB_TTY_MP_MAJOR 54
#define PCI_VENDOR_ID_MULTIPORT 0x14A1
#define PCI_DEVICE_ID_MP1 0x4d01
#define PCI_DEVICE_ID_MP2 0x4d02
#define PCI_DEVICE_ID_MP4 0x4d04
#define PCI_DEVICE_ID_MP4A 0x4d54
#define PCI_DEVICE_ID_MP6 0x4d06
#define PCI_DEVICE_ID_MP6A 0x4d56
#define PCI_DEVICE_ID_MP8 0x4d08
#define PCI_DEVICE_ID_MP32 0x4d32
/* Parallel port */
#define PCI_DEVICE_ID_MP1P 0x4301
#define PCI_DEVICE_ID_MP2S1P 0x4303
#define PCIE_DEVICE_ID_MP1 0x4501
#define PCIE_DEVICE_ID_MP2 0x4502
#define PCIE_DEVICE_ID_MP4 0x4504
#define PCIE_DEVICE_ID_MP8 0x4508
#define PCIE_DEVICE_ID_MP32 0x4532
#define PCIE_DEVICE_ID_MP1E 0x4e01
#define PCIE_DEVICE_ID_MP2E 0x4e02
#define PCIE_DEVICE_ID_MP2B 0x4b02
#define PCIE_DEVICE_ID_MP4B 0x4b04
#define PCIE_DEVICE_ID_MP8B 0x4b08
#define PCI_DEVICE_ID_GT_MP4 0x0004
#define PCI_DEVICE_ID_GT_MP4A 0x0054
#define PCI_DEVICE_ID_GT_MP6 0x0006
#define PCI_DEVICE_ID_GT_MP6A 0x0056
#define PCI_DEVICE_ID_GT_MP8 0x0008
#define PCI_DEVICE_ID_GT_MP32 0x0032
#define PCIE_DEVICE_ID_GT_MP1 0x1501
#define PCIE_DEVICE_ID_GT_MP2 0x1502
#define PCIE_DEVICE_ID_GT_MP4 0x1504
#define PCIE_DEVICE_ID_GT_MP8 0x1508
#define PCIE_DEVICE_ID_GT_MP32 0x1532
#define PCI_DEVICE_ID_MP4M 0x4604 //modem
#define MAX_MP_DEV 8
#define BD_MAX_PORT 32 /* Max serial port in one board */
#define MAX_MP_PORT 256 /* Max serial port in one PC */
#define PORT_16C105XA 3
#define PORT_16C105X 2
#define PORT_16C55X 1
#define ENABLE 1
#define DISABLE 0
/* ioctls */
#define TIOCGNUMOFPORT 0x545F
#define TIOCSMULTIECHO 0x5440
#define TIOCSPTPNOECHO 0x5441
#define TIOCGOPTIONREG 0x5461
#define TIOCGDISABLEIRQ 0x5462
#define TIOCGENABLEIRQ 0x5463
#define TIOCGSOFTRESET 0x5464
#define TIOCGSOFTRESETR 0x5465
#define TIOCGREGINFO 0x5466
#define TIOCGGETLSR 0x5467
#define TIOCGGETDEVID 0x5468
#define TIOCGGETBDNO 0x5469
#define TIOCGGETINTERFACE 0x546A
#define TIOCGGETREV 0x546B
#define TIOCGGETNRPORTS 0x546C
#define TIOCGGETPORTTYPE 0x546D
#define GETDEEPFIFO 0x54AA
#define SETDEEPFIFO 0x54AB
#define SETFCR 0x54BA
#define SETTTR 0x54B1
#define SETRTR 0x54B2
#define GETTTR 0x54B3
#define GETRTR 0x54B4
/* multi-drop mode related ioctl commands */
#define TIOCSMULTIDROP 0x5470
#define TIOCSMDADDR 0x5471
#define TIOCGMDADDR 0x5472
#define TIOCSENDADDR 0x5473
/* serial interface */
#define RS232 1
#define RS422PTP 2
#define RS422MD 3
#define RS485NE 4
#define RS485ECHO 5
#define serial_inp(up, offset) serial_in(up, offset)
#define serial_outp(up, offset, value) serial_out(up, offset, value)
#define PASS_LIMIT 256
#define is_real_interrupt(irq) ((irq) != 0)
#define PROBE_ANY (~0)
static DEFINE_MUTEX(mp_mutex);
#define MP_MUTEX_LOCK(x) mutex_lock(&(x))
#define MP_MUTEX_UNLOCK(x) mutex_unlock(&(x))
#define MP_STATE_LOCK(x) mutex_lock(&((x)->mutex))
#define MP_STATE_UNLOCK(x) mutex_unlock(&((x)->mutex))
#define UART_LSR_SPECIAL 0x1E
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0))
//#define MP_DEBUG 1
#undef MP_DEBUG
#ifdef MP_DEBUG
#define DPRINTK(x...) printk(x)
#else
#define DPRINTK(x...) do { } while (0)
#endif
#ifdef MP_DEBUG
#define DEBUG_AUTOCONF(fmt...) printk(fmt)
#else
#define DEBUG_AUTOCONF(fmt...) do { } while (0)
#endif
#ifdef MP_DEBUG
#define DEBUG_INTR(fmt...) printk(fmt)
#else
#define DEBUG_INTR(fmt...) do { } while (0)
#endif
#if defined(__i386__) && defined(CONFIG_M486)
#define SERIAL_INLINE
#endif
#ifdef SERIAL_INLINE
#define _INLINE_ inline
#else
#define _INLINE_
#endif
#define TYPE_POLL 1
#define TYPE_INTERRUPT 2
struct mp_device_t {
unsigned short device_id;
unsigned char revision;
char *name;
unsigned long uart_access_addr;
unsigned long option_reg_addr;
unsigned long reserved_addr[4];
int irq;
int nr_ports;
int poll_type;
};
typedef struct mppcibrd {
char *name;
unsigned short vendor_id;
unsigned short device_id;
} mppcibrd_t;
static mppcibrd_t mp_pciboards[] = {
{ "Multi-1 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1} ,
{ "Multi-2 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2} ,
{ "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4} ,
{ "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4A} ,
{ "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6} ,
{ "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6A} ,
{ "Multi-8 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP8} ,
{ "Multi-32 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP32} ,
{ "Multi-1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1P} ,
{ "Multi-2S1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2S1P} ,
{ "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4} ,
{ "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4A} ,
{ "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6} ,
{ "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6A} ,
{ "Multi-8(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP8} ,
{ "Multi-32(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP32} ,
{ "Multi-1 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1} ,
{ "Multi-2 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2} ,
{ "Multi-4 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4} ,
{ "Multi-8 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8} ,
{ "Multi-32 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP32} ,
{ "Multi-1 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1E} ,
{ "Multi-2 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2E} ,
{ "Multi-2 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2B} ,
{ "Multi-4 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4B} ,
{ "Multi-8 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8B} ,
{ "Multi-1(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP1} ,
{ "Multi-2(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP2} ,
{ "Multi-4(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP4} ,
{ "Multi-8(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP8} ,
{ "Multi-32(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP32} ,
{ "Multi-4M PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4M} ,
};
struct mp_port {
struct sb_uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
unsigned int capabilities; /* port capabilities */
unsigned short rev;
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char lsr_break_flag;
void (*pm)(struct sb_uart_port *port,
unsigned int state, unsigned int old);
struct mp_device_t *device;
unsigned long interface_config_addr;
unsigned long option_base_addr;
unsigned char interface;
unsigned char poll_type;
};
struct irq_info {
spinlock_t lock;
struct list_head *head;
};
struct sb105x_uart_config {
char *name;
int dfl_xmit_fifo_size;
int flags;
};
static const struct sb105x_uart_config uart_config[] = {
{ "unknown", 1, 0 },
{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
{ "SB16C1050", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
{ "SB16C1050A", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
};
#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);
}
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment