atmel_serial.c

来自「linux 内核源代码」· C语言 代码 · 共 1,019 行 · 第 1/2 页

C
1,019
字号
	if (quot > 65535) {		/* BRGR is 16-bit, so switch to slower clock */		quot /= 8;		mode |= ATMEL_US_USCLKS_MCK_DIV8;	}	/* byte size */	switch (termios->c_cflag & CSIZE) {	case CS5:		mode |= ATMEL_US_CHRL_5;		break;	case CS6:		mode |= ATMEL_US_CHRL_6;		break;	case CS7:		mode |= ATMEL_US_CHRL_7;		break;	default:		mode |= ATMEL_US_CHRL_8;		break;	}	/* stop bits */	if (termios->c_cflag & CSTOPB)		mode |= ATMEL_US_NBSTOP_2;	/* parity */	if (termios->c_cflag & PARENB) {		if (termios->c_cflag & CMSPAR) {			/* Mark or Space parity */			if (termios->c_cflag & PARODD)				mode |= ATMEL_US_PAR_MARK;			else				mode |= ATMEL_US_PAR_SPACE;		}		else if (termios->c_cflag & PARODD)			mode |= ATMEL_US_PAR_ODD;		else			mode |= ATMEL_US_PAR_EVEN;	}	else		mode |= ATMEL_US_PAR_NONE;	spin_lock_irqsave(&port->lock, flags);	port->read_status_mask = ATMEL_US_OVRE;	if (termios->c_iflag & INPCK)		port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);	if (termios->c_iflag & (BRKINT | PARMRK))		port->read_status_mask |= ATMEL_US_RXBRK;	/*	 * Characters to ignore	 */	port->ignore_status_mask = 0;	if (termios->c_iflag & IGNPAR)		port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);	if (termios->c_iflag & IGNBRK) {		port->ignore_status_mask |= ATMEL_US_RXBRK;		/*		 * If we're ignoring parity and break indicators,		 * ignore overruns too (for real raw support).		 */		if (termios->c_iflag & IGNPAR)			port->ignore_status_mask |= ATMEL_US_OVRE;	}	// TODO: Ignore all characters if CREAD is set.	/* update the per-port timeout */	uart_update_timeout(port, termios->c_cflag, baud);	/* disable interrupts and drain transmitter */	imr = UART_GET_IMR(port);	/* get interrupt mask */	UART_PUT_IDR(port, -1);		/* disable all interrupts */	while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) { barrier(); }	/* disable receiver and transmitter */	UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);	/* set the parity, stop bits and data size */	UART_PUT_MR(port, mode);	/* set the baud rate */	UART_PUT_BRGR(port, quot);	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);	/* restore interrupts */	UART_PUT_IER(port, imr);	/* CTS flow-control and modem-status interrupts */	if (UART_ENABLE_MS(port, termios->c_cflag))		port->ops->enable_ms(port);	spin_unlock_irqrestore(&port->lock, flags);}/* * Return string describing the specified port */static const char *atmel_type(struct uart_port *port){	return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL;}/* * Release the memory region(s) being used by 'port'. */static void atmel_release_port(struct uart_port *port){	struct platform_device *pdev = to_platform_device(port->dev);	int size = pdev->resource[0].end - pdev->resource[0].start + 1;	release_mem_region(port->mapbase, size);	if (port->flags & UPF_IOREMAP) {		iounmap(port->membase);		port->membase = NULL;	}}/* * Request the memory region(s) being used by 'port'. */static int atmel_request_port(struct uart_port *port){	struct platform_device *pdev = to_platform_device(port->dev);	int size = pdev->resource[0].end - pdev->resource[0].start + 1;	if (!request_mem_region(port->mapbase, size, "atmel_serial"))		return -EBUSY;	if (port->flags & UPF_IOREMAP) {		port->membase = ioremap(port->mapbase, size);		if (port->membase == NULL) {			release_mem_region(port->mapbase, size);			return -ENOMEM;		}	}	return 0;}/* * Configure/autoconfigure the port. */static void atmel_config_port(struct uart_port *port, int flags){	if (flags & UART_CONFIG_TYPE) {		port->type = PORT_ATMEL;		atmel_request_port(port);	}}/* * Verify the new serial_struct (for TIOCSSERIAL). */static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser){	int ret = 0;	if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL)		ret = -EINVAL;	if (port->irq != ser->irq)		ret = -EINVAL;	if (ser->io_type != SERIAL_IO_MEM)		ret = -EINVAL;	if (port->uartclk / 16 != ser->baud_base)		ret = -EINVAL;	if ((void *)port->mapbase != ser->iomem_base)		ret = -EINVAL;	if (port->iobase != ser->port)		ret = -EINVAL;	if (ser->hub6 != 0)		ret = -EINVAL;	return ret;}static struct uart_ops atmel_pops = {	.tx_empty	= atmel_tx_empty,	.set_mctrl	= atmel_set_mctrl,	.get_mctrl	= atmel_get_mctrl,	.stop_tx	= atmel_stop_tx,	.start_tx	= atmel_start_tx,	.stop_rx	= atmel_stop_rx,	.enable_ms	= atmel_enable_ms,	.break_ctl	= atmel_break_ctl,	.startup	= atmel_startup,	.shutdown	= atmel_shutdown,	.set_termios	= atmel_set_termios,	.type		= atmel_type,	.release_port	= atmel_release_port,	.request_port	= atmel_request_port,	.config_port	= atmel_config_port,	.verify_port	= atmel_verify_port,	.pm		= atmel_serial_pm,};/* * Configure the port from the platform device resource info. */static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev){	struct uart_port *port = &atmel_port->uart;	struct atmel_uart_data *data = pdev->dev.platform_data;	port->iotype	= UPIO_MEM;	port->flags	= UPF_BOOT_AUTOCONF;	port->ops	= &atmel_pops;	port->fifosize	= 1;	port->line	= pdev->id;	port->dev	= &pdev->dev;	port->mapbase	= pdev->resource[0].start;	port->irq	= pdev->resource[1].start;	if (data->regs)		/* Already mapped by setup code */		port->membase = data->regs;	else {		port->flags	|= UPF_IOREMAP;		port->membase	= NULL;	}	if (!atmel_port->clk) {		/* for console, the clock could already be configured */		atmel_port->clk = clk_get(&pdev->dev, "usart");		clk_enable(atmel_port->clk);		port->uartclk = clk_get_rate(atmel_port->clk);	}}/* * Register board-specific modem-control line handlers. */void __init atmel_register_uart_fns(struct atmel_port_fns *fns){	if (fns->enable_ms)		atmel_pops.enable_ms = fns->enable_ms;	if (fns->get_mctrl)		atmel_pops.get_mctrl = fns->get_mctrl;	if (fns->set_mctrl)		atmel_pops.set_mctrl = fns->set_mctrl;	atmel_open_hook		= fns->open;	atmel_close_hook	= fns->close;	atmel_pops.pm		= fns->pm;	atmel_pops.set_wake	= fns->set_wake;}#ifdef CONFIG_SERIAL_ATMEL_CONSOLEstatic void atmel_console_putchar(struct uart_port *port, int ch){	while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY))		barrier();	UART_PUT_CHAR(port, ch);}/* * Interrupts are disabled on entering */static void atmel_console_write(struct console *co, const char *s, u_int count){	struct uart_port *port = &atmel_ports[co->index].uart;	unsigned int status, imr;	/*	 *	First, save IMR and then disable interrupts	 */	imr = UART_GET_IMR(port);	/* get interrupt mask */	UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);	uart_console_write(port, s, count, atmel_console_putchar);	/*	 *	Finally, wait for transmitter to become empty	 *	and restore IMR	 */	do {		status = UART_GET_CSR(port);	} while (!(status & ATMEL_US_TXRDY));	UART_PUT_IER(port, imr);	/* set interrupts back the way they were */}/* * If the port was already initialised (eg, by a boot loader), try to determine * the current setup. */static void __init atmel_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits){	unsigned int mr, quot;// TODO: CR is a write-only register//	unsigned int cr;////	cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN);//	if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) {//		/* ok, the port was enabled *///	}	mr = UART_GET_MR(port) & ATMEL_US_CHRL;	if (mr == ATMEL_US_CHRL_8)		*bits = 8;	else		*bits = 7;	mr = UART_GET_MR(port) & ATMEL_US_PAR;	if (mr == ATMEL_US_PAR_EVEN)		*parity = 'e';	else if (mr == ATMEL_US_PAR_ODD)		*parity = 'o';	/*	 * The serial core only rounds down when matching this to a	 * supported baud rate. Make sure we don't end up slightly	 * lower than one of those, as it would make us fall through	 * to a much lower baud rate than we really want.	 */	quot = UART_GET_BRGR(port);	*baud = port->uartclk / (16 * (quot - 1));}static int __init atmel_console_setup(struct console *co, char *options){	struct uart_port *port = &atmel_ports[co->index].uart;	int baud = 115200;	int bits = 8;	int parity = 'n';	int flow = 'n';	if (port->membase == 0)		/* Port not initialized yet - delay setup */		return -ENODEV;	UART_PUT_IDR(port, -1);				/* disable interrupts */	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);	if (options)		uart_parse_options(options, &baud, &parity, &bits, &flow);	else		atmel_console_get_options(port, &baud, &parity, &bits);	return uart_set_options(port, co, baud, parity, bits, flow);}static struct uart_driver atmel_uart;static struct console atmel_console = {	.name		= ATMEL_DEVICENAME,	.write		= atmel_console_write,	.device		= uart_console_device,	.setup		= atmel_console_setup,	.flags		= CON_PRINTBUFFER,	.index		= -1,	.data		= &atmel_uart,};#define ATMEL_CONSOLE_DEVICE	&atmel_console/* * Early console initialization (before VM subsystem initialized). */static int __init atmel_console_init(void){	if (atmel_default_console_device) {		add_preferred_console(ATMEL_DEVICENAME, atmel_default_console_device->id, NULL);		atmel_init_port(&(atmel_ports[atmel_default_console_device->id]), atmel_default_console_device);		register_console(&atmel_console);	}	return 0;}console_initcall(atmel_console_init);/* * Late console initialization. */static int __init atmel_late_console_init(void){	if (atmel_default_console_device && !(atmel_console.flags & CON_ENABLED))		register_console(&atmel_console);	return 0;}core_initcall(atmel_late_console_init);#else#define ATMEL_CONSOLE_DEVICE	NULL#endifstatic struct uart_driver atmel_uart = {	.owner			= THIS_MODULE,	.driver_name		= "atmel_serial",	.dev_name		= ATMEL_DEVICENAME,	.major			= SERIAL_ATMEL_MAJOR,	.minor			= MINOR_START,	.nr			= ATMEL_MAX_UART,	.cons			= ATMEL_CONSOLE_DEVICE,};#ifdef CONFIG_PMstatic int atmel_serial_suspend(struct platform_device *pdev, pm_message_t state){	struct uart_port *port = platform_get_drvdata(pdev);	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;	if (device_may_wakeup(&pdev->dev) && !at91_suspend_entering_slow_clock())		enable_irq_wake(port->irq);	else {		uart_suspend_port(&atmel_uart, port);		atmel_port->suspended = 1;	}	return 0;}static int atmel_serial_resume(struct platform_device *pdev){	struct uart_port *port = platform_get_drvdata(pdev);	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;	if (atmel_port->suspended) {		uart_resume_port(&atmel_uart, port);		atmel_port->suspended = 0;	}	else		disable_irq_wake(port->irq);	return 0;}#else#define atmel_serial_suspend NULL#define atmel_serial_resume NULL#endifstatic int __devinit atmel_serial_probe(struct platform_device *pdev){	struct atmel_uart_port *port;	int ret;	port = &atmel_ports[pdev->id];	atmel_init_port(port, pdev);	ret = uart_add_one_port(&atmel_uart, &port->uart);	if (!ret) {		device_init_wakeup(&pdev->dev, 1);		platform_set_drvdata(pdev, port);	}	return ret;}static int __devexit atmel_serial_remove(struct platform_device *pdev){	struct uart_port *port = platform_get_drvdata(pdev);	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;	int ret = 0;	clk_disable(atmel_port->clk);	clk_put(atmel_port->clk);	device_init_wakeup(&pdev->dev, 0);	platform_set_drvdata(pdev, NULL);	if (port) {		ret = uart_remove_one_port(&atmel_uart, port);		kfree(port);	}	return ret;}static struct platform_driver atmel_serial_driver = {	.probe		= atmel_serial_probe,	.remove		= __devexit_p(atmel_serial_remove),	.suspend	= atmel_serial_suspend,	.resume		= atmel_serial_resume,	.driver		= {		.name	= "atmel_usart",		.owner	= THIS_MODULE,	},};static int __init atmel_serial_init(void){	int ret;	ret = uart_register_driver(&atmel_uart);	if (ret)		return ret;	ret = platform_driver_register(&atmel_serial_driver);	if (ret)		uart_unregister_driver(&atmel_uart);	return ret;}static void __exit atmel_serial_exit(void){	platform_driver_unregister(&atmel_serial_driver);	uart_unregister_driver(&atmel_uart);}module_init(atmel_serial_init);module_exit(atmel_serial_exit);MODULE_AUTHOR("Rick Bronson");MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?