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 + -
显示快捷键?