diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-24 21:17:14 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-24 21:17:14 +0100 |
commit | 17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9 (patch) | |
tree | bf06872140bc74115625e34f590b9ccc6270cbd6 /drivers/tty | |
parent | Merge tag 'usb-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/greg... (diff) | |
parent | tty: n_gsm: add keep alive support (diff) | |
download | linux-17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9.tar.xz linux-17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9.zip |
Merge tag 'tty-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty / serial driver updates from Greg KH:
"Here is the big set of serial and tty driver updates for 6.3-rc1.
Once again, Jiri and Ilpo have done a number of core vt and tty/serial
layer cleanups that were much needed and appreciated. Other than that,
it's just a bunch of little tty/serial driver updates:
- qcom-geni-serial driver updates
- liteuart driver updates
- hvcs driver cleanups
- n_gsm updates and additions for new features
- more 8250 device support added
- fpga/dfl update and additions
- imx serial driver updates
- fsl_lpuart updates
- other tiny fixes and updates for serial drivers
All of these have been in linux-next for a while with no reported
problems"
* tag 'tty-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (143 commits)
tty: n_gsm: add keep alive support
serial: imx: remove a redundant check
dt-bindings: serial: snps-dw-apb-uart: add dma & dma-names properties
soc: qcom: geni-se: Move qcom-geni-se.h to linux/soc/qcom/geni-se.h
tty: n_gsm: add TIOCMIWAIT support
tty: n_gsm: add RING/CD control support
tty: n_gsm: mark unusable ioctl structure fields accordingly
serial: imx: get rid of registers shadowing
serial: imx: refine local variables in rxint()
serial: imx: stop using USR2 in FIFO reading loop
serial: imx: remove redundant USR2 read from FIFO reading loop
serial: imx: do not break from FIFO reading loop prematurely
serial: imx: do not sysrq broken chars
serial: imx: work-around for hardware RX flood
serial: imx: factor-out common code to imx_uart_soft_reset()
serial: 8250_pci1xxxx: Add power management functions to quad-uart driver
serial: 8250_pci1xxxx: Add RS485 support to quad-uart driver
serial: 8250_pci1xxxx: Add driver for quad-uart support
serial: 8250_pci: Add serial8250_pci_setup_port definition in 8250_pcilib.c
tty: pcn_uart: fix memory leak with using debugfs_lookup()
...
Diffstat (limited to 'drivers/tty')
42 files changed, 2116 insertions, 984 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index f52266766df9..d7515d61659e 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -502,7 +502,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) */ change_speed(tty, info, NULL); - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); local_irq_restore(flags); return 0; @@ -556,7 +556,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->tport, 0); + tty_port_set_initialized(&info->tport, false); local_irq_restore(flags); } @@ -1329,7 +1329,7 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(tty, info); info->tport.count = 0; - tty_port_set_active(&info->tport, 0); + tty_port_set_active(&info->tport, false); info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); } @@ -1454,18 +1454,18 @@ static const struct tty_operations serial_ops = { .proc_show = rs_proc_show, }; -static int amiga_carrier_raised(struct tty_port *port) +static bool amiga_carrier_raised(struct tty_port *port) { return !(ciab.pra & SER_DCD); } -static void amiga_dtr_rts(struct tty_port *port, int raise) +static void amiga_dtr_rts(struct tty_port *port, bool active) { struct serial_state *info = container_of(port, struct serial_state, tport); unsigned long flags; - if (raise) + if (active) info->MCR |= SER_DTR|SER_RTS; else info->MCR &= ~(SER_DTR|SER_RTS); diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index a683e21df19c..10c10cfdf92a 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -376,7 +376,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) /* We are ready... raise DTR/RTS */ if (C_BAUD(tty)) if (hp->ops->dtr_rts) - hp->ops->dtr_rts(hp, 1); + hp->ops->dtr_rts(hp, true); tty_port_set_initialized(&hp->port, true); } @@ -406,7 +406,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) if (C_HUPCL(tty)) if (hp->ops->dtr_rts) - hp->ops->dtr_rts(hp, 0); + hp->ops->dtr_rts(hp, false); if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 18d005814e4b..9668f821db01 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -66,7 +66,7 @@ struct hv_ops { int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); /* Callbacks to handle tty ports */ - void (*dtr_rts)(struct hvc_struct *hp, int raise); + void (*dtr_rts)(struct hvc_struct *hp, bool active); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 7d49a872de48..543f35ddf523 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -658,13 +658,13 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) /** * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS * @hp: Pointer the HVC device (struct hvc_struct) - * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * @active: True to raise or false to lower DTR/RTS lines * * This routine notifies the HVC back-end to raise or lower DTR/RTS * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to * drop the IUCV connection (similar to hang up the modem). */ -static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, bool active) { struct hvc_iucv_private *priv; struct iucv_path *path; @@ -672,7 +672,7 @@ static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) /* Raising the DTR/RTS is ignored as IUCV connections can be * established at any times. */ - if (raise) + if (active) return; priv = hvc_iucv_get_private(hp->vtermno); diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4ba24963685e..1de1a09bf82d 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -52,6 +52,7 @@ #include <linux/device.h> #include <linux/init.h> +#include <linux/completion.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/kref.h> @@ -285,6 +286,7 @@ struct hvcs_struct { char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ struct list_head next; /* list management */ struct vio_dev *vdev; + struct completion *destroyed; }; static LIST_HEAD(hvcs_structs); @@ -432,7 +434,7 @@ static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); -static struct attribute *hvcs_attrs[] = { +static struct attribute *hvcs_dev_attrs[] = { &dev_attr_partner_vtys.attr, &dev_attr_partner_clcs.attr, &dev_attr_current_vty.attr, @@ -441,9 +443,7 @@ static struct attribute *hvcs_attrs[] = { NULL, }; -static struct attribute_group hvcs_attr_group = { - .attrs = hvcs_attrs, -}; +ATTRIBUTE_GROUPS(hvcs_dev); static ssize_t rescan_show(struct device_driver *ddp, char *buf) { @@ -468,6 +468,13 @@ static ssize_t rescan_store(struct device_driver *ddp, const char * buf, static DRIVER_ATTR_RW(rescan); +static struct attribute *hvcs_attrs[] = { + &driver_attr_rescan.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(hvcs); + static void hvcs_kick(void) { hvcs_kicked = 1; @@ -658,11 +665,13 @@ static void hvcs_destruct_port(struct tty_port *p) { struct hvcs_struct *hvcsd = container_of(p, struct hvcs_struct, port); struct vio_dev *vdev; + struct completion *comp; unsigned long flags; spin_lock(&hvcs_structs_lock); spin_lock_irqsave(&hvcsd->lock, flags); + comp = hvcsd->destroyed; /* the list_del poisons the pointers */ list_del(&(hvcsd->next)); @@ -682,15 +691,16 @@ static void hvcs_destruct_port(struct tty_port *p) hvcsd->p_unit_address = 0; hvcsd->p_partition_ID = 0; + hvcsd->destroyed = NULL; hvcs_return_index(hvcsd->index); memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock(&hvcs_structs_lock); - sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); - kfree(hvcsd); + if (comp) + complete(comp); } static const struct tty_port_operations hvcs_port_ops = { @@ -721,7 +731,6 @@ static int hvcs_probe( { struct hvcs_struct *hvcsd; int index, rc; - int retval; if (!dev || !id) { printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); @@ -778,13 +787,6 @@ static int hvcs_probe( list_add_tail(&(hvcsd->next), &hvcs_structs); spin_unlock(&hvcs_structs_lock); - retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); - if (retval) { - printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", - hvcsd->vdev->unit_address); - return retval; - } - printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); /* @@ -797,6 +799,7 @@ static int hvcs_probe( static void hvcs_remove(struct vio_dev *dev) { struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); + DECLARE_COMPLETION_ONSTACK(comp); unsigned long flags; struct tty_struct *tty; @@ -804,24 +807,22 @@ static void hvcs_remove(struct vio_dev *dev) spin_lock_irqsave(&hvcsd->lock, flags); - tty = hvcsd->port.tty; + hvcsd->destroyed = ∁ + tty = tty_port_tty_get(&hvcsd->port); spin_unlock_irqrestore(&hvcsd->lock, flags); /* - * Let the last holder of this object cause it to be removed, which - * would probably be tty_hangup below. - */ - tty_port_put(&hvcsd->port); - - /* - * The hangup is a scheduled function which will auto chain call - * hvcs_hangup. The tty should always be valid at this time unless a + * The tty should always be valid at this time unless a * simultaneous tty close already cleaned up the hvcs_struct. */ - if (tty) - tty_hangup(tty); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + tty_port_put(&hvcsd->port); + wait_for_completion(&comp); printk(KERN_INFO "HVCS: vty-server@%X removed from the" " vio bus.\n", dev->unit_address); }; @@ -831,6 +832,10 @@ static struct vio_driver hvcs_vio_driver = { .probe = hvcs_probe, .remove = hvcs_remove, .name = hvcs_driver_name, + .driver = { + .groups = hvcs_groups, + .dev_groups = hvcs_dev_groups, + }, }; /* Only called from hvcs_get_pi please */ @@ -1171,7 +1176,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); - if (--hvcsd->port.count == 0) { + if (hvcsd->port.count == 0) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + return; + } else if (--hvcsd->port.count == 0) { vio_disable_interrupts(hvcsd->vdev); @@ -1215,12 +1223,9 @@ static void hvcs_hangup(struct tty_struct * tty) { struct hvcs_struct *hvcsd = tty->driver_data; unsigned long flags; - int temp_open_count; int irq; spin_lock_irqsave(&hvcsd->lock, flags); - /* Preserve this so that we know how many kref refs to put */ - temp_open_count = hvcsd->port.count; /* * Don't kref put inside the spinlock because the destruction @@ -1230,11 +1235,7 @@ static void hvcs_hangup(struct tty_struct * tty) vio_disable_interrupts(hvcsd->vdev); hvcsd->todo_mask = 0; - - /* I don't think the tty needs the hvcs_struct pointer after a hangup */ - tty->driver_data = NULL; hvcsd->port.tty = NULL; - hvcsd->port.count = 0; /* This will drop any buffered data on the floor which is OK in a hangup @@ -1247,21 +1248,6 @@ static void hvcs_hangup(struct tty_struct * tty) spin_unlock_irqrestore(&hvcsd->lock, flags); free_irq(irq, hvcsd); - - /* - * We need to kref_put() for every open_count we have since the - * tty_hangup() function doesn't invoke a close per open connection on a - * non-console device. - */ - while(temp_open_count) { - --temp_open_count; - /* - * The final put will trigger destruction of the hvcs_struct. - * NOTE: If this hangup was signaled from user space then the - * final put will never happen. - */ - tty_port_put(&hvcsd->port); - } } /* @@ -1525,13 +1511,6 @@ static int __init hvcs_module_init(void) pr_info("HVCS: Driver registered.\n"); - /* This needs to be done AFTER the vio_register_driver() call or else - * the kobjects won't be initialized properly. - */ - rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); - if (rc) - pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc); - return 0; } @@ -1556,8 +1535,6 @@ static void __exit hvcs_module_exit(void) hvcs_pi_buff = NULL; spin_unlock(&hvcs_pi_lock); - driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); - tty_unregister_driver(hvcs_tty_driver); hvcs_free_index_list(); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 35b6fddf0341..42fa4c878b2e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -501,16 +501,16 @@ static int moxa_tiocmset(struct tty_struct *tty, static void moxa_poll(struct timer_list *); static void moxa_set_tty_param(struct tty_struct *, const struct ktermios *); static void moxa_shutdown(struct tty_port *); -static int moxa_carrier_raised(struct tty_port *); -static void moxa_dtr_rts(struct tty_port *, int); +static bool moxa_carrier_raised(struct tty_port *); +static void moxa_dtr_rts(struct tty_port *, bool); /* * moxa board interface functions: */ static void MoxaPortEnable(struct moxa_port *); static void MoxaPortDisable(struct moxa_port *); static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t); -static int MoxaPortGetLineOut(struct moxa_port *, int *, int *); -static void MoxaPortLineCtrl(struct moxa_port *, int, int); +static int MoxaPortGetLineOut(struct moxa_port *, bool *, bool *); +static void MoxaPortLineCtrl(struct moxa_port *, bool, bool); static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); static int MoxaPortLineStatus(struct moxa_port *); static void MoxaPortFlushData(struct moxa_port *, int); @@ -1432,7 +1432,7 @@ static void moxa_shutdown(struct tty_port *port) MoxaPortFlushData(ch, 2); } -static int moxa_carrier_raised(struct tty_port *port) +static bool moxa_carrier_raised(struct tty_port *port) { struct moxa_port *ch = container_of(port, struct moxa_port, port); int dcd; @@ -1443,10 +1443,10 @@ static int moxa_carrier_raised(struct tty_port *port) return dcd; } -static void moxa_dtr_rts(struct tty_port *port, int onoff) +static void moxa_dtr_rts(struct tty_port *port, bool active) { struct moxa_port *ch = container_of(port, struct moxa_port, port); - MoxaPortLineCtrl(ch, onoff, onoff); + MoxaPortLineCtrl(ch, active, active); } @@ -1481,10 +1481,10 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) if (!tty_port_initialized(&ch->port)) { ch->statusflags = 0; moxa_set_tty_param(tty, &tty->termios); - MoxaPortLineCtrl(ch, 1, 1); + MoxaPortLineCtrl(ch, true, true); MoxaPortEnable(ch); MoxaSetFifo(ch, ch->type == PORT_16550A); - tty_port_set_initialized(&ch->port, 1); + tty_port_set_initialized(&ch->port, true); } mutex_unlock(&ch->port.mutex); mutex_unlock(&moxa_openlock); @@ -1557,19 +1557,21 @@ static unsigned int moxa_chars_in_buffer(struct tty_struct *tty) static int moxa_tiocmget(struct tty_struct *tty) { struct moxa_port *ch = tty->driver_data; - int flag = 0, dtr, rts; + bool dtr_active, rts_active; + int flag = 0; + int status; - MoxaPortGetLineOut(ch, &dtr, &rts); - if (dtr) + MoxaPortGetLineOut(ch, &dtr_active, &rts_active); + if (dtr_active) flag |= TIOCM_DTR; - if (rts) + if (rts_active) flag |= TIOCM_RTS; - dtr = MoxaPortLineStatus(ch); - if (dtr & 1) + status = MoxaPortLineStatus(ch); + if (status & 1) flag |= TIOCM_CTS; - if (dtr & 2) + if (status & 2) flag |= TIOCM_DSR; - if (dtr & 4) + if (status & 4) flag |= TIOCM_CD; return flag; } @@ -1577,8 +1579,8 @@ static int moxa_tiocmget(struct tty_struct *tty) static int moxa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { + bool dtr_active, rts_active; struct moxa_port *ch; - int dtr, rts; mutex_lock(&moxa_openlock); ch = tty->driver_data; @@ -1587,16 +1589,16 @@ static int moxa_tiocmset(struct tty_struct *tty, return -EINVAL; } - MoxaPortGetLineOut(ch, &dtr, &rts); + MoxaPortGetLineOut(ch, &dtr_active, &rts_active); if (set & TIOCM_RTS) - rts = 1; + rts_active = true; if (set & TIOCM_DTR) - dtr = 1; + dtr_active = true; if (clear & TIOCM_RTS) - rts = 0; + rts_active = false; if (clear & TIOCM_DTR) - dtr = 0; - MoxaPortLineCtrl(ch, dtr, rts); + dtr_active = false; + MoxaPortLineCtrl(ch, dtr_active, rts_active); mutex_unlock(&moxa_openlock); return 0; } @@ -1664,8 +1666,8 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, u16 __iomem *ip) { struct tty_struct *tty = tty_port_tty_get(&p->port); + bool inited = tty_port_initialized(&p->port); void __iomem *ofsAddr; - unsigned int inited = tty_port_initialized(&p->port); u16 intr; if (tty) { @@ -1877,12 +1879,12 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode) * * Function 13: Get the DTR/RTS state of this port. * Syntax: - * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); + * int MoxaPortGetLineOut(int port, bool *dtrState, bool *rtsState); * int port : port number (0 - 127) - * int * dtrState : pointer to INT to receive the current DTR + * bool * dtr_active : pointer to bool to receive the current DTR * state. (if NULL, this function will not * write to this address) - * int * rtsState : pointer to INT to receive the current RTS + * bool * rts_active : pointer to bool to receive the current RTS * state. (if NULL, this function will not * write to this address) * @@ -1892,10 +1894,10 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode) * * Function 14: Setting the DTR/RTS output state of this port. * Syntax: - * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); + * void MoxaPortLineCtrl(int port, bool dtrState, bool rtsState); * int port : port number (0 - 127) - * int dtrState : DTR output state (0: off, 1: on) - * int rtsState : RTS output state (0: off, 1: on) + * bool dtr_active : DTR output state + * bool rts_active : RTS output state * * * Function 15: Setting the flow control of this port. @@ -2103,24 +2105,24 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, return baud; } -static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState, - int *rtsState) +static int MoxaPortGetLineOut(struct moxa_port *port, bool *dtr_active, + bool *rts_active) { - if (dtrState) - *dtrState = !!(port->lineCtrl & DTR_ON); - if (rtsState) - *rtsState = !!(port->lineCtrl & RTS_ON); + if (dtr_active) + *dtr_active = port->lineCtrl & DTR_ON; + if (rts_active) + *rts_active = port->lineCtrl & RTS_ON; return 0; } -static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts) +static void MoxaPortLineCtrl(struct moxa_port *port, bool dtr_active, bool rts_active) { u8 mode = 0; - if (dtr) + if (dtr_active) mode |= DTR_ON; - if (rts) + if (rts_active) mode |= RTS_ON; port->lineCtrl = mode; moxafunc(port->tableAddr, FC_LineControl, mode); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 2436e0b10f9a..ef3116e87975 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -458,13 +458,14 @@ static void __mxser_stop_tx(struct mxser_port *info) outb(info->IER, info->ioaddr + UART_IER); } -static int mxser_carrier_raised(struct tty_port *port) +static bool mxser_carrier_raised(struct tty_port *port) { struct mxser_port *mp = container_of(port, struct mxser_port, port); - return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; + + return inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD; } -static void mxser_dtr_rts(struct tty_port *port, int on) +static void mxser_dtr_rts(struct tty_port *port, bool active) { struct mxser_port *mp = container_of(port, struct mxser_port, port); unsigned long flags; @@ -472,7 +473,7 @@ static void mxser_dtr_rts(struct tty_port *port, int on) spin_lock_irqsave(&mp->slock, flags); mcr = inb(mp->ioaddr + UART_MCR); - if (on) + if (active) mcr |= UART_MCR_DTR | UART_MCR_RTS; else mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); @@ -1063,7 +1064,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, } else { retval = mxser_activate(port, tty); if (retval == 0) - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } mutex_unlock(&port->mutex); return retval; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 348d85c13f91..7aa05ebed8e1 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -318,6 +318,11 @@ struct gsm_mux { struct gsm_control *pending_cmd;/* Our current pending command */ spinlock_t control_lock; /* Protects the pending command */ + /* Keep-alive */ + struct timer_list ka_timer; /* Keep-alive response timer */ + u8 ka_num; /* Keep-alive match pattern */ + signed int ka_retries; /* Keep-alive retry counter, -1 if not yet initialized */ + /* Configuration */ int adaption; /* 1 or 2 supported */ u8 ftype; /* UI or UIH */ @@ -325,6 +330,7 @@ struct gsm_mux { unsigned int t3; /* Power wake-up timer in seconds. */ int n2; /* Retry count */ u8 k; /* Window size */ + u32 keep_alive; /* Control channel keep-alive in 10ms */ /* Statistics (not currently exposed) */ unsigned long bad_fcs; @@ -540,6 +546,11 @@ static u8 gsm_encode_modem(const struct gsm_dlci *dlci) modembits |= MDM_IC; if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator) modembits |= MDM_DV; + /* special mappings for passive side to operate as UE */ + if (dlci->modem_tx & TIOCM_OUT1) + modembits |= MDM_IC; + if (dlci->modem_tx & TIOCM_OUT2) + modembits |= MDM_DV; return modembits; } @@ -1531,6 +1542,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, if (brk & 0x01) tty_insert_flip_char(&dlci->port, 0, TTY_BREAK); dlci->modem_rx = mlines; + wake_up_interruptible(&dlci->gsm->event); } /** @@ -1897,11 +1909,13 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, const u8 *data, int clen) { struct gsm_control *ctrl; + struct gsm_dlci *dlci; unsigned long flags; spin_lock_irqsave(&gsm->control_lock, flags); ctrl = gsm->pending_cmd; + dlci = gsm->dlci[0]; command |= 1; /* Does the reply match our command */ if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { @@ -1916,6 +1930,53 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, /* Or did we receive the PN response to our PN command */ } else if (command == CMD_PN) { gsm_control_negotiation(gsm, 0, data, clen); + /* Or did we receive the TEST response to our TEST command */ + } else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) { + gsm->ka_retries = -1; /* trigger new keep-alive message */ + if (dlci && !dlci->dead) + mod_timer(&gsm->ka_timer, jiffies + gsm->keep_alive * HZ / 100); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_keep_alive - check timeout or start keep-alive + * @t: timer contained in our gsm object + * + * Called off the keep-alive timer expiry signaling that our link + * partner is not responding anymore. Link will be closed. + * This is also called to startup our timer. + */ + +static void gsm_control_keep_alive(struct timer_list *t) +{ + struct gsm_mux *gsm = from_timer(gsm, t, ka_timer); + unsigned long flags; + + spin_lock_irqsave(&gsm->control_lock, flags); + if (gsm->ka_num && gsm->ka_retries == 0) { + /* Keep-alive expired -> close the link */ + if (debug & DBG_ERRORS) + pr_debug("%s keep-alive timed out\n", __func__); + spin_unlock_irqrestore(&gsm->control_lock, flags); + if (gsm->dlci[0]) + gsm_dlci_begin_close(gsm->dlci[0]); + return; + } else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) { + if (gsm->ka_retries > 0) { + /* T2 expired for keep-alive -> resend */ + gsm->ka_retries--; + } else { + /* Start keep-alive timer */ + gsm->ka_num++; + if (!gsm->ka_num) + gsm->ka_num++; + gsm->ka_retries = (signed int)gsm->n2; + } + gsm_control_command(gsm, CMD_TEST, &gsm->ka_num, + sizeof(gsm->ka_num)); + mod_timer(&gsm->ka_timer, + jiffies + gsm->t2 * HZ / 100); } spin_unlock_irqrestore(&gsm->control_lock, flags); } @@ -2059,14 +2120,16 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) tty_port_tty_hangup(&dlci->port, false); gsm_dlci_clear_queues(dlci->gsm, dlci); /* Ensure that gsmtty_open() can return. */ - tty_port_set_initialized(&dlci->port, 0); + tty_port_set_initialized(&dlci->port, false); wake_up_interruptible(&dlci->port.open_wait); - } else + } else { + del_timer(&dlci->gsm->ka_timer); dlci->gsm->dead = true; + } /* A DLCI 0 close is a MUX termination so we need to kick that back to userspace somehow */ gsm_dlci_data_kick(dlci); - wake_up(&dlci->gsm->event); + wake_up_all(&dlci->gsm->event); } /** @@ -2078,6 +2141,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) static void gsm_dlci_open(struct gsm_dlci *dlci) { + struct gsm_mux *gsm = dlci->gsm; + /* Note that SABM UA .. SABM UA first UA lost can mean that we go open -> open */ del_timer(&dlci->t1); @@ -2087,8 +2152,15 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) if (debug & DBG_ERRORS) pr_debug("DLCI %d goes open.\n", dlci->addr); /* Send current modem state */ - if (dlci->addr) + if (dlci->addr) { gsm_modem_update(dlci, 0); + } else { + /* Start keep-alive control */ + gsm->ka_num = 0; + gsm->ka_retries = -1; + mod_timer(&gsm->ka_timer, + jiffies + gsm->keep_alive * HZ / 100); + } gsm_dlci_data_kick(dlci); wake_up(&dlci->gsm->event); } @@ -2267,6 +2339,7 @@ static void gsm_dlci_begin_close(struct gsm_dlci *dlci) dlci->state = DLCI_CLOSING; gsm_command(dlci->gsm, dlci->addr, DISC|PF); mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + wake_up_interruptible(&gsm->event); } /** @@ -2840,6 +2913,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc) /* Finish outstanding timers, making sure they are done */ del_timer_sync(&gsm->kick_timer); del_timer_sync(&gsm->t2_timer); + del_timer_sync(&gsm->ka_timer); /* Finish writing to ldisc */ flush_work(&gsm->tx_work); @@ -2987,6 +3061,7 @@ static struct gsm_mux *gsm_alloc_mux(void) INIT_LIST_HEAD(&gsm->tx_data_list); timer_setup(&gsm->kick_timer, gsm_kick_timer, 0); timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0); + timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0); INIT_WORK(&gsm->tx_work, gsmld_write_task); init_waitqueue_head(&gsm->event); spin_lock_init(&gsm->control_lock); @@ -3003,6 +3078,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->keep_alive = 0; /* Disabled */ /* Store the instance to the mux array or abort if no space is * available. @@ -3138,6 +3214,29 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c) return 0; } +static void gsm_copy_config_ext_values(struct gsm_mux *gsm, + struct gsm_config_ext *ce) +{ + memset(ce, 0, sizeof(*ce)); + ce->keep_alive = gsm->keep_alive; +} + +static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) +{ + unsigned int i; + + /* + * Check that userspace doesn't put stuff in here to prevent breakages + * in the future. + */ + for (i = 0; i < ARRAY_SIZE(ce->reserved); i++) + if (ce->reserved[i]) + return -EINVAL; + + gsm->keep_alive = ce->keep_alive; + return 0; +} + /** * gsmld_output - write to link * @gsm: our mux @@ -3456,6 +3555,7 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct gsm_config c; + struct gsm_config_ext ce; struct gsm_mux *gsm = tty->disc_data; unsigned int base; @@ -3472,6 +3572,15 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, case GSMIOC_GETFIRST: base = mux_num_to_base(gsm); return put_user(base + 1, (__u32 __user *)arg); + case GSMIOC_GETCONF_EXT: + gsm_copy_config_ext_values(gsm, &ce); + if (copy_to_user((void __user *)arg, &ce, sizeof(ce))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF_EXT: + if (copy_from_user(&ce, (void __user *)arg, sizeof(ce))) + return -EFAULT; + return gsm_config_ext(gsm, &ce); default: return n_tty_ioctl_helper(tty, cmd, arg); } @@ -3770,16 +3879,43 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk) return -EPROTONOSUPPORT; } -static int gsm_carrier_raised(struct tty_port *port) +/** + * gsm_wait_modem_change - wait for modem status line change + * @dlci: channel + * @mask: modem status line bits + * + * The function returns if: + * - any given modem status line bit changed + * - the wait event function got interrupted (e.g. by a signal) + * - the underlying DLCI was closed + * - the underlying ldisc device was removed + */ +static int gsm_wait_modem_change(struct gsm_dlci *dlci, u32 mask) +{ + struct gsm_mux *gsm = dlci->gsm; + u32 old = dlci->modem_rx; + int ret; + + ret = wait_event_interruptible(gsm->event, gsm->dead || + dlci->state != DLCI_OPEN || + (old ^ dlci->modem_rx) & mask); + if (gsm->dead) + return -ENODEV; + if (dlci->state != DLCI_OPEN) + return -EL2NSYNC; + return ret; +} + +static bool gsm_carrier_raised(struct tty_port *port) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); struct gsm_mux *gsm = dlci->gsm; /* Not yet open so no carrier info */ if (dlci->state != DLCI_OPEN) - return 0; + return false; if (debug & DBG_CD_ON) - return 1; + return true; /* * Basic mode with control channel in ADM mode may not respond @@ -3787,16 +3923,16 @@ static int gsm_carrier_raised(struct tty_port *port) */ if (gsm->encoding == GSM_BASIC_OPT && gsm->dlci[0]->mode == DLCI_MODE_ADM && !dlci->modem_rx) - return 1; + return true; return dlci->modem_rx & TIOCM_CD; } -static void gsm_dtr_rts(struct tty_port *port, int onoff) +static void gsm_dtr_rts(struct tty_port *port, bool active) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); unsigned int modem_tx = dlci->modem_tx; - if (onoff) + if (active) modem_tx |= TIOCM_DTR | TIOCM_RTS; else modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); @@ -3880,7 +4016,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) dlci->modem_rx = 0; /* We could in theory open and close before we wait - eg if we get a DM straight back. This is ok as that will have caused a hangup */ - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); /* Start sending off SABM messages */ if (gsm->initiator) gsm_dlci_begin_open(dlci); @@ -4029,6 +4165,8 @@ static int gsmtty_ioctl(struct tty_struct *tty, gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); return 0; + case TIOCMIWAIT: + return gsm_wait_modem_change(dlci, (u32)arg); default: return -ENOIOCTLCMD; } diff --git a/drivers/tty/serial/8250/8250_dfl.c b/drivers/tty/serial/8250/8250_dfl.c new file mode 100644 index 000000000000..6c5ff019df4b --- /dev/null +++ b/drivers/tty/serial/8250/8250_dfl.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for FPGA UART + * + * Copyright (C) 2022 Intel Corporation. + * + * Authors: + * Ananda Ravuri <ananda.ravuri@intel.com> + * Matthew Gerlach <matthew.gerlach@linux.intel.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dfl.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/types.h> + +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#define DFHv1_PARAM_ID_CLK_FRQ 0x2 +#define DFHv1_PARAM_ID_FIFO_LEN 0x3 + +#define DFHv1_PARAM_ID_REG_LAYOUT 0x4 +#define DFHv1_PARAM_REG_LAYOUT_WIDTH GENMASK_ULL(63, 32) +#define DFHv1_PARAM_REG_LAYOUT_SHIFT GENMASK_ULL(31, 0) + +struct dfl_uart { + int line; +}; + +static int dfh_get_u64_param_val(struct dfl_device *dfl_dev, int param_id, u64 *pval) +{ + size_t psize; + u64 *p; + + p = dfh_find_param(dfl_dev, param_id, &psize); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (psize != sizeof(*pval)) + return -EINVAL; + + *pval = *p; + + return 0; +} + +static int dfl_uart_get_params(struct dfl_device *dfl_dev, struct uart_8250_port *uart) +{ + struct device *dev = &dfl_dev->dev; + u64 fifo_len, clk_freq, reg_layout; + u32 reg_width; + int ret; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_CLK_FRQ, &clk_freq); + if (ret) + return dev_err_probe(dev, ret, "missing CLK_FRQ param\n"); + + uart->port.uartclk = clk_freq; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_FIFO_LEN, &fifo_len); + if (ret) + return dev_err_probe(dev, ret, "missing FIFO_LEN param\n"); + + switch (fifo_len) { + case 32: + uart->port.type = PORT_ALTR_16550_F32; + break; + + case 64: + uart->port.type = PORT_ALTR_16550_F64; + break; + + case 128: + uart->port.type = PORT_ALTR_16550_F128; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported FIFO_LEN %llu\n", fifo_len); + } + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_REG_LAYOUT, ®_layout); + if (ret) + return dev_err_probe(dev, ret, "missing REG_LAYOUT param\n"); + + uart->port.regshift = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_SHIFT, reg_layout); + reg_width = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_WIDTH, reg_layout); + switch (reg_width) { + case 4: + uart->port.iotype = UPIO_MEM32; + break; + + case 2: + uart->port.iotype = UPIO_MEM16; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported reg-width %u\n", reg_width); + + } + + return 0; +} + +static int dfl_uart_probe(struct dfl_device *dfl_dev) +{ + struct device *dev = &dfl_dev->dev; + struct uart_8250_port uart = { }; + struct dfl_uart *dfluart; + int ret; + + uart.port.flags = UPF_IOREMAP; + uart.port.mapbase = dfl_dev->mmio_res.start; + uart.port.mapsize = resource_size(&dfl_dev->mmio_res); + + ret = dfl_uart_get_params(dfl_dev, &uart); + if (ret < 0) + return dev_err_probe(dev, ret, "failed uart feature walk\n"); + + if (dfl_dev->num_irqs == 1) + uart.port.irq = dfl_dev->irqs[0]; + + dfluart = devm_kzalloc(dev, sizeof(*dfluart), GFP_KERNEL); + if (!dfluart) + return -ENOMEM; + + dfluart->line = serial8250_register_8250_port(&uart); + if (dfluart->line < 0) + return dev_err_probe(dev, dfluart->line, "unable to register 8250 port.\n"); + + dev_set_drvdata(dev, dfluart); + + return 0; +} + +static void dfl_uart_remove(struct dfl_device *dfl_dev) +{ + struct dfl_uart *dfluart = dev_get_drvdata(&dfl_dev->dev); + + serial8250_unregister_port(dfluart->line); +} + +#define FME_FEATURE_ID_UART 0x24 + +static const struct dfl_device_id dfl_uart_ids[] = { + { FME_ID, FME_FEATURE_ID_UART }, + { } +}; +MODULE_DEVICE_TABLE(dfl, dfl_uart_ids); + +static struct dfl_driver dfl_uart_driver = { + .drv = { + .name = "dfl-uart", + }, + .id_table = dfl_uart_ids, + .probe = dfl_uart_probe, + .remove = dfl_uart_remove, +}; +module_dfl_driver(dfl_uart_driver); + +MODULE_DESCRIPTION("DFL Intel UART driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index f271becfc46c..0ebde0ab8167 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -136,11 +136,11 @@ static void __init init_port(struct earlycon_device *device) unsigned char c; unsigned int ier; - serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ + serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */ ier = serial8250_early_in(port, UART_IER); serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */ serial8250_early_out(port, UART_FCR, 0); /* no fifo */ - serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ + serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); if (port->uartclk) { divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 8e9f247590bd..c55be6fda0ca 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -24,6 +24,7 @@ #include <asm/io.h> #include "8250.h" +#include "8250_pcilib.h" /* * init function returns: @@ -89,28 +90,7 @@ static int setup_port(struct serial_private *priv, struct uart_8250_port *port, u8 bar, unsigned int offset, int regshift) { - struct pci_dev *dev = priv->dev; - - if (bar >= PCI_STD_NUM_BARS) - return -EINVAL; - - if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) - return -ENOMEM; - - port->port.iotype = UPIO_MEM; - port->port.iobase = 0; - port->port.mapbase = pci_resource_start(dev, bar) + offset; - port->port.membase = pcim_iomap_table(dev)[bar] + offset; - port->port.regshift = regshift; - } else { - port->port.iotype = UPIO_PORT; - port->port.iobase = pci_resource_start(dev, bar) + offset; - port->port.mapbase = 0; - port->port.membase = NULL; - port->port.regshift = 0; - } - return 0; + return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift); } /* @@ -5757,3 +5737,4 @@ module_pci_driver(serial_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); MODULE_DEVICE_TABLE(pci, serial_pci_tbl); +MODULE_IMPORT_NS(SERIAL_8250_PCI); diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c new file mode 100644 index 000000000000..a3b25779d921 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe module for 8250/16550-type MCHP PCI serial ports. + * + * Based on drivers/tty/serial/8250/8250_pci.c, + * + * Copyright (C) 2022 Microchip Technology Inc., All Rights Reserved. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/units.h> +#include <linux/tty.h> + +#include <asm/byteorder.h> + +#include "8250.h" +#include "8250_pcilib.h" + +#define PCI_DEVICE_ID_EFAR_PCI12000 0xa002 +#define PCI_DEVICE_ID_EFAR_PCI11010 0xa012 +#define PCI_DEVICE_ID_EFAR_PCI11101 0xa022 +#define PCI_DEVICE_ID_EFAR_PCI11400 0xa032 +#define PCI_DEVICE_ID_EFAR_PCI11414 0xa042 + +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p 0x0001 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012 0x0002 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013 0x0003 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023 0x0004 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123 0x0005 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01 0x0006 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02 0x0007 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03 0x0008 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12 0x0009 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13 0x000a +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23 0x000b +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0 0x000c +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1 0x000d +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2 0x000e +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 0x000f + +#define PCI_SUBDEVICE_ID_EFAR_PCI12000 PCI_DEVICE_ID_EFAR_PCI12000 +#define PCI_SUBDEVICE_ID_EFAR_PCI11010 PCI_DEVICE_ID_EFAR_PCI11010 +#define PCI_SUBDEVICE_ID_EFAR_PCI11101 PCI_DEVICE_ID_EFAR_PCI11101 +#define PCI_SUBDEVICE_ID_EFAR_PCI11400 PCI_DEVICE_ID_EFAR_PCI11400 +#define PCI_SUBDEVICE_ID_EFAR_PCI11414 PCI_DEVICE_ID_EFAR_PCI11414 + +#define UART_ACTV_REG 0x11 +#define UART_BLOCK_SET_ACTIVE BIT(0) + +#define UART_PCI_CTRL_REG 0x80 +#define UART_PCI_CTRL_SET_MULTIPLE_MSI BIT(4) +#define UART_PCI_CTRL_D3_CLK_ENABLE BIT(0) + +#define ADCL_CFG_REG 0x40 +#define ADCL_CFG_POL_SEL BIT(2) +#define ADCL_CFG_PIN_SEL BIT(1) +#define ADCL_CFG_EN BIT(0) + +#define UART_BIT_SAMPLE_CNT 16 +#define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8) +#define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8) +#define UART_CLOCK_DEFAULT (62500 * HZ_PER_KHZ) + +#define UART_WAKE_REG 0x8C +#define UART_WAKE_MASK_REG 0x90 +#define UART_WAKE_N_PIN BIT(2) +#define UART_WAKE_NCTS BIT(1) +#define UART_WAKE_INT BIT(0) +#define UART_WAKE_SRCS \ + (UART_WAKE_N_PIN | UART_WAKE_NCTS | UART_WAKE_INT) + +#define UART_BAUD_CLK_DIVISOR_REG 0x54 + +#define UART_RESET_REG 0x94 +#define UART_RESET_D3_RESET_DISABLE BIT(16) + +#define MAX_PORTS 4 +#define PORT_OFFSET 0x100 + +static const int logical_to_physical_port_idx[][MAX_PORTS] = { + {0, 1, 2, 3}, /* PCI12000, PCI11010, PCI11101, PCI11400, PCI11414 */ + {0, 1, 2, 3}, /* PCI4p */ + {0, 1, 2, -1}, /* PCI3p012 */ + {0, 1, 3, -1}, /* PCI3p013 */ + {0, 2, 3, -1}, /* PCI3p023 */ + {1, 2, 3, -1}, /* PCI3p123 */ + {0, 1, -1, -1}, /* PCI2p01 */ + {0, 2, -1, -1}, /* PCI2p02 */ + {0, 3, -1, -1}, /* PCI2p03 */ + {1, 2, -1, -1}, /* PCI2p12 */ + {1, 3, -1, -1}, /* PCI2p13 */ + {2, 3, -1, -1}, /* PCI2p23 */ + {0, -1, -1, -1}, /* PCI1p0 */ + {1, -1, -1, -1}, /* PCI1p1 */ + {2, -1, -1, -1}, /* PCI1p2 */ + {3, -1, -1, -1}, /* PCI1p3 */ +}; + +struct pci1xxxx_8250 { + unsigned int nr; + void __iomem *membase; + int line[]; +}; + +static int pci1xxxx_get_num_ports(struct pci_dev *dev) +{ + switch (dev->subsystem_device) { + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3: + case PCI_SUBDEVICE_ID_EFAR_PCI12000: + case PCI_SUBDEVICE_ID_EFAR_PCI11010: + case PCI_SUBDEVICE_ID_EFAR_PCI11101: + case PCI_SUBDEVICE_ID_EFAR_PCI11400: + default: + return 1; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23: + return 2; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023: + return 3; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p: + case PCI_SUBDEVICE_ID_EFAR_PCI11414: + return 4; + } +} + +static unsigned int pci1xxxx_get_divisor(struct uart_port *port, + unsigned int baud, unsigned int *frac) +{ + unsigned int quot; + + /* + * Calculate baud rate sampling period in nanoseconds. + * Fractional part x denotes x/255 parts of a nanosecond. + */ + quot = NSEC_PER_SEC / (baud * UART_BIT_SAMPLE_CNT); + *frac = (NSEC_PER_SEC - quot * baud * UART_BIT_SAMPLE_CNT) * + 255 / UART_BIT_SAMPLE_CNT / baud; + + return quot; +} + +static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int frac) +{ + writel(FIELD_PREP(BAUD_CLOCK_DIV_INT_MSK, quot) | frac, + port->membase + UART_BAUD_CLK_DIVISOR_REG); +} + +static int pci1xxxx_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 delay_in_baud_periods; + u32 baud_period_in_ns; + u32 mode_cfg = 0; + u32 clock_div; + + /* + * pci1xxxx's uart hardware supports only RTS delay after + * Tx and in units of bit times to a maximum of 15 + */ + if (rs485->flags & SER_RS485_ENABLED) { + mode_cfg = ADCL_CFG_EN | ADCL_CFG_PIN_SEL; + + if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) + mode_cfg |= ADCL_CFG_POL_SEL; + + if (rs485->delay_rts_after_send) { + clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG); + baud_period_in_ns = + FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) * + UART_BIT_SAMPLE_CNT; + delay_in_baud_periods = + rs485->delay_rts_after_send * NSEC_PER_MSEC / + baud_period_in_ns; + delay_in_baud_periods = + min_t(u32, delay_in_baud_periods, + FIELD_MAX(ADCL_CFG_RTS_DELAY_MASK)); + mode_cfg |= FIELD_PREP(ADCL_CFG_RTS_DELAY_MASK, + delay_in_baud_periods); + rs485->delay_rts_after_send = + baud_period_in_ns * delay_in_baud_periods / + NSEC_PER_MSEC; + } + } + writel(mode_cfg, port->membase + ADCL_CFG_REG); + return 0; +} + +static const struct serial_rs485 pci1xxxx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + +static bool pci1xxxx_port_suspend(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + bool ret = false; + u8 wakeup_mask; + + mutex_lock(&tport->mutex); + if (port->suspended == 0 && port->dev) { + wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG); + + spin_lock_irqsave(&port->lock, flags); + port->mctrl &= ~TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); + + ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS; + } + + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + mutex_unlock(&tport->mutex); + + return ret; +} + +static void pci1xxxx_port_resume(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + + mutex_lock(&tport->mutex); + writeb(UART_BLOCK_SET_ACTIVE, port->membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + + if (port->suspended == 0) { + spin_lock_irqsave(&port->lock, flags); + port->mctrl |= TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); + } + mutex_unlock(&tport->mutex); +} + +static int pci1xxxx_suspend(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + bool wakeup = false; + unsigned int data; + void __iomem *p; + int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + serial8250_suspend_port(priv->line[i]); + wakeup |= pci1xxxx_port_suspend(priv->line[i]); + } + } + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + + if (wakeup) + writeb(UART_PCI_CTRL_D3_CLK_ENABLE, p + UART_PCI_CTRL_REG); + + iounmap(p); + device_set_wakeup_enable(dev, true); + pci_wake_from_d3(pcidev, true); + + return 0; +} + +static int pci1xxxx_resume(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + unsigned int data; + void __iomem *p; + int i; + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + iounmap(p); + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + pci1xxxx_port_resume(priv->line[i]); + serial8250_resume_port(priv->line[i]); + } + } + + return 0; +} + +static int pci1xxxx_setup(struct pci_dev *pdev, + struct uart_8250_port *port, int port_idx) +{ + int ret; + + port->port.flags |= UPF_FIXED_TYPE | UPF_SKIP_TEST; + port->port.type = PORT_MCHP16550A; + port->port.set_termios = serial8250_do_set_termios; + port->port.get_divisor = pci1xxxx_get_divisor; + port->port.set_divisor = pci1xxxx_set_divisor; + port->port.rs485_config = pci1xxxx_rs485_config; + port->port.rs485_supported = pci1xxxx_rs485_supported; + + ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0); + if (ret < 0) + return ret; + + writeb(UART_BLOCK_SET_ACTIVE, port->port.membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->port.membase + UART_WAKE_REG); + writeb(UART_WAKE_N_PIN, port->port.membase + UART_WAKE_MASK_REG); + + return 0; +} + +static unsigned int pci1xxxx_get_max_port(int subsys_dev) +{ + unsigned int i = MAX_PORTS; + + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + while (i--) { + if (logical_to_physical_port_idx[subsys_dev][i] != -1) + return logical_to_physical_port_idx[subsys_dev][i] + 1; + } + + if (subsys_dev == PCI_SUBDEVICE_ID_EFAR_PCI11414) + return 4; + + return 1; +} + +static int pci1xxxx_logical_to_physical_port_translate(int subsys_dev, int port) +{ + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + return logical_to_physical_port_idx[subsys_dev][port]; + + return logical_to_physical_port_idx[0][port]; +} + +static int pci1xxxx_serial_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct pci1xxxx_8250 *priv; + struct uart_8250_port uart; + unsigned int max_vec_reqd; + unsigned int nr_ports, i; + int num_vectors; + int subsys_dev; + int port_idx; + int rc; + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + nr_ports = pci1xxxx_get_num_ports(pdev); + + priv = devm_kzalloc(dev, struct_size(priv, line, nr_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->membase = pci_ioremap_bar(pdev, 0); + if (!priv->membase) + return -ENOMEM; + + pci_set_master(pdev); + + priv->nr = nr_ports; + + subsys_dev = pdev->subsystem_device; + max_vec_reqd = pci1xxxx_get_max_port(subsys_dev); + + num_vectors = pci_alloc_irq_vectors(pdev, 1, max_vec_reqd, PCI_IRQ_ALL_TYPES); + if (num_vectors < 0) { + pci_iounmap(pdev, priv->membase); + return num_vectors; + } + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; + uart.port.uartclk = UART_CLOCK_DEFAULT; + uart.port.dev = dev; + + if (num_vectors == max_vec_reqd) + writeb(UART_PCI_CTRL_SET_MULTIPLE_MSI, priv->membase + UART_PCI_CTRL_REG); + + for (i = 0; i < nr_ports; i++) { + priv->line[i] = -ENODEV; + + port_idx = pci1xxxx_logical_to_physical_port_translate(subsys_dev, i); + + if (num_vectors == max_vec_reqd) + uart.port.irq = pci_irq_vector(pdev, port_idx); + else + uart.port.irq = pci_irq_vector(pdev, 0); + + rc = pci1xxxx_setup(pdev, &uart, port_idx); + if (rc) { + dev_warn(dev, "Failed to setup port %u\n", i); + continue; + } + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + dev_warn(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype, + priv->line[i]); + } + } + + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void pci1xxxx_serial_remove(struct pci_dev *dev) +{ + struct pci1xxxx_8250 *priv = pci_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) + serial8250_unregister_port(priv->line[i]); + } + + pci_free_irq_vectors(dev); + pci_iounmap(dev, priv->membase); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_pm_ops, pci1xxxx_suspend, pci1xxxx_resume); + +static const struct pci_device_id pci1xxxx_pci_tbl[] = { + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11010) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11101) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11400) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11414) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI12000) }, + {} +}; +MODULE_DEVICE_TABLE(pci, pci1xxxx_pci_tbl); + +static struct pci_driver pci1xxxx_pci_driver = { + .name = "pci1xxxx serial", + .probe = pci1xxxx_serial_probe, + .remove = pci1xxxx_serial_remove, + .driver = { + .pm = pm_sleep_ptr(&pci1xxxx_pm_ops), + }, + .id_table = pci1xxxx_pci_tbl, +}; +module_pci_driver(pci1xxxx_pci_driver); + +static_assert((ARRAY_SIZE(logical_to_physical_port_idx) == PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 + 1)); + +MODULE_IMPORT_NS(SERIAL_8250_PCI); +MODULE_DESCRIPTION("Microchip Technology Inc. PCIe to UART module"); +MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>"); +MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c new file mode 100644 index 000000000000..d234e9194feb --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250 PCI library. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "8250.h" +#include "8250_pcilib.h" + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, + u8 bar, unsigned int offset, int regshift) +{ + if (bar >= PCI_STD_NUM_BARS) + return -EINVAL; + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) + return -ENOMEM; + + port->port.iotype = UPIO_MEM; + port->port.iobase = 0; + port->port.mapbase = pci_resource_start(dev, bar) + offset; + port->port.membase = pcim_iomap_table(dev)[bar] + offset; + port->port.regshift = regshift; + } else { + port->port.iotype = UPIO_PORT; + port->port.iobase = pci_resource_start(dev, bar) + offset; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, SERIAL_8250_PCI); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.h b/drivers/tty/serial/8250/8250_pcilib.h new file mode 100644 index 000000000000..1aaf1b50ce9c --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 8250 PCI library header file. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ + +#include <linux/types.h> + +struct pci_dev; + +struct uart_8250_port; + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar, + unsigned int offset, int regshift); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index beba8f38b3dc..fa43df05342b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -313,6 +313,14 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO, }, + [PORT_MCHP16550A] = { + .name = "MCHP16550A", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, + .rxtrig_bytes = {2, 66, 130, 194}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -1050,11 +1058,12 @@ static void autoconfig_16550a(struct uart_8250_port *up) serial_out(up, UART_LCR, 0); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; + status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | + UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, 0); serial_out(up, UART_LCR, 0); - if (status1 == 7) + if (status1 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED)) up->port.type = PORT_16550A_FSL64; else DEBUG_AUTOCONF("Motorola 8xxx DUART "); @@ -1122,17 +1131,20 @@ static void autoconfig_16550a(struct uart_8250_port *up) */ serial_out(up, UART_LCR, 0); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; + status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status2 = serial_in(up, UART_IIR) >> 5; + status2 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, 0); DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); - if (status1 == 6 && status2 == 7) { + if (status1 == UART_IIR_FIFO_ENABLED_16550A && + status2 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED_16550A)) { up->port.type = PORT_16750; up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; return; @@ -1236,14 +1248,14 @@ static void autoconfig(struct uart_8250_port *up) * Mask out IER[7:4] bits for test as some UARTs (e.g. TL * 16C754B) allow only to modify them if an EFR bit is set. */ - scratch2 = serial_in(up, UART_IER) & 0x0f; - serial_out(up, UART_IER, 0x0F); + scratch2 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; + serial_out(up, UART_IER, UART_IER_ALL_INTR); #ifdef __i386__ outb(0, 0x080); #endif - scratch3 = serial_in(up, UART_IER) & 0x0f; + scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; serial_out(up, UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0F) { + if (scratch2 != 0 || scratch3 != UART_IER_ALL_INTR) { /* * We failed; there's nothing here */ @@ -1267,10 +1279,10 @@ static void autoconfig(struct uart_8250_port *up) * that conflicts with COM 1-4 --- we hope! */ if (!(port->flags & UPF_SKIP_TEST)) { - serial8250_out_MCR(up, UART_MCR_LOOP | 0x0A); - status1 = serial_in(up, UART_MSR) & 0xF0; + serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS); + status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS; serial8250_out_MCR(up, save_mcr); - if (status1 != 0x90) { + if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) { spin_unlock_irqrestore(&port->lock, flags); DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); @@ -1293,22 +1305,19 @@ static void autoconfig(struct uart_8250_port *up) serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - /* Assign this as it is to truncate any bits above 7. */ - scratch = serial_in(up, UART_IIR); - - switch (scratch >> 6) { - case 0: + switch (serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED) { + case UART_IIR_FIFO_ENABLED_8250: autoconfig_8250(up); break; - case 1: - port->type = PORT_UNKNOWN; - break; - case 2: + case UART_IIR_FIFO_ENABLED_16550: port->type = PORT_16550; break; - case 3: + case UART_IIR_FIFO_ENABLED_16550A: autoconfig_16550a(up); break; + default: + port->type = PORT_UNKNOWN; + break; } #ifdef CONFIG_SERIAL_8250_RSA @@ -1394,7 +1403,7 @@ static void autoconfig_irq(struct uart_8250_port *up) serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } - serial_out(up, UART_IER, 0x0f); /* enable all intrs */ + serial_out(up, UART_IER, UART_IER_ALL_INTR); serial_in(up, UART_LSR); serial_in(up, UART_RX); serial_in(up, UART_IIR); @@ -1511,8 +1520,6 @@ static inline void __stop_tx(struct uart_8250_port *p) u16 lsr = serial_lsr_in(p); u64 stop_delay = 0; - p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if (!(lsr & UART_LSR_THRE)) return; /* diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index b0f62345bc84..978dc196c29b 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -129,9 +129,13 @@ config SERIAL_8250_DMA This builds DMA support that can be used with 8250/16650 compatible UART controllers that support DMA signaling. +config SERIAL_8250_PCILIB + bool + config SERIAL_8250_PCI tristate "8250/16550 PCI device support" depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB default SERIAL_8250 help This builds standard PCI serial support. You may be able to @@ -291,6 +295,17 @@ config SERIAL_8250_HUB6 To compile this driver as a module, choose M here: the module will be called 8250_hub6. +config SERIAL_8250_PCI1XXXX + tristate "Microchip 8250 based serial port" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + default SERIAL_8250 + help + Select this option if you have a setup with Microchip PCIe + Switch with serial port enabled and wish to enable 8250 + serial driver for the serial interface. This driver support + will ensure to support baud rates upto 1.5Mpbs. + # # Misc. options/drivers. # @@ -370,6 +385,18 @@ config SERIAL_8250_FSL erratum for Freescale 16550 UARTs in the 8250 driver. It also enables support for ACPI enumeration. +config SERIAL_8250_DFL + tristate "DFL bus driver for Altera 16550 UART" + depends on SERIAL_8250 && FPGA_DFL + help + This option enables support for a Device Feature List (DFL) bus + driver for the Altera 16550 UART. One or more Altera 16550 UARTs + can be instantiated in a FPGA and then be discovered during + enumeration of the DFL bus. + + To compile this driver as a module, chose M here: the + module will be called 8250_dfl. + config SERIAL_8250_DW tristate "Support for Synopsys DesignWare 8250 quirks" depends on SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 1615bfdde2a0..4fc2fc1f41b6 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o 8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o 8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +8250_base-$(CONFIG_SERIAL_8250_PCILIB) += 8250_pcilib.o obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o @@ -26,8 +27,10 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o +obj-$(CONFIG_SERIAL_8250_DFL) += 8250_dfl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index ed0672d2d0ef..625358f44419 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -73,17 +73,17 @@ config SERIAL_AMBA_PL011_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_EARLYCON_ARM_SEMIHOST - bool "Early console using ARM semihosting" - depends on ARM64 || ARM +config SERIAL_EARLYCON_SEMIHOST + bool "Early console using Arm compatible semihosting" + depends on ARM64 || ARM || RISCV select SERIAL_CORE select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON help - Support for early debug console using ARM semihosting. This enables - the console before standard serial driver is probed. This is enabled - with "earlycon=smh" on the kernel command line. The console is - enabled when early_param is processed. + Support for early debug console using Arm compatible semihosting. + This enables the console before standard serial driver is probed. + This is enabled with "earlycon=smh" on the kernel command line. + The console is enabled when early_param is processed. config SERIAL_EARLYCON_RISCV_SBI bool "Early console using RISC-V SBI" @@ -1507,7 +1507,7 @@ config SERIAL_MILBEAUT_USIO_CONSOLE config SERIAL_LITEUART tristate "LiteUART serial port support" depends on HAS_IOMEM - depends on OF || COMPILE_TEST + depends on OF depends on LITEX || COMPILE_TEST select SERIAL_CORE help diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 238a9557b487..cd9afd9e3018 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o -obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o +obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o # These Sparc drivers have to appear before others such as 8250 diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-semihost.c index fcdec5f42376..e4692a8433f9 100644 --- a/drivers/tty/serial/earlycon-arm-semihost.c +++ b/drivers/tty/serial/earlycon-semihost.c @@ -11,30 +11,7 @@ #include <linux/console.h> #include <linux/init.h> #include <linux/serial_core.h> - -#ifdef CONFIG_THUMB2_KERNEL -#define SEMIHOST_SWI "0xab" -#else -#define SEMIHOST_SWI "0x123456" -#endif - -/* - * Semihosting-based debug console - */ -static void smh_putc(struct uart_port *port, unsigned char c) -{ -#ifdef CONFIG_ARM64 - asm volatile("mov x1, %0\n" - "mov x0, #3\n" - "hlt 0xf000\n" - : : "r" (&c) : "x0", "x1", "memory"); -#else - asm volatile("mov r1, %0\n" - "mov r0, #3\n" - "svc " SEMIHOST_SWI "\n" - : : "r" (&c) : "r0", "r1", "memory"); -#endif -} +#include <asm/semihost.h> static void smh_write(struct console *con, const char *s, unsigned n) { diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 4f6e9bf57169..a5fbb6ed38ae 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -120,7 +120,13 @@ static int __init parse_options(struct earlycon_device *device, char *options) } if (options) { + char *uartclk; + device->baud = simple_strtoul(options, NULL, 0); + uartclk = strchr(options, ','); + if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0) + pr_warn("[%s] unsupported earlycon uart clkrate option\n", + options); length = min(strcspn(options, " ") + 1, (size_t)(sizeof(device->options))); strscpy(device->options, options, length); @@ -139,7 +145,8 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) buf = NULL; spin_lock_init(&port->lock); - port->uartclk = BASE_BAUD * 16; + if (!port->uartclk) + port->uartclk = BASE_BAUD * 16; if (port->mapbase) port->membase = earlycon_map(port->mapbase, 64); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 5e69fb73f570..e945f41b93d4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -5,6 +5,8 @@ * Copyright 2012-2014 Freescale Semiconductor, Inc. */ +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/console.h> #include <linux/delay.h> @@ -181,7 +183,7 @@ #define UARTCTRL_SBK 0x00010000 #define UARTCTRL_MA1IE 0x00008000 #define UARTCTRL_MA2IE 0x00004000 -#define UARTCTRL_IDLECFG 0x00000100 +#define UARTCTRL_IDLECFG GENMASK(10, 8) #define UARTCTRL_LOOPS 0x00000080 #define UARTCTRL_DOZEEN 0x00000040 #define UARTCTRL_RSRC 0x00000020 @@ -199,6 +201,7 @@ #define UARTDATA_MASK 0x3ff #define UARTMODIR_IREN 0x00020000 +#define UARTMODIR_RTSWATER GENMASK(10, 8) #define UARTMODIR_TXCTSSRC 0x00000020 #define UARTMODIR_TXCTSC 0x00000010 #define UARTMODIR_RXRTSE 0x00000008 @@ -212,6 +215,7 @@ #define UARTFIFO_RXUF 0x00010000 #define UARTFIFO_TXFLUSH 0x00008000 #define UARTFIFO_RXFLUSH 0x00004000 +#define UARTFIFO_RXIDEN GENMASK(12, 10) #define UARTFIFO_TXOFE 0x00000200 #define UARTFIFO_RXUFE 0x00000100 #define UARTFIFO_TXFE 0x00000080 @@ -238,7 +242,7 @@ #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" -#define UART_NR 6 +#define UART_NR 8 /* IMX lpuart has four extra unused regs located at the beginning */ #define IMX_REG_OFF 0x10 @@ -248,6 +252,7 @@ enum lpuart_type { LS1021A_LPUART, LS1028A_LPUART, IMX7ULP_LPUART, + IMX8ULP_LPUART, IMX8QXP_LPUART, IMXRT1050_LPUART, }; @@ -260,6 +265,7 @@ struct lpuart_port { unsigned int txfifo_size; unsigned int rxfifo_size; + u8 rx_watermark; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; struct dma_chan *dma_tx_chan; @@ -286,38 +292,52 @@ struct lpuart_soc_data { enum lpuart_type devtype; char iotype; u8 reg_off; + u8 rx_watermark; }; static const struct lpuart_soc_data vf_data = { .devtype = VF610_LPUART, .iotype = UPIO_MEM, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1021a_data = { .devtype = LS1021A_LPUART, .iotype = UPIO_MEM32BE, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1028a_data = { .devtype = LS1028A_LPUART, .iotype = UPIO_MEM32, + .rx_watermark = 1, }; static struct lpuart_soc_data imx7ulp_data = { .devtype = IMX7ULP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, +}; + +static struct lpuart_soc_data imx8ulp_data = { + .devtype = IMX8ULP_LPUART, + .iotype = UPIO_MEM32, + .reg_off = IMX_REG_OFF, + .rx_watermark = 3, }; static struct lpuart_soc_data imx8qxp_data = { .devtype = IMX8QXP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 31, }; static struct lpuart_soc_data imxrt1050_data = { .devtype = IMXRT1050_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static const struct of_device_id lpuart_dt_ids[] = { @@ -325,6 +345,7 @@ static const struct of_device_id lpuart_dt_ids[] = { { .compatible = "fsl,ls1021a-lpuart", .data = &ls1021a_data, }, { .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, }, { .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, }, + { .compatible = "fsl,imx8ulp-lpuart", .data = &imx8ulp_data, }, { .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, }, { .compatible = "fsl,imxrt1050-lpuart", .data = &imxrt1050_data}, { /* sentinel */ } @@ -345,6 +366,11 @@ static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport) return sport->devtype == IMX7ULP_LPUART; } +static inline bool is_imx8ulp_lpuart(struct lpuart_port *sport) +{ + return sport->devtype == IMX8ULP_LPUART; +} + static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) { return sport->devtype == IMX8QXP_LPUART; @@ -1387,9 +1413,9 @@ static int lpuart32_config_rs485(struct uart_port *port, struct ktermios *termio * Note: UART is assumed to be active high. */ if (rs485->flags & SER_RS485_RTS_ON_SEND) - modem &= ~UARTMODEM_TXRTSPOL; - else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) modem |= UARTMODEM_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + modem &= ~UARTMODEM_TXRTSPOL; } lpuart32_write(&sport->port, modem, UARTMODIR); @@ -1462,12 +1488,32 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state) static void lpuart32_break_ctl(struct uart_port *port, int break_state) { - unsigned long temp; + unsigned long temp, modem; + struct tty_struct *tty; + unsigned int cflag = 0; + + tty = tty_port_tty_get(&port->state->port); + if (tty) { + cflag = tty->termios.c_cflag; + tty_kref_put(tty); + } temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK; + modem = lpuart32_read(port, UARTMODIR); - if (break_state != 0) + if (break_state != 0) { temp |= UARTCTRL_SBK; + /* + * LPUART CTS has higher priority than SBK, need to disable CTS before + * asserting SBK to avoid any interference if flow control is enabled. + */ + if (cflag & CRTSCTS && modem & UARTMODIR_TXCTSE) + lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR); + } else { + /* Re-enable the CTS when break off. */ + if (cflag & CRTSCTS && !(modem & UARTMODIR_TXCTSE)) + lpuart32_write(port, modem | UARTMODIR_TXCTSE, UARTMODIR); + } lpuart32_write(port, temp, UARTCTRL); } @@ -1497,8 +1543,10 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); } + if (uart_console(&sport->port)) + sport->rx_watermark = 1; writeb(0, sport->port.membase + UARTTWFIFO); - writeb(1, sport->port.membase + UARTRWFIFO); + writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO); /* Restore cr2 */ writeb(cr2_saved, sport->port.membase + UARTCR2); @@ -1523,19 +1571,30 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) ctrl = lpuart32_read(&sport->port, UARTCTRL); ctrl_saved = ctrl; ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | - UARTCTRL_RIE | UARTCTRL_RE); + UARTCTRL_RIE | UARTCTRL_RE | UARTCTRL_ILIE); lpuart32_write(&sport->port, ctrl, UARTCTRL); /* enable FIFO mode */ val = lpuart32_read(&sport->port, UARTFIFO); val |= UARTFIFO_TXFE | UARTFIFO_RXFE; val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + val |= FIELD_PREP(UARTFIFO_RXIDEN, 0x3); lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ - val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + if (uart_console(&sport->port)) + sport->rx_watermark = 1; + val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) | + (0x0 << UARTWATER_TXWATER_OFF); lpuart32_write(&sport->port, val, UARTWATER); + /* set RTS watermark */ + if (!uart_console(&sport->port)) { + val = lpuart32_read(&sport->port, UARTMODIR); + val |= FIELD_PREP(UARTMODIR_RTSWATER, sport->rxfifo_size >> 1); + lpuart32_write(&sport->port, val, UARTMODIR); + } + /* Restore cr2 */ lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); } @@ -1547,7 +1606,8 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport) lpuart32_setup_watermark(sport); temp = lpuart32_read(&sport->port, UARTCTRL); - temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE; + temp |= UARTCTRL_RE | UARTCTRL_TE; + temp |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7); lpuart32_write(&sport->port, temp, UARTCTRL); } @@ -1679,19 +1739,23 @@ static int lpuart_startup(struct uart_port *port) return 0; } +static void lpuart32_hw_disable(struct lpuart_port *sport) +{ + unsigned long temp; + + temp = lpuart32_read(&sport->port, UARTCTRL); + temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE | + UARTCTRL_TIE | UARTCTRL_TE); + lpuart32_write(&sport->port, temp, UARTCTRL); +} + static void lpuart32_configure(struct lpuart_port *sport) { unsigned long temp; - if (sport->lpuart_dma_rx_use) { - /* RXWATER must be 0 */ - temp = lpuart32_read(&sport->port, UARTWATER); - temp &= ~(UARTWATER_WATER_MASK << UARTWATER_RXWATER_OFF); - lpuart32_write(&sport->port, temp, UARTWATER); - } temp = lpuart32_read(&sport->port, UARTCTRL); if (!sport->lpuart_dma_rx_use) - temp |= UARTCTRL_RIE; + temp |= UARTCTRL_RIE | UARTCTRL_ILIE; if (!sport->lpuart_dma_tx_use) temp |= UARTCTRL_TIE; lpuart32_write(&sport->port, temp, UARTCTRL); @@ -1703,11 +1767,12 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) spin_lock_irqsave(&sport->port.lock, flags); - lpuart32_setup_watermark_enable(sport); + lpuart32_hw_disable(sport); lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); + lpuart32_setup_watermark_enable(sport); lpuart32_configure(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1796,10 +1861,19 @@ static void lpuart32_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); - /* disable Rx/Tx and interrupts */ + /* clear status */ + temp = lpuart32_read(&sport->port, UARTSTAT); + lpuart32_write(&sport->port, temp, UARTSTAT); + + /* disable Rx/Tx DMA */ + temp = lpuart32_read(port, UARTBAUD); + temp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + lpuart32_write(port, temp, UARTBAUD); + + /* disable Rx/Tx and interrupts and break condition */ temp = lpuart32_read(port, UARTCTRL); - temp &= ~(UARTCTRL_TE | UARTCTRL_RE | - UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_ILIE | + UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK); lpuart32_write(port, temp, UARTCTRL); spin_unlock_irqrestore(&port->lock, flags); @@ -2631,7 +2705,7 @@ static int lpuart_global_reset(struct lpuart_port *sport) return ret; } - if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { + if (is_imx7ulp_lpuart(sport) || is_imx8ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { /* * If the transmitter is used by earlycon, wait for transmit engine to * complete and then reset. @@ -2688,6 +2762,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->devtype = sdata->devtype; + sport->rx_watermark = sdata->rx_watermark; ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 757825edb0cd..523f296d5747 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -210,12 +210,8 @@ struct imx_port { struct mctrl_gpios *gpios; - /* shadow registers */ - unsigned int ucr1; - unsigned int ucr2; - unsigned int ucr3; - unsigned int ucr4; - unsigned int ufcr; + /* counter to stop 0xff flood */ + int idle_counter; /* DMA fields */ unsigned int dma_is_enabled:1; @@ -273,59 +269,14 @@ static const struct of_device_id imx_uart_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_uart_dt_ids); -static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) -{ - switch (offset) { - case UCR1: - sport->ucr1 = val; - break; - case UCR2: - sport->ucr2 = val; - break; - case UCR3: - sport->ucr3 = val; - break; - case UCR4: - sport->ucr4 = val; - break; - case UFCR: - sport->ufcr = val; - break; - default: - break; - } +static inline void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) +{ writel(val, sport->port.membase + offset); } -static u32 imx_uart_readl(struct imx_port *sport, u32 offset) +static inline u32 imx_uart_readl(struct imx_port *sport, u32 offset) { - switch (offset) { - case UCR1: - return sport->ucr1; - break; - case UCR2: - /* - * UCR2_SRST is the only bit in the cached registers that might - * differ from the value that was last written. As it only - * automatically becomes one after being cleared, reread - * conditionally. - */ - if (!(sport->ucr2 & UCR2_SRST)) - sport->ucr2 = readl(sport->port.membase + offset); - return sport->ucr2; - break; - case UCR3: - return sport->ucr3; - break; - case UCR4: - return sport->ucr4; - break; - case UFCR: - return sport->ufcr; - break; - default: - return readl(sport->port.membase + offset); - } + return readl(sport->port.membase + offset); } static inline unsigned imx_uart_uts_reg(struct imx_port *sport) @@ -398,6 +349,41 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) } /* called with port.lock taken and irqs off */ +static void imx_uart_soft_reset(struct imx_port *sport) +{ + int i = 10; + u32 ucr2, ubir, ubmr, uts; + + /* + * According to the Reference Manual description of the UART SRST bit: + * + * "Reset the transmit and receive state machines, + * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD + * and UTS[6-3]". + * + * We don't need to restore the old values from USR1, USR2, URXD and + * UTXD. UBRC is read only, so only save/restore the other three + * registers. + */ + ubir = imx_uart_readl(sport, UBIR); + ubmr = imx_uart_readl(sport, UBMR); + uts = imx_uart_readl(sport, IMX21_UTS); + + ucr2 = imx_uart_readl(sport, UCR2); + imx_uart_writel(sport, ucr2 & ~UCR2_SRST, UCR2); + + while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) + udelay(1); + + /* Restore the registers */ + imx_uart_writel(sport, ubir, UBIR); + imx_uart_writel(sport, ubmr, UBMR); + imx_uart_writel(sport, uts, IMX21_UTS); + + sport->idle_counter = 0; +} + +/* called with port.lock taken and irqs off */ static void imx_uart_start_rx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -476,7 +462,8 @@ static void imx_uart_stop_tx(struct uart_port *port) imx_uart_rts_inactive(sport, &ucr2); imx_uart_writel(sport, ucr2, UCR2); - imx_uart_start_rx(port); + if (!port->rs485_rx_during_tx_gpio) + imx_uart_start_rx(port); sport->tx_state = OFF; } @@ -705,7 +692,8 @@ static void imx_uart_start_tx(struct uart_port *port) imx_uart_rts_inactive(sport, &ucr2); imx_uart_writel(sport, ucr2, UCR2); - if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX) && + !port->rs485_rx_during_tx_gpio) imx_uart_stop_rx(port); sport->tx_state = WAIT_AFTER_RTS; @@ -771,7 +759,7 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id) imx_uart_writel(sport, USR1_RTSD, USR1); usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; - uart_handle_cts_change(&sport->port, !!usr1); + uart_handle_cts_change(&sport->port, usr1); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); return IRQ_HANDLED; @@ -801,33 +789,73 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) return IRQ_HANDLED; } +/* Check if hardware Rx flood is in progress, and issue soft reset to stop it. + * This is to be called from Rx ISRs only when some bytes were actually + * received. + * + * A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600 + * 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% of + * cases this starts a flood of "receiving" of 0xff characters by the iMX6 UART + * that is terminated by any activity on RxD line, or could be stopped by + * issuing soft reset to the UART (just stop/start of RX does not help). Note + * that what we do here is sending isolated start bit about 2.4 times shorter + * than it is to be on UART configured baud rate. + */ +static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) +{ + /* To detect hardware 0xff flood we monitor RxD line between RX + * interrupts to isolate "receiving" of char(s) with no activity + * on RxD line, that'd never happen on actual data transfers. + * + * We use USR2_WAKE bit to check for activity on RxD line, but we have a + * race here if we clear USR2_WAKE when receiving of a char is in + * progress, so we might get RX interrupt later with USR2_WAKE bit + * cleared. Note though that as we don't try to clear USR2_WAKE when we + * detected no activity, this race may hide actual activity only once. + * + * Yet another case where receive interrupt may occur without RxD + * activity is expiration of aging timer, so we consider this as well. + * + * We use 'idle_counter' to ensure that we got at least so many RX + * interrupts without any detected activity on RxD line. 2 cases + * described plus 1 to be on the safe side gives us a margin of 3, + * below. In practice I was not able to produce a false positive to + * induce soft reset at regular data transfers even using 1 as the + * margin, so 3 is actually very strong. + * + * We count interrupts, not chars in 'idle-counter' for simplicity. + */ + + if (usr2 & USR2_WAKE) { + imx_uart_writel(sport, USR2_WAKE, USR2); + sport->idle_counter = 0; + } else if (++sport->idle_counter > 3) { + dev_warn(sport->port.dev, "RX flood detected: soft reset."); + imx_uart_soft_reset(sport); /* also clears 'sport->idle_counter' */ + } +} + static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; + u32 usr2, rx; - while (imx_uart_readl(sport, USR2) & USR2_RDR) { - u32 usr2; + /* If we received something, check for 0xff flood */ + usr2 = imx_uart_readl(sport, USR2); + if (usr2 & USR2_RDR) + imx_uart_check_flood(sport, usr2); - flg = TTY_NORMAL; + while ((rx = imx_uart_readl(sport, URXD0)) & URXD_CHARRDY) { + unsigned int flg = TTY_NORMAL; sport->port.icount.rx++; - rx = imx_uart_readl(sport, URXD0); - - usr2 = imx_uart_readl(sport, USR2); - if (usr2 & USR2_BRCD) { - imx_uart_writel(sport, USR2_BRCD, USR2); - if (uart_handle_break(&sport->port)) - continue; - } - - if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) - continue; - if (unlikely(rx & URXD_ERR)) { - if (rx & URXD_BRK) + if (rx & URXD_BRK) { sport->port.icount.brk++; + if (uart_handle_break(&sport->port)) + continue; + } else if (rx & URXD_PRERR) sport->port.icount.parity++; else if (rx & URXD_FRMERR) @@ -835,11 +863,8 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) if (rx & URXD_OVRRUN) sport->port.icount.overrun++; - if (rx & sport->port.ignore_status_mask) { - if (++ignored > 100) - goto out; + if (rx & sport->port.ignore_status_mask) continue; - } rx &= (sport->port.read_status_mask | 0xFF); @@ -853,16 +878,17 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) flg = TTY_OVERRUN; sport->port.sysrq = 0; + } else if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) { + continue; } if (sport->port.ignore_status_mask & URXD_DUMMY_READ) - goto out; + continue; if (tty_insert_flip_char(port, rx, flg) == 0) sport->port.icount.buf_overrun++; } -out: tty_flip_buffer_push(port); return IRQ_HANDLED; @@ -1147,55 +1173,62 @@ static void imx_uart_dma_rx_callback(void *data) status = dmaengine_tx_status(chan, sport->rx_cookie, &state); if (status == DMA_ERROR) { + spin_lock(&sport->port.lock); imx_uart_clear_rx_errors(sport); + spin_unlock(&sport->port.lock); return; } - if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { + /* + * The state-residue variable represents the empty space + * relative to the entire buffer. Taking this in consideration + * the head is always calculated base on the buffer total + * length - DMA transaction residue. The UART script from the + * SDMA firmware will jump to the next buffer descriptor, + * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4). + * Taking this in consideration the tail is always at the + * beginning of the buffer descriptor that contains the head. + */ - /* - * The state-residue variable represents the empty space - * relative to the entire buffer. Taking this in consideration - * the head is always calculated base on the buffer total - * length - DMA transaction residue. The UART script from the - * SDMA firmware will jump to the next buffer descriptor, - * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4). - * Taking this in consideration the tail is always at the - * beginning of the buffer descriptor that contains the head. - */ + /* Calculate the head */ + rx_ring->head = sg_dma_len(sgl) - state.residue; + + /* Calculate the tail. */ + bd_size = sg_dma_len(sgl) / sport->rx_periods; + rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; - /* Calculate the head */ - rx_ring->head = sg_dma_len(sgl) - state.residue; + if (rx_ring->head <= sg_dma_len(sgl) && + rx_ring->head > rx_ring->tail) { - /* Calculate the tail. */ - bd_size = sg_dma_len(sgl) / sport->rx_periods; - rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; + /* Move data from tail to head */ + r_bytes = rx_ring->head - rx_ring->tail; - if (rx_ring->head <= sg_dma_len(sgl) && - rx_ring->head > rx_ring->tail) { + /* If we received something, check for 0xff flood */ + spin_lock(&sport->port.lock); + imx_uart_check_flood(sport, imx_uart_readl(sport, USR2)); + spin_unlock(&sport->port.lock); - /* Move data from tail to head */ - r_bytes = rx_ring->head - rx_ring->tail; + if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { /* CPU claims ownership of RX DMA buffer */ dma_sync_sg_for_cpu(sport->port.dev, sgl, 1, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); w_bytes = tty_insert_flip_string(port, - sport->rx_buf + rx_ring->tail, r_bytes); + sport->rx_buf + rx_ring->tail, r_bytes); /* UART retrieves ownership of RX DMA buffer */ dma_sync_sg_for_device(sport->port.dev, sgl, 1, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (w_bytes != r_bytes) sport->port.icount.buf_overrun++; sport->port.icount.rx += w_bytes; - } else { - WARN_ON(rx_ring->head > sg_dma_len(sgl)); - WARN_ON(rx_ring->head <= rx_ring->tail); } + } else { + WARN_ON(rx_ring->head > sg_dma_len(sgl)); + WARN_ON(rx_ring->head <= rx_ring->tail); } if (w_bytes) { @@ -1271,6 +1304,8 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport) imx_uart_writel(sport, USR2_ORE, USR2); } + sport->idle_counter = 0; + } #define TXTL_DEFAULT 2 /* reset default */ @@ -1398,7 +1433,7 @@ static void imx_uart_disable_dma(struct imx_port *sport) static int imx_uart_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - int retval, i; + int retval; unsigned long flags; int dma_is_inited = 0; u32 ucr1, ucr2, ucr3, ucr4, uts; @@ -1430,15 +1465,9 @@ static int imx_uart_startup(struct uart_port *port) dma_is_inited = 1; spin_lock_irqsave(&sport->port.lock, flags); - /* Reset fifo's and state machines */ - i = 100; - ucr2 = imx_uart_readl(sport, UCR2); - ucr2 &= ~UCR2_SRST; - imx_uart_writel(sport, ucr2, UCR2); - - while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) - udelay(1); + /* Reset fifo's and state machines */ + imx_uart_soft_reset(sport); /* * Finally, clear and enable interrupts @@ -1564,7 +1593,8 @@ static void imx_uart_shutdown(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); ucr1 = imx_uart_readl(sport, UCR1); - ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | UCR1_ATDMAEN); + ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | + UCR1_ATDMAEN | UCR1_SNDBRK); /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */ if (port->rs485.flags & SER_RS485_ENABLED && port->rs485.flags & SER_RS485_RTS_ON_SEND && @@ -1593,8 +1623,6 @@ static void imx_uart_flush_buffer(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; struct scatterlist *sgl = &sport->tx_sgl[0]; - u32 ucr2; - int i = 100, ubir, ubmr, uts; if (!sport->dma_chan_tx) return; @@ -1612,32 +1640,8 @@ static void imx_uart_flush_buffer(struct uart_port *port) sport->dma_is_txing = 0; } - /* - * According to the Reference Manual description of the UART SRST bit: - * - * "Reset the transmit and receive state machines, - * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD - * and UTS[6-3]". - * - * We don't need to restore the old values from USR1, USR2, URXD and - * UTXD. UBRC is read only, so only save/restore the other three - * registers. - */ - ubir = imx_uart_readl(sport, UBIR); - ubmr = imx_uart_readl(sport, UBMR); - uts = imx_uart_readl(sport, IMX21_UTS); - - ucr2 = imx_uart_readl(sport, UCR2); - ucr2 &= ~UCR2_SRST; - imx_uart_writel(sport, ucr2, UCR2); - - while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) - udelay(1); + imx_uart_soft_reset(sport); - /* Restore the registers */ - imx_uart_writel(sport, ubir, UBIR); - imx_uart_writel(sport, ubmr, UBMR); - imx_uart_writel(sport, uts, IMX21_UTS); } static void @@ -1955,6 +1959,10 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio rs485conf->flags & SER_RS485_RX_DURING_TX) imx_uart_start_rx(port); + if (port->rs485_rx_during_tx_gpio) + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); + return 0; } @@ -2340,13 +2348,6 @@ static int imx_uart_probe(struct platform_device *pdev) return ret; } - /* initialize shadow register values */ - sport->ucr1 = readl(sport->port.membase + UCR1); - sport->ucr2 = readl(sport->port.membase + UCR2); - sport->ucr3 = readl(sport->port.membase + UCR3); - sport->ucr4 = readl(sport->port.membase + UCR4); - sport->ufcr = readl(sport->port.membase + UFCR); - ret = uart_get_rs485_mode(&sport->port); if (ret) { clk_disable_unprepare(sport->clk_ipg); @@ -2374,6 +2375,11 @@ static int imx_uart_probe(struct platform_device *pdev) ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_RTSDEN); imx_uart_writel(sport, ucr1, UCR1); + /* Disable Ageing Timer interrupt */ + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~UCR2_ATEN; + imx_uart_writel(sport, ucr2, UCR2); + /* * In case RS485 is enabled without GPIO RTS control, the UART IP * is used to control CTS signal. Keep both the UART and Receiver diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 062812fe1b09..80de3a42b67b 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -5,7 +5,9 @@ * Copyright (C) 2019-2020 Antmicro <www.antmicro.com> */ +#include <linux/bits.h> #include <linux/console.h> +#include <linux/interrupt.h> #include <linux/litex.h> #include <linux/module.h> #include <linux/of.h> @@ -38,13 +40,13 @@ #define OFF_EV_ENABLE 0x14 /* events */ -#define EV_TX 0x1 -#define EV_RX 0x2 +#define EV_TX BIT(0) +#define EV_RX BIT(1) struct liteuart_port { struct uart_port port; struct timer_list timer; - u32 id; + u8 irq_reg; }; #define to_liteuart_port(port) container_of(port, struct liteuart_port, port) @@ -57,7 +59,7 @@ static struct console liteuart_console; static struct uart_driver liteuart_driver = { .owner = THIS_MODULE, - .driver_name = "liteuart", + .driver_name = KBUILD_MODNAME, .dev_name = "ttyLXU", .major = 0, .minor = 0, @@ -67,38 +69,95 @@ static struct uart_driver liteuart_driver = { #endif }; -static void liteuart_timer(struct timer_list *t) +static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask) +{ + struct liteuart_port *uart = to_liteuart_port(port); + + if (set) + uart->irq_reg |= mask; + else + uart->irq_reg &= ~mask; + + if (port->irq) + litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg); +} + +static void liteuart_stop_tx(struct uart_port *port) +{ + liteuart_update_irq_reg(port, false, EV_TX); +} + +static void liteuart_start_tx(struct uart_port *port) +{ + liteuart_update_irq_reg(port, true, EV_TX); +} + +static void liteuart_stop_rx(struct uart_port *port) +{ + struct liteuart_port *uart = to_liteuart_port(port); + + /* just delete timer */ + del_timer(&uart->timer); +} + +static void liteuart_rx_chars(struct uart_port *port) { - struct liteuart_port *uart = from_timer(uart, t, timer); - struct uart_port *port = &uart->port; unsigned char __iomem *membase = port->membase; - unsigned int flg = TTY_NORMAL; - int ch; - unsigned long status; + u8 ch; - while ((status = !litex_read8(membase + OFF_RXEMPTY)) == 1) { + while (!litex_read8(membase + OFF_RXEMPTY)) { ch = litex_read8(membase + OFF_RXTX); port->icount.rx++; /* necessary for RXEMPTY to refresh its value */ - litex_write8(membase + OFF_EV_PENDING, EV_TX | EV_RX); + litex_write8(membase + OFF_EV_PENDING, EV_RX); /* no overflow bits in status */ if (!(uart_handle_sysrq_char(port, ch))) - uart_insert_char(port, status, 0, ch, flg); - - tty_flip_buffer_push(&port->state->port); + uart_insert_char(port, 1, 0, ch, TTY_NORMAL); } - mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + tty_flip_buffer_push(&port->state->port); } -static void liteuart_putchar(struct uart_port *port, unsigned char ch) +static void liteuart_tx_chars(struct uart_port *port) { - while (litex_read8(port->membase + OFF_TXFULL)) - cpu_relax(); + u8 ch; - litex_write8(port->membase + OFF_RXTX, ch); + uart_port_tx(port, ch, + !litex_read8(port->membase + OFF_TXFULL), + litex_write8(port->membase + OFF_RXTX, ch)); +} + +static irqreturn_t liteuart_interrupt(int irq, void *data) +{ + struct liteuart_port *uart = data; + struct uart_port *port = &uart->port; + unsigned long flags; + u8 isr; + + /* + * if polling, the context would be "in_serving_softirq", so use + * irq[save|restore] spin_lock variants to cover all possibilities + */ + spin_lock_irqsave(&port->lock, flags); + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; + if (isr & EV_RX) + liteuart_rx_chars(port); + if (isr & EV_TX) + liteuart_tx_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_RETVAL(isr); +} + +static void liteuart_timer(struct timer_list *t) +{ + struct liteuart_port *uart = from_timer(uart, t, timer); + struct uart_port *port = &uart->port; + + liteuart_interrupt(0, port); + mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); } static unsigned int liteuart_tx_empty(struct uart_port *port) @@ -120,60 +179,49 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port) return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void liteuart_stop_tx(struct uart_port *port) -{ -} - -static void liteuart_start_tx(struct uart_port *port) +static int liteuart_startup(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned char ch; - - if (unlikely(port->x_char)) { - litex_write8(port->membase + OFF_RXTX, port->x_char); - port->icount.tx++; - port->x_char = 0; - } else if (!uart_circ_empty(xmit)) { - while (xmit->head != xmit->tail) { - ch = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - liteuart_putchar(port, ch); + struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; + int ret; + + if (port->irq) { + ret = request_irq(port->irq, liteuart_interrupt, 0, + KBUILD_MODNAME, uart); + if (ret) { + dev_warn(port->dev, + "line %d irq %d failed: switch to polling\n", + port->line, port->irq); + port->irq = 0; } } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static void liteuart_stop_rx(struct uart_port *port) -{ - struct liteuart_port *uart = to_liteuart_port(port); + spin_lock_irqsave(&port->lock, flags); + /* only enabling rx irqs during startup */ + liteuart_update_irq_reg(port, true, EV_RX); + spin_unlock_irqrestore(&port->lock, flags); - /* just delete timer */ - del_timer(&uart->timer); -} + if (!port->irq) { + timer_setup(&uart->timer, liteuart_timer, 0); + mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + } -static void liteuart_break_ctl(struct uart_port *port, int break_state) -{ - /* LiteUART doesn't support sending break signal */ + return 0; } -static int liteuart_startup(struct uart_port *port) +static void liteuart_shutdown(struct uart_port *port) { struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; - /* disable events */ - litex_write8(port->membase + OFF_EV_ENABLE, 0); - - /* prepare timer for polling */ - timer_setup(&uart->timer, liteuart_timer, 0); - mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); - - return 0; -} + spin_lock_irqsave(&port->lock, flags); + liteuart_update_irq_reg(port, false, EV_RX | EV_TX); + spin_unlock_irqrestore(&port->lock, flags); -static void liteuart_shutdown(struct uart_port *port) -{ + if (port->irq) + free_irq(port->irq, port); + else + del_timer_sync(&uart->timer); } static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, @@ -196,15 +244,6 @@ static const char *liteuart_type(struct uart_port *port) return "liteuart"; } -static void liteuart_release_port(struct uart_port *port) -{ -} - -static int liteuart_request_port(struct uart_port *port) -{ - return 0; -} - static void liteuart_config_port(struct uart_port *port, int flags) { /* @@ -231,13 +270,10 @@ static const struct uart_ops liteuart_ops = { .stop_tx = liteuart_stop_tx, .start_tx = liteuart_start_tx, .stop_rx = liteuart_stop_rx, - .break_ctl = liteuart_break_ctl, .startup = liteuart_startup, .shutdown = liteuart_shutdown, .set_termios = liteuart_set_termios, .type = liteuart_type, - .release_port = liteuart_release_port, - .request_port = liteuart_request_port, .config_port = liteuart_config_port, .verify_port = liteuart_verify_port, }; @@ -249,6 +285,23 @@ static int liteuart_probe(struct platform_device *pdev) struct xa_limit limit; int dev_id, ret; + uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + port = &uart->port; + + /* get membase */ + port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + ret = platform_get_irq_optional(pdev, 0); + if (ret < 0 && ret != -ENXIO) + return ret; + if (ret > 0) + port->irq = ret; + /* look for aliases; auto-enumerate for free index if not found */ dev_id = of_alias_get_id(pdev->dev.of_node, "serial"); if (dev_id < 0) @@ -256,32 +309,16 @@ static int liteuart_probe(struct platform_device *pdev) else limit = XA_LIMIT(dev_id, dev_id); - uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL); - if (!uart) - return -ENOMEM; - ret = xa_alloc(&liteuart_array, &dev_id, uart, limit, GFP_KERNEL); if (ret) return ret; - uart->id = dev_id; - port = &uart->port; - - /* get membase */ - port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(port->membase)) { - ret = PTR_ERR(port->membase); - goto err_erase_id; - } - /* values not from device tree */ port->dev = &pdev->dev; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &liteuart_ops; - port->regshift = 2; port->fifosize = 16; - port->iobase = 1; port->type = PORT_UNKNOWN; port->line = dev_id; spin_lock_init(&port->lock); @@ -295,7 +332,7 @@ static int liteuart_probe(struct platform_device *pdev) return 0; err_erase_id: - xa_erase(&liteuart_array, uart->id); + xa_erase(&liteuart_array, dev_id); return ret; } @@ -303,10 +340,10 @@ err_erase_id: static int liteuart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); - struct liteuart_port *uart = to_liteuart_port(port); + unsigned int line = port->line; uart_remove_one_port(&liteuart_driver, port); - xa_erase(&liteuart_array, uart->id); + xa_erase(&liteuart_array, line); return 0; } @@ -321,13 +358,21 @@ static struct platform_driver liteuart_platform_driver = { .probe = liteuart_probe, .remove = liteuart_remove, .driver = { - .name = "liteuart", + .name = KBUILD_MODNAME, .of_match_table = liteuart_of_match, }, }; #ifdef CONFIG_SERIAL_LITEUART_CONSOLE +static void liteuart_putchar(struct uart_port *port, unsigned char ch) +{ + while (litex_read8(port->membase + OFF_TXFULL)) + cpu_relax(); + + litex_write8(port->membase + OFF_RXTX, ch); +} + static void liteuart_console_write(struct console *co, const char *s, unsigned int count) { @@ -367,7 +412,7 @@ static int liteuart_console_setup(struct console *co, char *options) } static struct console liteuart_console = { - .name = "liteuart", + .name = KBUILD_MODNAME, .write = liteuart_console_write, .device = uart_console_device, .setup = liteuart_console_setup, @@ -415,12 +460,10 @@ static int __init liteuart_init(void) return res; res = platform_driver_register(&liteuart_platform_driver); - if (res) { + if (res) uart_unregister_driver(&liteuart_driver); - return res; - } - return 0; + return res; } static void __exit liteuart_exit(void) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index bb74f23251fe..86dcbff8faa3 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -247,7 +247,7 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) cts = (rx & MAX3100_CTS) > 0; if (s->cts != cts) { s->cts = cts; - uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0); + uart_handle_cts_change(&s->port, cts); } return ret; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 4eb24e3407f8..e9cacfe7e032 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -819,8 +819,7 @@ static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno) if (ists & MAX310X_IRQ_CTS_BIT) { lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); - uart_handle_cts_change(port, - !!(lsr & MAX310X_LSR_CTS_BIT)); + uart_handle_cts_change(port, lsr & MAX310X_LSR_CTS_BIT); } if (rxlen) max310x_handle_rx(port, rxlen); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 843798e63084..90953e679e38 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1120,6 +1120,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud, static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned long *saved_flags) + __must_hold(&port->lock) { unsigned int rxstale, watermark, mask; struct msm_port *msm_port = to_msm_port(port); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 9576ba8bbc40..cc83b772b7ca 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1775,7 +1775,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv) char name[32]; snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); - debugfs_remove(debugfs_lookup(name, NULL)); + debugfs_lookup_and_remove(name, NULL); uart_remove_one_port(&pch_uart_driver, &priv->port); free_page((unsigned long)priv->rxbuf.buf); } diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index ba3435263c1f..196a4e678451 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -889,6 +889,8 @@ static int pic32_uart_probe(struct platform_device *pdev) sport->irq_rx = irq_of_parse_and_map(np, 1); sport->irq_tx = irq_of_parse_and_map(np, 2); sport->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(sport->clk)) + return PTR_ERR(sport->clk); sport->dev = &pdev->dev; /* Hardware flow control: gpios diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 57f04f8bf504..d69592e5e2ec 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -16,7 +16,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/pm_wakeirq.h> -#include <linux/qcom-geni-se.h> +#include <linux/soc/qcom/geni-se.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/slab.h> @@ -39,76 +39,70 @@ #define SE_UART_MANUAL_RFR 0x2ac /* SE_UART_TRANS_CFG */ -#define UART_TX_PAR_EN BIT(0) -#define UART_CTS_MASK BIT(1) - -/* SE_UART_TX_WORD_LEN */ -#define TX_WORD_LEN_MSK GENMASK(9, 0) +#define UART_TX_PAR_EN BIT(0) +#define UART_CTS_MASK BIT(1) /* SE_UART_TX_STOP_BIT_LEN */ -#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0) -#define TX_STOP_BIT_LEN_1 0 -#define TX_STOP_BIT_LEN_1_5 1 -#define TX_STOP_BIT_LEN_2 2 - -/* SE_UART_TX_TRANS_LEN */ -#define TX_TRANS_LEN_MSK GENMASK(23, 0) +#define TX_STOP_BIT_LEN_1 0 +#define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_INS_STATUS_BIT BIT(2) -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(3) /* SE_UART_RX_WORD_LEN */ -#define RX_WORD_LEN_MASK GENMASK(9, 0) +#define RX_WORD_LEN_MASK GENMASK(9, 0) /* SE_UART_RX_STALE_CNT */ -#define RX_STALE_CNT GENMASK(23, 0) +#define RX_STALE_CNT GENMASK(23, 0) /* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */ -#define PAR_CALC_EN BIT(0) -#define PAR_MODE_MSK GENMASK(2, 1) -#define PAR_MODE_SHFT 1 -#define PAR_EVEN 0x00 -#define PAR_ODD 0x01 -#define PAR_SPACE 0x10 -#define PAR_MARK 0x11 +#define PAR_CALC_EN BIT(0) +#define PAR_EVEN 0x00 +#define PAR_ODD 0x01 +#define PAR_SPACE 0x10 /* SE_UART_MANUAL_RFR register fields */ -#define UART_MANUAL_RFR_EN BIT(31) -#define UART_RFR_NOT_READY BIT(1) -#define UART_RFR_READY BIT(0) +#define UART_MANUAL_RFR_EN BIT(31) +#define UART_RFR_NOT_READY BIT(1) +#define UART_RFR_READY BIT(0) /* UART M_CMD OP codes */ -#define UART_START_TX 0x1 -#define UART_START_BREAK 0x4 -#define UART_STOP_BREAK 0x5 +#define UART_START_TX 0x1 /* UART S_CMD OP codes */ -#define UART_START_READ 0x1 -#define UART_PARAM 0x1 - -#define UART_OVERSAMPLING 32 -#define STALE_TIMEOUT 16 -#define DEFAULT_BITS_PER_CHAR 10 -#define GENI_UART_CONS_PORTS 1 -#define GENI_UART_PORTS 3 -#define DEF_FIFO_DEPTH_WORDS 16 -#define DEF_TX_WM 2 -#define DEF_FIFO_WIDTH_BITS 32 -#define UART_RX_WM 2 +#define UART_START_READ 0x1 +#define UART_PARAM 0x1 +#define UART_PARAM_RFR_OPEN BIT(7) + +#define UART_OVERSAMPLING 32 +#define STALE_TIMEOUT 16 +#define DEFAULT_BITS_PER_CHAR 10 +#define GENI_UART_CONS_PORTS 1 +#define GENI_UART_PORTS 3 +#define DEF_FIFO_DEPTH_WORDS 16 +#define DEF_TX_WM 2 +#define DEF_FIFO_WIDTH_BITS 32 +#define UART_RX_WM 2 /* SE_UART_LOOPBACK_CFG */ -#define RX_TX_SORTED BIT(0) -#define CTS_RTS_SORTED BIT(1) -#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) +#define RX_TX_SORTED BIT(0) +#define CTS_RTS_SORTED BIT(1) +#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) /* UART pin swap value */ -#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0) +#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0) #define IO_MACRO_IO0_SEL 0x3 -#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) +#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) #define IO_MACRO_IO2_IO3_SWAP 0x4640 /* We always configure 4 bytes per FIFO word */ -#define BYTES_PER_FIFO_WORD 4 +#define BYTES_PER_FIFO_WORD 4U + +#define DMA_RX_BUF_SIZE 2048 + +struct qcom_geni_device_data { + bool console; + enum geni_se_xfer_mode mode; +}; struct qcom_geni_private_data { /* NOTE: earlycon port will have NULL here */ @@ -128,10 +122,11 @@ struct qcom_geni_serial_port { u32 tx_fifo_depth; u32 tx_fifo_width; u32 rx_fifo_depth; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; bool setup; - int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); unsigned int baud; - void *rx_fifo; + void *rx_buf; u32 loopback; bool brk; @@ -141,44 +136,42 @@ struct qcom_geni_serial_port { bool cts_rts_swap; struct qcom_geni_private_data private_data; + const struct qcom_geni_device_data *dev_data; }; static const struct uart_ops qcom_geni_console_pops; static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_uart_driver; -static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); -static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop); -static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); -static void qcom_geni_serial_stop_rx(struct uart_port *uport); -static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop); -#define to_dev_port(ptr, member) \ - container_of(ptr, struct qcom_geni_serial_port, member) +static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) +{ + return container_of(uport, struct qcom_geni_serial_port, uport); +} static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = { [0] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, }, }, [1] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, }, }, [2] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 2, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, }, }, }; @@ -195,7 +188,7 @@ static struct qcom_geni_serial_port qcom_geni_console_port = { static int qcom_geni_serial_request_port(struct uart_port *uport) { struct platform_device *pdev = to_platform_device(uport->dev); - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); uport->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(uport->membase)) @@ -232,7 +225,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, unsigned int mctrl) { u32 uart_manual_rfr = 0; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (uart_console(uport)) return; @@ -262,6 +255,16 @@ static struct qcom_geni_serial_port *get_port_from_line(int line, bool console) return port; } +static bool qcom_geni_serial_main_active(struct uart_port *uport) +{ + return readl(uport->membase + SE_GENI_STATUS) & M_GENI_CMD_ACTIVE; +} + +static bool qcom_geni_serial_secondary_active(struct uart_port *uport) +{ + return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE; +} + static bool qcom_geni_serial_poll_bit(struct uart_port *uport, int offset, int field, bool set) { @@ -273,7 +276,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, struct qcom_geni_private_data *private_data = uport->private_data; if (private_data->drv) { - port = to_dev_port(uport, uport); + port = to_dev_port(uport); baud = port->baud; if (!baud) baud = 115200; @@ -338,7 +341,6 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport) } #ifdef CONFIG_CONSOLE_POLL - static int qcom_geni_serial_get_char(struct uart_port *uport) { struct qcom_geni_private_data *private_data = uport->private_data; @@ -521,12 +523,12 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&uport->lock, flags); } -static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) { u32 i; unsigned char buf[sizeof(u32)]; struct tty_port *tport; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); tport = &uport->state->port; for (i = 0; i < bytes; ) { @@ -556,30 +558,21 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) } if (!drop) tty_flip_buffer_push(tport); - return 0; } #else -static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) { - return -EPERM; -} +} #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ -static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) { - struct tty_port *tport; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE; - u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw; + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct tty_port *tport = &uport->state->port; int ret; - tport = &uport->state->port; - ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words); - if (drop) - return 0; - - ret = tty_insert_flip_string(tport, port->rx_fifo, bytes); + ret = tty_insert_flip_string(tport, port->rx_buf, bytes); if (ret != bytes) { dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", __func__, ret, bytes); @@ -587,19 +580,82 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) } uport->icount.rx += ret; tty_flip_buffer_push(tport); - return ret; } -static void qcom_geni_serial_start_tx(struct uart_port *uport) +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) { - u32 irq_en; - u32 status; + return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); +} + +static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + bool done; + u32 m_irq_en; - status = readl(uport->membase + SE_GENI_STATUS); - if (status & M_GENI_CMD_ACTIVE) + if (!qcom_geni_serial_main_active(uport)) return; - if (!qcom_geni_serial_tx_empty(uport)) + if (port->rx_dma_addr) { + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, + port->tx_remaining); + port->tx_dma_addr = 0; + port->tx_remaining = 0; + } + + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN); + geni_se_cancel_m_cmd(&port->se); + + done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); + if (!done) { + geni_se_abort_m_cmd(&port->se); + done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + if (!done) + dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set"); + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + } + + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); +} + +static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + unsigned int xmit_size; + int ret; + + if (port->tx_dma_addr) + return; + + xmit_size = uart_circ_chars_pending(xmit); + if (xmit_size < WAKEUP_CHARS) + uart_write_wakeup(uport); + + xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + qcom_geni_serial_setup_tx(uport, xmit_size); + + ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail], + xmit_size, &port->tx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret); + qcom_geni_serial_stop_tx_dma(uport); + return; + } + + port->tx_remaining = xmit_size; +} + +static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) +{ + u32 irq_en; + + if (qcom_geni_serial_main_active(uport) || + !qcom_geni_serial_tx_empty(uport)) return; irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); @@ -609,19 +665,17 @@ static void qcom_geni_serial_start_tx(struct uart_port *uport) writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } -static void qcom_geni_serial_stop_tx(struct uart_port *uport) +static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) { u32 irq_en; - u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl(uport->membase + SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ - if (!(status & M_GENI_CMD_ACTIVE)) + if (!qcom_geni_serial_main_active(uport)) return; geni_se_cancel_m_cmd(&port->se); @@ -635,32 +689,34 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport) writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } -static void qcom_geni_serial_start_rx(struct uart_port *uport) +static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) { - u32 irq_en; u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - - status = readl(uport->membase + SE_GENI_STATUS); - if (status & S_GENI_CMD_ACTIVE) - qcom_geni_serial_stop_rx(uport); - - geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + u32 word_cnt; + u32 last_word_byte_cnt; + u32 last_word_partial; + u32 total_bytes; - irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); - irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; - writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); + word_cnt = status & RX_FIFO_WC_MSK; + last_word_partial = status & RX_LAST; + last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> + RX_LAST_BYTE_VALID_SHFT; - irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); - irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; - writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + if (!word_cnt) + return; + total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1); + if (last_word_partial && last_word_byte_cnt) + total_bytes += last_word_byte_cnt; + else + total_bytes += BYTES_PER_FIFO_WORD; + handle_rx_console(uport, total_bytes, drop); } -static void qcom_geni_serial_stop_rx(struct uart_port *uport) +static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport) { u32 irq_en; - u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); u32 s_irq_status; irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); @@ -671,9 +727,7 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport) irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl(uport->membase + SE_GENI_STATUS); - /* Possible stop rx is called multiple times. */ - if (!(status & S_GENI_CMD_ACTIVE)) + if (!qcom_geni_serial_secondary_active(uport)) return; geni_se_cancel_s_cmd(&port->se); @@ -686,52 +740,154 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport) s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); /* Flush the Rx buffer */ if (s_irq_status & S_RX_FIFO_LAST_EN) - qcom_geni_serial_handle_rx(uport, true); + qcom_geni_serial_handle_rx_fifo(uport, true); writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); - status = readl(uport->membase + SE_GENI_STATUS); - if (status & S_GENI_CMD_ACTIVE) + if (qcom_geni_serial_secondary_active(uport)) qcom_geni_serial_abort_rx(uport); } -static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) +static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport) { - u32 status; - u32 word_cnt; - u32 last_word_byte_cnt; - u32 last_word_partial; - u32 total_bytes; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + u32 irq_en; + struct qcom_geni_serial_port *port = to_dev_port(uport); - status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); - word_cnt = status & RX_FIFO_WC_MSK; - last_word_partial = status & RX_LAST; - last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> - RX_LAST_BYTE_VALID_SHFT; + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_stop_rx_fifo(uport); - if (!word_cnt) + geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + + irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); +} + +static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (!qcom_geni_serial_secondary_active(uport)) return; - total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1); - if (last_word_partial && last_word_byte_cnt) - total_bytes += last_word_byte_cnt; - else - total_bytes += BYTES_PER_FIFO_WORD; - port->handle_rx(uport, total_bytes, drop); + + geni_se_cancel_s_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); + + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_abort_rx(uport); + + if (port->rx_dma_addr) { + geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, + DMA_RX_BUF_SIZE); + port->rx_dma_addr = 0; + } +} + +static void qcom_geni_serial_start_rx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + int ret; + + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_stop_rx_dma(uport); + + geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN); + + ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, + DMA_RX_BUF_SIZE, + &port->rx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); + qcom_geni_serial_stop_rx_dma(uport); + } +} + +static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + u32 rx_in; + int ret; + + if (!qcom_geni_serial_secondary_active(uport)) + return; + + if (!port->rx_dma_addr) + return; + + geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); + port->rx_dma_addr = 0; + + rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); + if (!rx_in) { + dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n"); + return; + } + + if (!drop) + handle_rx_uart(uport, rx_in, drop); + + ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, + DMA_RX_BUF_SIZE, + &port->rx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); + qcom_geni_serial_stop_rx_dma(uport); + } +} + +static void qcom_geni_serial_start_rx(struct uart_port *uport) +{ + uport->ops->start_rx(uport); +} + +static void qcom_geni_serial_stop_rx(struct uart_port *uport) +{ + uport->ops->stop_rx(uport); +} + +static void qcom_geni_serial_stop_tx(struct uart_port *uport) +{ + uport->ops->stop_tx(uport); +} + +static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, + unsigned int chunk) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + unsigned int tx_bytes, c, remaining = chunk; + u8 buf[BYTES_PER_FIFO_WORD]; + + while (remaining) { + memset(buf, 0, sizeof(buf)); + tx_bytes = min(remaining, BYTES_PER_FIFO_WORD); + + for (c = 0; c < tx_bytes ; c++) { + buf[c] = xmit->buf[xmit->tail]; + uart_xmit_advance(uport, 1); + } + + iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); + + remaining -= tx_bytes; + port->tx_remaining -= tx_bytes; + } } -static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, - bool active) +static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, + bool done, bool active) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); struct circ_buf *xmit = &uport->state->xmit; size_t avail; - size_t remaining; size_t pending; - int i; u32 status; u32 irq_en; unsigned int chunk; - int tail; status = readl(uport->membase + SE_GENI_TX_FIFO_STATUS); @@ -743,14 +899,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, /* All data has been transmitted and acknowledged as received */ if (!pending && !status && done) { - qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_tx_fifo(uport); goto out_write_wakeup; } avail = port->tx_fifo_depth - (status & TX_FIFO_WC); avail *= BYTES_PER_FIFO_WORD; - tail = xmit->tail; chunk = min(avail, pending); if (!chunk) goto out_write_wakeup; @@ -765,29 +920,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, uport->membase + SE_GENI_M_IRQ_EN); } - remaining = chunk; - for (i = 0; i < chunk; ) { - unsigned int tx_bytes; - u8 buf[sizeof(u32)]; - int c; - - memset(buf, 0, sizeof(buf)); - tx_bytes = min_t(size_t, remaining, BYTES_PER_FIFO_WORD); - - for (c = 0; c < tx_bytes ; c++) { - buf[c] = xmit->buf[tail++]; - tail &= UART_XMIT_SIZE - 1; - } - - iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); - - i += tx_bytes; - uport->icount.tx += tx_bytes; - remaining -= tx_bytes; - port->tx_remaining -= tx_bytes; - } - - xmit->tail = tail; + qcom_geni_serial_send_chunk_fifo(uport, chunk); /* * The tx fifo watermark is level triggered and latched. Though we had @@ -809,16 +942,36 @@ out_write_wakeup: uart_write_wakeup(uport); } +static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + + uart_xmit_advance(uport, port->tx_remaining); + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); + port->tx_dma_addr = 0; + port->tx_remaining = 0; + + if (!uart_circ_empty(xmit)) + qcom_geni_serial_start_tx_dma(uport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(uport); +} + static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) { u32 m_irq_en; u32 m_irq_status; u32 s_irq_status; u32 geni_status; + u32 dma; + u32 dma_tx_status; + u32 dma_rx_status; struct uart_port *uport = dev; bool drop_rx = false; struct tty_port *tport = &uport->state->port; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (uport->suspended) return IRQ_NONE; @@ -827,10 +980,15 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + dma_tx_status = readl(uport->membase + SE_DMA_TX_IRQ_STAT); + dma_rx_status = readl(uport->membase + SE_DMA_RX_IRQ_STAT); geni_status = readl(uport->membase + SE_GENI_STATUS); + dma = readl(uport->membase + SE_GENI_DMA_MODE_EN); m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); + writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR); + writel(dma_rx_status, uport->membase + SE_DMA_RX_IRQ_CLR); if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN)) goto out_unlock; @@ -840,23 +998,44 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) tty_insert_flip_char(tport, 0, TTY_OVERRUN); } - if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) - qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, - geni_status & M_GENI_CMD_ACTIVE); - - if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) { + if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) { if (s_irq_status & S_GP_IRQ_0_EN) uport->icount.parity++; drop_rx = true; - } else if (s_irq_status & S_GP_IRQ_2_EN || - s_irq_status & S_GP_IRQ_3_EN) { + } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) { uport->icount.brk++; port->brk = true; } - if (s_irq_status & S_RX_FIFO_WATERMARK_EN || - s_irq_status & S_RX_FIFO_LAST_EN) - qcom_geni_serial_handle_rx(uport, drop_rx); + if (dma) { + if (dma_tx_status & TX_DMA_DONE) + qcom_geni_serial_handle_tx_dma(uport); + + if (dma_rx_status) { + if (dma_rx_status & RX_RESET_DONE) + goto out_unlock; + + if (dma_rx_status & RX_DMA_PARITY_ERR) { + uport->icount.parity++; + drop_rx = true; + } + + if (dma_rx_status & RX_DMA_BREAK) + uport->icount.brk++; + + if (dma_rx_status & (RX_DMA_DONE | RX_EOT)) + qcom_geni_serial_handle_rx_dma(uport, drop_rx); + } + } else { + if (m_irq_status & m_irq_en & + (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) + qcom_geni_serial_handle_tx_fifo(uport, + m_irq_status & M_CMD_DONE_EN, + geni_status & M_GENI_CMD_ACTIVE); + + if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN)) + qcom_geni_serial_handle_rx_fifo(uport, drop_rx); + } out_unlock: uart_unlock_and_check_sysrq(uport); @@ -876,11 +1055,11 @@ static int setup_fifos(struct qcom_geni_serial_port *port) uport->fifosize = (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; - if (port->rx_fifo && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) { - port->rx_fifo = devm_krealloc(uport->dev, port->rx_fifo, - port->rx_fifo_depth * sizeof(u32), - GFP_KERNEL); - if (!port->rx_fifo) + if (port->rx_buf && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) { + port->rx_buf = devm_krealloc(uport->dev, port->rx_buf, + port->rx_fifo_depth * sizeof(u32), + GFP_KERNEL); + if (!port->rx_buf) return -ENOMEM; } @@ -891,11 +1070,13 @@ static int setup_fifos(struct qcom_geni_serial_port *port) static void qcom_geni_serial_shutdown(struct uart_port *uport) { disable_irq(uport->irq); + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); } static int qcom_geni_serial_port_setup(struct uart_port *uport) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; u32 proto; u32 pin_swap; @@ -937,7 +1118,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD, false, true, true); geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); - geni_se_select_mode(&port->se, GENI_SE_FIFO); + geni_se_select_mode(&port->se, port->dev_data->mode); qcom_geni_serial_start_rx(uport); port->setup = true; @@ -947,7 +1128,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) static int qcom_geni_serial_startup(struct uart_port *uport) { int ret; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); @@ -1033,7 +1214,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, u32 stop_bit_len; unsigned int clk_div; u32 ser_clk_cfg; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); unsigned long clk_rate; u32 ver, sampling_rate; unsigned int avg_bw_core; @@ -1137,11 +1318,6 @@ out_restart_rx: qcom_geni_serial_start_rx(uport); } -static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) -{ - return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); -} - #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE static int qcom_geni_console_setup(struct console *co, char *options) { @@ -1323,7 +1499,7 @@ static struct uart_driver qcom_geni_uart_driver = { static void qcom_geni_serial_pm(struct uart_port *uport, unsigned int new_state, unsigned int old_state) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); /* If we've never been called, treat it as off */ if (old_state == UART_PM_STATE_UNDEFINED) @@ -1341,10 +1517,10 @@ static void qcom_geni_serial_pm(struct uart_port *uport, static const struct uart_ops qcom_geni_console_pops = { .tx_empty = qcom_geni_serial_tx_empty, - .stop_tx = qcom_geni_serial_stop_tx, - .start_tx = qcom_geni_serial_start_tx, - .stop_rx = qcom_geni_serial_stop_rx, - .start_rx = qcom_geni_serial_start_rx, + .stop_tx = qcom_geni_serial_stop_tx_fifo, + .start_tx = qcom_geni_serial_start_tx_fifo, + .stop_rx = qcom_geni_serial_stop_rx_fifo, + .start_rx = qcom_geni_serial_start_rx_fifo, .set_termios = qcom_geni_serial_set_termios, .startup = qcom_geni_serial_startup, .request_port = qcom_geni_serial_request_port, @@ -1362,9 +1538,10 @@ static const struct uart_ops qcom_geni_console_pops = { static const struct uart_ops qcom_geni_uart_pops = { .tx_empty = qcom_geni_serial_tx_empty, - .stop_tx = qcom_geni_serial_stop_tx, - .start_tx = qcom_geni_serial_start_tx, - .stop_rx = qcom_geni_serial_stop_rx, + .stop_tx = qcom_geni_serial_stop_tx_dma, + .start_tx = qcom_geni_serial_start_tx_dma, + .start_rx = qcom_geni_serial_start_rx_dma, + .stop_rx = qcom_geni_serial_stop_rx_dma, .set_termios = qcom_geni_serial_set_termios, .startup = qcom_geni_serial_startup, .request_port = qcom_geni_serial_request_port, @@ -1384,13 +1561,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) struct uart_port *uport; struct resource *res; int irq; - bool console = false; struct uart_driver *drv; + const struct qcom_geni_device_data *data; - if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart")) - console = true; + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -EINVAL; - if (console) { + if (data->console) { drv = &qcom_geni_console_driver; line = of_alias_get_id(pdev->dev.of_node, "serial"); } else { @@ -1400,7 +1578,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) line = of_alias_get_id(pdev->dev.of_node, "hsuart"); } - port = get_port_from_line(line, console); + port = get_port_from_line(line, data->console); if (IS_ERR(port)) { dev_err(&pdev->dev, "Invalid line %d\n", line); return PTR_ERR(port); @@ -1412,6 +1590,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return -ENODEV; uport->dev = &pdev->dev; + port->dev_data = data; port->se.dev = &pdev->dev; port->se.wrapper = dev_get_drvdata(pdev->dev.parent); port->se.clk = devm_clk_get(&pdev->dev, "se"); @@ -1430,10 +1609,10 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; - if (!console) { - port->rx_fifo = devm_kcalloc(uport->dev, - port->rx_fifo_depth, sizeof(u32), GFP_KERNEL); - if (!port->rx_fifo) + if (!data->console) { + port->rx_buf = devm_kzalloc(uport->dev, + DMA_RX_BUF_SIZE, GFP_KERNEL); + if (!port->rx_buf) return -ENOMEM; } @@ -1460,7 +1639,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) uport->irq = irq; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE); - if (!console) + if (!data->console) port->wakeup_irq = platform_get_irq_optional(pdev, 1); if (of_property_read_bool(pdev->dev.of_node, "rx-tx-swap")) @@ -1482,7 +1661,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->private_data.drv = drv; uport->private_data = &port->private_data; platform_set_drvdata(pdev, port); - port->handle_rx = console ? handle_rx_console : handle_rx_uart; ret = uart_add_one_port(drv, uport); if (ret) @@ -1594,6 +1772,16 @@ static int qcom_geni_serial_sys_hib_resume(struct device *dev) return ret; } +static const struct qcom_geni_device_data qcom_geni_console_data = { + .console = true, + .mode = GENI_SE_FIFO, +}; + +static const struct qcom_geni_device_data qcom_geni_uart_data = { + .console = false, + .mode = GENI_SE_DMA, +}; + static const struct dev_pm_ops qcom_geni_serial_pm_ops = { .suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend), .resume = pm_sleep_ptr(qcom_geni_serial_sys_resume), @@ -1604,8 +1792,14 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { }; static const struct of_device_id qcom_geni_serial_match_table[] = { - { .compatible = "qcom,geni-debug-uart", }, - { .compatible = "qcom,geni-uart", }, + { + .compatible = "qcom,geni-debug-uart", + .data = &qcom_geni_console_data, + }, + { + .compatible = "qcom,geni-uart", + .data = &qcom_geni_uart_data, + }, {} }; MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 39f92eb1e698..29c94be09159 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1423,25 +1423,6 @@ static int sc16is7xx_probe(struct device *dev, } sched_set_fifo(s->kworker_task); -#ifdef CONFIG_GPIOLIB - if (devtype->nr_gpio) { - /* Setup GPIO cotroller */ - s->gpio.owner = THIS_MODULE; - s->gpio.parent = dev; - s->gpio.label = dev_name(dev); - s->gpio.direction_input = sc16is7xx_gpio_direction_input; - s->gpio.get = sc16is7xx_gpio_get; - s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; - s->gpio.base = -1; - s->gpio.ngpio = devtype->nr_gpio; - s->gpio.can_sleep = 1; - ret = gpiochip_add_data(&s->gpio, s); - if (ret) - goto out_thread; - } -#endif - /* reset device, purging any pending irq / data */ regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, SC16IS7XX_IOCONTROL_SRESET_BIT); @@ -1518,6 +1499,25 @@ static int sc16is7xx_probe(struct device *dev, s->p[u].irda_mode = true; } +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.parent = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add_data(&s->gpio, s); + if (ret) + goto out_thread; + } +#endif + /* * Setup interrupt. We first try to acquire the IRQ line as level IRQ. * If that succeeds, we can allow sharing the interrupt as well. @@ -1537,18 +1537,19 @@ static int sc16is7xx_probe(struct device *dev, if (!ret) return 0; -out_ports: - for (i--; i >= 0; i--) { - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); - } - #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) gpiochip_remove(&s->gpio); out_thread: #endif + +out_ports: + for (i--; i >= 0; i--) { + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + clear_bit(s->p[i].port.line, &sc16is7xx_lines); + } + kthread_stop(s->kworker_task); out_clk: diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 7df687822634..4f2fc5f7bb19 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -913,23 +913,13 @@ static int sccnxp_probe(struct platform_device *pdev) } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) return -EPROBE_DEFER; - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); if (ret == -EPROBE_DEFER) goto err_out; uartclk = 0; } else { - ret = clk_prepare_enable(clk); - if (ret) - goto err_out; - - ret = devm_add_action_or_reset(&pdev->dev, - (void(*)(void *))clk_disable_unprepare, - clk); - if (ret) - goto err_out; - uartclk = clk_get_rate(clk); } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index e5b9773db5e3..1cf08b33456c 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1046,6 +1046,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) if (tup->cdata->fifo_mode_enable_status) { ret = tegra_uart_wait_fifo_mode_enabled(tup); if (ret < 0) { + clk_disable_unprepare(tup->uart_clk); dev_err(tup->uport.dev, "Failed to enable FIFO mode: %d\n", ret); return ret; @@ -1067,6 +1068,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) */ ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); if (ret < 0) { + clk_disable_unprepare(tup->uart_clk); dev_err(tup->uport.dev, "Failed to set baud rate\n"); return ret; } @@ -1226,10 +1228,13 @@ static int tegra_uart_startup(struct uart_port *u) dev_name(u->dev), tup); if (ret < 0) { dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq); - goto fail_hw_init; + goto fail_request_irq; } return 0; +fail_request_irq: + /* tup->uart_clk is already enabled in tegra_uart_hw_init */ + clk_disable_unprepare(tup->uart_clk); fail_hw_init: if (!tup->use_rx_pio) tegra_uart_dma_channel_free(tup, true); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ec874f3a567c..2bd32c8ece39 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -169,9 +169,9 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) #define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) #define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear) -static void uart_port_dtr_rts(struct uart_port *uport, int raise) +static void uart_port_dtr_rts(struct uart_port *uport, bool active) { - if (raise) + if (active) uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); else uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); @@ -182,7 +182,7 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise) * will be serialised by the per-port mutex. */ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, - int init_hw) + bool init_hw) { struct uart_port *uport = uart_port_check(state); unsigned long flags; @@ -239,7 +239,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, * port is open and ready to respond. */ if (init_hw && C_BAUD(tty)) - uart_port_dtr_rts(uport, 1); + uart_port_dtr_rts(uport, true); } /* @@ -254,7 +254,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, } static int uart_startup(struct tty_struct *tty, struct uart_state *state, - int init_hw) + bool init_hw) { struct tty_port *port = &state->port; int retval; @@ -290,7 +290,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) set_bit(TTY_IO_ERROR, &tty->flags); if (tty_port_initialized(port)) { - tty_port_set_initialized(port, 0); + tty_port_set_initialized(port, false); /* * Turn off DTR and RTS early. @@ -302,7 +302,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) } if (!tty || C_HUPCL(tty)) - uart_port_dtr_rts(uport, 0); + uart_port_dtr_rts(uport, false); uart_port_shutdown(port); } @@ -312,7 +312,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); /* * Do not free() the transmit buffer page under the port lock since @@ -997,7 +997,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, uart_change_speed(tty, state, NULL); } } else { - retval = uart_startup(tty, state, 1); + retval = uart_startup(tty, state, true); if (retval == 0) tty_port_set_initialized(port, true); if (retval > 0) @@ -1165,7 +1165,7 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) */ uport->ops->config_port(uport, flags); - ret = uart_startup(tty, state, 1); + ret = uart_startup(tty, state, true); if (ret == 0) tty_port_set_initialized(port, true); if (ret > 0) @@ -1725,7 +1725,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); /* * Free the transmit buffer. @@ -1827,7 +1827,7 @@ static void uart_hangup(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); port->count = 0; spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); tty_port_tty_set(port, NULL); if (uport && !uart_console(uport)) uart_change_pm(state, UART_PM_STATE_OFF); @@ -1861,7 +1861,7 @@ static void uart_port_shutdown(struct tty_port *port) } } -static int uart_carrier_raised(struct tty_port *port) +static bool uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; @@ -1875,18 +1875,17 @@ static int uart_carrier_raised(struct tty_port *port) * continue and not sleep */ if (WARN_ON(!uport)) - return 1; + return true; spin_lock_irq(&uport->lock); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); uart_port_deref(uport); - if (mctrl & TIOCM_CAR) - return 1; - return 0; + + return mctrl & TIOCM_CAR; } -static void uart_dtr_rts(struct tty_port *port, int raise) +static void uart_dtr_rts(struct tty_port *port, bool active) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; @@ -1894,7 +1893,7 @@ static void uart_dtr_rts(struct tty_port *port, int raise) uport = uart_port_ref(state); if (!uport) return; - uart_port_dtr_rts(uport, raise); + uart_port_dtr_rts(uport, active); uart_port_deref(uport); } @@ -1943,9 +1942,9 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) /* * Start up the serial port. */ - ret = uart_startup(tty, state, 0); + ret = uart_startup(tty, state, false); if (ret > 0) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return ret; } @@ -2349,8 +2348,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) int tries; unsigned int mctrl; - tty_port_set_suspended(port, 1); - tty_port_set_initialized(port, 0); + tty_port_set_suspended(port, true); + tty_port_set_initialized(port, false); spin_lock_irq(&uport->lock); ops->stop_tx(uport); @@ -2461,7 +2460,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) uart_rs485_config(uport); ops->start_tx(uport); spin_unlock_irq(&uport->lock); - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } else { /* * Failed to resume - maybe hardware went away? @@ -2472,7 +2471,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } } - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); } mutex_unlock(&port->mutex); @@ -3258,11 +3257,11 @@ EXPORT_SYMBOL(uart_match_port); /** * uart_handle_dcd_change - handle a change of carrier detect state * @uport: uart_port structure for the open port - * @status: new carrier detect status, nonzero if active + * @active: new carrier detect status * * Caller must hold uport->lock. */ -void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) +void uart_handle_dcd_change(struct uart_port *uport, bool active) { struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; @@ -3274,7 +3273,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) ld = tty_ldisc_ref(tty); if (ld) { if (ld->ops->dcd_change) - ld->ops->dcd_change(tty, status); + ld->ops->dcd_change(tty, active); tty_ldisc_deref(ld); } } @@ -3282,7 +3281,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) uport->icount.dcd++; if (uart_dcd_enabled(uport)) { - if (status) + if (active) wake_up_interruptible(&port->open_wait); else if (tty) tty_hangup(tty); @@ -3293,11 +3292,11 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); /** * uart_handle_cts_change - handle a change of clear-to-send state * @uport: uart_port structure for the open port - * @status: new clear to send status, nonzero if active + * @active: new clear-to-send status * * Caller must hold uport->lock. */ -void uart_handle_cts_change(struct uart_port *uport, unsigned int status) +void uart_handle_cts_change(struct uart_port *uport, bool active) { lockdep_assert_held_once(&uport->lock); @@ -3305,13 +3304,13 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) if (uart_softcts_mode(uport)) { if (uport->hw_stopped) { - if (status) { + if (active) { uport->hw_stopped = 0; uport->ops->start_tx(uport); uart_write_wakeup(uport); } } else { - if (!status) { + if (!active) { uport->hw_stopped = 1; uport->ops->stop_tx(uport); } @@ -3415,6 +3414,7 @@ int uart_get_rs485_mode(struct uart_port *port) struct device *dev = port->dev; u32 rs485_delay[2]; int ret; + int rx_during_tx_gpio_flag; ret = device_property_read_u32_array(dev, "rs485-rts-delay", rs485_delay, 2); @@ -3463,6 +3463,17 @@ int uart_get_rs485_mode(struct uart_port *port) if (port->rs485_term_gpio) port->rs485_supported.flags |= SER_RS485_TERMINATE_BUS; + rx_during_tx_gpio_flag = (rs485conf->flags & SER_RS485_RX_DURING_TX) ? + GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + port->rs485_rx_during_tx_gpio = devm_gpiod_get_optional(dev, + "rs485-rx-during-tx", + rx_during_tx_gpio_flag); + if (IS_ERR(port->rs485_rx_during_tx_gpio)) { + ret = PTR_ERR(port->rs485_rx_during_tx_gpio); + port->rs485_rx_during_tx_gpio = NULL; + return dev_err_probe(dev, ret, "Cannot get rs485-rx-during-tx-gpios\n"); + } + return 0; } EXPORT_SYMBOL_GPL(uart_get_rs485_mode); diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 409e91d6829a..767ff9fdb2e5 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -226,7 +226,11 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - rs485conf->flags |= SER_RS485_RX_DURING_TX; + if (port->rs485_rx_during_tx_gpio) + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); + else + rs485conf->flags |= SER_RS485_RX_DURING_TX; if (rs485conf->flags & SER_RS485_ENABLED) { cr1 = readl_relaxed(port->membase + ofs->cr1); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 16c746a63258..7d38c33ef506 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -87,10 +87,10 @@ static int receive_chars_getchar(struct uart_port *port) if (c == CON_HUP) { hung_up = 1; - uart_handle_dcd_change(port, 0); + uart_handle_dcd_change(port, false); } else if (hung_up) { hung_up = 0; - uart_handle_dcd_change(port, 1); + uart_handle_dcd_change(port, true); } if (port->state == NULL) { @@ -133,7 +133,7 @@ static int receive_chars_read(struct uart_port *port) bytes_read = 1; } else if (stat == CON_HUP) { hung_up = 1; - uart_handle_dcd_change(port, 0); + uart_handle_dcd_change(port, false); continue; } else { /* HV_EWOULDBLOCK, etc. */ @@ -143,7 +143,7 @@ static int receive_chars_read(struct uart_port *port) if (hung_up) { hung_up = 0; - uart_handle_dcd_change(port, 1); + uart_handle_dcd_change(port, true); } if (port->sysrq != 0 && *con_read_page) { diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index b09b6496ee3e..32c7a5b43f8e 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1468,6 +1468,8 @@ static int ucc_uart_remove(struct platform_device *ofdev) uart_remove_one_port(&ucc_uart_driver, &qe_port->port); + of_node_put(qe_port->np); + kfree(qe_port); return 0; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 72b76cdde534..33f258d6fef9 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -694,7 +694,7 @@ static void hangup(struct tty_struct *tty) info->port.count = 0; info->port.tty = NULL; spin_unlock_irqrestore(&info->port.lock, flags); - tty_port_set_active(&info->port, 0); + tty_port_set_active(&info->port, false); mutex_unlock(&info->port.mutex); wake_up_interruptible(&info->port.open_wait); @@ -2354,7 +2354,7 @@ static int startup(struct slgt_info *info) if (info->port.tty) clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - tty_port_set_initialized(&info->port, 1); + tty_port_set_initialized(&info->port, true); return 0; } @@ -2401,7 +2401,7 @@ static void shutdown(struct slgt_info *info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - tty_port_set_initialized(&info->port, 0); + tty_port_set_initialized(&info->port, false); } static void program_hw(struct slgt_info *info) @@ -3126,7 +3126,7 @@ static int tiocmset(struct tty_struct *tty, return 0; } -static int carrier_raised(struct tty_port *port) +static bool carrier_raised(struct tty_port *port) { unsigned long flags; struct slgt_info *info = container_of(port, struct slgt_info, port); @@ -3134,16 +3134,17 @@ static int carrier_raised(struct tty_port *port) spin_lock_irqsave(&info->lock,flags); get_gtsignals(info); spin_unlock_irqrestore(&info->lock,flags); - return (info->signals & SerialSignal_DCD) ? 1 : 0; + + return info->signals & SerialSignal_DCD; } -static void dtr_rts(struct tty_port *port, int on) +static void dtr_rts(struct tty_port *port, bool active) { unsigned long flags; struct slgt_info *info = container_of(port, struct slgt_info, port); spin_lock_irqsave(&info->lock,flags); - if (on) + if (active) info->signals |= SerialSignal_RTS | SerialSignal_DTR; else info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); @@ -3162,14 +3163,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, int retval; bool do_clocal = false; unsigned long flags; - int cd; + bool cd; struct tty_port *port = &info->port; DBGINFO(("%s block_til_ready\n", tty->driver->name)); if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { /* nonblock mode is set or port is not enabled */ - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } @@ -3226,7 +3227,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open--; if (!retval) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); return retval; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3149114bf130..36fb945fdad4 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1224,14 +1224,16 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, { struct tty_struct *tty; - if (driver->ops->lookup) + if (driver->ops->lookup) { if (!file) tty = ERR_PTR(-EIO); else tty = driver->ops->lookup(driver, file, idx); - else + } else { + if (idx >= driver->num) + return ERR_PTR(-EINVAL); tty = driver->ttys[idx]; - + } if (!IS_ERR(tty)) tty_kref_get(tty); return tty; diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index ce511557b98b..12983ce4e43e 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -270,13 +270,13 @@ EXPORT_SYMBOL(tty_termios_copy_hw); * between the two termios structures, or a speed change is needed. */ -int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b) +bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b) { if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) - return 1; + return true; if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL)) - return 1; - return 0; + return true; + return false; } EXPORT_SYMBOL(tty_termios_hw_change); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index dce08a6d7b5e..a788a6bf487d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -367,7 +367,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) goto out; if (tty_port_initialized(port)) { - tty_port_set_initialized(port, 0); + tty_port_set_initialized(port, false); /* * Drop DTR/RTS if HUPCL is set. This causes any attached * modem to hang up the line. @@ -403,7 +403,7 @@ void tty_port_hangup(struct tty_port *port) set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); tty_port_shutdown(port, tty); tty_kref_put(tty); wake_up_interruptible(&port->open_wait); @@ -444,10 +444,10 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ -int tty_port_carrier_raised(struct tty_port *port) +bool tty_port_carrier_raised(struct tty_port *port) { if (port->ops->carrier_raised == NULL) - return 1; + return true; return port->ops->carrier_raised(port); } EXPORT_SYMBOL(tty_port_carrier_raised); @@ -463,7 +463,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised); void tty_port_raise_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 1); + port->ops->dtr_rts(port, true); } EXPORT_SYMBOL(tty_port_raise_dtr_rts); @@ -478,7 +478,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); void tty_port_lower_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 0); + port->ops->dtr_rts(port, false); } EXPORT_SYMBOL(tty_port_lower_dtr_rts); @@ -518,14 +518,14 @@ int tty_port_block_til_ready(struct tty_port *port, * the port has just hung up or is in another error state. */ if (tty_io_error(tty)) { - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { /* Indicate we are open */ if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } @@ -588,7 +588,7 @@ int tty_port_block_til_ready(struct tty_port *port, port->blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval == 0) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); @@ -695,7 +695,7 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) wake_up_interruptible(&port->open_wait); } spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); } EXPORT_SYMBOL(tty_port_close_end); @@ -788,7 +788,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, return retval; } } - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } mutex_unlock(&port->mutex); return tty_port_block_til_ready(port, tty, filp); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 981d2bfcf9a5..57a5c23b51d4 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -316,73 +316,55 @@ void schedule_console_callback(void) * Code to manage unicode-based screen buffers */ -#ifdef NO_VC_UNI_SCREEN -/* this disables and optimizes related code away at compile time */ -#define get_vc_uniscr(vc) NULL -#else -#define get_vc_uniscr(vc) vc->vc_uni_screen -#endif - -#define VC_UNI_SCREEN_DEBUG 0 - -typedef uint32_t char32_t; - /* * Our screen buffer is preceded by an array of line pointers so that * scrolling only implies some pointer shuffling. */ -struct uni_screen { - char32_t *lines[0]; -}; -static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows) +static u32 **vc_uniscr_alloc(unsigned int cols, unsigned int rows) { - struct uni_screen *uniscr; + u32 **uni_lines; void *p; - unsigned int memsize, i; + unsigned int memsize, i, col_size = cols * sizeof(**uni_lines); /* allocate everything in one go */ - memsize = cols * rows * sizeof(char32_t); - memsize += rows * sizeof(char32_t *); - p = vzalloc(memsize); - if (!p) + memsize = col_size * rows; + memsize += rows * sizeof(*uni_lines); + uni_lines = vzalloc(memsize); + if (!uni_lines) return NULL; /* initial line pointers */ - uniscr = p; - p = uniscr->lines + rows; + p = uni_lines + rows; for (i = 0; i < rows; i++) { - uniscr->lines[i] = p; - p += cols * sizeof(char32_t); + uni_lines[i] = p; + p += col_size; } - return uniscr; + + return uni_lines; } -static void vc_uniscr_free(struct uni_screen *uniscr) +static void vc_uniscr_free(u32 **uni_lines) { - vfree(uniscr); + vfree(uni_lines); } -static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr) +static void vc_uniscr_set(struct vc_data *vc, u32 **new_uni_lines) { - vc_uniscr_free(vc->vc_uni_screen); - vc->vc_uni_screen = new_uniscr; + vc_uniscr_free(vc->vc_uni_lines); + vc->vc_uni_lines = new_uni_lines; } -static void vc_uniscr_putc(struct vc_data *vc, char32_t uc) +static void vc_uniscr_putc(struct vc_data *vc, u32 uc) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) - uniscr->lines[vc->state.y][vc->state.x] = uc; + if (vc->vc_uni_lines) + vc->vc_uni_lines[vc->state.y][vc->state.x] = uc; } static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; + if (vc->vc_uni_lines) { + u32 *ln = vc->vc_uni_lines[vc->state.y]; unsigned int x = vc->state.x, cols = vc->vc_cols; memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln)); @@ -392,10 +374,8 @@ static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; + if (vc->vc_uni_lines) { + u32 *ln = vc->vc_uni_lines[vc->state.y]; unsigned int x = vc->state.x, cols = vc->vc_cols; memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln)); @@ -406,86 +386,84 @@ static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr) static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; - - memset32(&ln[x], ' ', nr); - } + if (vc->vc_uni_lines) + memset32(&vc->vc_uni_lines[vc->state.y][x], ' ', nr); } static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + if (vc->vc_uni_lines) + while (nr--) + memset32(vc->vc_uni_lines[y++], ' ', vc->vc_cols); +} + +/* juggling array rotation algorithm (complexity O(N), size complexity O(1)) */ +static void juggle_array(u32 **array, unsigned int size, unsigned int nr) +{ + unsigned int gcd_idx; - if (uniscr) { - unsigned int cols = vc->vc_cols; + for (gcd_idx = 0; gcd_idx < gcd(nr, size); gcd_idx++) { + u32 *gcd_idx_val = array[gcd_idx]; + unsigned int dst_idx = gcd_idx; - while (nr--) - memset32(uniscr->lines[y++], ' ', cols); + while (1) { + unsigned int src_idx = (dst_idx + nr) % size; + if (src_idx == gcd_idx) + break; + + array[dst_idx] = array[src_idx]; + dst_idx = src_idx; + } + + array[dst_idx] = gcd_idx_val; } } -static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int nr) +static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; + unsigned int size = bottom - top; - if (uniscr) { - unsigned int i, j, k, sz, d, clear; + if (!uni_lines) + return; - sz = b - t; - clear = b - nr; - d = nr; - if (dir == SM_DOWN) { - clear = t; - d = sz - nr; - } - for (i = 0; i < gcd(d, sz); i++) { - char32_t *tmp = uniscr->lines[t + i]; - j = i; - while (1) { - k = j + d; - if (k >= sz) - k -= sz; - if (k == i) - break; - uniscr->lines[t + j] = uniscr->lines[t + k]; - j = k; - } - uniscr->lines[t + j] = tmp; - } - vc_uniscr_clear_lines(vc, clear, nr); + if (dir == SM_DOWN) { + juggle_array(&uni_lines[top], size, size - nr); + vc_uniscr_clear_lines(vc, top, nr); + } else { + juggle_array(&uni_lines[top], size, nr); + vc_uniscr_clear_lines(vc, bottom - nr, nr); } } -static void vc_uniscr_copy_area(struct uni_screen *dst, +static void vc_uniscr_copy_area(u32 **dst_lines, unsigned int dst_cols, unsigned int dst_rows, - struct uni_screen *src, + u32 **src_lines, unsigned int src_cols, unsigned int src_top_row, unsigned int src_bot_row) { unsigned int dst_row = 0; - if (!dst) + if (!dst_lines) return; while (src_top_row < src_bot_row) { - char32_t *src_line = src->lines[src_top_row]; - char32_t *dst_line = dst->lines[dst_row]; + u32 *src_line = src_lines[src_top_row]; + u32 *dst_line = dst_lines[dst_row]; - memcpy(dst_line, src_line, src_cols * sizeof(char32_t)); + memcpy(dst_line, src_line, src_cols * sizeof(*src_line)); if (dst_cols - src_cols) memset32(dst_line + src_cols, ' ', dst_cols - src_cols); src_top_row++; dst_row++; } while (dst_row < dst_rows) { - char32_t *dst_line = dst->lines[dst_row]; + u32 *dst_line = dst_lines[dst_row]; memset32(dst_line, ' ', dst_cols); dst_row++; @@ -500,23 +478,20 @@ static void vc_uniscr_copy_area(struct uni_screen *dst, */ int vc_uniscr_check(struct vc_data *vc) { - struct uni_screen *uniscr; + u32 **uni_lines; unsigned short *p; int x, y, mask; - if (__is_defined(NO_VC_UNI_SCREEN)) - return -EOPNOTSUPP; - WARN_CONSOLE_UNLOCKED(); if (!vc->vc_utf) return -ENODATA; - if (vc->vc_uni_screen) + if (vc->vc_uni_lines) return 0; - uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); - if (!uniscr) + uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); + if (!uni_lines) return -ENOMEM; /* @@ -528,14 +503,15 @@ int vc_uniscr_check(struct vc_data *vc) p = (unsigned short *)vc->vc_origin; mask = vc->vc_hi_font_mask | 0xff; for (y = 0; y < vc->vc_rows; y++) { - char32_t *line = uniscr->lines[y]; + u32 *line = uni_lines[y]; for (x = 0; x < vc->vc_cols; x++) { u16 glyph = scr_readw(p++) & mask; line[x] = inverse_translate(vc, glyph, true); } } - vc->vc_uni_screen = uniscr; + vc->vc_uni_lines = uni_lines; + return 0; } @@ -547,11 +523,12 @@ int vc_uniscr_check(struct vc_data *vc) void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, unsigned int row, unsigned int col, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; int offset = row * vc->vc_size_row + col * 2; unsigned long pos; - BUG_ON(!uniscr); + if (WARN_ON_ONCE(!uni_lines)) + return; pos = (unsigned long)screenpos(vc, offset, viewed); if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { @@ -562,7 +539,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, */ row = (pos - vc->vc_origin) / vc->vc_size_row; col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2; - memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); + memcpy(dest, &uni_lines[row][col], nr * sizeof(u32)); } else { /* * Scrollback is active. For now let's simply backtranslate @@ -572,7 +549,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, */ u16 *p = (u16 *)pos; int mask = vc->vc_hi_font_mask | 0xff; - char32_t *uni_buf = dest; + u32 *uni_buf = dest; while (nr--) { u16 glyph = scr_readw(p++) & mask; *uni_buf++ = inverse_translate(vc, glyph, true); @@ -580,64 +557,31 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, } } -/* this is for validation and debugging only */ -static void vc_uniscr_debug_check(struct vc_data *vc) +static void con_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - unsigned short *p; - int x, y, mask; + unsigned int rows = bottom - top; + u16 *clear, *dst, *src; - if (!VC_UNI_SCREEN_DEBUG || !uniscr) + if (top + nr >= bottom) + nr = rows - 1; + if (bottom > vc->vc_rows || top >= bottom || nr < 1) return; - WARN_CONSOLE_UNLOCKED(); - - /* - * Make sure our unicode screen translates into the same glyphs - * as the actual screen. This is brutal indeed. - */ - p = (unsigned short *)vc->vc_origin; - mask = vc->vc_hi_font_mask | 0xff; - for (y = 0; y < vc->vc_rows; y++) { - char32_t *line = uniscr->lines[y]; - for (x = 0; x < vc->vc_cols; x++) { - u16 glyph = scr_readw(p++) & mask; - char32_t uc = line[x]; - int tc = conv_uni_to_pc(vc, uc); - if (tc == -4) - tc = conv_uni_to_pc(vc, 0xfffd); - if (tc == -4) - tc = conv_uni_to_pc(vc, '?'); - if (tc != glyph) - pr_err_ratelimited( - "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n", - __func__, x, y, glyph, tc); - } - } -} - - -static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int nr) -{ - u16 *clear, *d, *s; - - if (t + nr >= b) - nr = b - t - 1; - if (b > vc->vc_rows || t >= b || nr < 1) - return; - vc_uniscr_scroll(vc, t, b, dir, nr); - if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr)) + vc_uniscr_scroll(vc, top, bottom, dir, nr); + if (con_is_visible(vc) && + vc->vc_sw->con_scroll(vc, top, bottom, dir, nr)) return; - s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t); - d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr)); + src = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * top); + dst = (u16 *)(vc->vc_origin + vc->vc_size_row * (top + nr)); if (dir == SM_UP) { - clear = s + (b - t - nr) * vc->vc_cols; - swap(s, d); + clear = src + (rows - nr) * vc->vc_cols; + swap(src, dst); } - scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); + scr_memmovew(dst, src, (rows - nr) * vc->vc_size_row); scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr); } @@ -1201,7 +1145,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned int user; unsigned short *oldscreen, *newscreen; - struct uni_screen *new_uniscr = NULL; + u32 **new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); @@ -1245,7 +1189,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (!newscreen) return -ENOMEM; - if (get_vc_uniscr(vc)) { + if (vc->vc_uni_lines) { new_uniscr = vc_uniscr_alloc(new_cols, new_rows); if (!new_uniscr) { kfree(newscreen); @@ -1297,7 +1241,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, end = old_origin + old_row_size * min(old_rows, new_rows); vc_uniscr_copy_area(new_uniscr, new_cols, new_rows, - get_vc_uniscr(vc), rlth/2, first_copied_row, + vc->vc_uni_lines, rlth/2, first_copied_row, min(old_rows, new_rows)); vc_uniscr_set(vc, new_uniscr); @@ -2959,7 +2903,6 @@ rescan_last_byte: goto rescan_last_byte; } con_flush(vc, &draw); - vc_uniscr_debug_check(vc); console_conditional_schedule(); notify_update(vc); console_unlock(); @@ -3156,8 +3099,14 @@ static struct tty_driver *vt_console_device(struct console *c, int *index) return console_driver; } +static int vt_console_setup(struct console *co, char *options) +{ + return co->index >= MAX_NR_CONSOLES ? -EINVAL : 0; +} + static struct console vt_console_driver = { .name = "tty", + .setup = vt_console_setup, .write = vt_console_print, .device = vt_console_device, .unblank = unblank_screen, @@ -4574,26 +4523,30 @@ void reset_palette(struct vc_data *vc) /* * Font switching * - * Currently we only support fonts up to 32 pixels wide, at a maximum height - * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, - * depending on width) reserved for each character which is kinda wasty, but - * this is done in order to maintain compatibility with the EGA/VGA fonts. It - * is up to the actual low-level console-driver convert data into its favorite - * format (maybe we should add a `fontoffset' field to the `display' - * structure so we won't have to convert the fontdata all the time. + * Currently we only support fonts up to 128 pixels wide, at a maximum height + * of 128 pixels. Userspace fontdata may have to be stored with 32 bytes + * (shorts/ints, depending on width) reserved for each character which is + * kinda wasty, but this is done in order to maintain compatibility with the + * EGA/VGA fonts. It is up to the actual low-level console-driver convert data + * into its favorite format (maybe we should add a `fontoffset' field to the + * `display' structure so we won't have to convert the fontdata all the time. * /Jes */ -#define max_font_size 65536 +#define max_font_width 64 +#define max_font_height 128 +#define max_font_glyphs 512 +#define max_font_size (max_font_glyphs*max_font_width*max_font_height) static int con_font_get(struct vc_data *vc, struct console_font_op *op) { struct console_font font; int rc = -EINVAL; int c; + unsigned int vpitch = op->op == KD_FONT_OP_GET_TALL ? op->height : 32; if (op->data) { - font.data = kmalloc(max_font_size, GFP_KERNEL); + font.data = kvmalloc(max_font_size, GFP_KERNEL); if (!font.data) return -ENOMEM; } else @@ -4603,7 +4556,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) if (vc->vc_mode != KD_TEXT) rc = -EINVAL; else if (vc->vc_sw->con_font_get) - rc = vc->vc_sw->con_font_get(vc, &font); + rc = vc->vc_sw->con_font_get(vc, &font, vpitch); else rc = -ENOSYS; console_unlock(); @@ -4611,7 +4564,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) if (rc) goto out; - c = (font.width+7)/8 * 32 * font.charcount; + c = (font.width+7)/8 * vpitch * font.charcount; if (op->data && font.charcount > op->charcount) rc = -ENOSPC; @@ -4628,7 +4581,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) rc = -EFAULT; out: - kfree(font.data); + kvfree(font.data); return rc; } @@ -4637,16 +4590,20 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) struct console_font font; int rc = -EINVAL; int size; + unsigned int vpitch = op->op == KD_FONT_OP_SET_TALL ? op->height : 32; if (vc->vc_mode != KD_TEXT) return -EINVAL; if (!op->data) return -EINVAL; - if (op->charcount > 512) + if (op->charcount > max_font_glyphs) + return -EINVAL; + if (op->width <= 0 || op->width > max_font_width || !op->height || + op->height > max_font_height) return -EINVAL; - if (op->width <= 0 || op->width > 32 || !op->height || op->height > 32) + if (vpitch < op->height) return -EINVAL; - size = (op->width+7)/8 * 32 * op->charcount; + size = (op->width+7)/8 * vpitch * op->charcount; if (size > max_font_size) return -ENOSPC; @@ -4664,7 +4621,7 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) else if (vc->vc_sw->con_font_set) { if (vc_is_sel(vc)) clear_selection(); - rc = vc->vc_sw->con_font_set(vc, &font, op->flags); + rc = vc->vc_sw->con_font_set(vc, &font, vpitch, op->flags); } else rc = -ENOSYS; console_unlock(); @@ -4710,8 +4667,10 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op) { switch (op->op) { case KD_FONT_OP_SET: + case KD_FONT_OP_SET_TALL: return con_font_set(vc, op); case KD_FONT_OP_GET: + case KD_FONT_OP_GET_TALL: return con_font_get(vc, op); case KD_FONT_OP_SET_DEFAULT: return con_font_default(vc, op); @@ -4740,10 +4699,11 @@ EXPORT_SYMBOL_GPL(screen_glyph); u32 screen_glyph_unicode(const struct vc_data *vc, int n) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; + + if (uni_lines) + return uni_lines[n / vc->vc_cols][n % vc->vc_cols]; - if (uniscr) - return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; return inverse_translate(vc, screen_glyph(vc, n * 2), true); } EXPORT_SYMBOL_GPL(screen_glyph_unicode); |