Merge branch 'tty-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6

* 'tty-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6:
  serial: bcm63xx_uart: fix irq storm after rx fifo overrun.
  amba pl011: platform data for reg lockup and glitch v2
  amba pl011: workaround for uart registers lockup
  tty: n_gsm: improper skb_pull() use was leaking framed data
  tty: n_gsm: Fixed logic to decode break signal from modem status
  TTY: ntty, add one more sanity check
  TTY: ldisc, do not close until there are readers
  8250: Fix capabilities when changing the port type
  8250_pci: Fix missing const from merges
  ARM: SAMSUNG: serial: Fix on handling of one clock source for UART
  serial: ioremap warning fix for jsm driver.
  8250_pci: add -ENODEV code for Intel EG20T PCH
This commit is contained in:
Linus Torvalds 2011-06-28 11:14:55 -07:00
commit 04b905942b
14 changed files with 295 additions and 19 deletions

View File

@ -35,6 +35,7 @@ void __init exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
tcfg->clocks = exynos4_serial_clocks;
tcfg->clocks_size = ARRAY_SIZE(exynos4_serial_clocks);
}
tcfg->flags |= NO_NEED_CHECK_CLKSRC;
}
s3c24xx_init_uartdevs("s5pv210-uart", s5p_uart_resources, cfg, no);

View File

@ -110,10 +110,18 @@ static pin_cfg_t mop500_pins_common[] = {
GPIO168_KP_O0,
/* UART */
GPIO0_U0_CTSn | PIN_INPUT_PULLUP,
GPIO1_U0_RTSn | PIN_OUTPUT_HIGH,
GPIO2_U0_RXD | PIN_INPUT_PULLUP,
GPIO3_U0_TXD | PIN_OUTPUT_HIGH,
/* uart-0 pins gpio configuration should be
* kept intact to prevent glitch in tx line
* when tty dev is opened. Later these pins
* are configured to uart mop500_pins_uart0
*
* It will be replaced with uart configuration
* once the issue is solved.
*/
GPIO0_GPIO | PIN_INPUT_PULLUP,
GPIO1_GPIO | PIN_OUTPUT_HIGH,
GPIO2_GPIO | PIN_INPUT_PULLUP,
GPIO3_GPIO | PIN_OUTPUT_HIGH,
GPIO29_U2_RXD | PIN_INPUT_PULLUP,
GPIO30_U2_TXD | PIN_OUTPUT_HIGH,

View File

@ -27,18 +27,21 @@
#include <linux/leds-lp5521.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/delay.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <plat/i2c.h>
#include <plat/ste_dma40.h>
#include <plat/pincfg.h>
#include <mach/hardware.h>
#include <mach/setup.h>
#include <mach/devices.h>
#include <mach/irqs.h>
#include "pins-db8500.h"
#include "ste-dma40-db8500.h"
#include "devices-db8500.h"
#include "board-mop500.h"
@ -393,12 +396,63 @@ static struct stedma40_chan_cfg uart2_dma_cfg_tx = {
};
#endif
static pin_cfg_t mop500_pins_uart0[] = {
GPIO0_U0_CTSn | PIN_INPUT_PULLUP,
GPIO1_U0_RTSn | PIN_OUTPUT_HIGH,
GPIO2_U0_RXD | PIN_INPUT_PULLUP,
GPIO3_U0_TXD | PIN_OUTPUT_HIGH,
};
#define PRCC_K_SOFTRST_SET 0x18
#define PRCC_K_SOFTRST_CLEAR 0x1C
static void ux500_uart0_reset(void)
{
void __iomem *prcc_rst_set, *prcc_rst_clr;
prcc_rst_set = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
PRCC_K_SOFTRST_SET);
prcc_rst_clr = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
PRCC_K_SOFTRST_CLEAR);
/* Activate soft reset PRCC_K_SOFTRST_CLEAR */
writel((readl(prcc_rst_clr) | 0x1), prcc_rst_clr);
udelay(1);
/* Release soft reset PRCC_K_SOFTRST_SET */
writel((readl(prcc_rst_set) | 0x1), prcc_rst_set);
udelay(1);
}
static void ux500_uart0_init(void)
{
int ret;
ret = nmk_config_pins(mop500_pins_uart0,
ARRAY_SIZE(mop500_pins_uart0));
if (ret < 0)
pr_err("pl011: uart pins_enable failed\n");
}
static void ux500_uart0_exit(void)
{
int ret;
ret = nmk_config_pins_sleep(mop500_pins_uart0,
ARRAY_SIZE(mop500_pins_uart0));
if (ret < 0)
pr_err("pl011: uart pins_disable failed\n");
}
static struct amba_pl011_data uart0_plat = {
#ifdef CONFIG_STE_DMA40
.dma_filter = stedma40_filter,
.dma_rx_param = &uart0_dma_cfg_rx,
.dma_tx_param = &uart0_dma_cfg_tx,
#endif
.init = ux500_uart0_init,
.exit = ux500_uart0_exit,
.reset = ux500_uart0_reset,
};
static struct amba_pl011_data uart1_plat = {

View File

@ -224,6 +224,8 @@
#define S5PV210_UFSTAT_RXMASK (255<<0)
#define S5PV210_UFSTAT_RXSHIFT (0)
#define NO_NEED_CHECK_CLKSRC 1
#ifndef __ASSEMBLY__
/* struct s3c24xx_uart_clksrc

View File

@ -875,7 +875,8 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
*dp++ = last << 7 | first << 6 | 1; /* EA */
len--;
}
memcpy(dp, skb_pull(dlci->skb, len), len);
memcpy(dp, dlci->skb->data, len);
skb_pull(dlci->skb, len);
__gsm_data_queue(dlci, msg);
if (last)
dlci->skb = NULL;
@ -984,10 +985,22 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data,
*/
static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
u32 modem)
u32 modem, int clen)
{
int mlines = 0;
u8 brk = modem >> 6;
u8 brk = 0;
/* The modem status command can either contain one octet (v.24 signals)
or two octets (v.24 signals + break signals). The length field will
either be 2 or 3 respectively. This is specified in section
5.4.6.3.7 of the 27.010 mux spec. */
if (clen == 2)
modem = modem & 0x7f;
else {
brk = modem & 0x7f;
modem = (modem >> 7) & 0x7f;
};
/* Flow control/ready to communicate */
if (modem & MDM_FC) {
@ -1061,7 +1074,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen)
return;
}
tty = tty_port_tty_get(&dlci->port);
gsm_process_modem(tty, dlci, modem);
gsm_process_modem(tty, dlci, modem, clen);
if (tty) {
tty_wakeup(tty);
tty_kref_put(tty);
@ -1482,12 +1495,13 @@ static void gsm_dlci_begin_close(struct gsm_dlci *dlci)
* open we shovel the bits down it, if not we drop them.
*/
static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len)
static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int clen)
{
/* krefs .. */
struct tty_port *port = &dlci->port;
struct tty_struct *tty = tty_port_tty_get(port);
unsigned int modem = 0;
int len = clen;
if (debug & 16)
pr_debug("%d bytes for tty %p\n", len, tty);
@ -1507,7 +1521,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len)
if (len == 0)
return;
}
gsm_process_modem(tty, dlci, modem);
gsm_process_modem(tty, dlci, modem, clen);
/* Line state will go via DLCI 0 controls only */
case 1:
default:

View File

@ -1815,6 +1815,7 @@ do_it_again:
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
BUG_ON(!tty->read_buf);
continue;
}
__set_current_state(TASK_RUNNING);

View File

@ -3318,6 +3318,7 @@ void serial8250_unregister_port(int line)
uart->port.flags &= ~UPF_BOOT_AUTOCONF;
uart->port.type = PORT_UNKNOWN;
uart->port.dev = &serial8250_isa_devs->dev;
uart->capabilities = uart_config[uart->port.type].flags;
uart_add_one_port(&serial8250_reg, &uart->port);
} else {
uart->port.dev = NULL;

View File

@ -994,6 +994,15 @@ static int skip_tx_en_setup(struct serial_private *priv,
return pci_default_setup(priv, board, port, idx);
}
static int pci_eg20t_init(struct pci_dev *dev)
{
#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE)
return -ENODEV;
#else
return 0;
#endif
}
/* This should be in linux/pci_ids.h */
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B
@ -1446,6 +1455,56 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.init = pci_oxsemi_tornado_init,
.setup = pci_default_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8811,
.init = pci_eg20t_init,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8812,
.init = pci_eg20t_init,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8813,
.init = pci_eg20t_init,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8814,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x8027,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x8028,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x8029,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x800C,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x800D,
.init = pci_eg20t_init,
},
{
.vendor = 0x10DB,
.device = 0x800D,
.init = pci_eg20t_init,
},
/*
* Cronyx Omega PCI (PLX-chip based)
*/

View File

@ -50,6 +50,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/sizes.h>
@ -65,6 +66,30 @@
#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
#define UART_DUMMY_DR_RX (1 << 16)
#define UART_WA_SAVE_NR 14
static void pl011_lockup_wa(unsigned long data);
static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
ST_UART011_DMAWM,
ST_UART011_TIMEOUT,
ST_UART011_LCRH_RX,
UART011_IBRD,
UART011_FBRD,
ST_UART011_LCRH_TX,
UART011_IFLS,
ST_UART011_XFCR,
ST_UART011_XON1,
ST_UART011_XON2,
ST_UART011_XOFF1,
ST_UART011_XOFF2,
UART011_CR,
UART011_IMSC
};
static u32 uart_wa_regdata[UART_WA_SAVE_NR];
static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
unsigned int ifls;
@ -72,6 +97,7 @@ struct vendor_data {
unsigned int lcrh_tx;
unsigned int lcrh_rx;
bool oversampling;
bool interrupt_may_hang; /* vendor-specific */
bool dma_threshold;
};
@ -90,9 +116,12 @@ static struct vendor_data vendor_st = {
.lcrh_tx = ST_UART011_LCRH_TX,
.lcrh_rx = ST_UART011_LCRH_RX,
.oversampling = true,
.interrupt_may_hang = true,
.dma_threshold = true,
};
static struct uart_amba_port *amba_ports[UART_NR];
/* Deals with DMA transactions */
struct pl011_sgbuf {
@ -132,6 +161,7 @@ struct uart_amba_port {
unsigned int lcrh_rx; /* vendor-specific */
bool autorts;
char type[12];
bool interrupt_may_hang; /* vendor-specific */
#ifdef CONFIG_DMA_ENGINE
/* DMA stuff */
bool using_tx_dma;
@ -1008,6 +1038,68 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
#endif
/*
* pl011_lockup_wa
* This workaround aims to break the deadlock situation
* when after long transfer over uart in hardware flow
* control, uart interrupt registers cannot be cleared.
* Hence uart transfer gets blocked.
*
* It is seen that during such deadlock condition ICR
* don't get cleared even on multiple write. This leads
* pass_counter to decrease and finally reach zero. This
* can be taken as trigger point to run this UART_BT_WA.
*
*/
static void pl011_lockup_wa(unsigned long data)
{
struct uart_amba_port *uap = amba_ports[0];
void __iomem *base = uap->port.membase;
struct circ_buf *xmit = &uap->port.state->xmit;
struct tty_struct *tty = uap->port.state->port.tty;
int buf_empty_retries = 200;
int loop;
/* Stop HCI layer from submitting data for tx */
tty->hw_stopped = 1;
while (!uart_circ_empty(xmit)) {
if (buf_empty_retries-- == 0)
break;
udelay(100);
}
/* Backup registers */
for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]);
/* Disable UART so that FIFO data is flushed out */
writew(0x00, uap->port.membase + UART011_CR);
/* Soft reset UART module */
if (uap->port.dev->platform_data) {
struct amba_pl011_data *plat;
plat = uap->port.dev->platform_data;
if (plat->reset)
plat->reset();
}
/* Restore registers */
for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
writew(uart_wa_regdata[loop] ,
uap->port.membase + uart_wa_reg[loop]);
/* Initialise the old status of the modem signals */
uap->old_status = readw(uap->port.membase + UART01x_FR) &
UART01x_FR_MODEM_ANY;
if (readl(base + UART011_MIS) & 0x2)
printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n");
/* Start Tx/Rx */
tty->hw_stopped = 0;
}
static void pl011_stop_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@ -1158,8 +1250,11 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
if (status & UART011_TXIS)
pl011_tx_chars(uap);
if (pass_counter-- == 0)
if (pass_counter-- == 0) {
if (uap->interrupt_may_hang)
tasklet_schedule(&pl011_lockup_tlet);
break;
}
status = readw(uap->port.membase + UART011_MIS);
} while (status != 0);
@ -1339,6 +1434,14 @@ static int pl011_startup(struct uart_port *port)
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
if (uap->port.dev->platform_data) {
struct amba_pl011_data *plat;
plat = uap->port.dev->platform_data;
if (plat->init)
plat->init();
}
return 0;
clk_dis:
@ -1394,6 +1497,15 @@ static void pl011_shutdown(struct uart_port *port)
* Shut down the clock producer
*/
clk_disable(uap->clk);
if (uap->port.dev->platform_data) {
struct amba_pl011_data *plat;
plat = uap->port.dev->platform_data;
if (plat->exit)
plat->exit();
}
}
static void
@ -1700,6 +1812,14 @@ static int __init pl011_console_setup(struct console *co, char *options)
if (!uap)
return -ENODEV;
if (uap->port.dev->platform_data) {
struct amba_pl011_data *plat;
plat = uap->port.dev->platform_data;
if (plat->init)
plat->init();
}
uap->port.uartclk = clk_get_rate(uap->clk);
if (options)
@ -1774,6 +1894,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->fifosize = vendor->fifosize;
uap->interrupt_may_hang = vendor->interrupt_may_hang;
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;

View File

@ -250,6 +250,20 @@ static void bcm_uart_do_rx(struct uart_port *port)
/* get overrun/fifo empty information from ier
* register */
iestat = bcm_uart_readl(port, UART_IR_REG);
if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) {
unsigned int val;
/* fifo reset is required to clear
* interrupt */
val = bcm_uart_readl(port, UART_CTL_REG);
val |= UART_CTL_RSTRXFIFO_MASK;
bcm_uart_writel(port, val, UART_CTL_REG);
port->icount.overrun++;
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
break;
@ -284,10 +298,6 @@ static void bcm_uart_do_rx(struct uart_port *port)
if (uart_handle_sysrq_char(port, c))
continue;
if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) {
port->icount.overrun++;
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
if ((cstat & port->ignore_status_mask) == 0)
tty_insert_flip_char(tty, c, flag);

View File

@ -125,7 +125,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
brd->bd_uart_offset = 0x200;
brd->bd_dividend = 921600;
brd->re_map_membase = ioremap(brd->membase, 0x1000);
brd->re_map_membase = ioremap(brd->membase, pci_resource_len(pdev, 0));
if (!brd->re_map_membase) {
dev_err(&pdev->dev,
"card has no PCI Memory resources, "

View File

@ -30,7 +30,7 @@ static int s5pv210_serial_setsource(struct uart_port *port,
struct s3c2410_uartcfg *cfg = port->dev->platform_data;
unsigned long ucon = rd_regl(port, S3C2410_UCON);
if ((cfg->clocks_size) == 1)
if (cfg->flags & NO_NEED_CHECK_CLKSRC)
return 0;
if (strcmp(clk->name, "pclk") == 0)
@ -55,7 +55,7 @@ static int s5pv210_serial_getsource(struct uart_port *port,
clk->divisor = 1;
if ((cfg->clocks_size) == 1)
if (cfg->flags & NO_NEED_CHECK_CLKSRC)
return 0;
switch (ucon & S5PV210_UCON_CLKMASK) {

View File

@ -555,7 +555,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
static int tty_ldisc_wait_idle(struct tty_struct *tty)
{
int ret;
ret = wait_event_interruptible_timeout(tty_ldisc_idle,
ret = wait_event_timeout(tty_ldisc_idle,
atomic_read(&tty->ldisc->users) == 1, 5 * HZ);
if (ret < 0)
return ret;
@ -763,6 +763,8 @@ static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
if (IS_ERR(ld))
return -1;
WARN_ON_ONCE(tty_ldisc_wait_idle(tty));
tty_ldisc_close(tty, tty->ldisc);
tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;

View File

@ -201,6 +201,9 @@ struct amba_pl011_data {
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
void *dma_rx_param;
void *dma_tx_param;
void (*init) (void);
void (*exit) (void);
void (*reset) (void);
};
#endif