📄 amba-pl010.c
字号:
lcr_h |= UART01x_LCRH_PEN; if (!(termios->c_cflag & PARODD)) lcr_h |= UART01x_LCRH_EPS; } if (uap->port.fifosize > 1) lcr_h |= UART01x_LCRH_FEN; spin_lock_irqsave(&uap->port.lock, flags); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); uap->port.read_status_mask = UART01x_RSR_OE; if (termios->c_iflag & INPCK) uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) uap->port.read_status_mask |= UART01x_RSR_BE; /* * Characters to ignore */ uap->port.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) uap->port.ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; if (termios->c_iflag & IGNBRK) { uap->port.ignore_status_mask |= UART01x_RSR_BE; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) uap->port.ignore_status_mask |= UART01x_RSR_OE; } /* * Ignore all characters if CREAD is not set. */ if ((termios->c_cflag & CREAD) == 0) uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX; /* first, disable everything */ old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE; if (UART_ENABLE_MS(port, termios->c_cflag)) old_cr |= UART010_CR_MSIE; writel(0, uap->port.membase + UART010_CR); /* Set baud rate */ quot -= 1; writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM); writel(quot & 0xff, uap->port.membase + UART010_LCRL); /* * ----------v----------v----------v----------v----- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L * ----------^----------^----------^----------^----- */ writel(lcr_h, uap->port.membase + UART010_LCRH); writel(old_cr, uap->port.membase + UART010_CR); spin_unlock_irqrestore(&uap->port.lock, flags);}static const char *pl010_type(struct uart_port *port){ return port->type == PORT_AMBA ? "AMBA" : NULL;}/* * Release the memory region(s) being used by 'port' */static void pl010_release_port(struct uart_port *port){ release_mem_region(port->mapbase, UART_PORT_SIZE);}/* * Request the memory region(s) being used by 'port' */static int pl010_request_port(struct uart_port *port){ return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010") != NULL ? 0 : -EBUSY;}/* * Configure/autoconfigure the port. */static void pl010_config_port(struct uart_port *port, int flags){ if (flags & UART_CONFIG_TYPE) { port->type = PORT_AMBA; pl010_request_port(port); }}/* * verify the new serial_struct (for TIOCSSERIAL). */static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser){ int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) ret = -EINVAL; if (ser->irq < 0 || ser->irq >= NR_IRQS) ret = -EINVAL; if (ser->baud_base < 9600) ret = -EINVAL; return ret;}static struct uart_ops amba_pl010_pops = { .tx_empty = pl010_tx_empty, .set_mctrl = pl010_set_mctrl, .get_mctrl = pl010_get_mctrl, .stop_tx = pl010_stop_tx, .start_tx = pl010_start_tx, .stop_rx = pl010_stop_rx, .enable_ms = pl010_enable_ms, .break_ctl = pl010_break_ctl, .startup = pl010_startup, .shutdown = pl010_shutdown, .set_termios = pl010_set_termios, .type = pl010_type, .release_port = pl010_release_port, .request_port = pl010_request_port, .config_port = pl010_config_port, .verify_port = pl010_verify_port,};static struct uart_amba_port *amba_ports[UART_NR];#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLEstatic void pl010_console_putchar(struct uart_port *port, int ch){ struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; do { status = readb(uap->port.membase + UART01x_FR); barrier(); } while (!UART_TX_READY(status)); writel(ch, uap->port.membase + UART01x_DR);}static voidpl010_console_write(struct console *co, const char *s, unsigned int count){ struct uart_amba_port *uap = amba_ports[co->index]; unsigned int status, old_cr; clk_enable(uap->clk); /* * First save the CR then disable the interrupts */ old_cr = readb(uap->port.membase + UART010_CR); writel(UART01x_CR_UARTEN, uap->port.membase + UART010_CR); uart_console_write(&uap->port, s, count, pl010_console_putchar); /* * Finally, wait for transmitter to become empty * and restore the TCR */ do { status = readb(uap->port.membase + UART01x_FR); barrier(); } while (status & UART01x_FR_BUSY); writel(old_cr, uap->port.membase + UART010_CR); clk_disable(uap->clk);}static void __initpl010_console_get_options(struct uart_amba_port *uap, int *baud, int *parity, int *bits){ if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) { unsigned int lcr_h, quot; lcr_h = readb(uap->port.membase + UART010_LCRH); *parity = 'n'; if (lcr_h & UART01x_LCRH_PEN) { if (lcr_h & UART01x_LCRH_EPS) *parity = 'e'; else *parity = 'o'; } if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) *bits = 7; else *bits = 8; quot = readb(uap->port.membase + UART010_LCRL) | readb(uap->port.membase + UART010_LCRM) << 8; *baud = uap->port.uartclk / (16 * (quot + 1)); }}static int __init pl010_console_setup(struct console *co, char *options){ struct uart_amba_port *uap; int baud = 38400; int bits = 8; int parity = 'n'; int flow = 'n'; /* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support. */ if (co->index >= UART_NR) co->index = 0; uap = amba_ports[co->index]; if (!uap) return -ENODEV; uap->port.uartclk = clk_get_rate(uap->clk); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else pl010_console_get_options(uap, &baud, &parity, &bits); return uart_set_options(&uap->port, co, baud, parity, bits, flow);}static struct uart_driver amba_reg;static struct console amba_console = { .name = "ttyAM", .write = pl010_console_write, .device = uart_console_device, .setup = pl010_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &amba_reg,};#define AMBA_CONSOLE &amba_console#else#define AMBA_CONSOLE NULL#endifstatic struct uart_driver amba_reg = { .owner = THIS_MODULE, .driver_name = "ttyAM", .dev_name = "ttyAM", .major = SERIAL_AMBA_MAJOR, .minor = SERIAL_AMBA_MINOR, .nr = UART_NR, .cons = AMBA_CONSOLE,};static int pl010_probe(struct amba_device *dev, void *id){ struct uart_amba_port *uap; void __iomem *base; int i, ret; for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == NULL) break; if (i == ARRAY_SIZE(amba_ports)) { ret = -EBUSY; goto out; } uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); if (!uap) { ret = -ENOMEM; goto out; } base = ioremap(dev->res.start, PAGE_SIZE); if (!base) { ret = -ENOMEM; goto free; } uap->clk = clk_get(&dev->dev, "UARTCLK"); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); goto unmap; } uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; uap->port.iotype = UPIO_MEM; uap->port.irq = dev->irq[0]; uap->port.fifosize = 16; uap->port.ops = &amba_pl010_pops; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; uap->dev = dev; uap->data = dev->dev.platform_data; amba_ports[i] = uap; amba_set_drvdata(dev, uap); ret = uart_add_one_port(&amba_reg, &uap->port); if (ret) { amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; clk_put(uap->clk); unmap: iounmap(base); free: kfree(uap); } out: return ret;}static int pl010_remove(struct amba_device *dev){ struct uart_amba_port *uap = amba_get_drvdata(dev); int i; amba_set_drvdata(dev, NULL); uart_remove_one_port(&amba_reg, &uap->port); for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == uap) amba_ports[i] = NULL; iounmap(uap->port.membase); clk_put(uap->clk); kfree(uap); return 0;}static int pl010_suspend(struct amba_device *dev, pm_message_t state){ struct uart_amba_port *uap = amba_get_drvdata(dev); if (uap) uart_suspend_port(&amba_reg, &uap->port); return 0;}static int pl010_resume(struct amba_device *dev){ struct uart_amba_port *uap = amba_get_drvdata(dev); if (uap) uart_resume_port(&amba_reg, &uap->port); return 0;}static struct amba_id pl010_ids[] __initdata = { { .id = 0x00041010, .mask = 0x000fffff, }, { 0, 0 },};static struct amba_driver pl010_driver = { .drv = { .name = "uart-pl010", }, .id_table = pl010_ids, .probe = pl010_probe, .remove = pl010_remove, .suspend = pl010_suspend, .resume = pl010_resume,};static int __init pl010_init(void){ int ret; printk(KERN_INFO "Serial: AMBA driver $Revision: 1.41 $\n"); ret = uart_register_driver(&amba_reg); if (ret == 0) { ret = amba_driver_register(&pl010_driver); if (ret) uart_unregister_driver(&amba_reg); } return ret;}static void __exit pl010_exit(void){ amba_driver_unregister(&pl010_driver); uart_unregister_driver(&amba_reg);}module_init(pl010_init);module_exit(pl010_exit);MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.41 $");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -