sunsab.c

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

C
1,149
字号
	spin_unlock_irqrestore(&up->port.lock, flags);	return 0;}/* port->lock is not held.  */static void sunsab_shutdown(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	/* Disable Interrupts */	up->interrupt_mask0 = 0xff;	writeb(up->interrupt_mask0, &up->regs->w.imr0);	up->interrupt_mask1 = 0xff;	writeb(up->interrupt_mask1, &up->regs->w.imr1);	/* Disable break condition */	up->cached_dafo = readb(&up->regs->rw.dafo);	up->cached_dafo &= ~SAB82532_DAFO_XBRK;	writeb(up->cached_dafo, &up->regs->rw.dafo);	/* Disable Receiver */		up->cached_mode &= ~SAB82532_MODE_RAC;	writeb(up->cached_mode, &up->regs->rw.mode);	/*	 * XXX FIXME	 *	 * If the chip is powered down here the system hangs/crashes during	 * reboot or shutdown.  This needs to be investigated further,	 * similar behaviour occurs in 2.4 when the driver is configured	 * as a module only.  One hint may be that data is sometimes	 * transmitted at 9600 baud during shutdown (regardless of the	 * speed the chip was configured for when the port was open).	 */#if 0	/* Power Down */		tmp = readb(&up->regs->rw.ccr0);	tmp &= ~SAB82532_CCR0_PU;	writeb(tmp, &up->regs->rw.ccr0);#endif	spin_unlock_irqrestore(&up->port.lock, flags);	free_irq(up->port.irq, up);}/* * This is used to figure out the divisor speeds. * * The formula is:    Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)), * * with               0 <= N < 64 and 0 <= M < 16 */static void calc_ebrg(int baud, int *n_ret, int *m_ret){	int	n, m;	if (baud == 0) {		*n_ret = 0;		*m_ret = 0;		return;	}     	/*	 * We scale numbers by 10 so that we get better accuracy	 * without having to use floating point.  Here we increment m	 * until n is within the valid range.	 */	n = (SAB_BASE_BAUD * 10) / baud;	m = 0;	while (n >= 640) {		n = n / 2;		m++;	}	n = (n+5) / 10;	/*	 * We try very hard to avoid speeds with M == 0 since they may	 * not work correctly for XTAL frequences above 10 MHz.	 */	if ((m == 0) && ((n & 1) == 0)) {		n = n / 2;		m++;	}	*n_ret = n - 1;	*m_ret = m;}/* Internal routine, port->lock is held and local interrupts are disabled.  */static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag,				  unsigned int iflag, unsigned int baud,				  unsigned int quot){	unsigned char dafo;	int bits, n, m;	/* Byte size and parity */	switch (cflag & CSIZE) {	      case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;	      case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;	      case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;	      case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;	      /* Never happens, but GCC is too dumb to figure it out */	      default:  dafo = SAB82532_DAFO_CHL5; bits = 7; break;	}	if (cflag & CSTOPB) {		dafo |= SAB82532_DAFO_STOP;		bits++;	}	if (cflag & PARENB) {		dafo |= SAB82532_DAFO_PARE;		bits++;	}	if (cflag & PARODD) {		dafo |= SAB82532_DAFO_PAR_ODD;	} else {		dafo |= SAB82532_DAFO_PAR_EVEN;	}	up->cached_dafo = dafo;	calc_ebrg(baud, &n, &m);	up->cached_ebrg = n | (m << 6);	up->tec_timeout = (10 * 1000000) / baud;	up->cec_timeout = up->tec_timeout >> 2;	/* CTS flow control flags */	/* We encode read_status_mask and ignore_status_mask like so:	 *	 * ---------------------	 * | ... | ISR1 | ISR0 |	 * ---------------------	 *  ..    15   8 7    0	 */	up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |				     SAB82532_ISR0_RFO | SAB82532_ISR0_RPF |				     SAB82532_ISR0_CDSC);	up->port.read_status_mask |= (SAB82532_ISR1_CSC |				      SAB82532_ISR1_ALLS |				      SAB82532_ISR1_XPR) << 8;	if (iflag & INPCK)		up->port.read_status_mask |= (SAB82532_ISR0_PERR |					      SAB82532_ISR0_FERR);	if (iflag & (BRKINT | PARMRK))		up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8);	/*	 * Characteres to ignore	 */	up->port.ignore_status_mask = 0;	if (iflag & IGNPAR)		up->port.ignore_status_mask |= (SAB82532_ISR0_PERR |						SAB82532_ISR0_FERR);	if (iflag & IGNBRK) {		up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8);		/*		 * If we're ignoring parity and break indicators,		 * ignore overruns too (for real raw support).		 */		if (iflag & IGNPAR)			up->port.ignore_status_mask |= SAB82532_ISR0_RFO;	}	/*	 * ignore all characters if CREAD is not set	 */	if ((cflag & CREAD) == 0)		up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |						SAB82532_ISR0_TCD);	uart_update_timeout(&up->port, cflag,			    (up->port.uartclk / (16 * quot)));	/* Now schedule a register update when the chip's	 * transmitter is idle.	 */	up->cached_mode |= SAB82532_MODE_RAC;	set_bit(SAB82532_REGS_PENDING, &up->irqflags);	if (test_bit(SAB82532_XPR, &up->irqflags))		sunsab_tx_idle(up);}/* port->lock is not held.  */static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios,			       struct ktermios *old){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);	unsigned int quot = uart_get_divisor(port, baud);	spin_lock_irqsave(&up->port.lock, flags);	sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot);	spin_unlock_irqrestore(&up->port.lock, flags);}static const char *sunsab_type(struct uart_port *port){	struct uart_sunsab_port *up = (void *)port;	static char buf[36];		sprintf(buf, "SAB82532 %s", sab82532_version[up->type]);	return buf;}static void sunsab_release_port(struct uart_port *port){}static int sunsab_request_port(struct uart_port *port){	return 0;}static void sunsab_config_port(struct uart_port *port, int flags){}static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser){	return -EINVAL;}static struct uart_ops sunsab_pops = {	.tx_empty	= sunsab_tx_empty,	.set_mctrl	= sunsab_set_mctrl,	.get_mctrl	= sunsab_get_mctrl,	.stop_tx	= sunsab_stop_tx,	.start_tx	= sunsab_start_tx,	.send_xchar	= sunsab_send_xchar,	.stop_rx	= sunsab_stop_rx,	.enable_ms	= sunsab_enable_ms,	.break_ctl	= sunsab_break_ctl,	.startup	= sunsab_startup,	.shutdown	= sunsab_shutdown,	.set_termios	= sunsab_set_termios,	.type		= sunsab_type,	.release_port	= sunsab_release_port,	.request_port	= sunsab_request_port,	.config_port	= sunsab_config_port,	.verify_port	= sunsab_verify_port,};static struct uart_driver sunsab_reg = {	.owner			= THIS_MODULE,	.driver_name		= "serial",	.dev_name		= "ttyS",	.major			= TTY_MAJOR,};static struct uart_sunsab_port *sunsab_ports;#ifdef CONFIG_SERIAL_SUNSAB_CONSOLEstatic void sunsab_console_putchar(struct uart_port *port, int c){	struct uart_sunsab_port *up = (struct uart_sunsab_port *)port;	sunsab_tec_wait(up);	writeb(c, &up->regs->w.tic);}static void sunsab_console_write(struct console *con, const char *s, unsigned n){	struct uart_sunsab_port *up = &sunsab_ports[con->index];	unsigned long flags;	int locked = 1;	local_irq_save(flags);	if (up->port.sysrq) {		locked = 0;	} else if (oops_in_progress) {		locked = spin_trylock(&up->port.lock);	} else		spin_lock(&up->port.lock);	uart_console_write(&up->port, s, n, sunsab_console_putchar);	sunsab_tec_wait(up);	if (locked)		spin_unlock(&up->port.lock);	local_irq_restore(flags);}static int sunsab_console_setup(struct console *con, char *options){	struct uart_sunsab_port *up = &sunsab_ports[con->index];	unsigned long flags;	unsigned int baud, quot;	/*	 * The console framework calls us for each and every port	 * registered. Defer the console setup until the requested	 * port has been properly discovered. A bit of a hack,	 * though...	 */	if (up->port.type != PORT_SUNSAB)		return -1;	printk("Console: ttyS%d (SAB82532)\n",	       (sunsab_reg.minor - 64) + con->index);	sunserial_console_termios(con);	switch (con->cflag & CBAUD) {	case B150: baud = 150; break;	case B300: baud = 300; break;	case B600: baud = 600; break;	case B1200: baud = 1200; break;	case B2400: baud = 2400; break;	case B4800: baud = 4800; break;	default: case B9600: baud = 9600; break;	case B19200: baud = 19200; break;	case B38400: baud = 38400; break;	case B57600: baud = 57600; break;	case B115200: baud = 115200; break;	case B230400: baud = 230400; break;	case B460800: baud = 460800; break;	};	/*	 * Temporary fix.	 */	spin_lock_init(&up->port.lock);	/*	 * Initialize the hardware	 */	sunsab_startup(&up->port);	spin_lock_irqsave(&up->port.lock, flags);	/*	 * Finally, enable interrupts	 */	up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |				SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;	writeb(up->interrupt_mask0, &up->regs->w.imr0);	up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |				SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |				SAB82532_IMR1_CSC | SAB82532_IMR1_XON |				SAB82532_IMR1_XPR;	writeb(up->interrupt_mask1, &up->regs->w.imr1);	quot = uart_get_divisor(&up->port, baud);	sunsab_convert_to_sab(up, con->cflag, 0, baud, quot);	sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);	spin_unlock_irqrestore(&up->port.lock, flags);		return 0;}static struct console sunsab_console = {	.name	=	"ttyS",	.write	=	sunsab_console_write,	.device	=	uart_console_device,	.setup	=	sunsab_console_setup,	.flags	=	CON_PRINTBUFFER,	.index	=	-1,	.data	=	&sunsab_reg,};static inline struct console *SUNSAB_CONSOLE(void){	return &sunsab_console;}#else#define SUNSAB_CONSOLE()	(NULL)#define sunsab_console_init()	do { } while (0)#endifstatic int __devinit sunsab_init_one(struct uart_sunsab_port *up,				     struct of_device *op,				     unsigned long offset,				     int line){	up->port.line = line;	up->port.dev = &op->dev;	up->port.mapbase = op->resource[0].start + offset;	up->port.membase = of_ioremap(&op->resource[0], offset,				      sizeof(union sab82532_async_regs),				      "sab");	if (!up->port.membase)		return -ENOMEM;	up->regs = (union sab82532_async_regs __iomem *) up->port.membase;	up->port.irq = op->irqs[0];	up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;	up->port.iotype = UPIO_MEM;	writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);	up->port.ops = &sunsab_pops;	up->port.type = PORT_SUNSAB;	up->port.uartclk = SAB_BASE_BAUD;	up->type = readb(&up->regs->r.vstr) & 0x0f;	writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);	writeb(0xff, &up->regs->w.pim);	if ((up->port.line & 0x1) == 0) {		up->pvr_dsr_bit = (1 << 0);		up->pvr_dtr_bit = (1 << 1);		up->gis_shift = 2;	} else {		up->pvr_dsr_bit = (1 << 3);		up->pvr_dtr_bit = (1 << 2);		up->gis_shift = 0;	}	up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);	writeb(up->cached_pvr, &up->regs->w.pvr);	up->cached_mode = readb(&up->regs->rw.mode);	up->cached_mode |= SAB82532_MODE_FRTS;	writeb(up->cached_mode, &up->regs->rw.mode);	up->cached_mode |= SAB82532_MODE_RTS;	writeb(up->cached_mode, &up->regs->rw.mode);	up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;	up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;	return 0;}static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match){	static int inst;	struct uart_sunsab_port *up;	int err;	up = &sunsab_ports[inst * 2];	err = sunsab_init_one(&up[0], op,			      0,			      (inst * 2) + 0);	if (err)		goto out;	err = sunsab_init_one(&up[1], op,			      sizeof(union sab82532_async_regs),			      (inst * 2) + 1);	if (err)		goto out1;	sunserial_console_match(SUNSAB_CONSOLE(), op->node,				&sunsab_reg, up[0].port.line);	sunserial_console_match(SUNSAB_CONSOLE(), op->node,				&sunsab_reg, up[1].port.line);	err = uart_add_one_port(&sunsab_reg, &up[0].port);	if (err)		goto out2;	err = uart_add_one_port(&sunsab_reg, &up[1].port);	if (err)		goto out3;	dev_set_drvdata(&op->dev, &up[0]);	inst++;	return 0;out3:	uart_remove_one_port(&sunsab_reg, &up[0].port);out2:	of_iounmap(&op->resource[0],		   up[1].port.membase,		   sizeof(union sab82532_async_regs));out1:	of_iounmap(&op->resource[0],		   up[0].port.membase,		   sizeof(union sab82532_async_regs));out:	return err;}static int __devexit sab_remove(struct of_device *op){	struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);	uart_remove_one_port(&sunsab_reg, &up[1].port);	uart_remove_one_port(&sunsab_reg, &up[0].port);	of_iounmap(&op->resource[0],		   up[1].port.membase,		   sizeof(union sab82532_async_regs));	of_iounmap(&op->resource[0],		   up[0].port.membase,		   sizeof(union sab82532_async_regs));	dev_set_drvdata(&op->dev, NULL);	return 0;}static struct of_device_id sab_match[] = {	{		.name = "se",	},	{		.name = "serial",		.compatible = "sab82532",	},	{},};MODULE_DEVICE_TABLE(of, sab_match);static struct of_platform_driver sab_driver = {	.name		= "sab",	.match_table	= sab_match,	.probe		= sab_probe,	.remove		= __devexit_p(sab_remove),};static int __init sunsab_init(void){	struct device_node *dp;	int err;	int num_channels = 0;	for_each_node_by_name(dp, "se")		num_channels += 2;	for_each_node_by_name(dp, "serial") {		if (of_device_is_compatible(dp, "sab82532"))			num_channels += 2;	}	if (num_channels) {		sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) *				       num_channels, GFP_KERNEL);		if (!sunsab_ports)			return -ENOMEM;		sunsab_reg.cons = SUNSAB_CONSOLE();		err = sunserial_register_minors(&sunsab_reg, num_channels);		if (err) {			kfree(sunsab_ports);			sunsab_ports = NULL;			return err;		}	}	return of_register_driver(&sab_driver, &of_bus_type);}static void __exit sunsab_exit(void){	of_unregister_driver(&sab_driver);	if (sunsab_reg.nr) {		sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr);	}	kfree(sunsab_ports);	sunsab_ports = NULL;}module_init(sunsab_init);module_exit(sunsab_exit);MODULE_AUTHOR("Eddie C. Dost and David S. Miller");MODULE_DESCRIPTION("Sun SAB82532 serial port driver");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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