Skip to content

Commit c7e1b40

Browse files
Aaron Sierragregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Aaron Sierra <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b027e22 commit c7e1b40

File tree

2 files changed

+30
-30
lines changed

2 files changed

+30
-30
lines changed

drivers/tty/serial/8250/8250_exar.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358
3535
#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358
3636

37+
#define UART_EXAR_INT0 0x80
3738
#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */
3839

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

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

134-
if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0))
135-
return -ENOMEM;
136-
137136
port->port.iotype = UPIO_MEM;
138137
port->port.mapbase = pci_resource_start(pcidev, bar) + offset;
139-
port->port.membase = pcim_iomap_table(pcidev)[bar] + offset;
138+
port->port.membase = priv->virt + offset;
140139
port->port.regshift = board->reg_shift;
141140

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

422+
/*
423+
* These Exar UARTs have an extra interrupt indicator that could fire for a
424+
* few interrupts that are not presented/cleared through IIR. One of which is
425+
* a wakeup interrupt when coming out of sleep. These interrupts are only
426+
* cleared by reading global INT0 or INT1 registers as interrupts are
427+
* associated with channel 0. The INT[3:0] registers _are_ accessible from each
428+
* channel's address space, but for the sake of bus efficiency we register a
429+
* dedicated handler at the PCI device level to handle them.
430+
*/
431+
static irqreturn_t exar_misc_handler(int irq, void *data)
432+
{
433+
struct exar8250 *priv = data;
434+
435+
/* Clear all PCI interrupts by reading INT0. No effect on IIR */
436+
ioread8(priv->virt + UART_EXAR_INT0);
437+
438+
return IRQ_HANDLED;
439+
}
440+
423441
static int
424442
exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
425443
{
@@ -448,6 +466,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
448466
return -ENOMEM;
449467

450468
priv->board = board;
469+
priv->virt = pcim_iomap(pcidev, bar, 0);
470+
if (!priv->virt)
471+
return -ENOMEM;
451472

452473
pci_set_master(pcidev);
453474

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

485+
rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler,
486+
IRQF_SHARED, "exar_uart", priv);
487+
if (rc)
488+
return rc;
489+
464490
for (i = 0; i < nr_ports && i < maxnr; i++) {
465491
rc = board->setup(priv, pcidev, &uart, i);
466492
if (rc) {

drivers/tty/serial/8250/8250_port.c

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,6 @@ static void io_serial_out(struct uart_port *p, int offset, int value)
441441
}
442442

443443
static int serial8250_default_handle_irq(struct uart_port *port);
444-
static int exar_handle_irq(struct uart_port *port);
445444

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

1886-
/*
1887-
* These Exar UARTs have an extra interrupt indicator that could
1888-
* fire for a few unimplemented interrupts. One of which is a
1889-
* wakeup event when coming out of sleep. Put this here just
1890-
* to be on the safe side that these interrupts don't go unhandled.
1891-
*/
1892-
static int exar_handle_irq(struct uart_port *port)
1893-
{
1894-
unsigned int iir = serial_port_in(port, UART_IIR);
1895-
int ret = 0;
1896-
1897-
if (((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) &&
1898-
serial_port_in(port, UART_EXAR_INT0) != 0)
1899-
ret = 1;
1900-
1901-
ret |= serial8250_handle_irq(port, iir);
1902-
1903-
return ret;
1904-
}
1905-
19061885
/*
19071886
* Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP
19081887
* 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)
30673046
if (port->type == PORT_UNKNOWN)
30683047
serial8250_release_std_resource(up);
30693048

3070-
/* Fixme: probably not the best place for this */
3071-
if ((port->type == PORT_XR17V35X) ||
3072-
(port->type == PORT_XR17D15X))
3073-
port->handle_irq = exar_handle_irq;
3074-
30753049
register_dev_spec_attr_grp(up);
30763050
up->fcr = uart_config[up->port.type].fcr;
30773051
}

0 commit comments

Comments
 (0)