/****************************************************************************** * ns16550.c * * Driver for 16550-series UARTs. This driver is to be kept within Xen as * it permits debugging of seriously-toasted machines (e.g., in situations * where a device driver within a guest OS would be inaccessible). * * Copyright (c) 2003-2005, K A Fraser */ #include #include #include #include #include #include #include #include #ifdef HAS_PCI #include #include #endif #include #include #include #ifdef HAS_DEVICE_TREE #include #endif #ifdef CONFIG_X86 #include #endif /* * Configure serial port with a string: * [/][,DPS[,[,[,[,]]]]]. * The tail of the string can be omitted if platform defaults are sufficient. * If the baud rate is pre-configured, perhaps by a bootloader, then 'auto' * can be specified in place of a numeric baud rate. Polled mode is specified * by requesting irq 0. */ static char __initdata opt_com1[30] = ""; static char __initdata opt_com2[30] = ""; string_param("com1", opt_com1); string_param("com2", opt_com2); static struct ns16550 { int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq; u64 io_base; /* I/O port or memory-mapped I/O address. */ u32 io_size; int reg_shift; /* Bits to shift register offset by */ int reg_width; /* Size of access to use, the registers * themselves are still bytes */ char __iomem *remapped_io_base; /* Remapped virtual address of MMIO. */ /* UART with IRQ line: interrupt-driven I/O. */ struct irqaction irqaction; #ifdef CONFIG_ARM struct vuart_info vuart; #endif /* UART with no IRQ line: periodically-polled I/O. */ struct timer timer; struct timer resume_timer; unsigned int timeout_ms; bool_t intr_works; bool_t dw_usr_bsy; #ifdef HAS_PCI /* PCI card parameters. */ unsigned int pb_bdf[3]; /* pci bridge BDF */ unsigned int ps_bdf[3]; /* pci serial port BDF */ bool_t pb_bdf_enable; /* if =1, pb-bdf effective, port behind bridge */ bool_t ps_bdf_enable; /* if =1, ps_bdf effective, port on pci card */ u32 bar; u16 cr; u8 bar_idx; #endif #ifdef HAS_DEVICE_TREE struct dt_irq dt_irq; #endif } ns16550_com[2] = { { 0 } }; static void ns16550_delayed_resume(void *data); static char ns_read_reg(struct ns16550 *uart, int reg) { void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); #ifdef 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, int reg, char c) { void __iomem *addr = uart->remapped_io_base + (reg << uart->reg_shift); #ifdef 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 (unsigned char)ns_read_reg(uart, UART_IER) == 0xff; } 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) ) { char lsr = ns_read_reg(uart, UART_LSR); if ( lsr & UART_LSR_THRE ) serial_tx_interrupt(port, regs); if ( lsr & UART_LSR_DR ) serial_rx_interrupt(port, regs); } } /* 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; serial_rx_interrupt(port, regs); } if ( ns_read_reg(uart, UART_LSR) & UART_LSR_THRE ) serial_tx_interrupt(port, regs); out: set_timer(&uart->timer, NOW() + MILLISECS(uart->timeout_ms)); } static void ns16550_poll(void *data) { this_cpu(poll_port) = data; #ifdef run_in_exception_handler run_in_exception_handler(__ns16550_poll); #else __ns16550_poll(guest_cpu_user_regs()); #endif } static int ns16550_tx_ready(struct serial_port *port) { struct ns16550 *uart = port->uart; if ( ns16550_ioport_invalid(uart) ) return -EIO; return ns_read_reg(uart, UART_LSR) & UART_LSR_THRE ? uart->fifo_size : 0; } static void ns16550_putc(struct serial_port *port, char c) { struct ns16550 *uart = port->uart; ns_write_reg(uart, UART_THR, c); } static int ns16550_getc(struct serial_port *port, char *pc) { struct ns16550 *uart = port->uart; if ( ns16550_ioport_invalid(uart) || !(ns_read_reg(uart, UART_LSR) & UART_LSR_DR) ) return 0; *pc = ns_read_reg(uart, UART_RBR); return 1; } static void pci_serial_early_init(struct ns16550 *uart) { #ifdef HAS_PCI if ( !uart->ps_bdf_enable || uart->io_base >= 0x10000 ) return; if ( uart->pb_bdf_enable ) pci_conf_write16(0, uart->pb_bdf[0], uart->pb_bdf[1], uart->pb_bdf[2], PCI_IO_BASE, (uart->io_base & 0xF000) | ((uart->io_base & 0xF000) >> 8)); pci_conf_write32(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], PCI_BASE_ADDRESS_0, uart->io_base | PCI_BASE_ADDRESS_SPACE_IO); pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], PCI_COMMAND, PCI_COMMAND_IO); #endif } static void ns16550_setup_preirq(struct ns16550 *uart) { unsigned char lcr; unsigned int divisor; uart->intr_works = 0; pci_serial_early_init(uart); lcr = (uart->data_bits - 5) | ((uart->stop_bits - 1) << 2) | uart->parity; /* No interrupts. */ ns_write_reg(uart, UART_IER, 0); 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. */ ns_read_reg(uart, UART_USR); } /* Line control and baud-rate generator. */ ns_write_reg(uart, UART_LCR, lcr | UART_LCR_DLAB); if ( uart->baud != BAUD_AUTO ) { /* Baud rate specified: program it into the divisor latch. */ divisor = uart->clock_hz / (uart->baud << 4); ns_write_reg(uart, UART_DLL, (char)divisor); ns_write_reg(uart, UART_DLM, (char)(divisor >> 8)); } else { /* Baud rate already set: read it out from the divisor latch. */ divisor = ns_read_reg(uart, UART_DLL); divisor |= ns_read_reg(uart, UART_DLM) << 8; uart->baud = uart->clock_hz / (divisor << 4); } ns_write_reg(uart, UART_LCR, lcr); /* No flow ctrl: DTR and RTS are both wedged high to keep remote happy. */ ns_write_reg(uart, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); /* Enable and clear the FIFOs. Set a large trigger threshold. */ ns_write_reg(uart, UART_FCR, UART_FCR_ENABLE | UART_FCR_CLRX | UART_FCR_CLTX | UART_FCR_TRG14); } static void __init ns16550_init_preirq(struct serial_port *port) { struct ns16550 *uart = port->uart; #ifdef HAS_IOPORTS /* I/O ports are distinguished by their size (16 bits). */ if ( uart->io_base >= 0x10000 ) #endif { #ifdef CONFIG_X86 enum fixed_addresses idx = FIX_COM_BEGIN + (uart - ns16550_com); set_fixmap_nocache(idx, uart->io_base); uart->remapped_io_base = (void __iomem *)fix_to_virt(idx); uart->remapped_io_base += uart->io_base & ~PAGE_MASK; #else uart->remapped_io_base = (char *)ioremap(uart->io_base, uart->io_size); #endif } ns16550_setup_preirq(uart); /* Check this really is a 16550+. Otherwise we have no FIFOs. */ if ( ((ns_read_reg(uart, UART_IIR) & 0xc0) == 0xc0) && ((ns_read_reg(uart, UART_FCR) & UART_FCR_TRG14) == UART_FCR_TRG14) ) uart->fifo_size = 16; } static void ns16550_setup_postirq(struct ns16550 *uart) { if ( uart->irq > 0 ) { /* Master interrupt enable; also keep DTR/RTS asserted. */ ns_write_reg(uart, UART_MCR, UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS); /* Enable receive and transmit interrupts. */ ns_write_reg(uart, UART_IER, UART_IER_ERDAI | UART_IER_ETHREI); } if ( uart->irq >= 0 ) set_timer(&uart->timer, NOW() + MILLISECS(uart->timeout_ms)); } static void __init ns16550_init_postirq(struct serial_port *port) { struct ns16550 *uart = port->uart; int rc, bits; if ( uart->irq < 0 ) return; serial_async_transmit(port); init_timer(&uart->timer, ns16550_poll, port, 0); init_timer(&uart->resume_timer, ns16550_delayed_resume, port, 0); /* Calculate time to fill RX FIFO and/or empty TX FIFO for polling. */ bits = uart->data_bits + uart->stop_bits + !!uart->parity; uart->timeout_ms = max_t( unsigned int, 1, (bits * uart->fifo_size * 1000) / uart->baud); if ( uart->irq > 0 ) { uart->irqaction.handler = ns16550_interrupt; uart->irqaction.name = "ns16550"; uart->irqaction.dev_id = port; #ifdef HAS_DEVICE_TREE if ( (rc = setup_dt_irq(&uart->dt_irq, &uart->irqaction)) != 0 ) printk("ERROR: Failed to allocate ns16550 DT IRQ.\n"); #else if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq); #endif } ns16550_setup_postirq(uart); #ifdef HAS_PCI if ( uart->bar || uart->ps_bdf_enable ) pci_hide_device(uart->ps_bdf[0], PCI_DEVFN(uart->ps_bdf[1], uart->ps_bdf[2])); #endif } static void ns16550_suspend(struct serial_port *port) { struct ns16550 *uart = port->uart; stop_timer(&uart->timer); #ifdef HAS_PCI if ( uart->bar ) uart->cr = pci_conf_read16(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 HAS_PCI struct ns16550 *uart = port->uart; if ( uart->bar ) { pci_conf_write32(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2], PCI_BASE_ADDRESS_0 + uart->bar_idx*4, uart->bar); pci_conf_write16(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 HAS_IOPORTS struct ns16550 *uart = port->uart; if ( uart->remapped_io_base ) return; if ( ioports_deny_access(dom0, uart->io_base, uart->io_base + 7) != 0 ) BUG(); #endif } static int __init ns16550_irq(struct serial_port *port) { struct ns16550 *uart = port->uart; return ((uart->irq > 0) ? uart->irq : -1); } #ifdef HAS_DEVICE_TREE static const struct dt_irq __init *ns16550_dt_irq(struct serial_port *port) { struct ns16550 *uart = port->uart; return &uart->dt_irq; } #endif #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_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, #ifdef HAS_DEVICE_TREE .dt_irq_get = ns16550_dt_irq, #endif #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 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 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 HAS_PCI static int pci_uart_config (struct ns16550 *uart, int skip_amt, int bar_idx) { uint32_t bar, len; int b, d, f, nextf; /* NB. Start at bus 1 to avoid AMT: a plug-in card cannot be on bus 0. */ for ( b = skip_amt ? 1 : 0; b < 0x100; b++ ) { for ( d = 0; d < 0x20; d++ ) { for ( f = 0; f < 8; f = nextf ) { nextf = (f || (pci_conf_read16(0, b, d, f, PCI_HEADER_TYPE) & 0x80)) ? f + 1 : 8; switch ( pci_conf_read16(0, b, d, f, PCI_CLASS_DEVICE) ) { case 0x0700: /* single port serial */ case 0x0702: /* multi port serial */ case 0x0780: /* other (e.g serial+parallel) */ break; case 0xffff: if ( !f ) nextf = 8; /* fall through */ default: continue; } bar = pci_conf_read32(0, b, d, f, PCI_BASE_ADDRESS_0 + bar_idx*4); /* Not IO */ if ( !(bar & PCI_BASE_ADDRESS_SPACE_IO) ) continue; pci_conf_write32(0, b, d, f, PCI_BASE_ADDRESS_0, ~0u); len = pci_conf_read32(0, b, d, f, PCI_BASE_ADDRESS_0); pci_conf_write32(0, b, d, f, PCI_BASE_ADDRESS_0 + bar_idx*4, bar); /* Not 8 bytes */ if ( (len & 0xffff) != 0xfff9 ) continue; uart->ps_bdf[0] = b; uart->ps_bdf[1] = d; uart->ps_bdf[2] = f; uart->bar = bar; uart->bar_idx = bar_idx; uart->io_base = bar & ~PCI_BASE_ADDRESS_SPACE_IO; uart->irq = pci_conf_read8(0, b, d, f, PCI_INTERRUPT_PIN) ? pci_conf_read8(0, b, d, f, PCI_INTERRUPT_LINE) : 0; return 0; } } } if ( !skip_amt ) return -1; uart->io_base = 0x3f8; uart->irq = 0; uart->clock_hz = UART_CLOCK_HZ; return 0; } #endif #define PARSE_ERR(_f, _a...) \ do { \ printk( "ERROR: " _f "\n" , ## _a ); \ return; \ } while ( 0 ) static void __init ns16550_parse_port_config( struct ns16550 *uart, const char *conf) { int baud; /* No user-specified configuration? */ if ( (conf == NULL) || (*conf == '\0') ) { /* Some platforms may automatically probe the UART configuartion. */ if ( uart->baud != 0 ) goto config_parsed; return; } if ( strncmp(conf, "auto", 4) == 0 ) { uart->baud = BAUD_AUTO; conf += 4; } else if ( (baud = simple_strtoul(conf, &conf, 10)) != 0 ) uart->baud = baud; if ( *conf == '/' ) { conf++; uart->clock_hz = simple_strtoul(conf, &conf, 0) << 4; } if ( *conf == ',' && *++conf != ',' ) { uart->data_bits = simple_strtoul(conf, &conf, 10); uart->parity = parse_parity_char(*conf); uart->stop_bits = simple_strtoul(conf + 1, &conf, 10); } if ( *conf == ',' && *++conf != ',' ) { #ifdef HAS_PCI if ( strncmp(conf, "pci", 3) == 0 ) { if ( pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com) ) return; conf += 3; } else if ( strncmp(conf, "amt", 3) == 0 ) { if ( pci_uart_config(uart, 0, uart - ns16550_com) ) return; conf += 3; } else #endif { uart->io_base = simple_strtoul(conf, &conf, 0); } } if ( *conf == ',' && *++conf != ',' ) uart->irq = simple_strtol(conf, &conf, 10); #ifdef HAS_PCI if ( *conf == ',' && *++conf != ',' ) { conf = parse_pci(conf, NULL, &uart->ps_bdf[0], &uart->ps_bdf[1], &uart->ps_bdf[2]); if ( !conf ) PARSE_ERR("Bad port PCI coordinates"); uart->ps_bdf_enable = 1; } if ( *conf == ',' && *++conf != ',' ) { if ( !parse_pci(conf, NULL, &uart->pb_bdf[0], &uart->pb_bdf[1], &uart->pb_bdf[2]) ) PARSE_ERR("Bad bridge PCI coordinates"); uart->pb_bdf_enable = 1; } #endif config_parsed: /* Sanity checks. */ if ( (uart->baud != BAUD_AUTO) && ((uart->baud < 1200) || (uart->baud > 115200)) ) PARSE_ERR("Baud rate %d outside supported range.", uart->baud); if ( (uart->data_bits < 5) || (uart->data_bits > 8) ) PARSE_ERR("%d data bits are unsupported.", uart->data_bits); if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) ) PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits); if ( uart->io_base == 0 ) PARSE_ERR("I/O base address must be specified."); if ( !check_existence(uart) ) PARSE_ERR("16550-compatible serial UART not present"); /* Register with generic serial driver. */ serial_register_uart(uart - ns16550_com, &ns16550_driver, uart); } void __init ns16550_init(int index, struct ns16550_defaults *defaults) { struct ns16550 *uart; if ( (index < 0) || (index > 1) ) return; uart = &ns16550_com[index]; uart->baud = (defaults->baud ? : console_has((index == 0) ? "com1" : "com2") ? BAUD_AUTO : 0); uart->clock_hz = UART_CLOCK_HZ; uart->data_bits = defaults->data_bits; uart->parity = parse_parity_char(defaults->parity); uart->stop_bits = defaults->stop_bits; uart->irq = defaults->irq; uart->io_base = defaults->io_base; uart->io_size = 8; uart->reg_width = 1; uart->reg_shift = 0; /* Default is no transmit FIFO. */ uart->fifo_size = 1; ns16550_parse_port_config(uart, (index == 0) ? opt_com1 : opt_com2); } #ifdef HAS_DEVICE_TREE static int __init ns16550_uart_dt_init(struct dt_device_node *dev, const void *data) { struct ns16550 *uart; int res; u32 reg_shift, reg_width; u64 io_size; uart = &ns16550_com[0]; uart->baud = BAUD_AUTO; uart->clock_hz = UART_CLOCK_HZ; uart->data_bits = 8; uart->parity = UART_PARITY_NONE; uart->stop_bits = 1; /* Default is no transmit FIFO. */ uart->fifo_size = 1; res = dt_device_get_address(dev, 0, &uart->io_base, &io_size); if ( res ) return res; uart->io_size = io_size; ASSERT(uart->io_size == io_size); /* Detect truncation */ res = dt_property_read_u32(dev, "reg-shift", ®_shift); if ( !res ) uart->reg_shift = 0; else uart->reg_shift = reg_shift; res = dt_property_read_u32(dev, "reg-io-width", ®_width); if ( !res ) uart->reg_width = 1; else uart->reg_width = reg_width; if ( uart->reg_width != 1 && uart->reg_width != 4 ) return -EINVAL; res = dt_device_get_irq(dev, 0, &uart->dt_irq); if ( res ) return res; /* The common bit of the driver mostly deals with irq not dt_irq. */ uart->irq = uart->dt_irq.irq; uart->dw_usr_bsy = dt_device_is_compatible(dev, "snps,dw-apb-uart"); uart->vuart.base_addr = uart->io_base; uart->vuart.size = uart->io_size; uart->vuart.data_off = UART_THR <reg_shift; uart->vuart.status_off = UART_LSR<reg_shift; uart->vuart.status = UART_LSR_THRE|UART_LSR_TEMT; /* Register with generic serial driver. */ serial_register_uart(uart - ns16550_com, &ns16550_driver, uart); dt_device_set_used_by(dev, DOMID_XEN); return 0; } static const char const *ns16550_dt_compat[] __initconst = { "ns16550", "snps,dw-apb-uart", NULL }; DT_DEVICE_START(ns16550, "NS16550 UART", DEVICE_SERIAL) .compatible = ns16550_dt_compat, .init = ns16550_uart_dt_init, DT_DEVICE_END #endif /* HAS_DEVICE_TREE */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */