[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen master] ns16550: move PCI arrays next to the function using them
commit 510cdda6865868080f898543fc44044020efca71 Author: Jan Beulich <jbeulich@xxxxxxxx> AuthorDate: Tue Nov 24 11:27:49 2020 +0100 Commit: Jan Beulich <jbeulich@xxxxxxxx> CommitDate: Tue Nov 24 11:27:49 2020 +0100 ns16550: move PCI arrays next to the function using them Pure code motion; no functional change intended. Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx> Reviewed-by: Rahul Singh <rahul.singh@xxxxxxx> --- xen/drivers/char/ns16550.c | 1247 ++++++++++++++++++++++---------------------- 1 file changed, 625 insertions(+), 622 deletions(-) diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c index d8b52eb813..6f7fad6093 100644 --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -153,418 +153,112 @@ struct ns16550_config_param { unsigned int uart_offset; unsigned int first_offset; }; +#endif -/* - * Create lookup tables for specific devices. It is assumed that if - * the device found is MMIO, then you have indexed it here. Else, the - * driver does nothing for MMIO based devices. - */ -static const struct ns16550_config_param __initconst uart_param[] = { - [param_default] = { - .reg_width = 1, - .lsr_mask = UART_LSR_THRE, - .max_ports = 1, - }, - [param_trumanage] = { - .reg_shift = 2, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = (UART_LSR_THRE | UART_LSR_TEMT), - .mmio = 1, - .max_ports = 1, - }, - [param_oxford] = { - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .mmio = 1, - .max_ports = 1, /* It can do more, but we would need more custom code.*/ - }, - [param_oxford_2port] = { - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .mmio = 1, - .max_ports = 2, - }, - [param_pericom_1port] = { - .base_baud = 921600, - .uart_offset = 8, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .bar0 = 1, - .max_ports = 1, - }, - [param_pericom_2port] = { - .base_baud = 921600, - .uart_offset = 8, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .bar0 = 1, - .max_ports = 2, - }, - /* - * Of the two following ones, we can't really use all of their ports, - * unless ns16550_com[] would get grown. - */ - [param_pericom_4port] = { - .base_baud = 921600, - .uart_offset = 8, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .bar0 = 1, - .max_ports = 4, - }, - [param_pericom_8port] = { - .base_baud = 921600, - .uart_offset = 8, - .reg_width = 1, - .fifo_size = 16, - .lsr_mask = UART_LSR_THRE, - .bar0 = 1, - .max_ports = 8, - } -}; -static const struct ns16550_config __initconst uart_config[] = +static void ns16550_delayed_resume(void *data); + +static u8 ns_read_reg(struct ns16550 *uart, unsigned int reg) { - /* Broadcom TruManage device */ - { - .vendor_id = PCI_VENDOR_ID_BROADCOM, - .dev_id = 0x160a, - .param = param_trumanage, - }, - /* OXPCIe952 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc11b, - .param = param_oxford, - }, - /* OXPCIe952 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc11f, - .param = param_oxford, - }, - /* OXPCIe952 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc138, - .param = param_oxford, - }, - /* OXPCIe952 2 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc158, - .param = param_oxford_2port, - }, - /* OXPCIe952 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc13d, - .param = param_oxford, - }, - /* OXPCIe952 2 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc15d, - .param = param_oxford_2port, - }, - /* OXPCIe952 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc40b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc40f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ + void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); +#ifdef CONFIG_HAS_IOPORTS + if ( uart->remapped_io_base == NULL ) + return inb(uart->io_base + reg); +#endif + switch ( uart->reg_width ) { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc41b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ + case 1: + return readb(addr); + case 4: + return readl(addr); + default: + return 0xff; + } +} + +static void ns_write_reg(struct ns16550 *uart, unsigned int reg, u8 c) +{ + void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); +#ifdef CONFIG_HAS_IOPORTS + if ( uart->remapped_io_base == NULL ) + return outb(c, uart->io_base + reg); +#endif + switch ( uart->reg_width ) { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc41f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ + case 1: + writeb(c, addr); + break; + case 4: + writel(c, addr); + break; + default: + /* Ignored */ + break; + } +} + +static int ns16550_ioport_invalid(struct ns16550 *uart) +{ + return ns_read_reg(uart, UART_IER) == 0xff; +} + +static void handle_dw_usr_busy_quirk(struct ns16550 *uart) +{ + if ( uart->dw_usr_bsy && + (ns_read_reg(uart, UART_IIR) & UART_IIR_BSY) == UART_IIR_BSY ) { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc42b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ + /* DesignWare 8250 detects if LCR is written while the UART is + * busy and raises a "busy detect" interrupt. Read the UART + * Status Register to clear this state. + * + * Allwinner/sunxi UART hardware is similar to DesignWare 8250 + * and also contains a "busy detect" interrupt. So this quirk + * fix will also be used for Allwinner UART. + */ + ns_read_reg(uart, UART_USR); + } +} + +static void ns16550_interrupt( + int irq, void *dev_id, struct cpu_user_regs *regs) +{ + struct serial_port *port = dev_id; + struct ns16550 *uart = port->uart; + + uart->intr_works = 1; + + while ( !(ns_read_reg(uart, UART_IIR) & UART_IIR_NOINT) ) { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc42f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc43b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc43f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc44b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc44f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc45b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc45f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc46b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc46f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc47b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc47f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc48b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc48f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc49b, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc49f, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4ab, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4af, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4bb, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4bf, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4cb, - .param = param_oxford, - }, - /* OXPCIe200 1 Native UART */ - { - .vendor_id = PCI_VENDOR_ID_OXSEMI, - .dev_id = 0xc4cf, - .param = param_oxford, - }, - /* Pericom PI7C9X7951 Uno UART */ - { - .vendor_id = PCI_VENDOR_ID_PERICOM, - .dev_id = 0x7951, - .param = param_pericom_1port - }, - /* Pericom PI7C9X7952 Duo UART */ - { - .vendor_id = PCI_VENDOR_ID_PERICOM, - .dev_id = 0x7952, - .param = param_pericom_2port - }, - /* Pericom PI7C9X7954 Quad UART */ - { - .vendor_id = PCI_VENDOR_ID_PERICOM, - .dev_id = 0x7954, - .param = param_pericom_4port - }, - /* Pericom PI7C9X7958 Octal UART */ - { - .vendor_id = PCI_VENDOR_ID_PERICOM, - .dev_id = 0x7958, - .param = param_pericom_8port - } -}; -#endif - -static void ns16550_delayed_resume(void *data); - -static u8 ns_read_reg(struct ns16550 *uart, unsigned int reg) -{ - void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); -#ifdef CONFIG_HAS_IOPORTS - if ( uart->remapped_io_base == NULL ) - return inb(uart->io_base + reg); -#endif - switch ( uart->reg_width ) - { - case 1: - return readb(addr); - case 4: - return readl(addr); - default: - return 0xff; - } -} - -static void ns_write_reg(struct ns16550 *uart, unsigned int reg, u8 c) -{ - void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); -#ifdef CONFIG_HAS_IOPORTS - if ( uart->remapped_io_base == NULL ) - return outb(c, uart->io_base + reg); -#endif - switch ( uart->reg_width ) - { - case 1: - writeb(c, addr); - break; - case 4: - writel(c, addr); - break; - default: - /* Ignored */ - break; - } -} - -static int ns16550_ioport_invalid(struct ns16550 *uart) -{ - return ns_read_reg(uart, UART_IER) == 0xff; -} - -static void handle_dw_usr_busy_quirk(struct ns16550 *uart) -{ - if ( uart->dw_usr_bsy && - (ns_read_reg(uart, UART_IIR) & UART_IIR_BSY) == UART_IIR_BSY ) - { - /* DesignWare 8250 detects if LCR is written while the UART is - * busy and raises a "busy detect" interrupt. Read the UART - * Status Register to clear this state. - * - * Allwinner/sunxi UART hardware is similar to DesignWare 8250 - * and also contains a "busy detect" interrupt. So this quirk - * fix will also be used for Allwinner UART. - */ - ns_read_reg(uart, UART_USR); - } -} - -static void ns16550_interrupt( - int irq, void *dev_id, struct cpu_user_regs *regs) -{ - struct serial_port *port = dev_id; - struct ns16550 *uart = port->uart; - - uart->intr_works = 1; - - while ( !(ns_read_reg(uart, UART_IIR) & UART_IIR_NOINT) ) - { - u8 lsr = ns_read_reg(uart, UART_LSR); - - if ( (lsr & uart->lsr_mask) == uart->lsr_mask ) - serial_tx_interrupt(port, regs); - if ( lsr & UART_LSR_DR ) - serial_rx_interrupt(port, regs); - - /* A "busy-detect" condition is observed on Allwinner/sunxi UART - * after LCR is written during setup. It needs to be cleared at - * this point or UART_IIR_NOINT will never be set and this loop - * will continue forever. - * - * This state can be cleared by calling the dw_usr_busy quirk - * handler that resolves "busy-detect" for DesignWare uart. - */ - handle_dw_usr_busy_quirk(uart); - } -} - -/* Safe: ns16550_poll() runs as softirq so not reentrant on a given CPU. */ -static DEFINE_PER_CPU(struct serial_port *, poll_port); - -static void __ns16550_poll(struct cpu_user_regs *regs) -{ - struct serial_port *port = this_cpu(poll_port); - struct ns16550 *uart = port->uart; - - if ( uart->intr_works ) - return; /* Interrupts work - no more polling */ - - while ( ns_read_reg(uart, UART_LSR) & UART_LSR_DR ) + u8 lsr = ns_read_reg(uart, UART_LSR); + + if ( (lsr & uart->lsr_mask) == uart->lsr_mask ) + serial_tx_interrupt(port, regs); + if ( lsr & UART_LSR_DR ) + serial_rx_interrupt(port, regs); + + /* A "busy-detect" condition is observed on Allwinner/sunxi UART + * after LCR is written during setup. It needs to be cleared at + * this point or UART_IIR_NOINT will never be set and this loop + * will continue forever. + * + * This state can be cleared by calling the dw_usr_busy quirk + * handler that resolves "busy-detect" for DesignWare uart. + */ + handle_dw_usr_busy_quirk(uart); + } +} + +/* Safe: ns16550_poll() runs as softirq so not reentrant on a given CPU. */ +static DEFINE_PER_CPU(struct serial_port *, poll_port); + +static void __ns16550_poll(struct cpu_user_regs *regs) +{ + struct serial_port *port = this_cpu(poll_port); + struct ns16550 *uart = port->uart; + + if ( uart->intr_works ) + return; /* Interrupts work - no more polling */ + + while ( ns_read_reg(uart, UART_LSR) & UART_LSR_DR ) { if ( ns16550_ioport_invalid(uart) ) goto out; @@ -825,226 +519,534 @@ static void __init ns16550_init_postirq(struct serial_port *port) if ( uart->irq > 0 ) { - uart->irqaction.handler = ns16550_interrupt; - uart->irqaction.name = "ns16550"; - uart->irqaction.dev_id = port; - if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 ) - printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq); - } - - ns16550_setup_postirq(uart); -} - -static void ns16550_suspend(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - - stop_timer(&uart->timer); - -#ifdef CONFIG_HAS_PCI - if ( uart->bar ) - uart->cr = pci_conf_read16(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], - uart->ps_bdf[2]), PCI_COMMAND); -#endif -} - -static void _ns16550_resume(struct serial_port *port) -{ -#ifdef CONFIG_HAS_PCI - struct ns16550 *uart = port->uart; - - if ( uart->bar ) + uart->irqaction.handler = ns16550_interrupt; + uart->irqaction.name = "ns16550"; + uart->irqaction.dev_id = port; + if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 ) + printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq); + } + + ns16550_setup_postirq(uart); +} + +static void ns16550_suspend(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + + stop_timer(&uart->timer); + +#ifdef CONFIG_HAS_PCI + if ( uart->bar ) + uart->cr = pci_conf_read16(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], + uart->ps_bdf[2]), PCI_COMMAND); +#endif +} + +static void _ns16550_resume(struct serial_port *port) +{ +#ifdef CONFIG_HAS_PCI + struct ns16550 *uart = port->uart; + + if ( uart->bar ) + { + pci_conf_write32(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], + uart->ps_bdf[2]), + PCI_BASE_ADDRESS_0 + uart->bar_idx*4, uart->bar); + + /* If 64 bit BAR, write higher 32 bits to BAR+4 */ + if ( uart->bar & PCI_BASE_ADDRESS_MEM_TYPE_64 ) + pci_conf_write32(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], + uart->ps_bdf[2]), + PCI_BASE_ADDRESS_0 + (uart->bar_idx+1)*4, uart->bar64); + + pci_conf_write16(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], + uart->ps_bdf[2]), + PCI_COMMAND, uart->cr); + } +#endif + + ns16550_setup_preirq(port->uart); + ns16550_setup_postirq(port->uart); +} + +static int delayed_resume_tries; +static void ns16550_delayed_resume(void *data) +{ + struct serial_port *port = data; + struct ns16550 *uart = port->uart; + + if ( ns16550_ioport_invalid(port->uart) && delayed_resume_tries-- ) + set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); + else + _ns16550_resume(port); +} + +static void ns16550_resume(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + + /* + * Check for ioport access, before fully resuming operation. + * On some systems, there is a SuperIO card that provides + * this legacy ioport on the LPC bus. + * + * We need to wait for dom0's ACPI processing to run the proper + * AML to re-initialize the chip, before we can use the card again. + * + * This may cause a small amount of garbage to be written + * to the serial log while we wait patiently for that AML to + * be executed. However, this is preferable to spinning in an + * infinite loop, as seen on a Lenovo T430, when serial was enabled. + */ + if ( ns16550_ioport_invalid(uart) ) + { + delayed_resume_tries = RESUME_RETRIES; + set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); + } + else + _ns16550_resume(port); +} + +static void __init ns16550_endboot(struct serial_port *port) +{ +#ifdef CONFIG_HAS_IOPORTS + struct ns16550 *uart = port->uart; + int rv; + + if ( uart->remapped_io_base ) + return; + rv = ioports_deny_access(hardware_domain, uart->io_base, uart->io_base + 7); + if ( rv != 0 ) + BUG(); +#endif +} + +static int __init ns16550_irq(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + return ((uart->irq > 0) ? uart->irq : -1); +} + +static void ns16550_start_tx(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + u8 ier = ns_read_reg(uart, UART_IER); + + /* Unmask transmit holding register empty interrupt if currently masked. */ + if ( !(ier & UART_IER_ETHREI) ) + ns_write_reg(uart, UART_IER, ier | UART_IER_ETHREI); +} + +static void ns16550_stop_tx(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + u8 ier = ns_read_reg(uart, UART_IER); + + /* Mask off transmit holding register empty interrupt if currently unmasked. */ + if ( ier & UART_IER_ETHREI ) + ns_write_reg(uart, UART_IER, ier & ~UART_IER_ETHREI); +} + +#ifdef CONFIG_ARM +static const struct vuart_info *ns16550_vuart_info(struct serial_port *port) +{ + struct ns16550 *uart = port->uart; + + return &uart->vuart; +} +#endif + +static struct uart_driver __read_mostly ns16550_driver = { + .init_preirq = ns16550_init_preirq, + .init_irq = ns16550_init_irq, + .init_postirq = ns16550_init_postirq, + .endboot = ns16550_endboot, + .suspend = ns16550_suspend, + .resume = ns16550_resume, + .tx_ready = ns16550_tx_ready, + .putc = ns16550_putc, + .getc = ns16550_getc, + .irq = ns16550_irq, + .start_tx = ns16550_start_tx, + .stop_tx = ns16550_stop_tx, +#ifdef CONFIG_ARM + .vuart_info = ns16550_vuart_info, +#endif +}; + +static int __init parse_parity_char(int c) +{ + switch ( c ) + { + case 'n': + return UART_PARITY_NONE; + case 'o': + return UART_PARITY_ODD; + case 'e': + return UART_PARITY_EVEN; + case 'm': + return UART_PARITY_MARK; + case 's': + return UART_PARITY_SPACE; + } + return 0; +} + +static int __init check_existence(struct ns16550 *uart) +{ + unsigned char status, scratch, scratch2, scratch3; + +#ifdef CONFIG_HAS_IOPORTS + /* + * We can't poke MMIO UARTs until they get I/O remapped later. Assume that + * if we're getting MMIO UARTs, the arch code knows what it's doing. + */ + if ( uart->io_base >= 0x10000 ) + return 1; +#else + return 1; /* Everything is MMIO */ +#endif + +#ifdef CONFIG_HAS_PCI + pci_serial_early_init(uart); +#endif + + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + */ + scratch = ns_read_reg(uart, UART_IER); + ns_write_reg(uart, UART_IER, 0); + + /* + * 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 = ns_read_reg(uart, UART_IER) & 0x0f; + ns_write_reg(uart,UART_IER, 0x0F); + scratch3 = ns_read_reg(uart, UART_IER) & 0x0f; + ns_write_reg(uart, UART_IER, scratch); + if ( (scratch2 != 0) || (scratch3 != 0x0F) ) + return 0; + + /* + * Check to see if a UART is really there. + * Use loopback test mode. + */ + ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | 0x0A); + status = ns_read_reg(uart, UART_MSR) & 0xF0; + return (status == 0x90); +} + +#ifdef CONFIG_HAS_PCI + +/* + * Create lookup tables for specific devices. It is assumed that if + * the device found is MMIO, then you have indexed it here. Else, the + * driver does nothing for MMIO based devices. + */ +static const struct ns16550_config_param __initconst uart_param[] = { + [param_default] = { + .reg_width = 1, + .lsr_mask = UART_LSR_THRE, + .max_ports = 1, + }, + [param_trumanage] = { + .reg_shift = 2, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = (UART_LSR_THRE | UART_LSR_TEMT), + .mmio = 1, + .max_ports = 1, + }, + [param_oxford] = { + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .mmio = 1, + .max_ports = 1, /* It can do more, but we would need more custom code.*/ + }, + [param_oxford_2port] = { + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .mmio = 1, + .max_ports = 2, + }, + [param_pericom_1port] = { + .base_baud = 921600, + .uart_offset = 8, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .bar0 = 1, + .max_ports = 1, + }, + [param_pericom_2port] = { + .base_baud = 921600, + .uart_offset = 8, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .bar0 = 1, + .max_ports = 2, + }, + /* + * Of the two following ones, we can't really use all of their ports, + * unless ns16550_com[] would get grown. + */ + [param_pericom_4port] = { + .base_baud = 921600, + .uart_offset = 8, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .bar0 = 1, + .max_ports = 4, + }, + [param_pericom_8port] = { + .base_baud = 921600, + .uart_offset = 8, + .reg_width = 1, + .fifo_size = 16, + .lsr_mask = UART_LSR_THRE, + .bar0 = 1, + .max_ports = 8, + } +}; + +static const struct ns16550_config __initconst uart_config[] = +{ + /* Broadcom TruManage device */ + { + .vendor_id = PCI_VENDOR_ID_BROADCOM, + .dev_id = 0x160a, + .param = param_trumanage, + }, + /* OXPCIe952 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc11b, + .param = param_oxford, + }, + /* OXPCIe952 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc11f, + .param = param_oxford, + }, + /* OXPCIe952 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc138, + .param = param_oxford, + }, + /* OXPCIe952 2 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc158, + .param = param_oxford_2port, + }, + /* OXPCIe952 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc13d, + .param = param_oxford, + }, + /* OXPCIe952 2 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc15d, + .param = param_oxford_2port, + }, + /* OXPCIe952 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc40b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc40f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc41b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc41f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc42b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc42f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc43b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc43f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc44b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc44f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc45b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc45f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc46b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc46f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc47b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc47f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc48b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc48f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc49b, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc49f, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4ab, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4af, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4bb, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ + { + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4bf, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ { - pci_conf_write32(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], - uart->ps_bdf[2]), - PCI_BASE_ADDRESS_0 + uart->bar_idx*4, uart->bar); - - /* If 64 bit BAR, write higher 32 bits to BAR+4 */ - if ( uart->bar & PCI_BASE_ADDRESS_MEM_TYPE_64 ) - pci_conf_write32(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], - uart->ps_bdf[2]), - PCI_BASE_ADDRESS_0 + (uart->bar_idx+1)*4, uart->bar64); - - pci_conf_write16(PCI_SBDF(0, uart->ps_bdf[0], uart->ps_bdf[1], - uart->ps_bdf[2]), - PCI_COMMAND, uart->cr); - } -#endif - - ns16550_setup_preirq(port->uart); - ns16550_setup_postirq(port->uart); -} - -static int delayed_resume_tries; -static void ns16550_delayed_resume(void *data) -{ - struct serial_port *port = data; - struct ns16550 *uart = port->uart; - - if ( ns16550_ioport_invalid(port->uart) && delayed_resume_tries-- ) - set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); - else - _ns16550_resume(port); -} - -static void ns16550_resume(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - - /* - * Check for ioport access, before fully resuming operation. - * On some systems, there is a SuperIO card that provides - * this legacy ioport on the LPC bus. - * - * We need to wait for dom0's ACPI processing to run the proper - * AML to re-initialize the chip, before we can use the card again. - * - * This may cause a small amount of garbage to be written - * to the serial log while we wait patiently for that AML to - * be executed. However, this is preferable to spinning in an - * infinite loop, as seen on a Lenovo T430, when serial was enabled. - */ - if ( ns16550_ioport_invalid(uart) ) + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4cb, + .param = param_oxford, + }, + /* OXPCIe200 1 Native UART */ { - delayed_resume_tries = RESUME_RETRIES; - set_timer(&uart->resume_timer, NOW() + RESUME_DELAY); - } - else - _ns16550_resume(port); -} - -static void __init ns16550_endboot(struct serial_port *port) -{ -#ifdef CONFIG_HAS_IOPORTS - struct ns16550 *uart = port->uart; - int rv; - - if ( uart->remapped_io_base ) - return; - rv = ioports_deny_access(hardware_domain, uart->io_base, uart->io_base + 7); - if ( rv != 0 ) - BUG(); -#endif -} - -static int __init ns16550_irq(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - return ((uart->irq > 0) ? uart->irq : -1); -} - -static void ns16550_start_tx(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - u8 ier = ns_read_reg(uart, UART_IER); - - /* Unmask transmit holding register empty interrupt if currently masked. */ - if ( !(ier & UART_IER_ETHREI) ) - ns_write_reg(uart, UART_IER, ier | UART_IER_ETHREI); -} - -static void ns16550_stop_tx(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - u8 ier = ns_read_reg(uart, UART_IER); - - /* Mask off transmit holding register empty interrupt if currently unmasked. */ - if ( ier & UART_IER_ETHREI ) - ns_write_reg(uart, UART_IER, ier & ~UART_IER_ETHREI); -} - -#ifdef CONFIG_ARM -static const struct vuart_info *ns16550_vuart_info(struct serial_port *port) -{ - struct ns16550 *uart = port->uart; - - return &uart->vuart; -} -#endif - -static struct uart_driver __read_mostly ns16550_driver = { - .init_preirq = ns16550_init_preirq, - .init_irq = ns16550_init_irq, - .init_postirq = ns16550_init_postirq, - .endboot = ns16550_endboot, - .suspend = ns16550_suspend, - .resume = ns16550_resume, - .tx_ready = ns16550_tx_ready, - .putc = ns16550_putc, - .getc = ns16550_getc, - .irq = ns16550_irq, - .start_tx = ns16550_start_tx, - .stop_tx = ns16550_stop_tx, -#ifdef CONFIG_ARM - .vuart_info = ns16550_vuart_info, -#endif -}; - -static int __init parse_parity_char(int c) -{ - switch ( c ) + .vendor_id = PCI_VENDOR_ID_OXSEMI, + .dev_id = 0xc4cf, + .param = param_oxford, + }, + /* Pericom PI7C9X7951 Uno UART */ { - case 'n': - return UART_PARITY_NONE; - case 'o': - return UART_PARITY_ODD; - case 'e': - return UART_PARITY_EVEN; - case 'm': - return UART_PARITY_MARK; - case 's': - return UART_PARITY_SPACE; + .vendor_id = PCI_VENDOR_ID_PERICOM, + .dev_id = 0x7951, + .param = param_pericom_1port + }, + /* Pericom PI7C9X7952 Duo UART */ + { + .vendor_id = PCI_VENDOR_ID_PERICOM, + .dev_id = 0x7952, + .param = param_pericom_2port + }, + /* Pericom PI7C9X7954 Quad UART */ + { + .vendor_id = PCI_VENDOR_ID_PERICOM, + .dev_id = 0x7954, + .param = param_pericom_4port + }, + /* Pericom PI7C9X7958 Octal UART */ + { + .vendor_id = PCI_VENDOR_ID_PERICOM, + .dev_id = 0x7958, + .param = param_pericom_8port } - return 0; -} - -static int __init check_existence(struct ns16550 *uart) -{ - unsigned char status, scratch, scratch2, scratch3; - -#ifdef CONFIG_HAS_IOPORTS - /* - * We can't poke MMIO UARTs until they get I/O remapped later. Assume that - * if we're getting MMIO UARTs, the arch code knows what it's doing. - */ - if ( uart->io_base >= 0x10000 ) - return 1; -#else - return 1; /* Everything is MMIO */ -#endif - -#ifdef CONFIG_HAS_PCI - pci_serial_early_init(uart); -#endif - - /* - * Do a simple existence test first; if we fail this, - * there's no point trying anything else. - */ - scratch = ns_read_reg(uart, UART_IER); - ns_write_reg(uart, UART_IER, 0); - - /* - * 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 = ns_read_reg(uart, UART_IER) & 0x0f; - ns_write_reg(uart,UART_IER, 0x0F); - scratch3 = ns_read_reg(uart, UART_IER) & 0x0f; - ns_write_reg(uart, UART_IER, scratch); - if ( (scratch2 != 0) || (scratch3 != 0x0F) ) - return 0; - - /* - * Check to see if a UART is really there. - * Use loopback test mode. - */ - ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | 0x0A); - status = ns_read_reg(uart, UART_MSR) & 0xF0; - return (status == 0x90); -} +}; -#ifdef CONFIG_HAS_PCI static int __init pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx) { @@ -1211,7 +1213,8 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx) return 0; } -#endif + +#endif /* CONFIG_HAS_PCI */ /* * Used to parse name value pairs and return which value it is along with -- generated by git-patchbot for /home/xen/git/xen.git#master
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |