📄 imx.c
字号:
error_out3: free_irq(sport->txirq, sport);error_out2: free_irq(sport->rxirq, sport);error_out1: return retval;}static void imx_shutdown(struct uart_port *port){ struct imx_port *sport = (struct imx_port *)port; unsigned long temp; /* * Stop our timer. */ del_timer_sync(&sport->timer); /* * Free the interrupts */ free_irq(sport->rtsirq, sport); free_irq(sport->txirq, sport); free_irq(sport->rxirq, sport); /* * Disable all interrupts, port and break condition. */ temp = readl(sport->port.membase + UCR1); temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); writel(temp, sport->port.membase + UCR1);}static voidimx_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old){ struct imx_port *sport = (struct imx_port *)port; unsigned long flags; unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; /* * If we don't support modem control lines, don't allow * these to be set. */ if (0) { termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); termios->c_cflag |= CLOCAL; } /* * We only support CS7 and CS8. */ while ((termios->c_cflag & CSIZE) != CS7 && (termios->c_cflag & CSIZE) != CS8) { termios->c_cflag &= ~CSIZE; termios->c_cflag |= old_csize; old_csize = CS8; } if ((termios->c_cflag & CSIZE) == CS8) ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; else ucr2 = UCR2_SRST | UCR2_IRTS; if (termios->c_cflag & CRTSCTS) { if( sport->have_rtscts ) { ucr2 &= ~UCR2_IRTS; ucr2 |= UCR2_CTSC; } else { termios->c_cflag &= ~CRTSCTS; } } if (termios->c_cflag & CSTOPB) ucr2 |= UCR2_STPB; if (termios->c_cflag & PARENB) { ucr2 |= UCR2_PREN; if (termios->c_cflag & PARODD) ucr2 |= UCR2_PROE; } /* * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); spin_lock_irqsave(&sport->port.lock, flags); sport->port.read_status_mask = 0; if (termios->c_iflag & INPCK) sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); if (termios->c_iflag & (BRKINT | PARMRK)) sport->port.read_status_mask |= URXD_BRK; /* * Characters to ignore */ sport->port.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) sport->port.ignore_status_mask |= URXD_PRERR; if (termios->c_iflag & IGNBRK) { sport->port.ignore_status_mask |= URXD_BRK; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) sport->port.ignore_status_mask |= URXD_OVRRUN; } del_timer_sync(&sport->timer); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* * disable interrupts and drain transmitter */ old_ucr1 = readl(sport->port.membase + UCR1); writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), sport->port.membase + UCR1); while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) barrier(); /* then, disable everything */ old_txrxen = readl(sport->port.membase + UCR2); writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), sport->port.membase + UCR2); old_txrxen &= (UCR2_TXEN | UCR2_RXEN); /* set the baud rate. We assume uartclk = 16 MHz * * baud * 16 UBIR - 1 * --------- = -------- * uartclk UBMR - 1 */ writel((baud / 100) - 1, sport->port.membase + UBIR); writel(10000 - 1, sport->port.membase + UBMR); writel(old_ucr1, sport->port.membase + UCR1); /* set the parity, stop bits and data size */ writel(ucr2 | old_txrxen, sport->port.membase + UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); spin_unlock_irqrestore(&sport->port.lock, flags);}static const char *imx_type(struct uart_port *port){ struct imx_port *sport = (struct imx_port *)port; return sport->port.type == PORT_IMX ? "IMX" : NULL;}/* * Release the memory region(s) being used by 'port'. */static void imx_release_port(struct uart_port *port){ struct imx_port *sport = (struct imx_port *)port; release_mem_region(sport->port.mapbase, UART_PORT_SIZE);}/* * Request the memory region(s) being used by 'port'. */static int imx_request_port(struct uart_port *port){ struct imx_port *sport = (struct imx_port *)port; return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, "imx-uart") != NULL ? 0 : -EBUSY;}/* * Configure/autoconfigure the port. */static void imx_config_port(struct uart_port *port, int flags){ struct imx_port *sport = (struct imx_port *)port; if (flags & UART_CONFIG_TYPE && imx_request_port(&sport->port) == 0) sport->port.type = PORT_IMX;}/* * Verify the new serial_struct (for TIOCSSERIAL). * The only change we allow are to the flags and type, and * even then only between PORT_IMX and PORT_UNKNOWN */static intimx_verify_port(struct uart_port *port, struct serial_struct *ser){ struct imx_port *sport = (struct imx_port *)port; int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX) ret = -EINVAL; if (sport->port.irq != ser->irq) ret = -EINVAL; if (ser->io_type != UPIO_MEM) ret = -EINVAL; if (sport->port.uartclk / 16 != ser->baud_base) ret = -EINVAL; if ((void *)sport->port.mapbase != ser->iomem_base) ret = -EINVAL; if (sport->port.iobase != ser->port) ret = -EINVAL; if (ser->hub6 != 0) ret = -EINVAL; return ret;}static struct uart_ops imx_pops = { .tx_empty = imx_tx_empty, .set_mctrl = imx_set_mctrl, .get_mctrl = imx_get_mctrl, .stop_tx = imx_stop_tx, .start_tx = imx_start_tx, .stop_rx = imx_stop_rx, .enable_ms = imx_enable_ms, .break_ctl = imx_break_ctl, .startup = imx_startup, .shutdown = imx_shutdown, .set_termios = imx_set_termios, .type = imx_type, .release_port = imx_release_port, .request_port = imx_request_port, .config_port = imx_config_port, .verify_port = imx_verify_port,};static struct imx_port imx_ports[] = { { .txirq = UART1_MINT_TX, .rxirq = UART1_MINT_RX, .rtsirq = UART1_MINT_RTS, .port = { .type = PORT_IMX, .iotype = UPIO_MEM, .membase = (void *)IMX_UART1_BASE, .mapbase = IMX_UART1_BASE, /* FIXME */ .irq = UART1_MINT_RX, .uartclk = 16000000, .fifosize = 32, .flags = UPF_BOOT_AUTOCONF, .ops = &imx_pops, .line = 0, }, }, { .txirq = UART2_MINT_TX, .rxirq = UART2_MINT_RX, .rtsirq = UART2_MINT_RTS, .port = { .type = PORT_IMX, .iotype = UPIO_MEM, .membase = (void *)IMX_UART2_BASE, .mapbase = IMX_UART2_BASE, /* FIXME */ .irq = UART2_MINT_RX, .uartclk = 16000000, .fifosize = 32, .flags = UPF_BOOT_AUTOCONF, .ops = &imx_pops, .line = 1, }, }};/* * Setup the IMX serial ports. * Note also that we support "console=ttySMXx" where "x" is either 0 or 1. * Which serial port this ends up being depends on the machine you're * running this kernel on. I'm not convinced that this is a good idea, * but that's the way it traditionally works. * */static void __init imx_init_ports(void){ static int first = 1; int i; if (!first) return; first = 0; for (i = 0; i < ARRAY_SIZE(imx_ports); i++) { init_timer(&imx_ports[i].timer); imx_ports[i].timer.function = imx_timeout; imx_ports[i].timer.data = (unsigned long)&imx_ports[i]; }}#ifdef CONFIG_SERIAL_IMX_CONSOLEstatic void imx_console_putchar(struct uart_port *port, int ch){ struct imx_port *sport = (struct imx_port *)port; while (readl(sport->port.membase + UTS) & UTS_TXFULL) barrier(); writel(ch, sport->port.membase + URTX0);}/* * Interrupts are disabled on entering */static voidimx_console_write(struct console *co, const char *s, unsigned int count){ struct imx_port *sport = &imx_ports[co->index]; unsigned int old_ucr1, old_ucr2; /* * First, save UCR1/2 and then disable interrupts */ old_ucr1 = readl(sport->port.membase + UCR1); old_ucr2 = readl(sport->port.membase + UCR2); writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), sport->port.membase + UCR1); writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); uart_console_write(&sport->port, s, count, imx_console_putchar); /* * Finally, wait for transmitter to become empty * and restore UCR1/2 */ while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); writel(old_ucr1, sport->port.membase + UCR1); writel(old_ucr2, sport->port.membase + UCR2);}/* * If the port was already initialised (eg, by a boot loader), * try to determine the current setup. */static void __initimx_console_get_options(struct imx_port *sport, int *baud, int *parity, int *bits){ if ( readl(sport->port.membase + UCR1) | UCR1_UARTEN ) { /* ok, the port was enabled */ unsigned int ucr2, ubir,ubmr, uartclk; unsigned int baud_raw; unsigned int ucfr_rfdiv; ucr2 = readl(sport->port.membase + UCR2); *parity = 'n'; if (ucr2 & UCR2_PREN) { if (ucr2 & UCR2_PROE) *parity = 'o'; else *parity = 'e'; } if (ucr2 & UCR2_WS) *bits = 8; else *bits = 7; ubir = readl(sport->port.membase + UBIR) & 0xffff; ubmr = readl(sport->port.membase + UBMR) & 0xffff; ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7; if (ucfr_rfdiv == 6) ucfr_rfdiv = 7; else ucfr_rfdiv = 6 - ucfr_rfdiv; uartclk = imx_get_perclk1(); uartclk /= ucfr_rfdiv; { /* * The next code provides exact computation of * baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1)) * without need of float support or long long division, * which would be required to prevent 32bit arithmetic overflow */ unsigned int mul = ubir + 1; unsigned int div = 16 * (ubmr + 1); unsigned int rem = uartclk % div; baud_raw = (uartclk / div) * mul; baud_raw += (rem * mul + div / 2) / div; *baud = (baud_raw + 50) / 100 * 100; } if(*baud != baud_raw) printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n", baud_raw, *baud); }}static int __initimx_console_setup(struct console *co, char *options){ struct imx_port *sport; int baud = 9600; 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 == -1 || co->index >= ARRAY_SIZE(imx_ports)) co->index = 0; sport = &imx_ports[co->index]; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else imx_console_get_options(sport, &baud, &parity, &bits); imx_setup_ufcr(sport, 0); return uart_set_options(&sport->port, co, baud, parity, bits, flow);}static struct uart_driver imx_reg;static struct console imx_console = { .name = "ttySMX", .write = imx_console_write, .device = uart_console_device, .setup = imx_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &imx_reg,};static int __init imx_rs_console_init(void){ imx_init_ports(); register_console(&imx_console); return 0;}console_initcall(imx_rs_console_init);#define IMX_CONSOLE &imx_console#else#define IMX_CONSOLE NULL#endifstatic struct uart_driver imx_reg = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttySMX", .major = SERIAL_IMX_MAJOR, .minor = MINOR_START, .nr = ARRAY_SIZE(imx_ports), .cons = IMX_CONSOLE,};static int serial_imx_suspend(struct platform_device *dev, pm_message_t state){ struct imx_port *sport = platform_get_drvdata(dev); if (sport) uart_suspend_port(&imx_reg, &sport->port); return 0;}static int serial_imx_resume(struct platform_device *dev){ struct imx_port *sport = platform_get_drvdata(dev); if (sport) uart_resume_port(&imx_reg, &sport->port); return 0;}static int serial_imx_probe(struct platform_device *dev){ struct imxuart_platform_data *pdata; imx_ports[dev->id].port.dev = &dev->dev; pdata = (struct imxuart_platform_data *)dev->dev.platform_data; if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) imx_ports[dev->id].have_rtscts = 1; uart_add_one_port(&imx_reg, &imx_ports[dev->id].port); platform_set_drvdata(dev, &imx_ports[dev->id]); return 0;}static int serial_imx_remove(struct platform_device *dev){ struct imx_port *sport = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); if (sport) uart_remove_one_port(&imx_reg, &sport->port); return 0;}static struct platform_driver serial_imx_driver = { .probe = serial_imx_probe, .remove = serial_imx_remove, .suspend = serial_imx_suspend, .resume = serial_imx_resume, .driver = { .name = "imx-uart", },};static int __init imx_serial_init(void){ int ret; printk(KERN_INFO "Serial: IMX driver\n"); imx_init_ports(); ret = uart_register_driver(&imx_reg); if (ret) return ret; ret = platform_driver_register(&serial_imx_driver); if (ret != 0) uart_unregister_driver(&imx_reg); return 0;}static void __exit imx_serial_exit(void){ platform_driver_unregister(&serial_imx_driver); uart_unregister_driver(&imx_reg);}module_init(imx_serial_init);module_exit(imx_serial_exit);MODULE_AUTHOR("Sascha Hauer");MODULE_DESCRIPTION("IMX generic serial port driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -