sunsab.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,166 行 · 第 1/2 页

C
1,166
字号
	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;	unsigned char tmp;	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 */	tmp = readb(&up->regs->rw.dafo);	tmp &= ~SAB82532_DAFO_XBRK;	writeb(tmp, &up->regs->rw.dafo);	/* Disable Receiver */		tmp = readb(&up->regs->rw.mode);	tmp &= ~SAB82532_MODE_RAC;	writeb(tmp, &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);}/* * 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, int baud){	unsigned int ebrg;	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;	}	calc_ebrg(baud, &n, &m);	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);	/* Now bang the new settings into the chip.  */	sunsab_cec_wait(up);	sunsab_tec_wait(up);	writeb(dafo, &up->regs->w.dafo);	writeb(ebrg & 0xff, &up->regs->w.bgr);	writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0),	       &up->regs->rw.ccr2);	writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);}/* port->lock is not held.  */static void sunsab_set_termios(struct uart_port *port, struct termios *termios,			       struct termios *old){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);	spin_lock_irqsave(&up->port.lock, flags);	sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud);	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",	.devfs_name		= "tts/",	.dev_name		= "ttyS",	.major			= TTY_MAJOR,};static struct uart_sunsab_port *sunsab_ports;static int num_channels;#ifdef CONFIG_SERIAL_SUNSAB_CONSOLEstatic __inline__ void sunsab_console_putchar(struct uart_sunsab_port *up, char c){	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	sunsab_tec_wait(up);	writeb(c, &up->regs->w.tic);	spin_unlock_irqrestore(&up->port.lock, flags);}static void sunsab_console_write(struct console *con, const char *s, unsigned n){	struct uart_sunsab_port *up = &sunsab_ports[con->index];	int i;	for (i = 0; i < n; i++) {		if (*s == '\n')			sunsab_console_putchar(up, '\r');		sunsab_console_putchar(up, *s++);	}	sunsab_tec_wait(up);}static int sunsab_console_setup(struct console *con, char *options){	struct uart_sunsab_port *up = &sunsab_ports[con->index];	unsigned long flags;	int baud;	printk("Console: ttyS%d (SAB82532)\n",	       (sunsab_reg.minor - 64) + con->index);	sunserial_console_termios(con);	/* Firmware console speed is limited to 150-->38400 baud so	 * this hackish cflag thing is OK.	 */	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;	};	/*	 * 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);	sunsab_convert_to_sab(up, con->cflag, 0, baud);	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,};#define SUNSAB_CONSOLE	(&sunsab_console)static void __init sunsab_console_init(void){	int i;	if (con_is_present())		return;	for (i = 0; i < num_channels; i++) {		int this_minor = sunsab_reg.minor + i;		if ((this_minor - 64) == (serial_console - 1))			break;	}	if (i == num_channels)		return;	sunsab_console.index = i;	register_console(&sunsab_console);}#else#define SUNSAB_CONSOLE		(NULL)#define sunsab_console_init()	do { } while (0)#endifstatic void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg){	struct linux_ebus *ebus;	struct linux_ebus_device *edev = NULL;	for_each_ebus(ebus) {		for_each_ebusdev(edev, ebus) {			if (!strcmp(edev->prom_name, "se")) {				callback(edev, arg);				continue;			} else if (!strcmp(edev->prom_name, "serial")) {				char compat[32];				int clen;				/* On RIO this can be an SE, check it.  We could				 * just check ebus->is_rio, but this is more portable.				 */				clen = prom_getproperty(edev->prom_node, "compatible",							compat, sizeof(compat));				if (clen > 0) {					if (strncmp(compat, "sab82532", 8) == 0) {						callback(edev, arg);						continue;					}				}			}		}	}}static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg){	int *count_p = arg;	(*count_p)++;}static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg){	int *instance_p = arg;	struct uart_sunsab_port *up;	unsigned long regs, offset;	int i;	/* Note: ports are located in reverse order */	regs = edev->resource[0].start;	offset = sizeof(union sab82532_async_regs);	for (i = 0; i < 2; i++) {		up = &sunsab_ports[(*instance_p * 2) + 1 - i];		memset(up, 0, sizeof(*up));		up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));		up->port.irq = edev->irqs[0];		up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;		up->port.mapbase = (unsigned long)up->regs;		up->port.iotype = SERIAL_IO_MEM;		writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);		offset -= sizeof(union sab82532_async_regs);	}		(*instance_p)++;}static int __init probe_for_sabs(void){	int this_sab = 0;	/* Find device instances.  */	for_each_sab_edev(&sab_count_callback, &this_sab);	if (!this_sab)		return -ENODEV;	/* Allocate tables.  */	sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2,			       GFP_KERNEL);	if (!sunsab_ports)		return -ENOMEM;	num_channels = this_sab * 2;	this_sab = 0;	for_each_sab_edev(&sab_attach_callback, &this_sab);	return 0;}static void __init sunsab_init_hw(void){	int i;	for (i = 0; i < num_channels; i++) {		struct uart_sunsab_port *up = &sunsab_ports[i];		up->port.line = i;		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 == 0) {			up->pvr_dsr_bit = (1 << 0);			up->pvr_dtr_bit = (1 << 1);		} else {			up->pvr_dsr_bit = (1 << 3);			up->pvr_dtr_bit = (1 << 2);		}		writeb((1 << 1) | (1 << 2) | (1 << 4), &up->regs->w.pvr);		writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,		       &up->regs->rw.mode);		writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,		       &up->regs->rw.mode);		up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;		up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;		if (!(up->port.line & 0x01)) {			if (request_irq(up->port.irq, sunsab_interrupt,			                SA_SHIRQ, "serial(sab82532)", up)) {				printk("sunsab%d: can't get IRQ %x\n",				       i, up->port.irq);				continue;			}		}	}}static int __init sunsab_init(void){	int ret = probe_for_sabs();	int i;	if (ret < 0)		return ret;	sunsab_init_hw();	sunsab_reg.minor = sunserial_current_minor;	sunsab_reg.nr = num_channels;	sunsab_reg.cons = SUNSAB_CONSOLE;	ret = uart_register_driver(&sunsab_reg);	if (ret < 0) {		int i;		for (i = 0; i < num_channels; i++) {			struct uart_sunsab_port *up = &sunsab_ports[i];			if (!(up->port.line & 0x01))				free_irq(up->port.irq, up);			iounmap(up->regs);		}		kfree(sunsab_ports);		sunsab_ports = NULL;		return ret;	}	sunserial_current_minor += num_channels;		for (i = 0; i < num_channels; i++) {		struct uart_sunsab_port *up = &sunsab_ports[i];		uart_add_one_port(&sunsab_reg, &up->port);	}	sunsab_console_init();	return 0;}static void __exit sunsab_exit(void){	int i;	for (i = 0; i < num_channels; i++) {		struct uart_sunsab_port *up = &sunsab_ports[i];		uart_remove_one_port(&sunsab_reg, &up->port);		if (!(up->port.line & 0x01))			free_irq(up->port.irq, up);		iounmap(up->regs);	}	sunserial_current_minor -= num_channels;	uart_unregister_driver(&sunsab_reg);	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 + -
显示快捷键?