Commit a9724125 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver patches from Greg KH:
 "Here's the big tty/serial driver update for 3.20-rc1.  Nothing huge
  here, just lots of driver updates and some core tty layer fixes as
  well.  All have been in linux-next with no reported issues"

* tag 'tty-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (119 commits)
  serial: 8250: Fix UART_BUG_TXEN workaround
  serial: driver for ETRAX FS UART
  tty: remove unused variable sprop
  serial: of-serial: fetch line number from DT
  serial: samsung: earlycon support depends on CONFIG_SERIAL_SAMSUNG_CONSOLE
  tty/serial: serial8250_set_divisor() can be static
  tty/serial: Add Spreadtrum sc9836-uart driver support
  Documentation: DT: Add bindings for Spreadtrum SoC Platform
  serial: samsung: remove redundant interrupt enabling
  tty: Remove external interface for tty_set_termios()
  serial: omap: Fix RTS handling
  serial: 8250_omap: Use UPSTAT_AUTORTS for RTS handling
  serial: core: Rework hw-assisted flow control support
  tty/serial: 8250_early: Add support for PXA UARTs
  tty/serial: of_serial: add support for PXA/MMP uarts
  tty/serial: of_serial: add DT alias ID handling
  serial: 8250: Prevent concurrent updates to shadow registers
  serial: 8250: Use canary to restart console after suspend
  serial: 8250: Refactor XR17V35X divisor calculation
  serial: 8250: Refactor divisor programming
  ...
parents 46f7b635 c09babfa
Spreadtrum SoC Platforms Device Tree Bindings
----------------------------------------------------
Sharkl64 is a Spreadtrum's SoC Platform which is based
on ARM 64-bit processor.
SC9836 openphone board with SC9836 SoC based on the
Sharkl64 Platform shall have the following properties.
Required root node properties:
- compatible = "sprd,sc9836-openphone", "sprd,sc9836";
Binding for Conexant Digicolor USART
Note: this binding is only applicable for using the USART peripheral as
UART. USART also support synchronous serial protocols like SPI and I2S. Use
the binding that matches the wiring of your system.
Required properties:
- compatible : should be "cnxt,cx92755-usart".
- reg: Should contain USART controller registers location and length.
- interrupts: Should contain a single USART controller interrupt.
- clocks: Must contain phandles to the USART clock
See ../clocks/clock-bindings.txt for details.
Note: Each UART port should have an alias correctly numbered
in "aliases" node.
Example:
aliases {
serial0 = &uart0;
};
uart0: uart@f0000740 {
compatible = "cnxt,cx92755-usart";
reg = <0xf0000740 0x20>;
clocks = <&main_clk>;
interrupts = <44>;
};
......@@ -2,7 +2,7 @@
Required properties:
- compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart",
"sirf,marco-uart" or "sirf,marco-bt-uart" which means
"sirf,atlas7-uart" or "sirf,atlas7-bt-uart" which means
uart located in BT module and used for BT.
- reg : Offset and length of the register set for the device
- interrupts : Should contain uart interrupt
......@@ -37,7 +37,7 @@ usp@b0090000 {
for uart use in BT module,
uart6: uart@11000000 {
cell-index = <6>;
compatible = "sirf,marco-bt-uart", "sirf,marco-uart";
compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart";
reg = <0x11000000 0x1000>;
interrupts = <0 100 0>;
clocks = <&clks 138>, <&clks 140>, <&clks 141>;
......
* Spreadtrum serial UART
Required properties:
- compatible: must be "sprd,sc9836-uart"
- reg: offset and length of the register set for the device
- interrupts: exactly one interrupt specifier
- clocks: phandles to input clocks.
......@@ -37,6 +37,7 @@ chrp Common Hardware Reference Platform
chunghwa Chunghwa Picture Tubes Ltd.
cirrus Cirrus Logic, Inc.
cnm Chips&Media, Inc.
cnxt Conexant Systems, Inc.
cortina Cortina Systems, Inc.
cosmic Cosmic Circuits
crystalfontz Crystalfontz America, Inc.
......@@ -162,6 +163,7 @@ snps Synopsys, Inc.
solidrun SolidRun
sony Sony Corporation
spansion Spansion Inc.
sprd Spreadtrum Communications Inc.
st STMicroelectronics
ste ST-Ericsson
stericsson ST-Ericsson
......
......@@ -970,6 +970,18 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
smh Use ARM semihosting calls for early console.
s3c2410,<addr>
s3c2412,<addr>
s3c2440,<addr>
s3c6400,<addr>
s5pv210,<addr>
exynos4210,<addr>
Use early console provided by serial driver available
on Samsung SoCs, requires selecting proper type and
a correct base address of the selected UART port. The
serial port must already be setup and configured.
Options are not yet supported.
earlyprintk= [X86,SH,BLACKFIN,ARM,M68k]
earlyprintk=vga
earlyprintk=efi
......
......@@ -31,6 +31,7 @@ memory {
chosen {
bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
stdout-path = &serial_2;
};
regulators {
......
......@@ -27,6 +27,7 @@ memory {
chosen {
bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
stdout-path = &serial_1;
};
sdhci@12530000 {
......
......@@ -28,6 +28,7 @@ memory {
chosen {
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
stdout-path = &serial_2;
};
regulators {
......
......@@ -26,6 +26,7 @@ memory {
chosen {
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
stdout-path = &serial_2;
};
sysram@02020000 {
......
......@@ -12,6 +12,10 @@
#include "exynos4412.dtsi"
/ {
chosen {
stdout-path = &serial_1;
};
firmware@0204F000 {
compatible = "samsung,secure-firmware";
reg = <0x0204F000 0x1000>;
......
......@@ -26,6 +26,7 @@ memory {
chosen {
bootargs ="console=ttySAC2,115200";
stdout-path = &serial_2;
};
firmware@0203F000 {
......
......@@ -25,6 +25,7 @@ memory {
chosen {
bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
stdout-path = &serial_1;
};
g2d@10800000 {
......
......@@ -18,6 +18,10 @@ / {
model = "FriendlyARM TINY4412 board based on Exynos4412";
compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
chosen {
stdout-path = &serial_0;
};
memory {
reg = <0x40000000 0x40000000>;
};
......
......@@ -32,6 +32,7 @@ memory {
chosen {
bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
stdout-path = &serial_2;
};
firmware@0204F000 {
......
......@@ -136,9 +136,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
#define SFI_MTMR_MAX_NUM 8
#define SFI_MRTC_MAX 8
extern struct console early_mrst_console;
extern void mrst_early_console_init(void);
extern struct console early_hsu_console;
extern void hsu_early_console_init(const char *);
......
......@@ -19,6 +19,7 @@
#include <linux/usb/ehci_def.h>
#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/pci_x86.h>
/* Simple VGA output */
#define VGABASE (__ISA_IO_base + 0xb8000)
......@@ -76,7 +77,7 @@ static struct console early_vga_console = {
/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
static int early_serial_base = 0x3f8; /* ttyS0 */
static unsigned long early_serial_base = 0x3f8; /* ttyS0 */
#define XMTRDY 0x20
......@@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8; /* ttyS0 */
#define DLL 0 /* Divisor Latch Low */
#define DLH 1 /* Divisor latch High */
static void mem32_serial_out(unsigned long addr, int offset, int value)
{
uint32_t *vaddr = (uint32_t *)addr;
/* shift implied by pointer type */
writel(value, vaddr + offset);
}
static unsigned int mem32_serial_in(unsigned long addr, int offset)
{
uint32_t *vaddr = (uint32_t *)addr;
/* shift implied by pointer type */
return readl(vaddr + offset);
}
static unsigned int io_serial_in(unsigned long addr, int offset)
{
return inb(addr + offset);
}
static void io_serial_out(unsigned long addr, int offset, int value)
{
outb(value, addr + offset);
}
static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in;
static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out;
static int early_serial_putc(unsigned char ch)
{
unsigned timeout = 0xffff;
while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
cpu_relax();
outb(ch, early_serial_base + TXR);
serial_out(early_serial_base, TXR, ch);
return timeout ? 0 : -1;
}
......@@ -114,13 +142,28 @@ static void early_serial_write(struct console *con, const char *s, unsigned n)
}
}
static __init void early_serial_hw_init(unsigned divisor)
{
unsigned char c;
serial_out(early_serial_base, LCR, 0x3); /* 8n1 */
serial_out(early_serial_base, IER, 0); /* no interrupt */
serial_out(early_serial_base, FCR, 0); /* no fifo */
serial_out(early_serial_base, MCR, 0x3); /* DTR + RTS */
c = serial_in(early_serial_base, LCR);
serial_out(early_serial_base, LCR, c | DLAB);
serial_out(early_serial_base, DLL, divisor & 0xff);
serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
serial_out(early_serial_base, LCR, c & ~DLAB);
}
#define DEFAULT_BAUD 9600
static __init void early_serial_init(char *s)
{
unsigned char c;
unsigned divisor;
unsigned baud = DEFAULT_BAUD;
unsigned long baud = DEFAULT_BAUD;
char *e;
if (*s == ',')
......@@ -145,24 +188,124 @@ static __init void early_serial_init(char *s)
s++;
}
outb(0x3, early_serial_base + LCR); /* 8n1 */
outb(0, early_serial_base + IER); /* no interrupt */
outb(0, early_serial_base + FCR); /* no fifo */
outb(0x3, early_serial_base + MCR); /* DTR + RTS */
if (*s) {
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
baud = DEFAULT_BAUD;
}
/* Convert from baud to divisor value */
divisor = 115200 / baud;
/* These will always be IO based ports */
serial_in = io_serial_in;
serial_out = io_serial_out;
/* Set up the HW */
early_serial_hw_init(divisor);
}
#ifdef CONFIG_PCI
/*
* early_pci_serial_init()
*
* This function is invoked when the early_printk param starts with "pciserial"
* The rest of the param should be ",B:D.F,baud" where B, D & F describe the
* location of a PCI device that must be a UART device.
*/
static __init void early_pci_serial_init(char *s)
{
unsigned divisor;
unsigned long baud = DEFAULT_BAUD;
u8 bus, slot, func;
uint32_t classcode, bar0;
uint16_t cmdreg;
char *e;
/*
* First, part the param to get the BDF values
*/
if (*s == ',')
++s;
if (*s == 0)
return;
bus = (u8)simple_strtoul(s, &e, 16);
s = e;
if (*s != ':')
return;
++s;
slot = (u8)simple_strtoul(s, &e, 16);
s = e;
if (*s != '.')
return;
++s;
func = (u8)simple_strtoul(s, &e, 16);
s = e;
/* A baud might be following */
if (*s == ',')
s++;
/*
* Second, find the device from the BDF
*/
cmdreg = read_pci_config(bus, slot, func, PCI_COMMAND);
classcode = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
bar0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
/*
* Verify it is a UART type device
*/
if (((classcode >> 16 != PCI_CLASS_COMMUNICATION_MODEM) &&
(classcode >> 16 != PCI_CLASS_COMMUNICATION_SERIAL)) ||
(((classcode >> 8) & 0xff) != 0x02)) /* 16550 I/F at BAR0 */
return;
/*
* Determine if it is IO or memory mapped
*/
if (bar0 & 0x01) {
/* it is IO mapped */
serial_in = io_serial_in;
serial_out = io_serial_out;
early_serial_base = bar0&0xfffffffc;
write_pci_config(bus, slot, func, PCI_COMMAND,
cmdreg|PCI_COMMAND_IO);
} else {
/* It is memory mapped - assume 32-bit alignment */
serial_in = mem32_serial_in;
serial_out = mem32_serial_out;
/* WARNING! assuming the address is always in the first 4G */
early_serial_base =
(unsigned long)early_ioremap(bar0 & 0xfffffff0, 0x10);
write_pci_config(bus, slot, func, PCI_COMMAND,
cmdreg|PCI_COMMAND_MEMORY);
}
/*
* Lastly, initalize the hardware
*/
if (*s) {
baud = simple_strtoul(s, &e, 0);
if (baud == 0 || s == e)
if (strcmp(s, "nocfg") == 0)
/* Sometimes, we want to leave the UART alone
* and assume the BIOS has set it up correctly.
* "nocfg" tells us this is the case, and we
* should do no more setup.
*/
return;
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
baud = DEFAULT_BAUD;
}
/* Convert from baud to divisor value */
divisor = 115200 / baud;
c = inb(early_serial_base + LCR);
outb(c | DLAB, early_serial_base + LCR);
outb(divisor & 0xff, early_serial_base + DLL);
outb((divisor >> 8) & 0xff, early_serial_base + DLH);
outb(c & ~DLAB, early_serial_base + LCR);
/* Set up the HW */
early_serial_hw_init(divisor);
}
#endif
static struct console early_serial_console = {
.name = "earlyser",
......@@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
early_serial_init(buf + 4);
early_console_register(&early_serial_console, keep);
}
#ifdef CONFIG_PCI
if (!strncmp(buf, "pciserial", 9)) {
early_pci_serial_init(buf + 9);
early_console_register(&early_serial_console, keep);
buf += 9; /* Keep from match the above "serial" */
}
#endif
if (!strncmp(buf, "vga", 3) &&
boot_params.screen_info.orig_video_isVGA == 1) {
max_xpos = boot_params.screen_info.orig_video_cols;
......@@ -226,11 +376,6 @@ static int __init setup_early_printk(char *buf)
early_console_register(&xenboot_console, keep);
#endif
#ifdef CONFIG_EARLY_PRINTK_INTEL_MID
if (!strncmp(buf, "mrst", 4)) {
mrst_early_console_init();
early_console_register(&early_mrst_console, keep);
}
if (!strncmp(buf, "hsu", 3)) {
hsu_early_console_init(buf + 3);
early_console_register(&early_hsu_console, keep);
......
......@@ -16,8 +16,6 @@ obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o
obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o
obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o
# SPI Devices
obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o
# MISC Devices
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o
/*
* platform_max3111.c: max3111 platform data initilization file
*
* (C) Copyright 2013 Intel Corporation
* Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com>
*
* 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; version 2
* of the License.
*/
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <asm/intel-mid.h>
static void __init *max3111_platform_data(void *info)
{
struct spi_board_info *spi_info = info;
int intr = get_gpio_by_name("max3111_int");
spi_info->mode = SPI_MODE_0;
if (intr == -1)
return NULL;
spi_info->irq = intr + INTEL_MID_IRQ_OFFSET;
return NULL;
}
static const struct devs_id max3111_dev_id __initconst = {
.name = "spi_max3111",
.type = SFI_DEV_TYPE_SPI,
.get_platform_data = &max3111_platform_data,
};
sfi_device(max3111_dev_id);
......@@ -10,15 +10,13 @@
*/
/*
* This file implements two early consoles named mrst and hsu.
* mrst is based on Maxim3110 spi-uart device, it exists in both
* Moorestown and Medfield platforms, while hsu is based on a High
* Speed UART device which only exists in the Medfield platform
* This file implements early console named hsu.
* hsu is based on a High Speed UART device which only exists in the Medfield
* platform
*/
#include <linux/serial_reg.h>
#include <linux/serial_mfd.h>
#include <linux/kmsg_dump.h>
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/delay.h>
......@@ -28,216 +26,6 @@
#include <asm/pgtable.h>
#include <asm/intel-mid.h>
#define MRST_SPI_TIMEOUT 0x200000
#define MRST_REGBASE_SPI0 0xff128000
#define MRST_REGBASE_SPI1 0xff128400
#define MRST_CLK_SPI0_REG 0xff11d86c
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
#define SPI_FRF_OFFSET 4
#define SPI_FRF_SPI 0x0
#define SPI_FRF_SSP 0x1
#define SPI_FRF_MICROWIRE 0x2
#define SPI_FRF_RESV 0x3
#define SPI_MODE_OFFSET 6
#define SPI_SCPH_OFFSET 6
#define SPI_SCOL_OFFSET 7
#define SPI_TMOD_OFFSET 8
#define SPI_TMOD_TR 0x0 /* xmit & recv */
#define SPI_TMOD_TO 0x1 /* xmit only */
#define SPI_TMOD_RO 0x2 /* recv only */
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
#define SPI_SLVOE_OFFSET 10
#define SPI_SRL_OFFSET 11
#define SPI_CFS_OFFSET 12
/* Bit fields in SR, 7 bits */
#define SR_MASK 0x7f /* cover 7 bits */
#define SR_BUSY (1 << 0)
#define SR_TF_NOT_FULL (1 << 1)
#define SR_TF_EMPT (1 << 2)
#define SR_RF_NOT_EMPT (1 << 3)
#define SR_RF_FULL (1 << 4)
#define SR_TX_ERR (1 << 5)
#define SR_DCOL (1 << 6)
struct dw_spi_reg {
u32 ctrl0;
u32 ctrl1;
u32 ssienr;
u32 mwcr;
u32 ser;
u32 baudr;
u32 txfltr;
u32 rxfltr;
u32 txflr;
u32 rxflr;
u32 sr;
u32 imr;
u32 isr;
u32 risr;
u32 txoicr;
u32 rxoicr;
u32 rxuicr;
u32 msticr;
u32 icr;
u32 dmacr;
u32 dmatdlr;
u32 dmardlr;
u32 idr;
u32 version;
/* Currently operates as 32 bits, though only the low 16 bits matter */
u32 dr;
} __packed;
#define dw_readl(dw, name) __raw_readl(&(dw)->name)
#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name)
/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */
static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0;
static u32 *pclk_spi0;
/* Always contains an accessible address, start with 0 */
static struct dw_spi_reg *pspi;
static struct kmsg_dumper dw_dumper;
static int dumper_registered;
static void dw_kmsg_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason)
{
static char line[1024];
size_t len;
/* When run to this, we'd better re-init the HW */
mrst_early_console_init();
while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
early_mrst_console.write(&early_mrst_console, line, len);
}
/* Set the ratio rate to 115200, 8n1, IRQ disabled */
static void max3110_write_config(void)
{
u16 config;
config = 0xc001;
dw_writel(pspi, dr, config);
}
/* Translate char to a eligible word and send to max3110 */
static void max3110_write_data(char c)
{
u16 data;
data = 0x8000 | c;
dw_writel(pspi, dr, data);
}
void mrst_early_console_init(void)
{
u32 ctrlr0 = 0;
u32 spi0_cdiv;
u32 freq; /* Freqency info only need be searched once */
/* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */
pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
MRST_CLK_SPI0_REG);
spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9;
freq = 100000000 / (spi0_cdiv + 1);
if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL)
mrst_spi_paddr = MRST_REGBASE_SPI1;
pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE,
mrst_spi_paddr);
/* Disable SPI controller */
dw_writel(pspi, ssienr, 0);
/* Set control param, 8 bits, transmit only mode */
ctrlr0 = dw_readl(pspi, ctrl0);
ctrlr0 &= 0xfcc0;
ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET)
| (SPI_TMOD_TO << SPI_TMOD_OFFSET);
dw_writel(pspi, ctrl0, ctrlr0);
/*
* Change the spi0 clk to comply with 115200 bps, use 100000 to
* calculate the clk dividor to make the clock a little slower
* than real baud rate.
*/
dw_writel(pspi, baudr, freq/100000);
/* Disable all INT for early phase */
dw_writel(pspi, imr, 0x0);
/* Set the cs to spi-uart */
dw_writel(pspi, ser, 0x2);
/* Enable the HW, the last step for HW init */
dw_writel(pspi, ssienr, 0x1);
/* Set the default configuration */
max3110_write_config();
/* Register the kmsg dumper */
if (!dumper_registered) {
dw_dumper.dump = dw_kmsg_dump;
kmsg_dump_register(&dw_dumper);
dumper_registered = 1;
}
}
/* Slave select should be called in the read/write function */
static void early_mrst_spi_putc(char c)
{
unsigned int timeout;
u32 sr;
timeout = MRST_SPI_TIMEOUT;
/* Early putc needs to make sure the TX FIFO is not full */
while (--timeout) {
sr = dw_readl(pspi, sr);
if (!(sr & SR_TF_NOT_FULL))
cpu_relax();
else
break;
}
if (!timeout)
pr_warn("MRST earlycon: timed out\n");
else
max3110_write_data(c);
}
/* Early SPI only uses polling mode */
static void early_mrst_spi_write(struct console *con, const char *str,
unsigned n)
{
int i;
for (i = 0; i < n && *str; i++) {
if (*str == '\n')
early_mrst_spi_putc('\r');
early_mrst_spi_putc(*str);
str++;
}
}
struct console early_mrst_console = {
.name = "earlymrst",
.write = early_mrst_spi_write,
.flags = CON_PRINTBUFFER,
.index = -1,
};
/*
* Following is the early console based on Medfield HSU (High
* Speed UART) device.
......@@ -259,7 +47,7 @@ void hsu_early_console_init(const char *s)
port = clamp_val(port, 0, 2);
paddr = HSU_PORT_BASE + port * 0x80;
phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
phsu = (void __iomem *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr);
/* Disable FIFO */
writeb(0x0, phsu + UART_FCR);
......
......@@ -51,33 +51,22 @@ struct ath_struct {
static int ath_wakeup_ar3k(struct tty_struct *tty)
{
struct ktermios ktermios;
int status = tty->driver->ops->tiocmget(tty);
if (status & TIOCM_CTS)
return status;
/* Disable Automatic RTSCTS */
ktermios = tty->termios;
ktermios.c_cflag &= ~CRTSCTS;
tty_set_termios(tty, &ktermios);
/* Clear RTS first */
status = tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
mdelay(20);
/* Set RTS, wake up board */
status = tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
mdelay(20);
status = tty->driver->ops->tiocmget(tty);
/* Enable Automatic RTSCTS */
ktermios.c_cflag |= CRTSCTS;
status = tty_set_termios(tty, &ktermios);
return status;
}
......
......@@ -182,7 +182,7 @@ static int __pnp_bus_suspend(struct device *dev, pm_message_t state)
return error;
}
if (pnp_dev->protocol->suspend)
if (pnp_can_suspend(pnp_dev))
pnp_dev->protocol->suspend(pnp_dev, state);
return 0;
}
......
......@@ -931,7 +931,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
struct serial_state *info = tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->name, "rs_send_char"))
if (serial_paranoia_check(info, tty->name, "rs_send_xchar"))
return;
info->x_char = ch;
......
......@@ -112,7 +112,6 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
static int find_console_handle(void)
{
struct device_node *np = of_stdout;
const char *sprop = NULL;
const uint32_t *iprop;
/* We don't care what the aliased node is actually called. We only
......
......@@ -1055,7 +1055,7 @@ static int isicom_send_break(struct tty_struct *tty, int length)
outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
outw((length & 0xff) << 8 | 0x00, base);
outw((length & 0xff00), base);
outw((length & 0xff00u), base);
InterruptTheCard(base);
unlock_card(card);
......
This diff is collapsed.
......@@ -87,19 +87,6 @@ static void pty_unthrottle(struct tty_struct *tty)
set_bit(TTY_THROTTLED, &tty->flags);
}
/**
* pty_space - report space left for writing
* @to: tty we are writing into
*
* Limit the buffer space used by ptys to 8k.
*/
static int pty_space(struct tty_struct *to)
{
int n = tty_buffer_space_avail(to->port);
return min(n, 8192);
}
/**
* pty_write - write to a pty
* @tty: the tty we write from
......@@ -141,7 +128,7 @@ static int pty_write_room(struct tty_struct *tty)
{
if (tty->stopped)
return 0;
return pty_space(tty->link);
return tty_buffer_space_avail(tty->link->port);
}
/**
......@@ -210,6 +197,9 @@ static int pty_signal(struct tty_struct *tty, int sig)
{
struct pid *pgrp;
if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
return -EINVAL;
if (tty->link) {
pgrp = tty_get_pgrp(tty->link);
if (pgrp)
......@@ -222,10 +212,16 @@ static int pty_signal(struct tty_struct *tty, int sig)
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
struct tty_ldisc *ld;
if (!to)
return;
/* tty_buffer_flush(to); FIXME */
ld = tty_ldisc_ref(to);
tty_buffer_flush(to, ld);
if (ld)
tty_ldisc_deref(ld);
if (to->packet) {
spin_lock_irq(&tty->ctrl_lock);
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
......@@ -399,6 +395,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
goto err_put_module;
tty_set_lock_subclass(o_tty);
lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
if (legacy) {
/* We always use new tty termios data so we can do this
......@@ -429,10 +426,14 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
o_tty->link = tty;
tty_port_init(ports[0]);
tty_port_init(ports[1]);
tty_buffer_set_limit(ports[0], 8192);
tty_buffer_set_limit(ports[1], 8192);
o_tty->port = ports[0];
tty->port = ports[1];
o_tty->port->itty = o_tty;
tty_buffer_set_lock_subclass(o_tty->port);
tty_driver_kref_get(driver);
tty->count++;
o_tty->count++;
......
......@@ -1390,7 +1390,7 @@ static void rp_unthrottle(struct tty_struct *tty)
tty->ldisc.chars_in_buffer(tty));
#endif
if (rocket_paranoia_check(info, "rp_throttle"))
if (rocket_paranoia_check(info, "rp_unthrottle"))
return;
if (I_IXOFF(tty))
......@@ -1458,7 +1458,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
orig_jiffies = jiffies;
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout,
jiffies);
printk(KERN_INFO "cps=%d...\n", info->cps);
#endif
......
This diff is collapsed.
......@@ -59,7 +59,6 @@ static void __dma_rx_complete(void *param)
dma->rx_running = 0;
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
dmaengine_terminate_all(dma->rxchan);
count = dma->rx_size - state.residue;
......@@ -81,6 +80,10 @@ int serial8250_tx_dma(struct uart_8250_port *p)
return 0;
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (dma->tx_size < p->port.fifosize) {
ret = -EINVAL;
goto err;
}
desc = dmaengine_prep_slave_single(dma->txchan,
dma->tx_addr + xmit->tail,
......@@ -131,6 +134,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
if (dma->rx_running) {
dmaengine_pause(dma->rxchan);
__dma_rx_complete(p);
dmaengine_terminate_all(dma->rxchan);
}
return -ETIMEDOUT;
default:
......
......@@ -351,10 +351,20 @@ static int dw8250_probe_of(struct uart_port *p,
static int dw8250_probe_acpi(struct uart_8250_port *up,
struct dw8250_data *data)
{
const struct acpi_device_id *id;
struct uart_port *p = &up->port;
dw8250_setup_port(up);
id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
if (!id)
return -ENODEV;
if (!p->uartclk)
if (device_property_read_u32(p->dev, "clock-frequency",
&p->uartclk))
return -EINVAL;
p->iotype = UPIO_MEM32;
p->serial_in = dw8250_serial_in32;
p->serial_out = dw8250_serial_out32;
......@@ -577,6 +587,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
{ "INT3435", 0 },
{ "80860F0A", 0 },
{ "8086228A", 0 },
{ "APMC0D08", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
......
......@@ -93,15 +93,18 @@ static void __init early_serial8250_write(struct console *console,
struct uart_port *port = &early_device->port;
unsigned int ier;
/* Save the IER and disable interrupts */
/* Save the IER and disable interrupts preserving the UUE bit */
ier = serial8250_early_in(port, UART_IER);
serial8250_early_out(port, UART_IER, 0);
if (ier)
serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
uart_console_write(port, s, count, serial_putc);
/* Wait for transmitter to become empty and restore the IER */
wait_for_xmitr(port);
serial8250_early_out(port, UART_IER, ier);
if (ier)
serial8250_early_out(port, UART_IER, ier);
}
static unsigned int __init probe_baud(struct uart_port *port)
......@@ -124,9 +127,11 @@ static void __init init_port(struct earlycon_device *device)
struct uart_port *port = &device->port;
unsigned int divisor;
unsigned char c;
unsigned int ier;
serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */
serial8250_early_out(port, UART_IER, 0); /* no interrupt */
ier = serial8250_early_in(port, UART_IER);
serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
serial8250_early_out(port, UART_FCR, 0); /* no fifo */
serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */
......
......@@ -106,6 +106,28 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg)
return readl(up->port.membase + (reg << up->port.regshift));
}
static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = up->port.private_data;
u8 lcr;
serial8250_do_set_mctrl(port, mctrl);
/*
* Turn off autoRTS if RTS is lowered and restore autoRTS setting
* if RTS is raised
*/
lcr = serial_in(up, UART_LCR);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
priv->efr |= UART_EFR_RTS;
else
priv->efr &= ~UART_EFR_RTS;
serial_out(up, UART_EFR, priv->efr);
serial_out(up, UART_LCR, lcr);
}
/*
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
* The access to uart register after MDR1 Access
......@@ -397,12 +419,12 @@ static void omap_8250_set_termios(struct uart_port *port,
priv->efr = 0;
up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTORTS and AUTOCTS */
priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
/* Ensure MCR RTS is asserted */
up->mcr |= UART_MCR_RTS;
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
priv->efr |= UART_EFR_CTS;
} else if (up->port.flags & UPF_SOFT_FLOW) {
/*
* IXON Flag:
......@@ -417,8 +439,10 @@ static void omap_8250_set_termios(struct uart_port *port,
* Enable XON/XOFF flow control on output.
* Transmit XON1, XOFF1
*/
if (termios->c_iflag & IXOFF)
if (termios->c_iflag & IXOFF) {
up->port.status |= UPSTAT_AUTOXOFF;
priv->efr |= OMAP_UART_SW_TX;
}
/*
* IXANY Flag:
......@@ -450,18 +474,18 @@ static void omap_8250_set_termios(struct uart_port *port,
static void omap_8250_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
struct omap8250_priv *priv = up->port.private_data;
struct uart_8250_port *up = up_to_u8250p(port);
u8 efr;
pm_runtime_get_sync(port->dev);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB);
efr = serial_in(up, UART_EFR);
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
serial_out(up, UART_LCR, 0);
serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, priv->efr);
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
pm_runtime_mark_last_busy(port->dev);
......@@ -1007,6 +1031,7 @@ static int omap8250_probe(struct platform_device *pdev)
up.capabilities |= UART_CAP_RPM;
#endif
up.port.set_termios = omap_8250_set_termios;
up.port.set_mctrl = omap8250_set_mctrl;
up.port.pm = omap_8250_pm;
up.port.startup = omap_8250_startup;
up.port.shutdown = omap_8250_shutdown;
......@@ -1248,6 +1273,46 @@ static int omap8250_runtime_resume(struct device *dev)
}
#endif
#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
static int __init omap8250_console_fixup(void)
{
char *omap_str;
char *options;
u8 idx;
if (strstr(boot_command_line, "console=ttyS"))
/* user set a ttyS based name for the console */
return 0;
omap_str = strstr(boot_command_line, "console=ttyO");
if (!omap_str)
/* user did not set ttyO based console, so we don't care */
return 0;
omap_str += 12;
if ('0' <= *omap_str && *omap_str <= '9')
idx = *omap_str - '0';
else
return 0;
omap_str++;
if (omap_str[0] == ',') {
omap_str++;
options = omap_str;
} else {
options = NULL;
}
add_preferred_console("ttyS", idx, options);
pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n",
idx, idx);
pr_err("This ensures that you still see kernel messages. Please\n");
pr_err("update your kernel commandline.\n");
return 0;
}
console_initcall(omap8250_console_fixup);
#endif
static const struct dev_pm_ops omap8250_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
......@@ -1269,7 +1334,6 @@ static struct platform_driver omap8250_platform_driver = {
.name = "omap8250",
.pm = &omap8250_dev_pm_ops,
.of_match_table = omap8250_dt_ids,
.owner = THIS_MODULE,
},
.probe = omap8250_probe,
.remove = omap8250_remove,
......
......@@ -221,13 +221,13 @@ pci_hp_diva_setup(struct serial_private *priv,
*/
static int pci_inteli960ni_init(struct pci_dev *dev)
{
unsigned long oldval;
u32 oldval;
if (!(dev->subsystem_device & 0x1000))
return -ENODEV;
/* is firmware started? */
pci_read_config_dword(dev, 0x44, (void *)&oldval);
pci_read_config_dword(dev, 0x44, &oldval);
if (oldval == 0x00001000L) { /* RESET value */
dev_dbg(&dev->dev, "Local i960 firmware missing\n");
return -ENODEV;
......
......@@ -426,7 +426,7 @@ static int serial_pnp_guess_board(struct pnp_dev *dev)
static int
serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
struct uart_8250_port uart;
struct uart_8250_port uart, *port;
int ret, line, flags = dev_id->driver_data;
if (flags & UNKNOWN_DEV) {
......@@ -471,6 +471,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
if (line < 0 || (flags & CIR_PORT))
return -ENODEV;
port = serial8250_get_port(line);
if (uart_console(&port->port))
dev->capabilities |= PNP_CONSOLE;
pnp_set_drvdata(dev, (void *)((long)line + 1));
return 0;
}
......@@ -478,6 +482,8 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
static void serial_pnp_remove(struct pnp_dev *dev)
{
long line = (long)pnp_get_drvdata(dev);
dev->capabilities &= ~PNP_CONSOLE;
if (line)
serial8250_unregister_port(line - 1);
}
......
......@@ -308,6 +308,25 @@ config SERIAL_8250_OMAP
This driver uses ttyS instead of ttyO.
config SERIAL_8250_OMAP_TTYO_FIXUP
bool "Replace ttyO with ttyS"
depends on SERIAL_8250_OMAP=y && SERIAL_8250_CONSOLE
default y
help
This option replaces the "console=ttyO" argument with the matching
ttyS argument if the user did not specified it on the command line.
This ensures that the user can see the kernel output during boot
which he wouldn't see otherwise. The getty has still to be configured
for ttyS instead of ttyO regardless of this option.
This option is intended for people who "automatically" enable this
driver without knowing that this driver requires a different console=
argument. If you read this, please keep this option disabled and
instead update your kernel command line. If you prepare a kernel for a
distribution or other kind of larger user base then you probably want
to keep this option enabled. Otherwise people might complain about a
not booting kernel because the serial console remains silent in case
they forgot to update the command line.
config SERIAL_8250_FINTEK
tristate "Support for Fintek F81216A LPC to 4 UART"
depends on SERIAL_8250 && PNP
......
......@@ -241,6 +241,7 @@ config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
depends on PLAT_SAMSUNG || ARCH_EXYNOS
select SERIAL_CORE
select SERIAL_EARLYCON
help
Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
providing /dev/ttySAC0, 1 and 2 (note, some machines may not
......@@ -482,16 +483,6 @@ config SERIAL_SA1100_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
config SERIAL_MRST_MAX3110
tristate "SPI UART driver for Max3110"
depends on SPI_DW_PCI
select SERIAL_CORE
select SERIAL_CORE_CONSOLE
help
This is the UART protocol driver for the MAX3110 device on
the Intel Moorestown platform. On other systems use the max3100
driver.
config SERIAL_MFD_HSU
tristate "Medfield High Speed UART support"
depends on PCI
......@@ -1094,6 +1085,16 @@ config SERIAL_VT8500_CONSOLE
depends on SERIAL_VT8500=y
select SERIAL_CORE_CONSOLE
config SERIAL_ETRAXFS
bool "ETRAX FS serial port support"
depends on ETRAX_ARCH_V32 && OF
select SERIAL_CORE
config SERIAL_ETRAXFS_CONSOLE
bool "ETRAX FS serial console support"
depends on SERIAL_ETRAXFS
select SERIAL_CORE_CONSOLE
config SERIAL_NETX
tristate "NetX serial port support"
depends on ARCH_NETX
......@@ -1549,6 +1550,21 @@ config SERIAL_FSL_LPUART_CONSOLE
If you have enabled the lpuart serial port on the Freescale SoCs,
you can make it the console by answering Y to this option.
config SERIAL_CONEXANT_DIGICOLOR
tristate "Conexant Digicolor CX92xxx USART serial port support"
depends on OF
select SERIAL_CORE
help
Support for the on-chip USART on Conexant Digicolor SoCs.
config SERIAL_CONEXANT_DIGICOLOR_CONSOLE
bool "Console on Conexant Digicolor serial port"
depends on SERIAL_CONEXANT_DIGICOLOR=y
select SERIAL_CORE_CONSOLE
help
If you have enabled the USART serial port on Conexant Digicolor
SoCs, you can make it the console by answering Y to this option.
config SERIAL_ST_ASC
tristate "ST ASC serial port support"
select SERIAL_CORE
......@@ -1577,6 +1593,24 @@ config SERIAL_MEN_Z135
This driver can also be build as a module. If so, the module will be called
men_z135_uart.ko
config SERIAL_SPRD
tristate "Support for Spreadtrum serial"
depends on ARCH_SPRD
select SERIAL_CORE
help
This enables the driver for the Spreadtrum's serial.
config SERIAL_SPRD_CONSOLE
bool "Spreadtrum UART console support"
depends on SERIAL_SPRD=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Support for early debug console using Spreadtrum's serial. This enables
the console before standard serial driver is probed. This is enabled
with "earlycon" on the kernel command line. The console is
enabled when early_param is processed.
endmenu
config SERIAL_MCTRL_GPIO
......
......@@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o
obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
obj-$(CONFIG_SERIAL_JSM) += jsm/
......@@ -77,7 +78,6 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
......@@ -92,7 +92,9 @@ 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
obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
......@@ -441,6 +441,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
port->iotype = SERIAL_IO_MEM;
port->ops = &altera_jtaguart_ops;
port->flags = UPF_BOOT_AUTOCONF;
port->dev = &pdev->dev;
uart_add_one_port(&altera_jtaguart_driver, port);
......
......@@ -589,6 +589,7 @@ static int altera_uart_probe(struct platform_device *pdev)
port->iotype = SERIAL_IO_MEM;
port->ops = &altera_uart_ops;
port->flags = UPF_BOOT_AUTOCONF;
port->dev = &pdev->dev;
platform_set_drvdata(pdev, port);
......
......@@ -341,13 +341,37 @@ static u_int atmel_tx_empty(struct uart_port *port)
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
unsigned int mode;
unsigned int mode = UART_GET_MR(port);
unsigned int rts_paused, rts_ready;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* override mode to RS485 if needed, otherwise keep the current mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
if ((port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
mode &= ~ATMEL_US_USMODE;
mode |= ATMEL_US_USMODE_RS485;
}
/* set the RTS line state according to the mode */
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
/* force RTS line to high level */
rts_paused = ATMEL_US_RTSEN;
/* give the control of the RTS line back to the hardware */
rts_ready = ATMEL_US_RTSDIS;
} else {
/* force RTS line to high level */
rts_paused = ATMEL_US_RTSDIS;
/* force RTS line to low level */
rts_ready = ATMEL_US_RTSEN;
}
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
control |= rts_ready;
else
control |= ATMEL_US_RTSDIS;
control |= rts_paused;
if (mctrl & TIOCM_DTR)
control |= ATMEL_US_DTREN;
......@@ -359,23 +383,12 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mctrl_gpio_set(atmel_port->gpios, mctrl);
/* Local loopback mode? */
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
mode &= ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
/* Resetting serial mode to RS232 (0x0) */
mode &= ~ATMEL_US_USMODE;
if (port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
if ((port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
}
UART_PUT_MR(port, mode);
}
......@@ -725,7 +738,11 @@ static void atmel_complete_tx_dma(void *arg)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
/* Do we really need this? */
/*
* xmit is a circular buffer so, if we have just send data from
* xmit->tail to the end of xmit->buf, now we have to transmit the
* remaining data from the beginning of xmit->buf to xmit->head.
*/
if (!uart_circ_empty(xmit))
tasklet_schedule(&atmel_port->tasklet);
......@@ -784,17 +801,17 @@ static void atmel_tx_dma(struct uart_port *port)
BUG_ON(!sg_dma_len(sg));
desc = dmaengine_prep_slave_sg(chan,
sg,
1,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
sg,
1,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
if (!desc) {
dev_err(port->dev, "Failed to send via dma!\n");
return;
}
dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
atmel_port->desc_tx = desc;
desc->callback = atmel_complete_tx_dma;
......@@ -927,7 +944,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
dma_sync_sg_for_cpu(port->dev,
&atmel_port->sg_rx,
1,
DMA_DEV_TO_MEM);
DMA_FROM_DEVICE);
/*
* ring->head points to the end of data already written by the DMA.
......@@ -974,7 +991,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
dma_sync_sg_for_device(port->dev,
&atmel_port->sg_rx,
1,
DMA_DEV_TO_MEM);
DMA_FROM_DEVICE);
/*
* Drop the lock here since it might end up calling
......@@ -1012,13 +1029,13 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
/* UART circular rx buffer is an aligned page. */
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
sg_set_page(&atmel_port->sg_rx,
virt_to_page(ring->buf),
ATMEL_SERIAL_RINGSIZE,
(int)ring->buf & ~PAGE_MASK);
nent = dma_map_sg(port->dev,
&atmel_port->sg_rx,
1,
DMA_FROM_DEVICE);
virt_to_page(ring->buf),
ATMEL_SERIAL_RINGSIZE,
(int)ring->buf & ~PAGE_MASK);
nent = dma_map_sg(port->dev,
&atmel_port->sg_rx,
1,
DMA_FROM_DEVICE);
if (!nent) {
dev_dbg(port->dev, "need to release resource of dma\n");
......@@ -1047,11 +1064,11 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
* each one is half ring buffer size
*/
desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
sg_dma_address(&atmel_port->sg_rx),
sg_dma_len(&atmel_port->sg_rx),
sg_dma_len(&atmel_port->sg_rx)/2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
sg_dma_address(&atmel_port->sg_rx),
sg_dma_len(&atmel_port->sg_rx),
sg_dma_len(&atmel_port->sg_rx)/2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
desc->callback = atmel_complete_rx_dma;
desc->callback_param = port;
atmel_port->desc_rx = desc;
......@@ -1921,12 +1938,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
unsigned long flags;
unsigned int mode, imr, quot, baud;
unsigned int old_mode, mode, imr, quot, baud;
/* Get current mode register */
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
| ATMEL_US_NBSTOP | ATMEL_US_PAR
| ATMEL_US_USMODE);
/* save the current mode register */
mode = old_mode = UART_GET_MR(port);
/* reset the mode, clock divisor, parity, stop bits and data size */
mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
ATMEL_US_PAR | ATMEL_US_USMODE);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
......@@ -1971,12 +1990,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
} else
mode |= ATMEL_US_PAR_NONE;
/* hardware handshake (RTS/CTS) */
if (termios->c_cflag & CRTSCTS)
mode |= ATMEL_US_USMODE_HWHS;
else
mode |= ATMEL_US_USMODE_NORMAL;
spin_lock_irqsave(&port->lock, flags);
port->read_status_mask = ATMEL_US_OVRE;
......@@ -2020,18 +2033,40 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
/* Resetting serial mode to RS232 (0x0) */
mode &= ~ATMEL_US_USMODE;
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
if ((port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
mode |= ATMEL_US_USMODE_HWHS;
} else {
/* RS232 without hadware handshake */
mode |= ATMEL_US_USMODE_NORMAL;
}
/* set the parity, stop bits and data size */
/* set the mode, clock divisor, parity, stop bits and data size */
UART_PUT_MR(port, mode);
/*
* when switching the mode, set the RTS line state according to the
* new mode, otherwise keep the former state
*/
if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
unsigned int rts_state;
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
/* let the hardware control the RTS line */
rts_state = ATMEL_US_RTSDIS;
} else {
/* force RTS line to low level */
rts_state = ATMEL_US_RTSEN;
}
UART_PUT_CR(port, rts_state);
}
/* set the baud rate */
UART_PUT_BRGR(port, quot);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
......@@ -2565,7 +2600,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
ret = atmel_init_port(port, pdev);
if (ret)
goto err;
goto err_clear_bit;
if (!atmel_use_pdc_rx(&port->uart)) {
ret = -ENOMEM;
......@@ -2596,6 +2631,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
/*
* The peripheral clock has been disabled by atmel_init_port():
* enable it before accessing I/O registers
*/
clk_prepare_enable(port->clk);
if (rs485_enabled) {
UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
......@@ -2606,6 +2647,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
*/
atmel_get_ip_name(&port->uart);
/*
* The peripheral clock can now safely be disabled till the port
* is used
*/
clk_disable_unprepare(port->clk);
return 0;
err_add_port:
......@@ -2616,6 +2663,8 @@ static int atmel_serial_probe(struct platform_device *pdev)
clk_put(port->clk);
port->clk = NULL;
}
err_clear_bit:
clear_bit(port->uart.line, atmel_ports_in_use);
err:
return ret;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -3,7 +3,7 @@
/*
* mcf.c -- Freescale ColdFire UART driver
*
* (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
* (C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org>
*
* 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
......@@ -198,7 +198,6 @@ static void mcf_shutdown(struct uart_port *port)
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned long flags;
unsigned int baud, baudclk;
#if defined(CONFIG_M5272)
......@@ -441,7 +440,6 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)
/* Enable or disable the RS485 support */
static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned char mr1, mr2;
/* Get mode registers */
......@@ -631,6 +629,7 @@ static int mcf_probe(struct platform_device *pdev)
port->mapbase = platp[i].mapbase;
port->membase = (platp[i].membase) ? platp[i].membase :
(unsigned char __iomem *) platp[i].mapbase;
port->dev = &pdev->dev;
port->iotype = SERIAL_IO_MEM;
port->irq = platp[i].irq;
port->uartclk = MCF_BUSCLK;
......@@ -702,7 +701,7 @@ static void __exit mcf_exit(void)
module_init(mcf_init);
module_exit(mcf_exit);
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
MODULE_DESCRIPTION("Freescale ColdFire UART driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mcfuart");
......
......@@ -23,7 +23,6 @@
#define MEN_Z135_MAX_PORTS 12
#define MEN_Z135_BASECLK 29491200
#define MEN_Z135_FIFO_SIZE 1024
#define MEN_Z135_NUM_MSI_VECTORS 2
#define MEN_Z135_FIFO_WATERMARK 1020
#define MEN_Z135_STAT_REG 0x0
......@@ -34,12 +33,11 @@
#define MEN_Z135_CONF_REG 0x808
#define MEN_Z135_UART_FREQ 0x80c
#define MEN_Z135_BAUD_REG 0x810
#define MENZ135_TIMEOUT 0x814
#define MEN_Z135_TIMEOUT 0x814
#define MEN_Z135_MEM_SIZE 0x818
#define IS_IRQ(x) ((x) & 1)
#define IRQ_ID(x) (((x) >> 1) & 7)
#define IRQ_ID(x) ((x) & 0x1f)
#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
......@@ -94,11 +92,11 @@
#define MEN_Z135_LSR_TEXP BIT(6)
#define MEN_Z135_LSR_RXFIFOERR BIT(7)
#define MEN_Z135_IRQ_ID_MST 0
#define MEN_Z135_IRQ_ID_TSA 1
#define MEN_Z135_IRQ_ID_RDA 2
#define MEN_Z135_IRQ_ID_RLS 3
#define MEN_Z135_IRQ_ID_CTI 6
#define MEN_Z135_IRQ_ID_RLS BIT(0)
#define MEN_Z135_IRQ_ID_RDA BIT(1)
#define MEN_Z135_IRQ_ID_CTI BIT(2)
#define MEN_Z135_IRQ_ID_TSA BIT(3)
#define MEN_Z135_IRQ_ID_MST BIT(4)
#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
......@@ -118,12 +116,18 @@ static int align;
module_param(align, int, S_IRUGO);
MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
static uint rx_timeout;
module_param(rx_timeout, uint, S_IRUGO);
MODULE_PARM_DESC(rx_timeout, "RX timeout. "
"Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg");
struct men_z135_port {
struct uart_port port;
struct mcb_device *mdev;
unsigned char *rxbuf;
u32 stat_reg;
spinlock_t lock;
bool automode;
};
#define to_men_z135(port) container_of((port), struct men_z135_port, port)
......@@ -180,12 +184,16 @@ static inline void men_z135_reg_clr(struct men_z135_port *uart,
*/
static void men_z135_handle_modem_status(struct men_z135_port *uart)
{
if (uart->stat_reg & MEN_Z135_MSR_DDCD)
u8 msr;
msr = (uart->stat_reg >> 8) & 0xff;
if (msr & MEN_Z135_MSR_DDCD)
uart_handle_dcd_change(&uart->port,
uart->stat_reg & ~MEN_Z135_MSR_DCD);
if (uart->stat_reg & MEN_Z135_MSR_DCTS)
msr & MEN_Z135_MSR_DCD);
if (msr & MEN_Z135_MSR_DCTS)
uart_handle_cts_change(&uart->port,
uart->stat_reg & ~MEN_Z135_MSR_CTS);
msr & MEN_Z135_MSR_CTS);
}
static void men_z135_handle_lsr(struct men_z135_port *uart)
......@@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
txfree = MEN_Z135_FIFO_WATERMARK - txc;
if (txfree <= 0) {
pr_err("Not enough room in TX FIFO have %d, need %d\n",
dev_err(&uart->mdev->dev,
"Not enough room in TX FIFO have %d, need %d\n",
txfree, qlen);
goto irq_en;
}
......@@ -373,43 +382,54 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
* @irq: The IRQ number
* @data: Pointer to UART port
*
* Check IIR register to see which tasklet to start.
* Check IIR register to find the cause of the interrupt and handle it.
* It is possible that multiple interrupts reason bits are set and reading
* the IIR is a destructive read, so we always need to check for all possible
* interrupts and handle them.
*/
static irqreturn_t men_z135_intr(int irq, void *data)
{
struct men_z135_port *uart = (struct men_z135_port *)data;
struct uart_port *port = &uart->port;
bool handled = false;
unsigned long flags;
int irq_id;
uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
/* IRQ pending is low active */
if (IS_IRQ(uart->stat_reg))
return IRQ_NONE;
irq_id = IRQ_ID(uart->stat_reg);
switch (irq_id) {
case MEN_Z135_IRQ_ID_MST:
men_z135_handle_modem_status(uart);
break;
case MEN_Z135_IRQ_ID_TSA:
men_z135_handle_tx(uart);
break;
case MEN_Z135_IRQ_ID_CTI:
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
/* Fallthrough */
case MEN_Z135_IRQ_ID_RDA:
/* Reading data clears RX IRQ */
men_z135_handle_rx(uart);
break;
case MEN_Z135_IRQ_ID_RLS:
if (!irq_id)
goto out;
spin_lock_irqsave(&port->lock, flags);
/* It's save to write to IIR[7:6] RXC[9:8] */
iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG);
if (irq_id & MEN_Z135_IRQ_ID_RLS) {
men_z135_handle_lsr(uart);
break;
default:
dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
return IRQ_NONE;
handled = true;
}
if (irq_id & (MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI)) {
if (irq_id & MEN_Z135_IRQ_ID_CTI)
dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
men_z135_handle_rx(uart);
handled = true;
}
if (irq_id & MEN_Z135_IRQ_ID_TSA) {
men_z135_handle_tx(uart);
handled = true;
}
return IRQ_HANDLED;
if (irq_id & MEN_Z135_IRQ_ID_MST) {
men_z135_handle_modem_status(uart);
handled = true;
}
spin_unlock_irqrestore(&port->lock, flags);
out:
return IRQ_RETVAL(handled);
}
/**
......@@ -464,21 +484,37 @@ static unsigned int men_z135_tx_empty(struct uart_port *port)
*/
static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct men_z135_port *uart = to_men_z135(port);
u32 conf_reg = 0;
u32 old;
u32 conf_reg;
conf_reg = old = ioread32(port->membase + MEN_Z135_CONF_REG);
if (mctrl & TIOCM_RTS)
conf_reg |= MEN_Z135_MCR_RTS;
else
conf_reg &= ~MEN_Z135_MCR_RTS;
if (mctrl & TIOCM_DTR)
conf_reg |= MEN_Z135_MCR_DTR;
else
conf_reg &= ~MEN_Z135_MCR_DTR;
if (mctrl & TIOCM_OUT1)
conf_reg |= MEN_Z135_MCR_OUT1;
else
conf_reg &= ~MEN_Z135_MCR_OUT1;
if (mctrl & TIOCM_OUT2)
conf_reg |= MEN_Z135_MCR_OUT2;
else
conf_reg &= ~MEN_Z135_MCR_OUT2;
if (mctrl & TIOCM_LOOP)
conf_reg |= MEN_Z135_MCR_LOOP;
else
conf_reg &= ~MEN_Z135_MCR_LOOP;
men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg);
if (conf_reg != old)
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
}
/**
......@@ -490,12 +526,9 @@ static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
static unsigned int men_z135_get_mctrl(struct uart_port *port)
{
unsigned int mctrl = 0;
u32 stat_reg;
u8 msr;
stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
msr = ~((stat_reg >> 8) & 0xff);
msr = ioread8(port->membase + MEN_Z135_STAT_REG + 1);
if (msr & MEN_Z135_MSR_CTS)
mctrl |= TIOCM_CTS;
......@@ -524,6 +557,19 @@ static void men_z135_stop_tx(struct uart_port *port)
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
}
/*
* men_z135_disable_ms() - Disable Modem Status
* port: The UART port
*
* Enable Modem Status IRQ.
*/
static void men_z135_disable_ms(struct uart_port *port)
{
struct men_z135_port *uart = to_men_z135(port);
men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN);
}
/**
* men_z135_start_tx() - Start transmitting characters
* @port: The UART port
......@@ -535,6 +581,9 @@ static void men_z135_start_tx(struct uart_port *port)
{
struct men_z135_port *uart = to_men_z135(port);
if (uart->automode)
men_z135_disable_ms(port);
men_z135_handle_tx(uart);
}
......@@ -584,6 +633,9 @@ static int men_z135_startup(struct uart_port *port)
iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
if (rx_timeout)
iowrite32(rx_timeout, port->membase + MEN_Z135_TIMEOUT);
return 0;
}
......@@ -603,6 +655,7 @@ static void men_z135_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct men_z135_port *uart = to_men_z135(port);
unsigned int baud;
u32 conf_reg;
u32 bd_reg;
......@@ -643,6 +696,16 @@ static void men_z135_set_termios(struct uart_port *port,
} else
lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
conf_reg |= MEN_Z135_IER_MSIEN;
if (termios->c_cflag & CRTSCTS) {
conf_reg |= MEN_Z135_MCR_RCFC;
uart->automode = true;
termios->c_cflag &= ~CLOCAL;
} else {
conf_reg &= ~MEN_Z135_MCR_RCFC;
uart->automode = false;
}
termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -421,8 +421,8 @@ struct sirfsoc_uart_port {
bool is_bt_uart;
struct clk *clk_general;
struct clk *clk_noc;
/* for SiRFmarco, there are SET/CLR for UART_INT_EN */
bool is_marco;
/* for SiRFatlas7, there are SET/CLR for UART_INT_EN */
bool is_atlas7;
struct sirfsoc_uart_register *uart_reg;
struct dma_chan *rx_dma_chan;
struct dma_chan *tx_dma_chan;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment