From 9828def35a366fc1045cb794d99b6b12de89fa70 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 19 Mar 2019 23:16:46 +0800 Subject: serial: 8250_fintek: Make fintek_8250_set_termios static Fix sparse warning: drivers/tty/serial/8250/8250_fintek.c:306:6: warning: symbol 'fintek_8250_set_termios' was not declared. Should it be static? Signed-off-by: YueHaibing Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 79a4958b3f5c..31c91c2f8c6e 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -303,8 +303,9 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart, } } -void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +static void fintek_8250_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) { struct fintek_8250 *pdata = port->private_data; unsigned int baud = tty_termios_baud_rate(termios); -- cgit v1.2.3 From 4007098f4ce46c87c16ba34ddc00b042aa444b59 Mon Sep 17 00:00:00 2001 From: Lanqing Liu Date: Mon, 4 Mar 2019 16:58:22 +0800 Subject: serial: sprd: Add power management for the Spreadtrum serial controller This patch adds power management for the Spreadtrum serial controller. Signed-off-by: Lanqing Liu Signed-off-by: Baolin Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 61 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 1891a45ac05d..8f45b6671c19 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -100,10 +100,12 @@ #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) #define SPRD_IMSR_BREAK_DETECT BIT(7) #define SPRD_IMSR_TIMEOUT BIT(13) +#define SPRD_DEFAULT_SOURCE_CLK 26000000 struct sprd_uart_port { struct uart_port port; char name[16]; + struct clk *clk; }; static struct sprd_uart_port *sprd_port[UART_NR_MAX]; @@ -491,6 +493,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static void sprd_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct sprd_uart_port *sup = + container_of(port, struct sprd_uart_port, port); + + switch (state) { + case UART_PM_STATE_ON: + clk_prepare_enable(sup->clk); + break; + case UART_PM_STATE_OFF: + clk_disable_unprepare(sup->clk); + break; + } +} + static const struct uart_ops serial_sprd_ops = { .tx_empty = sprd_tx_empty, .get_mctrl = sprd_get_mctrl, @@ -507,6 +525,7 @@ static const struct uart_ops serial_sprd_ops = { .request_port = sprd_request_port, .config_port = sprd_config_port, .verify_port = sprd_verify_port, + .pm = sprd_pm, }; #ifdef CONFIG_SERIAL_SPRD_CONSOLE @@ -671,11 +690,45 @@ static int sprd_remove(struct platform_device *dev) return 0; } +static int sprd_clk_init(struct uart_port *uport) +{ + struct clk *clk_uart, *clk_parent; + struct sprd_uart_port *u = sprd_port[uport->line]; + + clk_uart = devm_clk_get(uport->dev, "uart"); + if (IS_ERR(clk_uart)) { + dev_warn(uport->dev, "uart%d can't get uart clock\n", + uport->line); + clk_uart = NULL; + } + + clk_parent = devm_clk_get(uport->dev, "source"); + if (IS_ERR(clk_parent)) { + dev_warn(uport->dev, "uart%d can't get source clock\n", + uport->line); + clk_parent = NULL; + } + + if (!clk_uart || clk_set_parent(clk_uart, clk_parent)) + uport->uartclk = SPRD_DEFAULT_SOURCE_CLK; + else + uport->uartclk = clk_get_rate(clk_uart); + + u->clk = devm_clk_get(uport->dev, "enable"); + if (IS_ERR(u->clk)) { + if (PTR_ERR(u->clk) != -EPROBE_DEFER) + dev_err(uport->dev, "uart%d can't get enable clock\n", + uport->line); + return PTR_ERR(u->clk); + } + + return 0; +} + static int sprd_probe(struct platform_device *pdev) { struct resource *res; struct uart_port *up; - struct clk *clk; int irq; int index; int ret; @@ -704,9 +757,9 @@ static int sprd_probe(struct platform_device *pdev) up->ops = &serial_sprd_ops; up->flags = UPF_BOOT_AUTOCONF; - clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR_OR_NULL(clk)) - up->uartclk = clk_get_rate(clk); + ret = sprd_clk_init(up); + if (ret) + return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); up->membase = devm_ioremap_resource(&pdev->dev, res); -- cgit v1.2.3 From f4487db58eb780a52d768f3b36aaaa8fd5839215 Mon Sep 17 00:00:00 2001 From: Lanqing Liu Date: Mon, 4 Mar 2019 16:58:24 +0800 Subject: serial: sprd: Add DMA mode support Add DMA mode support for the Spreadtrum serial controller. Signed-off-by: Lanqing Liu Signed-off-by: Baolin Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 440 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 426 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 8f45b6671c19..6aebd77cd3c0 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -75,6 +78,7 @@ /* control register 1 */ #define SPRD_CTL1 0x001C +#define SPRD_DMA_EN BIT(15) #define RX_HW_FLOW_CTL_THLD BIT(6) #define RX_HW_FLOW_CTL_EN BIT(7) #define TX_HW_FLOW_CTL_EN BIT(8) @@ -86,6 +90,7 @@ #define THLD_TX_EMPTY 0x40 #define THLD_TX_EMPTY_SHIFT 8 #define THLD_RX_FULL 0x40 +#define THLD_RX_FULL_MASK GENMASK(6, 0) /* config baud rate register */ #define SPRD_CLKD0 0x0024 @@ -102,15 +107,36 @@ #define SPRD_IMSR_TIMEOUT BIT(13) #define SPRD_DEFAULT_SOURCE_CLK 26000000 +#define SPRD_RX_DMA_STEP 1 +#define SPRD_RX_FIFO_FULL 1 +#define SPRD_TX_FIFO_FULL 0x20 +#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4) + +struct sprd_uart_dma { + struct dma_chan *chn; + unsigned char *virt; + dma_addr_t phys_addr; + dma_cookie_t cookie; + u32 trans_len; + bool enable; +}; + struct sprd_uart_port { struct uart_port port; char name[16]; struct clk *clk; + struct sprd_uart_dma tx_dma; + struct sprd_uart_dma rx_dma; + dma_addr_t pos; + unsigned char *rx_buf_tail; }; static struct sprd_uart_port *sprd_port[UART_NR_MAX]; static int sprd_ports_num; +static int sprd_start_dma_rx(struct uart_port *port); +static int sprd_tx_dma_config(struct uart_port *port); + static inline unsigned int serial_in(struct uart_port *port, unsigned int offset) { @@ -141,45 +167,389 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) /* nothing to do */ } -static void sprd_stop_tx(struct uart_port *port) +static void sprd_stop_rx(struct uart_port *port) { + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); unsigned int ien, iclr; + if (sp->rx_dma.enable) + dmaengine_terminate_all(sp->rx_dma.chn); + iclr = serial_in(port, SPRD_ICLR); ien = serial_in(port, SPRD_IEN); - iclr |= SPRD_IEN_TX_EMPTY; - ien &= ~SPRD_IEN_TX_EMPTY; + ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); + iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; - serial_out(port, SPRD_ICLR, iclr); serial_out(port, SPRD_IEN, ien); + serial_out(port, SPRD_ICLR, iclr); } -static void sprd_start_tx(struct uart_port *port) +static void sprd_uart_dma_enable(struct uart_port *port, bool enable) { - unsigned int ien; + u32 val = serial_in(port, SPRD_CTL1); - ien = serial_in(port, SPRD_IEN); - if (!(ien & SPRD_IEN_TX_EMPTY)) { - ien |= SPRD_IEN_TX_EMPTY; - serial_out(port, SPRD_IEN, ien); + if (enable) + val |= SPRD_DMA_EN; + else + val &= ~SPRD_DMA_EN; + + serial_out(port, SPRD_CTL1, val); +} + +static void sprd_stop_tx_dma(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct circ_buf *xmit = &port->state->xmit; + struct dma_tx_state state; + u32 trans_len; + + dmaengine_pause(sp->tx_dma.chn); + + dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state); + if (state.residue) { + trans_len = state.residue - sp->tx_dma.phys_addr; + xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1); + port->icount.tx += trans_len; + dma_unmap_single(port->dev, sp->tx_dma.phys_addr, + sp->tx_dma.trans_len, DMA_TO_DEVICE); } + + dmaengine_terminate_all(sp->tx_dma.chn); + sp->tx_dma.trans_len = 0; } -static void sprd_stop_rx(struct uart_port *port) +static int sprd_tx_buf_remap(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct circ_buf *xmit = &port->state->xmit; + + sp->tx_dma.trans_len = + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + sp->tx_dma.phys_addr = dma_map_single(port->dev, + (void *)&(xmit->buf[xmit->tail]), + sp->tx_dma.trans_len, + DMA_TO_DEVICE); + return dma_mapping_error(port->dev, sp->tx_dma.phys_addr); +} + +static void sprd_complete_tx_dma(void *data) +{ + struct uart_port *port = (struct uart_port *)data; + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + dma_unmap_single(port->dev, sp->tx_dma.phys_addr, + sp->tx_dma.trans_len, DMA_TO_DEVICE); + + xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1); + port->icount.tx += sp->tx_dma.trans_len; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) || + sprd_tx_dma_config(port)) + sp->tx_dma.trans_len = 0; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int sprd_uart_dma_submit(struct uart_port *port, + struct sprd_uart_dma *ud, u32 trans_len, + enum dma_transfer_direction direction, + dma_async_tx_callback callback) { + struct dma_async_tx_descriptor *dma_des; + unsigned long flags; + + flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE, + SPRD_DMA_NO_TRG, + SPRD_DMA_FRAG_REQ, + SPRD_DMA_TRANS_INT); + + dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len, + direction, flags); + if (!dma_des) + return -ENODEV; + + dma_des->callback = callback; + dma_des->callback_param = port; + + ud->cookie = dmaengine_submit(dma_des); + if (dma_submit_error(ud->cookie)) + return dma_submit_error(ud->cookie); + + dma_async_issue_pending(ud->chn); + + return 0; +} + +static int sprd_tx_dma_config(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ? + SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len; + int ret; + struct dma_slave_config cfg = { + .dst_addr = port->mapbase + SPRD_TXD, + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = burst, + }; + + ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg); + if (ret < 0) + return ret; + + return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len, + DMA_MEM_TO_DEV, sprd_complete_tx_dma); +} + +static void sprd_start_tx_dma(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + serial_out(port, SPRD_TXD, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sprd_stop_tx_dma(port); + return; + } + + if (sp->tx_dma.trans_len) + return; + + if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port)) + sp->tx_dma.trans_len = 0; +} + +static void sprd_rx_full_thld(struct uart_port *port, u32 thld) +{ + u32 val = serial_in(port, SPRD_CTL2); + + val &= ~THLD_RX_FULL_MASK; + val |= thld & THLD_RX_FULL_MASK; + serial_out(port, SPRD_CTL2, val); +} + +static int sprd_rx_alloc_buf(struct sprd_uart_port *sp) +{ + sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE, + &sp->rx_dma.phys_addr, GFP_KERNEL); + if (!sp->rx_dma.virt) + return -ENOMEM; + + return 0; +} + +static void sprd_rx_free_buf(struct sprd_uart_port *sp) +{ + if (sp->rx_dma.virt) + dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE, + sp->rx_dma.virt, sp->rx_dma.phys_addr); + +} + +static int sprd_rx_dma_config(struct uart_port *port, u32 burst) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct dma_slave_config cfg = { + .src_addr = port->mapbase + SPRD_RXD, + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .src_maxburst = burst, + }; + + return dmaengine_slave_config(sp->rx_dma.chn, &cfg); +} + +static void sprd_uart_dma_rx(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct tty_port *tty = &port->state->port; + + port->icount.rx += sp->rx_dma.trans_len; + tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len); + tty_flip_buffer_push(tty); +} + +static void sprd_uart_dma_irq(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(sp->rx_dma.chn, + sp->rx_dma.cookie, &state); + if (status == DMA_ERROR) + sprd_stop_rx(port); + + if (!state.residue && sp->pos == sp->rx_dma.phys_addr) + return; + + if (!state.residue) { + sp->rx_dma.trans_len = SPRD_UART_RX_SIZE + + sp->rx_dma.phys_addr - sp->pos; + sp->pos = sp->rx_dma.phys_addr; + } else { + sp->rx_dma.trans_len = state.residue - sp->pos; + sp->pos = state.residue; + } + + sprd_uart_dma_rx(port); + sp->rx_buf_tail += sp->rx_dma.trans_len; +} + +static void sprd_complete_rx_dma(void *data) +{ + struct uart_port *port = (struct uart_port *)data; + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + status = dmaengine_tx_status(sp->rx_dma.chn, + sp->rx_dma.cookie, &state); + if (status != DMA_COMPLETE) { + sprd_stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + return; + } + + if (sp->pos != sp->rx_dma.phys_addr) { + sp->rx_dma.trans_len = SPRD_UART_RX_SIZE + + sp->rx_dma.phys_addr - sp->pos; + sprd_uart_dma_rx(port); + sp->rx_buf_tail += sp->rx_dma.trans_len; + } + + if (sprd_start_dma_rx(port)) + sprd_stop_rx(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int sprd_start_dma_rx(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + int ret; + + if (!sp->rx_dma.enable) + return 0; + + sp->pos = sp->rx_dma.phys_addr; + sp->rx_buf_tail = sp->rx_dma.virt; + sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL); + ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP); + if (ret) + return ret; + + return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE, + DMA_DEV_TO_MEM, sprd_complete_rx_dma); +} + +static void sprd_release_dma(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + + sprd_uart_dma_enable(port, false); + + if (sp->rx_dma.enable) + dma_release_channel(sp->rx_dma.chn); + + if (sp->tx_dma.enable) + dma_release_channel(sp->tx_dma.chn); + + sp->tx_dma.enable = false; + sp->rx_dma.enable = false; +} + +static void sprd_request_dma(struct uart_port *port) +{ + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); + + sp->tx_dma.enable = true; + sp->rx_dma.enable = true; + + sp->tx_dma.chn = dma_request_chan(port->dev, "tx"); + if (IS_ERR(sp->tx_dma.chn)) { + dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n", + PTR_ERR(sp->tx_dma.chn)); + sp->tx_dma.enable = false; + } + + sp->rx_dma.chn = dma_request_chan(port->dev, "rx"); + if (IS_ERR(sp->rx_dma.chn)) { + dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n", + PTR_ERR(sp->tx_dma.chn)); + sp->rx_dma.enable = false; + } +} + +static void sprd_stop_tx(struct uart_port *port) +{ + struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, + port); unsigned int ien, iclr; + if (sp->tx_dma.enable) { + sprd_stop_tx_dma(port); + return; + } + iclr = serial_in(port, SPRD_ICLR); ien = serial_in(port, SPRD_IEN); - ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); - iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; + iclr |= SPRD_IEN_TX_EMPTY; + ien &= ~SPRD_IEN_TX_EMPTY; serial_out(port, SPRD_IEN, ien); serial_out(port, SPRD_ICLR, iclr); } +static void sprd_start_tx(struct uart_port *port) +{ + struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, + port); + unsigned int ien; + + if (sp->tx_dma.enable) { + sprd_start_tx_dma(port); + return; + } + + ien = serial_in(port, SPRD_IEN); + if (!(ien & SPRD_IEN_TX_EMPTY)) { + ien |= SPRD_IEN_TX_EMPTY; + serial_out(port, SPRD_IEN, ien); + } +} + /* The Sprd serial does not support this function. */ static void sprd_break_ctl(struct uart_port *port, int break_state) { @@ -220,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port, static inline void sprd_rx(struct uart_port *port) { + struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, + port); struct tty_port *tty = &port->state->port; unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; + if (sp->rx_dma.enable) { + sprd_uart_dma_irq(port); + return; + } + while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) && max_count--) { lsr = serial_in(port, SPRD_LSR); @@ -306,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void sprd_uart_dma_startup(struct uart_port *port, + struct sprd_uart_port *sp) +{ + int ret; + + sprd_request_dma(port); + if (!(sp->rx_dma.enable || sp->tx_dma.enable)) + return; + + ret = sprd_start_dma_rx(port); + if (ret) { + sp->rx_dma.enable = false; + dma_release_channel(sp->rx_dma.chn); + dev_warn(port->dev, "fail to start RX dma mode\n"); + } + + sprd_uart_dma_enable(port, true); +} + static int sprd_startup(struct uart_port *port) { int ret = 0; @@ -334,6 +730,9 @@ static int sprd_startup(struct uart_port *port) /* allocate irq */ sp = container_of(port, struct sprd_uart_port, port); snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); + + sprd_uart_dma_startup(port, sp); + ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, IRQF_SHARED, sp->name, port); if (ret) { @@ -348,7 +747,9 @@ static int sprd_startup(struct uart_port *port) /* enable interrupt */ spin_lock_irqsave(&port->lock, flags); ien = serial_in(port, SPRD_IEN); - ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + if (!sp->rx_dma.enable) + ien |= SPRD_IEN_RX_FULL; serial_out(port, SPRD_IEN, ien); spin_unlock_irqrestore(&port->lock, flags); @@ -357,6 +758,7 @@ static int sprd_startup(struct uart_port *port) static void sprd_shutdown(struct uart_port *port) { + sprd_release_dma(port); serial_out(port, SPRD_IEN, 0); serial_out(port, SPRD_ICLR, ~0); devm_free_irq(port->dev, port->irq, port); @@ -687,6 +1089,8 @@ static int sprd_remove(struct platform_device *dev) if (!sprd_ports_num) uart_unregister_driver(&sprd_uart_driver); + sprd_rx_free_buf(sup); + return 0; } @@ -775,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev) } up->irq = irq; + /* + * Allocate one dma buffer to prepare for receive transfer, in case + * memory allocation failure at runtime. + */ + ret = sprd_rx_alloc_buf(sprd_port[index]); + if (ret) + return ret; + if (!sprd_ports_num) { ret = uart_register_driver(&sprd_uart_driver); if (ret < 0) { -- cgit v1.2.3 From 24bc6e68efa00f95034dbef0ba91661dd80bd37d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 18 Mar 2019 12:29:15 +0200 Subject: serial: sc16is7xx: Respect clock-frequency property If the property is provided and there are no other possibilities to detect UART clock frequency, use it as a fallback. Tested-By: Georgii Staroselskii Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 635178cf3eed..7240c8608256 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1179,7 +1180,8 @@ static int sc16is7xx_probe(struct device *dev, struct regmap *regmap, int irq, unsigned long flags) { struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 }; - unsigned long freq, *pfreq = dev_get_platdata(dev); + unsigned long freq = 0, *pfreq = dev_get_platdata(dev); + u32 uartclk = 0; int i, ret; struct sc16is7xx_port *s; @@ -1193,10 +1195,17 @@ static int sc16is7xx_probe(struct device *dev, return -ENOMEM; } + /* Always ask for fixed clock rate from a property. */ + device_property_read_u32(dev, "clock-frequency", &uartclk); + s->clk = devm_clk_get(dev, NULL); if (IS_ERR(s->clk)) { + if (uartclk) + freq = uartclk; if (pfreq) freq = *pfreq; + if (freq) + dev_dbg(dev, "Clock frequency: %luHz\n", freq); else return PTR_ERR(s->clk); } else { -- cgit v1.2.3 From 00d9f0c65401a35100f4b98741576b4d2536fa28 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 18 Mar 2019 12:29:16 +0200 Subject: serial: sc16is7xx: Switch to use device_get_match_data() Instead of open coded variants, switch to direct use of device_get_match_data(). Tested-By: Georgii Staroselskii Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 7240c8608256..fe4d2da77301 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -14,9 +14,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -1393,13 +1393,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi) return ret; if (spi->dev.of_node) { - const struct of_device_id *of_id = - of_match_device(sc16is7xx_dt_ids, &spi->dev); - - if (!of_id) + devtype = device_get_match_data(&spi->dev); + if (!devtype) return -ENODEV; - - devtype = (struct sc16is7xx_devtype *)of_id->data; } else { const struct spi_device_id *id_entry = spi_get_device_id(spi); @@ -1454,13 +1450,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c, struct regmap *regmap; if (i2c->dev.of_node) { - const struct of_device_id *of_id = - of_match_device(sc16is7xx_dt_ids, &i2c->dev); - - if (!of_id) + devtype = device_get_match_data(&i2c->dev); + if (!devtype) return -ENODEV; - - devtype = (struct sc16is7xx_devtype *)of_id->data; } else { devtype = (struct sc16is7xx_devtype *)id->driver_data; flags = IRQF_TRIGGER_FALLING; -- cgit v1.2.3 From 6b77908fea62f8416b0f67d55c30b472a8eae890 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 18 Mar 2019 12:29:17 +0200 Subject: serial: sc16is7xx: Drop of_match_ptr() use There is an ACPI method to enumerate such devices via specific ACPI ID and use of compatible strings. It will not work for the drivers which have no OF match ID table present. Reported-by: Georgii Staroselskii Tested-By: Georgii Staroselskii Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index fe4d2da77301..c25cf9ab743e 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -1431,7 +1430,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); static struct spi_driver sc16is7xx_spi_uart_driver = { .driver = { .name = SC16IS7XX_NAME, - .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + .of_match_table = sc16is7xx_dt_ids, }, .probe = sc16is7xx_spi_probe, .remove = sc16is7xx_spi_remove, @@ -1485,7 +1484,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); static struct i2c_driver sc16is7xx_i2c_uart_driver = { .driver = { .name = SC16IS7XX_NAME, - .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + .of_match_table = sc16is7xx_dt_ids, }, .probe = sc16is7xx_i2c_probe, .remove = sc16is7xx_i2c_remove, -- cgit v1.2.3 From 6ac972dd4db42ef4e298669647b946de4e0243aa Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Wed, 13 Mar 2019 11:40:34 +0000 Subject: tty/sysrq: Convert show_lock to raw_spinlock_t Systems which don't provide arch_trigger_cpumask_backtrace() will invoke showacpu() from a smp_call_function() function which is invoked with disabled interrupts even on -RT systems. The function acquires the show_lock lock which only purpose is to ensure that the CPUs don't print simultaneously. Otherwise the output would clash and it would be hard to tell the output from CPUx apart from CPUy. On -RT the spin_lock() can not be acquired from this context. A raw_spin_lock() is required. It will introduce the system's latency by performing the sysrq request and other CPUs will block on the lock until the request is done. This is okay because the user asked for a backtrace of all active CPUs and under "normal circumstances in production" this path should not be triggered. Signed-off-by: Julien Grall [bigeasy@linuxtronix.de: commit description] Signed-off-by: Sebastian Andrzej Siewior Acked-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index fa0ce7dd9e24..59e82e6d776d 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -208,7 +208,7 @@ static struct sysrq_key_op sysrq_showlocks_op = { #endif #ifdef CONFIG_SMP -static DEFINE_SPINLOCK(show_lock); +static DEFINE_RAW_SPINLOCK(show_lock); static void showacpu(void *dummy) { @@ -218,10 +218,10 @@ static void showacpu(void *dummy) if (idle_cpu(smp_processor_id())) return; - spin_lock_irqsave(&show_lock, flags); + raw_spin_lock_irqsave(&show_lock, flags); pr_info("CPU%d:\n", smp_processor_id()); show_stack(NULL, NULL); - spin_unlock_irqrestore(&show_lock, flags); + raw_spin_unlock_irqrestore(&show_lock, flags); } static void sysrq_showregs_othercpus(struct work_struct *dummy) -- cgit v1.2.3 From 3adf2aa8af2d5122917b78988de9cdeb828cdd9d Mon Sep 17 00:00:00 2001 From: Valdis Klētnieks Date: Tue, 12 Mar 2019 07:49:03 -0400 Subject: drivers/tty/tty_jobctrl.c - fix non-kerneldoc comment Building with W=1 reports (among other things): CC drivers/tty/tty_jobctrl.o drivers/tty/tty_jobctrl.c:317: warning: Cannot understand * on line 317 - I thought it was a doc line Fix up the non-kerneldoc comment. (other warnings to be cleaned up in separate patch) Signed-off-by Valdis Kletnieks Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_jobctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index c4ecd66fafef..1393f71bb170 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -313,7 +313,7 @@ void disassociate_ctty(int on_exit) read_unlock(&tasklist_lock); } -/** +/* * * no_tty - Ensure the current process does not have a controlling tty */ -- cgit v1.2.3 From cf90c06f8115016fa4a94644a26a4ba093f0055e Mon Sep 17 00:00:00 2001 From: David Emett Date: Sun, 10 Mar 2019 21:43:47 +0000 Subject: tty: fix read of tty->pgrp outside of ctrl_lock The intention was clearly to use the tty_pgrp local variable rather than re-read tty->pgrp outside of ctrl_lock, so do that. This bug was introduced by commit 2812d9e9fd94 ("tty: Combine SIGTTOU/SIGTTIN handling"). Signed-off-by: David Emett Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_jobctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index 1393f71bb170..f8ed50a16848 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -44,7 +44,7 @@ int __tty_check_change(struct tty_struct *tty, int sig) tty_pgrp = tty->pgrp; spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (tty_pgrp && pgrp != tty->pgrp) { + if (tty_pgrp && pgrp != tty_pgrp) { if (is_ignored(sig)) { if (sig == SIGTTIN) ret = -EIO; -- cgit v1.2.3 From 1bbb1c318cd8a3a39e8c3e2e83d5e90542d6c3e3 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 15 Mar 2019 02:07:12 -0500 Subject: tty: ipwireless: fix missing checks for ioremap ipw->attr_memory and ipw->common_memory are assigned with the return value of ioremap. ioremap may fail, but no checks are enforced. The fix inserts the checks to avoid potential NULL pointer dereferences. Signed-off-by: Kangjie Lu Reviewed-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ipwireless/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c index 3475e841ef5c..4c18bbfe1a92 100644 --- a/drivers/tty/ipwireless/main.c +++ b/drivers/tty/ipwireless/main.c @@ -114,6 +114,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ipw->common_memory = ioremap(p_dev->resource[2]->start, resource_size(p_dev->resource[2])); + if (!ipw->common_memory) { + ret = -ENOMEM; + goto exit1; + } if (!request_mem_region(p_dev->resource[2]->start, resource_size(p_dev->resource[2]), IPWIRELESS_PCCARD_NAME)) { @@ -134,6 +138,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ipw->attr_memory = ioremap(p_dev->resource[3]->start, resource_size(p_dev->resource[3])); + if (!ipw->attr_memory) { + ret = -ENOMEM; + goto exit3; + } if (!request_mem_region(p_dev->resource[3]->start, resource_size(p_dev->resource[3]), IPWIRELESS_PCCARD_NAME)) { -- cgit v1.2.3 From 46ca3f735f345c9d87383dd3a09fa5d43870770e Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sun, 10 Mar 2019 21:24:15 +0000 Subject: tty/vt: fix write/write race in ioctl(KDSKBSENT) handler The bug manifests as an attempt to access deallocated memory: BUG: unable to handle kernel paging request at ffff9c8735448000 #PF error: [PROT] [WRITE] PGD 288a05067 P4D 288a05067 PUD 288a07067 PMD 7f60c2063 PTE 80000007f5448161 Oops: 0003 [#1] PREEMPT SMP CPU: 6 PID: 388 Comm: loadkeys Tainted: G C 5.0.0-rc6-00153-g5ded5871030e #91 Hardware name: Gigabyte Technology Co., Ltd. To be filled by O.E.M./H77M-D3H, BIOS F12 11/14/2013 RIP: 0010:__memmove+0x81/0x1a0 Code: 4c 89 4f 10 4c 89 47 18 48 8d 7f 20 73 d4 48 83 c2 20 e9 a2 00 00 00 66 90 48 89 d1 4c 8b 5c 16 f8 4c 8d 54 17 f8 48 c1 e9 03 48 a5 4d 89 1a e9 0c 01 00 00 0f 1f 40 00 48 89 d1 4c 8b 1e 49 RSP: 0018:ffffa1b9002d7d08 EFLAGS: 00010203 RAX: ffff9c873541af43 RBX: ffff9c873541af43 RCX: 00000c6f105cd6bf RDX: 0000637882e986b6 RSI: ffff9c8735447ffb RDI: ffff9c8735447ffb RBP: ffff9c8739cd3800 R08: ffff9c873b802f00 R09: 00000000fffff73b R10: ffffffffb82b35f1 R11: 00505b1b004d5b1b R12: 0000000000000000 R13: ffff9c873541af3d R14: 000000000000000b R15: 000000000000000c FS: 00007f450c390580(0000) GS:ffff9c873f180000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffff9c8735448000 CR3: 00000007e213c002 CR4: 00000000000606e0 Call Trace: vt_do_kdgkb_ioctl+0x34d/0x440 vt_ioctl+0xba3/0x1190 ? __bpf_prog_run32+0x39/0x60 ? mem_cgroup_commit_charge+0x7b/0x4e0 tty_ioctl+0x23f/0x920 ? preempt_count_sub+0x98/0xe0 ? __seccomp_filter+0x67/0x600 do_vfs_ioctl+0xa2/0x6a0 ? syscall_trace_enter+0x192/0x2d0 ksys_ioctl+0x3a/0x70 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x54/0xe0 entry_SYSCALL_64_after_hwframe+0x49/0xbe The bug manifests on systemd systems with multiple vtcon devices: # cat /sys/devices/virtual/vtconsole/vtcon0/name (S) dummy device # cat /sys/devices/virtual/vtconsole/vtcon1/name (M) frame buffer device There systemd runs 'loadkeys' tool in tapallel for each vtcon instance. This causes two parallel ioctl(KDSKBSENT) calls to race into adding the same entry into 'func_table' array at: drivers/tty/vt/keyboard.c:vt_do_kdgkb_ioctl() The function has no locking around writes to 'func_table'. The simplest reproducer is to have initrams with the following init on a 8-CPU machine x86_64: #!/bin/sh loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & loadkeys -q windowkeys ru4 & wait The change adds lock on write path only. Reads are still racy. CC: Greg Kroah-Hartman CC: Jiri Slaby Link: https://lkml.org/lkml/2019/2/17/256 Signed-off-by: Sergei Trofimovich Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 88312c6c92cc..0617e87ab343 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals); static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); static DEFINE_SPINLOCK(led_lock); +static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */ static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static bool dead_key_next; @@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) char *p; u_char *q; u_char __user *up; - int sz; + int sz, fnw_sz; int delta; char *first_free, *fj, *fnw; int i, j, k; int ret; + unsigned long flags; if (!capable(CAP_SYS_TTY_CONFIG)) perm = 0; @@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) goto reterr; } + fnw = NULL; + fnw_sz = 0; + /* race aginst other writers */ + again: + spin_lock_irqsave(&func_buf_lock, flags); q = func_table[i]; + + /* fj pointer to next entry after 'q' */ first_free = funcbufptr + (funcbufsize - funcbufleft); for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ; @@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) fj = func_table[j]; else fj = first_free; - + /* buffer usage increase by new entry */ delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); + if (delta <= funcbufleft) { /* it fits in current buf */ if (j < MAX_NR_FUNC) { + /* make enough space for new entry at 'fj' */ memmove(fj + delta, fj, first_free - fj); for (k = j; k < MAX_NR_FUNC; k++) if (func_table[k]) @@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) sz = 256; while (sz < funcbufsize - funcbufleft + delta) sz <<= 1; - fnw = kmalloc(sz, GFP_KERNEL); - if(!fnw) { - ret = -ENOMEM; - goto reterr; + if (fnw_sz != sz) { + spin_unlock_irqrestore(&func_buf_lock, flags); + kfree(fnw); + fnw = kmalloc(sz, GFP_KERNEL); + fnw_sz = sz; + if (!fnw) { + ret = -ENOMEM; + goto reterr; + } + goto again; } if (!q) func_table[i] = fj; + /* copy data before insertion point to new location */ if (fj > funcbufptr) memmove(fnw, funcbufptr, fj - funcbufptr); for (k = 0; k < j; k++) if (func_table[k]) func_table[k] = fnw + (func_table[k] - funcbufptr); + /* copy data after insertion point to new location */ if (first_free > fj) { memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); for (k = j; k < MAX_NR_FUNC; k++) @@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) funcbufleft = funcbufleft - delta + sz - funcbufsize; funcbufsize = sz; } + /* finally insert item itself */ strcpy(func_table[i], kbs->kb_string); + spin_unlock_irqrestore(&func_buf_lock, flags); break; } ret = 0; -- cgit v1.2.3 From 75ddbc1fb11efac87b611d48e9802f6fe2bb2163 Mon Sep 17 00:00:00 2001 From: Yifeng Li Date: Tue, 5 Mar 2019 07:02:49 +0800 Subject: tty: vt.c: Fix TIOCL_BLANKSCREEN console blanking if blankinterval == 0 Previously, in the userspace, it was possible to use the "setterm" command from util-linux to blank the VT console by default, using the following command. According to the man page, > The force option keeps the screen blank even if a key is pressed. It was implemented by calling TIOCL_BLANKSCREEN. case BLANKSCREEN: ioctlarg = TIOCL_BLANKSCREEN; if (ioctl(STDIN_FILENO, TIOCLINUX, &ioctlarg)) warn(_("cannot force blank")); break; However, after Linux 4.12, this command ceased to work anymore, which is unexpected. By inspecting the kernel source, it shows that the issue was triggered by the side-effect from commit a4199f5eb809 ("tty: Disable default console blanking interval"). The console blanking is implemented by function do_blank_screen() in vt.c: "blank_state" will be initialized to "blank_normal_wait" in con_init() if AND ONLY IF ("blankinterval" > 0). If "blankinterval" is 0, "blank_state" will be "blank_off" (== 0), and a call to do_blank_screen() will always abort, even if a forced blanking is required from the user by calling TIOCL_BLANKSCREEN, the console won't be blanked. This behavior is unexpected from a user's point-of-view, since it's not mentioned in any documentation. The setterm man page suggests it will always work, and the kernel comments in uapi/linux/tiocl.h says > /* keep screen blank even if a key is pressed */ > #define TIOCL_BLANKSCREEN 14 To fix it, we simply remove the "blank_state != blank_off" check, as pointed out by Nicolas Pitre, this check doesn't logically make sense and it's safe to remove. Suggested-by: Nicolas Pitre Fixes: a4199f5eb809 ("tty: Disable default console blanking interval") Signed-off-by: Yifeng Li Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index d34984aa646d..721edee50234 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4178,8 +4178,6 @@ void do_blank_screen(int entering_gfx) return; } - if (blank_state != blank_normal_wait) - return; blank_state = blank_off; /* don't blank graphics */ -- cgit v1.2.3 From b9ca5f8560af244489b4a1bc1ae88b341f24bc95 Mon Sep 17 00:00:00 2001 From: Sahara Date: Mon, 11 Feb 2019 11:09:15 +0400 Subject: tty: pty: Fix race condition between release_one_tty and pty_write Especially when a linked tty is used such as pty, the linked tty port's buf works have not been cancelled while master tty port's buf work has been cancelled. Since release_one_tty and flush_to_ldisc run in workqueue threads separately, when pty_cleanup happens and link tty port is freed, flush_to_ldisc tries to access freed port and port->itty, eventually it causes a panic. This patch utilizes the magic value with holding the tty_mutex to check if the tty->link is valid. Fixes: 2b022ab7542d ("pty: cancel pty slave port buf's work in tty_release") Signed-off-by: Sahara Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 7 +++++++ drivers/tty/tty_io.c | 3 +++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 00099a8439d2..ef72031ab5b9 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -116,6 +116,12 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) if (tty->stopped) return 0; + mutex_lock(&tty_mutex); + if (to->magic != TTY_MAGIC) { + mutex_unlock(&tty_mutex); + return -EIO; + } + if (c > 0) { spin_lock_irqsave(&to->port->lock, flags); /* Stuff the data into the input queue of the other end */ @@ -125,6 +131,7 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) tty_flip_buffer_push(to->port); spin_unlock_irqrestore(&to->port->lock, flags); } + mutex_unlock(&tty_mutex); return c; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 5fa250157025..c27777f3b8c4 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1448,10 +1448,13 @@ static void release_one_tty(struct work_struct *work) struct tty_driver *driver = tty->driver; struct module *owner = driver->owner; + mutex_lock(&tty_mutex); if (tty->ops->cleanup) tty->ops->cleanup(tty); tty->magic = 0; + mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); module_put(owner); -- cgit v1.2.3 From 67fbfc3943e03464541d2cbe8ff8ff77cfe7fe93 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Mon, 11 Mar 2019 10:08:45 +0100 Subject: vt: use /dev/vcs (not /dev/vcs0) in comment Both /dev/vcs and /dev/vcs0 were in use in the past, but these days /dev/vcs0 is mostly historical curiosity. * "/dev/vcs" is the name that has always been in the Linux allocated devices list. * "vcs" is the device name in sysfs since Linux v2.6.12. * MAKEDEV(1) in Debian used to create /dev/vcs0 only, but /dev/vcs was added in 1999: https://bugs.debian.org/45698 * MAKEDEV(1) in RedHat switched from /dev/vcs0 to /dev/vcs in 2000: * Fri Oct 20 2000 Nalin Dahyabhai - change vcs0 to vcs (ditto for vcsa0) Signed-off-by: Jakub Wilk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 160f46115aaa..1f042346e722 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Provide access to virtual console memory. - * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) + * /dev/vcs: the screen as it is being viewed right now (possibly scrolled) * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) * [minor: N] * -- cgit v1.2.3 From 4b0a2c5ff7215206ea6135a405f17c5f6fca7d00 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Tue, 19 Mar 2019 11:34:06 +0530 Subject: virtio_console: initialize vtermno value for ports For regular serial ports we do not initialize value of vtermno variable. A garbage value is assigned for non console ports. The value can be observed as a random integer with [1]. [1] vim /sys/kernel/debug/virtio-ports/vport*p* This patch initialize the value of vtermno for console serial ports to '1' and regular serial ports are initiaized to '0'. Reported-by: siliu@redhat.com Signed-off-by: Pankaj Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index fbeb71953526..05dbfdb9f4af 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -75,7 +75,7 @@ struct ports_driver_data { /* All the console devices handled by this driver */ struct list_head consoles; }; -static struct ports_driver_data pdrvdata; +static struct ports_driver_data pdrvdata = { .next_vtermno = 1}; static DEFINE_SPINLOCK(pdrvdata_lock); static DECLARE_COMPLETION(early_console_added); @@ -1394,6 +1394,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->async_queue = NULL; port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + port->cons.vtermno = 0; port->host_connected = port->guest_connected = false; port->stats = (struct port_stats) { 0 }; -- cgit v1.2.3 From 0eae4686a128d87503863a261e84887750c8bc68 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 29 Mar 2019 15:47:40 +0100 Subject: Revert "tty: pty: Fix race condition between release_one_tty and pty_write" This reverts commit b9ca5f8560af244489b4a1bc1ae88b341f24bc95 as 0-day shows it has a circular locking dependency. Fixes: b9ca5f8560af ("tty: pty: Fix race condition between release_one_tty and pty_write") Reported-by: kernel test robot Cc: Sahara Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 7 ------- drivers/tty/tty_io.c | 3 --- 2 files changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index ef72031ab5b9..00099a8439d2 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -116,12 +116,6 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) if (tty->stopped) return 0; - mutex_lock(&tty_mutex); - if (to->magic != TTY_MAGIC) { - mutex_unlock(&tty_mutex); - return -EIO; - } - if (c > 0) { spin_lock_irqsave(&to->port->lock, flags); /* Stuff the data into the input queue of the other end */ @@ -131,7 +125,6 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) tty_flip_buffer_push(to->port); spin_unlock_irqrestore(&to->port->lock, flags); } - mutex_unlock(&tty_mutex); return c; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index c27777f3b8c4..5fa250157025 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1448,13 +1448,10 @@ static void release_one_tty(struct work_struct *work) struct tty_driver *driver = tty->driver; struct module *owner = driver->owner; - mutex_lock(&tty_mutex); if (tty->ops->cleanup) tty->ops->cleanup(tty); tty->magic = 0; - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); module_put(owner); -- cgit v1.2.3 From 38c91d1d55fe842bf7edde4469e812b34a711cd8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Apr 2019 16:07:07 +0200 Subject: tty: add SPDX identifiers to Kconfig and Makefiles There were a few Kconfig and Makefiles under drivers/tty/ that were missing a SPDX identifier. Fix that up so that automated tools can properly classify all kernel source files. Cc: Jiri Slaby Cc: Jiri Kosina Cc: Rob Herring Acked-by: David Sterba Reviewed-by: Mukesh Ojha Signed-off-by: Greg Kroah-Hartman --- drivers/tty/Kconfig | 1 + drivers/tty/hvc/Kconfig | 1 + drivers/tty/ipwireless/Makefile | 1 + drivers/tty/serdev/Kconfig | 1 + drivers/tty/serdev/Makefile | 1 + drivers/tty/serial/8250/Kconfig | 1 + drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/cpm_uart/Makefile | 1 + drivers/tty/serial/jsm/Makefile | 1 + 9 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index e0a04bfc873e..8034473c54ca 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config TTY bool "Enable TTY" if EXPERT default y diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 4293c172e120..1a910ec9f643 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 if TTY config HVC_DRIVER diff --git a/drivers/tty/ipwireless/Makefile b/drivers/tty/ipwireless/Makefile index fe2e1730986b..a665d021e24d 100644 --- a/drivers/tty/ipwireless/Makefile +++ b/drivers/tty/ipwireless/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for the IPWireless driver # diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig index 1dbc8352e027..46ae732bfc68 100644 --- a/drivers/tty/serdev/Kconfig +++ b/drivers/tty/serdev/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Serial bus device driver configuration # diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile index 0cbdb9444d9d..078417e5b068 100644 --- a/drivers/tty/serdev/Makefile +++ b/drivers/tty/serdev/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 serdev-objs := core.o obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 15c2c5463835..296115f6a4d8 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # The 8250/16550 serial drivers. You shouldn't be in this list unless # you somehow have an implicit or explicit dependency on SERIAL_8250. diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 72966bc0ac76..a5032e58c737 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Serial device configuration # diff --git a/drivers/tty/serial/cpm_uart/Makefile b/drivers/tty/serial/cpm_uart/Makefile index 896a5d57881c..3f3a6ed02ed4 100644 --- a/drivers/tty/serial/cpm_uart/Makefile +++ b/drivers/tty/serial/cpm_uart/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for the Motorola 8xx FEC ethernet controller # diff --git a/drivers/tty/serial/jsm/Makefile b/drivers/tty/serial/jsm/Makefile index 705d1ff6fdeb..4f2dbada7741 100644 --- a/drivers/tty/serial/jsm/Makefile +++ b/drivers/tty/serial/jsm/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for Jasmine adapter # -- cgit v1.2.3 From f16aa97d3f575ea660f49d4698efe1c4a4c60919 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Apr 2019 16:07:08 +0200 Subject: tty: fix up a few remaining files without SPDX identifiers There were a few straggling files under drivers/tty/ that did not have any SPDX identifier either because they entered the tree recently, or they somehow missed the mass-tagging of commit b24413180f56 ("License cleanup: add SPDX GPL-2.0 license identifier to files with no license") This commit follows the same rule as b24413180f56 ("License cleanup: add SPDX GPL-2.0 license identifier to files with no license") where files without any specified license in them fall under GPL-2.0 as the correct license for the individual file. Add that identifier to these remaining files so that we don't have to guess at the license of them in the future. Cc: Jiri Slaby Cc: "David S. Miller" Reviewed-by: Mukesh Ojha Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_men_mcb.c | 1 + drivers/tty/serial/sn_console.c | 1 + drivers/tty/vcc.c | 1 + drivers/tty/vt/.gitignore | 1 + drivers/tty/vt/cp437.uni | 1 + drivers/tty/vt/defkeymap.c_shipped | 1 + drivers/tty/vt/defkeymap.map | 1 + 7 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c index 127017cc41d9..02c5aff58a74 100644 --- a/drivers/tty/serial/8250/8250_men_mcb.c +++ b/drivers/tty/serial/8250/8250_men_mcb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index fe9170731c16..283493358a62 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * C-Brick Serial Port (and console) driver for SGI Altix machines. * diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 58b454c34560..d2a1e1228c82 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* vcc.c: sun4v virtual channel concentrator * * Copyright (C) 2017 Oracle. All rights reserved. diff --git a/drivers/tty/vt/.gitignore b/drivers/tty/vt/.gitignore index 83683a2d8e6a..9b38b85f9d9a 100644 --- a/drivers/tty/vt/.gitignore +++ b/drivers/tty/vt/.gitignore @@ -1,2 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 consolemap_deftbl.c defkeymap.c diff --git a/drivers/tty/vt/cp437.uni b/drivers/tty/vt/cp437.uni index bc6163484f62..a1991904c559 100644 --- a/drivers/tty/vt/cp437.uni +++ b/drivers/tty/vt/cp437.uni @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Unicode table for IBM Codepage 437. Note that there are many more # substitutions that could be conceived (for example, thick-line diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped index d2208dfe3f67..c7095fb7d2d1 100644 --- a/drivers/tty/vt/defkeymap.c_shipped +++ b/drivers/tty/vt/defkeymap.c_shipped @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Do not edit this file! It was automatically generated by */ /* loadkeys --mktable defkeymap.map > defkeymap.c */ diff --git a/drivers/tty/vt/defkeymap.map b/drivers/tty/vt/defkeymap.map index 50b30cace261..37f1ac6ddfb9 100644 --- a/drivers/tty/vt/defkeymap.map +++ b/drivers/tty/vt/defkeymap.map @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Default kernel keymap. This uses 7 modifier combinations. keymaps 0-2,4-5,8,12 # Change the above line into -- cgit v1.2.3 From 9ef8927f45f2f8e365cda7cda81c079f29b8ad74 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 30 Mar 2019 00:46:28 +0000 Subject: n_tty: check for negative and zero space return from tty_write_room The return from tty_write_room could potentially be negative if a tty write_room driver returns an error number (not that any seem to do). Rather than just check for a zero return, also check for a -ve return. This avoids the unsigned nr being set to a large unsigned value on the assignment from variable space and can lead to overflowing the buffer buf. Better to be safe than assume all write_room implementations in tty drivers are going to do the right thing. Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 9cdb0fa3c4bf..f9c584244f72 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -550,9 +550,9 @@ static ssize_t process_output_block(struct tty_struct *tty, mutex_lock(&ldata->output_lock); space = tty_write_room(tty); - if (!space) { + if (space <= 0) { mutex_unlock(&ldata->output_lock); - return 0; + return space; } if (nr > space) nr = space; -- cgit v1.2.3 From 7a7457ba40f1474aa1bae2010b6f8582ba5ed57f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 30 Mar 2019 01:47:24 +0000 Subject: serial: sprd: Fix a copy-paste err in sprd_request_dma() when dma_request_chan rx_dma fails, dev_err log should print rx_dma err log. Signed-off-by: YueHaibing Reviewed-by: Mukesh Ojha Reviewed-by: Baolin Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 6aebd77cd3c0..73d71a4e6c0c 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -506,7 +506,7 @@ static void sprd_request_dma(struct uart_port *port) sp->rx_dma.chn = dma_request_chan(port->dev, "rx"); if (IS_ERR(sp->rx_dma.chn)) { dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n", - PTR_ERR(sp->tx_dma.chn)); + PTR_ERR(sp->rx_dma.chn)); sp->rx_dma.enable = false; } } -- cgit v1.2.3 From f692f7766f335ca38f0d6d1cb4c32dfad81820d4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 12 Apr 2019 12:17:39 +0200 Subject: tty: remove redundant 'default n' from Kconfig-s 'default n' is the default value for any bool or tristate Kconfig setting so there is no need to write it explicitly. Also since commit f467c5640c29 ("kconfig: only write '# CONFIG_FOO is not set' for visible symbols") the Kconfig behavior is the same regardless of 'default n' being present or not: ... One side effect of (and the main motivation for) this change is making the following two definitions behave exactly the same: config FOO bool config FOO bool default n With this change, neither of these will generate a '# CONFIG_FOO is not set' line (assuming FOO isn't selected/implied). That might make it clearer to people that a bare 'default n' is redundant. ... Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/Kconfig | 3 --- drivers/tty/hvc/Kconfig | 2 -- drivers/tty/serial/Kconfig | 3 --- 3 files changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 8034473c54ca..0ebb8d6ab341 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -84,7 +84,6 @@ config HW_CONSOLE config VT_HW_CONSOLE_BINDING bool "Support for binding and unbinding console drivers" depends on HW_CONSOLE - default n ---help--- The virtual terminal is the device that interacts with the physical terminal through console drivers. On these systems, at least one @@ -313,7 +312,6 @@ config N_GSM config TRACE_ROUTER tristate "Trace data router for MIPI P1149.7 cJTAG standard" depends on TRACE_SINK - default n help The trace router uses the Linux tty line discipline framework to route trace data coming from a tty port (say UART for example) to @@ -329,7 +327,6 @@ config TRACE_ROUTER config TRACE_SINK tristate "Trace data sink for MIPI P1149.7 cJTAG standard" - default n help The trace sink uses the Linux line discipline framework to receive trace data coming from the trace router line discipline driver diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 1a910ec9f643..4d22b911111f 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -25,7 +25,6 @@ config HVC_CONSOLE config HVC_OLD_HVSI bool "Old driver for pSeries serial port (/dev/hvsi*)" depends on HVC_CONSOLE - default n config HVC_OPAL bool "OPAL Console support" @@ -74,7 +73,6 @@ config HVC_UDBG bool "udbg based fake hypervisor console" depends on PPC select HVC_DRIVER - default n help This is meant to be used during HW bring up or debugging when no other console mechanism exist but udbg, to get you a quick diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a5032e58c737..6d0ab0399a72 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -370,7 +370,6 @@ config SERIAL_MAX310X depends on SPI_MASTER select SERIAL_CORE select REGMAP_SPI if SPI_MASTER - default n help This selects support for an advanced UART from Maxim (Dallas). Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. @@ -653,7 +652,6 @@ config SERIAL_MUX_CONSOLE config PDC_CONSOLE bool "PDC software console support" depends on PARISC && !SERIAL_MUX && VT - default n help Saying Y here will enable the software based PDC console to be used as the system console. This is useful for machines in @@ -1110,7 +1108,6 @@ config SERIAL_QE depends on QUICC_ENGINE select SERIAL_CORE select FW_LOADER - default n help This driver supports the QE serial ports on Freescale embedded PowerPC that contain a QUICC Engine. -- cgit v1.2.3 From fa2b360f261e31f2a54f997095713f91bac0e503 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Mon, 15 Apr 2019 14:17:55 +0300 Subject: tty/vt: avoid high order pages allocation on GIO_UNIMAP ioctl GIO_UNIMAP can easily result in a high order allocation, seen 6th order allocation on radeondrmfb: fbcon: radeondrmfb (fb0) is primary device Console: switching to colour frame buffer device 160x64 radeon 0000:01:05.0: fb0: radeondrmfb frame buffer device WARNING: CPU: 0 PID: 78661 at mm/page_alloc.c:3532 __alloc_pages_nodemask+0x1b1/0x600 order 6 >= 3, gfp 0x40d0 The warning is generated by a debug patch. At the same time it's safe to use kvmalloc() for allocation in con_get_unimap(), so let's do the substitution. And do the same for con_set_unimap(). Signed-off-by: Konstantin Khorenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 7c7ada0b3ea0..b28aa0d289f8 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -542,7 +542,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) if (!ct) return 0; - unilist = memdup_user(list, ct * sizeof(struct unipair)); + unilist = vmemdup_user(list, ct * sizeof(struct unipair)); if (IS_ERR(unilist)) return PTR_ERR(unilist); @@ -641,7 +641,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) out_unlock: console_unlock(); - kfree(unilist); + kvfree(unilist); return err; } @@ -743,7 +743,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni struct uni_pagedir *p; struct unipair *unilist; - unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); + unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); if (!unilist) return -ENOMEM; @@ -775,7 +775,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair))) ret = -EFAULT; put_user(ect, uct); - kfree(unilist); + kvfree(unilist); return ret ? ret : (ect <= ct) ? 0 : -ENOMEM; } -- cgit v1.2.3 From ba44dc04300441b47618f9933bf36e75a280e5fe Mon Sep 17 00:00:00 2001 From: Sugaya Taichi Date: Mon, 15 Apr 2019 20:31:40 +0900 Subject: serial: Add Milbeaut serial control Add Milbeaut serial control including earlycon and console. Signed-off-by: Sugaya Taichi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 26 ++ drivers/tty/serial/Makefile | 1 + drivers/tty/serial/milbeaut_usio.c | 621 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 651 insertions(+) create mode 100644 drivers/tty/serial/milbeaut_usio.c (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 6d0ab0399a72..14768ad0307e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1580,6 +1580,32 @@ config SERIAL_RDA_CONSOLE Say 'Y' here if you wish to use the RDA8810PL UART as the system console. Only earlycon is implemented currently. +config SERIAL_MILBEAUT_USIO + tristate "Milbeaut USIO/UART serial port support" + depends on ARCH_MILBEAUT || (COMPILE_TEST && OF) + default ARCH_MILBEAUT + select SERIAL_CORE + help + This selects the USIO/UART IP found in Socionext Milbeaut SoCs. + +config SERIAL_MILBEAUT_USIO_PORTS + int "Maximum number of CSIO/UART ports (1-8)" + range 1 8 + depends on SERIAL_MILBEAUT_USIO + default "4" + +config SERIAL_MILBEAUT_USIO_CONSOLE + bool "Support for console on MILBEAUT USIO/UART serial port" + depends on SERIAL_MILBEAUT_USIO=y + default y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Say 'Y' here if you wish to use a USIO/UART of Socionext Milbeaut + SoCs as the system console (the system console is the device which + receives all kernel messages and warnings and which allows logins in + single user mode). + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 40b702aaa85e..43ca2d0edde8 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o obj-$(CONFIG_SERIAL_OWL) += owl-uart.o obj-$(CONFIG_SERIAL_RDA) += rda-uart.o +obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c new file mode 100644 index 000000000000..d303b7d4bb20 --- /dev/null +++ b/drivers/tty/serial/milbeaut_usio.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Socionext Inc. + */ + +#if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USIO_NAME "mlb-usio-uart" +#define USIO_UART_DEV_NAME "ttyUSI" + +static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS]; + +#define RX 0 +#define TX 1 +static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2]; + +#define MLB_USIO_REG_SMR 0 +#define MLB_USIO_REG_SCR 1 +#define MLB_USIO_REG_ESCR 2 +#define MLB_USIO_REG_SSR 3 +#define MLB_USIO_REG_DR 4 +#define MLB_USIO_REG_BGR 6 +#define MLB_USIO_REG_FCR 12 +#define MLB_USIO_REG_FBYTE 14 + +#define MLB_USIO_SMR_SOE BIT(0) +#define MLB_USIO_SMR_SBL BIT(3) +#define MLB_USIO_SCR_TXE BIT(0) +#define MLB_USIO_SCR_RXE BIT(1) +#define MLB_USIO_SCR_TBIE BIT(2) +#define MLB_USIO_SCR_TIE BIT(3) +#define MLB_USIO_SCR_RIE BIT(4) +#define MLB_USIO_SCR_UPCL BIT(7) +#define MLB_USIO_ESCR_L_8BIT 0 +#define MLB_USIO_ESCR_L_5BIT 1 +#define MLB_USIO_ESCR_L_6BIT 2 +#define MLB_USIO_ESCR_L_7BIT 3 +#define MLB_USIO_ESCR_P BIT(3) +#define MLB_USIO_ESCR_PEN BIT(4) +#define MLB_USIO_ESCR_FLWEN BIT(7) +#define MLB_USIO_SSR_TBI BIT(0) +#define MLB_USIO_SSR_TDRE BIT(1) +#define MLB_USIO_SSR_RDRF BIT(2) +#define MLB_USIO_SSR_ORE BIT(3) +#define MLB_USIO_SSR_FRE BIT(4) +#define MLB_USIO_SSR_PE BIT(5) +#define MLB_USIO_SSR_REC BIT(7) +#define MLB_USIO_SSR_BRK BIT(8) +#define MLB_USIO_FCR_FE1 BIT(0) +#define MLB_USIO_FCR_FE2 BIT(1) +#define MLB_USIO_FCR_FCL1 BIT(2) +#define MLB_USIO_FCR_FCL2 BIT(3) +#define MLB_USIO_FCR_FSET BIT(4) +#define MLB_USIO_FCR_FTIE BIT(9) +#define MLB_USIO_FCR_FDRQ BIT(10) +#define MLB_USIO_FCR_FRIIE BIT(11) + +static void mlb_usio_stop_tx(struct uart_port *port) +{ + writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, + port->membase + MLB_USIO_REG_FCR); + writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE, + port->membase + MLB_USIO_REG_SCR); +} + +static void mlb_usio_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, + port->membase + MLB_USIO_REG_FCR); + writeb(readb(port->membase + MLB_USIO_REG_SCR) & + ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE), + port->membase + MLB_USIO_REG_SCR); + + if (port->x_char) { + writew(port->x_char, port->membase + MLB_USIO_REG_DR); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mlb_usio_stop_tx(port); + return; + } + + count = port->fifosize - + (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff); + + do { + writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + + } while (--count > 0); + + writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ, + port->membase + MLB_USIO_REG_FCR); + + writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, + port->membase + MLB_USIO_REG_SCR); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + mlb_usio_stop_tx(port); +} + +static void mlb_usio_start_tx(struct uart_port *port) +{ + u16 fcr = readw(port->membase + MLB_USIO_REG_FCR); + + writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR); + if (!(fcr & MLB_USIO_FCR_FDRQ)) + return; + + writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, + port->membase + MLB_USIO_REG_SCR); + + if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) + mlb_usio_tx_chars(port); +} + +static void mlb_usio_stop_rx(struct uart_port *port) +{ + writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE, + port->membase + MLB_USIO_REG_SCR); +} + +static void mlb_usio_enable_ms(struct uart_port *port) +{ + writeb(readb(port->membase + MLB_USIO_REG_SCR) | + MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE, + port->membase + MLB_USIO_REG_SCR); +} + +static void mlb_usio_rx_chars(struct uart_port *port) +{ + struct tty_port *ttyport = &port->state->port; + unsigned long flag = 0; + char ch = 0; + u8 status; + int max_count = 2; + + while (max_count--) { + status = readb(port->membase + MLB_USIO_REG_SSR); + + if (!(status & MLB_USIO_SSR_RDRF)) + break; + + if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE | + MLB_USIO_SSR_PE))) { + ch = readw(port->membase + MLB_USIO_REG_DR); + flag = TTY_NORMAL; + port->icount.rx++; + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, MLB_USIO_SSR_ORE, + ch, flag); + continue; + } + if (status & MLB_USIO_SSR_PE) + port->icount.parity++; + if (status & MLB_USIO_SSR_ORE) + port->icount.overrun++; + status &= port->read_status_mask; + if (status & MLB_USIO_SSR_BRK) { + flag = TTY_BREAK; + ch = 0; + } else + if (status & MLB_USIO_SSR_PE) { + flag = TTY_PARITY; + ch = 0; + } else + if (status & MLB_USIO_SSR_FRE) { + flag = TTY_FRAME; + ch = 0; + } + if (flag) + uart_insert_char(port, status, MLB_USIO_SSR_ORE, + ch, flag); + + writeb(readb(port->membase + MLB_USIO_REG_SSR) | + MLB_USIO_SSR_REC, + port->membase + MLB_USIO_REG_SSR); + + max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8; + writew(readw(port->membase + MLB_USIO_REG_FCR) | + MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, + port->membase + MLB_USIO_REG_FCR); + } + + tty_flip_buffer_push(ttyport); +} + +static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + + spin_lock(&port->lock); + mlb_usio_rx_chars(port); + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + + spin_lock(&port->lock); + if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) + mlb_usio_tx_chars(port); + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int mlb_usio_tx_empty(struct uart_port *port) +{ + return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ? + TIOCSER_TEMT : 0; +} + +static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int mlb_usio_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; + +} + +static void mlb_usio_break_ctl(struct uart_port *port, int break_state) +{ +} + +static int mlb_usio_startup(struct uart_port *port) +{ + const char *portname = to_platform_device(port->dev)->name; + unsigned long flags; + int ret, index = port->line; + unsigned char escr; + + ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq, + 0, portname, port); + if (ret) + return ret; + ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq, + 0, portname, port); + if (ret) { + free_irq(mlb_usio_irq[index][RX], port); + return ret; + } + + escr = readb(port->membase + MLB_USIO_REG_ESCR); + if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) + escr |= MLB_USIO_ESCR_FLWEN; + spin_lock_irqsave(&port->lock, flags); + writeb(0, port->membase + MLB_USIO_REG_SCR); + writeb(escr, port->membase + MLB_USIO_REG_ESCR); + writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); + writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR); + writew(0, port->membase + MLB_USIO_REG_FCR); + writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2, + port->membase + MLB_USIO_REG_FCR); + writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, + port->membase + MLB_USIO_REG_FCR); + writew(0, port->membase + MLB_USIO_REG_FBYTE); + writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); + + writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE | + MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void mlb_usio_shutdown(struct uart_port *port) +{ + int index = port->line; + + free_irq(mlb_usio_irq[index][RX], port); + free_irq(mlb_usio_irq[index][TX], port); +} + +static void mlb_usio_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int escr, smr = MLB_USIO_SMR_SOE; + unsigned long flags, baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + escr = MLB_USIO_ESCR_L_5BIT; + break; + case CS6: + escr = MLB_USIO_ESCR_L_6BIT; + break; + case CS7: + escr = MLB_USIO_ESCR_L_7BIT; + break; + case CS8: + default: + escr = MLB_USIO_ESCR_L_8BIT; + break; + } + + if (termios->c_cflag & CSTOPB) + smr |= MLB_USIO_SMR_SBL; + + if (termios->c_cflag & PARENB) { + escr |= MLB_USIO_ESCR_PEN; + if (termios->c_cflag & PARODD) + escr |= MLB_USIO_ESCR_P; + } + /* Set hard flow control */ + if (of_property_read_bool(port->dev->of_node, "auto-flow-control") || + (termios->c_cflag & CRTSCTS)) + escr |= MLB_USIO_ESCR_FLWEN; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); + if (baud > 1) + quot = port->uartclk / baud - 1; + else + quot = 0; + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); + port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF | + MLB_USIO_SSR_TDRE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; + + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; + if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR)) + port->ignore_status_mask |= MLB_USIO_SSR_ORE; + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= MLB_USIO_SSR_RDRF; + + writeb(0, port->membase + MLB_USIO_REG_SCR); + writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); + writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR); + writew(0, port->membase + MLB_USIO_REG_FCR); + writeb(smr, port->membase + MLB_USIO_REG_SMR); + writeb(escr, port->membase + MLB_USIO_REG_ESCR); + writew(quot, port->membase + MLB_USIO_REG_BGR); + writew(0, port->membase + MLB_USIO_REG_FCR); + writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 | + MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, + port->membase + MLB_USIO_REG_FCR); + writew(0, port->membase + MLB_USIO_REG_FBYTE); + writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); + writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE | + MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *mlb_usio_type(struct uart_port *port) +{ + return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL); +} + +static void mlb_usio_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_MLB_USIO; +} + +static const struct uart_ops mlb_usio_ops = { + .tx_empty = mlb_usio_tx_empty, + .set_mctrl = mlb_usio_set_mctrl, + .get_mctrl = mlb_usio_get_mctrl, + .stop_tx = mlb_usio_stop_tx, + .start_tx = mlb_usio_start_tx, + .stop_rx = mlb_usio_stop_rx, + .enable_ms = mlb_usio_enable_ms, + .break_ctl = mlb_usio_break_ctl, + .startup = mlb_usio_startup, + .shutdown = mlb_usio_shutdown, + .set_termios = mlb_usio_set_termios, + .type = mlb_usio_type, + .config_port = mlb_usio_config_port, +}; + +#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE + +static void mlb_usio_console_putchar(struct uart_port *port, int c) +{ + while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE)) + cpu_relax(); + + writew(c, port->membase + MLB_USIO_REG_DR); +} + +static void mlb_usio_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &mlb_usio_ports[co->index]; + + uart_console_write(port, s, count, mlb_usio_console_putchar); +} + +static int __init mlb_usio_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int parity = 'n'; + int flow = 'n'; + int bits = 8; + + if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS) + return -ENODEV; + + port = &mlb_usio_ports[co->index]; + if (!port->membase) + return -ENODEV; + + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) + flow = 'r'; + + return uart_set_options(port, co, baud, parity, bits, flow); +} + + +static struct uart_driver mlb_usio_uart_driver; +static struct console mlb_usio_console = { + .name = USIO_UART_DEV_NAME, + .write = mlb_usio_console_write, + .device = uart_console_device, + .setup = mlb_usio_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mlb_usio_uart_driver, +}; + +static int __init mlb_usio_console_init(void) +{ + register_console(&mlb_usio_console); + return 0; +} +console_initcall(mlb_usio_console_init); + + +static void mlb_usio_early_console_write(struct console *co, const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + uart_console_write(&dev->port, s, count, mlb_usio_console_putchar); +} + +static int __init mlb_usio_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + device->con->write = mlb_usio_early_console_write; + return 0; +} + +OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart", + mlb_usio_early_console_setup); + +#define USIO_CONSOLE (&mlb_usio_console) +#else +#define USIO_CONSOLE NULL +#endif + +static struct uart_driver mlb_usio_uart_driver = { + .owner = THIS_MODULE, + .driver_name = USIO_NAME, + .dev_name = USIO_UART_DEV_NAME, + .cons = USIO_CONSOLE, + .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS, +}; + +static int mlb_usio_probe(struct platform_device *pdev) +{ + struct clk *clk = devm_clk_get(&pdev->dev, 0); + struct uart_port *port; + struct resource *res; + int index = 0; + int ret; + + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Missing clock\n"); + return PTR_ERR(clk); + } + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "Clock enable failed: %d\n", ret); + return ret; + } + of_property_read_u32(pdev->dev.of_node, "index", &index); + port = &mlb_usio_ports[index]; + + port->private_data = (void *)clk; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Missing regs\n"); + ret = -ENODEV; + goto failed; + } + port->mapbase = res->start; + port->membase = ioremap(res->start, (res->end - res->start + 1)); + port->membase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + ret = platform_get_irq_byname(pdev, "rx"); + mlb_usio_irq[index][RX] = ret; + + ret = platform_get_irq_byname(pdev, "tx"); + mlb_usio_irq[index][TX] = ret; + + port->irq = mlb_usio_irq[index][RX]; + port->uartclk = clk_get_rate(clk); + port->fifosize = 128; + port->iotype = UPIO_MEM32; + port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI; + port->line = index; + port->ops = &mlb_usio_ops; + port->dev = &pdev->dev; + + ret = uart_add_one_port(&mlb_usio_uart_driver, port); + if (ret) { + dev_err(&pdev->dev, "Adding port failed: %d\n", ret); + goto failed1; + } + return 0; + +failed1: + iounmap(port->membase); + +failed: + clk_disable_unprepare(clk); + clk_put(clk); + + return ret; +} + +static int mlb_usio_remove(struct platform_device *pdev) +{ + struct uart_port *port = &mlb_usio_ports[pdev->id]; + struct clk *clk = port->private_data; + + uart_remove_one_port(&mlb_usio_uart_driver, port); + clk_disable_unprepare(clk); + clk_put(clk); + + return 0; +} + +static const struct of_device_id mlb_usio_dt_ids[] = { + { .compatible = "socionext,milbeaut-usio-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); + +static struct platform_driver mlb_usio_driver = { + .probe = mlb_usio_probe, + .remove = mlb_usio_remove, + .driver = { + .name = USIO_NAME, + .of_match_table = mlb_usio_dt_ids, + }, +}; + +static int __init mlb_usio_init(void) +{ + int ret = uart_register_driver(&mlb_usio_uart_driver); + + if (ret) { + pr_err("%s: uart registration failed: %d\n", __func__, ret); + return ret; + } + ret = platform_driver_register(&mlb_usio_driver); + if (ret) { + uart_unregister_driver(&mlb_usio_uart_driver); + pr_err("%s: drv registration failed: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static void __exit mlb_usio_exit(void) +{ + platform_driver_unregister(&mlb_usio_driver); + uart_unregister_driver(&mlb_usio_uart_driver); +} + +module_init(mlb_usio_init); +module_exit(mlb_usio_exit); + +MODULE_AUTHOR("SOCIONEXT"); +MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 6009ee2c2e99..a51c747b52c5 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -287,4 +287,7 @@ /* RDA UART */ #define PORT_RDA 118 +/* Socionext Milbeaut UART */ +#define PORT_MLB_USIO 119 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3 From 0d3cb6f6c67aab6284921619c838382fb6e4f5db Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 3 Apr 2019 09:40:53 +0200 Subject: Revert "tty: fix NULL pointer issue when tty_port ops is not set" This reverts commit f4e68d58cf2b20a581759bbc7228052534652673. TTY drivers using the tty-port abstraction all provide a pointer to a set of port operations, which specifically cannot be NULL (or we'd find out at first attempt to open a port). Revert the recent commit which added unnecessary NULL-checks and whose commit message indicated that it was fixing a real problem, which it did not. Note that even the two tty drivers for virtual devices currently providing an empty set of operations probably should be implementing at least some of the callbacks. Cc: Fabien Dessenne Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index a9e12b3bc31d..044c3cbdcfa4 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) if (tty && C_HUPCL(tty)) tty_port_lower_dtr_rts(port); - if (port->ops && port->ops->shutdown) + if (port->ops->shutdown) port->ops->shutdown(port); } out: @@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); */ int tty_port_carrier_raised(struct tty_port *port) { - if (!port->ops || !port->ops->carrier_raised) + if (port->ops->carrier_raised == NULL) return 1; return port->ops->carrier_raised(port); } @@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised); */ void tty_port_raise_dtr_rts(struct tty_port *port) { - if (port->ops && port->ops->dtr_rts) + if (port->ops->dtr_rts) port->ops->dtr_rts(port, 1); } EXPORT_SYMBOL(tty_port_raise_dtr_rts); @@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); */ void tty_port_lower_dtr_rts(struct tty_port *port) { - if (port->ops && port->ops->dtr_rts) + if (port->ops->dtr_rts) port->ops->dtr_rts(port, 0); } EXPORT_SYMBOL(tty_port_lower_dtr_rts); @@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, if (!tty_port_initialized(port)) { clear_bit(TTY_IO_ERROR, &tty->flags); - if (port->ops && port->ops->activate) { + if (port->ops->activate) { int retval = port->ops->activate(port, tty); if (retval) { mutex_unlock(&port->mutex); -- cgit v1.2.3 From 89bb1e1ee529d9d06ca694ba22a35dc2a3d6ac67 Mon Sep 17 00:00:00 2001 From: Reinis Danne Date: Thu, 11 Apr 2019 14:50:54 +0300 Subject: tty: vt: keyboard: Allow Unicode compose base char MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass Unicode character to k_unicode handler function to honor possible accent_table definition. With introduction of KDSKBDIACRUC ioctl, it has been possible to set compose characters (diacr, base and result) to any Unicode character (below 0xf000 code point) as defined in struct kbdiacruc. But it does not work with characters beyond Latin-1 as base, because they are printed early and not passed to any of the handler functions, thus breaking compose and dead keys. It affects keyboard layouts defining such characters on level 1 and relying on dead keys to access level 3 (e.g., lv-modern with some modifications for extra characters on level 3 for ē, ā and ī keys). Signed-off-by: Reinis Danne Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 0617e87ab343..515fc095e3b4 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1450,7 +1450,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) KBD_UNICODE, ¶m); if (rc != NOTIFY_STOP) if (down && !raw_mode) - to_utf8(vc, keysym); + k_unicode(vc, keysym, !down); return; } -- cgit v1.2.3 From 3117ff13f104e98b05b61e19cc754d1377e92e15 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Wed, 3 Apr 2019 13:33:27 +0200 Subject: tty: Add NULL TTY driver If no console driver is enabled (or if a non-present driver is selected with something like console=null in an attempt to disable the console), opening /dev/console errors out, and init scripts and other userspace code that relies on the existence of a console will fail. Symlinking /dev/null to /dev/console does not solve the problem since /dev/null does not behave like a real TTY. To just provide a dummy console to userspace when no console driver is available or desired, add a ttynull driver which simply discards all writes. It can be chosen on the command line in the standard way, i.e. with console=ttynull. Signed-off-by: Vincent Whitchurch Signed-off-by: Greg Kroah-Hartman --- drivers/tty/Kconfig | 14 +++++++ drivers/tty/Makefile | 1 + drivers/tty/ttynull.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 drivers/tty/ttynull.c (limited to 'drivers') diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 0ebb8d6ab341..80f65767aacf 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -374,6 +374,20 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE there simply will be no early console output. This is true also if you don't boot under a hypervisor at all. +config NULL_TTY + tristate "NULL TTY driver" + help + Say Y here if you want a NULL TTY which simply discards messages. + + This is useful to allow userspace applications which expect a console + device to work without modifications even when no console is + available or desired. + + In order to use this driver, you should redirect the console to this + TTY, or boot the kernel with console=ttynull. + + If unsure, say N. + config GOLDFISH_TTY tristate "Goldfish TTY Driver" depends on GOLDFISH diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index c72cafdf32b4..020b1cd9294f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_ISI) += isicom.o obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o obj-$(CONFIG_NOZOMI) += nozomi.o +obj-$(CONFIG_NULL_TTY) += ttynull.o obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o diff --git a/drivers/tty/ttynull.c b/drivers/tty/ttynull.c new file mode 100644 index 000000000000..17f05b7eb6d3 --- /dev/null +++ b/drivers/tty/ttynull.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Axis Communications AB + * + * Based on ttyprintk.c: + * Copyright (C) 2010 Samo Pogacnik + */ + +#include +#include +#include + +static const struct tty_port_operations ttynull_port_ops; +static struct tty_driver *ttynull_driver; +static struct tty_port ttynull_port; + +static int ttynull_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(&ttynull_port, tty, filp); +} + +static void ttynull_close(struct tty_struct *tty, struct file *filp) +{ + tty_port_close(&ttynull_port, tty, filp); +} + +static void ttynull_hangup(struct tty_struct *tty) +{ + tty_port_hangup(&ttynull_port); +} + +static int ttynull_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + return count; +} + +static int ttynull_write_room(struct tty_struct *tty) +{ + return 65536; +} + +static const struct tty_operations ttynull_ops = { + .open = ttynull_open, + .close = ttynull_close, + .hangup = ttynull_hangup, + .write = ttynull_write, + .write_room = ttynull_write_room, +}; + +static struct tty_driver *ttynull_device(struct console *c, int *index) +{ + *index = 0; + return ttynull_driver; +} + +static struct console ttynull_console = { + .name = "ttynull", + .device = ttynull_device, +}; + +static int __init ttynull_init(void) +{ + struct tty_driver *driver; + int ret; + + driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE); + if (IS_ERR(driver)) + return PTR_ERR(driver); + + tty_port_init(&ttynull_port); + ttynull_port.ops = &ttynull_port_ops; + + driver->driver_name = "ttynull"; + driver->name = "ttynull"; + driver->type = TTY_DRIVER_TYPE_CONSOLE; + driver->init_termios = tty_std_termios; + driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; + tty_set_operations(driver, &ttynull_ops); + tty_port_link_device(&ttynull_port, driver, 0); + + ret = tty_register_driver(driver); + if (ret < 0) { + put_tty_driver(driver); + tty_port_destroy(&ttynull_port); + return ret; + } + + ttynull_driver = driver; + register_console(&ttynull_console); + + return 0; +} + +static void __exit ttynull_exit(void) +{ + unregister_console(&ttynull_console); + tty_unregister_driver(ttynull_driver); + put_tty_driver(ttynull_driver); + tty_port_destroy(&ttynull_port); +} + +module_init(ttynull_init); +module_exit(ttynull_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c7a6b9e4df4c5d9e95e38766923f98117b045c39 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Thu, 28 Mar 2019 08:47:33 +0530 Subject: tty:serial_core: Spelling mistake fix spelling mistake "overriden" -> "overridden" This fix resolves warning reported by checkpatch tool. Signed-off-by: Hariprasad Kelam Reviewed-by: Mukesh Ojha Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 351843f847c0..69f48717546b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1514,7 +1514,7 @@ static void uart_set_termios(struct tty_struct *tty, } uart_change_speed(tty, state, old_termios); - /* reload cflag from termios; port driver may have overriden flags */ + /* reload cflag from termios; port driver may have overridden flags */ cflag = tty->termios.c_cflag; /* Handle transition to B0 status */ -- cgit v1.2.3 From 6bc3703dcf0e6891a55987cbae0823c8c185c5e5 Mon Sep 17 00:00:00 2001 From: Sugaya Taichi Date: Thu, 18 Apr 2019 15:21:25 +0900 Subject: serial: Fix using plain integer instead of Null pointer Fix build warning that using plain integer as Null pointer. This is reported by kbuild test robot. Fixes: ba44dc043004 ("serial: Add Milbeaut serial control") Signed-off-by: Sugaya Taichi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/milbeaut_usio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index d303b7d4bb20..4a10604211be 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -500,7 +500,7 @@ static struct uart_driver mlb_usio_uart_driver = { static int mlb_usio_probe(struct platform_device *pdev) { - struct clk *clk = devm_clk_get(&pdev->dev, 0); + struct clk *clk = devm_clk_get(&pdev->dev, NULL); struct uart_port *port; struct resource *res; int index = 0; -- cgit v1.2.3 From 4cdd17ba1dff20ffc99fdbd2e6f0201fc7fe67df Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 17 Apr 2019 10:58:53 +0200 Subject: TTY: serial_core, add ->install We need to compute the uart state only on the first open. This is usually what is done in the ->install hook. serial_core used to do this in ->open on every open. So move it to ->install. As a side effect, it ensures the state is set properly in the window after tty_init_dev is called, but before uart_open. This fixes a bunch of races between tty_open and flush_to_ldisc we were dealing with recently. One of such bugs was attempted to fix in commit fedb5760648a (serial: fix race between flush_to_ldisc and tty_open), but it only took care of a couple of functions (uart_start and uart_unthrottle). I was able to reproduce the crash on a SLE system, but in uart_write_room which is also called from flush_to_ldisc via process_echoes. I was *unable* to reproduce the bug locally. It is due to having this patch in my queue since 2012! general protection fault: 0000 [#1] SMP KASAN PTI CPU: 1 PID: 5 Comm: kworker/u4:0 Tainted: G L 4.12.14-396-default #1 SLE15-SP1 (unreleased) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c89-prebuilt.qemu.org 04/01/2014 Workqueue: events_unbound flush_to_ldisc task: ffff8800427d8040 task.stack: ffff8800427f0000 RIP: 0010:uart_write_room+0xc4/0x590 RSP: 0018:ffff8800427f7088 EFLAGS: 00010202 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 000000000000002f RSI: 00000000000000ee RDI: ffff88003888bd90 RBP: ffffffffb9545850 R08: 0000000000000001 R09: 0000000000000400 R10: ffff8800427d825c R11: 000000000000006e R12: 1ffff100084fee12 R13: ffffc900004c5000 R14: ffff88003888bb28 R15: 0000000000000178 FS: 0000000000000000(0000) GS:ffff880043300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000561da0794148 CR3: 000000000ebf4000 CR4: 00000000000006e0 Call Trace: tty_write_room+0x6d/0xc0 __process_echoes+0x55/0x870 n_tty_receive_buf_common+0x105e/0x26d0 tty_ldisc_receive_buf+0xb7/0x1c0 tty_port_default_receive_buf+0x107/0x180 flush_to_ldisc+0x35d/0x5c0 ... 0 in rbx means tty->driver_data is NULL in uart_write_room. 0x178 is tried to be dereferenced (0x178 >> 3 is 0x2f in rdx) at uart_write_room+0xc4. 0x178 is exactly (struct uart_state *)NULL->refcount used in uart_port_lock from uart_write_room. So revert the upstream commit here as my local patch should fix the whole family. Signed-off-by: Jiri Slaby Cc: Li RongQing Cc: Wang Li Cc: Zhang Yu Cc: Greg Kroah-Hartman Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 69f48717546b..0decb0bf991d 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -130,9 +130,6 @@ static void uart_start(struct tty_struct *tty) struct uart_port *port; unsigned long flags; - if (!state) - return; - port = uart_port_lock(state, flags); __uart_start(tty); uart_port_unlock(port, flags); @@ -730,9 +727,6 @@ static void uart_unthrottle(struct tty_struct *tty) upstat_t mask = UPSTAT_SYNC_FIFO; struct uart_port *port; - if (!state) - return; - port = uart_port_ref(state); if (!port) return; @@ -1747,6 +1741,16 @@ static void uart_dtr_rts(struct tty_port *port, int raise) uart_port_deref(uport); } +static int uart_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct uart_driver *drv = driver->driver_state; + struct uart_state *state = drv->state + tty->index; + + tty->driver_data = state; + + return tty_standard_install(driver, tty); +} + /* * Calls to uart_open are serialised by the tty_lock in * drivers/tty/tty_io.c:tty_open() @@ -1759,11 +1763,8 @@ static void uart_dtr_rts(struct tty_port *port, int raise) */ static int uart_open(struct tty_struct *tty, struct file *filp) { - struct uart_driver *drv = tty->driver->driver_state; - int retval, line = tty->index; - struct uart_state *state = drv->state + line; - - tty->driver_data = state; + struct uart_state *state = tty->driver_data; + int retval; retval = tty_port_open(&state->port, tty, filp); if (retval > 0) @@ -2448,6 +2449,7 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) #endif static const struct tty_operations uart_ops = { + .install = uart_install, .open = uart_open, .close = uart_close, .write = uart_write, -- cgit v1.2.3 From 3e51ceea1006186a73367a78bb5cab76cf39d1bd Mon Sep 17 00:00:00 2001 From: Su Bao Cheng Date: Thu, 18 Apr 2019 11:15:04 +0200 Subject: serial: 8250_exar: Adjust IOT2000 matching Since there are more IOT2040 variants with identical hardware but different asset tags, the asset tag matching should be adjusted to support them. As only the IOT2040 variants have the Exar chip on board, matching on their board name is enough. In the future there will be no other devices with the "SIMATIC IOT2000" DMI board name but different hardware. Signed-off-by: Su Bao Cheng Reviewed-by: Jan Kiszka Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 0089aa305ef9..edd6dfe055bf 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -361,12 +361,15 @@ static const struct exar8250_platform iot2040_platform = { .register_gpio = iot2040_register_gpio, }; +/* + * For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device, + * IOT2020 doesn't have. Therefore it is sufficient to match on the common + * board name after the device was found. + */ static const struct dmi_system_id exar_platforms[] = { { .matches = { DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), - DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, - "6ES7647-0AA00-1YA2"), }, .driver_data = (void *)&iot2040_platform, }, -- cgit v1.2.3 From f137401780278c5a257945330936607701fbd2fd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Apr 2019 10:27:22 -0300 Subject: docs: serial: convert docs to ReST and rename to *.rst The converted files are focused at the Kernel internal API, so, this is a good candidate for the kernel API set of books. The conversion is actually: - add blank lines and identation in order to identify paragraphs; - fix tables markups; - add some lists markups; - mark literal blocks; - adjust title markups. At its new index.rst, let's add a :orphan: while this is not linked to the main index.rst file, in order to avoid build warnings. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/README.cycladesZ | 8 - Documentation/serial/cyclades_z.rst | 11 + Documentation/serial/driver | 486 ------------------------- Documentation/serial/driver.rst | 549 ++++++++++++++++++++++++++++ Documentation/serial/index.rst | 32 ++ Documentation/serial/moxa-smartio | 523 --------------------------- Documentation/serial/moxa-smartio.rst | 615 ++++++++++++++++++++++++++++++++ Documentation/serial/n_gsm.rst | 103 ++++++ Documentation/serial/n_gsm.txt | 96 ----- Documentation/serial/rocket.rst | 185 ++++++++++ Documentation/serial/rocket.txt | 189 ---------- Documentation/serial/serial-iso7816.rst | 90 +++++ Documentation/serial/serial-iso7816.txt | 83 ----- Documentation/serial/serial-rs485.rst | 103 ++++++ Documentation/serial/serial-rs485.txt | 95 ----- Documentation/serial/tty.rst | 328 +++++++++++++++++ Documentation/serial/tty.txt | 313 ---------------- MAINTAINERS | 4 +- drivers/tty/Kconfig | 4 +- drivers/tty/serial/ucc_uart.c | 2 +- include/linux/serial_core.h | 2 +- 21 files changed, 2022 insertions(+), 1799 deletions(-) delete mode 100644 Documentation/serial/README.cycladesZ create mode 100644 Documentation/serial/cyclades_z.rst delete mode 100644 Documentation/serial/driver create mode 100644 Documentation/serial/driver.rst create mode 100644 Documentation/serial/index.rst delete mode 100644 Documentation/serial/moxa-smartio create mode 100644 Documentation/serial/moxa-smartio.rst create mode 100644 Documentation/serial/n_gsm.rst delete mode 100644 Documentation/serial/n_gsm.txt create mode 100644 Documentation/serial/rocket.rst delete mode 100644 Documentation/serial/rocket.txt create mode 100644 Documentation/serial/serial-iso7816.rst delete mode 100644 Documentation/serial/serial-iso7816.txt create mode 100644 Documentation/serial/serial-rs485.rst delete mode 100644 Documentation/serial/serial-rs485.txt create mode 100644 Documentation/serial/tty.rst delete mode 100644 Documentation/serial/tty.txt (limited to 'drivers') diff --git a/Documentation/serial/README.cycladesZ b/Documentation/serial/README.cycladesZ deleted file mode 100644 index 024a69443cc2..000000000000 --- a/Documentation/serial/README.cycladesZ +++ /dev/null @@ -1,8 +0,0 @@ - -The Cyclades-Z must have firmware loaded onto the card before it will -operate. This operation should be performed during system startup, - -The firmware, loader program and the latest device driver code are -available from Cyclades at - ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/ - diff --git a/Documentation/serial/cyclades_z.rst b/Documentation/serial/cyclades_z.rst new file mode 100644 index 000000000000..532ff67e2f1c --- /dev/null +++ b/Documentation/serial/cyclades_z.rst @@ -0,0 +1,11 @@ +================ +Cyclades-Z notes +================ + +The Cyclades-Z must have firmware loaded onto the card before it will +operate. This operation should be performed during system startup, + +The firmware, loader program and the latest device driver code are +available from Cyclades at + + ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/ diff --git a/Documentation/serial/driver b/Documentation/serial/driver deleted file mode 100644 index 86e47c19a924..000000000000 --- a/Documentation/serial/driver +++ /dev/null @@ -1,486 +0,0 @@ - - Low Level Serial API - -------------------- - - -This document is meant as a brief overview of some aspects of the new serial -driver. It is not complete, any questions you have should be directed to - - -The reference implementation is contained within amba-pl011.c. - - - -Low Level Serial Hardware Driver --------------------------------- - -The low level serial hardware driver is responsible for supplying port -information (defined by uart_port) and a set of control methods (defined -by uart_ops) to the core serial driver. The low level driver is also -responsible for handling interrupts for the port, and providing any -console support. - - -Console Support ---------------- - -The serial core provides a few helper functions. This includes identifing -the correct port structure (via uart_get_console) and decoding command line -arguments (uart_parse_options). - -There is also a helper function (uart_console_write) which performs a -character by character write, translating newlines to CRLF sequences. -Driver writers are recommended to use this function rather than implementing -their own version. - - -Locking -------- - -It is the responsibility of the low level hardware driver to perform the -necessary locking using port->lock. There are some exceptions (which -are described in the uart_ops listing below.) - -There are two locks. A per-port spinlock, and an overall semaphore. - -From the core driver perspective, the port->lock locks the following -data: - - port->mctrl - port->icount - port->state->xmit.head (circ_buf->head) - port->state->xmit.tail (circ_buf->tail) - -The low level driver is free to use this lock to provide any additional -locking. - -The port_sem semaphore is used to protect against ports being added/ -removed or reconfigured at inappropriate times. Since v2.6.27, this -semaphore has been the 'mutex' member of the tty_port struct, and -commonly referred to as the port mutex. - - -uart_ops --------- - -The uart_ops structure is the main interface between serial_core and the -hardware specific driver. It contains all the methods to control the -hardware. - - tx_empty(port) - This function tests whether the transmitter fifo and shifter - for the port described by 'port' is empty. If it is empty, - this function should return TIOCSER_TEMT, otherwise return 0. - If the port does not support this operation, then it should - return TIOCSER_TEMT. - - Locking: none. - Interrupts: caller dependent. - This call must not sleep - - set_mctrl(port, mctrl) - This function sets the modem control lines for port described - by 'port' to the state described by mctrl. The relevant bits - of mctrl are: - - TIOCM_RTS RTS signal. - - TIOCM_DTR DTR signal. - - TIOCM_OUT1 OUT1 signal. - - TIOCM_OUT2 OUT2 signal. - - TIOCM_LOOP Set the port into loopback mode. - If the appropriate bit is set, the signal should be driven - active. If the bit is clear, the signal should be driven - inactive. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - get_mctrl(port) - Returns the current state of modem control inputs. The state - of the outputs should not be returned, since the core keeps - track of their state. The state information should include: - - TIOCM_CAR state of DCD signal - - TIOCM_CTS state of CTS signal - - TIOCM_DSR state of DSR signal - - TIOCM_RI state of RI signal - The bit is set if the signal is currently driven active. If - the port does not support CTS, DCD or DSR, the driver should - indicate that the signal is permanently active. If RI is - not available, the signal should not be indicated as active. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - stop_tx(port) - Stop transmitting characters. This might be due to the CTS - line becoming inactive or the tty layer indicating we want - to stop transmission due to an XOFF character. - - The driver should stop transmitting characters as soon as - possible. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - start_tx(port) - Start transmitting characters. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - throttle(port) - Notify the serial driver that input buffers for the line discipline are - close to full, and it should somehow signal that no more characters - should be sent to the serial port. - This will be called only if hardware assisted flow control is enabled. - - Locking: serialized with .unthrottle() and termios modification by the - tty layer. - - unthrottle(port) - Notify the serial driver that characters can now be sent to the serial - port without fear of overrunning the input buffers of the line - disciplines. - This will be called only if hardware assisted flow control is enabled. - - Locking: serialized with .throttle() and termios modification by the - tty layer. - - send_xchar(port,ch) - Transmit a high priority character, even if the port is stopped. - This is used to implement XON/XOFF flow control and tcflow(). If - the serial driver does not implement this function, the tty core - will append the character to the circular buffer and then call - start_tx() / stop_tx() to flush the data out. - - Do not transmit if ch == '\0' (__DISABLED_CHAR). - - Locking: none. - Interrupts: caller dependent. - - stop_rx(port) - Stop receiving characters; the port is in the process of - being closed. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - enable_ms(port) - Enable the modem status interrupts. - - This method may be called multiple times. Modem status - interrupts should be disabled when the shutdown method is - called. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - break_ctl(port,ctl) - Control the transmission of a break signal. If ctl is - nonzero, the break signal should be transmitted. The signal - should be terminated when another call is made with a zero - ctl. - - Locking: caller holds tty_port->mutex - - startup(port) - Grab any interrupt resources and initialise any low level driver - state. Enable the port for reception. It should not activate - RTS nor DTR; this will be done via a separate call to set_mctrl. - - This method will only be called when the port is initially opened. - - Locking: port_sem taken. - Interrupts: globally disabled. - - shutdown(port) - Disable the port, disable any break condition that may be in - effect, and free any interrupt resources. It should not disable - RTS nor DTR; this will have already been done via a separate - call to set_mctrl. - - Drivers must not access port->state once this call has completed. - - This method will only be called when there are no more users of - this port. - - Locking: port_sem taken. - Interrupts: caller dependent. - - flush_buffer(port) - Flush any write buffers, reset any DMA state and stop any - ongoing DMA transfers. - - This will be called whenever the port->state->xmit circular - buffer is cleared. - - Locking: port->lock taken. - Interrupts: locally disabled. - This call must not sleep - - set_termios(port,termios,oldtermios) - Change the port parameters, including word length, parity, stop - bits. Update read_status_mask and ignore_status_mask to indicate - the types of events we are interested in receiving. Relevant - termios->c_cflag bits are: - CSIZE - word size - CSTOPB - 2 stop bits - PARENB - parity enable - PARODD - odd parity (when PARENB is in force) - CREAD - enable reception of characters (if not set, - still receive characters from the port, but - throw them away. - CRTSCTS - if set, enable CTS status change reporting - CLOCAL - if not set, enable modem status change - reporting. - Relevant termios->c_iflag bits are: - INPCK - enable frame and parity error events to be - passed to the TTY layer. - BRKINT - PARMRK - both of these enable break events to be - passed to the TTY layer. - - IGNPAR - ignore parity and framing errors - IGNBRK - ignore break errors, If IGNPAR is also - set, ignore overrun errors as well. - The interaction of the iflag bits is as follows (parity error - given as an example): - Parity error INPCK IGNPAR - n/a 0 n/a character received, marked as - TTY_NORMAL - None 1 n/a character received, marked as - TTY_NORMAL - Yes 1 0 character received, marked as - TTY_PARITY - Yes 1 1 character discarded - - Other flags may be used (eg, xon/xoff characters) if your - hardware supports hardware "soft" flow control. - - Locking: caller holds tty_port->mutex - Interrupts: caller dependent. - This call must not sleep - - set_ldisc(port,termios) - Notifier for discipline change. See Documentation/serial/tty.txt. - - Locking: caller holds tty_port->mutex - - pm(port,state,oldstate) - Perform any power management related activities on the specified - port. State indicates the new state (defined by - enum uart_pm_state), oldstate indicates the previous state. - - This function should not be used to grab any resources. - - This will be called when the port is initially opened and finally - closed, except when the port is also the system console. This - will occur even if CONFIG_PM is not set. - - Locking: none. - Interrupts: caller dependent. - - type(port) - Return a pointer to a string constant describing the specified - port, or return NULL, in which case the string 'unknown' is - substituted. - - Locking: none. - Interrupts: caller dependent. - - release_port(port) - Release any memory and IO region resources currently in use by - the port. - - Locking: none. - Interrupts: caller dependent. - - request_port(port) - Request any memory and IO region resources required by the port. - If any fail, no resources should be registered when this function - returns, and it should return -EBUSY on failure. - - Locking: none. - Interrupts: caller dependent. - - config_port(port,type) - Perform any autoconfiguration steps required for the port. `type` - contains a bit mask of the required configuration. UART_CONFIG_TYPE - indicates that the port requires detection and identification. - port->type should be set to the type found, or PORT_UNKNOWN if - no port was detected. - - UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, - which should be probed using standard kernel autoprobing techniques. - This is not necessary on platforms where ports have interrupts - internally hard wired (eg, system on a chip implementations). - - Locking: none. - Interrupts: caller dependent. - - verify_port(port,serinfo) - Verify the new serial port information contained within serinfo is - suitable for this port type. - - Locking: none. - Interrupts: caller dependent. - - ioctl(port,cmd,arg) - Perform any port specific IOCTLs. IOCTL commands must be defined - using the standard numbering system found in - - Locking: none. - Interrupts: caller dependent. - - poll_init(port) - Called by kgdb to perform the minimal hardware initialization needed - to support poll_put_char() and poll_get_char(). Unlike ->startup() - this should not request interrupts. - - Locking: tty_mutex and tty_port->mutex taken. - Interrupts: n/a. - - poll_put_char(port,ch) - Called by kgdb to write a single character directly to the serial - port. It can and should block until there is space in the TX FIFO. - - Locking: none. - Interrupts: caller dependent. - This call must not sleep - - poll_get_char(port) - Called by kgdb to read a single character directly from the serial - port. If data is available, it should be returned; otherwise - the function should return NO_POLL_CHAR immediately. - - Locking: none. - Interrupts: caller dependent. - This call must not sleep - -Other functions ---------------- - -uart_update_timeout(port,cflag,baud) - Update the FIFO drain timeout, port->timeout, according to the - number of bits, parity, stop bits and baud rate. - - Locking: caller is expected to take port->lock - Interrupts: n/a - -uart_get_baud_rate(port,termios,old,min,max) - Return the numeric baud rate for the specified termios, taking - account of the special 38400 baud "kludge". The B0 baud rate - is mapped to 9600 baud. - - If the baud rate is not within min..max, then if old is non-NULL, - the original baud rate will be tried. If that exceeds the - min..max constraint, 9600 baud will be returned. termios will - be updated to the baud rate in use. - - Note: min..max must always allow 9600 baud to be selected. - - Locking: caller dependent. - Interrupts: n/a - -uart_get_divisor(port,baud) - Return the divisor (baud_base / baud) for the specified baud - rate, appropriately rounded. - - If 38400 baud and custom divisor is selected, return the - custom divisor instead. - - Locking: caller dependent. - Interrupts: n/a - -uart_match_port(port1,port2) - This utility function can be used to determine whether two - uart_port structures describe the same port. - - Locking: n/a - Interrupts: n/a - -uart_write_wakeup(port) - A driver is expected to call this function when the number of - characters in the transmit buffer have dropped below a threshold. - - Locking: port->lock should be held. - Interrupts: n/a - -uart_register_driver(drv) - Register a uart driver with the core driver. We in turn register - with the tty layer, and initialise the core driver per-port state. - - drv->port should be NULL, and the per-port structures should be - registered using uart_add_one_port after this call has succeeded. - - Locking: none - Interrupts: enabled - -uart_unregister_driver() - Remove all references to a driver from the core driver. The low - level driver must have removed all its ports via the - uart_remove_one_port() if it registered them with uart_add_one_port(). - - Locking: none - Interrupts: enabled - -uart_suspend_port() - -uart_resume_port() - -uart_add_one_port() - -uart_remove_one_port() - -Other notes ------------ - -It is intended some day to drop the 'unused' entries from uart_port, and -allow low level drivers to register their own individual uart_port's with -the core. This will allow drivers to use uart_port as a pointer to a -structure containing both the uart_port entry with their own extensions, -thus: - - struct my_port { - struct uart_port port; - int my_stuff; - }; - -Modem control lines via GPIO ----------------------------- - -Some helpers are provided in order to set/get modem control lines via GPIO. - -mctrl_gpio_init(port, idx): - This will get the {cts,rts,...}-gpios from device tree if they are - present and request them, set direction etc, and return an - allocated structure. devm_* functions are used, so there's no need - to call mctrl_gpio_free(). - As this sets up the irq handling make sure to not handle changes to the - gpio input lines in your driver, too. - -mctrl_gpio_free(dev, gpios): - This will free the requested gpios in mctrl_gpio_init(). - As devm_* functions are used, there's generally no need to call - this function. - -mctrl_gpio_to_gpiod(gpios, gidx) - This returns the gpio_desc structure associated to the modem line - index. - -mctrl_gpio_set(gpios, mctrl): - This will sets the gpios according to the mctrl state. - -mctrl_gpio_get(gpios, mctrl): - This will update mctrl with the gpios values. - -mctrl_gpio_enable_ms(gpios): - Enables irqs and handling of changes to the ms lines. - -mctrl_gpio_disable_ms(gpios): - Disables irqs and handling of changes to the ms lines. diff --git a/Documentation/serial/driver.rst b/Documentation/serial/driver.rst new file mode 100644 index 000000000000..4537119bf624 --- /dev/null +++ b/Documentation/serial/driver.rst @@ -0,0 +1,549 @@ +==================== +Low Level Serial API +==================== + + +This document is meant as a brief overview of some aspects of the new serial +driver. It is not complete, any questions you have should be directed to + + +The reference implementation is contained within amba-pl011.c. + + + +Low Level Serial Hardware Driver +-------------------------------- + +The low level serial hardware driver is responsible for supplying port +information (defined by uart_port) and a set of control methods (defined +by uart_ops) to the core serial driver. The low level driver is also +responsible for handling interrupts for the port, and providing any +console support. + + +Console Support +--------------- + +The serial core provides a few helper functions. This includes identifing +the correct port structure (via uart_get_console) and decoding command line +arguments (uart_parse_options). + +There is also a helper function (uart_console_write) which performs a +character by character write, translating newlines to CRLF sequences. +Driver writers are recommended to use this function rather than implementing +their own version. + + +Locking +------- + +It is the responsibility of the low level hardware driver to perform the +necessary locking using port->lock. There are some exceptions (which +are described in the uart_ops listing below.) + +There are two locks. A per-port spinlock, and an overall semaphore. + +From the core driver perspective, the port->lock locks the following +data:: + + port->mctrl + port->icount + port->state->xmit.head (circ_buf->head) + port->state->xmit.tail (circ_buf->tail) + +The low level driver is free to use this lock to provide any additional +locking. + +The port_sem semaphore is used to protect against ports being added/ +removed or reconfigured at inappropriate times. Since v2.6.27, this +semaphore has been the 'mutex' member of the tty_port struct, and +commonly referred to as the port mutex. + + +uart_ops +-------- + +The uart_ops structure is the main interface between serial_core and the +hardware specific driver. It contains all the methods to control the +hardware. + + tx_empty(port) + This function tests whether the transmitter fifo and shifter + for the port described by 'port' is empty. If it is empty, + this function should return TIOCSER_TEMT, otherwise return 0. + If the port does not support this operation, then it should + return TIOCSER_TEMT. + + Locking: none. + + Interrupts: caller dependent. + + This call must not sleep + + set_mctrl(port, mctrl) + This function sets the modem control lines for port described + by 'port' to the state described by mctrl. The relevant bits + of mctrl are: + + - TIOCM_RTS RTS signal. + - TIOCM_DTR DTR signal. + - TIOCM_OUT1 OUT1 signal. + - TIOCM_OUT2 OUT2 signal. + - TIOCM_LOOP Set the port into loopback mode. + + If the appropriate bit is set, the signal should be driven + active. If the bit is clear, the signal should be driven + inactive. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + get_mctrl(port) + Returns the current state of modem control inputs. The state + of the outputs should not be returned, since the core keeps + track of their state. The state information should include: + + - TIOCM_CAR state of DCD signal + - TIOCM_CTS state of CTS signal + - TIOCM_DSR state of DSR signal + - TIOCM_RI state of RI signal + + The bit is set if the signal is currently driven active. If + the port does not support CTS, DCD or DSR, the driver should + indicate that the signal is permanently active. If RI is + not available, the signal should not be indicated as active. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + stop_tx(port) + Stop transmitting characters. This might be due to the CTS + line becoming inactive or the tty layer indicating we want + to stop transmission due to an XOFF character. + + The driver should stop transmitting characters as soon as + possible. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + start_tx(port) + Start transmitting characters. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + throttle(port) + Notify the serial driver that input buffers for the line discipline are + close to full, and it should somehow signal that no more characters + should be sent to the serial port. + This will be called only if hardware assisted flow control is enabled. + + Locking: serialized with .unthrottle() and termios modification by the + tty layer. + + unthrottle(port) + Notify the serial driver that characters can now be sent to the serial + port without fear of overrunning the input buffers of the line + disciplines. + + This will be called only if hardware assisted flow control is enabled. + + Locking: serialized with .throttle() and termios modification by the + tty layer. + + send_xchar(port,ch) + Transmit a high priority character, even if the port is stopped. + This is used to implement XON/XOFF flow control and tcflow(). If + the serial driver does not implement this function, the tty core + will append the character to the circular buffer and then call + start_tx() / stop_tx() to flush the data out. + + Do not transmit if ch == '\0' (__DISABLED_CHAR). + + Locking: none. + + Interrupts: caller dependent. + + stop_rx(port) + Stop receiving characters; the port is in the process of + being closed. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + enable_ms(port) + Enable the modem status interrupts. + + This method may be called multiple times. Modem status + interrupts should be disabled when the shutdown method is + called. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + break_ctl(port,ctl) + Control the transmission of a break signal. If ctl is + nonzero, the break signal should be transmitted. The signal + should be terminated when another call is made with a zero + ctl. + + Locking: caller holds tty_port->mutex + + startup(port) + Grab any interrupt resources and initialise any low level driver + state. Enable the port for reception. It should not activate + RTS nor DTR; this will be done via a separate call to set_mctrl. + + This method will only be called when the port is initially opened. + + Locking: port_sem taken. + + Interrupts: globally disabled. + + shutdown(port) + Disable the port, disable any break condition that may be in + effect, and free any interrupt resources. It should not disable + RTS nor DTR; this will have already been done via a separate + call to set_mctrl. + + Drivers must not access port->state once this call has completed. + + This method will only be called when there are no more users of + this port. + + Locking: port_sem taken. + + Interrupts: caller dependent. + + flush_buffer(port) + Flush any write buffers, reset any DMA state and stop any + ongoing DMA transfers. + + This will be called whenever the port->state->xmit circular + buffer is cleared. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + + set_termios(port,termios,oldtermios) + Change the port parameters, including word length, parity, stop + bits. Update read_status_mask and ignore_status_mask to indicate + the types of events we are interested in receiving. Relevant + termios->c_cflag bits are: + + CSIZE + - word size + CSTOPB + - 2 stop bits + PARENB + - parity enable + PARODD + - odd parity (when PARENB is in force) + CREAD + - enable reception of characters (if not set, + still receive characters from the port, but + throw them away. + CRTSCTS + - if set, enable CTS status change reporting + CLOCAL + - if not set, enable modem status change + reporting. + + Relevant termios->c_iflag bits are: + + INPCK + - enable frame and parity error events to be + passed to the TTY layer. + BRKINT / PARMRK + - both of these enable break events to be + passed to the TTY layer. + + IGNPAR + - ignore parity and framing errors + IGNBRK + - ignore break errors, If IGNPAR is also + set, ignore overrun errors as well. + + The interaction of the iflag bits is as follows (parity error + given as an example): + + =============== ======= ====== ============================= + Parity error INPCK IGNPAR + =============== ======= ====== ============================= + n/a 0 n/a character received, marked as + TTY_NORMAL + None 1 n/a character received, marked as + TTY_NORMAL + Yes 1 0 character received, marked as + TTY_PARITY + Yes 1 1 character discarded + =============== ======= ====== ============================= + + Other flags may be used (eg, xon/xoff characters) if your + hardware supports hardware "soft" flow control. + + Locking: caller holds tty_port->mutex + + Interrupts: caller dependent. + + This call must not sleep + + set_ldisc(port,termios) + Notifier for discipline change. See Documentation/serial/tty.rst. + + Locking: caller holds tty_port->mutex + + pm(port,state,oldstate) + Perform any power management related activities on the specified + port. State indicates the new state (defined by + enum uart_pm_state), oldstate indicates the previous state. + + This function should not be used to grab any resources. + + This will be called when the port is initially opened and finally + closed, except when the port is also the system console. This + will occur even if CONFIG_PM is not set. + + Locking: none. + + Interrupts: caller dependent. + + type(port) + Return a pointer to a string constant describing the specified + port, or return NULL, in which case the string 'unknown' is + substituted. + + Locking: none. + + Interrupts: caller dependent. + + release_port(port) + Release any memory and IO region resources currently in use by + the port. + + Locking: none. + + Interrupts: caller dependent. + + request_port(port) + Request any memory and IO region resources required by the port. + If any fail, no resources should be registered when this function + returns, and it should return -EBUSY on failure. + + Locking: none. + + Interrupts: caller dependent. + + config_port(port,type) + Perform any autoconfiguration steps required for the port. `type` + contains a bit mask of the required configuration. UART_CONFIG_TYPE + indicates that the port requires detection and identification. + port->type should be set to the type found, or PORT_UNKNOWN if + no port was detected. + + UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + which should be probed using standard kernel autoprobing techniques. + This is not necessary on platforms where ports have interrupts + internally hard wired (eg, system on a chip implementations). + + Locking: none. + + Interrupts: caller dependent. + + verify_port(port,serinfo) + Verify the new serial port information contained within serinfo is + suitable for this port type. + + Locking: none. + + Interrupts: caller dependent. + + ioctl(port,cmd,arg) + Perform any port specific IOCTLs. IOCTL commands must be defined + using the standard numbering system found in + + Locking: none. + + Interrupts: caller dependent. + + poll_init(port) + Called by kgdb to perform the minimal hardware initialization needed + to support poll_put_char() and poll_get_char(). Unlike ->startup() + this should not request interrupts. + + Locking: tty_mutex and tty_port->mutex taken. + + Interrupts: n/a. + + poll_put_char(port,ch) + Called by kgdb to write a single character directly to the serial + port. It can and should block until there is space in the TX FIFO. + + Locking: none. + + Interrupts: caller dependent. + + This call must not sleep + + poll_get_char(port) + Called by kgdb to read a single character directly from the serial + port. If data is available, it should be returned; otherwise + the function should return NO_POLL_CHAR immediately. + + Locking: none. + + Interrupts: caller dependent. + + This call must not sleep + +Other functions +--------------- + +uart_update_timeout(port,cflag,baud) + Update the FIFO drain timeout, port->timeout, according to the + number of bits, parity, stop bits and baud rate. + + Locking: caller is expected to take port->lock + + Interrupts: n/a + +uart_get_baud_rate(port,termios,old,min,max) + Return the numeric baud rate for the specified termios, taking + account of the special 38400 baud "kludge". The B0 baud rate + is mapped to 9600 baud. + + If the baud rate is not within min..max, then if old is non-NULL, + the original baud rate will be tried. If that exceeds the + min..max constraint, 9600 baud will be returned. termios will + be updated to the baud rate in use. + + Note: min..max must always allow 9600 baud to be selected. + + Locking: caller dependent. + + Interrupts: n/a + +uart_get_divisor(port,baud) + Return the divisor (baud_base / baud) for the specified baud + rate, appropriately rounded. + + If 38400 baud and custom divisor is selected, return the + custom divisor instead. + + Locking: caller dependent. + + Interrupts: n/a + +uart_match_port(port1,port2) + This utility function can be used to determine whether two + uart_port structures describe the same port. + + Locking: n/a + + Interrupts: n/a + +uart_write_wakeup(port) + A driver is expected to call this function when the number of + characters in the transmit buffer have dropped below a threshold. + + Locking: port->lock should be held. + + Interrupts: n/a + +uart_register_driver(drv) + Register a uart driver with the core driver. We in turn register + with the tty layer, and initialise the core driver per-port state. + + drv->port should be NULL, and the per-port structures should be + registered using uart_add_one_port after this call has succeeded. + + Locking: none + + Interrupts: enabled + +uart_unregister_driver() + Remove all references to a driver from the core driver. The low + level driver must have removed all its ports via the + uart_remove_one_port() if it registered them with uart_add_one_port(). + + Locking: none + + Interrupts: enabled + +**uart_suspend_port()** + +**uart_resume_port()** + +**uart_add_one_port()** + +**uart_remove_one_port()** + +Other notes +----------- + +It is intended some day to drop the 'unused' entries from uart_port, and +allow low level drivers to register their own individual uart_port's with +the core. This will allow drivers to use uart_port as a pointer to a +structure containing both the uart_port entry with their own extensions, +thus:: + + struct my_port { + struct uart_port port; + int my_stuff; + }; + +Modem control lines via GPIO +---------------------------- + +Some helpers are provided in order to set/get modem control lines via GPIO. + +mctrl_gpio_init(port, idx): + This will get the {cts,rts,...}-gpios from device tree if they are + present and request them, set direction etc, and return an + allocated structure. `devm_*` functions are used, so there's no need + to call mctrl_gpio_free(). + As this sets up the irq handling make sure to not handle changes to the + gpio input lines in your driver, too. + +mctrl_gpio_free(dev, gpios): + This will free the requested gpios in mctrl_gpio_init(). + As `devm_*` functions are used, there's generally no need to call + this function. + +mctrl_gpio_to_gpiod(gpios, gidx) + This returns the gpio_desc structure associated to the modem line + index. + +mctrl_gpio_set(gpios, mctrl): + This will sets the gpios according to the mctrl state. + +mctrl_gpio_get(gpios, mctrl): + This will update mctrl with the gpios values. + +mctrl_gpio_enable_ms(gpios): + Enables irqs and handling of changes to the ms lines. + +mctrl_gpio_disable_ms(gpios): + Disables irqs and handling of changes to the ms lines. diff --git a/Documentation/serial/index.rst b/Documentation/serial/index.rst new file mode 100644 index 000000000000..d0ba22ea23bf --- /dev/null +++ b/Documentation/serial/index.rst @@ -0,0 +1,32 @@ +:orphan: + +========================== +Support for Serial devices +========================== + +.. toctree:: + :maxdepth: 1 + + + driver + tty + +Serial drivers +============== + +.. toctree:: + :maxdepth: 1 + + cyclades_z + moxa-smartio + n_gsm + rocket + serial-iso7816 + serial-rs485 + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/serial/moxa-smartio b/Documentation/serial/moxa-smartio deleted file mode 100644 index 5d2a33be0bd8..000000000000 --- a/Documentation/serial/moxa-smartio +++ /dev/null @@ -1,523 +0,0 @@ -============================================================================= - MOXA Smartio/Industio Family Device Driver Installation Guide - for Linux Kernel 2.4.x, 2.6.x - Copyright (C) 2008, Moxa Inc. -============================================================================= -Date: 01/21/2008 - -Content - -1. Introduction -2. System Requirement -3. Installation - 3.1 Hardware installation - 3.2 Driver files - 3.3 Device naming convention - 3.4 Module driver configuration - 3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x. - 3.6 Custom configuration - 3.7 Verify driver installation -4. Utilities -5. Setserial -6. Troubleshooting - ------------------------------------------------------------------------------ -1. Introduction - - The Smartio/Industio/UPCI family Linux driver supports following multiport - boards. - - - 2 ports multiport board - CP-102U, CP-102UL, CP-102UF - CP-132U-I, CP-132UL, - CP-132, CP-132I, CP132S, CP-132IS, - CI-132, CI-132I, CI-132IS, - (C102H, C102HI, C102HIS, C102P, CP-102, CP-102S) - - - 4 ports multiport board - CP-104EL, - CP-104UL, CP-104JU, - CP-134U, CP-134U-I, - C104H/PCI, C104HS/PCI, - CP-114, CP-114I, CP-114S, CP-114IS, CP-114UL, - C104H, C104HS, - CI-104J, CI-104JS, - CI-134, CI-134I, CI-134IS, - (C114HI, CT-114I, C104P) - POS-104UL, - CB-114, - CB-134I - - - 8 ports multiport board - CP-118EL, CP-168EL, - CP-118U, CP-168U, - C168H/PCI, - C168H, C168HS, - (C168P), - CB-108 - - This driver and installation procedure have been developed upon Linux Kernel - 2.4.x and 2.6.x. This driver supports Intel x86 hardware platform. In order - to maintain compatibility, this version has also been properly tested with - RedHat, Mandrake, Fedora and S.u.S.E Linux. However, if compatibility problem - occurs, please contact Moxa at support@moxa.com.tw. - - In addition to device driver, useful utilities are also provided in this - version. They are - - msdiag Diagnostic program for displaying installed Moxa - Smartio/Industio boards. - - msmon Monitor program to observe data count and line status signals. - - msterm A simple terminal program which is useful in testing serial - ports. - - io-irq.exe Configuration program to setup ISA boards. Please note that - this program can only be executed under DOS. - - All the drivers and utilities are published in form of source code under - GNU General Public License in this version. Please refer to GNU General - Public License announcement in each source code file for more detail. - - In Moxa's Web sites, you may always find latest driver at http://www.moxa.com/. - - This version of driver can be installed as Loadable Module (Module driver) - or built-in into kernel (Static driver). You may refer to following - installation procedure for suitable one. Before you install the driver, - please refer to hardware installation procedure in the User's Manual. - - We assume the user should be familiar with following documents. - - Serial-HOWTO - - Kernel-HOWTO - ------------------------------------------------------------------------------ -2. System Requirement - - Hardware platform: Intel x86 machine - - Kernel version: 2.4.x or 2.6.x - - gcc version 2.72 or later - - Maximum 4 boards can be installed in combination - ------------------------------------------------------------------------------ -3. Installation - - 3.1 Hardware installation - 3.2 Driver files - 3.3 Device naming convention - 3.4 Module driver configuration - 3.5 Static driver configuration for Linux kernel 2.4.x, 2.6.x. - 3.6 Custom configuration - 3.7 Verify driver installation - - - 3.1 Hardware installation - - There are two types of buses, ISA and PCI, for Smartio/Industio - family multiport board. - - ISA board - --------- - You'll have to configure CAP address, I/O address, Interrupt Vector - as well as IRQ before installing this driver. Please refer to hardware - installation procedure in User's Manual before proceed any further. - Please make sure the JP1 is open after the ISA board is set properly. - - PCI/UPCI board - -------------- - You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict - with other ISA devices. Please refer to hardware installation - procedure in User's Manual in advance. - - PCI IRQ Sharing - ----------- - Each port within the same multiport board shares the same IRQ. Up to - 4 Moxa Smartio/Industio PCI Family multiport boards can be installed - together on one system and they can share the same IRQ. - - - 3.2 Driver files - - The driver file may be obtained from ftp, CD-ROM or floppy disk. The - first step, anyway, is to copy driver file "mxser.tgz" into specified - directory. e.g. /moxa. The execute commands as below. - - # cd / - # mkdir moxa - # cd /moxa - # tar xvf /dev/fd0 - - or - - # cd / - # mkdir moxa - # cd /moxa - # cp /mnt/cdrom//mxser.tgz . - # tar xvfz mxser.tgz - - - 3.3 Device naming convention - - You may find all the driver and utilities files in /moxa/mxser. - Following installation procedure depends on the model you'd like to - run the driver. If you prefer module driver, please refer to 3.4. - If static driver is required, please refer to 3.5. - - Dialin and callout port - ----------------------- - This driver remains traditional serial device properties. There are - two special file name for each serial port. One is dial-in port - which is named "ttyMxx". For callout port, the naming convention - is "cumxx". - - Device naming when more than 2 boards installed - ----------------------------------------------- - Naming convention for each Smartio/Industio multiport board is - pre-defined as below. - - Board Num. Dial-in Port Callout port - 1st board ttyM0 - ttyM7 cum0 - cum7 - 2nd board ttyM8 - ttyM15 cum8 - cum15 - 3rd board ttyM16 - ttyM23 cum16 - cum23 - 4th board ttyM24 - ttym31 cum24 - cum31 - - - !!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Under Kernel 2.6 the cum Device is Obsolete. So use ttyM* - device instead. - !!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - Board sequence - -------------- - This driver will activate ISA boards according to the parameter set - in the driver. After all specified ISA board activated, PCI board - will be installed in the system automatically driven. - Therefore the board number is sorted by the CAP address of ISA boards. - For PCI boards, their sequence will be after ISA boards and C168H/PCI - has higher priority than C104H/PCI boards. - - 3.4 Module driver configuration - Module driver is easiest way to install. If you prefer static driver - installation, please skip this paragraph. - - - ------------- Prepare to use the MOXA driver-------------------- - 3.4.1 Create tty device with correct major number - Before using MOXA driver, your system must have the tty devices - which are created with driver's major number. We offer one shell - script "msmknod" to simplify the procedure. - This step is only needed to be executed once. But you still - need to do this procedure when: - a. You change the driver's major number. Please refer the "3.7" - section. - b. Your total installed MOXA boards number is changed. Maybe you - add/delete one MOXA board. - c. You want to change the tty name. This needs to modify the - shell script "msmknod" - - The procedure is: - # cd /moxa/mxser/driver - # ./msmknod - - This shell script will require the major number for dial-in - device and callout device to create tty device. You also need - to specify the total installed MOXA board number. Default major - numbers for dial-in device and callout device are 30, 35. If - you need to change to other number, please refer section "3.7" - for more detailed procedure. - Msmknod will delete any special files occupying the same device - naming. - - 3.4.2 Build the MOXA driver and utilities - Before using the MOXA driver and utilities, you need compile the - all the source code. This step is only need to be executed once. - But you still re-compile the source code if you modify the source - code. For example, if you change the driver's major number (see - "3.7" section), then you need to do this step again. - - Find "Makefile" in /moxa/mxser, then run - - # make clean; make install - - !!!!!!!!!! NOTE !!!!!!!!!!!!!!!!! - For Red Hat 9, Red Hat Enterprise Linux AS3/ES3/WS3 & Fedora Core1: - # make clean; make installsp1 - - For Red Hat Enterprise Linux AS4/ES4/WS4: - # make clean; make installsp2 - !!!!!!!!!! NOTE !!!!!!!!!!!!!!!!! - - The driver files "mxser.o" and utilities will be properly compiled - and copied to system directories respectively. - - ------------- Load MOXA driver-------------------- - 3.4.3 Load the MOXA driver - - # modprobe mxser - - will activate the module driver. You may run "lsmod" to check - if "mxser" is activated. If the MOXA board is ISA board, the - is needed. Please refer to section "3.4.5" for more - information. - - - ------------- Load MOXA driver on boot -------------------- - 3.4.4 For the above description, you may manually execute - "modprobe mxser" to activate this driver and run - "rmmod mxser" to remove it. - However, it's better to have a boot time configuration to - eliminate manual operation. Boot time configuration can be - achieved by rc file. We offer one "rc.mxser" file to simplify - the procedure under "moxa/mxser/driver". - - But if you use ISA board, please modify the "modprobe ..." command - to add the argument (see "3.4.5" section). After modifying the - rc.mxser, please try to execute "/moxa/mxser/driver/rc.mxser" - manually to make sure the modification is ok. If any error - encountered, please try to modify again. If the modification is - completed, follow the below step. - - Run following command for setting rc files. - - # cd /moxa/mxser/driver - # cp ./rc.mxser /etc/rc.d - # cd /etc/rc.d - - Check "rc.serial" is existed or not. If "rc.serial" doesn't exist, - create it by vi, run "chmod 755 rc.serial" to change the permission. - Add "/etc/rc.d/rc.mxser" in last line, - - Reboot and check if moxa.o activated by "lsmod" command. - - 3.4.5. If you'd like to drive Smartio/Industio ISA boards in the system, - you'll have to add parameter to specify CAP address of given - board while activating "mxser.o". The format for parameters are - as follows. - - modprobe mxser ioaddr=0x???,0x???,0x???,0x??? - | | | | - | | | +- 4th ISA board - | | +------ 3rd ISA board - | +------------ 2nd ISA board - +------------------- 1st ISA board - - 3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x - - Note: To use static driver, you must install the linux kernel - source package. - - 3.5.1 Backup the built-in driver in the kernel. - # cd /usr/src/linux/drivers/char - # mv mxser.c mxser.c.old - - For Red Hat 7.x user, you need to create link: - # cd /usr/src - # ln -s linux-2.4 linux - - 3.5.2 Create link - # cd /usr/src/linux/drivers/char - # ln -s /moxa/mxser/driver/mxser.c mxser.c - - 3.5.3 Add CAP address list for ISA boards. For PCI boards user, - please skip this step. - - In module mode, the CAP address for ISA board is given by - parameter. In static driver configuration, you'll have to - assign it within driver's source code. If you will not - install any ISA boards, you may skip to next portion. - The instructions to modify driver source code are as - below. - a. # cd /moxa/mxser/driver - # vi mxser.c - b. Find the array mxserBoardCAP[] as below. - - static int mxserBoardCAP[] - = {0x00, 0x00, 0x00, 0x00}; - - c. Change the address within this array using vi. For - example, to driver 2 ISA boards with CAP address - 0x280 and 0x180 as 1st and 2nd board. Just to change - the source code as follows. - - static int mxserBoardCAP[] - = {0x280, 0x180, 0x00, 0x00}; - - 3.5.4 Setup kernel configuration - - Configure the kernel: - - # cd /usr/src/linux - # make menuconfig - - You will go into a menu-driven system. Please select [Character - devices][Non-standard serial port support], enable the [Moxa - SmartIO support] driver with "[*]" for built-in (not "[M]"), then - select [Exit] to exit this program. - - 3.5.5 Rebuild kernel - The following are for Linux kernel rebuilding, for your - reference only. - For appropriate details, please refer to the Linux document. - - a. cd /usr/src/linux - b. make clean /* take a few minutes */ - c. make dep /* take a few minutes */ - d. make bzImage /* take probably 10-20 minutes */ - e. make install /* copy boot image to correct position */ - f. Please make sure the boot kernel (vmlinuz) is in the - correct position. - g. If you use 'lilo' utility, you should check /etc/lilo.conf - 'image' item specified the path which is the 'vmlinuz' path, - or you will load wrong (or old) boot kernel image (vmlinuz). - After checking /etc/lilo.conf, please run "lilo". - - Note that if the result of "make bzImage" is ERROR, then you have to - go back to Linux configuration Setup. Type "make menuconfig" in - directory /usr/src/linux. - - - 3.5.6 Make tty device and special file - # cd /moxa/mxser/driver - # ./msmknod - - 3.5.7 Make utility - # cd /moxa/mxser/utility - # make clean; make install - - 3.5.8 Reboot - - - - 3.6 Custom configuration - Although this driver already provides you default configuration, you - still can change the device name and major number. The instruction to - change these parameters are shown as below. - - Change Device name - ------------------ - If you'd like to use other device names instead of default naming - convention, all you have to do is to modify the internal code - within the shell script "msmknod". First, you have to open "msmknod" - by vi. Locate each line contains "ttyM" and "cum" and change them - to the device name you desired. "msmknod" creates the device names - you need next time executed. - - Change Major number - ------------------- - If major number 30 and 35 had been occupied, you may have to select - 2 free major numbers for this driver. There are 3 steps to change - major numbers. - - 3.6.1 Find free major numbers - In /proc/devices, you may find all the major numbers occupied - in the system. Please select 2 major numbers that are available. - e.g. 40, 45. - 3.6.2 Create special files - Run /moxa/mxser/driver/msmknod to create special files with - specified major numbers. - 3.6.3 Modify driver with new major number - Run vi to open /moxa/mxser/driver/mxser.c. Locate the line - contains "MXSERMAJOR". Change the content as below. - #define MXSERMAJOR 40 - #define MXSERCUMAJOR 45 - 3.6.4 Run "make clean; make install" in /moxa/mxser/driver. - - 3.7 Verify driver installation - You may refer to /var/log/messages to check the latest status - log reported by this driver whenever it's activated. - ------------------------------------------------------------------------------ -4. Utilities - There are 3 utilities contained in this driver. They are msdiag, msmon and - msterm. These 3 utilities are released in form of source code. They should - be compiled into executable file and copied into /usr/bin. - - Before using these utilities, please load driver (refer 3.4 & 3.5) and - make sure you had run the "msmknod" utility. - - msdiag - Diagnostic - -------------------- - This utility provides the function to display what Moxa Smartio/Industio - board found by driver in the system. - - msmon - Port Monitoring - ----------------------- - This utility gives the user a quick view about all the MOXA ports' - activities. One can easily learn each port's total received/transmitted - (Rx/Tx) character count since the time when the monitoring is started. - Rx/Tx throughputs per second are also reported in interval basis (e.g. - the last 5 seconds) and in average basis (since the time the monitoring - is started). You can reset all ports' count by key. <+> <-> - (plus/minus) keys to change the displaying time interval. Press - on the port, that cursor stay, to view the port's communication - parameters, signal status, and input/output queue. - - msterm - Terminal Emulation - --------------------------- - This utility provides data sending and receiving ability of all tty ports, - especially for MOXA ports. It is quite useful for testing simple - application, for example, sending AT command to a modem connected to the - port or used as a terminal for login purpose. Note that this is only a - dumb terminal emulation without handling full screen operation. - ------------------------------------------------------------------------------ -5. Setserial - - Supported Setserial parameters are listed as below. - - uart set UART type(16450-->disable FIFO, 16550A-->enable FIFO) - close_delay set the amount of time(in 1/100 of a second) that DTR - should be kept low while being closed. - closing_wait set the amount of time(in 1/100 of a second) that the - serial port should wait for data to be drained while - being closed, before the receiver is disable. - spd_hi Use 57.6kb when the application requests 38.4kb. - spd_vhi Use 115.2kb when the application requests 38.4kb. - spd_shi Use 230.4kb when the application requests 38.4kb. - spd_warp Use 460.8kb when the application requests 38.4kb. - spd_normal Use 38.4kb when the application requests 38.4kb. - spd_cust Use the custom divisor to set the speed when the - application requests 38.4kb. - divisor This option set the custom division. - baud_base This option set the base baud rate. - ------------------------------------------------------------------------------ -6. Troubleshooting - - The boot time error messages and solutions are stated as clearly as - possible. If all the possible solutions fail, please contact our technical - support team to get more help. - - - Error msg: More than 4 Moxa Smartio/Industio family boards found. Fifth board - and after are ignored. - Solution: - To avoid this problem, please unplug fifth and after board, because Moxa - driver supports up to 4 boards. - - Error msg: Request_irq fail, IRQ(?) may be conflict with another device. - Solution: - Other PCI or ISA devices occupy the assigned IRQ. If you are not sure - which device causes the situation, please check /proc/interrupts to find - free IRQ and simply change another free IRQ for Moxa board. - - Error msg: Board #: C1xx Series(CAP=xxx) interrupt number invalid. - Solution: - Each port within the same multiport board shares the same IRQ. Please set - one IRQ (IRQ doesn't equal to zero) for one Moxa board. - - Error msg: No interrupt vector be set for Moxa ISA board(CAP=xxx). - Solution: - Moxa ISA board needs an interrupt vector.Please refer to user's manual - "Hardware Installation" chapter to set interrupt vector. - - Error msg: Couldn't install MOXA Smartio/Industio family driver! - Solution: - Load Moxa driver fail, the major number may conflict with other devices. - Please refer to previous section 3.7 to change a free major number for - Moxa driver. - - Error msg: Couldn't install MOXA Smartio/Industio family callout driver! - Solution: - Load Moxa callout driver fail, the callout device major number may - conflict with other devices. Please refer to previous section 3.7 to - change a free callout device major number for Moxa driver. - - ------------------------------------------------------------------------------ - diff --git a/Documentation/serial/moxa-smartio.rst b/Documentation/serial/moxa-smartio.rst new file mode 100644 index 000000000000..156100f17c3f --- /dev/null +++ b/Documentation/serial/moxa-smartio.rst @@ -0,0 +1,615 @@ +============================================================= +MOXA Smartio/Industio Family Device Driver Installation Guide +============================================================= + +.. note:: + + This file is outdated. It needs some care in order to make it + updated to Kernel 5.0 and upper + +Copyright (C) 2008, Moxa Inc. + +Date: 01/21/2008 + +.. Content + + 1. Introduction + 2. System Requirement + 3. Installation + 3.1 Hardware installation + 3.2 Driver files + 3.3 Device naming convention + 3.4 Module driver configuration + 3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x. + 3.6 Custom configuration + 3.7 Verify driver installation + 4. Utilities + 5. Setserial + 6. Troubleshooting + +1. Introduction +^^^^^^^^^^^^^^^ + + The Smartio/Industio/UPCI family Linux driver supports following multiport + boards. + + - 2 ports multiport board + CP-102U, CP-102UL, CP-102UF + CP-132U-I, CP-132UL, + CP-132, CP-132I, CP132S, CP-132IS, + CI-132, CI-132I, CI-132IS, + (C102H, C102HI, C102HIS, C102P, CP-102, CP-102S) + + - 4 ports multiport board + CP-104EL, + CP-104UL, CP-104JU, + CP-134U, CP-134U-I, + C104H/PCI, C104HS/PCI, + CP-114, CP-114I, CP-114S, CP-114IS, CP-114UL, + C104H, C104HS, + CI-104J, CI-104JS, + CI-134, CI-134I, CI-134IS, + (C114HI, CT-114I, C104P), + POS-104UL, + CB-114, + CB-134I + + - 8 ports multiport board + CP-118EL, CP-168EL, + CP-118U, CP-168U, + C168H/PCI, + C168H, C168HS, + (C168P), + CB-108 + + This driver and installation procedure have been developed upon Linux Kernel + 2.4.x and 2.6.x. This driver supports Intel x86 hardware platform. In order + to maintain compatibility, this version has also been properly tested with + RedHat, Mandrake, Fedora and S.u.S.E Linux. However, if compatibility problem + occurs, please contact Moxa at support@moxa.com.tw. + + In addition to device driver, useful utilities are also provided in this + version. They are: + + - msdiag + Diagnostic program for displaying installed Moxa + Smartio/Industio boards. + - msmon + Monitor program to observe data count and line status signals. + - msterm A simple terminal program which is useful in testing serial + ports. + - io-irq.exe + Configuration program to setup ISA boards. Please note that + this program can only be executed under DOS. + + All the drivers and utilities are published in form of source code under + GNU General Public License in this version. Please refer to GNU General + Public License announcement in each source code file for more detail. + + In Moxa's Web sites, you may always find latest driver at http://www.moxa.com/. + + This version of driver can be installed as Loadable Module (Module driver) + or built-in into kernel (Static driver). You may refer to following + installation procedure for suitable one. Before you install the driver, + please refer to hardware installation procedure in the User's Manual. + + We assume the user should be familiar with following documents. + + - Serial-HOWTO + - Kernel-HOWTO + +2. System Requirement +^^^^^^^^^^^^^^^^^^^^^ + + - Hardware platform: Intel x86 machine + - Kernel version: 2.4.x or 2.6.x + - gcc version 2.72 or later + - Maximum 4 boards can be installed in combination + +3. Installation +^^^^^^^^^^^^^^^ + +3.1 Hardware installation +========================= + + There are two types of buses, ISA and PCI, for Smartio/Industio + family multiport board. + +ISA board +--------- + + You'll have to configure CAP address, I/O address, Interrupt Vector + as well as IRQ before installing this driver. Please refer to hardware + installation procedure in User's Manual before proceed any further. + Please make sure the JP1 is open after the ISA board is set properly. + +PCI/UPCI board +-------------- + + You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict + with other ISA devices. Please refer to hardware installation + procedure in User's Manual in advance. + +PCI IRQ Sharing +--------------- + + Each port within the same multiport board shares the same IRQ. Up to + 4 Moxa Smartio/Industio PCI Family multiport boards can be installed + together on one system and they can share the same IRQ. + + +3.2 Driver files +================ + + The driver file may be obtained from ftp, CD-ROM or floppy disk. The + first step, anyway, is to copy driver file "mxser.tgz" into specified + directory. e.g. /moxa. The execute commands as below:: + + # cd / + # mkdir moxa + # cd /moxa + # tar xvf /dev/fd0 + +or:: + + # cd / + # mkdir moxa + # cd /moxa + # cp /mnt/cdrom//mxser.tgz . + # tar xvfz mxser.tgz + + +3.3 Device naming convention +============================ + + You may find all the driver and utilities files in /moxa/mxser. + Following installation procedure depends on the model you'd like to + run the driver. If you prefer module driver, please refer to 3.4. + If static driver is required, please refer to 3.5. + +Dialin and callout port +----------------------- + + This driver remains traditional serial device properties. There are + two special file name for each serial port. One is dial-in port + which is named "ttyMxx". For callout port, the naming convention + is "cumxx". + +Device naming when more than 2 boards installed +----------------------------------------------- + + Naming convention for each Smartio/Industio multiport board is + pre-defined as below. + + ============ =============== ============== + Board Num. Dial-in Port Callout port + 1st board ttyM0 - ttyM7 cum0 - cum7 + 2nd board ttyM8 - ttyM15 cum8 - cum15 + 3rd board ttyM16 - ttyM23 cum16 - cum23 + 4th board ttyM24 - ttym31 cum24 - cum31 + ============ =============== ============== + +.. note:: + + Under Kernel 2.6 and upper, the cum Device is Obsolete. So use ttyM* + device instead. + +Board sequence +-------------- + + This driver will activate ISA boards according to the parameter set + in the driver. After all specified ISA board activated, PCI board + will be installed in the system automatically driven. + Therefore the board number is sorted by the CAP address of ISA boards. + For PCI boards, their sequence will be after ISA boards and C168H/PCI + has higher priority than C104H/PCI boards. + +3.4 Module driver configuration +=============================== + + Module driver is easiest way to install. If you prefer static driver + installation, please skip this paragraph. + + + ------------- Prepare to use the MOXA driver -------------------- + +3.4.1 Create tty device with correct major number +------------------------------------------------- + + Before using MOXA driver, your system must have the tty devices + which are created with driver's major number. We offer one shell + script "msmknod" to simplify the procedure. + This step is only needed to be executed once. But you still + need to do this procedure when: + + a. You change the driver's major number. Please refer the "3.7" + section. + b. Your total installed MOXA boards number is changed. Maybe you + add/delete one MOXA board. + c. You want to change the tty name. This needs to modify the + shell script "msmknod" + + The procedure is:: + + # cd /moxa/mxser/driver + # ./msmknod + + This shell script will require the major number for dial-in + device and callout device to create tty device. You also need + to specify the total installed MOXA board number. Default major + numbers for dial-in device and callout device are 30, 35. If + you need to change to other number, please refer section "3.7" + for more detailed procedure. + Msmknod will delete any special files occupying the same device + naming. + +3.4.2 Build the MOXA driver and utilities +----------------------------------------- + + Before using the MOXA driver and utilities, you need compile the + all the source code. This step is only need to be executed once. + But you still re-compile the source code if you modify the source + code. For example, if you change the driver's major number (see + "3.7" section), then you need to do this step again. + + Find "Makefile" in /moxa/mxser, then run + + # make clean; make install + + ..note:: + + For Red Hat 9, Red Hat Enterprise Linux AS3/ES3/WS3 & Fedora Core1: + # make clean; make installsp1 + + For Red Hat Enterprise Linux AS4/ES4/WS4: + # make clean; make installsp2 + + The driver files "mxser.o" and utilities will be properly compiled + and copied to system directories respectively. + +------------- Load MOXA driver-------------------- + +3.4.3 Load the MOXA driver +-------------------------- + + :: + + # modprobe mxser + + will activate the module driver. You may run "lsmod" to check + if "mxser" is activated. If the MOXA board is ISA board, the + is needed. Please refer to section "3.4.5" for more + information. + +------------- Load MOXA driver on boot -------------------- + +3.4.4 Load the mxser driver +--------------------------- + + + For the above description, you may manually execute + "modprobe mxser" to activate this driver and run + "rmmod mxser" to remove it. + + However, it's better to have a boot time configuration to + eliminate manual operation. Boot time configuration can be + achieved by rc file. We offer one "rc.mxser" file to simplify + the procedure under "moxa/mxser/driver". + + But if you use ISA board, please modify the "modprobe ..." command + to add the argument (see "3.4.5" section). After modifying the + rc.mxser, please try to execute "/moxa/mxser/driver/rc.mxser" + manually to make sure the modification is ok. If any error + encountered, please try to modify again. If the modification is + completed, follow the below step. + + Run following command for setting rc files:: + + # cd /moxa/mxser/driver + # cp ./rc.mxser /etc/rc.d + # cd /etc/rc.d + + Check "rc.serial" is existed or not. If "rc.serial" doesn't exist, + create it by vi, run "chmod 755 rc.serial" to change the permission. + + Add "/etc/rc.d/rc.mxser" in last line. + + Reboot and check if moxa.o activated by "lsmod" command. + +3.4.5. specify CAP address +-------------------------- + + If you'd like to drive Smartio/Industio ISA boards in the system, + you'll have to add parameter to specify CAP address of given + board while activating "mxser.o". The format for parameters are + as follows.:: + + modprobe mxser ioaddr=0x???,0x???,0x???,0x??? + | | | | + | | | +- 4th ISA board + | | +------ 3rd ISA board + | +------------ 2nd ISA board + +-------------------1st ISA board + +3.5 Static driver configuration for Linux kernel 2.4.x and 2.6.x +================================================================ + + Note: + To use static driver, you must install the linux kernel + source package. + +3.5.1 Backup the built-in driver in the kernel +---------------------------------------------- + + :: + + # cd /usr/src/linux/drivers/char + # mv mxser.c mxser.c.old + + For Red Hat 7.x user, you need to create link: + # cd /usr/src + # ln -s linux-2.4 linux + +3.5.2 Create link +----------------- + :: + + # cd /usr/src/linux/drivers/char + # ln -s /moxa/mxser/driver/mxser.c mxser.c + +3.5.3 Add CAP address list for ISA boards. +------------------------------------------ + + For PCI boards user, please skip this step. + + In module mode, the CAP address for ISA board is given by + parameter. In static driver configuration, you'll have to + assign it within driver's source code. If you will not + install any ISA boards, you may skip to next portion. + The instructions to modify driver source code are as + below. + + a. run:: + + # cd /moxa/mxser/driver + # vi mxser.c + + b. Find the array mxserBoardCAP[] as below:: + + static int mxserBoardCAP[] = {0x00, 0x00, 0x00, 0x00}; + + c. Change the address within this array using vi. For + example, to driver 2 ISA boards with CAP address + 0x280 and 0x180 as 1st and 2nd board. Just to change + the source code as follows:: + + static int mxserBoardCAP[] = {0x280, 0x180, 0x00, 0x00}; + +3.5.4 Setup kernel configuration +-------------------------------- + + Configure the kernel:: + + # cd /usr/src/linux + # make menuconfig + + You will go into a menu-driven system. Please select [Character + devices][Non-standard serial port support], enable the [Moxa + SmartIO support] driver with "[*]" for built-in (not "[M]"), then + select [Exit] to exit this program. + +3.5.5 Rebuild kernel +-------------------- + + The following are for Linux kernel rebuilding, for your + reference only. + + For appropriate details, please refer to the Linux document: + + a. Run the following commands:: + + cd /usr/src/linux + make clean # take a few minutes + make dep # take a few minutes + make bzImage # take probably 10-20 minutes + make install # copy boot image to correct position + + f. Please make sure the boot kernel (vmlinuz) is in the + correct position. + g. If you use 'lilo' utility, you should check /etc/lilo.conf + 'image' item specified the path which is the 'vmlinuz' path, + or you will load wrong (or old) boot kernel image (vmlinuz). + After checking /etc/lilo.conf, please run "lilo". + + Note that if the result of "make bzImage" is ERROR, then you have to + go back to Linux configuration Setup. Type "make menuconfig" in + directory /usr/src/linux. + + +3.5.6 Make tty device and special file +-------------------------------------- + + :: + # cd /moxa/mxser/driver + # ./msmknod + +3.5.7 Make utility +------------------ + + :: + + # cd /moxa/mxser/utility + # make clean; make install + +3.5.8 Reboot +------------ + + + +3.6 Custom configuration +======================== + + Although this driver already provides you default configuration, you + still can change the device name and major number. The instruction to + change these parameters are shown as below. + +a. Change Device name + + If you'd like to use other device names instead of default naming + convention, all you have to do is to modify the internal code + within the shell script "msmknod". First, you have to open "msmknod" + by vi. Locate each line contains "ttyM" and "cum" and change them + to the device name you desired. "msmknod" creates the device names + you need next time executed. + +b. Change Major number + + If major number 30 and 35 had been occupied, you may have to select + 2 free major numbers for this driver. There are 3 steps to change + major numbers. + +3.6.1 Find free major numbers +----------------------------- + + In /proc/devices, you may find all the major numbers occupied + in the system. Please select 2 major numbers that are available. + e.g. 40, 45. + +3.6.2 Create special files +-------------------------- + + Run /moxa/mxser/driver/msmknod to create special files with + specified major numbers. + +3.6.3 Modify driver with new major number +----------------------------------------- + + Run vi to open /moxa/mxser/driver/mxser.c. Locate the line + contains "MXSERMAJOR". Change the content as below:: + + #define MXSERMAJOR 40 + #define MXSERCUMAJOR 45 + + 3.6.4 Run "make clean; make install" in /moxa/mxser/driver. + +3.7 Verify driver installation +============================== + + You may refer to /var/log/messages to check the latest status + log reported by this driver whenever it's activated. + +4. Utilities +^^^^^^^^^^^^ + + There are 3 utilities contained in this driver. They are msdiag, msmon and + msterm. These 3 utilities are released in form of source code. They should + be compiled into executable file and copied into /usr/bin. + + Before using these utilities, please load driver (refer 3.4 & 3.5) and + make sure you had run the "msmknod" utility. + +msdiag - Diagnostic +=================== + + This utility provides the function to display what Moxa Smartio/Industio + board found by driver in the system. + +msmon - Port Monitoring +======================= + + This utility gives the user a quick view about all the MOXA ports' + activities. One can easily learn each port's total received/transmitted + (Rx/Tx) character count since the time when the monitoring is started. + + Rx/Tx throughputs per second are also reported in interval basis (e.g. + the last 5 seconds) and in average basis (since the time the monitoring + is started). You can reset all ports' count by key. <+> <-> + (plus/minus) keys to change the displaying time interval. Press + on the port, that cursor stay, to view the port's communication + parameters, signal status, and input/output queue. + +msterm - Terminal Emulation +=========================== + + This utility provides data sending and receiving ability of all tty ports, + especially for MOXA ports. It is quite useful for testing simple + application, for example, sending AT command to a modem connected to the + port or used as a terminal for login purpose. Note that this is only a + dumb terminal emulation without handling full screen operation. + +5. Setserial +^^^^^^^^^^^^ + + Supported Setserial parameters are listed as below. + + ============== ========================================================= + uart set UART type(16450-->disable FIFO, 16550A-->enable FIFO) + close_delay set the amount of time(in 1/100 of a second) that DTR + should be kept low while being closed. + closing_wait set the amount of time(in 1/100 of a second) that the + serial port should wait for data to be drained while + being closed, before the receiver is disable. + spd_hi Use 57.6kb when the application requests 38.4kb. + spd_vhi Use 115.2kb when the application requests 38.4kb. + spd_shi Use 230.4kb when the application requests 38.4kb. + spd_warp Use 460.8kb when the application requests 38.4kb. + spd_normal Use 38.4kb when the application requests 38.4kb. + spd_cust Use the custom divisor to set the speed when the + application requests 38.4kb. + divisor This option set the custom division. + baud_base This option set the base baud rate. + ============== ========================================================= + +6. Troubleshooting +^^^^^^^^^^^^^^^^^^ + + The boot time error messages and solutions are stated as clearly as + possible. If all the possible solutions fail, please contact our technical + support team to get more help. + + + Error msg: + More than 4 Moxa Smartio/Industio family boards found. Fifth board + and after are ignored. + + Solution: + To avoid this problem, please unplug fifth and after board, because Moxa + driver supports up to 4 boards. + + Error msg: + Request_irq fail, IRQ(?) may be conflict with another device. + + Solution: + Other PCI or ISA devices occupy the assigned IRQ. If you are not sure + which device causes the situation, please check /proc/interrupts to find + free IRQ and simply change another free IRQ for Moxa board. + + Error msg: + Board #: C1xx Series(CAP=xxx) interrupt number invalid. + + Solution: + Each port within the same multiport board shares the same IRQ. Please set + one IRQ (IRQ doesn't equal to zero) for one Moxa board. + + Error msg: + No interrupt vector be set for Moxa ISA board(CAP=xxx). + + Solution: + Moxa ISA board needs an interrupt vector.Please refer to user's manual + "Hardware Installation" chapter to set interrupt vector. + + Error msg: + Couldn't install MOXA Smartio/Industio family driver! + + Solution: + Load Moxa driver fail, the major number may conflict with other devices. + Please refer to previous section 3.7 to change a free major number for + Moxa driver. + + Error msg: + Couldn't install MOXA Smartio/Industio family callout driver! + + Solution: + Load Moxa callout driver fail, the callout device major number may + conflict with other devices. Please refer to previous section 3.7 to + change a free callout device major number for Moxa driver. diff --git a/Documentation/serial/n_gsm.rst b/Documentation/serial/n_gsm.rst new file mode 100644 index 000000000000..f3ad9fd26408 --- /dev/null +++ b/Documentation/serial/n_gsm.rst @@ -0,0 +1,103 @@ +============================== +GSM 0710 tty multiplexor HOWTO +============================== + +This line discipline implements the GSM 07.10 multiplexing protocol +detailed in the following 3GPP document: + + http://www.3gpp.org/ftp/Specs/archive/07_series/07.10/0710-720.zip + +This document give some hints on how to use this driver with GPRS and 3G +modems connected to a physical serial port. + +How to use it +------------- +1. initialize the modem in 0710 mux mode (usually AT+CMUX= command) through + its serial port. Depending on the modem used, you can pass more or less + parameters to this command, +2. switch the serial line to using the n_gsm line discipline by using + TIOCSETD ioctl, +3. configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl, + +Major parts of the initialization program : +(a good starting point is util-linux-ng/sys-utils/ldattach.c):: + + #include + #define N_GSM0710 21 /* GSM 0710 Mux */ + #define DEFAULT_SPEED B115200 + #define SERIAL_PORT /dev/ttyS0 + + int ldisc = N_GSM0710; + struct gsm_config c; + struct termios configuration; + + /* open the serial port connected to the modem */ + fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY); + + /* configure the serial port : speed, flow control ... */ + + /* send the AT commands to switch the modem to CMUX mode + and check that it's successful (should return OK) */ + write(fd, "AT+CMUX=0\r", 10); + + /* experience showed that some modems need some time before + being able to answer to the first MUX packet so a delay + may be needed here in some case */ + sleep(3); + + /* use n_gsm line discipline */ + ioctl(fd, TIOCSETD, &ldisc); + + /* get n_gsm configuration */ + ioctl(fd, GSMIOC_GETCONF, &c); + /* we are initiator and need encoding 0 (basic) */ + c.initiator = 1; + c.encapsulation = 0; + /* our modem defaults to a maximum size of 127 bytes */ + c.mru = 127; + c.mtu = 127; + /* set the new configuration */ + ioctl(fd, GSMIOC_SETCONF, &c); + + /* and wait for ever to keep the line discipline enabled */ + daemon(0,0); + pause(); + +4. create the devices corresponding to the "virtual" serial ports (take care, + each modem has its configuration and some DLC have dedicated functions, + for example GPS), starting with minor 1 (DLC0 is reserved for the management + of the mux):: + + MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}` + for i in `seq 1 4`; do + mknod /dev/ttygsm$i c $MAJOR $i + done + +5. use these devices as plain serial ports. + + for example, it's possible: + + - and to use gnokii to send / receive SMS on ttygsm1 + - to use ppp to establish a datalink on ttygsm2 + +6. first close all virtual ports before closing the physical port. + + Note that after closing the physical port the modem is still in multiplexing + mode. This may prevent a successful re-opening of the port later. To avoid + this situation either reset the modem if your hardware allows that or send + a disconnect command frame manually before initializing the multiplexing mode + for the second time. The byte sequence for the disconnect command frame is:: + + 0xf9, 0x03, 0xef, 0x03, 0xc3, 0x16, 0xf9. + +Additional Documentation +------------------------ +More practical details on the protocol and how it's supported by industrial +modems can be found in the following documents : + +- http://www.telit.com/module/infopool/download.php?id=616 +- http://www.u-blox.com/images/downloads/Product_Docs/LEON-G100-G200-MuxImplementation_ApplicationNote_%28GSM%20G1-CS-10002%29.pdf +- http://www.sierrawireless.com/Support/Downloads/AirPrime/WMP_Series/~/media/Support_Downloads/AirPrime/Application_notes/CMUX_Feature_Application_Note-Rev004.ashx +- http://wm.sim.com/sim/News/photo/2010721161442.pdf + +11-03-08 - Eric Bénard - diff --git a/Documentation/serial/n_gsm.txt b/Documentation/serial/n_gsm.txt deleted file mode 100644 index 875361bb7cb4..000000000000 --- a/Documentation/serial/n_gsm.txt +++ /dev/null @@ -1,96 +0,0 @@ -n_gsm.c GSM 0710 tty multiplexor HOWTO -=================================================== - -This line discipline implements the GSM 07.10 multiplexing protocol -detailed in the following 3GPP document : -http://www.3gpp.org/ftp/Specs/archive/07_series/07.10/0710-720.zip - -This document give some hints on how to use this driver with GPRS and 3G -modems connected to a physical serial port. - -How to use it -------------- -1- initialize the modem in 0710 mux mode (usually AT+CMUX= command) through -its serial port. Depending on the modem used, you can pass more or less -parameters to this command, -2- switch the serial line to using the n_gsm line discipline by using -TIOCSETD ioctl, -3- configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl, - -Major parts of the initialization program : -(a good starting point is util-linux-ng/sys-utils/ldattach.c) -#include -#define N_GSM0710 21 /* GSM 0710 Mux */ -#define DEFAULT_SPEED B115200 -#define SERIAL_PORT /dev/ttyS0 - - int ldisc = N_GSM0710; - struct gsm_config c; - struct termios configuration; - - /* open the serial port connected to the modem */ - fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY); - - /* configure the serial port : speed, flow control ... */ - - /* send the AT commands to switch the modem to CMUX mode - and check that it's successful (should return OK) */ - write(fd, "AT+CMUX=0\r", 10); - - /* experience showed that some modems need some time before - being able to answer to the first MUX packet so a delay - may be needed here in some case */ - sleep(3); - - /* use n_gsm line discipline */ - ioctl(fd, TIOCSETD, &ldisc); - - /* get n_gsm configuration */ - ioctl(fd, GSMIOC_GETCONF, &c); - /* we are initiator and need encoding 0 (basic) */ - c.initiator = 1; - c.encapsulation = 0; - /* our modem defaults to a maximum size of 127 bytes */ - c.mru = 127; - c.mtu = 127; - /* set the new configuration */ - ioctl(fd, GSMIOC_SETCONF, &c); - - /* and wait for ever to keep the line discipline enabled */ - daemon(0,0); - pause(); - -4- create the devices corresponding to the "virtual" serial ports (take care, -each modem has its configuration and some DLC have dedicated functions, -for example GPS), starting with minor 1 (DLC0 is reserved for the management -of the mux) - -MAJOR=`cat /proc/devices |grep gsmtty | awk '{print $1}` -for i in `seq 1 4`; do - mknod /dev/ttygsm$i c $MAJOR $i -done - -5- use these devices as plain serial ports. -for example, it's possible : -- and to use gnokii to send / receive SMS on ttygsm1 -- to use ppp to establish a datalink on ttygsm2 - -6- first close all virtual ports before closing the physical port. - -Note that after closing the physical port the modem is still in multiplexing -mode. This may prevent a successful re-opening of the port later. To avoid -this situation either reset the modem if your hardware allows that or send -a disconnect command frame manually before initializing the multiplexing mode -for the second time. The byte sequence for the disconnect command frame is: -0xf9, 0x03, 0xef, 0x03, 0xc3, 0x16, 0xf9. - -Additional Documentation ------------------------- -More practical details on the protocol and how it's supported by industrial -modems can be found in the following documents : -http://www.telit.com/module/infopool/download.php?id=616 -http://www.u-blox.com/images/downloads/Product_Docs/LEON-G100-G200-MuxImplementation_ApplicationNote_%28GSM%20G1-CS-10002%29.pdf -http://www.sierrawireless.com/Support/Downloads/AirPrime/WMP_Series/~/media/Support_Downloads/AirPrime/Application_notes/CMUX_Feature_Application_Note-Rev004.ashx -http://wm.sim.com/sim/News/photo/2010721161442.pdf - -11-03-08 - Eric Bénard - diff --git a/Documentation/serial/rocket.rst b/Documentation/serial/rocket.rst new file mode 100644 index 000000000000..23761eae4282 --- /dev/null +++ b/Documentation/serial/rocket.rst @@ -0,0 +1,185 @@ +================================================ +Comtrol(tm) RocketPort(R)/RocketModem(TM) Series +================================================ + +Device Driver for the Linux Operating System +============================================ + +Product overview +---------------- + +This driver provides a loadable kernel driver for the Comtrol RocketPort +and RocketModem PCI boards. These boards provide, 2, 4, 8, 16, or 32 +high-speed serial ports or modems. This driver supports up to a combination +of four RocketPort or RocketModems boards in one machine simultaneously. +This file assumes that you are using the RocketPort driver which is +integrated into the kernel sources. + +The driver can also be installed as an external module using the usual +"make;make install" routine. This external module driver, obtainable +from the Comtrol website listed below, is useful for updating the driver +or installing it into kernels which do not have the driver configured +into them. Installations instructions for the external module +are in the included README and HW_INSTALL files. + +RocketPort ISA and RocketModem II PCI boards currently are only supported by +this driver in module form. + +The RocketPort ISA board requires I/O ports to be configured by the DIP +switches on the board. See the section "ISA Rocketport Boards" below for +information on how to set the DIP switches. + +You pass the I/O port to the driver using the following module parameters: + +board1: + I/O port for the first ISA board +board2: + I/O port for the second ISA board +board3: + I/O port for the third ISA board +board4: + I/O port for the fourth ISA board + +There is a set of utilities and scripts provided with the external driver +(downloadable from http://www.comtrol.com) that ease the configuration and +setup of the ISA cards. + +The RocketModem II PCI boards require firmware to be loaded into the card +before it will function. The driver has only been tested as a module for this +board. + +Installation Procedures +----------------------- + +RocketPort/RocketModem PCI cards require no driver configuration, they are +automatically detected and configured. + +The RocketPort driver can be installed as a module (recommended) or built +into the kernel. This is selected, as for other drivers, through the `make config` +command from the root of the Linux source tree during the kernel build process. + +The RocketPort/RocketModem serial ports installed by this driver are assigned +device major number 46, and will be named /dev/ttyRx, where x is the port number +starting at zero (ex. /dev/ttyR0, /devttyR1, ...). If you have multiple cards +installed in the system, the mapping of port names to serial ports is displayed +in the system log at /var/log/messages. + +If installed as a module, the module must be loaded. This can be done +manually by entering "modprobe rocket". To have the module loaded automatically +upon system boot, edit a `/etc/modprobe.d/*.conf` file and add the line +"alias char-major-46 rocket". + +In order to use the ports, their device names (nodes) must be created with mknod. +This is only required once, the system will retain the names once created. To +create the RocketPort/RocketModem device names, use the command +"mknod /dev/ttyRx c 46 x" where x is the port number starting at zero. + +For example:: + + > mknod /dev/ttyR0 c 46 0 + > mknod /dev/ttyR1 c 46 1 + > mknod /dev/ttyR2 c 46 2 + +The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes) +for you:: + + >/dev/MAKEDEV ttyR + +ISA Rocketport Boards +--------------------- + +You must assign and configure the I/O addresses used by the ISA Rocketport +card before installing and using it. This is done by setting a set of DIP +switches on the Rocketport board. + + +Setting the I/O address +----------------------- + +Before installing RocketPort(R) or RocketPort RA boards, you must find +a range of I/O addresses for it to use. The first RocketPort card +requires a 68-byte contiguous block of I/O addresses, starting at one +of the following: 0x100h, 0x140h, 0x180h, 0x200h, 0x240h, 0x280h, +0x300h, 0x340h, 0x380h. This I/O address must be reflected in the DIP +switches of *all* of the Rocketport cards. + +The second, third, and fourth RocketPort cards require a 64-byte +contiguous block of I/O addresses, starting at one of the following +I/O addresses: 0x100h, 0x140h, 0x180h, 0x1C0h, 0x200h, 0x240h, 0x280h, +0x2C0h, 0x300h, 0x340h, 0x380h, 0x3C0h. The I/O address used by the +second, third, and fourth Rocketport cards (if present) are set via +software control. The DIP switch settings for the I/O address must be +set to the value of the first Rocketport cards. + +In order to distinguish each of the card from the others, each card +must have a unique board ID set on the dip switches. The first +Rocketport board must be set with the DIP switches corresponding to +the first board, the second board must be set with the DIP switches +corresponding to the second board, etc. IMPORTANT: The board ID is +the only place where the DIP switch settings should differ between the +various Rocketport boards in a system. + +The I/O address range used by any of the RocketPort cards must not +conflict with any other cards in the system, including other +RocketPort cards. Below, you will find a list of commonly used I/O +address ranges which may be in use by other devices in your system. +On a Linux system, "cat /proc/ioports" will also be helpful in +identifying what I/O addresses are being used by devices on your +system. + +Remember, the FIRST RocketPort uses 68 I/O addresses. So, if you set it +for 0x100, it will occupy 0x100 to 0x143. This would mean that you +CAN NOT set the second, third or fourth board for address 0x140 since +the first 4 bytes of that range are used by the first board. You would +need to set the second, third, or fourth board to one of the next available +blocks such as 0x180. + +RocketPort and RocketPort RA SW1 Settings:: + + +-------------------------------+ + | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + +-------+-------+---------------+ + | Unused| Card | I/O Port Block| + +-------------------------------+ + + DIP Switches DIP Switches + 7 8 6 5 + =================== =================== + On On UNUSED, MUST BE ON. On On First Card <==== Default + On Off Second Card + Off On Third Card + Off Off Fourth Card + + DIP Switches I/O Address Range + 4 3 2 1 Used by the First Card + ===================================== + On Off On Off 100-143 + On Off Off On 140-183 + On Off Off Off 180-1C3 <==== Default + Off On On Off 200-243 + Off On Off On 240-283 + Off On Off Off 280-2C3 + Off Off On Off 300-343 + Off Off Off On 340-383 + Off Off Off Off 380-3C3 + +Reporting Bugs +-------------- + +For technical support, please provide the following +information: Driver version, kernel release, distribution of +kernel, and type of board you are using. Error messages and log +printouts port configuration details are especially helpful. + +USA: + :Phone: (612) 494-4100 + :FAX: (612) 494-4199 + :email: support@comtrol.com + +Comtrol Europe: + :Phone: +44 (0) 1 869 323-220 + :FAX: +44 (0) 1 869 323-211 + :email: support@comtrol.co.uk + +Web: http://www.comtrol.com +FTP: ftp.comtrol.com diff --git a/Documentation/serial/rocket.txt b/Documentation/serial/rocket.txt deleted file mode 100644 index 60b039891057..000000000000 --- a/Documentation/serial/rocket.txt +++ /dev/null @@ -1,189 +0,0 @@ -Comtrol(tm) RocketPort(R)/RocketModem(TM) Series -Device Driver for the Linux Operating System - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -PRODUCT OVERVIEW ----------------- - -This driver provides a loadable kernel driver for the Comtrol RocketPort -and RocketModem PCI boards. These boards provide, 2, 4, 8, 16, or 32 -high-speed serial ports or modems. This driver supports up to a combination -of four RocketPort or RocketModems boards in one machine simultaneously. -This file assumes that you are using the RocketPort driver which is -integrated into the kernel sources. - -The driver can also be installed as an external module using the usual -"make;make install" routine. This external module driver, obtainable -from the Comtrol website listed below, is useful for updating the driver -or installing it into kernels which do not have the driver configured -into them. Installations instructions for the external module -are in the included README and HW_INSTALL files. - -RocketPort ISA and RocketModem II PCI boards currently are only supported by -this driver in module form. - -The RocketPort ISA board requires I/O ports to be configured by the DIP -switches on the board. See the section "ISA Rocketport Boards" below for -information on how to set the DIP switches. - -You pass the I/O port to the driver using the following module parameters: - -board1 : I/O port for the first ISA board -board2 : I/O port for the second ISA board -board3 : I/O port for the third ISA board -board4 : I/O port for the fourth ISA board - -There is a set of utilities and scripts provided with the external driver -( downloadable from http://www.comtrol.com ) that ease the configuration and -setup of the ISA cards. - -The RocketModem II PCI boards require firmware to be loaded into the card -before it will function. The driver has only been tested as a module for this -board. - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -INSTALLATION PROCEDURES ------------------------ - -RocketPort/RocketModem PCI cards require no driver configuration, they are -automatically detected and configured. - -The RocketPort driver can be installed as a module (recommended) or built -into the kernel. This is selected, as for other drivers, through the `make config` -command from the root of the Linux source tree during the kernel build process. - -The RocketPort/RocketModem serial ports installed by this driver are assigned -device major number 46, and will be named /dev/ttyRx, where x is the port number -starting at zero (ex. /dev/ttyR0, /devttyR1, ...). If you have multiple cards -installed in the system, the mapping of port names to serial ports is displayed -in the system log at /var/log/messages. - -If installed as a module, the module must be loaded. This can be done -manually by entering "modprobe rocket". To have the module loaded automatically -upon system boot, edit a /etc/modprobe.d/*.conf file and add the line -"alias char-major-46 rocket". - -In order to use the ports, their device names (nodes) must be created with mknod. -This is only required once, the system will retain the names once created. To -create the RocketPort/RocketModem device names, use the command -"mknod /dev/ttyRx c 46 x" where x is the port number starting at zero. For example: - ->mknod /dev/ttyR0 c 46 0 ->mknod /dev/ttyR1 c 46 1 ->mknod /dev/ttyR2 c 46 2 - -The Linux script MAKEDEV will create the first 16 ttyRx device names (nodes) -for you: - ->/dev/MAKEDEV ttyR - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -ISA Rocketport Boards ---------------------- - -You must assign and configure the I/O addresses used by the ISA Rocketport -card before installing and using it. This is done by setting a set of DIP -switches on the Rocketport board. - - -SETTING THE I/O ADDRESS ------------------------ - -Before installing RocketPort(R) or RocketPort RA boards, you must find -a range of I/O addresses for it to use. The first RocketPort card -requires a 68-byte contiguous block of I/O addresses, starting at one -of the following: 0x100h, 0x140h, 0x180h, 0x200h, 0x240h, 0x280h, -0x300h, 0x340h, 0x380h. This I/O address must be reflected in the DIP -switches of *all* of the Rocketport cards. - -The second, third, and fourth RocketPort cards require a 64-byte -contiguous block of I/O addresses, starting at one of the following -I/O addresses: 0x100h, 0x140h, 0x180h, 0x1C0h, 0x200h, 0x240h, 0x280h, -0x2C0h, 0x300h, 0x340h, 0x380h, 0x3C0h. The I/O address used by the -second, third, and fourth Rocketport cards (if present) are set via -software control. The DIP switch settings for the I/O address must be -set to the value of the first Rocketport cards. - -In order to distinguish each of the card from the others, each card -must have a unique board ID set on the dip switches. The first -Rocketport board must be set with the DIP switches corresponding to -the first board, the second board must be set with the DIP switches -corresponding to the second board, etc. IMPORTANT: The board ID is -the only place where the DIP switch settings should differ between the -various Rocketport boards in a system. - -The I/O address range used by any of the RocketPort cards must not -conflict with any other cards in the system, including other -RocketPort cards. Below, you will find a list of commonly used I/O -address ranges which may be in use by other devices in your system. -On a Linux system, "cat /proc/ioports" will also be helpful in -identifying what I/O addresses are being used by devices on your -system. - -Remember, the FIRST RocketPort uses 68 I/O addresses. So, if you set it -for 0x100, it will occupy 0x100 to 0x143. This would mean that you -CAN NOT set the second, third or fourth board for address 0x140 since -the first 4 bytes of that range are used by the first board. You would -need to set the second, third, or fourth board to one of the next available -blocks such as 0x180. - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -RocketPort and RocketPort RA SW1 Settings: - - +-------------------------------+ - | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - +-------+-------+---------------+ - | Unused| Card | I/O Port Block| - +-------------------------------+ - -DIP Switches DIP Switches -7 8 6 5 -=================== =================== -On On UNUSED, MUST BE ON. On On First Card <==== Default - On Off Second Card - Off On Third Card - Off Off Fourth Card - -DIP Switches I/O Address Range -4 3 2 1 Used by the First Card -===================================== -On Off On Off 100-143 -On Off Off On 140-183 -On Off Off Off 180-1C3 <==== Default -Off On On Off 200-243 -Off On Off On 240-283 -Off On Off Off 280-2C3 -Off Off On Off 300-343 -Off Off Off On 340-383 -Off Off Off Off 380-3C3 - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -REPORTING BUGS --------------- - -For technical support, please provide the following -information: Driver version, kernel release, distribution of -kernel, and type of board you are using. Error messages and log -printouts port configuration details are especially helpful. - -USA - Phone: (612) 494-4100 - FAX: (612) 494-4199 - email: support@comtrol.com - -Comtrol Europe - Phone: +44 (0) 1 869 323-220 - FAX: +44 (0) 1 869 323-211 - email: support@comtrol.co.uk - -Web: http://www.comtrol.com -FTP: ftp.comtrol.com - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - - diff --git a/Documentation/serial/serial-iso7816.rst b/Documentation/serial/serial-iso7816.rst new file mode 100644 index 000000000000..d990143de0c6 --- /dev/null +++ b/Documentation/serial/serial-iso7816.rst @@ -0,0 +1,90 @@ +============================= +ISO7816 Serial Communications +============================= + +1. Introduction +=============== + + ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC) + also known as smart cards. + +2. Hardware-related considerations +================================== + + Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of + handling communication with a smart card. + + For these microcontrollers, the Linux driver should be made capable of + working in both modes, and proper ioctls (see later) should be made + available at user-level to allow switching from one mode to the other, and + vice versa. + +3. Data Structures Already Available in the Kernel +================================================== + + The Linux kernel provides the serial_iso7816 structure (see [1]) to handle + ISO7816 communications. This data structure is used to set and configure + ISO7816 parameters in ioctls. + + Any driver for devices capable of working both as RS232 and ISO7816 should + implement the iso7816_config callback in the uart_port structure. The + serial_core calls iso7816_config to do the device specific part in response + to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config + callback receives a pointer to struct serial_iso7816. + +4. Usage from user-level +======================== + + From user-level, ISO7816 configuration can be get/set using the previous + ioctls. For instance, to set ISO7816 you can use the following code:: + + #include + + /* Include definition for ISO7816 ioctls: TIOCSISO7816 and TIOCGISO7816 */ + #include + + /* Open your specific device (e.g., /dev/mydevice): */ + int fd = open ("/dev/mydevice", O_RDWR); + if (fd < 0) { + /* Error handling. See errno. */ + } + + struct serial_iso7816 iso7816conf; + + /* Reserved fields as to be zeroed */ + memset(&iso7816conf, 0, sizeof(iso7816conf)); + + /* Enable ISO7816 mode: */ + iso7816conf.flags |= SER_ISO7816_ENABLED; + + /* Select the protocol: */ + /* T=0 */ + iso7816conf.flags |= SER_ISO7816_T(0); + /* or T=1 */ + iso7816conf.flags |= SER_ISO7816_T(1); + + /* Set the guard time: */ + iso7816conf.tg = 2; + + /* Set the clock frequency*/ + iso7816conf.clk = 3571200; + + /* Set transmission factors: */ + iso7816conf.sc_fi = 372; + iso7816conf.sc_di = 1; + + if (ioctl(fd_usart, TIOCSISO7816, &iso7816conf) < 0) { + /* Error handling. See errno. */ + } + + /* Use read() and write() syscalls here... */ + + /* Close the device when finished: */ + if (close (fd) < 0) { + /* Error handling. See errno. */ + } + +5. References +============= + + [1] include/uapi/linux/serial.h diff --git a/Documentation/serial/serial-iso7816.txt b/Documentation/serial/serial-iso7816.txt deleted file mode 100644 index 3193d24a2b0f..000000000000 --- a/Documentation/serial/serial-iso7816.txt +++ /dev/null @@ -1,83 +0,0 @@ - ISO7816 SERIAL COMMUNICATIONS - -1. INTRODUCTION - - ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC) - also known as smart cards. - -2. HARDWARE-RELATED CONSIDERATIONS - - Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of - handling communication with a smart card. - - For these microcontrollers, the Linux driver should be made capable of - working in both modes, and proper ioctls (see later) should be made - available at user-level to allow switching from one mode to the other, and - vice versa. - -3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL - - The Linux kernel provides the serial_iso7816 structure (see [1]) to handle - ISO7816 communications. This data structure is used to set and configure - ISO7816 parameters in ioctls. - - Any driver for devices capable of working both as RS232 and ISO7816 should - implement the iso7816_config callback in the uart_port structure. The - serial_core calls iso7816_config to do the device specific part in response - to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config - callback receives a pointer to struct serial_iso7816. - -4. USAGE FROM USER-LEVEL - - From user-level, ISO7816 configuration can be get/set using the previous - ioctls. For instance, to set ISO7816 you can use the following code: - - #include - - /* Include definition for ISO7816 ioctls: TIOCSISO7816 and TIOCGISO7816 */ - #include - - /* Open your specific device (e.g., /dev/mydevice): */ - int fd = open ("/dev/mydevice", O_RDWR); - if (fd < 0) { - /* Error handling. See errno. */ - } - - struct serial_iso7816 iso7816conf; - - /* Reserved fields as to be zeroed */ - memset(&iso7816conf, 0, sizeof(iso7816conf)); - - /* Enable ISO7816 mode: */ - iso7816conf.flags |= SER_ISO7816_ENABLED; - - /* Select the protocol: */ - /* T=0 */ - iso7816conf.flags |= SER_ISO7816_T(0); - /* or T=1 */ - iso7816conf.flags |= SER_ISO7816_T(1); - - /* Set the guard time: */ - iso7816conf.tg = 2; - - /* Set the clock frequency*/ - iso7816conf.clk = 3571200; - - /* Set transmission factors: */ - iso7816conf.sc_fi = 372; - iso7816conf.sc_di = 1; - - if (ioctl(fd_usart, TIOCSISO7816, &iso7816conf) < 0) { - /* Error handling. See errno. */ - } - - /* Use read() and write() syscalls here... */ - - /* Close the device when finished: */ - if (close (fd) < 0) { - /* Error handling. See errno. */ - } - -5. REFERENCES - - [1] include/uapi/linux/serial.h diff --git a/Documentation/serial/serial-rs485.rst b/Documentation/serial/serial-rs485.rst new file mode 100644 index 000000000000..6bc824f948f9 --- /dev/null +++ b/Documentation/serial/serial-rs485.rst @@ -0,0 +1,103 @@ +=========================== +RS485 Serial Communications +=========================== + +1. Introduction +=============== + + EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the + electrical characteristics of drivers and receivers for use in balanced + digital multipoint systems. + This standard is widely used for communications in industrial automation + because it can be used effectively over long distances and in electrically + noisy environments. + +2. Hardware-related Considerations +================================== + + Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in + half-duplex mode capable of automatically controlling line direction by + toggling RTS or DTR signals. That can be used to control external + half-duplex hardware like an RS485 transceiver or any RS232-connected + half-duplex devices like some modems. + + For these microcontrollers, the Linux driver should be made capable of + working in both modes, and proper ioctls (see later) should be made + available at user-level to allow switching from one mode to the other, and + vice versa. + +3. Data Structures Already Available in the Kernel +================================================== + + The Linux kernel provides the serial_rs485 structure (see [1]) to handle + RS485 communications. This data structure is used to set and configure RS485 + parameters in the platform data and in ioctls. + + The device tree can also provide RS485 boot time parameters (see [2] + for bindings). The driver is in charge of filling this data structure from + the values given by the device tree. + + Any driver for devices capable of working both as RS232 and RS485 should + implement the rs485_config callback in the uart_port structure. The + serial_core calls rs485_config to do the device specific part in response + to TIOCSRS485 and TIOCGRS485 ioctls (see below). The rs485_config callback + receives a pointer to struct serial_rs485. + +4. Usage from user-level +======================== + + From user-level, RS485 configuration can be get/set using the previous + ioctls. For instance, to set RS485 you can use the following code:: + + #include + + /* Include definition for RS485 ioctls: TIOCGRS485 and TIOCSRS485 */ + #include + + /* Open your specific device (e.g., /dev/mydevice): */ + int fd = open ("/dev/mydevice", O_RDWR); + if (fd < 0) { + /* Error handling. See errno. */ + } + + struct serial_rs485 rs485conf; + + /* Enable RS485 mode: */ + rs485conf.flags |= SER_RS485_ENABLED; + + /* Set logical level for RTS pin equal to 1 when sending: */ + rs485conf.flags |= SER_RS485_RTS_ON_SEND; + /* or, set logical level for RTS pin equal to 0 when sending: */ + rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND); + + /* Set logical level for RTS pin equal to 1 after sending: */ + rs485conf.flags |= SER_RS485_RTS_AFTER_SEND; + /* or, set logical level for RTS pin equal to 0 after sending: */ + rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND); + + /* Set rts delay before send, if needed: */ + rs485conf.delay_rts_before_send = ...; + + /* Set rts delay after send, if needed: */ + rs485conf.delay_rts_after_send = ...; + + /* Set this flag if you want to receive data even while sending data */ + rs485conf.flags |= SER_RS485_RX_DURING_TX; + + if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) { + /* Error handling. See errno. */ + } + + /* Use read() and write() syscalls here... */ + + /* Close the device when finished: */ + if (close (fd) < 0) { + /* Error handling. See errno. */ + } + +5. References +============= + + [1] include/uapi/linux/serial.h + + [2] Documentation/devicetree/bindings/serial/rs485.txt diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt deleted file mode 100644 index ce0c1a9b8aab..000000000000 --- a/Documentation/serial/serial-rs485.txt +++ /dev/null @@ -1,95 +0,0 @@ - RS485 SERIAL COMMUNICATIONS - -1. INTRODUCTION - - EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the - electrical characteristics of drivers and receivers for use in balanced - digital multipoint systems. - This standard is widely used for communications in industrial automation - because it can be used effectively over long distances and in electrically - noisy environments. - -2. HARDWARE-RELATED CONSIDERATIONS - - Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in - half-duplex mode capable of automatically controlling line direction by - toggling RTS or DTR signals. That can be used to control external - half-duplex hardware like an RS485 transceiver or any RS232-connected - half-duplex devices like some modems. - - For these microcontrollers, the Linux driver should be made capable of - working in both modes, and proper ioctls (see later) should be made - available at user-level to allow switching from one mode to the other, and - vice versa. - -3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL - - The Linux kernel provides the serial_rs485 structure (see [1]) to handle - RS485 communications. This data structure is used to set and configure RS485 - parameters in the platform data and in ioctls. - - The device tree can also provide RS485 boot time parameters (see [2] - for bindings). The driver is in charge of filling this data structure from - the values given by the device tree. - - Any driver for devices capable of working both as RS232 and RS485 should - implement the rs485_config callback in the uart_port structure. The - serial_core calls rs485_config to do the device specific part in response - to TIOCSRS485 and TIOCGRS485 ioctls (see below). The rs485_config callback - receives a pointer to struct serial_rs485. - -4. USAGE FROM USER-LEVEL - - From user-level, RS485 configuration can be get/set using the previous - ioctls. For instance, to set RS485 you can use the following code: - - #include - - /* Include definition for RS485 ioctls: TIOCGRS485 and TIOCSRS485 */ - #include - - /* Open your specific device (e.g., /dev/mydevice): */ - int fd = open ("/dev/mydevice", O_RDWR); - if (fd < 0) { - /* Error handling. See errno. */ - } - - struct serial_rs485 rs485conf; - - /* Enable RS485 mode: */ - rs485conf.flags |= SER_RS485_ENABLED; - - /* Set logical level for RTS pin equal to 1 when sending: */ - rs485conf.flags |= SER_RS485_RTS_ON_SEND; - /* or, set logical level for RTS pin equal to 0 when sending: */ - rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND); - - /* Set logical level for RTS pin equal to 1 after sending: */ - rs485conf.flags |= SER_RS485_RTS_AFTER_SEND; - /* or, set logical level for RTS pin equal to 0 after sending: */ - rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND); - - /* Set rts delay before send, if needed: */ - rs485conf.delay_rts_before_send = ...; - - /* Set rts delay after send, if needed: */ - rs485conf.delay_rts_after_send = ...; - - /* Set this flag if you want to receive data even while sending data */ - rs485conf.flags |= SER_RS485_RX_DURING_TX; - - if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) { - /* Error handling. See errno. */ - } - - /* Use read() and write() syscalls here... */ - - /* Close the device when finished: */ - if (close (fd) < 0) { - /* Error handling. See errno. */ - } - -5. REFERENCES - - [1] include/uapi/linux/serial.h - [2] Documentation/devicetree/bindings/serial/rs485.txt diff --git a/Documentation/serial/tty.rst b/Documentation/serial/tty.rst new file mode 100644 index 000000000000..dd972caacf3e --- /dev/null +++ b/Documentation/serial/tty.rst @@ -0,0 +1,328 @@ +================= +The Lockronomicon +================= + +Your guide to the ancient and twisted locking policies of the tty layer and +the warped logic behind them. Beware all ye who read on. + + +Line Discipline +--------------- + +Line disciplines are registered with tty_register_ldisc() passing the +discipline number and the ldisc structure. At the point of registration the +discipline must be ready to use and it is possible it will get used before +the call returns success. If the call returns an error then it won't get +called. Do not re-use ldisc numbers as they are part of the userspace ABI +and writing over an existing ldisc will cause demons to eat your computer. +After the return the ldisc data has been copied so you may free your own +copy of the structure. You must not re-register over the top of the line +discipline even with the same data or your computer again will be eaten by +demons. + +In order to remove a line discipline call tty_unregister_ldisc(). +In ancient times this always worked. In modern times the function will +return -EBUSY if the ldisc is currently in use. Since the ldisc referencing +code manages the module counts this should not usually be a concern. + +Heed this warning: the reference count field of the registered copies of the +tty_ldisc structure in the ldisc table counts the number of lines using this +discipline. The reference count of the tty_ldisc structure within a tty +counts the number of active users of the ldisc at this instant. In effect it +counts the number of threads of execution within an ldisc method (plus those +about to enter and exit although this detail matters not). + +Line Discipline Methods +----------------------- + +TTY side interfaces +^^^^^^^^^^^^^^^^^^^ + +======================= ======================================================= +open() Called when the line discipline is attached to + the terminal. No other call into the line + discipline for this tty will occur until it + completes successfully. Should initialize any + state needed by the ldisc, and set receive_room + in the tty_struct to the maximum amount of data + the line discipline is willing to accept from the + driver with a single call to receive_buf(). + Returning an error will prevent the ldisc from + being attached. Can sleep. + +close() This is called on a terminal when the line + discipline is being unplugged. At the point of + execution no further users will enter the + ldisc code for this tty. Can sleep. + +hangup() Called when the tty line is hung up. + The line discipline should cease I/O to the tty. + No further calls into the ldisc code will occur. + The return value is ignored. Can sleep. + +read() (optional) A process requests reading data from + the line. Multiple read calls may occur in parallel + and the ldisc must deal with serialization issues. + If not defined, the process will receive an EIO + error. May sleep. + +write() (optional) A process requests writing data to the + line. Multiple write calls are serialized by the + tty layer for the ldisc. If not defined, the + process will receive an EIO error. May sleep. + +flush_buffer() (optional) May be called at any point between + open and close, and instructs the line discipline + to empty its input buffer. + +set_termios() (optional) Called on termios structure changes. + The caller passes the old termios data and the + current data is in the tty. Called under the + termios semaphore so allowed to sleep. Serialized + against itself only. + +poll() (optional) Check the status for the poll/select + calls. Multiple poll calls may occur in parallel. + May sleep. + +ioctl() (optional) Called when an ioctl is handed to the + tty layer that might be for the ldisc. Multiple + ioctl calls may occur in parallel. May sleep. + +compat_ioctl() (optional) Called when a 32 bit ioctl is handed + to the tty layer that might be for the ldisc. + Multiple ioctl calls may occur in parallel. + May sleep. +======================= ======================================================= + +Driver Side Interfaces +^^^^^^^^^^^^^^^^^^^^^^ + +======================= ======================================================= +receive_buf() (optional) Called by the low-level driver to hand + a buffer of received bytes to the ldisc for + processing. The number of bytes is guaranteed not + to exceed the current value of tty->receive_room. + All bytes must be processed. + +receive_buf2() (optional) Called by the low-level driver to hand + a buffer of received bytes to the ldisc for + processing. Returns the number of bytes processed. + + If both receive_buf() and receive_buf2() are + defined, receive_buf2() should be preferred. + +write_wakeup() May be called at any point between open and close. + The TTY_DO_WRITE_WAKEUP flag indicates if a call + is needed but always races versus calls. Thus the + ldisc must be careful about setting order and to + handle unexpected calls. Must not sleep. + + The driver is forbidden from calling this directly + from the ->write call from the ldisc as the ldisc + is permitted to call the driver write method from + this function. In such a situation defer it. + +dcd_change() Report to the tty line the current DCD pin status + changes and the relative timestamp. The timestamp + cannot be NULL. +======================= ======================================================= + + +Driver Access +^^^^^^^^^^^^^ + +Line discipline methods can call the following methods of the underlying +hardware driver through the function pointers within the tty->driver +structure: + +======================= ======================================================= +write() Write a block of characters to the tty device. + Returns the number of characters accepted. The + character buffer passed to this method is already + in kernel space. + +put_char() Queues a character for writing to the tty device. + If there is no room in the queue, the character is + ignored. + +flush_chars() (Optional) If defined, must be called after + queueing characters with put_char() in order to + start transmission. + +write_room() Returns the numbers of characters the tty driver + will accept for queueing to be written. + +ioctl() Invoke device specific ioctl. + Expects data pointers to refer to userspace. + Returns ENOIOCTLCMD for unrecognized ioctl numbers. + +set_termios() Notify the tty driver that the device's termios + settings have changed. New settings are in + tty->termios. Previous settings should be passed in + the "old" argument. + + The API is defined such that the driver should return + the actual modes selected. This means that the + driver function is responsible for modifying any + bits in the request it cannot fulfill to indicate + the actual modes being used. A device with no + hardware capability for change (e.g. a USB dongle or + virtual port) can provide NULL for this method. + +throttle() Notify the tty driver that input buffers for the + line discipline are close to full, and it should + somehow signal that no more characters should be + sent to the tty. + +unthrottle() Notify the tty driver that characters can now be + sent to the tty without fear of overrunning the + input buffers of the line disciplines. + +stop() Ask the tty driver to stop outputting characters + to the tty device. + +start() Ask the tty driver to resume sending characters + to the tty device. + +hangup() Ask the tty driver to hang up the tty device. + +break_ctl() (Optional) Ask the tty driver to turn on or off + BREAK status on the RS-232 port. If state is -1, + then the BREAK status should be turned on; if + state is 0, then BREAK should be turned off. + If this routine is not implemented, use ioctls + TIOCSBRK / TIOCCBRK instead. + +wait_until_sent() Waits until the device has written out all of the + characters in its transmitter FIFO. + +send_xchar() Send a high-priority XON/XOFF character to the device. +======================= ======================================================= + + +Flags +^^^^^ + +Line discipline methods have access to tty->flags field containing the +following interesting flags: + +======================= ======================================================= +TTY_THROTTLED Driver input is throttled. The ldisc should call + tty->driver->unthrottle() in order to resume + reception when it is ready to process more data. + +TTY_DO_WRITE_WAKEUP If set, causes the driver to call the ldisc's + write_wakeup() method in order to resume + transmission when it can accept more data + to transmit. + +TTY_IO_ERROR If set, causes all subsequent userspace read/write + calls on the tty to fail, returning -EIO. + +TTY_OTHER_CLOSED Device is a pty and the other side has closed. + +TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into + smaller chunks. +======================= ======================================================= + + +Locking +^^^^^^^ + +Callers to the line discipline functions from the tty layer are required to +take line discipline locks. The same is true of calls from the driver side +but not yet enforced. + +Three calls are now provided:: + + ldisc = tty_ldisc_ref(tty); + +takes a handle to the line discipline in the tty and returns it. If no ldisc +is currently attached or the ldisc is being closed and re-opened at this +point then NULL is returned. While this handle is held the ldisc will not +change or go away:: + + tty_ldisc_deref(ldisc) + +Returns the ldisc reference and allows the ldisc to be closed. Returning the +reference takes away your right to call the ldisc functions until you take +a new reference:: + + ldisc = tty_ldisc_ref_wait(tty); + +Performs the same function as tty_ldisc_ref except that it will wait for an +ldisc change to complete and then return a reference to the new ldisc. + +While these functions are slightly slower than the old code they should have +minimal impact as most receive logic uses the flip buffers and they only +need to take a reference when they push bits up through the driver. + +A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc +functions are called with the ldisc unavailable. Thus tty_ldisc_ref will +fail in this situation if used within these functions. Ldisc and driver +code calling its own functions must be careful in this case. + + +Driver Interface +---------------- + +======================= ======================================================= +open() Called when a device is opened. May sleep + +close() Called when a device is closed. At the point of + return from this call the driver must make no + further ldisc calls of any kind. May sleep + +write() Called to write bytes to the device. May not + sleep. May occur in parallel in special cases. + Because this includes panic paths drivers generally + shouldn't try and do clever locking here. + +put_char() Stuff a single character onto the queue. The + driver is guaranteed following up calls to + flush_chars. + +flush_chars() Ask the kernel to write put_char queue + +write_room() Return the number of characters that can be stuffed + into the port buffers without overflow (or less). + The ldisc is responsible for being intelligent + about multi-threading of write_room/write calls + +ioctl() Called when an ioctl may be for the driver + +set_termios() Called on termios change, serialized against + itself by a semaphore. May sleep. + +set_ldisc() Notifier for discipline change. At the point this + is done the discipline is not yet usable. Can now + sleep (I think) + +throttle() Called by the ldisc to ask the driver to do flow + control. Serialization including with unthrottle + is the job of the ldisc layer. + +unthrottle() Called by the ldisc to ask the driver to stop flow + control. + +stop() Ldisc notifier to the driver to stop output. As with + throttle the serializations with start() are down + to the ldisc layer. + +start() Ldisc notifier to the driver to start output. + +hangup() Ask the tty driver to cause a hangup initiated + from the host side. [Can sleep ??] + +break_ctl() Send RS232 break. Can sleep. Can get called in + parallel, driver must serialize (for now), and + with write calls. + +wait_until_sent() Wait for characters to exit the hardware queue + of the driver. Can sleep + +send_xchar() Send XON/XOFF and if possible jump the queue with + it in order to get fast flow control responses. + Cannot sleep ?? +======================= ======================================================= diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt deleted file mode 100644 index b48780977a68..000000000000 --- a/Documentation/serial/tty.txt +++ /dev/null @@ -1,313 +0,0 @@ - - The Lockronomicon - -Your guide to the ancient and twisted locking policies of the tty layer and -the warped logic behind them. Beware all ye who read on. - - -Line Discipline ---------------- - -Line disciplines are registered with tty_register_ldisc() passing the -discipline number and the ldisc structure. At the point of registration the -discipline must be ready to use and it is possible it will get used before -the call returns success. If the call returns an error then it won't get -called. Do not re-use ldisc numbers as they are part of the userspace ABI -and writing over an existing ldisc will cause demons to eat your computer. -After the return the ldisc data has been copied so you may free your own -copy of the structure. You must not re-register over the top of the line -discipline even with the same data or your computer again will be eaten by -demons. - -In order to remove a line discipline call tty_unregister_ldisc(). -In ancient times this always worked. In modern times the function will -return -EBUSY if the ldisc is currently in use. Since the ldisc referencing -code manages the module counts this should not usually be a concern. - -Heed this warning: the reference count field of the registered copies of the -tty_ldisc structure in the ldisc table counts the number of lines using this -discipline. The reference count of the tty_ldisc structure within a tty -counts the number of active users of the ldisc at this instant. In effect it -counts the number of threads of execution within an ldisc method (plus those -about to enter and exit although this detail matters not). - -Line Discipline Methods ------------------------ - -TTY side interfaces: - -open() - Called when the line discipline is attached to - the terminal. No other call into the line - discipline for this tty will occur until it - completes successfully. Should initialize any - state needed by the ldisc, and set receive_room - in the tty_struct to the maximum amount of data - the line discipline is willing to accept from the - driver with a single call to receive_buf(). - Returning an error will prevent the ldisc from - being attached. Can sleep. - -close() - This is called on a terminal when the line - discipline is being unplugged. At the point of - execution no further users will enter the - ldisc code for this tty. Can sleep. - -hangup() - Called when the tty line is hung up. - The line discipline should cease I/O to the tty. - No further calls into the ldisc code will occur. - The return value is ignored. Can sleep. - -read() - (optional) A process requests reading data from - the line. Multiple read calls may occur in parallel - and the ldisc must deal with serialization issues. - If not defined, the process will receive an EIO - error. May sleep. - -write() - (optional) A process requests writing data to the - line. Multiple write calls are serialized by the - tty layer for the ldisc. If not defined, the - process will receive an EIO error. May sleep. - -flush_buffer() - (optional) May be called at any point between - open and close, and instructs the line discipline - to empty its input buffer. - -set_termios() - (optional) Called on termios structure changes. - The caller passes the old termios data and the - current data is in the tty. Called under the - termios semaphore so allowed to sleep. Serialized - against itself only. - -poll() - (optional) Check the status for the poll/select - calls. Multiple poll calls may occur in parallel. - May sleep. - -ioctl() - (optional) Called when an ioctl is handed to the - tty layer that might be for the ldisc. Multiple - ioctl calls may occur in parallel. May sleep. - -compat_ioctl() - (optional) Called when a 32 bit ioctl is handed - to the tty layer that might be for the ldisc. - Multiple ioctl calls may occur in parallel. - May sleep. - -Driver Side Interfaces: - -receive_buf() - (optional) Called by the low-level driver to hand - a buffer of received bytes to the ldisc for - processing. The number of bytes is guaranteed not - to exceed the current value of tty->receive_room. - All bytes must be processed. - -receive_buf2() - (optional) Called by the low-level driver to hand - a buffer of received bytes to the ldisc for - processing. Returns the number of bytes processed. - - If both receive_buf() and receive_buf2() are - defined, receive_buf2() should be preferred. - -write_wakeup() - May be called at any point between open and close. - The TTY_DO_WRITE_WAKEUP flag indicates if a call - is needed but always races versus calls. Thus the - ldisc must be careful about setting order and to - handle unexpected calls. Must not sleep. - - The driver is forbidden from calling this directly - from the ->write call from the ldisc as the ldisc - is permitted to call the driver write method from - this function. In such a situation defer it. - -dcd_change() - Report to the tty line the current DCD pin status - changes and the relative timestamp. The timestamp - cannot be NULL. - - -Driver Access - -Line discipline methods can call the following methods of the underlying -hardware driver through the function pointers within the tty->driver -structure: - -write() Write a block of characters to the tty device. - Returns the number of characters accepted. The - character buffer passed to this method is already - in kernel space. - -put_char() Queues a character for writing to the tty device. - If there is no room in the queue, the character is - ignored. - -flush_chars() (Optional) If defined, must be called after - queueing characters with put_char() in order to - start transmission. - -write_room() Returns the numbers of characters the tty driver - will accept for queueing to be written. - -ioctl() Invoke device specific ioctl. - Expects data pointers to refer to userspace. - Returns ENOIOCTLCMD for unrecognized ioctl numbers. - -set_termios() Notify the tty driver that the device's termios - settings have changed. New settings are in - tty->termios. Previous settings should be passed in - the "old" argument. - - The API is defined such that the driver should return - the actual modes selected. This means that the - driver function is responsible for modifying any - bits in the request it cannot fulfill to indicate - the actual modes being used. A device with no - hardware capability for change (e.g. a USB dongle or - virtual port) can provide NULL for this method. - -throttle() Notify the tty driver that input buffers for the - line discipline are close to full, and it should - somehow signal that no more characters should be - sent to the tty. - -unthrottle() Notify the tty driver that characters can now be - sent to the tty without fear of overrunning the - input buffers of the line disciplines. - -stop() Ask the tty driver to stop outputting characters - to the tty device. - -start() Ask the tty driver to resume sending characters - to the tty device. - -hangup() Ask the tty driver to hang up the tty device. - -break_ctl() (Optional) Ask the tty driver to turn on or off - BREAK status on the RS-232 port. If state is -1, - then the BREAK status should be turned on; if - state is 0, then BREAK should be turned off. - If this routine is not implemented, use ioctls - TIOCSBRK / TIOCCBRK instead. - -wait_until_sent() Waits until the device has written out all of the - characters in its transmitter FIFO. - -send_xchar() Send a high-priority XON/XOFF character to the device. - - -Flags - -Line discipline methods have access to tty->flags field containing the -following interesting flags: - -TTY_THROTTLED Driver input is throttled. The ldisc should call - tty->driver->unthrottle() in order to resume - reception when it is ready to process more data. - -TTY_DO_WRITE_WAKEUP If set, causes the driver to call the ldisc's - write_wakeup() method in order to resume - transmission when it can accept more data - to transmit. - -TTY_IO_ERROR If set, causes all subsequent userspace read/write - calls on the tty to fail, returning -EIO. - -TTY_OTHER_CLOSED Device is a pty and the other side has closed. - -TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into - smaller chunks. - - -Locking - -Callers to the line discipline functions from the tty layer are required to -take line discipline locks. The same is true of calls from the driver side -but not yet enforced. - -Three calls are now provided - - ldisc = tty_ldisc_ref(tty); - -takes a handle to the line discipline in the tty and returns it. If no ldisc -is currently attached or the ldisc is being closed and re-opened at this -point then NULL is returned. While this handle is held the ldisc will not -change or go away. - - tty_ldisc_deref(ldisc) - -Returns the ldisc reference and allows the ldisc to be closed. Returning the -reference takes away your right to call the ldisc functions until you take -a new reference. - - ldisc = tty_ldisc_ref_wait(tty); - -Performs the same function as tty_ldisc_ref except that it will wait for an -ldisc change to complete and then return a reference to the new ldisc. - -While these functions are slightly slower than the old code they should have -minimal impact as most receive logic uses the flip buffers and they only -need to take a reference when they push bits up through the driver. - -A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc -functions are called with the ldisc unavailable. Thus tty_ldisc_ref will -fail in this situation if used within these functions. Ldisc and driver -code calling its own functions must be careful in this case. - - -Driver Interface ----------------- - -open() - Called when a device is opened. May sleep - -close() - Called when a device is closed. At the point of - return from this call the driver must make no - further ldisc calls of any kind. May sleep - -write() - Called to write bytes to the device. May not - sleep. May occur in parallel in special cases. - Because this includes panic paths drivers generally - shouldn't try and do clever locking here. - -put_char() - Stuff a single character onto the queue. The - driver is guaranteed following up calls to - flush_chars. - -flush_chars() - Ask the kernel to write put_char queue - -write_room() - Return the number of characters that can be stuffed - into the port buffers without overflow (or less). - The ldisc is responsible for being intelligent - about multi-threading of write_room/write calls - -ioctl() - Called when an ioctl may be for the driver - -set_termios() - Called on termios change, serialized against - itself by a semaphore. May sleep. - -set_ldisc() - Notifier for discipline change. At the point this - is done the discipline is not yet usable. Can now - sleep (I think) - -throttle() - Called by the ldisc to ask the driver to do flow - control. Serialization including with unthrottle - is the job of the ldisc layer. - -unthrottle() - Called by the ldisc to ask the driver to stop flow - control. - -stop() - Ldisc notifier to the driver to stop output. As with - throttle the serializations with start() are down - to the ldisc layer. - -start() - Ldisc notifier to the driver to start output. - -hangup() - Ask the tty driver to cause a hangup initiated - from the host side. [Can sleep ??] - -break_ctl() - Send RS232 break. Can sleep. Can get called in - parallel, driver must serialize (for now), and - with write calls. - -wait_until_sent() - Wait for characters to exit the hardware queue - of the driver. Can sleep - -send_xchar() - Send XON/XOFF and if possible jump the queue with - it in order to get fast flow control responses. - Cannot sleep ?? - diff --git a/MAINTAINERS b/MAINTAINERS index 09f43f1bdd15..be2a1176410e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10433,7 +10433,7 @@ F: include/uapi/linux/meye.h MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD M: Jiri Slaby S: Maintained -F: Documentation/serial/moxa-smartio +F: Documentation/serial/moxa-smartio.rst F: drivers/tty/mxser.* MR800 AVERMEDIA USB FM RADIO DRIVER @@ -13317,7 +13317,7 @@ ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com S: Maintained -F: Documentation/serial/rocket.txt +F: Documentation/serial/rocket.rst F: drivers/tty/rocket* ROCKETPORT EXPRESS/INFINITY DRIVER diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 80f65767aacf..3b1d312bb175 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -175,7 +175,7 @@ config ROCKETPORT This driver supports Comtrol RocketPort and RocketModem PCI boards. These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or modems. For information about the RocketPort/RocketModem boards - and this driver read . + and this driver read . To compile this driver as a module, choose M here: the module will be called rocket. @@ -193,7 +193,7 @@ config CYCLADES your Linux box, for instance in order to become a dial-in server. For information about the Cyclades-Z card, read - . + . To compile this driver as a module, choose M here: the module will be called cyclades. diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 2b6376e6e5ad..6e3c66ab0e62 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1081,7 +1081,7 @@ static int qe_uart_verify_port(struct uart_port *port, } /* UART operations * - * Details on these functions can be found in Documentation/serial/driver + * Details on these functions can be found in Documentation/serial/driver.rst */ static const struct uart_ops qe_uart_pops = { .tx_empty = qe_uart_tx_empty, diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 5fe2b037e833..fea2216a893f 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -45,7 +45,7 @@ struct device; /* * This structure describes all the operations that can be done on the - * physical hardware. See Documentation/serial/driver for details. + * physical hardware. See Documentation/serial/driver.rst for details. */ struct uart_ops { unsigned int (*tx_empty)(struct uart_port *); -- cgit v1.2.3 From 8ceeb4709845e38560a68b6c1b30cd607282abc0 Mon Sep 17 00:00:00 2001 From: Long Cheng Date: Thu, 25 Apr 2019 16:41:30 +0800 Subject: serial: 8250-mtk: add follow control Add SW and HW follow control function. Signed-off-by: Long Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 60 +++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index c1fdbc0b6840..959fd85310bf 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -21,12 +21,14 @@ #include "8250.h" -#define UART_MTK_HIGHS 0x09 /* Highspeed register */ -#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */ -#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ +#define MTK_UART_HIGHS 0x09 /* Highspeed register */ +#define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */ +#define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */ #define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ - #define MTK_UART_DMA_EN 0x13 /* DMA Enable register */ +#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */ +#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */ +#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */ #define MTK_UART_DMA_EN_TX 0x2 #define MTK_UART_DMA_EN_RX 0x5 @@ -46,6 +48,7 @@ enum dma_rx_status { struct mtk8250_data { int line; unsigned int rx_pos; + unsigned int clk_count; struct clk *uart_clk; struct clk *bus_clk; struct uart_8250_dma *dma; @@ -196,9 +199,15 @@ static void mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + unsigned short fraction_L_mapping[] = { + 0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF + }; + unsigned short fraction_M_mapping[] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3 + }; struct uart_8250_port *up = up_to_u8250p(port); + unsigned int baud, quot, fraction; unsigned long flags; - unsigned int baud, quot; #ifdef CONFIG_SERIAL_8250_DMA if (up->dma) { @@ -214,7 +223,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, serial8250_do_set_termios(port, termios, old); /* - * Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS) + * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS) * * We need to recalcualte the quot register, as the claculation depends * on the vaule in the highspeed register. @@ -230,18 +239,11 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, port->uartclk / 16 / UART_DIV_MAX, port->uartclk); - if (baud <= 115200) { - serial_port_out(port, UART_MTK_HIGHS, 0x0); + if (baud < 115200) { + serial_port_out(port, MTK_UART_HIGHS, 0x0); quot = uart_get_divisor(port, baud); - } else if (baud <= 576000) { - serial_port_out(port, UART_MTK_HIGHS, 0x2); - - /* Set to next lower baudrate supported */ - if ((baud == 500000) || (baud == 576000)) - baud = 460800; - quot = DIV_ROUND_UP(port->uartclk, 4 * baud); } else { - serial_port_out(port, UART_MTK_HIGHS, 0x3); + serial_port_out(port, MTK_UART_HIGHS, 0x3); quot = DIV_ROUND_UP(port->uartclk, 256 * baud); } @@ -258,17 +260,29 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, /* reset DLAB */ serial_port_out(port, UART_LCR, up->lcr); - if (baud > 460800) { + if (baud >= 115200) { unsigned int tmp; - tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud); - serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1); - serial_port_out(port, UART_MTK_SAMPLE_POINT, - (tmp - 2) >> 1); + tmp = (port->uartclk / (baud * quot)) - 1; + serial_port_out(port, MTK_UART_SAMPLE_COUNT, tmp); + serial_port_out(port, MTK_UART_SAMPLE_POINT, + (tmp >> 1) - 1); + + /*count fraction to set fractoin register */ + fraction = ((port->uartclk * 100) / baud / quot) % 100; + fraction = DIV_ROUND_CLOSEST(fraction, 10); + serial_port_out(port, MTK_UART_FRACDIV_L, + fraction_L_mapping[fraction]); + serial_port_out(port, MTK_UART_FRACDIV_M, + fraction_M_mapping[fraction]); } else { - serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00); - serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff); + serial_port_out(port, MTK_UART_SAMPLE_COUNT, 0x00); + serial_port_out(port, MTK_UART_SAMPLE_POINT, 0xff); + serial_port_out(port, MTK_UART_FRACDIV_L, 0x00); + serial_port_out(port, MTK_UART_FRACDIV_M, 0x00); } + if (uart_console(port)) + up->port.cons->cflag = termios->c_cflag; spin_unlock_irqrestore(&port->lock, flags); /* Don't rewrite B0 */ -- cgit v1.2.3 From bdbd0a7f8f0342dd545cc3f1e47aa30265807846 Mon Sep 17 00:00:00 2001 From: Long Cheng Date: Thu, 25 Apr 2019 16:41:31 +0800 Subject: serial: 8250-mtk: modify baudrate setting In termios function, add Fractional divider to adjust baudrate. Signed-off-by: Long Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 959fd85310bf..417c7c810df9 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -25,13 +25,28 @@ #define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */ #define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */ #define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ +#define MTK_UART_ESCAPE_DAT 0x10 /* Escape Character register */ +#define MTK_UART_ESCAPE_EN 0x11 /* Escape Enable register */ #define MTK_UART_DMA_EN 0x13 /* DMA Enable register */ #define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */ #define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */ #define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */ +#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */ +#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */ +#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */ + +#define MTK_UART_EFR_EN 0x10 /* Enable enhancement feature */ +#define MTK_UART_EFR_RTS 0x40 /* Enable hardware rx flow control */ +#define MTK_UART_EFR_CTS 0x80 /* Enable hardware tx flow control */ +#define MTK_UART_EFR_NO_SW_FC 0x0 /* no sw flow control */ +#define MTK_UART_EFR_XON1_XOFF1 0xa /* XON1/XOFF1 as sw flow control */ +#define MTK_UART_EFR_XON2_XOFF2 0x5 /* XON2/XOFF2 as sw flow control */ +#define MTK_UART_EFR_SW_FC_MASK 0xf /* Enable CTS Modem status interrupt */ +#define MTK_UART_EFR_HW_FC (MTK_UART_EFR_RTS | MTK_UART_EFR_CTS) #define MTK_UART_DMA_EN_TX 0x2 #define MTK_UART_DMA_EN_RX 0x5 +#define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */ #define MTK_UART_TX_SIZE UART_XMIT_SIZE #define MTK_UART_RX_SIZE 0x8000 #define MTK_UART_TX_TRIGGER 1 @@ -57,6 +72,13 @@ struct mtk8250_data { #endif }; +/* flow control mode */ +enum { + MTK_UART_FC_NONE, + MTK_UART_FC_SW, + MTK_UART_FC_HW, +}; + #ifdef CONFIG_SERIAL_8250_DMA static void mtk8250_rx_dma(struct uart_8250_port *up); @@ -195,6 +217,75 @@ static void mtk8250_shutdown(struct uart_port *port) return serial8250_do_shutdown(port); } +static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) +{ + serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask)); +} + +static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask) +{ + serial_out(up, UART_IER, serial_in(up, UART_IER) | mask); +} + +static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode) +{ + struct uart_port *port = &up->port; + int lcr = serial_in(up, UART_LCR); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, lcr); + lcr = serial_in(up, UART_LCR); + + switch (mode) { + case MTK_UART_FC_NONE: + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x00); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, serial_in(up, UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))); + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI | + MTK_UART_IER_RTSI | MTK_UART_IER_CTSI); + break; + + case MTK_UART_FC_HW: + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x00); + serial_out(up, UART_MCR, UART_MCR_RTS); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /*enable hw flow control*/ + serial_out(up, UART_EFR, MTK_UART_EFR_HW_FC | + (serial_in(up, UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)))); + + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI); + mtk8250_enable_intrs(up, MTK_UART_IER_CTSI | MTK_UART_IER_RTSI); + break; + + case MTK_UART_FC_SW: /*MTK software flow control */ + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x01); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /*enable sw flow control */ + serial_out(up, UART_EFR, MTK_UART_EFR_XON1_XOFF1 | + (serial_in(up, UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)))); + + serial_out(up, UART_XON1, START_CHAR(port->state->port.tty)); + serial_out(up, UART_XOFF1, STOP_CHAR(port->state->port.tty)); + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_CTSI|MTK_UART_IER_RTSI); + mtk8250_enable_intrs(up, MTK_UART_IER_XOFFI); + break; + default: + break; + } +} + static void mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -208,6 +299,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, struct uart_8250_port *up = up_to_u8250p(port); unsigned int baud, quot, fraction; unsigned long flags; + int mode; #ifdef CONFIG_SERIAL_8250_DMA if (up->dma) { @@ -281,6 +373,16 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, MTK_UART_FRACDIV_L, 0x00); serial_port_out(port, MTK_UART_FRACDIV_M, 0x00); } + + if ((termios->c_cflag & CRTSCTS) && (!(termios->c_iflag & CRTSCTS))) + mode = MTK_UART_FC_HW; + else if (termios->c_iflag & CRTSCTS) + mode = MTK_UART_FC_SW; + else + mode = MTK_UART_FC_NONE; + + mtk8250_set_flow_ctrl(up, mode); + if (uart_console(port)) up->port.cons->cflag = termios->c_cflag; -- cgit v1.2.3 From 050dfc09f8a43ae41f966d87f81ef35dfad754d5 Mon Sep 17 00:00:00 2001 From: Sergey Organov Date: Tue, 23 Apr 2019 08:06:58 +0300 Subject: tty: serial_core: fix error code returned by uart_register_driver() uart_register_driver() returned -ENOMEM on any error, even when tty_register_driver() call returned another one, such as -EBUSY. Signed-off-by: Sergey Organov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0decb0bf991d..83f4dd0bfd74 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2507,7 +2507,7 @@ static const struct tty_port_operations uart_port_ops = { int uart_register_driver(struct uart_driver *drv) { struct tty_driver *normal; - int i, retval; + int i, retval = -ENOMEM; BUG_ON(drv->state); @@ -2559,7 +2559,7 @@ int uart_register_driver(struct uart_driver *drv) out_kfree: kfree(drv->state); out: - return -ENOMEM; + return retval; } /** -- cgit v1.2.3 From 8daa89e099708db1ffc694c812cb3c0737f1e22b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2019 18:05:54 +0200 Subject: tty: update obsolete termios comment Update an obsolete comment referring to the termios_locked structure which was removed over a decade ago by commit fe6e29fdb1a7 ("tty: simplify ktermios allocation"). While at it, fix the "Thus" typo. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 5fa250157025..033ac7e6a70d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1173,7 +1173,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, * tty_init_termios - helper for termios setup * @tty: the tty to set up * - * Initialise the termios structures for this tty. Thus runs under + * Initialise the termios structure for this tty. This runs under * the tty_mutex currently so we can be relaxed about ordering. */ -- cgit v1.2.3 From 29973f8a88b001ebc605c88cdee124f7256ecdbf Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Thu, 18 Apr 2019 12:35:57 +0800 Subject: tty: rocket: Remove RCPK_GET_STRUCT ioctl If the cmd is RCPK_GET_STRUCT, copy_to_user will copy info to user space. As info->port.ops is the address of a constant object rocket_port_ops (assigned in init_r_port), a kernel address leakage happens. Remove the RCPK_GET_STRUCT ioctl. Signed-off-by: Fuqian Huang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 4 ---- drivers/tty/rocket.h | 1 - 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index b121d8f8f3d7..b6543e28bd8b 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1283,10 +1283,6 @@ static int rp_ioctl(struct tty_struct *tty, return -ENXIO; switch (cmd) { - case RCKP_GET_STRUCT: - if (copy_to_user(argp, info, sizeof (struct r_port))) - ret = -EFAULT; - break; case RCKP_GET_CONFIG: ret = get_config(info, argp); break; diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h index d0560203f215..d62ed6587f32 100644 --- a/drivers/tty/rocket.h +++ b/drivers/tty/rocket.h @@ -71,7 +71,6 @@ struct rocket_version { /* * Rocketport ioctls -- "RP" */ -#define RCKP_GET_STRUCT 0x00525001 #define RCKP_GET_CONFIG 0x00525002 #define RCKP_SET_CONFIG 0x00525003 #define RCKP_GET_PORTS 0x00525004 -- cgit v1.2.3 From 8ef795653d753e3d6ae67e6f4a0168ff3c059deb Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Thu, 18 Apr 2019 12:35:58 +0800 Subject: tty: rocket: deprecate the rp_ioctl The rp_ioctl is deprecated. Add dev_warn_ratelimited to warn the use of rp_ioctl. Signed-off-by: Fuqian Huang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index b6543e28bd8b..1e1bcb54b680 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1284,18 +1284,28 @@ static int rp_ioctl(struct tty_struct *tty, switch (cmd) { case RCKP_GET_CONFIG: + dev_warn_ratelimited(tty->dev, + "RCKP_GET_CONFIG option is deprecated\n"); ret = get_config(info, argp); break; case RCKP_SET_CONFIG: + dev_warn_ratelimited(tty->dev, + "RCKP_SET_CONFIG option is deprecated\n"); ret = set_config(tty, info, argp); break; case RCKP_GET_PORTS: + dev_warn_ratelimited(tty->dev, + "RCKP_GET_PORTS option is deprecated\n"); ret = get_ports(info, argp); break; case RCKP_RESET_RM2: + dev_warn_ratelimited(tty->dev, + "RCKP_RESET_RM2 option is deprecated\n"); ret = reset_rm2(info, argp); break; case RCKP_GET_VERSION: + dev_warn_ratelimited(tty->dev, + "RCKP_GET_VERSION option is deprecated\n"); ret = get_version(info, argp); break; default: -- cgit v1.2.3 From 74cdf8cec7dd4c9c06533555a49b4afb429cb5be Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 25 Apr 2019 15:10:44 +0000 Subject: serial: milbeaut_usio: Fix error handling in probe and remove devm_clk_get() is used so there is no reason to explicitly call clk_put() in probe or remove functions. Also remove duplicate assign for port->membase. Fixes: ba44dc043004 ("serial: Add Milbeaut serial control") Signed-off-by: Wei Yongjun Tested-by: Sugaya Taichi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/milbeaut_usio.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index 4a10604211be..949ab7efc4fc 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -525,8 +525,6 @@ static int mlb_usio_probe(struct platform_device *pdev) ret = -ENODEV; goto failed; } - port->mapbase = res->start; - port->membase = ioremap(res->start, (res->end - res->start + 1)); port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); @@ -548,16 +546,12 @@ static int mlb_usio_probe(struct platform_device *pdev) ret = uart_add_one_port(&mlb_usio_uart_driver, port); if (ret) { dev_err(&pdev->dev, "Adding port failed: %d\n", ret); - goto failed1; + goto failed; } return 0; -failed1: - iounmap(port->membase); - failed: clk_disable_unprepare(clk); - clk_put(clk); return ret; } @@ -569,7 +563,6 @@ static int mlb_usio_remove(struct platform_device *pdev) uart_remove_one_port(&mlb_usio_uart_driver, port); clk_disable_unprepare(clk); - clk_put(clk); return 0; } -- cgit v1.2.3 From 1863178b20c5add7e6d217f2bf6d094ff13660f7 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 9 Apr 2019 13:15:24 +0530 Subject: serial: uartps: Add support for cts-override Having flow is configurable. Add support for the same by checking for cts-override. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 74089f5e5b53..605354fd60b1 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -193,6 +193,7 @@ struct cdns_uart { int id; struct notifier_block clk_rate_change_nb; u32 quirks; + bool cts_override; }; struct cdns_platform_data { u32 quirks; @@ -1000,6 +1001,11 @@ static void cdns_uart_config_port(struct uart_port *port, int flags) */ static unsigned int cdns_uart_get_mctrl(struct uart_port *port) { + struct cdns_uart *cdns_uart_data = port->private_data; + + if (cdns_uart_data->cts_override) + return 0; + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } @@ -1007,6 +1013,10 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { u32 val; u32 mode_reg; + struct cdns_uart *cdns_uart_data = port->private_data; + + if (cdns_uart_data->cts_override) + return; val = readl(port->membase + CDNS_UART_MODEMCR); mode_reg = readl(port->membase + CDNS_UART_MR); @@ -1665,6 +1675,8 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = NULL; #endif + cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node, + "cts-override"); return 0; err_out_pm_disable: -- cgit v1.2.3 From 45c054d0815b1530d7c7ff8441389a0421dd05e7 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Fri, 12 Apr 2019 19:01:13 -0700 Subject: tty: serial: add driver for the SiFive UART Add a serial driver for the SiFive UART, found on SiFive FU540 devices (among others). The underlying serial IP block is relatively basic, and currently does not support serial break detection. Further information on the IP block can be found in the documentation and Chisel sources: https://static.dev.sifive.com/FU540-C000-v1.0.pdf https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart This driver was written in collaboration with Wesley Terpstra . Tested on a SiFive HiFive Unleashed A00 board, using BBL and the open- source FSBL (using a DT file based on what's targeted for mainline). This revision incorporates changes based on comments by Julia Lawall , Emil Renner Berthing , and Andreas Schwab . Thanks also to Andreas for testing the driver with his userspace and reporting a bug with the set_termios implementation. Signed-off-by: Paul Walmsley Signed-off-by: Paul Walmsley Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Palmer Dabbelt Cc: Wesley Terpstra Cc: linux-serial@vger.kernel.org Cc: linux-riscv@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: Julia Lawall Cc: Emil Renner Berthing Cc: Andreas Schwab Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 24 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sifive.c | 1056 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1084 insertions(+) create mode 100644 drivers/tty/serial/sifive.c (limited to 'drivers') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 14768ad0307e..0d31251e04cc 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1094,6 +1094,30 @@ config SERIAL_OMAP_CONSOLE your boot loader about how to pass options to the kernel at boot time.) +config SERIAL_SIFIVE + tristate "SiFive UART support" + depends on OF + select SERIAL_CORE + help + Select this option if you are building a kernel for a device that + contains a SiFive UART IP block. This type of UART is present on + SiFive FU540 SoCs, among others. + +config SERIAL_SIFIVE_CONSOLE + bool "Console on SiFive UART" + depends on SERIAL_SIFIVE=y + select SERIAL_CORE_CONSOLE + help + Select this option if you would like to use a SiFive UART as the + system console. + + 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=ttySIFx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + config SERIAL_LANTIQ bool "Lantiq serial driver" depends on LANTIQ diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 43ca2d0edde8..79c3d513db7e 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o obj-$(CONFIG_SERIAL_OWL) += owl-uart.o obj-$(CONFIG_SERIAL_RDA) += rda-uart.o obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o +obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c new file mode 100644 index 000000000000..be4687814353 --- /dev/null +++ b/drivers/tty/serial/sifive.c @@ -0,0 +1,1056 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SiFive UART driver + * Copyright (C) 2018 Paul Walmsley + * Copyright (C) 2018-2019 SiFive + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based partially on: + * - drivers/tty/serial/pxa.c + * - drivers/tty/serial/amba-pl011.c + * - drivers/tty/serial/uartlite.c + * - drivers/tty/serial/omap-serial.c + * - drivers/pwm/pwm-sifive.c + * + * See the following sources for further documentation: + * - Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of + * SiFive FE310-G000 v2p3 + * - The tree/master/src/main/scala/devices/uart directory of + * https://github.com/sifive/sifive-blocks/ + * + * The SiFive UART design is not 8250-compatible. The following common + * features are not supported: + * - Word lengths other than 8 bits + * - Break handling + * - Parity + * - Flow control + * - Modem signals (DSR, RI, etc.) + * On the other hand, the design is free from the baggage of the 8250 + * programming model. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Register offsets + */ + +/* TXDATA */ +#define SIFIVE_SERIAL_TXDATA_OFFS 0x0 +#define SIFIVE_SERIAL_TXDATA_FULL_SHIFT 31 +#define SIFIVE_SERIAL_TXDATA_FULL_MASK (1 << SIFIVE_SERIAL_TXDATA_FULL_SHIFT) +#define SIFIVE_SERIAL_TXDATA_DATA_SHIFT 0 +#define SIFIVE_SERIAL_TXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_TXDATA_DATA_SHIFT) + +/* RXDATA */ +#define SIFIVE_SERIAL_RXDATA_OFFS 0x4 +#define SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT 31 +#define SIFIVE_SERIAL_RXDATA_EMPTY_MASK (1 << SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT) +#define SIFIVE_SERIAL_RXDATA_DATA_SHIFT 0 +#define SIFIVE_SERIAL_RXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_RXDATA_DATA_SHIFT) + +/* TXCTRL */ +#define SIFIVE_SERIAL_TXCTRL_OFFS 0x8 +#define SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT 16 +#define SIFIVE_SERIAL_TXCTRL_TXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) +#define SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT 1 +#define SIFIVE_SERIAL_TXCTRL_NSTOP_MASK (1 << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT) +#define SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT 0 +#define SIFIVE_SERIAL_TXCTRL_TXEN_MASK (1 << SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT) + +/* RXCTRL */ +#define SIFIVE_SERIAL_RXCTRL_OFFS 0xC +#define SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT 16 +#define SIFIVE_SERIAL_RXCTRL_RXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) +#define SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT 0 +#define SIFIVE_SERIAL_RXCTRL_RXEN_MASK (1 << SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT) + +/* IE */ +#define SIFIVE_SERIAL_IE_OFFS 0x10 +#define SIFIVE_SERIAL_IE_RXWM_SHIFT 1 +#define SIFIVE_SERIAL_IE_RXWM_MASK (1 << SIFIVE_SERIAL_IE_RXWM_SHIFT) +#define SIFIVE_SERIAL_IE_TXWM_SHIFT 0 +#define SIFIVE_SERIAL_IE_TXWM_MASK (1 << SIFIVE_SERIAL_IE_TXWM_SHIFT) + +/* IP */ +#define SIFIVE_SERIAL_IP_OFFS 0x14 +#define SIFIVE_SERIAL_IP_RXWM_SHIFT 1 +#define SIFIVE_SERIAL_IP_RXWM_MASK (1 << SIFIVE_SERIAL_IP_RXWM_SHIFT) +#define SIFIVE_SERIAL_IP_TXWM_SHIFT 0 +#define SIFIVE_SERIAL_IP_TXWM_MASK (1 << SIFIVE_SERIAL_IP_TXWM_SHIFT) + +/* DIV */ +#define SIFIVE_SERIAL_DIV_OFFS 0x18 +#define SIFIVE_SERIAL_DIV_DIV_SHIFT 0 +#define SIFIVE_SERIAL_DIV_DIV_MASK (0xffff << SIFIVE_SERIAL_IP_DIV_SHIFT) + +/* + * Config macros + */ + +/* + * SIFIVE_SERIAL_MAX_PORTS: maximum number of UARTs on a device that can + * host a serial console + */ +#define SIFIVE_SERIAL_MAX_PORTS 8 + +/* + * SIFIVE_DEFAULT_BAUD_RATE: default baud rate that the driver should + * configure itself to use + */ +#define SIFIVE_DEFAULT_BAUD_RATE 115200 + +/* SIFIVE_SERIAL_NAME: our driver's name that we pass to the operating system */ +#define SIFIVE_SERIAL_NAME "sifive-serial" + +/* SIFIVE_TTY_PREFIX: tty name prefix for SiFive serial ports */ +#define SIFIVE_TTY_PREFIX "ttySIF" + +/* SIFIVE_TX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */ +#define SIFIVE_TX_FIFO_DEPTH 8 + +/* SIFIVE_RX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */ +#define SIFIVE_RX_FIFO_DEPTH 8 + +#if (SIFIVE_TX_FIFO_DEPTH != SIFIVE_RX_FIFO_DEPTH) +#error Driver does not support configurations with different TX, RX FIFO sizes +#endif + +/* + * + */ + +/** + * sifive_serial_port - driver-specific data extension to struct uart_port + * @port: struct uart_port embedded in this struct + * @dev: struct device * + * @ier: shadowed copy of the interrupt enable register + * @clkin_rate: input clock to the UART IP block. + * @baud_rate: UART serial line rate (e.g., 115200 baud) + * @clk_notifier: clock rate change notifier for upstream clock changes + * + * Configuration data specific to this SiFive UART. + */ +struct sifive_serial_port { + struct uart_port port; + struct device *dev; + unsigned char ier; + unsigned long clkin_rate; + unsigned long baud_rate; + struct clk *clk; + struct notifier_block clk_notifier; +}; + +/* + * Structure container-of macros + */ + +#define port_to_sifive_serial_port(p) (container_of((p), \ + struct sifive_serial_port, \ + port)) + +#define notifier_to_sifive_serial_port(nb) (container_of((nb), \ + struct sifive_serial_port, \ + clk_notifier)) + +/* + * Forward declarations + */ +static void sifive_serial_stop_tx(struct uart_port *port); + +/* + * Internal functions + */ + +/** + * __ssp_early_writel() - write to a SiFive serial port register (early) + * @port: pointer to a struct uart_port record + * @offs: register address offset from the IP block base address + * @v: value to write to the register + * + * Given a pointer @port to a struct uart_port record, write the value + * @v to the IP block register address offset @offs. This function is + * intended for early console use. + * + * Context: Intended to be used only by the earlyconsole code. + */ +static void __ssp_early_writel(u32 v, u16 offs, struct uart_port *port) +{ + writel_relaxed(v, port->membase + offs); +} + +/** + * __ssp_early_readl() - read from a SiFive serial port register (early) + * @port: pointer to a struct uart_port record + * @offs: register address offset from the IP block base address + * + * Given a pointer @port to a struct uart_port record, read the + * contents of the IP block register located at offset @offs from the + * IP block base and return it. This function is intended for early + * console use. + * + * Context: Intended to be called only by the earlyconsole code or by + * __ssp_readl() or __ssp_writel() (in this driver) + * + * Returns: the register value read from the UART. + */ +static u32 __ssp_early_readl(struct uart_port *port, u16 offs) +{ + return readl_relaxed(port->membase + offs); +} + +/** + * __ssp_writel() - write to a SiFive serial port register + * @v: value to write to the register + * @offs: register address offset from the IP block base address + * @ssp: pointer to a struct sifive_serial_port record + * + * Write the value @v to the IP block register located at offset @offs from the + * IP block base, given a pointer @ssp to a struct sifive_serial_port record. + * + * Context: Any context. + */ +static void __ssp_writel(u32 v, u16 offs, struct sifive_serial_port *ssp) +{ + __ssp_early_writel(v, offs, &ssp->port); +} + +/** + * __ssp_readl() - read from a SiFive serial port register + * @ssp: pointer to a struct sifive_serial_port record + * @offs: register address offset from the IP block base address + * + * Read the contents of the IP block register located at offset @offs from the + * IP block base, given a pointer @ssp to a struct sifive_serial_port record. + * + * Context: Any context. + * + * Returns: the value of the UART register + */ +static u32 __ssp_readl(struct sifive_serial_port *ssp, u16 offs) +{ + return __ssp_early_readl(&ssp->port, offs); +} + +/** + * sifive_serial_is_txfifo_full() - is the TXFIFO full? + * @ssp: pointer to a struct sifive_serial_port + * + * Read the transmit FIFO "full" bit, returning a non-zero value if the + * TX FIFO is full, or zero if space remains. Intended to be used to prevent + * writes to the TX FIFO when it's full. + * + * Returns: SIFIVE_SERIAL_TXDATA_FULL_MASK (non-zero) if the transmit FIFO + * is full, or 0 if space remains. + */ +static int sifive_serial_is_txfifo_full(struct sifive_serial_port *ssp) +{ + return __ssp_readl(ssp, SIFIVE_SERIAL_TXDATA_OFFS) & + SIFIVE_SERIAL_TXDATA_FULL_MASK; +} + +/** + * __ssp_transmit_char() - enqueue a byte to transmit onto the TX FIFO + * @ssp: pointer to a struct sifive_serial_port + * @ch: character to transmit + * + * Enqueue a byte @ch onto the transmit FIFO, given a pointer @ssp to the + * struct sifive_serial_port * to transmit on. Caller should first check to + * ensure that the TXFIFO has space; see sifive_serial_is_txfifo_full(). + * + * Context: Any context. + */ +static void __ssp_transmit_char(struct sifive_serial_port *ssp, int ch) +{ + __ssp_writel(ch, SIFIVE_SERIAL_TXDATA_OFFS, ssp); +} + +/** + * __ssp_transmit_chars() - enqueue multiple bytes onto the TX FIFO + * @ssp: pointer to a struct sifive_serial_port + * + * Transfer up to a TX FIFO size's worth of characters from the Linux serial + * transmit buffer to the SiFive UART TX FIFO. + * + * Context: Any context. Expects @ssp->port.lock to be held by caller. + */ +static void __ssp_transmit_chars(struct sifive_serial_port *ssp) +{ + struct circ_buf *xmit = &ssp->port.state->xmit; + int count; + + if (ssp->port.x_char) { + __ssp_transmit_char(ssp, ssp->port.x_char); + ssp->port.icount.tx++; + ssp->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) { + sifive_serial_stop_tx(&ssp->port); + return; + } + count = SIFIVE_TX_FIFO_DEPTH; + do { + __ssp_transmit_char(ssp, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + ssp->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&ssp->port); + + if (uart_circ_empty(xmit)) + sifive_serial_stop_tx(&ssp->port); +} + +/** + * __ssp_enable_txwm() - enable transmit watermark interrupts + * @ssp: pointer to a struct sifive_serial_port + * + * Enable interrupt generation when the transmit FIFO watermark is reached + * on the SiFive UART referred to by @ssp. + */ +static void __ssp_enable_txwm(struct sifive_serial_port *ssp) +{ + if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK) + return; + + ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK; + __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp); +} + +/** + * __ssp_enable_rxwm() - enable receive watermark interrupts + * @ssp: pointer to a struct sifive_serial_port + * + * Enable interrupt generation when the receive FIFO watermark is reached + * on the SiFive UART referred to by @ssp. + */ +static void __ssp_enable_rxwm(struct sifive_serial_port *ssp) +{ + if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK) + return; + + ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK; + __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp); +} + +/** + * __ssp_disable_txwm() - disable transmit watermark interrupts + * @ssp: pointer to a struct sifive_serial_port + * + * Disable interrupt generation when the transmit FIFO watermark is reached + * on the UART referred to by @ssp. + */ +static void __ssp_disable_txwm(struct sifive_serial_port *ssp) +{ + if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)) + return; + + ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK; + __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp); +} + +/** + * __ssp_disable_rxwm() - disable receive watermark interrupts + * @ssp: pointer to a struct sifive_serial_port + * + * Disable interrupt generation when the receive FIFO watermark is reached + * on the UART referred to by @ssp. + */ +static void __ssp_disable_rxwm(struct sifive_serial_port *ssp) +{ + if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)) + return; + + ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK; + __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp); +} + +/** + * __ssp_receive_char() - receive a byte from the UART + * @ssp: pointer to a struct sifive_serial_port + * @is_empty: char pointer to return whether the RX FIFO is empty + * + * Try to read a byte from the SiFive UART RX FIFO, referenced by + * @ssp, and to return it. Also returns the RX FIFO empty bit in + * the char pointed to by @ch. The caller must pass the byte back to the + * Linux serial layer if needed. + * + * Returns: the byte read from the UART RX FIFO. + */ +static char __ssp_receive_char(struct sifive_serial_port *ssp, char *is_empty) +{ + u32 v; + u8 ch; + + v = __ssp_readl(ssp, SIFIVE_SERIAL_RXDATA_OFFS); + + if (!is_empty) + WARN_ON(1); + else + *is_empty = (v & SIFIVE_SERIAL_RXDATA_EMPTY_MASK) >> + SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT; + + ch = (v & SIFIVE_SERIAL_RXDATA_DATA_MASK) >> + SIFIVE_SERIAL_RXDATA_DATA_SHIFT; + + return ch; +} + +/** + * __ssp_receive_chars() - receive multiple bytes from the UART + * @ssp: pointer to a struct sifive_serial_port + * + * Receive up to an RX FIFO's worth of bytes from the SiFive UART referred + * to by @ssp and pass them up to the Linux serial layer. + * + * Context: Expects ssp->port.lock to be held by caller. + */ +static void __ssp_receive_chars(struct sifive_serial_port *ssp) +{ + unsigned char ch; + char is_empty; + int c; + + for (c = SIFIVE_RX_FIFO_DEPTH; c > 0; --c) { + ch = __ssp_receive_char(ssp, &is_empty); + if (is_empty) + break; + + ssp->port.icount.rx++; + uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); + } + + spin_unlock(&ssp->port.lock); + tty_flip_buffer_push(&ssp->port.state->port); + spin_lock(&ssp->port.lock); +} + +/** + * __ssp_update_div() - calculate the divisor setting by the line rate + * @ssp: pointer to a struct sifive_serial_port + * + * Calculate the appropriate value of the clock divisor for the UART + * and target line rate referred to by @ssp and write it into the + * hardware. + */ +static void __ssp_update_div(struct sifive_serial_port *ssp) +{ + u16 div; + + div = DIV_ROUND_UP(ssp->clkin_rate, ssp->baud_rate) - 1; + + __ssp_writel(div, SIFIVE_SERIAL_DIV_OFFS, ssp); +} + +/** + * __ssp_update_baud_rate() - set the UART "baud rate" + * @ssp: pointer to a struct sifive_serial_port + * @rate: new target bit rate + * + * Calculate the UART divisor value for the target bit rate @rate for the + * SiFive UART described by @ssp and program it into the UART. There may + * be some error between the target bit rate and the actual bit rate implemented + * by the UART due to clock ratio granularity. + */ +static void __ssp_update_baud_rate(struct sifive_serial_port *ssp, + unsigned int rate) +{ + if (ssp->baud_rate == rate) + return; + + ssp->baud_rate = rate; + __ssp_update_div(ssp); +} + +/** + * __ssp_set_stop_bits() - set the number of stop bits + * @ssp: pointer to a struct sifive_serial_port + * @nstop: 1 or 2 (stop bits) + * + * Program the SiFive UART referred to by @ssp to use @nstop stop bits. + */ +static void __ssp_set_stop_bits(struct sifive_serial_port *ssp, char nstop) +{ + u32 v; + + if (nstop < 1 || nstop > 2) { + WARN_ON(1); + return; + } + + v = __ssp_readl(ssp, SIFIVE_SERIAL_TXCTRL_OFFS); + v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK; + v |= (nstop - 1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT; + __ssp_writel(v, SIFIVE_SERIAL_TXCTRL_OFFS, ssp); +} + +/** + * __ssp_wait_for_xmitr() - wait for an empty slot on the TX FIFO + * @ssp: pointer to a struct sifive_serial_port + * + * Delay while the UART TX FIFO referred to by @ssp is marked as full. + * + * Context: Any context. + */ +static void __maybe_unused __ssp_wait_for_xmitr(struct sifive_serial_port *ssp) +{ + while (sifive_serial_is_txfifo_full(ssp)) + udelay(1); /* XXX Could probably be more intelligent here */ +} + +/* + * Linux serial API functions + */ + +static void sifive_serial_stop_tx(struct uart_port *port) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_disable_txwm(ssp); +} + +static void sifive_serial_stop_rx(struct uart_port *port) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_disable_rxwm(ssp); +} + +static void sifive_serial_start_tx(struct uart_port *port) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_enable_txwm(ssp); +} + +static irqreturn_t sifive_serial_irq(int irq, void *dev_id) +{ + struct sifive_serial_port *ssp = dev_id; + u32 ip; + + spin_lock(&ssp->port.lock); + + ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS); + if (!ip) { + spin_unlock(&ssp->port.lock); + return IRQ_NONE; + } + + if (ip & SIFIVE_SERIAL_IP_RXWM_MASK) + __ssp_receive_chars(ssp); + if (ip & SIFIVE_SERIAL_IP_TXWM_MASK) + __ssp_transmit_chars(ssp); + + spin_unlock(&ssp->port.lock); + + return IRQ_HANDLED; +} + +static unsigned int sifive_serial_tx_empty(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +static unsigned int sifive_serial_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* IP block does not support these signals */ +} + +static void sifive_serial_break_ctl(struct uart_port *port, int break_state) +{ + /* IP block does not support sending a break */ +} + +static int sifive_serial_startup(struct uart_port *port) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_enable_rxwm(ssp); + + return 0; +} + +static void sifive_serial_shutdown(struct uart_port *port) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_disable_rxwm(ssp); + __ssp_disable_txwm(ssp); +} + +/** + * sifive_serial_clk_notifier() - clock post-rate-change notifier + * @nb: pointer to the struct notifier_block, from the notifier code + * @event: event mask from the notifier code + * @data: pointer to the struct clk_notifier_data from the notifier code + * + * On the V0 SoC, the UART IP block is derived from the CPU clock source + * after a synchronous divide-by-two divider, so any CPU clock rate change + * requires the UART baud rate to be updated. This presumably could corrupt any + * serial word currently being transmitted or received. It would probably + * be better to stop receives and transmits, then complete the baud rate + * change, then re-enable them. + */ +static int sifive_serial_clk_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *cnd = data; + struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb); + + if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) { + ssp->clkin_rate = cnd->new_rate; + __ssp_update_div(ssp); + } + + return NOTIFY_OK; +} + +static void sifive_serial_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + unsigned long flags; + u32 v, old_v; + int rate; + char nstop; + + if ((termios->c_cflag & CSIZE) != CS8) + dev_err_once(ssp->port.dev, "only 8-bit words supported\n"); + if (termios->c_iflag & (INPCK | PARMRK)) + dev_err_once(ssp->port.dev, "parity checking not supported\n"); + if (termios->c_iflag & BRKINT) + dev_err_once(ssp->port.dev, "BREAK detection not supported\n"); + + /* Set number of stop bits */ + nstop = (termios->c_cflag & CSTOPB) ? 2 : 1; + __ssp_set_stop_bits(ssp, nstop); + + /* Set line rate */ + rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16); + __ssp_update_baud_rate(ssp, rate); + + spin_lock_irqsave(&ssp->port.lock, flags); + + /* Update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, rate); + + ssp->port.read_status_mask = 0; + + /* Ignore all characters if CREAD is not set */ + v = __ssp_readl(ssp, SIFIVE_SERIAL_RXCTRL_OFFS); + old_v = v; + if ((termios->c_cflag & CREAD) == 0) + v &= SIFIVE_SERIAL_RXCTRL_RXEN_MASK; + else + v |= SIFIVE_SERIAL_RXCTRL_RXEN_MASK; + if (v != old_v) + __ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp); + + spin_unlock_irqrestore(&ssp->port.lock, flags); +} + +static void sifive_serial_release_port(struct uart_port *port) +{ +} + +static int sifive_serial_request_port(struct uart_port *port) +{ + return 0; +} + +static void sifive_serial_config_port(struct uart_port *port, int flags) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + ssp->port.type = PORT_SIFIVE_V0; +} + +static int sifive_serial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *sifive_serial_type(struct uart_port *port) +{ + return port->type == PORT_SIFIVE_V0 ? "SiFive UART v0" : NULL; +} + +/* + * Early console support + */ + +#ifdef CONFIG_SERIAL_EARLYCON +static void early_sifive_serial_putc(struct uart_port *port, int c) +{ + while (__ssp_early_readl(port, SIFIVE_SERIAL_TXDATA_OFFS) & + SIFIVE_SERIAL_TXDATA_FULL_MASK) + cpu_relax(); + + __ssp_early_writel(c, SIFIVE_SERIAL_TXDATA_OFFS, port); +} + +static void early_sifive_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct earlycon_device *dev = con->data; + struct uart_port *port = &dev->port; + + uart_console_write(port, s, n, early_sifive_serial_putc); +} + +static int __init early_sifive_serial_setup(struct earlycon_device *dev, + const char *options) +{ + struct uart_port *port = &dev->port; + + if (!port->membase) + return -ENODEV; + + dev->con->write = early_sifive_serial_write; + + return 0; +} + +OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup); +OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0", + early_sifive_serial_setup); +#endif /* CONFIG_SERIAL_EARLYCON */ + +/* + * Linux console interface + */ + +#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE + +static struct sifive_serial_port *sifive_serial_console_ports[SIFIVE_SERIAL_MAX_PORTS]; + +static void sifive_serial_console_putchar(struct uart_port *port, int ch) +{ + struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + + __ssp_wait_for_xmitr(ssp); + __ssp_transmit_char(ssp, ch); +} + +static void sifive_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + if (!ssp) + return; + + local_irq_save(flags); + if (ssp->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&ssp->port.lock); + else + spin_lock(&ssp->port.lock); + + ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); + __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); + + uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar); + + __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); + + if (locked) + spin_unlock(&ssp->port.lock); + local_irq_restore(flags); +} + +static int __init sifive_serial_console_setup(struct console *co, char *options) +{ + struct sifive_serial_port *ssp; + int baud = SIFIVE_DEFAULT_BAUD_RATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= SIFIVE_SERIAL_MAX_PORTS) + return -ENODEV; + + ssp = sifive_serial_console_ports[co->index]; + if (!ssp) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&ssp->port, co, baud, parity, bits, flow); +} + +static struct uart_driver sifive_serial_uart_driver; + +static struct console sifive_serial_console = { + .name = SIFIVE_TTY_PREFIX, + .write = sifive_serial_console_write, + .device = uart_console_device, + .setup = sifive_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sifive_serial_uart_driver, +}; + +static int __init sifive_console_init(void) +{ + register_console(&sifive_serial_console); + return 0; +} + +console_initcall(sifive_console_init); + +static void __ssp_add_console_port(struct sifive_serial_port *ssp) +{ + sifive_serial_console_ports[ssp->port.line] = ssp; +} + +static void __ssp_remove_console_port(struct sifive_serial_port *ssp) +{ + sifive_serial_console_ports[ssp->port.line] = 0; +} + +#define SIFIVE_SERIAL_CONSOLE (&sifive_serial_console) + +#else + +#define SIFIVE_SERIAL_CONSOLE NULL + +static void __ssp_add_console_port(struct sifive_serial_port *ssp) +{} +static void __ssp_remove_console_port(struct sifive_serial_port *ssp) +{} + +#endif + +static const struct uart_ops sifive_serial_uops = { + .tx_empty = sifive_serial_tx_empty, + .set_mctrl = sifive_serial_set_mctrl, + .get_mctrl = sifive_serial_get_mctrl, + .stop_tx = sifive_serial_stop_tx, + .start_tx = sifive_serial_start_tx, + .stop_rx = sifive_serial_stop_rx, + .break_ctl = sifive_serial_break_ctl, + .startup = sifive_serial_startup, + .shutdown = sifive_serial_shutdown, + .set_termios = sifive_serial_set_termios, + .type = sifive_serial_type, + .release_port = sifive_serial_release_port, + .request_port = sifive_serial_request_port, + .config_port = sifive_serial_config_port, + .verify_port = sifive_serial_verify_port, +}; + +static struct uart_driver sifive_serial_uart_driver = { + .owner = THIS_MODULE, + .driver_name = SIFIVE_SERIAL_NAME, + .dev_name = SIFIVE_TTY_PREFIX, + .nr = SIFIVE_SERIAL_MAX_PORTS, + .cons = SIFIVE_SERIAL_CONSOLE, +}; + +static int sifive_serial_probe(struct platform_device *pdev) +{ + struct sifive_serial_port *ssp; + struct resource *mem; + struct clk *clk; + void __iomem *base; + int irq, id, r; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not acquire interrupt\n"); + return -EPROBE_DEFER; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "could not acquire device memory\n"); + return PTR_ERR(base); + } + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to find controller clock\n"); + return PTR_ERR(clk); + } + + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) { + dev_err(&pdev->dev, "missing aliases entry\n"); + return id; + } + +#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE + if (id > SIFIVE_SERIAL_MAX_PORTS) { + dev_err(&pdev->dev, "too many UARTs (%d)\n", id); + return -EINVAL; + } +#endif + + ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL); + if (!ssp) + return -ENOMEM; + + ssp->port.dev = &pdev->dev; + ssp->port.type = PORT_SIFIVE_V0; + ssp->port.iotype = UPIO_MEM; + ssp->port.irq = irq; + ssp->port.fifosize = SIFIVE_TX_FIFO_DEPTH; + ssp->port.ops = &sifive_serial_uops; + ssp->port.line = id; + ssp->port.mapbase = mem->start; + ssp->port.membase = base; + ssp->dev = &pdev->dev; + ssp->clk = clk; + ssp->clk_notifier.notifier_call = sifive_serial_clk_notifier; + + r = clk_notifier_register(ssp->clk, &ssp->clk_notifier); + if (r) { + dev_err(&pdev->dev, "could not register clock notifier: %d\n", + r); + goto probe_out1; + } + + /* Set up clock divider */ + ssp->clkin_rate = clk_get_rate(ssp->clk); + ssp->baud_rate = SIFIVE_DEFAULT_BAUD_RATE; + __ssp_update_div(ssp); + + platform_set_drvdata(pdev, ssp); + + /* Enable transmits and set the watermark level to 1 */ + __ssp_writel((1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) | + SIFIVE_SERIAL_TXCTRL_TXEN_MASK, + SIFIVE_SERIAL_TXCTRL_OFFS, ssp); + + /* Enable receives and set the watermark level to 0 */ + __ssp_writel((0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) | + SIFIVE_SERIAL_RXCTRL_RXEN_MASK, + SIFIVE_SERIAL_RXCTRL_OFFS, ssp); + + r = request_irq(ssp->port.irq, sifive_serial_irq, ssp->port.irqflags, + dev_name(&pdev->dev), ssp); + if (r) { + dev_err(&pdev->dev, "could not attach interrupt: %d\n", r); + goto probe_out2; + } + + __ssp_add_console_port(ssp); + + r = uart_add_one_port(&sifive_serial_uart_driver, &ssp->port); + if (r != 0) { + dev_err(&pdev->dev, "could not add uart: %d\n", r); + goto probe_out3; + } + + return 0; + +probe_out3: + __ssp_remove_console_port(ssp); + free_irq(ssp->port.irq, ssp); +probe_out2: + clk_notifier_unregister(ssp->clk, &ssp->clk_notifier); +probe_out1: + return r; +} + +static int sifive_serial_remove(struct platform_device *dev) +{ + struct sifive_serial_port *ssp = platform_get_drvdata(dev); + + __ssp_remove_console_port(ssp); + uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port); + free_irq(ssp->port.irq, ssp); + clk_notifier_unregister(ssp->clk, &ssp->clk_notifier); + + return 0; +} + +static const struct of_device_id sifive_serial_of_match[] = { + { .compatible = "sifive,fu540-c000-uart0" }, + { .compatible = "sifive,uart0" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sifive_serial_of_match); + +static struct platform_driver sifive_serial_platform_driver = { + .probe = sifive_serial_probe, + .remove = sifive_serial_remove, + .driver = { + .name = SIFIVE_SERIAL_NAME, + .of_match_table = of_match_ptr(sifive_serial_of_match), + }, +}; + +static int __init sifive_serial_init(void) +{ + int r; + + r = uart_register_driver(&sifive_serial_uart_driver); + if (r) + goto init_out1; + + r = platform_driver_register(&sifive_serial_platform_driver); + if (r) + goto init_out2; + + return 0; + +init_out2: + uart_unregister_driver(&sifive_serial_uart_driver); +init_out1: + return r; +} + +static void __exit sifive_serial_exit(void) +{ + platform_driver_unregister(&sifive_serial_platform_driver); + uart_unregister_driver(&sifive_serial_uart_driver); +} + +module_init(sifive_serial_init); +module_exit(sifive_serial_exit); + +MODULE_DESCRIPTION("SiFive UART serial driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Paul Walmsley "); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index a51c747b52c5..67c4aaaa2308 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -290,4 +290,7 @@ /* Socionext Milbeaut UART */ #define PORT_MLB_USIO 119 +/* SiFive UART */ +#define PORT_SIFIVE_V0 120 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3