summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 11:32:06 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-02 11:32:06 -0700
commit0de10f9ea67d1046b1e16c91fa26accf23939aab (patch)
tree1255e106529d0c708b3744dfd10367a535bda6f9 /drivers
parenta84270189e2afc7028b1123415a66d444f460977 (diff)
parent828c6a102b1f2b8583fadc0e779c46b31d448f0b (diff)
Merge tag 'tty-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial updates from Greg KH: "Here is the big TTY / Serial driver merge for 3.11-rc1. It's not all that big, nothing major changed in the tty api, which is a nice change, just a number of serial driver fixes and updates and new drivers, along with some n_tty fixes to help resolve some reported issues. All of these have been in the linux-next releases for a while, with the exception of the last revert patch, which was reported this past weekend by two different people as being needed." * tag 'tty-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (51 commits) Revert "serial: 8250_pci: add support for another kind of NetMos Technology PCI 9835 Multi-I/O Controller" pch_uart: Add uart_clk selection for the MinnowBoard tty: atmel_serial: prepare clk before calling enable tty: Reset itty for other pty n_tty: Buffer work should not reschedule itself n_tty: Fix unsafe update of available buffer space n_tty: Untangle read completion variables n_tty: Encapsulate minimum_to_wake within N_TTY serial: omap: Fix device tree based PM runtime serial: imx: Fix serial clock unbalance serial/mpc52xx_uart: fix kernel panic when system reboot serial: mfd: Add sysrq support serial: imx: enable the clocks for console tty: serial: add Freescale lpuart driver support serial: imx: Improve Kconfig text serial: imx: Allow module build serial: imx: Fix warning when !CONFIG_SERIAL_IMX_CONSOLE tty/serial/sirf: fix error propagation in sirfsoc_uart_probe() serial: omap: fix potential NULL pointer dereference in serial_omap_runtime_suspend() tty: serial: Enable uartlite for ARM zynq ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/Kconfig12
-rw-r--r--drivers/tty/Makefile2
-rw-r--r--drivers/tty/hvc/hvc_iucv.c2
-rw-r--r--drivers/tty/n_tty.c88
-rw-r--r--drivers/tty/serial/8250/8250_pci.c4
-rw-r--r--drivers/tty/serial/8250/Kconfig5
-rw-r--r--drivers/tty/serial/Kconfig27
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/altera_uart.c1
-rw-r--r--drivers/tty/serial/amba-pl011.c11
-rw-r--r--drivers/tty/serial/atmel_serial.c41
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c4
-rw-r--r--drivers/tty/serial/fsl_lpuart.c874
-rw-r--r--drivers/tty/serial/imx.c47
-rw-r--r--drivers/tty/serial/mfd.c4
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c424
-rw-r--r--drivers/tty/serial/of_serial.c4
-rw-r--r--drivers/tty/serial/omap-serial.c43
-rw-r--r--drivers/tty/serial/pch_uart.c5
-rw-r--r--drivers/tty/serial/samsung.c8
-rw-r--r--drivers/tty/serial/sc26xx.c5
-rw-r--r--drivers/tty/serial/serial_core.c6
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c5
-rw-r--r--drivers/tty/serial/sunhv.c6
-rw-r--r--drivers/tty/serial/sunsab.c6
-rw-r--r--drivers/tty/serial/sunsu.c8
-rw-r--r--drivers/tty/serial/sunzilog.c6
-rw-r--r--drivers/tty/serial/ucc_uart.c9
-rw-r--r--drivers/tty/serial/vt8500_serial.c2
-rw-r--r--drivers/tty/serial/xilinx_uartps.c36
-rw-r--r--drivers/tty/sysrq.c2
-rw-r--r--drivers/tty/tty_io.c19
-rw-r--r--drivers/tty/tty_ldsem.c453
-rw-r--r--drivers/tty/vt/vt.c117
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c18
-rw-r--r--drivers/video/console/fbcon.c35
-rw-r--r--drivers/video/console/mdacon.c8
-rw-r--r--drivers/video/console/newport_con.c9
-rw-r--r--drivers/video/console/sticon.c6
39 files changed, 1988 insertions, 375 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 3bb6fa3930b..14219972c74 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -15,18 +15,6 @@ config DEVKMEM
kind of kernel debugging operations.
When in doubt, say "N".
-config STALDRV
- bool "Stallion multiport serial support"
- depends on SERIAL_NONSTANDARD
- help
- Stallion cards give you many serial ports. You would need something
- like this to connect more than two modems to your Linux box, for
- instance in order to become a dial-in server. If you say Y here,
- you will be asked for your specific card model in the next
- questions. Make sure to read <file:Documentation/serial/stallion.txt>
- in this case. If you have never heard about all this, it's safe to
- say N.
-
config SGI_SNSC
bool "SGI Altix system controller communication support"
depends on (IA64_SGI_SN2 || IA64_GENERIC)
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 6b78399bc7c..58ad1c05b7f 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
- tty_buffer.o tty_port.o tty_mutex.o
+ tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-$(CONFIG_AUDIT) += tty_audit.o
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index b6f7d52f7c3..9d47f50c275 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -1328,7 +1328,7 @@ out_error:
*/
static int __init hvc_iucv_config(char *val)
{
- return strict_strtoul(val, 10, &hvc_iucv_devices);
+ return kstrtoul(val, 10, &hvc_iucv_devices);
}
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 6c7fe90ad72..4bf0fc0843d 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -89,6 +89,7 @@ struct n_tty_data {
int read_head;
int read_tail;
int read_cnt;
+ int minimum_to_wake;
unsigned char *echo_buf;
unsigned int echo_pos;
@@ -114,22 +115,25 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
}
/**
- * n_tty_set__room - receive space
+ * n_tty_set_room - receive space
* @tty: terminal
*
- * Called by the driver to find out how much data it is
- * permitted to feed to the line discipline without any being lost
- * and thus to manage flow control. Not serialized. Answers for the
- * "instant".
+ * Updates tty->receive_room to reflect the currently available space
+ * in the input buffer, and re-schedules the flip buffer work if space
+ * just became available.
+ *
+ * Locks: Concurrent update is protected with read_lock
*/
-static void n_tty_set_room(struct tty_struct *tty)
+static int set_room(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
int left;
int old_left;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&ldata->read_lock, flags);
- /* ldata->read_cnt is not read locked ? */
if (I_PARMRK(tty)) {
/* Multiply read_cnt by 3, since each byte might take up to
* three times as many spaces when PARMRK is set (depending on
@@ -149,8 +153,15 @@ static void n_tty_set_room(struct tty_struct *tty)
old_left = tty->receive_room;
tty->receive_room = left;
+ raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
+
+ return left && !old_left;
+}
+
+static void n_tty_set_room(struct tty_struct *tty)
+{
/* Did this open up the receive buffer? We may need to flip */
- if (left && !old_left) {
+ if (set_room(tty)) {
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
/* see if ldisc has been killed - if so, this means that
@@ -647,8 +658,7 @@ static void process_echoes(struct tty_struct *tty)
if (no_space_left)
break;
} else {
- if (O_OPOST(tty) &&
- !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
+ if (O_OPOST(tty)) {
int retval = do_output_char(c, tty, space);
if (retval < 0)
break;
@@ -1454,9 +1464,9 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
tty->ops->flush_chars(tty);
}
- n_tty_set_room(tty);
+ set_room(tty);
- if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) ||
+ if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) ||
L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
@@ -1516,12 +1526,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
wake_up_interruptible(&tty->read_wait);
ldata->icanon = (L_ICANON(tty) != 0);
- if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {
- ldata->raw = 1;
- ldata->real_raw = 1;
- n_tty_set_room(tty);
- return;
- }
+
if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
@@ -1642,7 +1647,7 @@ static int n_tty_open(struct tty_struct *tty)
tty->disc_data = ldata;
reset_buffer_flags(tty->disc_data);
ldata->column = 0;
- tty->minimum_to_wake = 1;
+ ldata->minimum_to_wake = 1;
tty->closing = 0;
/* indicate buffer work may resume */
clear_bit(TTY_LDISC_HALTED, &tty->flags);
@@ -1806,21 +1811,17 @@ do_it_again:
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!ldata->icanon) {
- time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
if (minimum) {
+ time = (HZ / 10) * TIME_CHAR(tty);
if (time)
- tty->minimum_to_wake = 1;
+ ldata->minimum_to_wake = 1;
else if (!waitqueue_active(&tty->read_wait) ||
- (tty->minimum_to_wake > minimum))
- tty->minimum_to_wake = minimum;
+ (ldata->minimum_to_wake > minimum))
+ ldata->minimum_to_wake = minimum;
} else {
- timeout = 0;
- if (time) {
- timeout = time;
- time = 0;
- }
- tty->minimum_to_wake = minimum = 1;
+ timeout = (HZ / 10) * TIME_CHAR(tty);
+ ldata->minimum_to_wake = minimum = 1;
}
}
@@ -1860,9 +1861,9 @@ do_it_again:
TASK_RUNNING. */
set_current_state(TASK_INTERRUPTIBLE);
- if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
+ if (((minimum - (b - buf)) < ldata->minimum_to_wake) &&
((minimum - (b - buf)) >= 1))
- tty->minimum_to_wake = (minimum - (b - buf));
+ ldata->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
@@ -1881,7 +1882,6 @@ do_it_again:
retval = -ERESTARTSYS;
break;
}
- /* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
continue;
@@ -1979,7 +1979,7 @@ do_it_again:
remove_wait_queue(&tty->read_wait, &wait);
if (!waitqueue_active(&tty->read_wait))
- tty->minimum_to_wake = minimum;
+ ldata->minimum_to_wake = minimum;
__set_current_state(TASK_RUNNING);
size = b - buf;
@@ -2045,7 +2045,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
retval = -EIO;
break;
}
- if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
+ if (O_OPOST(tty)) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
@@ -2111,6 +2111,7 @@ break_out:
static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
poll_table *wait)
{
+ struct n_tty_data *ldata = tty->disc_data;
unsigned int mask = 0;
poll_wait(file, &tty->read_wait, wait);
@@ -2125,9 +2126,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
mask |= POLLHUP;
if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
if (MIN_CHAR(tty) && !TIME_CHAR(tty))
- tty->minimum_to_wake = MIN_CHAR(tty);
+ ldata->minimum_to_wake = MIN_CHAR(tty);
else
- tty->minimum_to_wake = 1;
+ ldata->minimum_to_wake = 1;
}
if (tty->ops->write && !tty_is_writelocked(tty) &&
tty_chars_in_buffer(tty) < WAKEUP_CHARS &&
@@ -2175,6 +2176,18 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
}
}
+static void n_tty_fasync(struct tty_struct *tty, int on)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (!waitqueue_active(&tty->read_wait)) {
+ if (on)
+ ldata->minimum_to_wake = 1;
+ else if (!tty->fasync)
+ ldata->minimum_to_wake = N_TTY_BUF_SIZE;
+ }
+}
+
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
@@ -2188,7 +2201,8 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
- .write_wakeup = n_tty_write_wakeup
+ .write_wakeup = n_tty_write_wakeup,
+ .fasync = n_tty_fasync,
};
/**
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 26e3a97ab15..c52948b368d 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -4797,10 +4797,6 @@ static struct pci_device_id serial_pci_tbl[] = {
PCI_VENDOR_ID_IBM, 0x0299,
0, 0, pbn_b0_bt_2_115200 },
- { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
- 0x1000, 0x0012,
- 0, 0, pbn_b0_bt_2_115200 },
-
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
0xA000, 0x1000,
0, 0, pbn_b0_1_115200 },
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 80fe91e64a5..a1ba94d6488 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -12,9 +12,8 @@ config SERIAL_8250
here are those that are setting up dedicated Ethernet WWW/FTP
servers, or users that have one of the various bus mice instead of a
serial mouse and don't intend to use their machine's standard serial
- port for anything. (Note that the Cyclades and Stallion multi
- serial port drivers do not need this driver built in for them to
- work.)
+ port for anything. (Note that the Cyclades multi serial port driver
+ does not need this driver built in for it to work.)
To compile this driver as a module, choose M here: the
module will be called 8250.
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 7e7006fd404..46dd1c72fed 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -551,7 +551,7 @@ config BFIN_UART3_CTSRTS
Enable hardware flow control in the driver.
config SERIAL_IMX
- bool "IMX serial port support"
+ tristate "IMX serial port support"
depends on ARCH_MXC
select SERIAL_CORE
select RATIONAL
@@ -561,22 +561,21 @@ config SERIAL_IMX
config SERIAL_IMX_CONSOLE
bool "Console on IMX serial port"
- depends on SERIAL_IMX
+ depends on SERIAL_IMX=y
select SERIAL_CORE_CONSOLE
help
- If you have enabled the serial port on the Motorola IMX
+ If you have enabled the serial port on the Freescale IMX
CPU you can make it the console by answering Y to this option.
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=ttySA0". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
+ "console=ttymxc0". (Try "man bootparam" or see the documentation of
+ your bootloader about how to pass options to the kernel at boot time.)
config SERIAL_UARTLITE
tristate "Xilinx uartlite serial port support"
- depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE
+ depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ
select SERIAL_CORE
help
Say Y here if you want to use the Xilinx uartlite serial controller.
@@ -1484,6 +1483,20 @@ config SERIAL_RP2_NR_UARTS
If multiple cards are present, the default limit of 32 ports may
need to be increased.
+config SERIAL_FSL_LPUART
+ tristate "Freescale lpuart serial port support"
+ select SERIAL_CORE
+ help
+ Support for the on-chip lpuart on some Freescale SOCs.
+
+config SERIAL_FSL_LPUART_CONSOLE
+ bool "Console on Freescale lpuart serial port"
+ depends on SERIAL_FSL_LPUART=y
+ select SERIAL_CORE_CONSOLE
+ help
+ If you have enabled the lpuart serial port on the Freescale SoCs,
+ you can make it the console by answering Y to this option.
+
endmenu
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index eedfec40e3d..cf650f0cd6e 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -85,3 +85,4 @@ obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
+obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 13471dd9579..1d46966e2a6 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -604,7 +604,6 @@ static int altera_uart_remove(struct platform_device *pdev)
if (port) {
uart_remove_one_port(&altera_uart_driver, port);
- platform_set_drvdata(pdev, NULL);
port->mapbase = 0;
}
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index e2774f9ecd5..ad41319d1d9 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -79,13 +79,12 @@ struct vendor_data {
bool dma_threshold;
bool cts_event_workaround;
- unsigned int (*get_fifosize)(unsigned int periphid);
+ unsigned int (*get_fifosize)(struct amba_device *dev);
};
-static unsigned int get_fifosize_arm(unsigned int periphid)
+static unsigned int get_fifosize_arm(struct amba_device *dev)
{
- unsigned int rev = (periphid >> 20) & 0xf;
- return rev < 3 ? 16 : 32;
+ return amba_rev(dev) < 3 ? 16 : 32;
}
static struct vendor_data vendor_arm = {
@@ -98,7 +97,7 @@ static struct vendor_data vendor_arm = {
.get_fifosize = get_fifosize_arm,
};
-static unsigned int get_fifosize_st(unsigned int periphid)
+static unsigned int get_fifosize_st(struct amba_device *dev)
{
return 64;
}
@@ -2157,7 +2156,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
uap->lcrh_rx = vendor->lcrh_rx;
uap->lcrh_tx = vendor->lcrh_tx;
uap->old_cr = 0;
- uap->fifosize = vendor->get_fifosize(dev->periphid);
+ uap->fifosize = vendor->get_fifosize(dev);
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 3467462869c..691265faebb 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1100,7 +1100,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
* Enable the peripheral clock for this serial port.
* This is called on uart_open() or a resume event.
*/
- clk_enable(atmel_port->clk);
+ clk_prepare_enable(atmel_port->clk);
/* re-enable interrupts if we disabled some on suspend */
UART_PUT_IER(port, atmel_port->backup_imr);
@@ -1114,7 +1114,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
* Disable the peripheral clock for this serial port.
* This is called on uart_close() or a suspend event.
*/
- clk_disable(atmel_port->clk);
+ clk_disable_unprepare(atmel_port->clk);
break;
default:
printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
@@ -1458,9 +1458,10 @@ static void atmel_of_init_port(struct atmel_uart_port *atmel_port,
/*
* Configure the port from the platform device resource info.
*/
-static void atmel_init_port(struct atmel_uart_port *atmel_port,
+static int atmel_init_port(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
+ int ret;
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *pdata = pdev->dev.platform_data;
@@ -1496,9 +1497,19 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port,
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
atmel_port->clk = clk_get(&pdev->dev, "usart");
- clk_enable(atmel_port->clk);
+ if (IS_ERR(atmel_port->clk)) {
+ ret = PTR_ERR(atmel_port->clk);
+ atmel_port->clk = NULL;
+ return ret;
+ }
+ ret = clk_prepare_enable(atmel_port->clk);
+ if (ret) {
+ clk_put(atmel_port->clk);
+ atmel_port->clk = NULL;
+ return ret;
+ }
port->uartclk = clk_get_rate(atmel_port->clk);
- clk_disable(atmel_port->clk);
+ clk_disable_unprepare(atmel_port->clk);
/* only enable clock when USART is in use */
}
@@ -1511,6 +1522,8 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port,
} else {
atmel_port->tx_done_mask = ATMEL_US_TXRDY;
}
+
+ return 0;
}
struct platform_device *atmel_default_console_device; /* the serial console device */
@@ -1601,6 +1614,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud,
static int __init atmel_console_setup(struct console *co, char *options)
{
+ int ret;
struct uart_port *port = &atmel_ports[co->index].uart;
int baud = 115200;
int bits = 8;
@@ -1612,7 +1626,9 @@ static int __init atmel_console_setup(struct console *co, char *options)
return -ENODEV;
}
- clk_enable(atmel_ports[co->index].clk);
+ ret = clk_prepare_enable(atmel_ports[co->index].clk);
+ if (ret)
+ return ret;
UART_PUT_IDR(port, -1);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
@@ -1645,6 +1661,7 @@ static struct console atmel_console = {
*/
static int __init atmel_console_init(void)
{
+ int ret;
if (atmel_default_console_device) {
struct atmel_uart_data *pdata =
atmel_default_console_device->dev.platform_data;
@@ -1655,7 +1672,9 @@ static int __init atmel_console_init(void)
port->uart.line = id;
add_preferred_console(ATMEL_DEVICENAME, id, NULL);
- atmel_init_port(port, atmel_default_console_device);
+ ret = atmel_init_port(port, atmel_default_console_device);
+ if (ret)
+ return ret;
register_console(&atmel_console);
}
@@ -1786,7 +1805,9 @@ static int atmel_serial_probe(struct platform_device *pdev)
port->backup_imr = 0;
port->uart.line = ret;
- atmel_init_port(port, pdev);
+ ret = atmel_init_port(port, pdev);
+ if (ret)
+ goto err;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
@@ -1812,9 +1833,9 @@ static int atmel_serial_probe(struct platform_device *pdev)
&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
/*
* The serial core enabled the clock for us, so undo
- * the clk_enable() in atmel_console_setup()
+ * the clk_prepare_enable() in atmel_console_setup()
*/
- clk_disable(port->clk);
+ clk_disable_unprepare(port->clk);
}
#endif
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index 97f4e185864..f7672cae532 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -1384,7 +1384,7 @@ static int cpm_uart_probe(struct platform_device *ofdev)
if (index >= UART_NR)
return -ENODEV;
- dev_set_drvdata(&ofdev->dev, pinfo);
+ platform_set_drvdata(ofdev, pinfo);
/* initialize the device pointer for the port */
pinfo->port.dev = &ofdev->dev;
@@ -1398,7 +1398,7 @@ static int cpm_uart_probe(struct platform_device *ofdev)
static int cpm_uart_remove(struct platform_device *ofdev)
{
- struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev);
+ struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev);
return uart_remove_one_port(&cpm_reg, &pinfo->port);
}
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
new file mode 100644
index 00000000000..263cfaabe9e
--- /dev/null
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -0,0 +1,874 @@
+/*
+ * Freescale lpuart serial port driver
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#if defined(CONFIG_SERIAL_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+
+/* All registers are 8-bit width */
+#define UARTBDH 0x00
+#define UARTBDL 0x01
+#define UARTCR1 0x02
+#define UARTCR2 0x03
+#define UARTSR1 0x04
+#define UARTCR3 0x06
+#define UARTDR 0x07
+#define UARTCR4 0x0a
+#define UARTCR5 0x0b
+#define UARTMODEM 0x0d
+#define UARTPFIFO 0x10
+#define UARTCFIFO 0x11
+#define UARTSFIFO 0x12
+#define UARTTWFIFO 0x13
+#define UARTTCFIFO 0x14
+#define UARTRWFIFO 0x15
+
+#define UARTBDH_LBKDIE 0x80
+#define UARTBDH_RXEDGIE 0x40
+#define UARTBDH_SBR_MASK 0x1f
+
+#define UARTCR1_LOOPS 0x80
+#define UARTCR1_RSRC 0x20
+#define UARTCR1_M 0x10
+#define UARTCR1_WAKE 0x08
+#define UARTCR1_ILT 0x04
+#define UARTCR1_PE 0x02
+#define UARTCR1_PT 0x01
+
+#define UARTCR2_TIE 0x80
+#define UARTCR2_TCIE 0x40
+#define UARTCR2_RIE 0x20
+#define UARTCR2_ILIE 0x10
+#define UARTCR2_TE 0x08
+#define UARTCR2_RE 0x04
+#define UARTCR2_RWU 0x02
+#define UARTCR2_SBK 0x01
+
+#define UARTSR1_TDRE 0x80
+#define UARTSR1_TC 0x40
+#define UARTSR1_RDRF 0x20
+#define UARTSR1_IDLE 0x10
+#define UARTSR1_OR 0x08
+#define UARTSR1_NF 0x04
+#define UARTSR1_FE 0x02
+#define UARTSR1_PE 0x01
+
+#define UARTCR3_R8 0x80
+#define UARTCR3_T8 0x40
+#define UARTCR3_TXDIR 0x20
+#define UARTCR3_TXINV 0x10
+#define UARTCR3_ORIE 0x08
+#define UARTCR3_NEIE 0x04
+#define UARTCR3_FEIE 0x02
+#define UARTCR3_PEIE 0x01
+
+#define UARTCR4_MAEN1 0x80
+#define UARTCR4_MAEN2 0x40
+#define UARTCR4_M10 0x20
+#define UARTCR4_BRFA_MASK 0x1f
+#define UARTCR4_BRFA_OFF 0
+
+#define UARTCR5_TDMAS 0x80
+#define UARTCR5_RDMAS 0x20
+
+#define UARTMODEM_RXRTSE 0x08
+#define UARTMODEM_TXRTSPOL 0x04
+#define UARTMODEM_TXRTSE 0x02
+#define UARTMODEM_TXCTSE 0x01
+
+#define UARTPFIFO_TXFE 0x80
+#define UARTPFIFO_FIFOSIZE_MASK 0x7
+#define UARTPFIFO_TXSIZE_OFF 4
+#define UARTPFIFO_RXFE 0x08
+#define UARTPFIFO_RXSIZE_OFF 0
+
+#define UARTCFIFO_TXFLUSH 0x80
+#define UARTCFIFO_RXFLUSH 0x40
+#define UARTCFIFO_RXOFE 0x04
+#define UARTCFIFO_TXOFE 0x02
+#define UARTCFIFO_RXUFE 0x01
+
+#define UARTSFIFO_TXEMPT 0x80
+#define UARTSFIFO_RXEMPT 0x40
+#define UARTSFIFO_RXOF 0x04
+#define UARTSFIFO_TXOF 0x02
+#define UARTSFIFO_RXUF 0x01
+
+#define DRIVER_NAME "fsl-lpuart"
+#define DEV_NAME "ttyLP"
+#define UART_NR 6
+
+struct lpuart_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int txfifo_size;
+ unsigned int rxfifo_size;
+};
+
+static struct of_device_id lpuart_dt_ids[] = {
+ {
+ .compatible = "fsl,vf610-lpuart",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
+
+static void lpuart_stop_tx(struct uart_port *port)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2);
+ temp &= ~(UARTCR2_TIE | UARTCR2_TCIE);
+ writeb(temp, port->membase + UARTCR2);
+}
+
+static void lpuart_stop_rx(struct uart_port *port)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2);
+ writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2);
+}
+
+static void lpuart_enable_ms(struct uart_port *port)
+{
+}
+
+static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
+{
+ struct circ_buf *xmit = &sport->port.state->xmit;
+
+ while (!uart_circ_empty(xmit) &&
+ (readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) {
+ writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ sport->port.icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+ if (uart_circ_empty(xmit))
+ lpuart_stop_tx(&sport->port);
+}
+
+static void lpuart_start_tx(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2);
+ writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
+
+ if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+ lpuart_transmit_buffer(sport);
+}
+
+static irqreturn_t lpuart_txint(int irq, void *dev_id)
+{
+ struct lpuart_port *sport = dev_id;
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (sport->port.x_char) {
+ writeb(sport->port.x_char, sport->port.membase + UARTDR);
+ goto out;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+ lpuart_stop_tx(&sport->port);
+ goto out;
+ }
+
+ lpuart_transmit_buffer(sport);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+out:
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lpuart_rxint(int irq, void *dev_id)
+{
+ struct lpuart_port *sport = dev_id;
+ unsigned int flg, ignored = 0;
+ struct tty_port *port = &sport->port.state->port;
+ unsigned long flags;
+ unsigned char rx, sr;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) {
+ flg = TTY_NORMAL;
+ sport->port.icount.rx++;
+ /*
+ * to clear the FE, OR, NF, FE, PE flags,
+ * read SR1 then read DR
+ */
+ sr = readb(sport->port.membase + UARTSR1);
+ rx = readb(sport->port.membase + UARTDR);
+
+ if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+ continue;
+
+ if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) {
+ if (sr & UARTSR1_PE)
+ sport->port.icount.parity++;
+ else if (sr & UARTSR1_FE)
+ sport->port.icount.frame++;
+
+ if (sr & UARTSR1_OR)
+ sport->port.icount.overrun++;
+
+ if (sr & sport->port.ignore_status_mask) {
+ if (++ignored > 100)
+ goto out;
+ continue;
+ }
+
+ sr &= sport->port.read_status_mask;
+
+ if (sr & UARTSR1_PE)
+ flg = TTY_PARITY;
+ else if (sr & UARTSR1_FE)
+ flg = TTY_FRAME;
+
+ if (sr & UARTSR1_OR)
+ flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+ sport->port.sysrq = 0;
+#endif
+ }
+
+ tty_insert_flip_char(port, rx, flg);
+ }
+
+out:
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ tty_flip_buffer_push(port);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lpuart_int(int irq, void *dev_id)
+{
+ struct lpuart_port *sport = dev_id;
+ unsigned char sts;
+
+ sts = readb(sport->port.membase + UARTSR1);
+
+ if (sts & UARTSR1_RDRF)
+ lpuart_rxint(irq, dev_id);
+
+ if (sts & UARTSR1_TDRE &&
+ !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
+ lpuart_txint(irq, dev_id);
+
+ return IRQ_HANDLED;
+}
+
+/* return TIOCSER_TEMT when transmitter is not busy */
+static unsigned int lpuart_tx_empty(struct uart_port *port)
+{
+ return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
+ TIOCSER_TEMT : 0;
+}
+
+static unsigned int lpuart_get_mctrl(struct uart_port *port)
+{
+ unsigned int temp = 0;
+ unsigned char reg;
+
+ reg = readb(port->membase + UARTMODEM);
+ if (reg & UARTMODEM_TXCTSE)
+ temp |= TIOCM_CTS;
+
+ if (reg & UARTMODEM_RXRTSE)
+ temp |= TIOCM_RTS;
+
+ return temp;
+}
+
+static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTMODEM) &
+ ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+
+ if (mctrl & TIOCM_RTS)
+ temp |= UARTMODEM_RXRTSE;
+
+ if (mctrl & TIOCM_CTS)
+ temp |= UARTMODEM_TXCTSE;
+
+ writeb(temp, port->membase + UARTMODEM);
+}
+
+static void lpuart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK;
+
+ if (break_state != 0)
+ temp |= UARTCR2_SBK;
+
+ writeb(temp, port->membase + UARTCR2);
+}
+
+static void lpuart_setup_watermark(struct lpuart_port *sport)
+{
+ unsigned char val, cr2;
+
+ cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE |
+ UARTCR2_RIE | UARTCR2_RE);
+ writeb(cr2, sport->port.membase + UARTCR2);
+
+ /* determine FIFO size and enable FIFO mode */
+ val = readb(sport->port.membase + UARTPFIFO);
+
+ sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK) + 1);
+
+ sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK) + 1);
+
+ writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
+ sport->port.membase + UARTPFIFO);
+
+ /* flush Tx and Rx FIFO */
+ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
+ sport->port.membase + UARTCFIFO);
+
+ writeb(2, sport->port.membase + UARTTWFIFO);
+ writeb(1, sport->port.membase + UARTRWFIFO);
+}
+
+static int lpuart_startup(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ int ret;
+ unsigned long flags;
+ unsigned char temp;
+
+ ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
+ DRIVER_NAME, sport);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ lpuart_setup_watermark(sport);
+
+ temp = readb(sport->port.membase + UARTCR2);
+ temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
+ writeb(temp, sport->port.membase + UARTCR2);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return 0;
+}
+
+static void lpuart_shutdown(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ unsigned char temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* disable Rx/Tx and interrupts */
+ temp = readb(port->membase + UARTCR2);
+ temp &= ~(UARTCR2_TE | UARTCR2_RE |
+ UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
+ writeb(temp, port->membase + UARTCR2);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ devm_free_irq(port->dev, port->irq, sport);
+}
+
+static void
+lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ unsigned long flags;
+ unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+ unsigned int baud;
+ unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+ unsigned int sbr, brfa;
+
+ cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
+ old_cr2 = readb(sport->port.membase + UARTCR2);
+ cr4 = readb(sport->port.membase + UARTCR4);
+ bdh = readb(sport->port.membase + UARTBDH);
+ modem = readb(sport->port.membase + UARTMODEM);
+ /*
+ * only support CS8 and CS7, and for CS7 must enable PE.
+ * supported mode:
+ * - (7,e/o,1)
+ * - (8,n,1)
+ * - (8,m/s,1)
+ * - (8,e/o,1)
+ */
+ while ((termios->c_cflag & CSIZE) != CS8 &&
+ (termios->c_cflag & CSIZE) != CS7) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= old_csize;
+ old_csize = CS8;
+ }
+
+ if ((termios->c_cflag & CSIZE) == CS8 ||
+ (termios->c_cflag & CSIZE) == CS7)
+ cr1 = old_cr1 & ~UARTCR1_M;
+
+ if (termios->c_cflag & CMSPAR) {
+ if ((termios->c_cflag & CSIZE) != CS8) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= CS8;
+ }
+ cr1 |= UARTCR1_M;
+ }
+
+ if (termios->c_cflag & CRTSCTS) {
+ modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+ } else {
+ termios->c_cflag &= ~CRTSCTS;
+ modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ termios->c_cflag &= ~CSTOPB;
+
+ /* parity must be enabled when CS7 to match 8-bits format */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ termios->c_cflag |= PARENB;
+
+ if ((termios->c_cflag & PARENB)) {
+ if (termios->c_cflag & CMSPAR) {
+ cr1 &= ~UARTCR1_PE;
+ cr1 |= UARTCR1_M;
+ } else {
+ cr1 |= UARTCR1_PE;
+ if ((termios->c_cflag & CSIZE) == CS8)
+ cr1 |= UARTCR1_M;
+ if (termios->c_cflag & PARODD)
+ cr1 |= UARTCR1_PT;
+ else
+ cr1 &= ~UARTCR1_PT;
+ }
+ }
+
+ /* ask the core to calculate the divisor */
+ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ sport->port.read_status_mask = 0;
+ if (termios->c_iflag & INPCK)
+ sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE);
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ sport->port.read_status_mask |= UARTSR1_FE;
+
+ /* characters to ignore */
+ sport->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ sport->port.ignore_status_mask |= UARTSR1_PE;
+ if (termios->c_iflag & IGNBRK) {
+ sport->port.ignore_status_mask |= UARTSR1_FE;
+ /*
+ * if we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ sport->port.ignore_status_mask |= UARTSR1_OR;
+ }
+
+ /* update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ /* wait transmit engin complete */
+ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
+ barrier();
+
+ /* disable transmit and receive */
+ writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE),
+ sport->port.membase + UARTCR2);
+
+ sbr = sport->port.uartclk / (16 * baud);
+ brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud;
+ bdh &= ~UARTBDH_SBR_MASK;
+ bdh |= (sbr >> 8) & 0x1F;
+ cr4 &= ~UARTCR4_BRFA_MASK;
+ brfa &= UARTCR4_BRFA_MASK;
+ writeb(cr4 | brfa, sport->port.membase + UARTCR4);
+ writeb(bdh, sport->port.membase + UARTBDH);
+ writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+ writeb(cr1, sport->port.membase + UARTCR1);
+ writeb(modem, sport->port.membase + UARTMODEM);
+
+ /* restore control register */
+ writeb(old_cr2, sport->port.membase + UARTCR2);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *lpuart_type(struct uart_port *port)
+{
+ return "FSL_LPUART";
+}
+
+static void lpuart_release_port(struct uart_port *port)
+{
+ /* nothing to do */
+}
+
+static int lpuart_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/* configure/autoconfigure the port */
+static void lpuart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_LPUART;
+}
+
+static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ int ret = 0;
+
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART)
+ ret = -EINVAL;
+ if (port->irq != ser->irq)
+ ret = -EINVAL;
+ if (ser->io_type != UPIO_MEM)
+ ret = -EINVAL;
+ if (port->uartclk / 16 != ser->baud_base)
+ ret = -EINVAL;
+ if (port->iobase != ser->port)
+ ret = -EINVAL;
+ if (ser->hub6 != 0)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops lpuart_pops = {
+ .tx_empty = lpuart_tx_empty,
+ .set_mctrl = lpuart_set_mctrl,
+ .get_mctrl = lpuart_get_mctrl,
+ .stop_tx = lpuart_stop_tx,
+ .start_tx = lpuart_start_tx,
+ .stop_rx = lpuart_stop_rx,
+ .enable_ms = lpuart_enable_ms,
+ .break_ctl = lpuart_break_ctl,
+ .startup = lpuart_startup,
+ .shutdown = lpuart_shutdown,
+ .set_termios = lpuart_set_termios,
+ .type = lpuart_type,
+ .request_port = lpuart_request_port,
+ .release_port = lpuart_release_port,
+ .config_port = lpuart_config_port,
+ .verify_port = lpuart_verify_port,
+};
+
+static struct lpuart_port *lpuart_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE
+static void lpuart_console_putchar(struct uart_port *port, int ch)
+{
+ while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
+ barrier();
+
+ writeb(ch, port->membase + UARTDR);
+}
+
+static void
+lpuart_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct lpuart_port *sport = lpuart_ports[co->index];
+ unsigned char old_cr2, cr2;
+
+ /* first save CR2 and then disable interrupts */
+ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 |= (UARTCR2_TE | UARTCR2_RE);
+ cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
+ writeb(cr2, sport->port.membase + UARTCR2);
+
+ uart_console_write(&sport->port, s, count, lpuart_console_putchar);
+
+ /* wait for transmitter finish complete and restore CR2 */
+ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
+ barrier();
+
+ writeb(old_cr2, sport->port.membase + UARTCR2);
+}
+
+/*
+ * if the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+lpuart_console_get_options(struct lpuart_port *sport, int *baud,
+ int *parity, int *bits)
+{
+ unsigned char cr, bdh, bdl, brfa;
+ unsigned int sbr, uartclk, baud_raw;
+
+ cr = readb(sport->port.membase + UARTCR2);
+ cr &= UARTCR2_TE | UARTCR2_RE;
+ if (!cr)
+ return;
+
+ /* ok, the port was enabled */
+
+ cr = readb(sport->port.membase + UARTCR1);
+
+ *parity = 'n';
+ if (cr & UARTCR1_PE) {
+ if (cr & UARTCR1_PT)
+ *parity = 'o';
+ else
+ *parity = 'e';
+ }
+
+ if (cr & UARTCR1_M)
+ *bits = 9;
+ else
+ *bits = 8;
+
+ bdh = readb(sport->port.membase + UARTBDH);
+ bdh &= UARTBDH_SBR_MASK;
+ bdl = readb(sport->port.membase + UARTBDL);
+ sbr = bdh;
+ sbr <<= 8;
+ sbr |= bdl;
+ brfa = readb(sport->port.membase + UARTCR4);
+ brfa &= UARTCR4_BRFA_MASK;
+
+ uartclk = clk_get_rate(sport->clk);
+ /*
+ * baud = mod_clk/(16*(sbr[13]+(brfa)/32)
+ */
+ baud_raw = uartclk / (16 * (sbr + brfa / 32));
+
+ if (*baud != baud_raw)
+ printk(KERN_INFO "Serial: Console lpuart rounded baud rate"
+ "from %d to %d\n", baud_raw, *baud);
+}
+
+static int __init lpuart_console_setup(struct console *co, char *options)
+{
+ struct lpuart_port *sport;
+ int baud = 115200;
+ 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 == -1 || co->index >= ARRAY_SIZE(lpuart_ports))
+ co->index = 0;
+
+ sport = lpuart_ports[co->index];
+ if (sport == NULL)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ lpuart_console_get_options(sport, &baud, &parity, &bits);
+
+ lpuart_setup_watermark(sport);
+
+ return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver lpuart_reg;
+static struct console lpuart_console = {
+ .name = DEV_NAME,
+ .write = lpuart_console_write,
+ .device = uart_console_device,
+ .setup = lpuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lpuart_reg,
+};
+
+#define LPUART_CONSOLE (&lpuart_console)
+#else
+#define LPUART_CONSOLE NULL
+#endif
+
+static struct uart_driver lpuart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = DEV_NAME,
+ .nr = ARRAY_SIZE(lpuart_ports),
+ .cons = LPUART_CONSOLE,
+};
+
+static int lpuart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct lpuart_port *sport;
+ struct resource *res;
+ int ret;
+
+ sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+ if (!sport)
+ return -ENOMEM;
+
+ pdev->dev.coherent_dma_mask = 0;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ sport->port.line = ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ sport->port.mapbase = res->start;
+ sport->port.membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sport->port.membase))
+ return PTR_ERR(sport->port.membase);
+
+ sport->port.dev = &pdev->dev;
+ sport->port.type = PORT_LPUART;
+ sport->port.iotype = UPIO_MEM;
+ sport->port.irq = platform_get_irq(pdev, 0);
+ sport->port.ops = &lpuart_pops;
+ sport->port.flags = UPF_BOOT_AUTOCONF;
+
+ sport->clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(sport->clk)) {
+ ret = PTR_ERR(sport->clk);
+ dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sport->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret);
+ return ret;
+ }
+
+ sport->port.uartclk = clk_get_rate(sport->clk);
+
+ lpuart_ports[sport->port.line] = sport;
+
+ platform_set_drvdata(pdev, &sport->port);
+
+ ret = uart_add_one_port(&lpuart_reg, &sport->port);
+ if (ret) {
+ clk_disable_unprepare(sport->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lpuart_remove(struct platform_device *pdev)
+{
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&lpuart_reg, &sport->port);
+
+ clk_disable_unprepare(sport->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lpuart_suspend(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+
+ uart_suspend_port(&lpuart_reg, &sport->port);
+
+ return 0;
+}
+
+static int lpuart_resume(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+
+ uart_resume_port(&lpuart_reg, &sport->port);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume);
+
+static struct platform_driver lpuart_driver = {
+ .probe = lpuart_probe,
+ .remove = lpuart_remove,
+ .driver = {
+ .name = "fsl-lpuart",
+ .owner = THIS_MODULE,
+ .of_match_table = lpuart_dt_ids,
+ .pm = &lpuart_pm_ops,
+ },
+};
+
+static int __init lpuart_serial_init(void)
+{
+ int ret;
+
+ pr_info("serial: Freescale lpuart driver\n");
+
+ ret = uart_register_driver(&lpuart_reg);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&lpuart_driver);
+ if (ret)
+ uart_unregister_driver(&lpuart_reg);
+
+ return 0;
+}
+
+static void __exit lpuart_serial_exit(void)
+{
+ platform_driver_unregister(&lpuart_driver);
+ uart_unregister_driver(&lpuart_reg);
+}
+
+module_init(lpuart_serial_init);
+module_exit(lpuart_serial_exit);
+
+MODULE_DESCRIPTION("Freescale lpuart serial port driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 8cdfbd36589..415cec62073 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -201,6 +201,7 @@ struct imx_port {
unsigned int old_status;
int txirq, rxirq, rtsirq;
unsigned int have_rtscts:1;
+ unsigned int dte_mode:1;
unsigned int use_irda:1;
unsigned int irda_inv_rx:1;
unsigned int irda_inv_tx:1;
@@ -271,6 +272,7 @@ static inline int is_imx21_uart(struct imx_port *sport)
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_port_ucrs_save(struct uart_port *port,
struct imx_port_ucrs *ucr)
{
@@ -288,6 +290,7 @@ static void imx_port_ucrs_restore(struct uart_port *port,
writel(ucr->ucr2, port->membase + UCR2);
writel(ucr->ucr3, port->membase + UCR3);
}
+#endif
/*
* Handle any change of modem status signal since we were last called.
@@ -449,6 +452,13 @@ static void imx_start_tx(struct uart_port *port)
temp &= ~(UCR1_RRDYEN);
writel(temp, sport->port.membase + UCR1);
}
+ /* Clear any pending ORE flag before enabling interrupt */
+ temp = readl(sport->port.membase + USR2);
+ writel(temp | USR2_ORE, sport->port.membase + USR2);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_OREN;
+ writel(temp, sport->port.membase + UCR4);
temp = readl(sport->port.membase + UCR1);
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
@@ -582,6 +592,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int sts;
+ unsigned int sts2;
sts = readl(sport->port.membase + USR1);
@@ -598,6 +609,13 @@ static irqreturn_t imx_int(int irq, void *dev_id)
if (sts & USR1_AWAKE)
writel(USR1_AWAKE, sport->port.membase + USR1);
+ sts2 = readl(sport->port.membase + USR2);
+ if (sts2 & USR2_ORE) {
+ dev_err(sport->port.dev, "Rx FIFO overrun\n");
+ sport->port.icount.overrun++;
+ writel(sts2 | USR2_ORE, sport->port.membase + USR2);
+ }
+
return IRQ_HANDLED;
}
@@ -684,6 +702,17 @@ static int imx_startup(struct uart_port *port)
int retval;
unsigned long flags, temp;
+ if (!uart_console(port)) {
+ retval = clk_prepare_enable(sport->clk_per);
+ if (retval)
+ goto error_out1;
+ retval = clk_prepare_enable(sport->clk_ipg);
+ if (retval) {
+ clk_disable_unprepare(sport->clk_per);
+ goto error_out1;
+ }
+ }
+
imx_setup_ufcr(sport, 0);
/* disable the DREN bit (Data Ready interrupt enable) before
@@ -871,6 +900,11 @@ static void imx_shutdown(struct uart_port *port)
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ if (!uart_console(&sport->port)) {
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
+ }
}
static void
@@ -1007,6 +1041,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+ if (sport->dte_mode)
+ ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
@@ -1431,6 +1467,9 @@ static int serial_imx_probe_dt(struct imx_port *sport,
if (of_get_property(np, "fsl,irda-mode", NULL))
sport->use_irda = 1;
+ if (of_get_property(np, "fsl,dte-mode", NULL))
+ sport->dte_mode = 1;
+
sport->devdata = of_id->data;
return 0;
@@ -1544,6 +1583,11 @@ static int serial_imx_probe(struct platform_device *pdev)
goto deinit;
platform_set_drvdata(pdev, sport);
+ if (!uart_console(&sport->port)) {
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
+ }
+
return 0;
deinit:
if (pdata && pdata->exit)
@@ -1565,9 +1609,6 @@ static int serial_imx_remove(struct platform_device *pdev)
uart_remove_one_port(&imx_reg, &sport->port);
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
-
if (pdata && pdata->exit)
pdata->exit(pdev);
diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c
index 5f4765a7a5c..e266eca0ec7 100644
--- a/drivers/tty/serial/mfd.c
+++ b/drivers/tty/serial/mfd.c
@@ -21,6 +21,10 @@
* be triggered
*/
+#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index f51b280f3bf..e1280a20b7a 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void);
static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id);
static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port);
-
-/* Simple macro to test if a port is console or not. This one is taken
- * for serial_core.c and maybe should be moved to serial_core.h ? */
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
-#define uart_console(port) \
- ((port)->cons && (port)->cons->index == (port)->line)
-#else
-#define uart_console(port) (0)
-#endif
-
/* ======================================================================== */
/* PSC fifo operations for isolating differences between 52xx and 512x */
/* ======================================================================== */
@@ -122,6 +112,15 @@ struct psc_ops {
void (*fifoc_uninit)(void);
void (*get_irq)(struct uart_port *, struct device_node *);
irqreturn_t (*handle_irq)(struct uart_port *port);
+ u16 (*get_status)(struct uart_port *port);
+ u8 (*get_ipcr)(struct uart_port *port);
+ void (*command)(struct uart_port *port, u8 cmd);
+ void (*set_mode)(struct uart_port *port, u8 mr1, u8 mr2);
+ void (*set_rts)(struct uart_port *port, int state);
+ void (*enable_ms)(struct uart_port *port);
+ void (*set_sicr)(struct uart_port *port, u32 val);
+ void (*set_imr)(struct uart_port *port, u16 val);
+ u8 (*get_mr1)(struct uart_port *port);
};
/* setting the prescaler and divisor reg is common for all chips */
@@ -134,6 +133,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
out_8(&psc->ctlr, divisor & 0xff);
}
+static u16 mpc52xx_psc_get_status(struct uart_port *port)
+{
+ return in_be16(&PSC(port)->mpc52xx_psc_status);
+}
+
+static u8 mpc52xx_psc_get_ipcr(struct uart_port *port)
+{
+ return in_8(&PSC(port)->mpc52xx_psc_ipcr);
+}
+
+static void mpc52xx_psc_command(struct uart_port *port, u8 cmd)
+{
+ out_8(&PSC(port)->command, cmd);
+}
+
+static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2)
+{
+ out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1);
+ out_8(&PSC(port)->mode, mr1);
+ out_8(&PSC(port)->mode, mr2);
+}
+
+static void mpc52xx_psc_set_rts(struct uart_port *port, int state)
+{
+ if (state)
+ out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
+ else
+ out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
+}
+
+static void mpc52xx_psc_enable_ms(struct uart_port *port)
+{
+ struct mpc52xx_psc __iomem *psc = PSC(port);
+
+ /* clear D_*-bits by reading them */
+ in_8(&psc->mpc52xx_psc_ipcr);
+ /* enable CTS and DCD as IPC interrupts */
+ out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val)
+{
+ out_be32(&PSC(port)->sicr, val);
+}
+
+static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val)
+{
+ out_be16(&PSC(port)->mpc52xx_psc_imr, val);
+}
+
+static u8 mpc52xx_psc_get_mr1(struct uart_port *port)
+{
+ out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1);
+ return in_8(&PSC(port)->mode);
+}
+
#ifdef CONFIG_PPC_MPC52xx
#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
static void mpc52xx_psc_fifo_init(struct uart_port *port)
@@ -304,6 +362,15 @@ static struct psc_ops mpc52xx_psc_ops = {
.set_baudrate = mpc5200_psc_set_baudrate,
.get_irq = mpc52xx_psc_get_irq,
.handle_irq = mpc52xx_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
static struct psc_ops mpc5200b_psc_ops = {
@@ -325,6 +392,15 @@ static struct psc_ops mpc5200b_psc_ops = {
.set_baudrate = mpc5200b_psc_set_baudrate,
.get_irq = mpc52xx_psc_get_irq,
.handle_irq = mpc52xx_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
#endif /* CONFIG_MPC52xx */
@@ -572,6 +648,246 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np)
port->irqflags = IRQF_SHARED;
port->irq = psc_fifoc_irq;
}
+#endif
+
+#ifdef CONFIG_PPC_MPC512x
+
+#define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase))
+#define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1))
+
+static void mpc5125_psc_fifo_init(struct uart_port *port)
+{
+ /* /32 prescaler */
+ out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd);
+
+ out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+ out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+ out_be32(&FIFO_5125(port)->txalarm, 1);
+ out_be32(&FIFO_5125(port)->tximr, 0);
+
+ out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+ out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+ out_be32(&FIFO_5125(port)->rxalarm, 1);
+ out_be32(&FIFO_5125(port)->rximr, 0);
+
+ out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM);
+ out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM);
+}
+
+static int mpc5125_psc_raw_rx_rdy(struct uart_port *port)
+{
+ return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY);
+}
+
+static int mpc5125_psc_raw_tx_rdy(struct uart_port *port)
+{
+ return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL);
+}
+
+static int mpc5125_psc_rx_rdy(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->rxsr) &
+ in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc5125_psc_tx_rdy(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->txsr) &
+ in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc5125_psc_tx_empty(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY;
+}
+
+static void mpc5125_psc_stop_rx(struct uart_port *port)
+{
+ unsigned long rx_fifo_imr;
+
+ rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr);
+ rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr);
+}
+
+static void mpc5125_psc_start_tx(struct uart_port *port)
+{
+ unsigned long tx_fifo_imr;
+
+ tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr);
+ tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc5125_psc_stop_tx(struct uart_port *port)
+{
+ unsigned long tx_fifo_imr;
+
+ tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr);
+ tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc5125_psc_rx_clr_irq(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr));
+}
+
+static void mpc5125_psc_tx_clr_irq(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr));
+}
+
+static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c)
+{
+ out_8(&FIFO_5125(port)->txdata_8, c);
+}
+
+static unsigned char mpc5125_psc_read_char(struct uart_port *port)
+{
+ return in_8(&FIFO_5125(port)->rxdata_8);
+}
+
+static void mpc5125_psc_cw_disable_ints(struct uart_port *port)
+{
+ port->read_status_mask =
+ in_be32(&FIFO_5125(port)->tximr) << 16 |
+ in_be32(&FIFO_5125(port)->rximr);
+ out_be32(&FIFO_5125(port)->tximr, 0);
+ out_be32(&FIFO_5125(port)->rximr, 0);
+}
+
+static void mpc5125_psc_cw_restore_ints(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->tximr,
+ (port->read_status_mask >> 16) & 0x7f);
+ out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f);
+}
+
+static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc,
+ u8 prescaler, unsigned int divisor)
+{
+ /* select prescaler */
+ out_8(&psc->mpc52xx_psc_clock_select, prescaler);
+ out_8(&psc->ctur, divisor >> 8);
+ out_8(&psc->ctlr, divisor & 0xff);
+}
+
+static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ unsigned int baud;
+ unsigned int divisor;
+
+ /*
+ * Calculate with a /16 prescaler here.
+ */
+
+ /* uartclk contains the ips freq */
+ baud = uart_get_baud_rate(port, new, old,
+ port->uartclk / (16 * 0xffff) + 1,
+ port->uartclk / 16);
+ divisor = (port->uartclk + 8 * baud) / (16 * baud);
+
+ /* enable the /16 prescaler and set the divisor */
+ mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor);
+ return baud;
+}
+
+/*
+ * MPC5125 have compatible PSC FIFO Controller.
+ * Special init not needed.
+ */
+static u16 mpc5125_psc_get_status(struct uart_port *port)
+{
+ return in_be16(&PSC_5125(port)->mpc52xx_psc_status);
+}
+
+static u8 mpc5125_psc_get_ipcr(struct uart_port *port)
+{
+ return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr);
+}
+
+static void mpc5125_psc_command(struct uart_port *port, u8 cmd)
+{
+ out_8(&PSC_5125(port)->command, cmd);
+}
+
+static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2)
+{
+ out_8(&PSC_5125(port)->mr1, mr1);
+ out_8(&PSC_5125(port)->mr2, mr2);
+}
+
+static void mpc5125_psc_set_rts(struct uart_port *port, int state)
+{
+ if (state & TIOCM_RTS)
+ out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS);
+ else
+ out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS);
+}
+
+static void mpc5125_psc_enable_ms(struct uart_port *port)
+{
+ struct mpc5125_psc __iomem *psc = PSC_5125(port);
+
+ /* clear D_*-bits by reading them */
+ in_8(&psc->mpc52xx_psc_ipcr);
+ /* enable CTS and DCD as IPC interrupts */
+ out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val)
+{
+ out_be32(&PSC_5125(port)->sicr, val);
+}
+
+static void mpc5125_psc_set_imr(struct uart_port *port, u16 val)
+{
+ out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val);
+}
+
+static u8 mpc5125_psc_get_mr1(struct uart_port *port)
+{
+ return in_8(&PSC_5125(port)->mr1);
+}
+
+static struct psc_ops mpc5125_psc_ops = {
+ .fifo_init = mpc5125_psc_fifo_init,
+ .raw_rx_rdy = mpc5125_psc_raw_rx_rdy,
+ .raw_tx_rdy = mpc5125_psc_raw_tx_rdy,
+ .rx_rdy = mpc5125_psc_rx_rdy,
+ .tx_rdy = mpc5125_psc_tx_rdy,
+ .tx_empty = mpc5125_psc_tx_empty,
+ .stop_rx = mpc5125_psc_stop_rx,
+ .start_tx = mpc5125_psc_start_tx,
+ .stop_tx = mpc5125_psc_stop_tx,
+ .rx_clr_irq = mpc5125_psc_rx_clr_irq,
+ .tx_clr_irq = mpc5125_psc_tx_clr_irq,
+ .write_char = mpc5125_psc_write_char,
+ .read_char = mpc5125_psc_read_char,
+ .cw_disable_ints = mpc5125_psc_cw_disable_ints,
+ .cw_restore_ints = mpc5125_psc_cw_restore_ints,
+ .set_baudrate = mpc5125_psc_set_baudrate,
+ .clock = mpc512x_psc_clock,
+ .fifoc_init = mpc512x_psc_fifoc_init,
+ .fifoc_uninit = mpc512x_psc_fifoc_uninit,
+ .get_irq = mpc512x_psc_get_irq,
+ .handle_irq = mpc512x_psc_handle_irq,
+ .get_status = mpc5125_psc_get_status,
+ .get_ipcr = mpc5125_psc_get_ipcr,
+ .command = mpc5125_psc_command,
+ .set_mode = mpc5125_psc_set_mode,
+ .set_rts = mpc5125_psc_set_rts,
+ .enable_ms = mpc5125_psc_enable_ms,
+ .set_sicr = mpc5125_psc_set_sicr,
+ .set_imr = mpc5125_psc_set_imr,
+ .get_mr1 = mpc5125_psc_get_mr1,
+};
static struct psc_ops mpc512x_psc_ops = {
.fifo_init = mpc512x_psc_fifo_init,
@@ -595,8 +911,18 @@ static struct psc_ops mpc512x_psc_ops = {
.fifoc_uninit = mpc512x_psc_fifoc_uninit,
.get_irq = mpc512x_psc_get_irq,
.handle_irq = mpc512x_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
-#endif
+#endif /* CONFIG_PPC_MPC512x */
+
static const struct psc_ops *psc_ops;
@@ -613,17 +939,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port)
static void
mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- if (mctrl & TIOCM_RTS)
- out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
- else
- out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
+ psc_ops->set_rts(port, mctrl & TIOCM_RTS);
}
static unsigned int
mpc52xx_uart_get_mctrl(struct uart_port *port)
{
unsigned int ret = TIOCM_DSR;
- u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+ u8 status = psc_ops->get_ipcr(port);
if (!(status & MPC52xx_PSC_CTS))
ret |= TIOCM_CTS;
@@ -673,15 +996,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port)
static void
mpc52xx_uart_enable_ms(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
-
- /* clear D_*-bits by reading them */
- in_8(&psc->mpc52xx_psc_ipcr);
- /* enable CTS and DCD as IPC interrupts */
- out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
-
- port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
- out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+ psc_ops->enable_ms(port);
}
static void
@@ -691,9 +1006,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
spin_lock_irqsave(&port->lock, flags);
if (ctl == -1)
- out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK);
+ psc_ops->command(port, MPC52xx_PSC_START_BRK);
else
- out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK);
+ psc_ops->command(port, MPC52xx_PSC_STOP_BRK);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -701,7 +1016,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
static int
mpc52xx_uart_startup(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
int ret;
if (psc_ops->clock) {
@@ -717,15 +1031,15 @@ mpc52xx_uart_startup(struct uart_port *port)
return ret;
/* Reset/activate the port, clear and enable interrupts */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
- out_be32(&psc->sicr, 0); /* UART mode DCD ignored */
+ psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */
psc_ops->fifo_init(port);
- out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
- out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_TX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_RX_ENABLE);
return 0;
}
@@ -733,19 +1047,20 @@ mpc52xx_uart_startup(struct uart_port *port)
static void
mpc52xx_uart_shutdown(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
-
/* Shut down the port. Leave TX active if on a console port */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
if (!uart_console(port))
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
port->read_status_mask = 0;
- out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+ psc_ops->set_imr(port, port->read_status_mask);
if (psc_ops->clock)
psc_ops->clock(port, 0);
+ /* Disable interrupt */
+ psc_ops->cw_disable_ints(port);
+
/* Release interrupt */
free_irq(port->irq, port);
}
@@ -754,7 +1069,6 @@ static void
mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
struct ktermios *old)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
unsigned long flags;
unsigned char mr1, mr2;
unsigned int j;
@@ -818,13 +1132,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
"Some chars may have been lost.\n");
/* Reset the TX & RX */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
/* Send new mode settings */
- out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
- out_8(&psc->mode, mr1);
- out_8(&psc->mode, mr2);
+ psc_ops->set_mode(port, mr1, mr2);
baud = psc_ops->set_baudrate(port, new, old);
/* Update the per-port timeout */
@@ -834,8 +1146,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
mpc52xx_uart_enable_ms(port);
/* Reenable TX & RX */
- out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
- out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_TX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_RX_ENABLE);
/* We're all set, release the lock */
spin_unlock_irqrestore(&port->lock, flags);
@@ -963,7 +1275,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
flag = TTY_NORMAL;
port->icount.rx++;
- status = in_be16(&PSC(port)->mpc52xx_psc_status);
+ status = psc_ops->get_status(port);
if (status & (MPC52xx_PSC_SR_PE |
MPC52xx_PSC_SR_FE |
@@ -983,7 +1295,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
}
/* Clear error condition */
- out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT);
+ psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT);
}
tty_insert_flip_char(tport, ch, flag);
@@ -1066,7 +1378,7 @@ mpc5xxx_uart_process_int(struct uart_port *port)
if (psc_ops->tx_rdy(port))
keepgoing |= mpc52xx_uart_int_tx_chars(port);
- status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+ status = psc_ops->get_ipcr(port);
if (status & MPC52xx_PSC_D_DCD)
uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
@@ -1107,14 +1419,12 @@ static void __init
mpc52xx_console_get_options(struct uart_port *port,
int *baud, int *parity, int *bits, int *flow)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
unsigned char mr1;
pr_debug("mpc52xx_console_get_options(port=%p)\n", port);
/* Read the mode registers */
- out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
- mr1 = in_8(&psc->mode);
+ mr1 = psc_ops->get_mr1(port);
/* CT{U,L}R are write-only ! */
*baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
@@ -1304,6 +1614,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = {
#endif
#ifdef CONFIG_PPC_MPC512x
{ .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, },
+ { .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, },
#endif
{},
};
@@ -1372,15 +1683,14 @@ static int mpc52xx_uart_of_probe(struct platform_device *op)
if (ret)
return ret;
- dev_set_drvdata(&op->dev, (void *)port);
+ platform_set_drvdata(op, (void *)port);
return 0;
}
static int
mpc52xx_uart_of_remove(struct platform_device *op)
{
- struct uart_port *port = dev_get_drvdata(&op->dev);
- dev_set_drvdata(&op->dev, NULL);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_remove_one_port(&mpc52xx_uart_driver, port);
@@ -1392,7 +1702,7 @@ mpc52xx_uart_of_remove(struct platform_device *op)
static int
mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
{
- struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
+ struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
if (port)
uart_suspend_port(&mpc52xx_uart_driver, port);
@@ -1403,7 +1713,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
static int
mpc52xx_uart_of_resume(struct platform_device *op)
{
- struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
+ struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
if (port)
uart_resume_port(&mpc52xx_uart_driver, port);
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 39c7ea4cb14..2caf9c6f614 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -204,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
info->type = port_type;
info->line = ret;
- dev_set_drvdata(&ofdev->dev, info);
+ platform_set_drvdata(ofdev, info);
return 0;
out:
kfree(info);
@@ -217,7 +217,7 @@ out:
*/
static int of_platform_serial_remove(struct platform_device *ofdev)
{
- struct of_serial_info *info = dev_get_drvdata(&ofdev->dev);
+ struct of_serial_info *info = platform_get_drvdata(ofdev);
switch (info->type) {
#ifdef CONFIG_SERIAL_8250
case PORT_8250 ... PORT_MAX_8250:
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index f0b9f6b52b3..b6d17287307 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -161,6 +161,7 @@ struct uart_omap_port {
u32 calc_latency;
struct work_struct qos_work;
struct pinctrl *pins;
+ bool is_suspending;
};
#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port)))
@@ -197,7 +198,7 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
struct omap_uart_port_info *pdata = up->dev->platform_data;
if (!pdata || !pdata->get_context_loss_count)
- return 0;
+ return -EINVAL;
return pdata->get_context_loss_count(up->dev);
}
@@ -1289,6 +1290,22 @@ static struct uart_driver serial_omap_reg = {
};
#ifdef CONFIG_PM_SLEEP
+static int serial_omap_prepare(struct device *dev)
+{
+ struct uart_omap_port *up = dev_get_drvdata(dev);
+
+ up->is_suspending = true;
+
+ return 0;
+}
+
+static void serial_omap_complete(struct device *dev)
+{
+ struct uart_omap_port *up = dev_get_drvdata(dev);
+
+ up->is_suspending = false;
+}
+
static int serial_omap_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
@@ -1307,7 +1324,10 @@ static int serial_omap_resume(struct device *dev)
return 0;
}
-#endif
+#else
+#define serial_omap_prepare NULL
+#define serial_omap_complete NULL
+#endif /* CONFIG_PM_SLEEP */
static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
{
@@ -1482,6 +1502,9 @@ static int serial_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, up);
pm_runtime_enable(&pdev->dev);
+ if (omap_up_info->autosuspend_timeout == 0)
+ omap_up_info->autosuspend_timeout = -1;
+ device_init_wakeup(up->dev, true);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev,
omap_up_info->autosuspend_timeout);
@@ -1591,13 +1614,19 @@ static void serial_omap_restore_context(struct uart_omap_port *up)
static int serial_omap_runtime_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- struct omap_uart_port_info *pdata = dev->platform_data;
if (!up)
return -EINVAL;
- if (!pdata)
- return 0;
+ /*
+ * When using 'no_console_suspend', the console UART must not be
+ * suspended. Since driver suspend is managed by runtime suspend,
+ * preventing runtime suspend (by returning error) will keep device
+ * active during suspend.
+ */
+ if (up->is_suspending && !console_suspend_enabled &&
+ uart_console(&up->port))
+ return -EBUSY;
up->context_loss_cnt = serial_omap_get_context_loss_count(up);
@@ -1626,7 +1655,7 @@ static int serial_omap_runtime_resume(struct device *dev)
int loss_cnt = serial_omap_get_context_loss_count(up);
if (loss_cnt < 0) {
- dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n",
+ dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
loss_cnt);
serial_omap_restore_context(up);
} else if (up->context_loss_cnt != loss_cnt) {
@@ -1643,6 +1672,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
serial_omap_runtime_resume, NULL)
+ .prepare = serial_omap_prepare,
+ .complete = serial_omap_complete,
};
#if defined(CONFIG_OF)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 21a7e179edf..572d48189de 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -217,6 +217,7 @@ enum {
#define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */
#define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */
#define NTC1_UARTCLK 64000000 /* 64.0000 MHz */
+#define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */
struct pch_uart_buffer {
unsigned char *buf;
@@ -398,6 +399,10 @@ static int pch_uart_get_uartclk(void)
strstr(cmp, "nanoETXexpress-TT")))
return NTC1_UARTCLK;
+ cmp = dmi_get_system_info(DMI_BOARD_NAME);
+ if (cmp && strstr(cmp, "MinnowBoard"))
+ return MINNOW_UARTCLK;
+
return DEFAULT_UARTCLK;
}
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 0c8a9fa2be6..81ebc86a9e2 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1811,7 +1811,13 @@ static int __init s3c24xx_serial_modinit(void)
return ret;
}
- return platform_driver_register(&samsung_serial_driver);
+ ret = platform_driver_register(&samsung_serial_driver);
+ if (ret < 0) {
+ pr_err("Failed to register platform driver\n");
+ uart_unregister_driver(&s3c24xx_uart_drv);
+ }
+
+ return ret;
}
static void __exit s3c24xx_serial_modexit(void)
diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c
index c9735680762..4b1434d53e9 100644
--- a/drivers/tty/serial/sc26xx.c
+++ b/drivers/tty/serial/sc26xx.c
@@ -696,7 +696,7 @@ static int sc26xx_probe(struct platform_device *dev)
if (err)
goto out_remove_ports;
- dev_set_drvdata(&dev->dev, up);
+ platform_set_drvdata(dev, up);
return 0;
out_remove_ports:
@@ -716,7 +716,7 @@ out_free_port:
static int __exit sc26xx_driver_remove(struct platform_device *dev)
{
- struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev);
+ struct uart_sc26xx_port *up = platform_get_drvdata(dev);
free_irq(up->port[0].irq, up);
@@ -728,7 +728,6 @@ static int __exit sc26xx_driver_remove(struct platform_device *dev)
kfree(up);
sc26xx_port = NULL;
- dev_set_drvdata(&dev->dev, NULL);
return 0;
}
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index f87dbfd3277..28cdd282913 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -50,12 +50,6 @@ static struct lock_class_key port_lock_key;
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
-#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line)
-#else
-#define uart_console(port) (0)
-#endif
-
static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
struct ktermios *old_termios);
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 03465b67394..1fd564b8194 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -687,9 +687,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
if (sirfport->hw_flow_ctrl) {
sirfport->p = pinctrl_get_select_default(&pdev->dev);
- ret = IS_ERR(sirfport->p);
- if (ret)
+ if (IS_ERR(sirfport->p)) {
+ ret = PTR_ERR(sirfport->p);
goto err;
+ }
}
sirfport->clk = clk_get(&pdev->dev, NULL);
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index ba60708053e..cf86e729532 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -577,7 +577,7 @@ static int hv_probe(struct platform_device *op)
if (err)
goto out_remove_port;
- dev_set_drvdata(&op->dev, port);
+ platform_set_drvdata(op, port);
return 0;
@@ -601,7 +601,7 @@ out_free_port:
static int hv_remove(struct platform_device *dev)
{
- struct uart_port *port = dev_get_drvdata(&dev->dev);
+ struct uart_port *port = platform_get_drvdata(dev);
free_irq(port->irq, port);
@@ -612,8 +612,6 @@ static int hv_remove(struct platform_device *dev)
kfree(port);
sunhv_port = NULL;
- dev_set_drvdata(&dev->dev, NULL);
-
return 0;
}
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index a422c8b55a4..5d6136b2a04 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -1037,7 +1037,7 @@ static int sab_probe(struct platform_device *op)
if (err)
goto out3;
- dev_set_drvdata(&op->dev, &up[0]);
+ platform_set_drvdata(op, &up[0]);
inst++;
@@ -1059,7 +1059,7 @@ out:
static int sab_remove(struct platform_device *op)
{
- struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunsab_port *up = platform_get_drvdata(op);
uart_remove_one_port(&sunsab_reg, &up[1].port);
uart_remove_one_port(&sunsab_reg, &up[0].port);
@@ -1070,8 +1070,6 @@ static int sab_remove(struct platform_device *op)
up[0].port.membase,
sizeof(union sab82532_async_regs));
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 0d846572847..699cc1b5f6a 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -1454,7 +1454,7 @@ static int su_probe(struct platform_device *op)
kfree(up);
return err;
}
- dev_set_drvdata(&op->dev, up);
+ platform_set_drvdata(op, up);
nr_inst++;
@@ -1483,7 +1483,7 @@ static int su_probe(struct platform_device *op)
if (err)
goto out_unmap;
- dev_set_drvdata(&op->dev, up);
+ platform_set_drvdata(op, up);
nr_inst++;
@@ -1496,7 +1496,7 @@ out_unmap:
static int su_remove(struct platform_device *op)
{
- struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunsu_port *up = platform_get_drvdata(op);
bool kbdms = false;
if (up->su_type == SU_PORT_MS ||
@@ -1516,8 +1516,6 @@ static int su_remove(struct platform_device *op)
if (kbdms)
kfree(up);
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 813ef8eb8ef..135a1520353 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -1495,7 +1495,7 @@ static int zs_probe(struct platform_device *op)
kbm_inst++;
}
- dev_set_drvdata(&op->dev, &up[0]);
+ platform_set_drvdata(op, &up[0]);
return 0;
}
@@ -1512,7 +1512,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up)
static int zs_remove(struct platform_device *op)
{
- struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunzilog_port *up = platform_get_drvdata(op);
struct zilog_layout __iomem *regs;
zs_remove_one(&up[0]);
@@ -1521,8 +1521,6 @@ static int zs_remove(struct platform_device *op)
regs = sunzilog_chip_regs[up[0].port.line / 2];
of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout));
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 7355303dad9..88317482b81 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1451,7 +1451,7 @@ static int ucc_uart_probe(struct platform_device *ofdev)
goto out_np;
}
- dev_set_drvdata(&ofdev->dev, qe_port);
+ platform_set_drvdata(ofdev, qe_port);
dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n",
qe_port->ucc_num + 1, qe_port->port.line);
@@ -1471,13 +1471,12 @@ out_free:
static int ucc_uart_remove(struct platform_device *ofdev)
{
- struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev);
+ struct uart_qe_port *qe_port = platform_get_drvdata(ofdev);
dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line);
uart_remove_one_port(&ucc_uart_driver, &qe_port->port);
- dev_set_drvdata(&ofdev->dev, NULL);
kfree(qe_port);
return 0;
@@ -1518,9 +1517,11 @@ static int __init ucc_uart_init(void)
}
ret = platform_driver_register(&ucc_uart_of_driver);
- if (ret)
+ if (ret) {
printk(KERN_ERR
"ucc-uart: could not register platform driver\n");
+ uart_unregister_driver(&ucc_uart_driver);
+ }
return ret;
}
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 1a8bc2275ea..48af43de346 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -648,7 +648,7 @@ static struct platform_driver vt8500_platform_driver = {
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(wmt_dt_ids),
+ .of_match_table = wmt_dt_ids,
},
};
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 4e5c77834c5..6c917453042 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -974,12 +974,11 @@ static int xuartps_probe(struct platform_device *pdev)
port->dev = &pdev->dev;
port->uartclk = clk_get_rate(clk);
port->private_data = clk;
- dev_set_drvdata(&pdev->dev, port);
+ platform_set_drvdata(pdev, port);
rc = uart_add_one_port(&xuartps_uart_driver, port);
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
- dev_set_drvdata(&pdev->dev, NULL);
return rc;
}
return 0;
@@ -994,46 +993,17 @@ static int xuartps_probe(struct platform_device *pdev)
**/
static int xuartps_remove(struct platform_device *pdev)
{
- struct uart_port *port = dev_get_drvdata(&pdev->dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
struct clk *clk = port->private_data;
int rc;
/* Remove the xuartps port from the serial core */
rc = uart_remove_one_port(&xuartps_uart_driver, port);
- dev_set_drvdata(&pdev->dev, NULL);
port->mapbase = 0;
clk_disable_unprepare(clk);
return rc;
}
-/**
- * xuartps_suspend - suspend event
- * @pdev: Pointer to the platform device structure
- * @state: State of the device
- *
- * Returns 0
- **/
-static int xuartps_suspend(struct platform_device *pdev, pm_message_t state)
-{
- /* Call the API provided in serial_core.c file which handles
- * the suspend.
- */
- uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
- return 0;
-}
-
-/**
- * xuartps_resume - Resume after a previous suspend
- * @pdev: Pointer to the platform device structure
- *
- * Returns 0
- **/
-static int xuartps_resume(struct platform_device *pdev)
-{
- uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
- return 0;
-}
-
/* Match table for of_platform binding */
static struct of_device_id xuartps_of_match[] = {
{ .compatible = "xlnx,xuartps", },
@@ -1044,8 +1014,6 @@ MODULE_DEVICE_TABLE(of, xuartps_of_match);
static struct platform_driver xuartps_platform_driver = {
.probe = xuartps_probe, /* Probe method */
.remove = xuartps_remove, /* Detach method */
- .suspend = xuartps_suspend, /* Suspend */
- .resume = xuartps_resume, /* Resume after a suspend */
.driver = {
.owner = THIS_MODULE,
.name = XUARTPS_NAME, /* Driver name */
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index b51c15408ff..5f68f2cfdfd 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -932,7 +932,7 @@ static int sysrq_reset_seq_param_set(const char *buffer,
unsigned long val;
int error;
- error = strict_strtoul(buffer, 0, &val);
+ error = kstrtoul(buffer, 0, &val);
if (error < 0)
return error;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 6464029e486..366af832794 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1618,6 +1618,8 @@ static void release_tty(struct tty_struct *tty, int idx)
tty_free_termios(tty);
tty_driver_remove_tty(tty->driver, tty);
tty->port->itty = NULL;
+ if (tty->link)
+ tty->link->port->itty = NULL;
cancel_work_sync(&tty->port->buf.work);
if (tty->link)
@@ -2138,6 +2140,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
static int __tty_fasync(int fd, struct file *filp, int on)
{
struct tty_struct *tty = file_tty(filp);
+ struct tty_ldisc *ldisc;
unsigned long flags;
int retval = 0;
@@ -2148,11 +2151,17 @@ static int __tty_fasync(int fd, struct file *filp, int on)
if (retval <= 0)
goto out;
+ ldisc = tty_ldisc_ref(tty);
+ if (ldisc) {
+ if (ldisc->ops->fasync)
+ ldisc->ops->fasync(tty, on);
+ tty_ldisc_deref(ldisc);
+ }
+
if (on) {
enum pid_type type;
struct pid *pid;
- if (!waitqueue_active(&tty->read_wait))
- tty->minimum_to_wake = 1;
+
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (tty->pgrp) {
pid = tty->pgrp;
@@ -2165,13 +2174,7 @@ static int __tty_fasync(int fd, struct file *filp, int on)
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
retval = __f_setown(filp, pid, type, 0);
put_pid(pid);
- if (retval)
- goto out;
- } else {
- if (!tty->fasync && !waitqueue_active(&tty->read_wait))
- tty->minimum_to_wake = N_TTY_BUF_SIZE;
}
- retval = 0;
out:
return retval;
}
diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c
new file mode 100644
index 00000000000..22fad8ad5ac
--- /dev/null
+++ b/drivers/tty/tty_ldsem.c
@@ -0,0 +1,453 @@
+/*
+ * Ldisc rw semaphore
+ *
+ * The ldisc semaphore is semantically a rw_semaphore but which enforces
+ * an alternate policy, namely:
+ * 1) Supports lock wait timeouts
+ * 2) Write waiter has priority
+ * 3) Downgrading is not supported
+ *
+ * Implementation notes:
+ * 1) Upper half of semaphore count is a wait count (differs from rwsem
+ * in that rwsem normalizes the upper half to the wait bias)
+ * 2) Lacks overflow checking
+ *
+ * The generic counting was copied and modified from include/asm-generic/rwsem.h
+ * by Paul Mackerras <paulus@samba.org>.
+ *
+ * The scheduling policy was copied and modified from lib/rwsem.c
+ * Written by David Howells (dhowells@redhat.com).
+ *
+ * This implementation incorporates the write lock stealing work of
+ * Michel Lespinasse <walken@google.com>.
+ *
+ * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com>
+ *
+ * This file may be redistributed under the terms of the GNU General Public
+ * License v2.
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define __acq(l, s, t, r, c, n, i) \
+ lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
+# define __rel(l, n, i) \
+ lock_release(&(l)->dep_map, n, i)
+# ifdef CONFIG_PROVE_LOCKING
+# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 2, NULL, i)
+# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 2, n, i)
+# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 2, NULL, i)
+# define lockdep_release(l, n, i) __rel(l, n, i)
+# else
+# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i)
+# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i)
+# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i)
+# define lockdep_release(l, n, i) __rel(l, n, i)
+# endif
+#else
+# define lockdep_acquire(l, s, t, i) do { } while (0)
+# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0)
+# define lockdep_acquire_read(l, s, t, i) do { } while (0)
+# define lockdep_release(l, n, i) do { } while (0)
+#endif
+
+#ifdef CONFIG_LOCK_STAT
+# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_)
+#else
+# define lock_stat(_lock, stat) do { } while (0)
+#endif
+
+
+#if BITS_PER_LONG == 64
+# define LDSEM_ACTIVE_MASK 0xffffffffL
+#else
+# define LDSEM_ACTIVE_MASK 0x0000ffffL
+#endif
+
+#define LDSEM_UNLOCKED 0L
+#define LDSEM_ACTIVE_BIAS 1L
+#define LDSEM_WAIT_BIAS (-LDSEM_ACTIVE_MASK-1)
+#define LDSEM_READ_BIAS LDSEM_ACTIVE_BIAS
+#define LDSEM_WRITE_BIAS (LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS)
+
+struct ldsem_waiter {
+ struct list_head list;
+ struct task_struct *task;
+};
+
+static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem)
+{
+ return atomic_long_add_return(delta, (atomic_long_t *)&sem->count);
+}
+
+static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem)
+{
+ long tmp = *old;
+ *old = atomic_long_cmpxchg(&sem->count, *old, new);
+ return *old == tmp;
+}
+
+/*
+ * Initialize an ldsem:
+ */
+void __init_ldsem(struct ld_semaphore *sem, const char *name,
+ struct lock_class_key *key)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ /*
+ * Make sure we are not reinitializing a held semaphore:
+ */
+ debug_check_no_locks_freed((void *)sem, sizeof(*sem));
+ lockdep_init_map(&sem->dep_map, name, key, 0);
+#endif
+ sem->count = LDSEM_UNLOCKED;
+ sem->wait_readers = 0;
+ raw_spin_lock_init(&sem->wait_lock);
+ INIT_LIST_HEAD(&sem->read_wait);
+ INIT_LIST_HEAD(&sem->write_wait);
+}
+
+static void __ldsem_wake_readers(struct ld_semaphore *sem)
+{
+ struct ldsem_waiter *waiter, *next;
+ struct task_struct *tsk;
+ long adjust, count;
+
+ /* Try to grant read locks to all readers on the read wait list.
+ * Note the 'active part' of the count is incremented by
+ * the number of readers before waking any processes up.
+ */
+ adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS);
+ count = ldsem_atomic_update(adjust, sem);
+ do {
+ if (count > 0)
+ break;
+ if (ldsem_cmpxchg(&count, count - adjust, sem))
+ return;
+ } while (1);
+
+ list_for_each_entry_safe(waiter, next, &sem->read_wait, list) {
+ tsk = waiter->task;
+ smp_mb();
+ waiter->task = NULL;
+ wake_up_process(tsk);
+ put_task_struct(tsk);
+ }
+ INIT_LIST_HEAD(&sem->read_wait);
+ sem->wait_readers = 0;
+}
+
+static inline int writer_trylock(struct ld_semaphore *sem)
+{
+ /* only wake this writer if the active part of the count can be
+ * transitioned from 0 -> 1
+ */
+ long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem);
+ do {
+ if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS)
+ return 1;
+ if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem))
+ return 0;
+ } while (1);
+}
+
+static void __ldsem_wake_writer(struct ld_semaphore *sem)
+{
+ struct ldsem_waiter *waiter;
+
+ waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list);
+ wake_up_process(waiter->task);
+}
+
+/*
+ * handle the lock release when processes blocked on it that can now run
+ * - if we come here from up_xxxx(), then:
+ * - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed)
+ * - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so)
+ * - the spinlock must be held by the caller
+ * - woken process blocks are discarded from the list after having task zeroed
+ */
+static void __ldsem_wake(struct ld_semaphore *sem)
+{
+ if (!list_empty(&sem->write_wait))
+ __ldsem_wake_writer(sem);
+ else if (!list_empty(&sem->read_wait))
+ __ldsem_wake_readers(sem);
+}
+
+static void ldsem_wake(struct ld_semaphore *sem)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&sem->wait_lock, flags);
+ __ldsem_wake(sem);
+ raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
+}
+
+/*
+ * wait for the read lock to be granted
+ */
+static struct ld_semaphore __sched *
+down_read_failed(struct ld_semaphore *sem, long count, long timeout)
+{
+ struct ldsem_waiter waiter;
+ struct task_struct *tsk = current;
+ long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS;
+
+ /* set up my own style of waitqueue */
+ raw_spin_lock_irq(&sem->wait_lock);
+
+ /* Try to reverse the lock attempt but if the count has changed
+ * so that reversing fails, check if there are are no waiters,
+ * and early-out if not */
+ do {
+ if (ldsem_cmpxchg(&count, count + adjust, sem))
+ break;
+ if (count > 0) {
+ raw_spin_unlock_irq(&sem->wait_lock);
+ return sem;
+ }
+ } while (1);
+
+ list_add_tail(&waiter.list, &sem->read_wait);
+ sem->wait_readers++;
+
+ waiter.task = tsk;
+ get_task_struct(tsk);
+
+ /* if there are no active locks, wake the new lock owner(s) */
+ if ((count & LDSEM_ACTIVE_MASK) == 0)
+ __ldsem_wake(sem);
+
+ raw_spin_unlock_irq(&sem->wait_lock);
+
+ /* wait to be given the lock */
+ for (;;) {
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+
+ if (!waiter.task)
+ break;
+ if (!timeout)
+ break;
+ timeout = schedule_timeout(timeout);
+ }
+
+ __set_task_state(tsk, TASK_RUNNING);
+
+ if (!timeout) {
+ /* lock timed out but check if this task was just
+ * granted lock ownership - if so, pretend there
+ * was no timeout; otherwise, cleanup lock wait */
+ raw_spin_lock_irq(&sem->wait_lock);
+ if (waiter.task) {
+ ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
+ list_del(&waiter.list);
+ raw_spin_unlock_irq(&sem->wait_lock);
+ put_task_struct(waiter.task);
+ return NULL;
+ }
+ raw_spin_unlock_irq(&sem->wait_lock);
+ }
+
+ return sem;
+}
+
+/*
+ * wait for the write lock to be granted
+ */
+static struct ld_semaphore __sched *
+down_write_failed(struct ld_semaphore *sem, long count, long timeout)
+{
+ struct ldsem_waiter waiter;
+ struct task_struct *tsk = current;
+ long adjust = -LDSEM_ACTIVE_BIAS;
+ int locked = 0;
+
+ /* set up my own style of waitqueue */
+ raw_spin_lock_irq(&sem->wait_lock);
+
+ /* Try to reverse the lock attempt but if the count has changed
+ * so that reversing fails, check if the lock is now owned,
+ * and early-out if so */
+ do {
+ if (ldsem_cmpxchg(&count, count + adjust, sem))
+ break;
+ if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) {
+ raw_spin_unlock_irq(&sem->wait_lock);
+ return sem;
+ }
+ } while (1);
+
+ list_add_tail(&waiter.list, &sem->write_wait);
+
+ waiter.task = tsk;
+
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ for (;;) {
+ if (!timeout)
+ break;
+ raw_spin_unlock_irq(&sem->wait_lock);
+ timeout = schedule_timeout(timeout);
+ raw_spin_lock_irq(&sem->wait_lock);
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if ((locked = writer_trylock(sem)))
+ break;
+ }
+
+ if (!locked)
+ ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem);
+ list_del(&waiter.list);
+ raw_spin_unlock_irq(&sem->wait_lock);
+
+ __set_task_state(tsk, TASK_RUNNING);
+
+ /* lock wait may have timed out */
+ if (!locked)
+ return NULL;
+ return sem;
+}
+
+
+
+static inline int __ldsem_down_read_nested(struct ld_semaphore *sem,
+ int subclass, long timeout)
+{
+ long count;
+
+ lockdep_acquire_read(sem, subclass, 0, _RET_IP_);
+
+ count = ldsem_atomic_update(LDSEM_READ_BIAS, sem);
+ if (count <= 0) {
+ lock_stat(sem, contended);
+ if (!down_read_failed(sem, count, timeout)) {
+ lockdep_release(sem, 1, _RET_IP_);
+ return 0;
+ }
+ }
+ lock_stat(sem, acquired);
+ return 1;
+}
+
+static inline int __ldsem_down_write_nested(struct ld_semaphore *sem,
+ int subclass, long timeout)
+{
+ long count;
+
+ lockdep_acquire(sem, subclass, 0, _RET_IP_);
+
+ count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem);
+ if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
+ lock_stat(sem, contended);
+ if (!down_write_failed(sem, count, timeout)) {
+ lockdep_release(sem, 1, _RET_IP_);
+ return 0;
+ }
+ }
+ lock_stat(sem, acquired);
+ return 1;
+}
+
+
+/*
+ * lock for reading -- returns 1 if successful, 0 if timed out
+ */
+int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout)
+{
+ might_sleep();
+ return __ldsem_down_read_nested(sem, 0, timeout);
+}
+
+/*
+ * trylock for reading -- returns 1 if successful, 0 if contention
+ */
+int ldsem_down_read_trylock(struct ld_semaphore *sem)
+{
+ long count = sem->count;
+
+ while (count >= 0) {
+ if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) {
+ lockdep_acquire_read(sem, 0, 1, _RET_IP_);
+ lock_stat(sem, acquired);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * lock for writing -- returns 1 if successful, 0 if timed out
+ */
+int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout)
+{
+ might_sleep();
+ return __ldsem_down_write_nested(sem, 0, timeout);
+}
+
+/*
+ * trylock for writing -- returns 1 if successful, 0 if contention
+ */
+int ldsem_down_write_trylock(struct ld_semaphore *sem)
+{
+ long count = sem->count;
+
+ while ((count & LDSEM_ACTIVE_MASK) == 0) {
+ if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) {
+ lockdep_acquire(sem, 0, 1, _RET_IP_);
+ lock_stat(sem, acquired);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * release a read lock
+ */
+void ldsem_up_read(struct ld_semaphore *sem)
+{
+ long count;
+
+ lockdep_release(sem, 1, _RET_IP_);
+
+ count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem);
+ if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
+ ldsem_wake(sem);
+}
+
+/*
+ * release a write lock
+ */
+void ldsem_up_write(struct ld_semaphore *sem)
+{
+ long count;
+
+ lockdep_release(sem, 1, _RET_IP_);
+
+ count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem);
+ if (count < 0)
+ ldsem_wake(sem);
+}
+
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+
+int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout)
+{
+ might_sleep();
+ return __ldsem_down_read_nested(sem, subclass, timeout);
+}
+
+int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass,
+ long timeout)
+{
+ might_sleep();
+ return __ldsem_down_write_nested(sem, subclass, timeout);
+}
+
+#endif
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 740202d8a5c..c677829baa8 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -3086,17 +3086,6 @@ err:
};
-static int bind_con_driver(const struct consw *csw, int first, int last,
- int deflt)
-{
- int ret;
-
- console_lock();
- ret = do_bind_con_driver(csw, first, last, deflt);
- console_unlock();
- return ret;
-}
-
#ifdef CONFIG_VT_HW_CONSOLE_BINDING
static int con_is_graphics(const struct consw *csw, int first, int last)
{
@@ -3114,34 +3103,6 @@ static int con_is_graphics(const struct consw *csw, int first, int last)
return retval;
}
-/**
- * unbind_con_driver - unbind a console driver
- * @csw: pointer to console driver to unregister
- * @first: first in range of consoles that @csw should be unbound from
- * @last: last in range of consoles that @csw should be unbound from
- * @deflt: should next bound console driver be default after @csw is unbound?
- *
- * To unbind a driver from all possible consoles, pass 0 as @first and
- * %MAX_NR_CONSOLES as @last.
- *
- * @deflt controls whether the console that ends up replacing @csw should be
- * the default console.
- *
- * RETURNS:
- * -ENODEV if @csw isn't a registered console driver or can't be unregistered
- * or 0 on success.
- */
-int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
-{
- int retval;
-
- console_lock();
- retval = do_unbind_con_driver(csw, first, last, deflt);
- console_unlock();
- return retval;
-}
-EXPORT_SYMBOL(unbind_con_driver);
-
/* unlocked version of unbind_con_driver() */
int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
{
@@ -3262,8 +3223,11 @@ static int vt_bind(struct con_driver *con)
if (first == 0 && last == MAX_NR_CONSOLES -1)
deflt = 1;
- if (first != -1)
- bind_con_driver(csw, first, last, deflt);
+ if (first != -1) {
+ console_lock();
+ do_bind_con_driver(csw, first, last, deflt);
+ console_unlock();
+ }
first = -1;
last = -1;
@@ -3301,8 +3265,11 @@ static int vt_unbind(struct con_driver *con)
if (first == 0 && last == MAX_NR_CONSOLES -1)
deflt = 1;
- if (first != -1)
- unbind_con_driver(csw, first, last, deflt);
+ if (first != -1) {
+ console_lock();
+ do_unbind_con_driver(csw, first, last, deflt);
+ console_unlock();
+ }
first = -1;
last = -1;
@@ -3574,29 +3541,9 @@ err:
return retval;
}
-/**
- * register_con_driver - register console driver to console layer
- * @csw: console driver
- * @first: the first console to take over, minimum value is 0
- * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
- *
- * DESCRIPTION: This function registers a console driver which can later
- * bind to a range of consoles specified by @first and @last. It will
- * also initialize the console driver by calling con_startup().
- */
-int register_con_driver(const struct consw *csw, int first, int last)
-{
- int retval;
-
- console_lock();
- retval = do_register_con_driver(csw, first, last);
- console_unlock();
- return retval;
-}
-EXPORT_SYMBOL(register_con_driver);
/**
- * unregister_con_driver - unregister console driver from console layer
+ * do_unregister_con_driver - unregister console driver from console layer
* @csw: console driver
*
* DESCRIPTION: All drivers that registers to the console layer must
@@ -3606,17 +3553,6 @@ EXPORT_SYMBOL(register_con_driver);
*
* The driver must unbind first prior to unregistration.
*/
-int unregister_con_driver(const struct consw *csw)
-{
- int retval;
-
- console_lock();
- retval = do_unregister_con_driver(csw);
- console_unlock();
- return retval;
-}
-EXPORT_SYMBOL(unregister_con_driver);
-
int do_unregister_con_driver(const struct consw *csw)
{
int i, retval = -ENODEV;
@@ -3654,7 +3590,7 @@ EXPORT_SYMBOL_GPL(do_unregister_con_driver);
* when a driver wants to take over some existing consoles
* and become default driver for newly opened ones.
*
- * take_over_console is basically a register followed by unbind
+ * do_take_over_console is basically a register followed by unbind
*/
int do_take_over_console(const struct consw *csw, int first, int last, int deflt)
{
@@ -3675,30 +3611,6 @@ int do_take_over_console(const struct consw *csw, int first, int last, int deflt
}
EXPORT_SYMBOL_GPL(do_take_over_console);
-/*
- * If we support more console drivers, this function is used
- * when a driver wants to take over some existing consoles
- * and become default driver for newly opened ones.
- *
- * take_over_console is basically a register followed by unbind
- */
-int take_over_console(const struct consw *csw, int first, int last, int deflt)
-{
- int err;
-
- err = register_con_driver(csw, first, last);
- /*
- * If we get an busy error we still want to bind the console driver
- * and return success, as we may have unbound the console driver
- * but not unregistered it.
- */
- if (err == -EBUSY)
- err = 0;
- if (!err)
- bind_con_driver(csw, first, last, deflt);
-
- return err;
-}
/*
* give_up_console is a wrapper to unregister_con_driver. It will only
@@ -3706,7 +3618,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
*/
void give_up_console(const struct consw *csw)
{
- unregister_con_driver(csw);
+ console_lock();
+ do_unregister_con_driver(csw);
+ console_unlock();
}
static int __init vtconsole_class_init(void)
@@ -4262,6 +4176,5 @@ EXPORT_SYMBOL(console_blanked);
EXPORT_SYMBOL(vc_cons);
EXPORT_SYMBOL(global_cursor_default);
#ifndef VT_SINGLE_DRIVER
-EXPORT_SYMBOL(take_over_console);
EXPORT_SYMBOL(give_up_console);
#endif
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index 411e605f448..a638c4e9a94 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -208,7 +208,7 @@ sisusbcon_init(struct vc_data *c, int init)
struct sisusb_usb_data *sisusb;
int cols, rows;
- /* This is called by take_over_console(),
+ /* This is called by do_take_over_console(),
* ie by us/under our control. It is
* only called after text mode and fonts
* are set up/restored.
@@ -273,7 +273,7 @@ sisusbcon_deinit(struct vc_data *c)
struct sisusb_usb_data *sisusb;
int i;
- /* This is called by take_over_console()
+ /* This is called by do_take_over_console()
* and others, ie not under our control.
*/
@@ -1490,8 +1490,9 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
mutex_unlock(&sisusb->lock);
/* Now grab the desired console(s) */
- ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
-
+ console_lock();
+ ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0);
+ console_unlock();
if (!ret)
sisusb->haveconsole = 1;
else {
@@ -1535,11 +1536,14 @@ sisusb_console_exit(struct sisusb_usb_data *sisusb)
if (sisusb->haveconsole) {
for (i = 0; i < MAX_NR_CONSOLES; i++)
- if (sisusb->havethisconsole[i])
- take_over_console(&sisusb_dummy_con, i, i, 0);
+ if (sisusb->havethisconsole[i]) {
+ console_lock();
+ do_take_over_console(&sisusb_dummy_con, i, i, 0);
+ console_unlock();
/* At this point, con_deinit for all our
- * consoles is executed by take_over_console().
+ * consoles is executed by do_take_over_console().
*/
+ }
sisusb->haveconsole = 0;
}
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index a92783e480e..d55b3375746 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -556,34 +556,6 @@ static int do_fbcon_takeover(int show_logo)
return err;
}
-static int fbcon_takeover(int show_logo)
-{
- int err, i;
-
- if (!num_registered_fb)
- return -ENODEV;
-
- if (!show_logo)
- logo_shown = FBCON_LOGO_DONTSHOW;
-
- for (i = first_fb_vc; i <= last_fb_vc; i++)
- con2fb_map[i] = info_idx;
-
- err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
- fbcon_is_default);
-
- if (err) {
- for (i = first_fb_vc; i <= last_fb_vc; i++) {
- con2fb_map[i] = -1;
- }
- info_idx = -1;
- } else {
- fbcon_has_console_bind = 1;
- }
-
- return err;
-}
-
#ifdef MODULE
static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
int cols, int rows, int new_cols, int new_rows)
@@ -901,7 +873,7 @@ static int set_con2fb_map(int unit, int newidx, int user)
/*
* Low Level Operations
*/
-/* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
+/* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
static int var_to_display(struct display *disp,
struct fb_var_screeninfo *var,
struct fb_info *info)
@@ -3543,8 +3515,9 @@ static void fbcon_start(void)
}
}
+ do_fbcon_takeover(0);
console_unlock();
- fbcon_takeover(0);
+
}
}
@@ -3648,8 +3621,8 @@ static void __exit fb_console_exit(void)
fbcon_deinit_device();
device_destroy(fb_class, MKDEV(0, 0));
fbcon_exit();
+ do_unregister_con_driver(&fb_con);
console_unlock();
- unregister_con_driver(&fb_con);
}
module_exit(fb_console_exit);
diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c
index 0b67866cae1..296e9456155 100644
--- a/drivers/video/console/mdacon.c
+++ b/drivers/video/console/mdacon.c
@@ -585,10 +585,14 @@ static const struct consw mda_con = {
int __init mda_console_init(void)
{
+ int err;
+
if (mda_first_vc > mda_last_vc)
return 1;
-
- return take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0);
+ console_lock();
+ err = do_take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0);
+ console_unlock();
+ return err;
}
static void __exit mda_console_exit(void)
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index b05afd03729..a6ab9299813 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -297,7 +297,7 @@ static void newport_exit(void)
newport_set_def_font(i, NULL);
}
-/* Can't be __init, take_over_console may call it later */
+/* Can't be __init, do_take_over_console may call it later */
static const char *newport_startup(void)
{
int i;
@@ -746,6 +746,7 @@ static int newport_probe(struct gio_device *dev,
const struct gio_device_id *id)
{
unsigned long newport_addr;
+ int err;
if (!dev->resource.start)
return -EINVAL;
@@ -759,8 +760,10 @@ static int newport_probe(struct gio_device *dev,
npregs = (struct newport_regs *)/* ioremap cannot fail */
ioremap(newport_addr, sizeof(struct newport_regs));
-
- return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
+ console_lock();
+ err = do_take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
+ console_unlock();
+ return err;
}
static void newport_remove(struct gio_device *dev)
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c
index 491c1c1baf4..5f65ca3d856 100644
--- a/drivers/video/console/sticon.c
+++ b/drivers/video/console/sticon.c
@@ -372,6 +372,7 @@ static const struct consw sti_con = {
static int __init sticonsole_init(void)
{
+ int err;
/* already initialized ? */
if (sticon_sti)
return 0;
@@ -382,7 +383,10 @@ static int __init sticonsole_init(void)
if (conswitchp == &dummy_con) {
printk(KERN_INFO "sticon: Initializing STI text console.\n");
- return take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1);
+ console_lock();
+ err = do_take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1, 1);
+ console_unlock();
+ return err;
}
return 0;
}