📄 s3c2410_serial.c
字号:
static struct s3c24xx_uart_clksrc tmp_clksrc = { .name = "pclk", .min_baud = 0, .max_baud = 0, .divisor = 1,};static inline ints3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c){ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); return (info->get_clksrc)(port, c);}static inline ints3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c){ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); return (info->set_clksrc)(port, c);}struct baud_calc { struct s3c24xx_uart_clksrc *clksrc; unsigned int calc; unsigned int quot; struct clk *src;};static int s3c24xx_serial_calcbaud(struct baud_calc *calc, struct uart_port *port, struct s3c24xx_uart_clksrc *clksrc, unsigned int baud){ unsigned long rate; calc->src = clk_get(port->dev, clksrc->name); if (calc->src == NULL || IS_ERR(calc->src)) return 0; rate = clk_get_rate(calc->src); rate /= clksrc->divisor; calc->clksrc = clksrc; calc->quot = (rate + (8 * baud)) / (16 * baud); calc->calc = (rate / (calc->quot * 16)); calc->quot--; return 1;}static unsigned int s3c24xx_serial_getclk(struct uart_port *port, struct s3c24xx_uart_clksrc **clksrc, struct clk **clk, unsigned int baud){ struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); struct s3c24xx_uart_clksrc *clkp; struct baud_calc res[MAX_CLKS]; struct baud_calc *resptr, *best, *sptr; int i; clkp = cfg->clocks; best = NULL; if (cfg->clocks_size < 2) { if (cfg->clocks_size == 0) clkp = &tmp_clksrc; /* check to see if we're sourcing fclk, and if so we're * going to have to update the clock source */ if (strcmp(clkp->name, "fclk") == 0) { struct s3c24xx_uart_clksrc src; s3c24xx_serial_getsource(port, &src); /* check that the port already using fclk, and if * not, then re-select fclk */ if (strcmp(src.name, clkp->name) == 0) { s3c24xx_serial_setsource(port, clkp); s3c24xx_serial_getsource(port, &src); } clkp->divisor = src.divisor; } s3c24xx_serial_calcbaud(res, port, clkp, baud); best = res; resptr = best + 1; } else { resptr = res; for (i = 0; i < cfg->clocks_size; i++, clkp++) { if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) resptr++; } } /* ok, we now need to select the best clock we found */ if (!best) { unsigned int deviation = (1<<30)|((1<<30)-1); int calc_deviation; for (sptr = res; sptr < resptr; sptr++) { printk(KERN_DEBUG "found clk %p (%s) quot %d, calc %d\n", sptr->clksrc, sptr->clksrc->name, sptr->quot, sptr->calc); calc_deviation = baud - sptr->calc; if (calc_deviation < 0) calc_deviation = -calc_deviation; if (calc_deviation < deviation) { best = sptr; deviation = calc_deviation; } } printk(KERN_DEBUG "best %p (deviation %d)\n", best, deviation); } printk(KERN_DEBUG "selected clock %p (%s) quot %d, calc %d\n", best->clksrc, best->clksrc->name, best->quot, best->calc); /* store results to pass back */ *clksrc = best->clksrc; *clk = best->src; return best->quot;}static void s3c24xx_serial_set_termios(struct uart_port *port, struct termios *termios, struct termios *old){ struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); struct s3c24xx_uart_port *ourport = to_ourport(port); struct s3c24xx_uart_clksrc *clksrc = NULL; struct clk *clk = NULL; unsigned long flags; unsigned int baud, quot; unsigned int ulcon; unsigned int umcon; /* * We don't support modem control lines. */ termios->c_cflag &= ~(HUPCL | CMSPAR); termios->c_cflag |= CLOCAL; /* * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; else quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); /* check to see if we need to change clock source */ if (ourport->clksrc != clksrc || ourport->baudclk != clk) { s3c24xx_serial_setsource(port, clksrc); if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { clk_disable(ourport->baudclk); ourport->baudclk = NULL; } clk_enable(clk); ourport->clksrc = clksrc; ourport->baudclk = clk; } switch (termios->c_cflag & CSIZE) { case CS5: dbg("config: 5bits/char\n"); ulcon = S3C2410_LCON_CS5; break; case CS6: dbg("config: 6bits/char\n"); ulcon = S3C2410_LCON_CS6; break; case CS7: dbg("config: 7bits/char\n"); ulcon = S3C2410_LCON_CS7; break; case CS8: default: dbg("config: 8bits/char\n"); ulcon = S3C2410_LCON_CS8; break; } /* preserve original lcon IR settings */ ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); if (termios->c_cflag & CSTOPB) ulcon |= S3C2410_LCON_STOPB; umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; if (termios->c_cflag & PARENB) { if (termios->c_cflag & PARODD) ulcon |= S3C2410_LCON_PODD; else ulcon |= S3C2410_LCON_PEVEN; } else { ulcon |= S3C2410_LCON_PNONE; } spin_lock_irqsave(&port->lock, flags); dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot); wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); wr_regl(port, S3C2410_UMCON, umcon); dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", rd_regl(port, S3C2410_ULCON), rd_regl(port, S3C2410_UCON), rd_regl(port, S3C2410_UFCON)); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* * Which character status flags are we interested in? */ port->read_status_mask = S3C2410_UERSTAT_OVERRUN; if (termios->c_iflag & INPCK) port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; /* * Which character status flags should we ignore? */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; /* * Ignore all characters if CREAD is not set. */ if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= RXSTAT_DUMMY_READ; spin_unlock_irqrestore(&port->lock, flags);}static const char *s3c24xx_serial_type(struct uart_port *port){ switch (port->type) { case PORT_S3C2410: return "S3C2410"; case PORT_S3C2440: return "S3C2440"; default: return NULL; }}#define MAP_SIZE (0x100)static void s3c24xx_serial_release_port(struct uart_port *port){ release_mem_region(port->mapbase, MAP_SIZE);}static int s3c24xx_serial_request_port(struct uart_port *port){ const char *name = s3c24xx_serial_portname(port); return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;}static void s3c24xx_serial_config_port(struct uart_port *port, int flags){ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); if (flags & UART_CONFIG_TYPE && s3c24xx_serial_request_port(port) == 0) port->type = info->type;}/* * verify the new serial_struct (for TIOCSSERIAL). */static ints3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser){ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); if (ser->type != PORT_UNKNOWN && ser->type != info->type) return -EINVAL; return 0;}#ifdef CONFIG_SERIAL_S3C2410_CONSOLEstatic struct console s3c24xx_serial_console;#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console#else#define S3C24XX_SERIAL_CONSOLE NULL#endifstatic struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, .set_mctrl = s3c24xx_serial_set_mctrl, .stop_tx = s3c24xx_serial_stop_tx, .start_tx = s3c24xx_serial_start_tx, .stop_rx = s3c24xx_serial_stop_rx, .enable_ms = s3c24xx_serial_enable_ms, .break_ctl = s3c24xx_serial_break_ctl, .startup = s3c24xx_serial_startup, .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, .release_port = s3c24xx_serial_release_port, .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port,};static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = "s3c2410_serial", .nr = 3, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME, .devfs_name = S3C24XX_SERIAL_DEVFS, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR,};static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = { [0] = { .port = { .lock = SPIN_LOCK_UNLOCKED, .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX0, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 0, } }, [1] = { .port = { .lock = SPIN_LOCK_UNLOCKED, .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX1, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 1, } },#if NR_PORTS > 2 [2] = { .port = { .lock = SPIN_LOCK_UNLOCKED, .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX2, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 2, } }#endif};/* s3c24xx_serial_resetport * * wrapper to call the specific reset for this port (reset the fifos * and the settings)*/static inline int s3c24xx_serial_resetport(struct uart_port * port, struct s3c2410_uartcfg *cfg){ struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); return (info->reset_port)(port, cfg);}/* s3c24xx_serial_init_port * * initialise a single serial port from the platform device given */static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, struct s3c24xx_uart_info *info, struct platform_device *platdev){ struct uart_port *port = &ourport->port; struct s3c2410_uartcfg *cfg; struct resource *res; dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); if (platdev == NULL) return -ENODEV; cfg = s3c24xx_dev_to_cfg(&platdev->dev); if (port->mapbase != 0) return 0; if (cfg->hwport > 3) return -EINVAL; /* setup info for port */ port->dev = &platdev->dev; ourport->info = info; /* copy the info in from provided structure */ ourport->port.fifosize = info->fifosize; dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); port->uartclk = 1; if (cfg->uart_flags & UPF_CONS_FLOW) { dbg("s3c24xx_serial_init_port: enabling flow control\n"); port->flags |= UPF_CONS_FLOW; } /* sort our the physical and virtual addresses for each UART */ res = platform_get_resource(platdev, IORESOURCE_MEM, 0); if (res == NULL) { printk(KERN_ERR "failed to find memory resource for uart\n"); return -EINVAL; } dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); port->mapbase = res->start; port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART); port->irq = platform_get_irq(platdev, 0); ourport->clk = clk_get(&platdev->dev, "uart"); dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n", port->mapbase, port->membase, port->irq, port->uartclk); /* reset the fifos (and setup the uart) */ s3c24xx_serial_resetport(port, cfg); return 0;}/* Device driver serial port probe */static int probe_index = 0;static int s3c24xx_serial_probe(struct platform_device *dev, struct s3c24xx_uart_info *info){ struct s3c24xx_uart_port *ourport; int ret; dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); ourport = &s3c24xx_serial_ports[probe_index]; probe_index++; dbg("%s: initialising port %p...\n", __FUNCTION__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev); if (ret < 0) goto probe_err; dbg("%s: adding port\n", __FUNCTION__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(dev, &ourport->port); return 0; probe_err: return ret;}static int s3c24xx_serial_remove(struct platform_device *dev){ struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); if (port) uart_remove_one_port(&s3c24xx_uart_drv, port); return 0;}/* UART power management code */#ifdef CONFIG_PMstatic int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state){ struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); if (port) uart_suspend_port(&s3c24xx_uart_drv, port); return 0;}static int s3c24xx_serial_resume(struct platform_device *dev){ struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { clk_enable(ourport->clk); s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); clk_disable(ourport->clk); uart_resume_port(&s3c24xx_uart_drv, port); } return 0;}#else#define s3c24xx_serial_suspend NULL#define s3c24xx_serial_resume NULL#endifstatic int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info){ dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); return platform_driver_register(drv);}/* now comes the code to initialise either the s3c2410 or s3c2440 serial * port information*//* cpu specific variations on the serial port support */#ifdef CONFIG_CPU_S3C2400static int s3c2400_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk){ clk->divisor = 1; clk->name = "pclk"; return 0;}static int s3c2400_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk){ return 0;}static int s3c2400_serial_resetport(struct uart_port *port, struct s3c2410_uartcfg *cfg){ dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n", port, port->mapbase, cfg); wr_regl(port, S3C2410_UCON, cfg->ucon); wr_regl(port, S3C2410_ULCON, cfg->ulcon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); return 0;}static struct s3c24xx_uart_info s3c2400_uart_inf = {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -