diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-27 11:46:26 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-27 11:46:26 -0700 |
| commit | b39667abcdcc754e32a0eb0df9cf49d45333d4ae (patch) | |
| tree | 690334d5d728b9b5fa4232fe62cc03fd856e41c3 /drivers/tty | |
| parent | 4010e62b5b684d7a6090f3f9c69f8a5be31910e5 (diff) | |
| parent | 2b3174c96696cde676393474f0e01d0d108462f5 (diff) | |
| download | linux-b39667abcdcc754e32a0eb0df9cf49d45333d4ae.tar.gz linux-b39667abcdcc754e32a0eb0df9cf49d45333d4ae.tar.bz2 linux-b39667abcdcc754e32a0eb0df9cf49d45333d4ae.zip | |
Merge tag 'tty-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty / serial updates from Greg KH:
"Here is the big set of tty/serial driver updates for 6.4-rc1.
Nothing major, just lots of tiny, constant, forward development. This
includes:
- obligatory n_gsm updates and feature additions
- 8250_em driver updates
- sh-sci driver updates
- dts cleanups and updates
- general cleanups and improvements by Ilpo and Jiri
- other small serial driver core fixes and driver updates
All of these have been in linux-next for a while with no reported
problems"
* tag 'tty-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (87 commits)
n_gsm: Use array_index_nospec() with index that comes from userspace
tty: vt: drop checks for undefined VT_SINGLE_DRIVER
tty: vt: distribute EXPORT_SYMBOL()
tty: vt: simplify some cases in tioclinux()
tty: vt: reformat tioclinux()
tty: serial: sh-sci: Fix end of transmission on SCI
tty: serial: sh-sci: Add support for tx end interrupt handling
tty: serial: sh-sci: Fix TE setting on SCI IP
tty: serial: sh-sci: Add RZ/G2L SCIFA DMA rx support
tty: serial: sh-sci: Add RZ/G2L SCIFA DMA tx support
serial: max310x: fix IO data corruption in batched operations
serial: core: Disable uart_start() on uart_remove_one_port()
serial: 8250: Reinit port->pm on port specific driver unbind
serial: 8250: Add missing wakeup event reporting
tty: serial: fsl_lpuart: use UARTMODIR register bits for lpuart32 platform
tty: serial: fsl_lpuart: adjust buffer length to the intended size
serial: fix TIOCSRS485 locking
serial: make SiFive serial drivers depend on ARCH_ symbols
tty: synclink_gt: don't allocate and pass dummy flags
tty: serial: simplify qcom_geni_serial_send_chunk_fifo()
...
Diffstat (limited to 'drivers/tty')
34 files changed, 771 insertions, 401 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index d35fc068da74..84caac32f0fd 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -160,7 +160,7 @@ config LEGACY_TIOCSTI a dangerous legacy operation, and can be disabled on most systems. - Say 'Y here only if you have confirmed that your system's + Say Y here only if you have confirmed that your system's userspace depends on this functionality to continue operating normally. diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index d7515d61659e..c06ad0a0744b 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -347,7 +347,7 @@ static void check_modem_status(struct serial_state *info) #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif - port->tty->hw_stopped = 0; + port->tty->hw_stopped = false; info->IER |= UART_IER_THRI; amiga_custom.intena = IF_SETCLR | IF_TBE; mb(); @@ -362,7 +362,7 @@ static void check_modem_status(struct serial_state *info) #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif - port->tty->hw_stopped = 1; + port->tty->hw_stopped = true; info->IER &= ~UART_IER_THRI; /* disable Tx interrupt and remove any pending interrupts */ amiga_custom.intena = IF_TBE; @@ -1197,7 +1197,7 @@ static void rs_set_termios(struct tty_struct *tty, const struct ktermios *old_te /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped = 0; + tty->hw_stopped = false; rs_start(tty); } diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index ef3116e87975..10855e66fda1 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -553,7 +553,7 @@ static void mxser_handle_cts(struct tty_struct *tty, struct mxser_port *info, if (tty->hw_stopped) { if (cts) { - tty->hw_stopped = 0; + tty->hw_stopped = false; if (!mxser_16550A_or_MUST(info)) __mxser_start_tx(info); @@ -563,7 +563,7 @@ static void mxser_handle_cts(struct tty_struct *tty, struct mxser_port *info, } else if (cts) return; - tty->hw_stopped = 1; + tty->hw_stopped = true; if (!mxser_16550A_or_MUST(info)) __mxser_stop_tx(info); } @@ -1361,7 +1361,7 @@ static void mxser_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&info->slock, flags); if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) { - tty->hw_stopped = 0; + tty->hw_stopped = false; mxser_start(tty); } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 7aa05ebed8e1..b411a26cc092 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -42,6 +42,7 @@ #include <linux/ctype.h> #include <linux/mm.h> #include <linux/math.h> +#include <linux/nospec.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/poll.h> @@ -128,6 +129,7 @@ struct gsm_msg { enum gsm_dlci_state { DLCI_CLOSED, + DLCI_WAITING_CONFIG, /* Waiting for DLCI configuration from user */ DLCI_CONFIGURE, /* Sending PN (for adaption > 1) */ DLCI_OPENING, /* Sending SABM not seen UA */ DLCI_OPEN, /* SABM/UA complete */ @@ -330,6 +332,7 @@ struct gsm_mux { unsigned int t3; /* Power wake-up timer in seconds. */ int n2; /* Retry count */ u8 k; /* Window size */ + bool wait_config; /* Wait for configuration by ioctl before DLCI open */ u32 keep_alive; /* Control channel keep-alive in 10ms */ /* Statistics (not currently exposed) */ @@ -451,6 +454,7 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk); static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, u8 ctrl); static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg); +static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr); static void gsmld_write_trigger(struct gsm_mux *gsm); static void gsmld_write_task(struct work_struct *work); @@ -2280,6 +2284,7 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci) switch (dlci->state) { case DLCI_CLOSED: + case DLCI_WAITING_CONFIG: case DLCI_CLOSING: dlci->retries = gsm->n2; if (!need_pn) { @@ -2311,6 +2316,7 @@ static void gsm_dlci_set_opening(struct gsm_dlci *dlci) { switch (dlci->state) { case DLCI_CLOSED: + case DLCI_WAITING_CONFIG: case DLCI_CLOSING: dlci->state = DLCI_OPENING; break; @@ -2320,6 +2326,24 @@ static void gsm_dlci_set_opening(struct gsm_dlci *dlci) } /** + * gsm_dlci_set_wait_config - wait for channel configuration + * @dlci: DLCI to configure + * + * Wait for a DLCI configuration from the application. + */ +static void gsm_dlci_set_wait_config(struct gsm_dlci *dlci) +{ + switch (dlci->state) { + case DLCI_CLOSED: + case DLCI_CLOSING: + dlci->state = DLCI_WAITING_CONFIG; + break; + default: + break; + } +} + +/** * gsm_dlci_begin_close - start channel open procedure * @dlci: DLCI to open * @@ -2453,6 +2477,128 @@ static void gsm_kick_timer(struct timer_list *t) pr_info("%s TX queue stalled\n", __func__); } +/** + * gsm_dlci_copy_config_values - copy DLCI configuration + * @dlci: source DLCI + * @dc: configuration structure to fill + */ +static void gsm_dlci_copy_config_values(struct gsm_dlci *dlci, struct gsm_dlci_config *dc) +{ + memset(dc, 0, sizeof(*dc)); + dc->channel = (u32)dlci->addr; + dc->adaption = (u32)dlci->adaption; + dc->mtu = (u32)dlci->mtu; + dc->priority = (u32)dlci->prio; + if (dlci->ftype == UIH) + dc->i = 1; + else + dc->i = 2; + dc->k = (u32)dlci->k; +} + +/** + * gsm_dlci_config - configure DLCI from configuration + * @dlci: DLCI to configure + * @dc: DLCI configuration + * @open: open DLCI after configuration? + */ +static int gsm_dlci_config(struct gsm_dlci *dlci, struct gsm_dlci_config *dc, int open) +{ + struct gsm_mux *gsm; + bool need_restart = false; + bool need_open = false; + unsigned int i; + + /* + * Check that userspace doesn't put stuff in here to prevent breakages + * in the future. + */ + for (i = 0; i < ARRAY_SIZE(dc->reserved); i++) + if (dc->reserved[i]) + return -EINVAL; + + if (!dlci) + return -EINVAL; + gsm = dlci->gsm; + + /* Stuff we don't support yet - I frame transport */ + if (dc->adaption != 1 && dc->adaption != 2) + return -EOPNOTSUPP; + if (dc->mtu > MAX_MTU || dc->mtu < MIN_MTU || dc->mtu > gsm->mru) + return -EINVAL; + if (dc->priority >= 64) + return -EINVAL; + if (dc->i == 0 || dc->i > 2) /* UIH and UI only */ + return -EINVAL; + if (dc->k > 7) + return -EINVAL; + + /* + * See what is needed for reconfiguration + */ + /* Framing fields */ + if (dc->adaption != dlci->adaption) + need_restart = true; + if (dc->mtu != dlci->mtu) + need_restart = true; + if (dc->i != dlci->ftype) + need_restart = true; + /* Requires care */ + if (dc->priority != dlci->prio) + need_restart = true; + + if ((open && gsm->wait_config) || need_restart) + need_open = true; + if (dlci->state == DLCI_WAITING_CONFIG) { + need_restart = false; + need_open = true; + } + + /* + * Close down what is needed, restart and initiate the new + * configuration. + */ + if (need_restart) { + gsm_dlci_begin_close(dlci); + wait_event_interruptible(gsm->event, dlci->state == DLCI_CLOSED); + if (signal_pending(current)) + return -EINTR; + } + /* + * Setup the new configuration values + */ + dlci->adaption = (int)dc->adaption; + + if (dc->mtu) + dlci->mtu = (unsigned int)dc->mtu; + else + dlci->mtu = gsm->mtu; + + if (dc->priority) + dlci->prio = (u8)dc->priority; + else + dlci->prio = roundup(dlci->addr + 1, 8) - 1; + + if (dc->i == 1) + dlci->ftype = UIH; + else if (dc->i == 2) + dlci->ftype = UI; + + if (dc->k) + dlci->k = (u8)dc->k; + else + dlci->k = gsm->k; + + if (need_open) { + if (gsm->initiator) + gsm_dlci_begin_open(dlci); + else + gsm_dlci_set_opening(dlci); + } + + return 0; +} + /* * Allocate/Free DLCI channels */ @@ -3078,6 +3224,7 @@ static struct gsm_mux *gsm_alloc_mux(void) gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ gsm->mtu = 64; gsm->dead = true; /* Avoid early tty opens */ + gsm->wait_config = false; /* Disabled */ gsm->keep_alive = 0; /* Disabled */ /* Store the instance to the mux array or abort if no space is @@ -3130,8 +3277,8 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c) int need_close = 0; int need_restart = 0; - /* Stuff we don't support yet - UI or I frame transport, windowing */ - if ((c->adaption != 1 && c->adaption != 2) || c->k) + /* Stuff we don't support yet - UI or I frame transport */ + if (c->adaption != 1 && c->adaption != 2) return -EOPNOTSUPP; /* Check the MRU/MTU range looks sane */ if (c->mru < MIN_MTU || c->mtu < MIN_MTU) @@ -3218,6 +3365,7 @@ static void gsm_copy_config_ext_values(struct gsm_mux *gsm, struct gsm_config_ext *ce) { memset(ce, 0, sizeof(*ce)); + ce->wait_config = gsm->wait_config ? 1 : 0; ce->keep_alive = gsm->keep_alive; } @@ -3233,7 +3381,12 @@ static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) if (ce->reserved[i]) return -EINVAL; + /* + * Setup the new configuration values + */ + gsm->wait_config = ce->wait_config ? true : false; gsm->keep_alive = ce->keep_alive; + return 0; } @@ -3433,9 +3586,16 @@ static int gsmld_open(struct tty_struct *tty) tty->receive_room = 65536; /* Attach the initial passive connection */ - gsm->encoding = GSM_ADV_OPT; gsmld_attach_gsm(tty, gsm); + /* The mux will not be activated yet, we wait for correct + * configuration first. + */ + if (gsm->encoding == GSM_BASIC_OPT) + gsm->receive = gsm0_receive; + else + gsm->receive = gsm1_receive; + return 0; } @@ -3556,8 +3716,10 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, { struct gsm_config c; struct gsm_config_ext ce; + struct gsm_dlci_config dc; struct gsm_mux *gsm = tty->disc_data; - unsigned int base; + unsigned int base, addr; + struct gsm_dlci *dlci; switch (cmd) { case GSMIOC_GETCONF: @@ -3581,6 +3743,35 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, if (copy_from_user(&ce, (void __user *)arg, sizeof(ce))) return -EFAULT; return gsm_config_ext(gsm, &ce); + case GSMIOC_GETCONF_DLCI: + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) + return -EFAULT; + if (dc.channel == 0 || dc.channel >= NUM_DLCI) + return -EINVAL; + addr = array_index_nospec(dc.channel, NUM_DLCI); + dlci = gsm->dlci[addr]; + if (!dlci) { + dlci = gsm_dlci_alloc(gsm, addr); + if (!dlci) + return -ENOMEM; + } + gsm_dlci_copy_config_values(dlci, &dc); + if (copy_to_user((void __user *)arg, &dc, sizeof(dc))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF_DLCI: + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) + return -EFAULT; + if (dc.channel == 0 || dc.channel >= NUM_DLCI) + return -EINVAL; + addr = array_index_nospec(dc.channel, NUM_DLCI); + dlci = gsm->dlci[addr]; + if (!dlci) { + dlci = gsm_dlci_alloc(gsm, addr); + if (!dlci) + return -ENOMEM; + } + return gsm_dlci_config(dlci, &dc, 0); default: return n_tty_ioctl_helper(tty, cmd, arg); } @@ -4008,7 +4199,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) { struct gsm_dlci *dlci = tty->driver_data; struct tty_port *port = &dlci->port; - struct gsm_mux *gsm = dlci->gsm; port->count++; tty_port_tty_set(port, tty); @@ -4018,10 +4208,15 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) a DM straight back. This is ok as that will have caused a hangup */ tty_port_set_initialized(port, true); /* Start sending off SABM messages */ - if (gsm->initiator) - gsm_dlci_begin_open(dlci); - else - gsm_dlci_set_opening(dlci); + if (!dlci->gsm->wait_config) { + /* Start sending off SABM messages */ + if (dlci->gsm->initiator) + gsm_dlci_begin_open(dlci); + else + gsm_dlci_set_opening(dlci); + } else { + gsm_dlci_set_wait_config(dlci); + } /* And wait for virtual carrier */ return tty_port_block_til_ready(port, tty, filp); } @@ -4142,6 +4337,7 @@ static int gsmtty_ioctl(struct tty_struct *tty, { struct gsm_dlci *dlci = tty->driver_data; struct gsm_netconfig nc; + struct gsm_dlci_config dc; int index; if (dlci->state == DLCI_CLOSED) @@ -4165,6 +4361,23 @@ static int gsmtty_ioctl(struct tty_struct *tty, gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); return 0; + case GSMIOC_GETCONF_DLCI: + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) + return -EFAULT; + if (dc.channel != dlci->addr) + return -EPERM; + gsm_dlci_copy_config_values(dlci, &dc); + if (copy_to_user((void __user *)arg, &dc, sizeof(dc))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF_DLCI: + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) + return -EFAULT; + if (dc.channel >= NUM_DLCI) + return -EINVAL; + if (dc.channel != 0 && dc.channel != dlci->addr) + return -EPERM; + return gsm_dlci_config(dlci, &dc, 1); case TIOCMIWAIT: return gsm_wait_modem_change(dlci, (u32)arg); default: diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index c8f56c9b1a1c..1c9e5d2ea7de 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -28,27 +28,26 @@ * EAGAIN */ -#include <linux/types.h> -#include <linux/major.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/ctype.h> #include <linux/errno.h> -#include <linux/signal.h> +#include <linux/export.h> #include <linux/fcntl.h> +#include <linux/file.h> +#include <linux/jiffies.h> +#include <linux/math.h> +#include <linux/poll.h> +#include <linux/ratelimit.h> #include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/timer.h> -#include <linux/ctype.h> -#include <linux/mm.h> -#include <linux/string.h> +#include <linux/signal.h> #include <linux/slab.h> -#include <linux/poll.h> -#include <linux/bitops.h> -#include <linux/audit.h> -#include <linux/file.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/types.h> #include <linux/uaccess.h> -#include <linux/module.h> -#include <linux/ratelimit.h> #include <linux/vmalloc.h> + #include "tty.h" /* @@ -625,7 +624,7 @@ static size_t __process_echoes(struct tty_struct *tty) c = echo_buf(ldata, tail); if (c == ECHO_OP_START) { unsigned char op; - int no_space_left = 0; + bool space_left = true; /* * Since add_echo_byte() is called without holding @@ -664,7 +663,7 @@ static size_t __process_echoes(struct tty_struct *tty) num_bs = 8 - (num_chars & 7); if (num_bs > space) { - no_space_left = 1; + space_left = false; break; } space -= num_bs; @@ -690,7 +689,7 @@ static size_t __process_echoes(struct tty_struct *tty) case ECHO_OP_START: /* This is an escaped echo op start code */ if (!space) { - no_space_left = 1; + space_left = false; break; } tty_put_char(tty, ECHO_OP_START); @@ -710,7 +709,7 @@ static size_t __process_echoes(struct tty_struct *tty) * */ if (space < 2) { - no_space_left = 1; + space_left = false; break; } tty_put_char(tty, '^'); @@ -720,7 +719,7 @@ static size_t __process_echoes(struct tty_struct *tty) tail += 2; } - if (no_space_left) + if (!space_left) break; } else { if (O_OPOST(tty)) { @@ -1177,7 +1176,7 @@ static void n_tty_receive_overrun(struct tty_struct *tty) ldata->num_overrun++; if (time_after(jiffies, ldata->overrun_time + HZ) || - time_after(ldata->overrun_time, jiffies)) { + time_after(ldata->overrun_time, jiffies)) { tty_warn(tty, "%d input overrun(s)\n", ldata->num_overrun); ldata->overrun_time = jiffies; ldata->num_overrun = 0; @@ -1691,7 +1690,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, room = N_TTY_BUF_SIZE - (ldata->read_head - tail); if (I_PARMRK(tty)) - room = (room + 2) / 3; + room = DIV_ROUND_UP(room, 3); room--; if (room <= 0) { overflow = ldata->icanon && ldata->canon_head == tail; diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 287153d32536..1e8fe44a7099 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -365,6 +365,13 @@ static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p) if (dma->prepare_rx_dma) dma->prepare_rx_dma(p); } + +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + return dma && dma->tx_running; +} #else static inline int serial8250_tx_dma(struct uart_8250_port *p) { @@ -380,6 +387,11 @@ static inline int serial8250_request_dma(struct uart_8250_port *p) return -1; } static inline void serial8250_release_dma(struct uart_8250_port *p) { } + +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p) +{ + return false; +} #endif static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index ed5a94747692..f801b1f5b46c 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1014,14 +1014,16 @@ static int brcmuart_probe(struct platform_device *pdev) /* See if a Baud clock has been specified */ baud_mux_clk = of_clk_get_by_name(np, "sw_baud"); if (IS_ERR(baud_mux_clk)) { - if (PTR_ERR(baud_mux_clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(baud_mux_clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto release_dma; + } dev_dbg(dev, "BAUD MUX clock not specified\n"); } else { dev_dbg(dev, "BAUD MUX clock found\n"); ret = clk_prepare_enable(baud_mux_clk); if (ret) - return ret; + goto release_dma; priv->baud_mux_clk = baud_mux_clk; init_real_clk_rates(dev, priv); clk_rate = priv->default_mux_rate; @@ -1029,7 +1031,8 @@ static int brcmuart_probe(struct platform_device *pdev) if (clk_rate == 0) { dev_err(dev, "clock-frequency or clk not defined\n"); - return -EINVAL; + ret = -EINVAL; + goto release_dma; } dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); @@ -1116,7 +1119,9 @@ err1: serial8250_unregister_port(priv->line); err: brcmuart_free_bufs(dev, priv); - brcmuart_arbitration(priv, 0); +release_dma: + if (priv->dma_enabled) + brcmuart_arbitration(priv, 0); return ret; } @@ -1128,7 +1133,8 @@ static int brcmuart_remove(struct platform_device *pdev) hrtimer_cancel(&priv->hrt); serial8250_unregister_port(priv->line); brcmuart_free_bufs(&pdev->dev, priv); - brcmuart_arbitration(priv, 0); + if (priv->dma_enabled) + brcmuart_arbitration(priv, 0); return 0; } diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index ab63c308be0a..13bf535eedcd 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1158,6 +1158,7 @@ void serial8250_unregister_port(int line) uart->port.type = PORT_UNKNOWN; uart->port.dev = &serial8250_isa_devs->dev; uart->capabilities = 0; + serial8250_init_port(uart); serial8250_apply_quirks(uart); uart_add_one_port(&serial8250_reg, &uart->port); } else { diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index d94c3811a8f7..25a9ecf26be6 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -13,36 +13,49 @@ #include <linux/serial_reg.h> #include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/slab.h> #include "8250.h" #define UART_DLL_EM 9 #define UART_DLM_EM 10 +#define UART_HCR0_EM 11 + +/* + * A high value for UART_FCR_EM avoids overlapping with existing UART_* + * register defines. UART_FCR_EM_HW is the real HW register offset. + */ +#define UART_FCR_EM 0x10003 +#define UART_FCR_EM_HW 3 + +#define UART_HCR0_EM_SW_RESET BIT(7) /* SW Reset */ struct serial8250_em_priv { - struct clk *sclk; int line; }; -static void serial8250_em_serial_out(struct uart_port *p, int offset, int value) +static void serial8250_em_serial_out_helper(struct uart_port *p, int offset, + int value) { switch (offset) { case UART_TX: /* TX @ 0x00 */ writeb(value, p->membase); break; - case UART_FCR: /* FCR @ 0x0c (+1) */ case UART_LCR: /* LCR @ 0x10 (+1) */ case UART_MCR: /* MCR @ 0x14 (+1) */ case UART_SCR: /* SCR @ 0x20 (+1) */ writel(value, p->membase + ((offset + 1) << 2)); break; + case UART_FCR_EM: + writel(value, p->membase + (UART_FCR_EM_HW << 2)); + break; case UART_IER: /* IER @ 0x04 */ value &= 0x0f; /* only 4 valid bits - not Xscale */ fallthrough; case UART_DLL_EM: /* DLL @ 0x24 (+9) */ case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + case UART_HCR0_EM: /* HCR0 @ 0x2c */ writel(value, p->membase + (offset << 2)); + break; } } @@ -51,20 +64,81 @@ static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset) switch (offset) { case UART_RX: /* RX @ 0x00 */ return readb(p->membase); + case UART_LCR: /* LCR @ 0x10 (+1) */ case UART_MCR: /* MCR @ 0x14 (+1) */ case UART_LSR: /* LSR @ 0x18 (+1) */ case UART_MSR: /* MSR @ 0x1c (+1) */ case UART_SCR: /* SCR @ 0x20 (+1) */ return readl(p->membase + ((offset + 1) << 2)); + case UART_FCR_EM: + return readl(p->membase + (UART_FCR_EM_HW << 2)); case UART_IER: /* IER @ 0x04 */ case UART_IIR: /* IIR @ 0x08 */ case UART_DLL_EM: /* DLL @ 0x24 (+9) */ case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + case UART_HCR0_EM: /* HCR0 @ 0x2c */ return readl(p->membase + (offset << 2)); } return 0; |
