TTY/Serial fixes for 4.12-rc3

Here are some serial and tty fixes for 4.12-rc3.  They are a bit
 "bigger" than normal, which is why I had them "bake" in linux-next for a
 few weeks and didn't send them to you for -rc2.
 
 They revert a few of the serdev patches from 4.12-rc1, and bring things
 back to how they were in 4.11, to try to make things a bit more stable
 there.  Rob and Johan both agree that this is the way forward, so this
 isn't people squabbling over semantics.  Other than that, just a few
 minor serial driver fixes that people have had problems with.
 
 All of these have been in linux-next for a few weeks with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWSlOHA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylDCACgn7RHT16JUASggJmRUBeadxQcFQAAnjtxX2kc
 0AQLqXxqGyFxVZClAYMy
 =Y6+X
 -----END PGP SIGNATURE-----

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

Pull tty/serial fixes from Greg KH:
 "Here are some serial and tty fixes for 4.12-rc3. They are a bit bigger
  than normal, which is why I had them bake in linux-next for a few
  weeks and didn't send them to you for -rc2.

  They revert a few of the serdev patches from 4.12-rc1, and bring
  things back to how they were in 4.11, to try to make things a bit more
  stable there. Rob and Johan both agree that this is the way forward,
  so this isn't people squabbling over semantics. Other than that, just
  a few minor serial driver fixes that people have had problems with.

  All of these have been in linux-next for a few weeks with no reported
  issues"

* tag 'tty-4.12-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  serial: altera_uart: call iounmap() at driver remove
  serial: imx: ensure UCR3 and UFCR are setup correctly
  MAINTAINERS/serial: Change maintainer of jsm driver
  serial: enable serdev support
  tty/serdev: add serdev registration interface
  serdev: Restore serdev_device_write_buf for atomic context
  serial: core: fix crash in uart_suspend_port
  tty: fix port buffer locking
  tty: ehv_bytechan: clean up init error handling
  serial: ifx6x60: fix use-after-free on module unload
  serial: altera_jtaguart: adding iounmap()
  serial: exar: Fix stuck MSIs
  serial: efm32: Fix parity management in 'efm32_uart_console_get_options()'
  serdev: fix tty-port client deregistration
  Revert "tty_port: register tty ports with serdev bus"
  drivers/tty: 8250: only call fintek_8250_probe when doing port I/O
This commit is contained in:
Linus Torvalds 2017-05-27 09:39:09 -07:00
commit 249f1efd8e
14 changed files with 163 additions and 50 deletions

View File

@ -7143,7 +7143,7 @@ S: Maintained
F: drivers/media/platform/rcar_jpu.c
JSM Neo PCI based serial card
M: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com>
M: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com>
L: linux-serial@vger.kernel.org
S: Maintained
F: drivers/tty/serial/jsm/

View File

@ -764,7 +764,7 @@ static int __init ehv_bc_init(void)
ehv_bc_driver = alloc_tty_driver(count);
if (!ehv_bc_driver) {
ret = -ENOMEM;
goto error;
goto err_free_bcs;
}
ehv_bc_driver->driver_name = "ehv-bc";
@ -778,24 +778,23 @@ static int __init ehv_bc_init(void)
ret = tty_register_driver(ehv_bc_driver);
if (ret) {
pr_err("ehv-bc: could not register tty driver (ret=%i)\n", ret);
goto error;
goto err_put_tty_driver;
}
ret = platform_driver_register(&ehv_bc_tty_driver);
if (ret) {
pr_err("ehv-bc: could not register platform driver (ret=%i)\n",
ret);
goto error;
goto err_deregister_tty_driver;
}
return 0;
error:
if (ehv_bc_driver) {
tty_unregister_driver(ehv_bc_driver);
put_tty_driver(ehv_bc_driver);
}
err_deregister_tty_driver:
tty_unregister_driver(ehv_bc_driver);
err_put_tty_driver:
put_tty_driver(ehv_bc_driver);
err_free_bcs:
kfree(bcs);
return ret;

View File

@ -122,6 +122,18 @@ void serdev_device_write_wakeup(struct serdev_device *serdev)
}
EXPORT_SYMBOL_GPL(serdev_device_write_wakeup);
int serdev_device_write_buf(struct serdev_device *serdev,
const unsigned char *buf, size_t count)
{
struct serdev_controller *ctrl = serdev->ctrl;
if (!ctrl || !ctrl->ops->write_buf)
return -EINVAL;
return ctrl->ops->write_buf(ctrl, buf, count);
}
EXPORT_SYMBOL_GPL(serdev_device_write_buf);
int serdev_device_write(struct serdev_device *serdev,
const unsigned char *buf, size_t count,
unsigned long timeout)

View File

@ -102,9 +102,6 @@ static int ttyport_open(struct serdev_controller *ctrl)
return PTR_ERR(tty);
serport->tty = tty;
serport->port->client_ops = &client_ops;
serport->port->client_data = ctrl;
if (tty->ops->open)
tty->ops->open(serport->tty, NULL);
else
@ -215,6 +212,7 @@ struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
struct tty_driver *drv, int idx)
{
const struct tty_port_client_operations *old_ops;
struct serdev_controller *ctrl;
struct serport *serport;
int ret;
@ -233,28 +231,37 @@ struct device *serdev_tty_port_register(struct tty_port *port,
ctrl->ops = &ctrl_ops;
old_ops = port->client_ops;
port->client_ops = &client_ops;
port->client_data = ctrl;
ret = serdev_controller_add(ctrl);
if (ret)
goto err_controller_put;
goto err_reset_data;
dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
return &ctrl->dev;
err_controller_put:
err_reset_data:
port->client_data = NULL;
port->client_ops = old_ops;
serdev_controller_put(ctrl);
return ERR_PTR(ret);
}
void serdev_tty_port_unregister(struct tty_port *port)
int serdev_tty_port_unregister(struct tty_port *port)
{
struct serdev_controller *ctrl = port->client_data;
struct serport *serport = serdev_controller_get_drvdata(ctrl);
if (!serport)
return;
return -ENODEV;
serdev_controller_remove(ctrl);
port->client_ops = NULL;
port->client_data = NULL;
serdev_controller_put(ctrl);
return 0;
}

View File

@ -47,6 +47,7 @@
/*
* These are definitions for the Exar XR17V35X and XR17(C|D)15X
*/
#define UART_EXAR_INT0 0x80
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
#define UART_EXAR_DVID 0x8d /* Device identification */
@ -1337,7 +1338,7 @@ out_lock:
/*
* Check if the device is a Fintek F81216A
*/
if (port->type == PORT_16550A)
if (port->type == PORT_16550A && port->iotype == UPIO_PORT)
fintek_8250_probe(up);
if (up->capabilities != old_capabilities) {
@ -1869,17 +1870,13 @@ static int serial8250_default_handle_irq(struct uart_port *port)
static int exar_handle_irq(struct uart_port *port)
{
unsigned int iir = serial_port_in(port, UART_IIR);
int ret;
int ret = 0;
ret = serial8250_handle_irq(port, iir);
if (((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) &&
serial_port_in(port, UART_EXAR_INT0) != 0)
ret = 1;
if ((port->type == PORT_XR17V35X) ||
(port->type == PORT_XR17D15X)) {
serial_port_in(port, 0x80);
serial_port_in(port, 0x81);
serial_port_in(port, 0x82);
serial_port_in(port, 0x83);
}
ret |= serial8250_handle_irq(port, iir);
return ret;
}
@ -2177,6 +2174,8 @@ int serial8250_do_startup(struct uart_port *port)
serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
serial_port_in(port, UART_EXAR_INT0);
/*
* At this point, there's no way the LSR could still be 0xff;
@ -2335,6 +2334,8 @@ dont_test_tx_en:
serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
serial_port_in(port, UART_EXAR_INT0);
up->lsr_saved_flags = 0;
up->msr_saved_flags = 0;

View File

@ -478,6 +478,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev)
port = &altera_jtaguart_ports[i].port;
uart_remove_one_port(&altera_jtaguart_driver, port);
iounmap(port->membase);
return 0;
}

View File

@ -615,6 +615,7 @@ static int altera_uart_remove(struct platform_device *pdev)
if (port) {
uart_remove_one_port(&altera_uart_driver, port);
port->mapbase = 0;
iounmap(port->membase);
}
return 0;

View File

@ -27,6 +27,7 @@
#define UARTn_FRAME 0x04
#define UARTn_FRAME_DATABITS__MASK 0x000f
#define UARTn_FRAME_DATABITS(n) ((n) - 3)
#define UARTn_FRAME_PARITY__MASK 0x0300
#define UARTn_FRAME_PARITY_NONE 0x0000
#define UARTn_FRAME_PARITY_EVEN 0x0200
#define UARTn_FRAME_PARITY_ODD 0x0300
@ -572,12 +573,16 @@ static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
16 * (4 + (clkdiv >> 6)));
frame = efm32_uart_read32(efm_port, UARTn_FRAME);
if (frame & UARTn_FRAME_PARITY_ODD)
switch (frame & UARTn_FRAME_PARITY__MASK) {
case UARTn_FRAME_PARITY_ODD:
*parity = 'o';
else if (frame & UARTn_FRAME_PARITY_EVEN)
break;
case UARTn_FRAME_PARITY_EVEN:
*parity = 'e';
else
break;
default:
*parity = 'n';
}
*bits = (frame & UARTn_FRAME_DATABITS__MASK) -
UARTn_FRAME_DATABITS(4) + 4;

View File

@ -1382,9 +1382,9 @@ static struct spi_driver ifx_spi_driver = {
static void __exit ifx_spi_exit(void)
{
/* unregister */
spi_unregister_driver(&ifx_spi_driver);
tty_unregister_driver(tty_drv);
put_tty_driver(tty_drv);
spi_unregister_driver(&ifx_spi_driver);
unregister_reboot_notifier(&ifx_modem_reboot_notifier_block);
}

View File

@ -2184,7 +2184,9 @@ static int serial_imx_probe(struct platform_device *pdev)
* and DCD (when they are outputs) or enables the respective
* irqs. So set this bit early, i.e. before requesting irqs.
*/
writel(UFCR_DCEDTE, sport->port.membase + UFCR);
reg = readl(sport->port.membase + UFCR);
if (!(reg & UFCR_DCEDTE))
writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR);
/*
* Disable UCR3_RI and UCR3_DCD irqs. They are also not
@ -2195,7 +2197,15 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->port.membase + UCR3);
} else {
writel(0, sport->port.membase + UFCR);
unsigned long ucr3 = UCR3_DSR;
reg = readl(sport->port.membase + UFCR);
if (reg & UFCR_DCEDTE)
writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR);
if (!is_imx1_uart(sport))
ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
writel(ucr3, sport->port.membase + UCR3);
}
clk_disable_unprepare(sport->clk_ipg);

View File

@ -2083,7 +2083,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
mutex_lock(&port->mutex);
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (device_may_wakeup(tty_dev)) {
if (tty_dev && device_may_wakeup(tty_dev)) {
if (!enable_irq_wake(uport->irq))
uport->irq_wake = 1;
put_device(tty_dev);
@ -2782,7 +2782,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this port's parameters.
*/
tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
@ -2845,7 +2845,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
/*
* Remove the devices from the tty layer
*/
tty_unregister_device(drv->tty_driver, uport->line);
tty_port_unregister_device(port, drv->tty_driver, uport->line);
tty = tty_port_tty_get(port);
if (tty) {

View File

@ -34,7 +34,9 @@ static int tty_port_default_receive_buf(struct tty_port *port,
if (!disc)
return 0;
mutex_lock(&tty->atomic_write_lock);
ret = tty_ldisc_receive_buf(disc, p, (char *)f, count);
mutex_unlock(&tty->atomic_write_lock);
tty_ldisc_deref(disc);
@ -128,20 +130,86 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
tty_port_link_device(port, driver, index);
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
/**
* tty_port_register_device_attr_serdev - register tty or serdev device
* @port: tty_port of the device
* @driver: tty_driver for this device
* @index: index of the tty
* @device: parent if exists, otherwise NULL
* @drvdata: driver data for the device
* @attr_grp: attribute group for the device
*
* Register a serdev or tty device depending on if the parent device has any
* defined serdev clients or not.
*/
struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
struct device *dev;
tty_port_link_device(port, driver, index);
dev = serdev_tty_port_register(port, device, driver, index);
if (PTR_ERR(dev) != -ENODEV)
if (PTR_ERR(dev) != -ENODEV) {
/* Skip creating cdev if we registered a serdev device */
return dev;
}
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev);
/**
* tty_port_register_device_serdev - register tty or serdev device
* @port: tty_port of the device
* @driver: tty_driver for this device
* @index: index of the tty
* @device: parent if exists, otherwise NULL
*
* Register a serdev or tty device depending on if the parent device has any
* defined serdev clients or not.
*/
struct device *tty_port_register_device_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device)
{
return tty_port_register_device_attr_serdev(port, driver, index,
device, NULL, NULL);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_serdev);
/**
* tty_port_unregister_device - deregister a tty or serdev device
* @port: tty_port of the device
* @driver: tty_driver for this device
* @index: index of the tty
*
* If a tty or serdev device is registered with a call to
* tty_port_register_device_serdev() then this function must be called when
* the device is gone.
*/
void tty_port_unregister_device(struct tty_port *port,
struct tty_driver *driver, unsigned index)
{
int ret;
ret = serdev_tty_port_unregister(port);
if (ret == 0)
return;
tty_unregister_device(driver, index);
}
EXPORT_SYMBOL_GPL(tty_port_unregister_device);
int tty_port_alloc_xmit_buf(struct tty_port *port)
{
@ -189,9 +257,6 @@ static void tty_port_destructor(struct kref *kref)
/* check if last port ref was dropped before tty release */
if (WARN_ON(port->itty))
return;
serdev_tty_port_unregister(port);
if (port->xmit_buf)
free_page((unsigned long)port->xmit_buf);
tty_port_destroy(port);

View File

@ -195,6 +195,7 @@ int serdev_device_open(struct serdev_device *);
void serdev_device_close(struct serdev_device *);
unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
void serdev_device_set_flow_control(struct serdev_device *, bool);
int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
void serdev_device_wait_until_sent(struct serdev_device *, long);
int serdev_device_get_tiocm(struct serdev_device *);
int serdev_device_set_tiocm(struct serdev_device *, int, int);
@ -236,6 +237,12 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev
return 0;
}
static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {}
static inline int serdev_device_write_buf(struct serdev_device *serdev,
const unsigned char *buf,
size_t count)
{
return -ENODEV;
}
static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {}
static inline int serdev_device_get_tiocm(struct serdev_device *serdev)
{
@ -301,7 +308,7 @@ struct tty_driver;
struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
struct tty_driver *drv, int idx);
void serdev_tty_port_unregister(struct tty_port *port);
int serdev_tty_port_unregister(struct tty_port *port);
#else
static inline struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
@ -309,14 +316,10 @@ static inline struct device *serdev_tty_port_register(struct tty_port *port,
{
return ERR_PTR(-ENODEV);
}
static inline void serdev_tty_port_unregister(struct tty_port *port) {}
static inline int serdev_tty_port_unregister(struct tty_port *port)
{
return -ENODEV;
}
#endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */
static inline int serdev_device_write_buf(struct serdev_device *serdev,
const unsigned char *data,
size_t count)
{
return serdev_device_write(serdev, data, count, 0);
}
#endif /*_LINUX_SERDEV_H */

View File

@ -558,6 +558,15 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp);
extern struct device *tty_port_register_device_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device);
extern struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp);
extern void tty_port_unregister_device(struct tty_port *port,
struct tty_driver *driver, unsigned index);
extern int tty_port_alloc_xmit_buf(struct tty_port *port);
extern void tty_port_free_xmit_buf(struct tty_port *port);
extern void tty_port_destroy(struct tty_port *port);