21_串口驱动

一、linux串口简介

​ 串口驱动芯片厂商已经编写好,当系统启动以后驱动和设备匹配成功,相应的串口就会被驱动起来,生成 /dev/ttymxcX(X=0…n) 文件。

1、uart_driver注册与注销

​ uart_driver 结构体表示 UART 驱动,结构体定义在 include/linux/serial_core.h 中:

struct uart_driver {
	struct module		*owner;			/* 模块所属者 */
	const char		*driver_name;		/* 驱动名字 */
	const char		*dev_name;			/* 设备名字 */
	int			 major;					/* 主设备号 */
	int			 minor;					/* 次设备号 */
	int			 nr;					/* 设备数 */
	struct console		*cons;			/* 控制台 */

	/*
	 * these are private; the low level driver should not
	 * touch these; they should be initialised to NULL
	 */
	struct uart_state	*state;
	struct tty_driver	*tty_driver;
};

​ 定义了 uart_driver 后,加载驱动的时候通过 uart_register_driver 函数向系统注册 uart_driver,函数原型如下:

int uart_register_driver(struct uart_driver *drv)

drv:要注册的 uart_driver 。

返回值:0,成功;负值,失败。

​ 注销驱动时也要注销 uart_driver,用到 uart_unregister_driver 函数,函数原型如下:

void uart_unregister_driver(struct uart_driver *drv)

drv:要注销的 uart_driver 。

2、uart_port 的添加与移除

​ uart_port 表示一个具体的 port,uart_port 义在 include/linux/serial_core.h文件,内容如下:

struct uart_port {
	spinlock_t		lock;			/* port lock */
	unsigned long		iobase;			/* in/out[bwl] */
	unsigned char __iomem	*membase;		/* read/write[bwl] */
	unsigned int		(*serial_in)(struct uart_port *, int);
	void			(*serial_out)(struct uart_port *, int, int);
	void			(*set_termios)(struct uart_port *,
				               struct ktermios *new,
				               struct ktermios *old);
	void			(*set_mctrl)(struct uart_port *, unsigned int);
	int			(*startup)(struct uart_port *port);
	void			(*shutdown)(struct uart_port *port);
	void			(*throttle)(struct uart_port *port);
	void			(*unthrottle)(struct uart_port *port);
	int			(*handle_irq)(struct uart_port *);
	void			(*pm)(struct uart_port *, unsigned int state,
				      unsigned int old);
	void			(*handle_break)(struct uart_port *);
	int			(*rs485_config)(struct uart_port *,
						struct serial_rs485 *rs485);
	unsigned int		irq;			/* irq number */
	unsigned long		irqflags;		/* irq flags  */
	unsigned int		uartclk;		/* base uart clock */
	unsigned int		fifosize;		/* tx fifo size */
	unsigned char		x_char;			/* xon/xoff char */
	unsigned char		regshift;		/* reg offset shift */
	unsigned char		iotype;			/* io access style */
	unsigned char		unused1;
    ......
}

​ uart_port 中有一个 ops 的成员,ops 包含了串口的具体驱动函数。

​ 使用 uart_add_one_port 函数,将 uart_port 和 uart_driver,函数原型如下:

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

drv:此 port 对应的 uart_driver。

uport:要添加到 uart_driver 中的 port。

返回值:0,成功;负值,失败。

​ 卸载 UART 驱动的时候也需要将 uart_port 从相应的 uart_driver 中移除,需要用到 uart_remove_one_port 函数,函数原型如下:

int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)

drv:要卸载的 port 对应的 uart_driver。

uport:要卸载的 uart_port 。

返回值:0,成功;负值,失败。

3、uart_ops实现

​ linux 系统收发数据最终调用的都是 ops 中的函数。ops 是 uart_ops 类型的结构体指针变量,uart_ops 定义在 include/linux/serial_core.h中:

struct uart_ops {
	unsigned int	(*tx_empty)(struct uart_port *);
	void		(*set_mctrl)(struct uart_port *, unsigned int mctrl);
	unsigned int	(*get_mctrl)(struct uart_port *);
	void		(*stop_tx)(struct uart_port *);
	void		(*start_tx)(struct uart_port *);
	void		(*throttle)(struct uart_port *);
	void		(*unthrottle)(struct uart_port *);
	void		(*send_xchar)(struct uart_port *, char ch);
	void		(*stop_rx)(struct uart_port *);
	void		(*enable_ms)(struct uart_port *);
	void		(*break_ctl)(struct uart_port *, int ctl);
	int		(*startup)(struct uart_port *);
	void		(*shutdown)(struct uart_port *);
	void		(*flush_buffer)(struct uart_port *);
	void		(*set_termios)(struct uart_port *, struct ktermios *new,
				       struct ktermios *old);
	void		(*set_ldisc)(struct uart_port *, struct ktermios *);
	void		(*pm)(struct uart_port *, unsigned int state,
			      unsigned int oldstate);

	/*
	 * Return a string describing the type of the port
	 */
	const char	*(*type)(struct uart_port *);

	/*
	 * Release IO and memory resources used by the port.
	 * This includes iounmap if necessary.
	 */
	void		(*release_port)(struct uart_port *);

	/*
	 * Request IO and memory resources used by the port.
	 * This includes iomapping the port if necessary.
	 */
	int		(*request_port)(struct uart_port *);
	void		(*config_port)(struct uart_port *, int);
	int		(*verify_port)(struct uart_port *, struct serial_struct *);
	int		(*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
	int		(*poll_init)(struct uart_port *);
	void		(*poll_put_char)(struct uart_port *, unsigned char);
	int		(*poll_get_char)(struct uart_port *);
#endif
};

​ UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,要和 UART 寄存器打交道。

二、IMX6U串口驱动分析

1、设备树节点

​ 找到 UART3 对应的设备树节点:

uart3: serial@021ec000 {
    compatible = "fsl,imx6ul-uart",
             "fsl,imx6q-uart", "fsl,imx21-uart";
    reg = <0x021ec000 0x4000>;
    interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_UART3_IPG>,
         <&clks IMX6UL_CLK_UART3_SERIAL>;
    clock-names = "ipg", "per";
    dmas = <&sdma 29 4 0>, <&sdma 30 4 0>;
    dma-names = "rx", "tx";
    status = "disabled";
};

2、串口驱动

1)匹配框架

​ 根据 cpmpatible 属性找到串口驱动文件 drivers/tty/serial/imx.c

/* 传统匹配列表 */
static struct platform_device_id imx_uart_devtype[] = {
	{
		.name = "imx1-uart",
		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
	}, {
		.name = "imx21-uart",
		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
	}, {
		.name = "imx6q-uart",
		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(platform, imx_uart_devtype);

/* 设备树匹配列表 */
static const struct of_device_id imx_uart_dt_ids[] = {
	{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
	{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
	{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);

/* platform驱动结构体 */
static struct platform_driver serial_imx_driver = {
	.probe		= serial_imx_probe,
	.remove		= serial_imx_remove,

	.suspend	= serial_imx_suspend,
	.resume		= serial_imx_resume,
	.id_table	= imx_uart_devtype,
	.driver		= {
		.name	= "imx-uart",
		.of_match_table = imx_uart_dt_ids,
	},
};

/* 入口函数 */
static int __init imx_serial_init(void)
{
	int ret = uart_register_driver(&imx_reg);

	if (ret)
		return ret;

	ret = platform_driver_register(&serial_imx_driver);
	if (ret != 0)
		uart_unregister_driver(&imx_reg);

	return ret;
}

/* 出口函数 */
static void __exit imx_serial_exit(void)
{
	platform_driver_unregister(&serial_imx_driver);
	uart_unregister_driver(&imx_reg);
}

module_init(imx_serial_init);
module_exit(imx_serial_exit);

2)uart_driver初始化

​ 在 imx_serial_init 函数中向 Linux 内核注册了 imx_reg imx_reg 就是 uart_driver 类型的结构体变量, imx_reg 定义如下:

static struct uart_driver imx_reg = {
	.owner          = THIS_MODULE,
	.driver_name    = DRIVER_NAME,
	.dev_name       = DEV_NAME,
	.major          = SERIAL_IMX_MAJOR,
	.minor          = MINOR_START,
	.nr             = ARRAY_SIZE(imx_ports),
	.cons           = IMX_CONSOLE,
};

3)uart_port初始化与添加

​ 在 probe 函数中,重点初始化 uart_port,将其添加到 uart_driver 中。uart_port 定义在 imx_port 结构体中:

struct imx_port {
	struct uart_port	port;
	struct timer_list	timer;
	unsigned int		old_status;
	unsigned int		have_rtscts:1;
	unsigned int		dte_mode:1;
	unsigned int		irda_inv_rx:1;
	unsigned int		irda_inv_tx:1;
	unsigned short		trcv_delay; /* transceiver delay */
	struct clk		*clk_ipg;
	struct clk		*clk_per;
	const struct imx_uart_data *devdata;

	/* DMA fields */
	unsigned int		dma_is_inited:1;
	unsigned int		dma_is_enabled:1;
	unsigned int		dma_is_rxing:1;
	unsigned int		dma_is_txing:1;
	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
	struct scatterlist	tx_sgl[2];
	struct imx_dma_rxbuf	rx_buf;
	unsigned int		tx_bytes;
	unsigned int		dma_tx_nents;
	struct delayed_work	tsk_dma_tx;
	wait_queue_head_t	dma_wait;
	unsigned int            saved_reg[10];
#define DMA_TX_IS_WORKING 1
	unsigned long		flags;
};

​ 具体的 probe 函数如下:

static int serial_imx_probe(struct platform_device *pdev)
{
	struct imx_port *sport;		//定义一个 imx_port 类型的指针
	void __iomem *base;
	int ret = 0;
	struct resource *res;
	int txirq, rxirq, rtsirq;

	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);	//申请内存
	if (!sport)
		return -ENOMEM;

	ret = serial_imx_probe_dt(sport, pdev);
	if (ret > 0)
		serial_imx_probe_pdata(sport, pdev);
	else if (ret < 0)
		return ret;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	//获取寄存器首地址
	base = devm_ioremap_resource(&pdev->dev, res);	//地址映射
	if (IS_ERR(base))
		return PTR_ERR(base);

	rxirq = platform_get_irq(pdev, 0);	//获取中断信息
	txirq = platform_get_irq(pdev, 1);
	rtsirq = platform_get_irq(pdev, 2);

    /* 初始化sport */
	sport->port.dev = &pdev->dev;
	sport->port.mapbase = res->start;
	sport->port.membase = base;
	sport->port.type = PORT_IMX,
	sport->port.iotype = UPIO_MEM;
	sport->port.irq = rxirq;
	sport->port.fifosize = 32;
	sport->port.ops = &imx_pops;		//初始化驱动函数集合
	sport->port.rs485_config = imx_rs485_config;
	sport->port.rs485.flags =
		SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
	sport->port.flags = UPF_BOOT_AUTOCONF;
	init_timer(&sport->timer);
	sport->timer.function = imx_timeout;
	sport->timer.data     = (unsigned long)sport;

	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(sport->clk_ipg)) {
		ret = PTR_ERR(sport->clk_ipg);
		dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
		return ret;
	}

	sport->clk_per = devm_clk_get(&pdev->dev, "per");
	if (IS_ERR(sport->clk_per)) {
		ret = PTR_ERR(sport->clk_per);
		dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
		return ret;
	}

	sport->port.uartclk = clk_get_rate(sport->clk_per);
	if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
		ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
		if (ret < 0) {
			dev_err(&pdev->dev, "clk_set_rate() failed\n");
			return ret;
		}
	}
	sport->port.uartclk = clk_get_rate(sport->clk_per);

	/*
	 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
	 * chips only have one interrupt.
	 */
	if (txirq > 0) {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,		//申请中断
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;

		ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,		//申请中断
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;
	} else {
		ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
				       dev_name(&pdev->dev), sport);
		if (ret)
			return ret;
	}

	imx_ports[sport->port.line] = sport;

	platform_set_drvdata(pdev, sport);

	return uart_add_one_port(&imx_reg, &sport->port);	//向 uart_driver 添加 uart_port
}

4)imx_pops结构体变量

​ imx_pops 是串口最底层的操作函数,定义如下:

static struct uart_ops imx_pops = {
	.tx_empty	= imx_tx_empty,
	.set_mctrl	= imx_set_mctrl,
	.get_mctrl	= imx_get_mctrl,
	.stop_tx	= imx_stop_tx,
	.start_tx	= imx_start_tx,
	.stop_rx	= imx_stop_rx,
	.enable_ms	= imx_enable_ms,
	.break_ctl	= imx_break_ctl,
	.startup	= imx_startup,
	.shutdown	= imx_shutdown,
	.flush_buffer	= imx_flush_buffer,
	.set_termios	= imx_set_termios,
	.type		= imx_type,
	.config_port	= imx_config_port,
	.verify_port	= imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
	.poll_init      = imx_poll_init,
	.poll_get_char  = imx_poll_get_char,
	.poll_put_char  = imx_poll_put_char,
#endif
};

三、串口使用

1、添加设备树节点

&uart3 { 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_uart3>; 
    status = "okay"; 
};

2、添加pinctrl节点

pinctrl_uart3: uart3grp { 
    fsl,pins = < 
        MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0X1b0b1 
        MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0X1b0b1 
    >; 
};

​ 完成以后重新编译设备树并使用新的设备树启动 Linux 如果设备树修改成功的话,系统启动以后就会生成一个名为 “/dev/ttymxc2” 的设备文件 ttymxc2 就是 UART3 对应的设备文件,应用程序可以通过访问 ttymxc2 来实现对 UART3的操作。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值