mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	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: Aaron Sierra <asierra@xes-inc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									b027e2298b
								
							
						
					
					
						commit
						c7e1b40590
					
				| @ -34,6 +34,7 @@ | |||||||
| #define PCI_DEVICE_ID_EXAR_XR17V4358		0x4358 | #define PCI_DEVICE_ID_EXAR_XR17V4358		0x4358 | ||||||
| #define PCI_DEVICE_ID_EXAR_XR17V8358		0x8358 | #define PCI_DEVICE_ID_EXAR_XR17V8358		0x8358 | ||||||
| 
 | 
 | ||||||
|  | #define UART_EXAR_INT0		0x80 | ||||||
| #define UART_EXAR_8XMODE	0x88	/* 8X sampling rate select */ | #define UART_EXAR_8XMODE	0x88	/* 8X sampling rate select */ | ||||||
| 
 | 
 | ||||||
| #define UART_EXAR_FCTR		0x08	/* Feature Control Register */ | #define UART_EXAR_FCTR		0x08	/* Feature Control Register */ | ||||||
| @ -121,6 +122,7 @@ struct exar8250_board { | |||||||
| struct exar8250 { | struct exar8250 { | ||||||
| 	unsigned int		nr; | 	unsigned int		nr; | ||||||
| 	struct exar8250_board	*board; | 	struct exar8250_board	*board; | ||||||
|  | 	void __iomem		*virt; | ||||||
| 	int			line[0]; | 	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; | 	const struct exar8250_board *board = priv->board; | ||||||
| 	unsigned int bar = 0; | 	unsigned int bar = 0; | ||||||
| 
 | 
 | ||||||
| 	if (!pcim_iomap_table(pcidev)[bar] && !pcim_iomap(pcidev, bar, 0)) |  | ||||||
| 		return -ENOMEM; |  | ||||||
| 
 |  | ||||||
| 	port->port.iotype = UPIO_MEM; | 	port->port.iotype = UPIO_MEM; | ||||||
| 	port->port.mapbase = pci_resource_start(pcidev, bar) + offset; | 	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; | 	port->port.regshift = board->reg_shift; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| @ -420,6 +419,25 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev) | |||||||
| 	port->port.private_data = NULL; | 	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 | static int | ||||||
| exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) | 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; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	priv->board = board; | 	priv->board = board; | ||||||
|  | 	priv->virt = pcim_iomap(pcidev, bar, 0); | ||||||
|  | 	if (!priv->virt) | ||||||
|  | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	pci_set_master(pcidev); | 	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.irq = pci_irq_vector(pcidev, 0); | ||||||
| 	uart.port.dev = &pcidev->dev; | 	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++) { | 	for (i = 0; i < nr_ports && i < maxnr; i++) { | ||||||
| 		rc = board->setup(priv, pcidev, &uart, i); | 		rc = board->setup(priv, pcidev, &uart, i); | ||||||
| 		if (rc) { | 		if (rc) { | ||||||
|  | |||||||
| @ -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 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) | 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; | 	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 |  * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP | ||||||
|  * have a programmable TX threshold that triggers the THRE interrupt in |  * 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) | 	if (port->type == PORT_UNKNOWN) | ||||||
| 		serial8250_release_std_resource(up); | 		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); | 	register_dev_spec_attr_grp(up); | ||||||
| 	up->fcr = uart_config[up->port.type].fcr; | 	up->fcr = uart_config[up->port.type].fcr; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Aaron Sierra
						Aaron Sierra