⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c2410_serial.c

📁 S3C2410的串口驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
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 + -