Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7e1b405 authored by Aaron Sierra's avatar Aaron Sierra Committed by Greg Kroah-Hartman
Browse files

tty: serial: exar: Relocate sleep wake-up handling



Exar sleep wake-up handling has been done on a per-channel basis by
virtue of INT0 being accessible from each channel's address space. I
believe this was initially done out of necessity, but now that Exar
devices have their own driver, we can do things more efficiently by
registering a dedicated INT0 handler at the PCI device level.

I see this change providing the following benefits:

    1. If more than one port is active, eliminates the redundant bus
       cycles for reading INT0 on every interrupt.
    2. This note associated with hooking in the per-channel handler in
       8250_port.c is resolved:
        /* Fixme: probably not the best place for this */

Cc: Matt Schulte <matts@commtech-fastcom.com>
Signed-off-by: default avatarAaron Sierra <asierra@xes-inc.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b027e229
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#define PCI_DEVICE_ID_EXAR_XR17V4358		0x4358
#define PCI_DEVICE_ID_EXAR_XR17V8358		0x8358

#define UART_EXAR_INT0		0x80
#define UART_EXAR_8XMODE	0x88	/* 8X sampling rate select */

#define UART_EXAR_FCTR		0x08	/* Feature Control Register */
@@ -121,6 +122,7 @@ struct exar8250_board {
struct exar8250 {
	unsigned int		nr;
	struct exar8250_board	*board;
	void __iomem		*virt;
	int			line[0];
};

@@ -131,12 +133,9 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev,
	const struct exar8250_board *board = priv->board;
	unsigned int bar = 0;

	if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0))
		return -ENOMEM;

	port->port.iotype = UPIO_MEM;
	port->port.mapbase = pci_resource_start(pcidev, bar) + offset;
	port->port.membase = pcim_iomap_table(pcidev)[bar] + offset;
	port->port.membase = priv->virt + offset;
	port->port.regshift = board->reg_shift;

	return 0;
@@ -420,6 +419,25 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev)
	port->port.private_data = NULL;
}

/*
 * These Exar UARTs have an extra interrupt indicator that could fire for a
 * few interrupts that are not presented/cleared through IIR.  One of which is
 * a wakeup interrupt when coming out of sleep.  These interrupts are only
 * cleared by reading global INT0 or INT1 registers as interrupts are
 * associated with channel 0. The INT[3:0] registers _are_ accessible from each
 * channel's address space, but for the sake of bus efficiency we register a
 * dedicated handler at the PCI device level to handle them.
 */
static irqreturn_t exar_misc_handler(int irq, void *data)
{
	struct exar8250 *priv = data;

	/* Clear all PCI interrupts by reading INT0. No effect on IIR */
	ioread8(priv->virt + UART_EXAR_INT0);

	return IRQ_HANDLED;
}

static int
exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
{
@@ -448,6 +466,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
		return -ENOMEM;

	priv->board = board;
	priv->virt = pcim_iomap(pcidev, bar, 0);
	if (!priv->virt)
		return -ENOMEM;

	pci_set_master(pcidev);

@@ -461,6 +482,11 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
	uart.port.irq = pci_irq_vector(pcidev, 0);
	uart.port.dev = &pcidev->dev;

	rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler,
			 IRQF_SHARED, "exar_uart", priv);
	if (rc)
		return rc;

	for (i = 0; i < nr_ports && i < maxnr; i++) {
		rc = board->setup(priv, pcidev, &uart, i);
		if (rc) {
+0 −26
Original line number Diff line number Diff line
@@ -441,7 +441,6 @@ static void io_serial_out(struct uart_port *p, int offset, int value)
}

static int serial8250_default_handle_irq(struct uart_port *port);
static int exar_handle_irq(struct uart_port *port);

static void set_io_from_upio(struct uart_port *p)
{
@@ -1883,26 +1882,6 @@ static int serial8250_default_handle_irq(struct uart_port *port)
	return ret;
}

/*
 * These Exar UARTs have an extra interrupt indicator that could
 * fire for a few unimplemented interrupts.  One of which is a
 * wakeup event when coming out of sleep.  Put this here just
 * to be on the safe side that these interrupts don't go unhandled.
 */
static int exar_handle_irq(struct uart_port *port)
{
	unsigned int iir = serial_port_in(port, UART_IIR);
	int ret = 0;

	if (((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) &&
	    serial_port_in(port, UART_EXAR_INT0) != 0)
		ret = 1;

	ret |= serial8250_handle_irq(port, iir);

	return ret;
}

/*
 * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP
 * have a programmable TX threshold that triggers the THRE interrupt in
@@ -3067,11 +3046,6 @@ static void serial8250_config_port(struct uart_port *port, int flags)
	if (port->type == PORT_UNKNOWN)
		serial8250_release_std_resource(up);

	/* Fixme: probably not the best place for this */
	if ((port->type == PORT_XR17V35X) ||
	   (port->type == PORT_XR17D15X))
		port->handle_irq = exar_handle_irq;

	register_dev_spec_attr_grp(up);
	up->fcr = uart_config[up->port.type].fcr;
}