* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (65 commits)
tty: serial: imx: move del_timer_sync() to avoid potential deadlock
imx: add polled io uart methods
imx: Add save/restore functions for UART control regs
serial/imx: let probing fail for the dt case without a valid alias
serial/imx: propagate error from of_alias_get_id instead of using -ENODEV
tty: serial: imx: Allow UART to be a source for wakeup
serial: driver for m32 arch should not have DEC alpha errata
serial/documentation: fix documented name of DCD cpp symbol
atmel_serial: fix spinlock lockup in RS485 code
tty: Fix memory leak in virtual console when enable unicode translation
serial: use DIV_ROUND_CLOSEST instead of open coding it
serial: add support for 400 and 800 v3 series Titan cards
serial: bfin-uart: Remove ASYNC_CTS_FLOW flag for hardware automatic CTS.
serial: bfin-uart: Enable hardware automatic CTS only when CTS pin is available.
serial: make FSL errata depend on 8250_CONSOLE, not just 8250
serial: add irq handler for Freescale 16550 errata.
serial: manually inline serial8250_handle_port
serial: make 8250 timeout use the specified IRQ handler
serial: export the key functions for an 8250 IRQ handler
serial: clean up parameter passing for 8250 Rx IRQ handling
...
Returns the current state of modem control inputs. The state
of the outputs should not be returned, since the core keeps
track of their state. The state information should include:
- - TIOCM_DCD state of DCD signal
+ - TIOCM_CAR state of DCD signal
- TIOCM_CTS state of CTS signal
- TIOCM_DSR state of DSR signal
- TIOCM_RI state of RI signal
return;
port->irq = virq;
+
+ if (of_device_is_compatible(np, "fsl,ns16550"))
+ port->handle_irq = fsl8250_handle_irq;
}
static void __init fixup_port_pio(int index,
extern void mrst_early_console_init(void);
extern struct console early_hsu_console;
-extern void hsu_early_console_init(void);
+extern void hsu_early_console_init(const char *);
extern void intel_scu_devices_create(void);
extern void intel_scu_devices_destroy(void);
}
if (!strncmp(buf, "hsu", 3)) {
- hsu_early_console_init();
+ hsu_early_console_init(buf + 3);
early_console_register(&early_hsu_console, keep);
}
#endif
* Following is the early console based on Medfield HSU (High
* Speed UART) device.
*/
-#define HSU_PORT2_PADDR 0xffa28180
+#define HSU_PORT_BASE 0xffa28080
static void __iomem *phsu;
-void hsu_early_console_init(void)
+void hsu_early_console_init(const char *s)
{
+ unsigned long paddr, port = 0;
u8 lcr;
- phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
- HSU_PORT2_PADDR);
+ /*
+ * Select the early HSU console port if specified by user in the
+ * kernel command line.
+ */
+ if (*s && !kstrtoul(s, 10, &port))
+ port = clamp_val(port, 0, 2);
+
+ paddr = HSU_PORT_BASE + port * 0x80;
+ phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
/* Disable FIFO */
writeb(0x0, phsu + UART_FCR);
.resume = parport_ax88796_resume,
};
-static int __init parport_ax88796_init(void)
-{
- return platform_driver_register(&axdrv);
-}
-
-static void __exit parport_ax88796_exit(void)
-{
- platform_driver_unregister(&axdrv);
-}
-
-module_init(parport_ax88796_init)
-module_exit(parport_ax88796_exit)
+module_platform_driver(axdrv);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("AX88796 Parport parallel port driver");
.remove = __devexit_p(bpp_remove),
};
-static int __init parport_sunbpp_init(void)
-{
- return platform_driver_register(&bpp_sbus_driver);
-}
-
-static void __exit parport_sunbpp_exit(void)
-{
- platform_driver_unregister(&bpp_sbus_driver);
-}
+module_platform_driver(bpp_sbus_driver);
MODULE_AUTHOR("Derrick J Brashear");
MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port");
MODULE_SUPPORTED_DEVICE("Sparc Bidirectional Parallel Port");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
-
-module_init(parport_sunbpp_init)
-module_exit(parport_sunbpp_exit)
__FILE__,__LINE__,tbuf,tbuf->count);
/* Send the next block of data to device */
- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
actual = tty->ops->write(tty, tbuf->buf, tbuf->count);
/* rollback was possible and has been done */
}
if (!tbuf)
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
/* Clear the re-entry flag */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
return;
if (tty != n_hdlc->tty) {
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return;
}
* controlling the space in the read buffer.
*/
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
-#define TTY_THRESHOLD_UNTHROTTLE 128
+#define TTY_THRESHOLD_UNTHROTTLE 128
/*
* Special byte codes used in the echo buffer to represent operations
const unsigned char *buf, unsigned int nr)
{
int space;
- int i;
+ int i;
const unsigned char *cp;
mutex_lock(&tty->output_lock);
}
/**
- * copy_from_read_buf - copy read data directly
+ * copy_from_read_buf - copy read data directly
* @tty: terminal device
* @b: user data
* @nr: size of data
if (nr)
clear_bit(TTY_PUSH, &tty->flags);
} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
- goto do_it_again;
+ goto do_it_again;
n_tty_set_room(tty);
return retval;
int pty_limit = NR_UNIX98_PTY_DEFAULT;
static int pty_limit_min;
static int pty_limit_max = NR_UNIX98_PTY_MAX;
-static int tty_count;
static int pty_count;
-static inline void pty_inc_count(void)
-{
- pty_count = (++tty_count) / 2;
-}
-
-static inline void pty_dec_count(void)
-{
- pty_count = (--tty_count) / 2;
-}
-
static struct cdev ptmx_cdev;
static struct ctl_table pty_table[] = {
*/
tty_driver_kref_get(driver);
tty->count++;
- pty_inc_count(); /* tty */
- pty_inc_count(); /* tty->link */
+ pty_count++;
return 0;
err_free_mem:
deinitialize_tty_struct(o_tty);
return -ENOMEM;
}
-static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
+static void ptm_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+ pty_count--;
+}
+
+static void pts_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
{
- pty_dec_count();
}
static const struct tty_operations ptm_unix98_ops = {
.lookup = ptm_unix98_lookup,
.install = pty_unix98_install,
- .remove = pty_unix98_remove,
+ .remove = ptm_unix98_remove,
.open = pty_open,
.close = pty_close,
.write = pty_write,
static const struct tty_operations pty_unix98_ops = {
.lookup = pts_unix98_lookup,
.install = pty_unix98_install,
- .remove = pty_unix98_remove,
+ .remove = pts_unix98_remove,
.open = pty_open,
.close = pty_close,
.write = pty_write,
static unsigned int probe_rsa_count;
#endif /* CONFIG_SERIAL_8250_RSA */
-struct uart_8250_port {
- struct uart_port port;
- struct timer_list timer; /* "no irq" timer */
- struct list_head list; /* ports on this IRQ */
- unsigned short capabilities; /* port capabilities */
- unsigned short bugs; /* port bugs */
- unsigned int tx_loadsz; /* transmit fifo load size */
- 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 cur_iotype; /* Running I/O type */
-
- /*
- * Some bits in registers are cleared on a read, so they must
- * be saved whenever the register is read but the bits will not
- * be immediately processed.
- */
-#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
- unsigned char lsr_saved_flags;
-#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
- unsigned char msr_saved_flags;
-};
-
struct irq_info {
struct hlist_node node;
int irq;
}
}
-static void transmit_chars(struct uart_8250_port *up);
-
static void serial8250_start_tx(struct uart_port *port)
{
struct uart_8250_port *up =
if ((up->port.type == PORT_RM9000) ?
(lsr & UART_LSR_THRE) :
(lsr & UART_LSR_TEMT))
- transmit_chars(up);
+ serial8250_tx_chars(up);
}
}
} while (1);
}
-static void
-receive_chars(struct uart_8250_port *up, unsigned int *status)
+/*
+ * serial8250_rx_chars: processes according to the passed in LSR
+ * value, and returns the remaining LSR bits not handled
+ * by this Rx routine.
+ */
+unsigned char
+serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
struct tty_struct *tty = up->port.state->port.tty;
- unsigned char ch, lsr = *status;
+ unsigned char ch;
int max_count = 256;
char flag;
spin_unlock(&up->port.lock);
tty_flip_buffer_push(tty);
spin_lock(&up->port.lock);
- *status = lsr;
+ return lsr;
}
+EXPORT_SYMBOL_GPL(serial8250_rx_chars);
-static void transmit_chars(struct uart_8250_port *up)
+void serial8250_tx_chars(struct uart_8250_port *up)
{
struct circ_buf *xmit = &up->port.state->xmit;
int count;
if (uart_circ_empty(xmit))
__stop_tx(up);
}
+EXPORT_SYMBOL_GPL(serial8250_tx_chars);
-static unsigned int check_modem_status(struct uart_8250_port *up)
+unsigned int serial8250_modem_status(struct uart_8250_port *up)
{
unsigned int status = serial_in(up, UART_MSR);
return status;
}
+EXPORT_SYMBOL_GPL(serial8250_modem_status);
/*
* This handles the interrupt from one port.
*/
-static void serial8250_handle_port(struct uart_8250_port *up)
+int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
{
- unsigned int status;
+ unsigned char status;
unsigned long flags;
+ struct uart_8250_port *up =
+ container_of(port, struct uart_8250_port, port);
+
+ if (iir & UART_IIR_NO_INT)
+ return 0;
spin_lock_irqsave(&up->port.lock, flags);
DEBUG_INTR("status = %x...", status);
if (status & (UART_LSR_DR | UART_LSR_BI))
- receive_chars(up, &status);
- check_modem_status(up);
+ status = serial8250_rx_chars(up, status);
+ serial8250_modem_status(up);
if (status & UART_LSR_THRE)
- transmit_chars(up);
+ serial8250_tx_chars(up);
spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
-{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
-
- if (!(iir & UART_IIR_NO_INT)) {
- serial8250_handle_port(up);
- return 1;
- }
-
- return 0;
+ return 1;
}
EXPORT_SYMBOL_GPL(serial8250_handle_irq);
do {
struct uart_8250_port *up;
struct uart_port *port;
+ bool skip;
up = list_entry(l, struct uart_8250_port, list);
port = &up->port;
+ skip = pass_counter && up->port.flags & UPF_IIR_ONCE;
- if (port->handle_irq(port)) {
+ if (!skip && port->handle_irq(port)) {
handled = 1;
end = NULL;
} else if (end == NULL)
static void serial8250_timeout(unsigned long data)
{
struct uart_8250_port *up = (struct uart_8250_port *)data;
- unsigned int iir;
- iir = serial_in(up, UART_IIR);
- if (!(iir & UART_IIR_NO_INT))
- serial8250_handle_port(up);
+ up->port.handle_irq(&up->port);
mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port));
}
}
if (!(iir & UART_IIR_NO_INT))
- transmit_chars(up);
+ serial8250_tx_chars(up);
if (is_real_interrupt(up->port.irq))
serial_out(up, UART_IER, ier);
unsigned int status;
unsigned int ret;
- status = check_modem_status(up);
+ status = serial8250_modem_status(up);
ret = 0;
if (status & UART_MSR_DCD)
serial_outp(up, UART_IER, 0);
serial_outp(up, UART_LCR, 0);
serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
- serial_outp(up, UART_LCR, 0xBF);
+ serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_outp(up, UART_EFR, UART_EFR_ECB);
serial_outp(up, UART_LCR, 0);
}
local_irq_save(flags);
if (up->port.sysrq) {
- /* serial8250_handle_port() already took the lock */
+ /* serial8250_handle_irq() already took the lock */
locked = 0;
} else if (oops_in_progress) {
locked = spin_trylock(&up->port.lock);
* while processing with interrupts off.
*/
if (up->msr_saved_flags)
- check_modem_status(up);
+ serial8250_modem_status(up);
if (locked)
spin_unlock(&up->port.lock);
#include <linux/serial_8250.h>
+struct uart_8250_port {
+ struct uart_port port;
+ struct timer_list timer; /* "no irq" timer */
+ struct list_head list; /* ports on this IRQ */
+ unsigned short capabilities; /* port capabilities */
+ unsigned short bugs; /* port bugs */
+ unsigned int tx_loadsz; /* transmit fifo load size */
+ 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 cur_iotype; /* Running I/O type */
+
+ /*
+ * Some bits in registers are cleared on a read, so they must
+ * be saved whenever the register is read but the bits will not
+ * be immediately processed.
+ */
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+ unsigned char lsr_saved_flags;
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+ unsigned char msr_saved_flags;
+};
+
struct old_serial_port {
unsigned int uart;
unsigned int baud_base;
.remove = __devexit_p(dw8250_remove),
};
-static int __init dw8250_init(void)
-{
- return platform_driver_register(&dw8250_platform_driver);
-}
-module_init(dw8250_init);
-
-static void __exit dw8250_exit(void)
-{
- platform_driver_unregister(&dw8250_platform_driver);
-}
-module_exit(dw8250_exit);
+module_platform_driver(dw8250_platform_driver);
MODULE_AUTHOR("Jamie Iles");
MODULE_LICENSE("GPL");
--- /dev/null
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
+
+#include "8250.h"
+
+/*
+ * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This isn't a full driver; it just provides an alternate IRQ
+ * handler to deal with an errata. Everything else is just
+ * using the bog standard 8250 support.
+ *
+ * We follow code flow of serial8250_default_handle_irq() but add
+ * a check for a break and insert a dummy read on the Rx for the
+ * immediately following IRQ event.
+ *
+ * We re-use the already existing "bug handling" lsr_saved_flags
+ * field to carry the "what we just did" information from the one
+ * IRQ event to the next one.
+ */
+
+int fsl8250_handle_irq(struct uart_port *port)
+{
+ unsigned char lsr, orig_lsr;
+ unsigned long flags;
+ unsigned int iir;
+ struct uart_8250_port *up =
+ container_of(port, struct uart_8250_port, port);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ iir = port->serial_in(port, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return 0;
+ }
+
+ /* This is the WAR; if last event was BRK, then read and return */
+ if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) {
+ up->lsr_saved_flags &= ~UART_LSR_BI;
+ port->serial_in(port, UART_RX);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return 1;
+ }
+
+ lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
+
+ if (lsr & (UART_LSR_DR | UART_LSR_BI))
+ lsr = serial8250_rx_chars(up, lsr);
+
+ serial8250_modem_status(up);
+
+ if (lsr & UART_LSR_THRE)
+ serial8250_tx_chars(up);
+
+ up->lsr_saved_flags = orig_lsr;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return 1;
+}
return pci_default_setup(priv, board, port, idx);
}
+static int kt_serial_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_port *port, int idx)
+{
+ port->flags |= UPF_IIR_ONCE;
+ return skip_tx_en_setup(priv, board, port, idx);
+}
+
static int pci_eg20t_init(struct pci_dev *dev)
{
#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE)
return pci_default_setup(priv, board, port, idx);
}
-/* This should be in linux/pci_ids.h */
+static int try_enable_msi(struct pci_dev *dev)
+{
+ /* use msi if available, but fallback to legacy otherwise */
+ pci_enable_msi(dev);
+ return 0;
+}
+
+static void disable_msi(struct pci_dev *dev)
+{
+ pci_disable_msi(dev);
+}
+
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B
#define PCI_DEVICE_ID_OCTPRO 0x0001
#define PCI_DEVICE_ID_TITAN_800E 0xA014
#define PCI_DEVICE_ID_TITAN_200EI 0xA016
#define PCI_DEVICE_ID_TITAN_200EISI 0xA017
+#define PCI_DEVICE_ID_TITAN_400V3 0xA310
+#define PCI_DEVICE_ID_TITAN_410V3 0xA312
+#define PCI_DEVICE_ID_TITAN_800V3 0xA314
+#define PCI_DEVICE_ID_TITAN_800V3B 0xA315
#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538
#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6
#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001
+#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
.subdevice = PCI_ANY_ID,
.setup = ce4100_serial_setup,
},
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = try_enable_msi,
+ .setup = kt_serial_setup,
+ .exit = disable_msi,
+ },
/*
* ITE
*/
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_oxsemi_2_4000000 },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
This builds standard PNP serial support. You may be able to
disable this feature if you only need legacy serial support.
+config SERIAL_8250_FSL
+ bool
+ depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550
+ default PPC
+
config SERIAL_8250_HP300
tristate
depends on SERIAL_8250 && HP300
help
Serial port support for Samsung's S5P Family of SoC's
+config SERIAL_SIRFSOC
+ tristate "SiRF SoC Platform Serial port support"
+ depends on ARM && ARCH_PRIMA2
+ select SERIAL_CORE
+ help
+ Support for the on-chip UART on the CSR SiRFprimaII series,
+ providing /dev/ttySiRF0, 1 and 2 (note, some machines may not
+ provide all of these ports, depending on how the serial port
+ pins are configured).
+
+config SERIAL_SIRFSOC_CONSOLE
+ bool "Support for console on SiRF SoC serial port"
+ depends on SERIAL_SIRFSOC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Even if you say Y here, the currently visible virtual console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttySiRFx". (Try "man bootparam" or see the documentation of
+ your boot loader about how to pass options to the kernel at
+ boot time.)
config SERIAL_MAX3100
tristate "MAX3100 support"
config SERIAL_OMAP
tristate "OMAP serial port support"
- depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4
+ depends on ARCH_OMAP2PLUS
select SERIAL_CORE
help
If you have a machine based on an Texas Instruments OMAP CPU you
ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+config SERIAL_PCH_UART_CONSOLE
+ bool "Support for console on Intel EG20T PCH UART/OKI SEMICONDUCTOR ML7213 IOH"
+ depends on SERIAL_PCH_UART=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Say Y here if you wish to use the PCH UART as the system console
+ (the system console is the device which receives all kernel messages and
+ warnings and which allows logins in single user mode).
+
config SERIAL_MSM_SMD
bool "Enable tty device interface for some SMD ports"
default n
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
+obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
+obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int mode;
+ unsigned long flags;
- spin_lock(&port->lock);
+ spin_lock_irqsave(&port->lock, flags);
/* Disable interrupts */
UART_PUT_IDR(port, atmel_port->tx_done_mask);
/* Enable interrupts */
UART_PUT_IER(port, atmel_port->tx_done_mask);
- spin_unlock(&port->lock);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static void atmel_set_ldisc(struct uart_port *port, int new)
{
- int line = port->line;
-
- if (line >= port->state->port.tty->driver->num)
- return;
-
- if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+ if (new == N_PPS) {
port->flags |= UPF_HARDPPS_CD;
atmel_enable_ms(port);
} else {
dev_info(port->dev, "Unable to attach BlackFin UART over SPORT CTS interrupt. So, disable it.\n");
}
}
- if (up->rts_pin >= 0)
- gpio_direction_output(up->rts_pin, 0);
+ if (up->rts_pin >= 0) {
+ if (gpio_request(up->rts_pin, DRV_NAME)) {
+ dev_info(port->dev, "fail to request RTS PIN at GPIO_%d\n", up->rts_pin);
+ up->rts_pin = -1;
+ } else
+ gpio_direction_output(up->rts_pin, 0);
+ }
#endif
return 0;
#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
if (up->cts_pin >= 0)
free_irq(gpio_to_irq(up->cts_pin), up);
+ if (up->rts_pin >= 0)
+ gpio_free(up->rts_pin);
#endif
}
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL)
sport->cts_pin = -1;
- else
+ else {
sport->cts_pin = res->start;
+ sport->port.flags |= ASYNC_CTS_FLOW;
+ }
res = platform_get_resource(pdev, IORESOURCE_IO, 1);
if (res == NULL)
sport->rts_pin = -1;
else
sport->rts_pin = res->start;
-
- if (sport->rts_pin >= 0)
- gpio_request(sport->rts_pin, DRV_NAME);
#endif
}
if (sport) {
uart_remove_one_port(&sport_uart_reg, &sport->port);
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
- if (sport->rts_pin >= 0)
- gpio_free(sport->rts_pin);
-#endif
iounmap(sport->port.membase);
peripheral_free_list(
(unsigned short *)pdev->dev.platform_data);
#define SPORT_GET_RX32(sport) \
({ \
unsigned int __ret; \
+ unsigned long flags; \
if (ANOMALY_05000473) \
- local_irq_disable(); \
+ local_irq_save(flags); \
__ret = bfin_read32((sport)->port.membase + OFFSET_RX); \
if (ANOMALY_05000473) \
- local_irq_enable(); \
+ local_irq_restore(flags); \
__ret; \
})
#define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1))
static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
- unsigned int status;
-
- status = bfin_serial_get_mctrl(&uart->port);
- uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
+ unsigned int status = bfin_serial_get_mctrl(&uart->port);
#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
- uart->scts = 1;
+ struct tty_struct *tty = uart->port.state->port.tty;
+
UART_CLEAR_SCTS(uart);
- UART_CLEAR_IER(uart, EDSSI);
+ if (tty->hw_stopped) {
+ if (status) {
+ tty->hw_stopped = 0;
+ uart_write_wakeup(&uart->port);
+ }
+ } else {
+ if (!status)
+ tty->hw_stopped = 1;
+ }
#endif
+ uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
return IRQ_HANDLED;
}
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
struct tty_struct *tty = uart->port.state->port.tty;
-#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
- if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) {
- uart->scts = 0;
- uart_handle_cts_change(&uart->port, uart->scts);
- }
-#endif
-
/*
* To avoid losting RX interrupt, we reset IR function
* before sending data.
{
struct bfin_serial_port *uart = dev_id;
-#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
- if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) {
- uart->scts = 0;
- uart_handle_cts_change(&uart->port, uart->scts);
- }
-#endif
spin_lock(&uart->port.lock);
if (UART_GET_LSR(uart) & THRE)
bfin_serial_tx_chars(uart);
struct bfin_serial_port *uart = dev_id;
struct circ_buf *xmit = &uart->port.state->xmit;
-#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
- if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) {
- uart->scts = 0;
- uart_handle_cts_change(&uart->port, uart->scts);
- }
-#endif
-
spin_lock(&uart->port.lock);
if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
disable_dma(uart->tx_dma_channel);
pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n");
}
}
- if (uart->rts_pin >= 0)
- gpio_direction_output(uart->rts_pin, 0);
+ if (uart->rts_pin >= 0) {
+ if (gpio_request(uart->rts_pin, DRIVER_NAME)) {
+ pr_info("fail to request RTS PIN at GPIO_%d\n", uart->rts_pin);
+ uart->rts_pin = -1;
+ } else
+ gpio_direction_output(uart->rts_pin, 0);
+ }
#endif
#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
- if (uart->cts_pin >= 0 && request_irq(uart->status_irq,
- bfin_serial_mctrl_cts_int,
- 0, "BFIN_UART_MODEM_STATUS", uart)) {
- uart->cts_pin = -1;
- pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n");
- }
+ if (uart->cts_pin >= 0) {
+ if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int,
+ IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) {
+ uart->cts_pin = -1;
+ dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n");
+ }
- /* CTS RTS PINs are negative assertive. */
- UART_PUT_MCR(uart, ACTS);
- UART_SET_IER(uart, EDSSI);
+ /* CTS RTS PINs are negative assertive. */
+ UART_PUT_MCR(uart, ACTS);
+ UART_SET_IER(uart, EDSSI);
+ }
#endif
UART_SET_IER(uart, ERBFI);
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
if (uart->cts_pin >= 0)
free_irq(gpio_to_irq(uart->cts_pin), uart);
+ if (uart->rts_pin >= 0)
+ gpio_free(uart->rts_pin);
#endif
#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
if (uart->cts_pin >= 0)
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL)
uart->cts_pin = -1;
- else
+ else {
uart->cts_pin = res->start;
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+ uart->port.flags |= ASYNC_CTS_FLOW;
+#endif
+ }
res = platform_get_resource(pdev, IORESOURCE_IO, 1);
if (res == NULL)
uart->rts_pin = -1;
else
uart->rts_pin = res->start;
-# if defined(CONFIG_SERIAL_BFIN_CTSRTS)
- if (uart->rts_pin >= 0)
- gpio_request(uart->rts_pin, DRIVER_NAME);
-# endif
#endif
}
if (uart) {
uart_remove_one_port(&bfin_serial_reg, &uart->port);
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
- if (uart->rts_pin >= 0)
- gpio_free(uart->rts_pin);
-#endif
iounmap(uart->port.membase);
peripheral_free_list(
(unsigned short *)pdev->dev.platform_data);
static const struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
- .bus = &spi_bus_type,
.pm = &ifx_spi_pm,
.owner = THIS_MODULE},
.probe = ifx_spi_spi_probe,
#define UCR2_STPB (1<<6) /* Stop */
#define UCR2_WS (1<<5) /* Word size */
#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
+#define UCR2_ATEN (1<<3) /* Aging Timer Enable */
#define UCR2_TXEN (1<<2) /* Transmitter enabled */
#define UCR2_RXEN (1<<1) /* Receiver enabled */
#define UCR2_SRST (1<<0) /* SW reset */
struct imx_uart_data *devdata;
};
+struct imx_port_ucrs {
+ unsigned int ucr1;
+ unsigned int ucr2;
+ unsigned int ucr3;
+};
+
#ifdef CONFIG_IRDA
#define USE_IRDA(sport) ((sport)->use_irda)
#else
return sport->devdata->devtype == IMX21_UART;
}
+/*
+ * Save and restore functions for UCR1, UCR2 and UCR3 registers
+ */
+static void imx_port_ucrs_save(struct uart_port *port,
+ struct imx_port_ucrs *ucr)
+{
+ /* save control registers */
+ ucr->ucr1 = readl(port->membase + UCR1);
+ ucr->ucr2 = readl(port->membase + UCR2);
+ ucr->ucr3 = readl(port->membase + UCR3);
+}
+
+static void imx_port_ucrs_restore(struct uart_port *port,
+ struct imx_port_ucrs *ucr)
+{
+ /* restore control registers */
+ writel(ucr->ucr1, port->membase + UCR1);
+ writel(ucr->ucr2, port->membase + UCR2);
+ writel(ucr->ucr3, port->membase + UCR3);
+}
+
/*
* Handle any change of modem status signal since we were last called.
*/
if (sts & USR1_RTSD)
imx_rtsint(irq, dev_id);
+ if (sts & USR1_AWAKE)
+ writel(USR1_AWAKE, sport->port.membase + USR1);
+
return IRQ_HANDLED;
}
ucr2 |= UCR2_PROE;
}
+ del_timer_sync(&sport->timer);
+
/*
* Ask the core to calculate the divisor for us.
*/
sport->port.ignore_status_mask |= URXD_OVRRUN;
}
- del_timer_sync(&sport->timer);
-
/*
* Update the per-port timeout.
*/
return ret;
}
+#if defined(CONFIG_CONSOLE_POLL)
+static int imx_poll_get_char(struct uart_port *port)
+{
+ struct imx_port_ucrs old_ucr;
+ unsigned int status;
+ unsigned char c;
+
+ /* save control registers */
+ imx_port_ucrs_save(port, &old_ucr);
+
+ /* disable interrupts */
+ writel(UCR1_UARTEN, port->membase + UCR1);
+ writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+ port->membase + UCR2);
+ writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+ port->membase + UCR3);
+
+ /* poll */
+ do {
+ status = readl(port->membase + USR2);
+ } while (~status & USR2_RDR);
+
+ /* read */
+ c = readl(port->membase + URXD0);
+
+ /* restore control registers */
+ imx_port_ucrs_restore(port, &old_ucr);
+
+ return c;
+}
+
+static void imx_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ struct imx_port_ucrs old_ucr;
+ unsigned int status;
+
+ /* save control registers */
+ imx_port_ucrs_save(port, &old_ucr);
+
+ /* disable interrupts */
+ writel(UCR1_UARTEN, port->membase + UCR1);
+ writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+ port->membase + UCR2);
+ writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+ port->membase + UCR3);
+
+ /* drain */
+ do {
+ status = readl(port->membase + USR1);
+ } while (~status & USR1_TRDY);
+
+ /* write */
+ writel(c, port->membase + URTX0);
+
+ /* flush */
+ do {
+ status = readl(port->membase + USR2);
+ } while (~status & USR2_TXDC);
+
+ /* restore control registers */
+ imx_port_ucrs_restore(port, &old_ucr);
+}
+#endif
+
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
.request_port = imx_request_port,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
+#if defined(CONFIG_CONSOLE_POLL)
+ .poll_get_char = imx_poll_get_char,
+ .poll_put_char = imx_poll_put_char,
+#endif
};
static struct imx_port *imx_ports[UART_NR];
imx_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
- unsigned int old_ucr1, old_ucr2, ucr1;
+ struct imx_port_ucrs old_ucr;
+ unsigned int ucr1;
/*
- * First, save UCR1/2 and then disable interrupts
+ * First, save UCR1/2/3 and then disable interrupts
*/
- ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
- old_ucr2 = readl(sport->port.membase + UCR2);
+ imx_port_ucrs_save(&sport->port, &old_ucr);
+ ucr1 = old_ucr.ucr1;
if (is_imx1_uart(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
writel(ucr1, sport->port.membase + UCR1);
- writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
+ writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
uart_console_write(&sport->port, s, count, imx_console_putchar);
/*
* Finally, wait for transmitter to become empty
- * and restore UCR1/2
+ * and restore UCR1/2/3
*/
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
- writel(old_ucr1, sport->port.membase + UCR1);
- writel(old_ucr2, sport->port.membase + UCR2);
+ imx_port_ucrs_restore(&sport->port, &old_ucr);
}
/*
static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
{
struct imx_port *sport = platform_get_drvdata(dev);
+ unsigned int val;
+
+ /* enable wakeup from i.MX UART */
+ val = readl(sport->port.membase + UCR3);
+ val |= UCR3_AWAKEN;
+ writel(val, sport->port.membase + UCR3);
if (sport)
uart_suspend_port(&imx_reg, &sport->port);
static int serial_imx_resume(struct platform_device *dev)
{
struct imx_port *sport = platform_get_drvdata(dev);
+ unsigned int val;
+
+ /* disable wakeup from i.MX UART */
+ val = readl(sport->port.membase + UCR3);
+ val &= ~UCR3_AWAKEN;
+ writel(val, sport->port.membase + UCR3);
if (sport)
uart_resume_port(&imx_reg, &sport->port);
}
#ifdef CONFIG_OF
+/*
+ * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
+ * could successfully get all information from dt or a negative errno.
+ */
static int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
int ret;
if (!np)
- return -ENODEV;
+ /* no device tree device */
+ return 1;
ret = of_alias_get_id(np, "serial");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
- return -ENODEV;
+ return ret;
}
sport->port.line = ret;
static inline int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
- return -ENODEV;
+ return 1;
}
#endif
return -ENOMEM;
ret = serial_imx_probe_dt(sport, pdev);
- if (ret == -ENODEV)
+ if (ret > 0)
serial_imx_probe_pdata(sport, pdev);
+ else if (ret < 0)
+ goto free;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
if (ret != 0)
uart_unregister_driver(&imx_reg);
- return 0;
+ return ret;
}
static void __exit imx_serial_exit(void)
init_timer(&up->timer);
up->timer.function = m32r_sio_timeout;
- /*
- * ALPHA_KLUDGE_MCR needs to be killed.
- */
- up->mcr_mask = ~ALPHA_KLUDGE_MCR;
- up->mcr_force = ALPHA_KLUDGE_MCR;
+ up->mcr_mask = ~0;
+ up->mcr_force = 0;
uart_add_one_port(drv, &up->port);
}
static struct spi_driver max3100_driver = {
.driver = {
.name = "max3100",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
},
static struct spi_driver max3107_driver = {
.driver = {
.name = "aava-max3107",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = max3107_probe_aava,
static struct spi_driver max3107_driver = {
.driver = {
.name = "max3107",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = max3107_probe_generic,
int bits = 8;
int parity = 'n';
int flow = 'n';
- int ret;
if (co->index == -1 || co->index >= serial_hsu_reg.nr)
co->index = 0;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
-
- return ret;
+ return uart_set_options(&up->port, co, baud, parity, bits, flow);
}
static struct console serial_hsu_console = {
.device = uart_console_device,
.setup = serial_hsu_console_setup,
.flags = CON_PRINTBUFFER,
- .index = 2,
+ .index = -1,
.data = &serial_hsu_reg,
};
+
+#define SERIAL_HSU_CONSOLE (&serial_hsu_console)
+#else
+#define SERIAL_HSU_CONSOLE NULL
#endif
struct uart_ops serial_hsu_pops = {
.major = TTY_MAJOR,
.minor = 128,
.nr = 3,
+ .cons = SERIAL_HSU_CONSOLE,
};
#ifdef CONFIG_PM
}
uart_add_one_port(&serial_hsu_reg, &uport->port);
-#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
- if (index == 2) {
- register_console(&serial_hsu_console);
- uport->port.cons = &serial_hsu_console;
- }
-#endif
pci_set_drvdata(pdev, uport);
}
static struct spi_driver uart_max3110_driver = {
.driver = {
.name = "spi_max3111",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = serial_m3110_probe,
msm_uport->rx.rbuffer);
dma_pool_destroy(msm_uport->rx.pool);
- dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *),
+ dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32),
DMA_TO_DEVICE);
- dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *),
+ dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32),
DMA_TO_DEVICE);
dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box),
DMA_TO_DEVICE);
*tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
/* Save tx_count to use in Callback */
tx->tx_count = tx_count;
}
/* Handle CTS changes (Called from interrupt handler) */
-static void msm_hs_handle_delta_cts(struct uart_port *uport)
+static void msm_hs_handle_delta_cts_locked(struct uart_port *uport)
{
- unsigned long flags;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- spin_lock_irqsave(&uport->lock, flags);
clk_enable(msm_uport->clk);
/* clear interrupt */
uport->icount.cts++;
clk_disable(msm_uport->clk);
- spin_unlock_irqrestore(&uport->lock, flags);
/* clear the IOCTL TIOCMIWAIT if called */
wake_up_interruptible(&uport->state->port.delta_msr_wait);
/* Change in CTS interrupt */
if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK)
- msm_hs_handle_delta_cts(uport);
+ msm_hs_handle_delta_cts_locked(uport);
spin_unlock_irqrestore(&uport->lock, flags);
if (!tx->command_ptr)
return -ENOMEM;
- tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
if (!tx->command_ptr_ptr) {
ret = -ENOMEM;
goto err_tx_command_ptr_ptr;
sizeof(dmov_box), DMA_TO_DEVICE);
tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
tx->command_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
init_waitqueue_head(&rx->wait);
goto err_rx_command_ptr;
}
- rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+ rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__);
ret = -ENOMEM;
*rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr);
rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work);
dma_pool_destroy(msm_uport->rx.pool);
err_dma_pool_create:
dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
sizeof(dmov_box), DMA_TO_DEVICE);
kfree(msm_uport->tx.command_ptr_ptr);
writel(xmit->buf[xmit->tail],
s->port.membase + AUART_DATA);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
} else
break;
}
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+
if (uart_circ_empty(&(s->port.state->xmit)))
writel(AUART_INTR_TXIEN,
s->port.membase + AUART_INTR_CLR);
static unsigned int serial_omap_get_mctrl(struct uart_port *port)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
- unsigned char status;
+ unsigned int status;
unsigned int ret = 0;
status = check_modem_status(up);
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/dmi.h>
+#include <linux/console.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/pch_dma.h>
#define PCI_VENDOR_ID_ROHM 0x10DB
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+#define DEFAULT_BAUD_RATE 1843200 /* 1.8432MHz */
+
struct pch_uart_buffer {
unsigned char *buf;
int size;
[pch_ml7831_uart1] = {PCH_UART_2LINE, 1},
};
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+static struct eg20t_port *pch_uart_ports[PCH_UART_NR];
+#endif
static unsigned int default_baud = 9600;
static const int trigger_level_256[4] = { 1, 64, 128, 224 };
static const int trigger_level_64[4] = { 1, 16, 32, 56 };
.verify_port = pch_uart_verify_port
};
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+
+/*
+ * Wait for transmitter & holding register to empty
+ */
+static void wait_for_xmitr(struct eg20t_port *up, int bits)
+{
+ unsigned int status, tmout = 10000;
+
+ /* Wait up to 10ms for the character(s) to be sent. */
+ for (;;) {
+ status = ioread8(up->membase + UART_LSR);
+
+ if ((status & bits) == bits)
+ break;
+ if (--tmout == 0)
+ break;
+ udelay(1);
+ }
+
+ /* Wait up to 1s for flow control if necessary */
+ if (up->port.flags & UPF_CONS_FLOW) {
+ unsigned int tmout;
+ for (tmout = 1000000; tmout; tmout--) {
+ unsigned int msr = ioread8(up->membase + UART_MSR);
+ if (msr & UART_MSR_CTS)
+ break;
+ udelay(1);
+ touch_nmi_watchdog();
+ }
+ }
+}
+
+static void pch_console_putchar(struct uart_port *port, int ch)
+{
+ struct eg20t_port *priv =
+ container_of(port, struct eg20t_port, port);
+
+ wait_for_xmitr(priv, UART_LSR_THRE);
+ iowrite8(ch, priv->membase + PCH_UART_THR);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
+ */
+static void
+pch_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct eg20t_port *priv;
+
+ unsigned long flags;
+ u8 ier;
+ int locked = 1;
+
+ priv = pch_uart_ports[co->index];
+
+ touch_nmi_watchdog();
+
+ local_irq_save(flags);
+ if (priv->port.sysrq) {
+ /* serial8250_handle_port() already took the lock */
+ locked = 0;
+ } else if (oops_in_progress) {
+ locked = spin_trylock(&priv->port.lock);
+ } else
+ spin_lock(&priv->port.lock);
+
+ /*
+ * First save the IER then disable the interrupts
+ */
+ ier = ioread8(priv->membase + UART_IER);
+
+ pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
+
+ uart_console_write(&priv->port, s, count, pch_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(priv, BOTH_EMPTY);
+ iowrite8(ier, priv->membase + UART_IER);
+
+ if (locked)
+ spin_unlock(&priv->port.lock);
+ local_irq_restore(flags);
+}
+
+static int __init pch_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= PCH_UART_NR)
+ co->index = 0;
+ port = &pch_uart_ports[co->index]->port;
+
+ if (!port || (!port->iobase && !port->membase))
+ return -ENODEV;
+
+ /* setup uartclock */
+ port->uartclk = DEFAULT_BAUD_RATE;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver pch_uart_driver;
+
+static struct console pch_console = {
+ .name = PCH_UART_DRIVER_DEVICE,
+ .write = pch_console_write,
+ .device = uart_console_device,
+ .setup = pch_console_setup,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME,
+ .index = -1,
+ .data = &pch_uart_driver,
+};
+
+#define PCH_CONSOLE (&pch_console)
+#else
+#define PCH_CONSOLE NULL
+#endif
+
static struct uart_driver pch_uart_driver = {
.owner = THIS_MODULE,
.driver_name = KBUILD_MODNAME,
.major = 0,
.minor = 0,
.nr = PCH_UART_NR,
+ .cons = PCH_CONSOLE,
};
static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
if (!rxbuf)
goto init_port_free_txbuf;
- base_baud = 1843200; /* 1.8432MHz */
+ base_baud = DEFAULT_BAUD_RATE;
/* quirk for CM-iTC board */
board_name = dmi_get_system_info(DMI_BOARD_NAME);
pci_set_drvdata(pdev, priv);
pch_uart_hal_request(pdev, fifosize, base_baud);
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+ pch_uart_ports[board->line_no] = priv;
+#endif
ret = uart_add_one_port(&pch_uart_driver, &priv->port);
if (ret < 0)
goto init_port_hal_free;
return priv;
init_port_hal_free:
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+ pch_uart_ports[board->line_no] = NULL;
+#endif
free_page((unsigned long)rxbuf);
init_port_free_txbuf:
kfree(priv);
priv = (struct eg20t_port *)pci_get_drvdata(pdev);
pci_disable_msi(pdev);
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+ pch_uart_ports[priv->port.line] = NULL;
+#endif
pch_uart_exit_port(priv);
pci_disable_device(pdev);
kfree(priv);
},
};
-static int __init sc26xx_init(void)
-{
- return platform_driver_register(&sc26xx_driver);
-}
-
-static void __exit sc26xx_exit(void)
-{
- platform_driver_unregister(&sc26xx_driver);
-}
-
-module_init(sc26xx_init);
-module_exit(sc26xx_exit);
-
+module_platform_driver(sc26xx_driver);
MODULE_AUTHOR("Thomas Bogendörfer");
MODULE_DESCRIPTION("SC681/SC2692 serial driver");
*/
#include <linux/module.h>
#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/console.h>
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
static void uart_change_pm(struct uart_state *state, int pm_state);
+static void uart_port_shutdown(struct tty_port *port);
+
/*
* This routine is used by the interrupt handler to schedule processing in
* the software interrupt portion of the driver.
* Startup the port. This will be called once per open. All calls
* will be serialised by the per-port mutex.
*/
-static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
+static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
+ int init_hw)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
unsigned long page;
int retval = 0;
- if (port->flags & ASYNC_INITIALIZED)
- return 0;
-
- /*
- * Set the TTY IO error marker - we will only clear this
- * once we have successfully opened the port. Also set
- * up the tty->alt_speed kludge
- */
- set_bit(TTY_IO_ERROR, &tty->flags);
-
if (uport->type == PORT_UNKNOWN)
- return 0;
+ return 1;
/*
* Initialise and allocate the transmit and temporary
tty->hw_stopped = 1;
spin_unlock_irq(&uport->lock);
}
-
- set_bit(ASYNCB_INITIALIZED, &port->flags);
-
- clear_bit(TTY_IO_ERROR, &tty->flags);
}
/*
* now.
*/
if (retval && capable(CAP_SYS_ADMIN))
+ return 1;
+
+ return retval;
+}
+
+static int uart_startup(struct tty_struct *tty, struct uart_state *state,
+ int init_hw)
+{
+ struct tty_port *port = &state->port;
+ int retval;
+
+ if (port->flags & ASYNC_INITIALIZED)
+ return 0;
+
+ /*
+ * Set the TTY IO error marker - we will only clear this
+ * once we have successfully opened the port.
+ */
+ set_bit(TTY_IO_ERROR, &tty->flags);
+
+ retval = uart_port_startup(tty, state, init_hw);
+ if (!retval) {
+ set_bit(ASYNCB_INITIALIZED, &port->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ } else if (retval > 0)
retval = 0;
return retval;
if (!tty || (tty->termios->c_cflag & HUPCL))
uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
- /*
- * clear delta_msr_wait queue to avoid mem leaks: we may free
- * the irq here so the queue might never be woken up. Note
- * that we won't end up waiting on delta_msr_wait again since
- * any outstanding file descriptors should be pointing at
- * hung_up_tty_fops now.
- */
- wake_up_interruptible(&port->delta_msr_wait);
-
- /*
- * Free the IRQ and disable the port.
- */
- uport->ops->shutdown(uport);
-
- /*
- * Ensure that the IRQ handler isn't running on another CPU.
- */
- synchronize_irq(uport->irq);
+ uart_port_shutdown(port);
}
/*
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
else
- quot = (port->uartclk + (8 * baud)) / (16 * baud);
+ quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
return quot;
}
tmp.flags = uport->flags;
tmp.xmit_fifo_size = uport->fifosize;
tmp.baud_base = uport->uartclk / 16;
- tmp.close_delay = port->close_delay / 10;
+ tmp.close_delay = jiffies_to_msecs(port->close_delay) / 10;
tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
- port->closing_wait / 10;
+ jiffies_to_msecs(port->closing_wait) / 10;
tmp.custom_divisor = uport->custom_divisor;
tmp.hub6 = uport->hub6;
tmp.io_type = uport->iotype;
new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
new_serial.irq = irq_canonicalize(new_serial.irq);
- close_delay = new_serial.close_delay * 10;
+ close_delay = msecs_to_jiffies(new_serial.close_delay * 10);
closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
- ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+ ASYNC_CLOSING_WAIT_NONE :
+ msecs_to_jiffies(new_serial.closing_wait * 10);
/*
* This semaphore protects port->count. It is also
pr_debug("uart_close(%d) called\n", uport->line);
- spin_lock_irqsave(&port->lock, flags);
-
- if (tty_hung_up_p(filp)) {
- spin_unlock_irqrestore(&port->lock, flags);
+ if (tty_port_close_start(port, tty, filp) == 0)
return;
- }
-
- if ((tty->count == 1) && (port->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. port->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, "
- "port->count is %d\n", port->count);
- port->count = 1;
- }
- if (--port->count < 0) {
- printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n",
- tty->name, port->count);
- port->count = 0;
- }
- if (port->count) {
- spin_unlock_irqrestore(&port->lock, flags);
- return;
- }
-
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters by
- * setting tty->closing.
- */
- set_bit(ASYNCB_CLOSING, &port->flags);
- tty->closing = 1;
- spin_unlock_irqrestore(&port->lock, flags);
-
- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent_from_close(tty,
- msecs_to_jiffies(port->closing_wait));
/*
* At this point, we stop accepting input. To do this, we
if (port->blocked_open) {
spin_unlock_irqrestore(&port->lock, flags);
if (port->close_delay)
- msleep_interruptible(port->close_delay);
+ msleep_interruptible(
+ jiffies_to_msecs(port->close_delay));
spin_lock_irqsave(&port->lock, flags);
} else if (!uart_console(uport)) {
spin_unlock_irqrestore(&port->lock, flags);
mutex_unlock(&port->mutex);
}
+static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ return 0;
+}
+
+static void uart_port_shutdown(struct tty_port *port)
+{
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = state->uart_port;
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free
+ * the irq here so the queue might never be woken up. Note
+ * that we won't end up waiting on delta_msr_wait again since
+ * any outstanding file descriptors should be pointing at
+ * hung_up_tty_fops now.
+ */
+ wake_up_interruptible(&port->delta_msr_wait);
+
+ /*
+ * Free the IRQ and disable the port.
+ */
+ uport->ops->shutdown(uport);
+
+ /*
+ * Ensure that the IRQ handler isn't running on another CPU.
+ */
+ synchronize_irq(uport->irq);
+}
+
static int uart_carrier_raised(struct tty_port *port)
{
struct uart_state *state = container_of(port, struct uart_state, port);
uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
}
-static struct uart_state *uart_get(struct uart_driver *drv, int line)
-{
- struct uart_state *state;
- struct tty_port *port;
- int ret = 0;
-
- state = drv->state + line;
- port = &state->port;
- if (mutex_lock_interruptible(&port->mutex)) {
- ret = -ERESTARTSYS;
- goto err;
- }
-
- port->count++;
- if (!state->uart_port || state->uart_port->flags & UPF_DEAD) {
- ret = -ENXIO;
- goto err_unlock;
- }
- return state;
-
- err_unlock:
- port->count--;
- mutex_unlock(&port->mutex);
- err:
- return ERR_PTR(ret);
-}
-
/*
* calls to uart_open are serialised by the BKL in
* fs/char_dev.c:chrdev_open()
static int uart_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
- struct uart_state *state;
- struct tty_port *port;
int retval, line = tty->index;
+ struct uart_state *state = drv->state + line;
+ struct tty_port *port = &state->port;
pr_debug("uart_open(%d) called\n", line);
/*
- * We take the semaphore inside uart_get to guarantee that we won't
- * be re-entered while allocating the state structure, or while we
- * request any IRQs that the driver may need. This also has the nice
- * side-effect that it delays the action of uart_hangup, so we can
- * guarantee that state->port.tty will always contain something
- * reasonable.
+ * We take the semaphore here to guarantee that we won't be re-entered
+ * while allocating the state structure, or while we request any IRQs
+ * that the driver may need. This also has the nice side-effect that
+ * it delays the action of uart_hangup, so we can guarantee that
+ * state->port.tty will always contain something reasonable.
*/
- state = uart_get(drv, line);
- if (IS_ERR(state)) {
- retval = PTR_ERR(state);
- goto fail;
+ if (mutex_lock_interruptible(&port->mutex)) {
+ retval = -ERESTARTSYS;
+ goto end;
+ }
+
+ port->count++;
+ if (!state->uart_port || state->uart_port->flags & UPF_DEAD) {
+ retval = -ENXIO;
+ goto err_dec_count;
}
- port = &state->port;
/*
* Once we set tty->driver_data here, we are guaranteed that
tty->driver_data = state;
state->uart_port->state = state;
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
- tty->alt_speed = 0;
tty_port_tty_set(port, tty);
/*
*/
if (tty_hung_up_p(filp)) {
retval = -EAGAIN;
- port->count--;
- mutex_unlock(&port->mutex);
- goto fail;
+ goto err_dec_count;
}
/*
if (retval == 0)
retval = tty_port_block_til_ready(port, tty, filp);
-fail:
+end:
return retval;
+err_dec_count:
+ port->count--;
+ mutex_unlock(&port->mutex);
+ goto end;
}
static const char *uart_type(struct uart_port *port)
EXPORT_SYMBOL_GPL(uart_set_options);
#endif /* CONFIG_SERIAL_CORE_CONSOLE */
+/**
+ * uart_change_pm - set power state of the port
+ *
+ * @state: port descriptor
+ * @pm_state: new state
+ *
+ * Locking: port->mutex has to be held
+ */
static void uart_change_pm(struct uart_state *state, int pm_state)
{
struct uart_port *port = state->uart_port;
};
static const struct tty_port_operations uart_port_ops = {
+ .activate = uart_port_activate,
+ .shutdown = uart_port_shutdown,
.carrier_raised = uart_carrier_raised,
.dtr_rts = uart_dtr_rts,
};
tty_port_init(port);
port->ops = &uart_port_ops;
- port->close_delay = 500; /* .5 seconds */
- port->closing_wait = 30000; /* 30 seconds */
+ port->close_delay = HZ / 2; /* .5 seconds */
+ port->closing_wait = 30 * HZ;/* 30 seconds */
}
retval = tty_register_driver(normal);
}
EXPORT_SYMBOL(uart_match_port);
+/**
+ * uart_handle_dcd_change - handle a change of carrier detect state
+ * @uport: uart_port structure for the open port
+ * @status: new carrier detect status, nonzero if active
+ */
+void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
+{
+ struct uart_state *state = uport->state;
+ struct tty_port *port = &state->port;
+ struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+ struct pps_event_time ts;
+
+ if (ld && ld->ops->dcd_change)
+ pps_get_ts(&ts);
+
+ uport->icount.dcd++;
+#ifdef CONFIG_HARD_PPS
+ if ((uport->flags & UPF_HARDPPS_CD) && status)
+ hardpps();
+#endif
+
+ if (port->flags & ASYNC_CHECK_CD) {
+ if (status)
+ wake_up_interruptible(&port->open_wait);
+ else if (port->tty)
+ tty_hangup(port->tty);
+ }
+
+ if (ld && ld->ops->dcd_change)
+ ld->ops->dcd_change(port->tty, status, &ts);
+ if (ld)
+ tty_ldisc_deref(ld);
+}
+EXPORT_SYMBOL_GPL(uart_handle_dcd_change);
+
+/**
+ * uart_handle_cts_change - handle a change of clear-to-send state
+ * @uport: uart_port structure for the open port
+ * @status: new clear to send status, nonzero if active
+ */
+void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
+{
+ struct tty_port *port = &uport->state->port;
+ struct tty_struct *tty = port->tty;
+
+ uport->icount.cts++;
+
+ if (port->flags & ASYNC_CTS_FLOW) {
+ if (tty->hw_stopped) {
+ if (status) {
+ tty->hw_stopped = 0;
+ uport->ops->start_tx(uport);
+ uart_write_wakeup(uport);
+ }
+ } else {
+ if (!status) {
+ tty->hw_stopped = 1;
+ uport->ops->stop_tx(uport);
+ }
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(uart_handle_cts_change);
+
+/**
+ * uart_insert_char - push a char to the uart layer
+ *
+ * User is responsible to call tty_flip_buffer_push when they are done with
+ * insertion.
+ *
+ * @port: corresponding port
+ * @status: state of the serial port RX buffer (LSR for 8250)
+ * @overrun: mask of overrun bits in @status
+ * @ch: character to push
+ * @flag: flag for the character (see TTY_NORMAL and friends)
+ */
+void uart_insert_char(struct uart_port *port, unsigned int status,
+ unsigned int overrun, unsigned int ch, unsigned int flag)
+{
+ struct tty_struct *tty = port->state->port.tty;
+
+ if ((status & port->ignore_status_mask & ~overrun) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+
+ /*
+ * Overrun is special. Since it's reported immediately,
+ * it doesn't affect the current character.
+ */
+ if (status & ~port->ignore_status_mask & overrun)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+}
+EXPORT_SYMBOL_GPL(uart_insert_char);
+
EXPORT_SYMBOL(uart_write_wakeup);
EXPORT_SYMBOL(uart_register_driver);
EXPORT_SYMBOL(uart_unregister_driver);
info->p_dev = link;
link->priv = info;
- link->config_flags |= CONF_ENABLE_IRQ;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
if (do_sound)
link->config_flags |= CONF_ENABLE_SPKR;
/* First pass: look for a config entry that looks normal.
* Two tries: without IO aliases, then with aliases */
- link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO;
+ link->config_flags |= CONF_AUTO_SET_VPP;
for (try = 0; try < 4; try++)
if (!pcmcia_loop_config(link, simple_config_check, &try))
goto found_port;
{
int *base2 = priv_data;
- if (!p_dev->resource[0]->end || !p_dev->resource[1]->end)
+ if (!p_dev->resource[0]->end || !p_dev->resource[1]->end ||
+ p_dev->resource[0]->start + 8 != p_dev->resource[1]->start)
return -ENODEV;
p_dev->resource[0]->end = p_dev->resource[1]->end = 8;
struct serial_info *info = link->priv;
int i, base2 = 0;
- link->config_flags |= CONF_AUTO_SET_IO;
/* First, look for a generic full-sized window */
if (!pcmcia_loop_config(link, multi_config_check, &info->multi))
base2 = link->resource[0]->start + 8;
--- /dev/null
+/*
+ * Driver for CSR SiRFprimaII onboard UARTs.
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "sirfsoc_uart.h"
+
+static unsigned int
+sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count);
+static unsigned int
+sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count);
+static struct uart_driver sirfsoc_uart_drv;
+
+static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = {
+ {4000000, 2359296},
+ {3500000, 1310721},
+ {3000000, 1572865},
+ {2500000, 1245186},
+ {2000000, 1572866},
+ {1500000, 1245188},
+ {1152000, 1638404},
+ {1000000, 1572869},
+ {921600, 1114120},
+ {576000, 1245196},
+ {500000, 1245198},
+ {460800, 1572876},
+ {230400, 1310750},
+ {115200, 1310781},
+ {57600, 1310843},
+ {38400, 1114328},
+ {19200, 1114545},
+ {9600, 1114979},
+};
+
+static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
+ [0] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ },
+ [1] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ },
+ [2] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ },
+};
+
+static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
+{
+ return container_of(port, struct sirfsoc_uart_port, port);
+}
+
+static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port)
+{
+ unsigned long reg;
+ reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS);
+ if (reg & SIRFUART_FIFOEMPTY_MASK(port))
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ if (!(sirfport->ms_enabled)) {
+ goto cts_asserted;
+ } else if (sirfport->hw_flow_ctrl) {
+ if (!(rd_regl(port, SIRFUART_AFC_CTRL) &
+ SIRFUART_CTS_IN_STATUS))
+ goto cts_asserted;
+ else
+ goto cts_deasserted;
+ }
+cts_deasserted:
+ return TIOCM_CAR | TIOCM_DSR;
+cts_asserted:
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned int assert = mctrl & TIOCM_RTS;
+ unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
+ unsigned int current_val;
+ if (sirfport->hw_flow_ctrl) {
+ current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF;
+ val |= current_val;
+ wr_regl(port, SIRFUART_AFC_CTRL, val);
+ }
+}
+
+static void sirfsoc_uart_stop_tx(struct uart_port *port)
+{
+ unsigned int regv;
+ regv = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN);
+}
+
+void sirfsoc_uart_start_tx(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned long regv;
+ sirfsoc_uart_pio_tx_chars(sirfport, 1);
+ wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START);
+ regv = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN);
+}
+
+static void sirfsoc_uart_stop_rx(struct uart_port *port)
+{
+ unsigned long regv;
+ wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+ regv = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN);
+}
+
+static void sirfsoc_uart_disable_ms(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned long reg;
+ sirfport->ms_enabled = 0;
+ if (!sirfport->hw_flow_ctrl)
+ return;
+ reg = rd_regl(port, SIRFUART_AFC_CTRL);
+ wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF);
+ reg = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN);
+}
+
+static void sirfsoc_uart_enable_ms(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned long reg;
+ unsigned long flg;
+ if (!sirfport->hw_flow_ctrl)
+ return;
+ flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN;
+ reg = rd_regl(port, SIRFUART_AFC_CTRL);
+ wr_regl(port, SIRFUART_AFC_CTRL, reg | flg);
+ reg = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN);
+ uart_handle_cts_change(port,
+ !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS));
+ sirfport->ms_enabled = 1;
+}
+
+static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL);
+ if (break_state)
+ ulcon |= SIRFUART_SET_BREAK;
+ else
+ ulcon &= ~SIRFUART_SET_BREAK;
+ wr_regl(port, SIRFUART_LINE_CTRL, ulcon);
+}
+
+static unsigned int
+sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
+{
+ unsigned int ch, rx_count = 0;
+ struct tty_struct *tty;
+
+ tty = tty_port_tty_get(&port->state->port);
+ if (!tty)
+ return -ENODEV;
+
+ while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) &
+ SIRFUART_FIFOEMPTY_MASK(port))) {
+ ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ;
+ if (unlikely(uart_handle_sysrq_char(port, ch)))
+ continue;
+ uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
+ rx_count++;
+ if (rx_count >= max_rx_count)
+ break;
+ }
+
+ port->icount.rx += rx_count;
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+
+ return rx_count;
+}
+
+static unsigned int
+sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
+{
+ struct uart_port *port = &sirfport->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int num_tx = 0;
+ while (!uart_circ_empty(xmit) &&
+ !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
+ SIRFUART_FIFOFULL_MASK(port)) &&
+ count--) {
+ wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ num_tx++;
+ }
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ return num_tx;
+}
+
+static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
+{
+ unsigned long intr_status;
+ unsigned long cts_status;
+ unsigned long flag = TTY_NORMAL;
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
+ struct uart_port *port = &sirfport->port;
+ struct uart_state *state = port->state;
+ struct circ_buf *xmit = &port->state->xmit;
+ intr_status = rd_regl(port, SIRFUART_INT_STATUS);
+ wr_regl(port, SIRFUART_INT_STATUS, intr_status);
+ intr_status &= rd_regl(port, SIRFUART_INT_EN);
+ if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) {
+ if (intr_status & SIRFUART_RXD_BREAK) {
+ if (uart_handle_break(port))
+ goto recv_char;
+ uart_insert_char(port, intr_status,
+ SIRFUART_RX_OFLOW, 0, TTY_BREAK);
+ return IRQ_HANDLED;
+ }
+ if (intr_status & SIRFUART_RX_OFLOW)
+ port->icount.overrun++;
+ if (intr_status & SIRFUART_FRM_ERR) {
+ port->icount.frame++;
+ flag = TTY_FRAME;
+ }
+ if (intr_status & SIRFUART_PARITY_ERR)
+ flag = TTY_PARITY;
+ wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+ intr_status &= port->read_status_mask;
+ uart_insert_char(port, intr_status,
+ SIRFUART_RX_OFLOW_INT, 0, flag);
+ }
+recv_char:
+ if (intr_status & SIRFUART_CTS_INT_EN) {
+ cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) &
+ SIRFUART_CTS_IN_STATUS);
+ if (cts_status != 0) {
+ uart_handle_cts_change(port, 1);
+ } else {
+ uart_handle_cts_change(port, 0);
+ wake_up_interruptible(&state->port.delta_msr_wait);
+ }
+ }
+ if (intr_status & SIRFUART_RX_IO_INT_EN)
+ sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT);
+ if (intr_status & SIRFUART_TX_INT_EN) {
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ return IRQ_HANDLED;
+ } else {
+ sirfsoc_uart_pio_tx_chars(sirfport,
+ SIRFSOC_UART_IO_TX_REASONABLE_CNT);
+ if ((uart_circ_empty(xmit)) &&
+ (rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
+ SIRFUART_FIFOEMPTY_MASK(port)))
+ sirfsoc_uart_stop_tx(port);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static void sirfsoc_uart_start_rx(struct uart_port *port)
+{
+ unsigned long regv;
+ regv = rd_regl(port, SIRFUART_INT_EN);
+ wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+}
+
+static unsigned int
+sirfsoc_calc_sample_div(unsigned long baud_rate,
+ unsigned long ioclk_rate, unsigned long *setted_baud)
+{
+ unsigned long min_delta = ~0UL;
+ unsigned short sample_div;
+ unsigned int regv = 0;
+ unsigned long ioclk_div;
+ unsigned long baud_tmp;
+ int temp_delta;
+
+ for (sample_div = SIRF_MIN_SAMPLE_DIV;
+ sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) {
+ ioclk_div = (ioclk_rate / (baud_rate * (sample_div + 1))) - 1;
+ if (ioclk_div > SIRF_IOCLK_DIV_MAX)
+ continue;
+ baud_tmp = ioclk_rate / ((ioclk_div + 1) * (sample_div + 1));
+ temp_delta = baud_tmp - baud_rate;
+ temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta;
+ if (temp_delta < min_delta) {
+ regv = regv & (~SIRF_IOCLK_DIV_MASK);
+ regv = regv | ioclk_div;
+ regv = regv & (~SIRF_SAMPLE_DIV_MASK);
+ regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT);
+ min_delta = temp_delta;
+ *setted_baud = baud_tmp;
+ }
+ }
+ return regv;
+}
+
+static void sirfsoc_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned long ioclk_rate;
+ unsigned long config_reg = 0;
+ unsigned long baud_rate;
+ unsigned long setted_baud;
+ unsigned long flags;
+ unsigned long ic;
+ unsigned int clk_div_reg = 0;
+ unsigned long temp_reg_val;
+ unsigned long rx_time_out;
+ int threshold_div;
+ int temp;
+
+ ioclk_rate = 150000000;
+ switch (termios->c_cflag & CSIZE) {
+ default:
+ case CS8:
+ config_reg |= SIRFUART_DATA_BIT_LEN_8;
+ break;
+ case CS7:
+ config_reg |= SIRFUART_DATA_BIT_LEN_7;
+ break;
+ case CS6:
+ config_reg |= SIRFUART_DATA_BIT_LEN_6;
+ break;
+ case CS5:
+ config_reg |= SIRFUART_DATA_BIT_LEN_5;
+ break;
+ }
+ if (termios->c_cflag & CSTOPB)
+ config_reg |= SIRFUART_STOP_BIT_LEN_2;
+ baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ spin_lock_irqsave(&port->lock, flags);
+ port->read_status_mask = SIRFUART_RX_OFLOW_INT;
+ port->ignore_status_mask = 0;
+ /* read flags */
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |=
+ SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= SIRFUART_RXD_BREAK_INT;
+ /* ignore flags */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |=
+ SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= SIRFUART_DUMMY_READ;
+ /* enable parity if PARENB is set*/
+ if (termios->c_cflag & PARENB) {
+ if (termios->c_cflag & CMSPAR) {
+ if (termios->c_cflag & PARODD)
+ config_reg |= SIRFUART_STICK_BIT_MARK;
+ else
+ config_reg |= SIRFUART_STICK_BIT_SPACE;
+ } else if (termios->c_cflag & PARODD) {
+ config_reg |= SIRFUART_STICK_BIT_ODD;
+ } else {
+ config_reg |= SIRFUART_STICK_BIT_EVEN;
+ }
+ }
+ /* Hardware Flow Control Settings */
+ if (UART_ENABLE_MS(port, termios->c_cflag)) {
+ if (!sirfport->ms_enabled)
+ sirfsoc_uart_enable_ms(port);
+ } else {
+ if (sirfport->ms_enabled)
+ sirfsoc_uart_disable_ms(port);
+ }
+
+ /* common rate: fast calculation */
+ for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
+ if (baud_rate == baudrate_to_regv[ic].baud_rate)
+ clk_div_reg = baudrate_to_regv[ic].reg_val;
+ setted_baud = baud_rate;
+ /* arbitary rate setting */
+ if (unlikely(clk_div_reg == 0))
+ clk_div_reg = sirfsoc_calc_sample_div(baud_rate, ioclk_rate,
+ &setted_baud);
+ wr_regl(port, SIRFUART_DIVISOR, clk_div_reg);
+
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, setted_baud, setted_baud);
+
+ /* set receive timeout */
+ rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000);
+ rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out;
+ config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out);
+ temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+ wr_regl(port, SIRFUART_TX_FIFO_OP,
+ temp_reg_val & ~SIRFUART_TX_FIFO_START);
+ wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO);
+ wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO);
+ wr_regl(port, SIRFUART_LINE_CTRL, config_reg);
+
+ /* Reset Rx/Tx FIFO Threshold level for proper baudrate */
+ if (baud_rate < 1000000)
+ threshold_div = 1;
+ else
+ threshold_div = 2;
+ temp = port->line == 1 ? 16 : 64;
+ wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div);
+ wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div);
+ temp_reg_val |= SIRFUART_TX_FIFO_START;
+ wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val);
+ uart_update_timeout(port, termios->c_cflag, baud_rate);
+ sirfsoc_uart_start_rx(port);
+ wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void startup_uart_controller(struct uart_port *port)
+{
+ unsigned long temp_regv;
+ int temp;
+ temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL);
+ wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO);
+ temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL);
+ wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO);
+ wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0);
+ wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0);
+ wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN);
+ wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET);
+ wr_regl(port, SIRFUART_TX_FIFO_OP, 0);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+ wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+ temp = port->line == 1 ? 16 : 64;
+ wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp);
+ wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp);
+}
+
+static int sirfsoc_uart_startup(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ unsigned int index = port->line;
+ int ret;
+ set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN);
+ ret = request_irq(port->irq,
+ sirfsoc_uart_isr,
+ 0,
+ SIRFUART_PORT_NAME,
+ sirfport);
+ if (ret != 0) {
+ dev_err(port->dev, "UART%d request IRQ line (%d) failed.\n",
+ index, port->irq);
+ goto irq_err;
+ }
+ startup_uart_controller(port);
+ enable_irq(port->irq);
+irq_err:
+ return ret;
+}
+
+static void sirfsoc_uart_shutdown(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ wr_regl(port, SIRFUART_INT_EN, 0);
+ free_irq(port->irq, sirfport);
+ if (sirfport->ms_enabled) {
+ sirfsoc_uart_disable_ms(port);
+ sirfport->ms_enabled = 0;
+ }
+}
+
+static const char *sirfsoc_uart_type(struct uart_port *port)
+{
+ return port->type == SIRFSOC_PORT_TYPE ? SIRFUART_PORT_NAME : NULL;
+}
+
+static int sirfsoc_uart_request_port(struct uart_port *port)
+{
+ void *ret;
+ ret = request_mem_region(port->mapbase,
+ SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME);
+ return ret ? 0 : -EBUSY;
+}
+
+static void sirfsoc_uart_release_port(struct uart_port *port)
+{
+ release_mem_region(port->mapbase, SIRFUART_MAP_SIZE);
+}
+
+static void sirfsoc_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = SIRFSOC_PORT_TYPE;
+ sirfsoc_uart_request_port(port);
+ }
+}
+
+static struct uart_ops sirfsoc_uart_ops = {
+ .tx_empty = sirfsoc_uart_tx_empty,
+ .get_mctrl = sirfsoc_uart_get_mctrl,
+ .set_mctrl = sirfsoc_uart_set_mctrl,
+ .stop_tx = sirfsoc_uart_stop_tx,
+ .start_tx = sirfsoc_uart_start_tx,
+ .stop_rx = sirfsoc_uart_stop_rx,
+ .enable_ms = sirfsoc_uart_enable_ms,
+ .break_ctl = sirfsoc_uart_break_ctl,
+ .startup = sirfsoc_uart_startup,
+ .shutdown = sirfsoc_uart_shutdown,
+ .set_termios = sirfsoc_uart_set_termios,
+ .type = sirfsoc_uart_type,
+ .release_port = sirfsoc_uart_release_port,
+ .request_port = sirfsoc_uart_request_port,
+ .config_port = sirfsoc_uart_config_port,
+};
+
+#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
+static int __init sirfsoc_uart_console_setup(struct console *co, char *options)
+{
+ unsigned int baud = 115200;
+ unsigned int bits = 8;
+ unsigned int parity = 'n';
+ unsigned int flow = 'n';
+ struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
+
+ if (co->index < 0 || co->index >= SIRFSOC_UART_NR)
+ return -EINVAL;
+
+ if (!port->mapbase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ port->cons = co;
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch)
+{
+ while (rd_regl(port,
+ SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port))
+ cpu_relax();
+ wr_regb(port, SIRFUART_TX_FIFO_DATA, ch);
+}
+
+static void sirfsoc_uart_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
+ uart_console_write(port, s, count, sirfsoc_uart_console_putchar);
+}
+
+static struct console sirfsoc_uart_console = {
+ .name = SIRFSOC_UART_NAME,
+ .device = uart_console_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .write = sirfsoc_uart_console_write,
+ .setup = sirfsoc_uart_console_setup,
+ .data = &sirfsoc_uart_drv,
+};
+
+static int __init sirfsoc_uart_console_init(void)
+{
+ register_console(&sirfsoc_uart_console);
+ return 0;
+}
+console_initcall(sirfsoc_uart_console_init);
+#endif
+
+static struct uart_driver sirfsoc_uart_drv = {
+ .owner = THIS_MODULE,
+ .driver_name = SIRFUART_PORT_NAME,
+ .nr = SIRFSOC_UART_NR,
+ .dev_name = SIRFSOC_UART_NAME,
+ .major = SIRFSOC_UART_MAJOR,
+ .minor = SIRFSOC_UART_MINOR,
+#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
+ .cons = &sirfsoc_uart_console,
+#else
+ .cons = NULL,
+#endif
+};
+
+int sirfsoc_uart_probe(struct platform_device *pdev)
+{
+ struct sirfsoc_uart_port *sirfport;
+ struct uart_port *port;
+ struct resource *res;
+ int ret;
+
+ if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {
+ dev_err(&pdev->dev,
+ "Unable to find cell-index in uart node.\n");
+ ret = -EFAULT;
+ goto err;
+ }
+
+ sirfport = &sirfsoc_uart_ports[pdev->id];
+ port = &sirfport->port;
+ port->dev = &pdev->dev;
+ port->private_data = sirfport;
+
+ if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
+ sirfport->hw_flow_ctrl = 1;
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "fifosize",
+ &port->fifosize)) {
+ dev_err(&pdev->dev,
+ "Unable to find fifosize in uart node.\n");
+ ret = -EFAULT;
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Insufficient resources.\n");
+ ret = -EFAULT;
+ goto err;
+ }
+ port->mapbase = res->start;
+ port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!port->membase) {
+ dev_err(&pdev->dev, "Cannot remap resource.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Insufficient resources.\n");
+ ret = -EFAULT;
+ goto irq_err;
+ }
+ port->irq = res->start;
+
+ if (sirfport->hw_flow_ctrl) {
+ sirfport->pmx = pinmux_get(&pdev->dev, NULL);
+ ret = IS_ERR(sirfport->pmx);
+ if (ret)
+ goto pmx_err;
+
+ pinmux_enable(sirfport->pmx);
+ }
+
+ port->ops = &sirfsoc_uart_ops;
+ spin_lock_init(&port->lock);
+
+ platform_set_drvdata(pdev, sirfport);
+ ret = uart_add_one_port(&sirfsoc_uart_drv, port);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id);
+ goto port_err;
+ }
+
+ return 0;
+
+port_err:
+ platform_set_drvdata(pdev, NULL);
+ if (sirfport->hw_flow_ctrl) {
+ pinmux_disable(sirfport->pmx);
+ pinmux_put(sirfport->pmx);
+ }
+pmx_err:
+irq_err:
+ devm_iounmap(&pdev->dev, port->membase);
+err:
+ return ret;
+}
+
+static int sirfsoc_uart_remove(struct platform_device *pdev)
+{
+ struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+ struct uart_port *port = &sirfport->port;
+ platform_set_drvdata(pdev, NULL);
+ if (sirfport->hw_flow_ctrl) {
+ pinmux_disable(sirfport->pmx);
+ pinmux_put(sirfport->pmx);
+ }
+ devm_iounmap(&pdev->dev, port->membase);
+ uart_remove_one_port(&sirfsoc_uart_drv, port);
+ return 0;
+}
+
+static int
+sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+ struct uart_port *port = &sirfport->port;
+ uart_suspend_port(&sirfsoc_uart_drv, port);
+ return 0;
+}
+
+static int sirfsoc_uart_resume(struct platform_device *pdev)
+{
+ struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+ struct uart_port *port = &sirfport->port;
+ uart_resume_port(&sirfsoc_uart_drv, port);
+ return 0;
+}
+
+static struct of_device_id sirfsoc_uart_ids[] __devinitdata = {
+ { .compatible = "sirf,prima2-uart", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match);
+
+static struct platform_driver sirfsoc_uart_driver = {
+ .probe = sirfsoc_uart_probe,
+ .remove = __devexit_p(sirfsoc_uart_remove),
+ .suspend = sirfsoc_uart_suspend,
+ .resume = sirfsoc_uart_resume,
+ .driver = {
+ .name = SIRFUART_PORT_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = sirfsoc_uart_ids,
+ },
+};
+
+static int __init sirfsoc_uart_init(void)
+{
+ int ret = 0;
+
+ ret = uart_register_driver(&sirfsoc_uart_drv);
+ if (ret)
+ goto out;
+
+ ret = platform_driver_register(&sirfsoc_uart_driver);
+ if (ret)
+ uart_unregister_driver(&sirfsoc_uart_drv);
+out:
+ return ret;
+}
+module_init(sirfsoc_uart_init);
+
+static void __exit sirfsoc_uart_exit(void)
+{
+ platform_driver_unregister(&sirfsoc_uart_driver);
+ uart_unregister_driver(&sirfsoc_uart_drv);
+}
+module_exit(sirfsoc_uart_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bin Shi <Bin.Shi@csr.com>, Rong Wang<Rong.Wang@csr.com>");
+MODULE_DESCRIPTION("CSR SiRFprimaII Uart Driver");
--- /dev/null
+/*
+ * Drivers for CSR SiRFprimaII onboard UARTs.
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/bitops.h>
+
+/* UART Register Offset Define */
+#define SIRFUART_LINE_CTRL 0x0040
+#define SIRFUART_TX_RX_EN 0x004c
+#define SIRFUART_DIVISOR 0x0050
+#define SIRFUART_INT_EN 0x0054
+#define SIRFUART_INT_STATUS 0x0058
+#define SIRFUART_TX_DMA_IO_CTRL 0x0100
+#define SIRFUART_TX_DMA_IO_LEN 0x0104
+#define SIRFUART_TX_FIFO_CTRL 0x0108
+#define SIRFUART_TX_FIFO_LEVEL_CHK 0x010C
+#define SIRFUART_TX_FIFO_OP 0x0110
+#define SIRFUART_TX_FIFO_STATUS 0x0114
+#define SIRFUART_TX_FIFO_DATA 0x0118
+#define SIRFUART_RX_DMA_IO_CTRL 0x0120
+#define SIRFUART_RX_DMA_IO_LEN 0x0124
+#define SIRFUART_RX_FIFO_CTRL 0x0128
+#define SIRFUART_RX_FIFO_LEVEL_CHK 0x012C
+#define SIRFUART_RX_FIFO_OP 0x0130
+#define SIRFUART_RX_FIFO_STATUS 0x0134
+#define SIRFUART_RX_FIFO_DATA 0x0138
+#define SIRFUART_AFC_CTRL 0x0140
+#define SIRFUART_SWH_DMA_IO 0x0148
+
+/* UART Line Control Register */
+#define SIRFUART_DATA_BIT_LEN_MASK 0x3
+#define SIRFUART_DATA_BIT_LEN_5 BIT(0)
+#define SIRFUART_DATA_BIT_LEN_6 1
+#define SIRFUART_DATA_BIT_LEN_7 2
+#define SIRFUART_DATA_BIT_LEN_8 3
+#define SIRFUART_STOP_BIT_LEN_1 0
+#define SIRFUART_STOP_BIT_LEN_2 BIT(2)
+#define SIRFUART_PARITY_EN BIT(3)
+#define SIRFUART_EVEN_BIT BIT(4)
+#define SIRFUART_STICK_BIT_MASK (7 << 3)
+#define SIRFUART_STICK_BIT_NONE (0 << 3)
+#define SIRFUART_STICK_BIT_EVEN BIT(3)
+#define SIRFUART_STICK_BIT_ODD (3 << 3)
+#define SIRFUART_STICK_BIT_MARK (5 << 3)
+#define SIRFUART_STICK_BIT_SPACE (7 << 3)
+#define SIRFUART_SET_BREAK BIT(6)
+#define SIRFUART_LOOP_BACK BIT(7)
+#define SIRFUART_PARITY_MASK (7 << 3)
+#define SIRFUART_DUMMY_READ BIT(16)
+
+#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000)
+#define SIRFUART_RECV_TIMEOUT_MASK (0xFFFF << 16)
+#define SIRFUART_RECV_TIMEOUT(x) (((x) & 0xFFFF) << 16)
+
+/* UART Auto Flow Control */
+#define SIRFUART_AFC_RX_THD_MASK 0x000000FF
+#define SIRFUART_AFC_RX_EN BIT(8)
+#define SIRFUART_AFC_TX_EN BIT(9)
+#define SIRFUART_CTS_CTRL BIT(10)
+#define SIRFUART_RTS_CTRL BIT(11)
+#define SIRFUART_CTS_IN_STATUS BIT(12)
+#define SIRFUART_RTS_OUT_STATUS BIT(13)
+
+/* UART Interrupt Enable Register */
+#define SIRFUART_RX_DONE_INT BIT(0)
+#define SIRFUART_TX_DONE_INT BIT(1)
+#define SIRFUART_RX_OFLOW_INT BIT(2)
+#define SIRFUART_TX_ALLOUT_INT BIT(3)
+#define SIRFUART_RX_IO_DMA_INT BIT(4)
+#define SIRFUART_TX_IO_DMA_INT BIT(5)
+#define SIRFUART_RXFIFO_FULL_INT BIT(6)
+#define SIRFUART_TXFIFO_EMPTY_INT BIT(7)
+#define SIRFUART_RXFIFO_THD_INT BIT(8)
+#define SIRFUART_TXFIFO_THD_INT BIT(9)
+#define SIRFUART_FRM_ERR_INT BIT(10)
+#define SIRFUART_RXD_BREAK_INT BIT(11)
+#define SIRFUART_RX_TIMEOUT_INT BIT(12)
+#define SIRFUART_PARITY_ERR_INT BIT(13)
+#define SIRFUART_CTS_INT_EN BIT(14)
+#define SIRFUART_RTS_INT_EN BIT(15)
+
+/* UART Interrupt Status Register */
+#define SIRFUART_RX_DONE BIT(0)
+#define SIRFUART_TX_DONE BIT(1)
+#define SIRFUART_RX_OFLOW BIT(2)
+#define SIRFUART_TX_ALL_EMPTY BIT(3)
+#define SIRFUART_DMA_IO_RX_DONE BIT(4)
+#define SIRFUART_DMA_IO_TX_DONE BIT(5)
+#define SIRFUART_RXFIFO_FULL BIT(6)
+#define SIRFUART_TXFIFO_EMPTY BIT(7)
+#define SIRFUART_RXFIFO_THD_REACH BIT(8)
+#define SIRFUART_TXFIFO_THD_REACH BIT(9)
+#define SIRFUART_FRM_ERR BIT(10)
+#define SIRFUART_RXD_BREAK BIT(11)
+#define SIRFUART_RX_TIMEOUT BIT(12)
+#define SIRFUART_PARITY_ERR BIT(13)
+#define SIRFUART_CTS_CHANGE BIT(14)
+#define SIRFUART_RTS_CHANGE BIT(15)
+#define SIRFUART_PLUG_IN BIT(16)
+
+#define SIRFUART_ERR_INT_STAT \
+ (SIRFUART_RX_OFLOW | \
+ SIRFUART_FRM_ERR | \
+ SIRFUART_RXD_BREAK | \
+ SIRFUART_PARITY_ERR)
+#define SIRFUART_ERR_INT_EN \
+ (SIRFUART_RX_OFLOW_INT | \
+ SIRFUART_FRM_ERR_INT | \
+ SIRFUART_RXD_BREAK_INT | \
+ SIRFUART_PARITY_ERR_INT)
+#define SIRFUART_TX_INT_EN SIRFUART_TXFIFO_EMPTY_INT
+#define SIRFUART_RX_IO_INT_EN \
+ (SIRFUART_RX_TIMEOUT_INT | \
+ SIRFUART_RXFIFO_THD_INT | \
+ SIRFUART_RXFIFO_FULL_INT | \
+ SIRFUART_ERR_INT_EN)
+
+/* UART FIFO Register */
+#define SIRFUART_TX_FIFO_STOP 0x0
+#define SIRFUART_TX_FIFO_RESET 0x1
+#define SIRFUART_TX_FIFO_START 0x2
+#define SIRFUART_RX_FIFO_STOP 0x0
+#define SIRFUART_RX_FIFO_RESET 0x1
+#define SIRFUART_RX_FIFO_START 0x2
+#define SIRFUART_TX_MODE_DMA 0
+#define SIRFUART_TX_MODE_IO 1
+#define SIRFUART_RX_MODE_DMA 0
+#define SIRFUART_RX_MODE_IO 1
+
+#define SIRFUART_RX_EN 0x1
+#define SIRFUART_TX_EN 0x2
+
+/* Generic Definitions */
+#define SIRFSOC_UART_NAME "ttySiRF"
+#define SIRFSOC_UART_MAJOR 0
+#define SIRFSOC_UART_MINOR 0
+#define SIRFUART_PORT_NAME "sirfsoc-uart"
+#define SIRFUART_MAP_SIZE 0x200
+#define SIRFSOC_UART_NR 3
+#define SIRFSOC_PORT_TYPE 0xa5
+
+/* Baud Rate Calculation */
+#define SIRF_MIN_SAMPLE_DIV 0xf
+#define SIRF_MAX_SAMPLE_DIV 0x3f
+#define SIRF_IOCLK_DIV_MAX 0xffff
+#define SIRF_SAMPLE_DIV_SHIFT 16
+#define SIRF_IOCLK_DIV_MASK 0xffff
+#define SIRF_SAMPLE_DIV_MASK 0x3f0000
+#define SIRF_BAUD_RATE_SUPPORT_NR 18
+
+/* For Fast Baud Rate Calculation */
+struct sirfsoc_baudrate_to_regv {
+ unsigned int baud_rate;
+ unsigned int reg_val;
+};
+
+struct sirfsoc_uart_port {
+ unsigned char hw_flow_ctrl;
+ unsigned char ms_enabled;
+
+ struct uart_port port;
+ struct pinmux *pmx;
+};
+
+/* Hardware Flow Control */
+#define SIRFUART_AFC_CTRL_RX_THD 0x70
+
+/* Register Access Control */
+#define portaddr(port, reg) ((port)->membase + (reg))
+#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
+#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
+#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
+#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
+
+/* UART Port Mask */
+#define SIRFUART_FIFOLEVEL_MASK(port) ((port->line == 1) ? (0x1f) : (0x7f))
+#define SIRFUART_FIFOFULL_MASK(port) ((port->line == 1) ? (0x20) : (0x80))
+#define SIRFUART_FIFOEMPTY_MASK(port) ((port->line == 1) ? (0x40) : (0x100))
+
+/* I/O Mode */
+#define SIRFSOC_UART_IO_RX_MAX_CNT 256
+#define SIRFSOC_UART_IO_TX_REASONABLE_CNT 6
.remove = __devexit_p(timbuart_remove),
};
-/*--------------------------------------------------------------------------*/
-
-static int __init timbuart_init(void)
-{
- return platform_driver_register(&timbuart_platform_driver);
-}
-
-static void __exit timbuart_exit(void)
-{
- platform_driver_unregister(&timbuart_platform_driver);
-}
-
-module_init(timbuart_init);
-module_exit(timbuart_exit);
+module_platform_driver(timbuart_platform_driver);
MODULE_DESCRIPTION("Timberdale UART driver");
MODULE_LICENSE("GPL v2");
},
};
-static int __init vr41xx_siu_init(void)
-{
- return platform_driver_register(&siu_device_driver);
-}
-
-static void __exit vr41xx_siu_exit(void)
-{
- platform_driver_unregister(&siu_device_driver);
-}
-
-module_init(vr41xx_siu_init);
-module_exit(vr41xx_siu_exit);
+module_platform_driver(siu_device_driver);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:SIU");
void disassociate_ctty(int on_exit)
{
struct tty_struct *tty;
- struct pid *tty_pgrp = NULL;
if (!current->signal->leader)
return;
tty = get_current_tty();
if (tty) {
- tty_pgrp = get_pid(tty->pgrp);
+ struct pid *tty_pgrp = get_pid(tty->pgrp);
if (on_exit) {
if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
}
tty_kref_put(tty);
+ if (tty_pgrp) {
+ kill_pgrp(tty_pgrp, SIGHUP, on_exit);
+ if (!on_exit)
+ kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+ put_pid(tty_pgrp);
+ }
} else if (on_exit) {
struct pid *old_pgrp;
spin_lock_irq(¤t->sighand->siglock);
}
return;
}
- if (tty_pgrp) {
- kill_pgrp(tty_pgrp, SIGHUP, on_exit);
- if (!on_exit)
- kill_pgrp(tty_pgrp, SIGCONT, on_exit);
- put_pid(tty_pgrp);
- }
spin_lock_irq(¤t->sighand->siglock);
put_pid(current->signal->tty_old_pgrp);
tty_kref_put(tty);
}
+/**
+ * tty_release_checks - check a tty before real release
+ * @tty: tty to check
+ * @o_tty: link of @tty (if any)
+ * @idx: index of the tty
+ *
+ * Performs some paranoid checking before true release of the @tty.
+ * This is a no-op unless TTY_PARANOIA_CHECK is defined.
+ */
+static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty,
+ int idx)
+{
+#ifdef TTY_PARANOIA_CHECK
+ if (idx < 0 || idx >= tty->driver->num) {
+ printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n",
+ __func__, tty->name);
+ return -1;
+ }
+
+ /* not much to check for devpts */
+ if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)
+ return 0;
+
+ if (tty != tty->driver->ttys[idx]) {
+ printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n",
+ __func__, idx, tty->name);
+ return -1;
+ }
+ if (tty->termios != tty->driver->termios[idx]) {
+ printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n",
+ __func__, idx, tty->name);
+ return -1;
+ }
+ if (tty->driver->other) {
+ if (o_tty != tty->driver->other->ttys[idx]) {
+ printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n",
+ __func__, idx, tty->name);
+ return -1;
+ }
+ if (o_tty->termios != tty->driver->other->termios[idx]) {
+ printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n",
+ __func__, idx, tty->name);
+ return -1;
+ }
+ if (o_tty->link != tty) {
+ printk(KERN_DEBUG "%s: bad pty pointers\n", __func__);
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
/**
* tty_release - vfs callback for close
* @inode: inode of tty
int idx;
char buf[64];
- if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+ if (tty_paranoia_check(tty, inode, __func__))
return 0;
tty_lock();
- check_tty_count(tty, "tty_release_dev");
+ check_tty_count(tty, __func__);
__tty_fasync(-1, filp, 0);
devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
o_tty = tty->link;
-#ifdef TTY_PARANOIA_CHECK
- if (idx < 0 || idx >= tty->driver->num) {
- printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
- "free (%s)\n", tty->name);
+ if (tty_release_checks(tty, o_tty, idx)) {
tty_unlock();
return 0;
}
- if (!devpts) {
- if (tty != tty->driver->ttys[idx]) {
- tty_unlock();
- printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
- "for (%s)\n", idx, tty->name);
- return 0;
- }
- if (tty->termios != tty->driver->termios[idx]) {
- tty_unlock();
- printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
- "for (%s)\n",
- idx, tty->name);
- return 0;
- }
- }
-#endif
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
- tty_name(tty, buf), tty->count);
+ printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__,
+ tty_name(tty, buf), tty->count);
#endif
-#ifdef TTY_PARANOIA_CHECK
- if (tty->driver->other &&
- !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
- if (o_tty != tty->driver->other->ttys[idx]) {
- tty_unlock();
- printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
- "not o_tty for (%s)\n",
- idx, tty->name);
- return 0 ;
- }
- if (o_tty->termios != tty->driver->other->termios[idx]) {
- tty_unlock();
- printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
- "not o_termios for (%s)\n",
- idx, tty->name);
- return 0;
- }
- if (o_tty->link != tty) {
- tty_unlock();
- printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
- return 0;
- }
- }
-#endif
if (tty->ops->close)
tty->ops->close(tty, filp);
if (!do_sleep)
break;
- printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
- "active!\n", tty_name(tty, buf));
+ printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
+ __func__, tty_name(tty, buf));
tty_unlock();
mutex_unlock(&tty_mutex);
schedule();
*/
if (pty_master) {
if (--o_tty->count < 0) {
- printk(KERN_WARNING "tty_release_dev: bad pty slave count "
- "(%d) for %s\n",
- o_tty->count, tty_name(o_tty, buf));
+ printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
+ __func__, o_tty->count, tty_name(o_tty, buf));
o_tty->count = 0;
}
}
if (--tty->count < 0) {
- printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
- tty->count, tty_name(tty, buf));
+ printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n",
+ __func__, tty->count, tty_name(tty, buf));
tty->count = 0;
}
}
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "freeing tty structure...");
+ printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);
#endif
/*
* Ask the line discipline code to release its structures
return 0;
}
+/**
+ * tty_open_current_tty - get tty of current task for open
+ * @device: device number
+ * @filp: file pointer to tty
+ * @return: tty of the current task iff @device is /dev/tty
+ *
+ * We cannot return driver and index like for the other nodes because
+ * devpts will not work then. It expects inodes to be from devpts FS.
+ */
+static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
+{
+ struct tty_struct *tty;
+
+ if (device != MKDEV(TTYAUX_MAJOR, 0))
+ return NULL;
+
+ tty = get_current_tty();
+ if (!tty)
+ return ERR_PTR(-ENXIO);
+
+ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
+ /* noctty = 1; */
+ tty_kref_put(tty);
+ /* FIXME: we put a reference and return a TTY! */
+ return tty;
+}
+
+/**
+ * tty_lookup_driver - lookup a tty driver for a given device file
+ * @device: device number
+ * @filp: file pointer to tty
+ * @noctty: set if the device should not become a controlling tty
+ * @index: index for the device in the @return driver
+ * @return: driver for this inode (with increased refcount)
+ *
+ * If @return is not erroneous, the caller is responsible to decrement the
+ * refcount by tty_driver_kref_put.
+ *
+ * Locking: tty_mutex protects get_tty_driver
+ */
+static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
+ int *noctty, int *index)
+{
+ struct tty_driver *driver;
+
+ switch (device) {
+#ifdef CONFIG_VT
+ case MKDEV(TTY_MAJOR, 0): {
+ extern struct tty_driver *console_driver;
+ driver = tty_driver_kref_get(console_driver);
+ *index = fg_console;
+ *noctty = 1;
+ break;
+ }
+#endif
+ case MKDEV(TTYAUX_MAJOR, 1): {
+ struct tty_driver *console_driver = console_device(index);
+ if (console_driver) {
+ driver = tty_driver_kref_get(console_driver);
+ if (driver) {
+ /* Don't let /dev/console block */
+ filp->f_flags |= O_NONBLOCK;
+ *noctty = 1;
+ break;
+ }
+ }
+ return ERR_PTR(-ENODEV);
+ }
+ default:
+ driver = get_tty_driver(device, index);
+ if (!driver)
+ return ERR_PTR(-ENODEV);
+ break;
+ }
+ return driver;
+}
+
/**
* tty_open - open a tty device
* @inode: inode of device file
* The termios state of a pty is reset on first open so that
* settings don't persist across reuse.
*
- * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
+ * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
* tty->count should protect the rest.
* ->siglock protects ->signal/->sighand
*/
static int tty_open(struct inode *inode, struct file *filp)
{
- struct tty_struct *tty = NULL;
+ struct tty_struct *tty;
int noctty, retval;
- struct tty_driver *driver;
+ struct tty_driver *driver = NULL;
int index;
dev_t device = inode->i_rdev;
unsigned saved_flags = filp->f_flags;
mutex_lock(&tty_mutex);
tty_lock();
- if (device == MKDEV(TTYAUX_MAJOR, 0)) {
- tty = get_current_tty();
- if (!tty) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- tty_free_file(filp);
- return -ENXIO;
- }
- driver = tty_driver_kref_get(tty->driver);
- index = tty->index;
- filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
- /* noctty = 1; */
- /* FIXME: Should we take a driver reference ? */
- tty_kref_put(tty);
- goto got_driver;
- }
-#ifdef CONFIG_VT
- if (device == MKDEV(TTY_MAJOR, 0)) {
- extern struct tty_driver *console_driver;
- driver = tty_driver_kref_get(console_driver);
- index = fg_console;
- noctty = 1;
- goto got_driver;
- }
-#endif
- if (device == MKDEV(TTYAUX_MAJOR, 1)) {
- struct tty_driver *console_driver = console_device(&index);
- if (console_driver) {
- driver = tty_driver_kref_get(console_driver);
- if (driver) {
- /* Don't let /dev/console block */
- filp->f_flags |= O_NONBLOCK;
- noctty = 1;
- goto got_driver;
- }
+ tty = tty_open_current_tty(device, filp);
+ if (IS_ERR(tty)) {
+ retval = PTR_ERR(tty);
+ goto err_unlock;
+ } else if (!tty) {
+ driver = tty_lookup_driver(device, filp, &noctty, &index);
+ if (IS_ERR(driver)) {
+ retval = PTR_ERR(driver);
+ goto err_unlock;
}
- tty_unlock();
- mutex_unlock(&tty_mutex);
- tty_free_file(filp);
- return -ENODEV;
- }
- driver = get_tty_driver(device, &index);
- if (!driver) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- tty_free_file(filp);
- return -ENODEV;
- }
-got_driver:
- if (!tty) {
/* check whether we're reopening an existing tty */
tty = tty_driver_lookup_tty(driver, inode, index);
-
if (IS_ERR(tty)) {
- tty_unlock();
- mutex_unlock(&tty_mutex);
- tty_driver_kref_put(driver);
- tty_free_file(filp);
- return PTR_ERR(tty);
+ retval = PTR_ERR(tty);
+ goto err_unlock;
}
}
tty = tty_init_dev(driver, index, 0);
mutex_unlock(&tty_mutex);
- tty_driver_kref_put(driver);
+ if (driver)
+ tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
tty_unlock();
- tty_free_file(filp);
- return PTR_ERR(tty);
+ retval = PTR_ERR(tty);
+ goto err_file;
}
tty_add_file(tty, filp);
- check_tty_count(tty, "tty_open");
+ check_tty_count(tty, __func__);
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "opening %s...", tty->name);
+ printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name);
#endif
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
if (retval) {
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "error %d in opening %s...", retval,
- tty->name);
+ printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__,
+ retval, tty->name);
#endif
tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
+err_unlock:
+ tty_unlock();
+ mutex_unlock(&tty_mutex);
+ /* after locks to avoid deadlock */
+ if (!IS_ERR_OR_NULL(driver))
+ tty_driver_kref_put(driver);
+err_file:
+ tty_free_file(filp);
+ return retval;
}
#include <linux/types.h>
-#include <linux/major.h>
#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
+#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/devpts_fs.h>
#include <linux/file.h>
-#include <linux/console.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/kd.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/bitops.h>
-#include <linux/delay.h>
#include <linux/seq_file.h>
-
#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-
-#include <linux/kmod.h>
-#include <linux/nsproxy.h>
#include <linux/ratelimit.h>
/*
long ret;
ret = wait_event_timeout(tty_ldisc_idle,
atomic_read(&tty->ldisc->users) == 1, timeout);
- if (ret < 0)
- return ret;
return ret > 0 ? 0 : -EBUSY;
}
return 0;
dflt->refcount++;
*vc->vc_uni_pagedir_loc = (unsigned long)dflt;
- if (p && --p->refcount) {
+ if (p && !--p->refcount) {
con_release_unimap(p);
kfree(p);
}
* dependent on the 8250 driver.
*/
struct uart_port;
+struct uart_8250_port;
int serial8250_register_port(struct uart_port *);
void serial8250_unregister_port(int line);
struct ktermios *termios, struct ktermios *old);
extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate);
+extern int fsl8250_handle_irq(struct uart_port *port);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
+unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
+void serial8250_tx_chars(struct uart_8250_port *up);
+unsigned int serial8250_modem_status(struct uart_8250_port *up);
extern void serial8250_set_isa_configurator(void (*v)
(int port, struct uart_port *up,
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR ((__force upf_t) (1 << 25))
+#define UPF_IIR_ONCE ((__force upf_t) (1 << 26))
/* The exact UART type is known and should not be probed. */
#define UPF_FIXED_TYPE ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
/*
* The following are helper functions for the low level drivers.
*/
+
+extern void uart_handle_dcd_change(struct uart_port *uport,
+ unsigned int status);
+extern void uart_handle_cts_change(struct uart_port *uport,
+ unsigned int status);
+
+extern void uart_insert_char(struct uart_port *port, unsigned int status,
+ unsigned int overrun, unsigned int ch, unsigned int flag);
+
+#ifdef SUPPORT_SYSRQ
static inline int
uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
{
-#ifdef SUPPORT_SYSRQ
if (port->sysrq) {
if (ch && time_before(jiffies, port->sysrq)) {
handle_sysrq(ch);
}
port->sysrq = 0;
}
-#endif
return 0;
}
-#ifndef SUPPORT_SYSRQ
-#define uart_handle_sysrq_char(port,ch) uart_handle_sysrq_char(port, 0)
+#else
+#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; })
#endif
/*
return 0;
}
-/**
- * uart_handle_dcd_change - handle a change of carrier detect state
- * @uport: uart_port structure for the open port
- * @status: new carrier detect status, nonzero if active
- */
-static inline void
-uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
-{
- struct uart_state *state = uport->state;
- struct tty_port *port = &state->port;
- struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
- struct pps_event_time ts;
-
- if (ld && ld->ops->dcd_change)
- pps_get_ts(&ts);
-
- uport->icount.dcd++;
-#ifdef CONFIG_HARD_PPS
- if ((uport->flags & UPF_HARDPPS_CD) && status)
- hardpps();
-#endif
-
- if (port->flags & ASYNC_CHECK_CD) {
- if (status)
- wake_up_interruptible(&port->open_wait);
- else if (port->tty)
- tty_hangup(port->tty);
- }
-
- if (ld && ld->ops->dcd_change)
- ld->ops->dcd_change(port->tty, status, &ts);
- if (ld)
- tty_ldisc_deref(ld);
-}
-
-/**
- * uart_handle_cts_change - handle a change of clear-to-send state
- * @uport: uart_port structure for the open port
- * @status: new clear to send status, nonzero if active
- */
-static inline void
-uart_handle_cts_change(struct uart_port *uport, unsigned int status)
-{
- struct tty_port *port = &uport->state->port;
- struct tty_struct *tty = port->tty;
-
- uport->icount.cts++;
-
- if (port->flags & ASYNC_CTS_FLOW) {
- if (tty->hw_stopped) {
- if (status) {
- tty->hw_stopped = 0;
- uport->ops->start_tx(uport);
- uart_write_wakeup(uport);
- }
- } else {
- if (!status) {
- tty->hw_stopped = 1;
- uport->ops->stop_tx(uport);
- }
- }
- }
-}
-
-#include <linux/tty_flip.h>
-
-static inline void
-uart_insert_char(struct uart_port *port, unsigned int status,
- unsigned int overrun, unsigned int ch, unsigned int flag)
-{
- struct tty_struct *tty = port->state->port.tty;
-
- if ((status & port->ignore_status_mask & ~overrun) == 0)
- tty_insert_flip_char(tty, ch, flag);
-
- /*
- * Overrun is special. Since it's reported immediately,
- * it doesn't affect the current character.
- */
- if (status & ~port->ignore_status_mask & overrun)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-}
-
/*
* UART_ENABLE_MS - determine if port should enable modem status irqs
*/