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;
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)
{
return 0;
}
-#endif
+
+#endif /* CONFIG_HAS_PCI */
/*
* Used to parse name value pairs and return which value it is along with