pl011设备驱动浅析

文章目录

  • 设备驱动流程
    • init
    • 关键函数注册
      • pl011_driver
      • pl011_probe
      • amba_pl011_pops
  • pl011设备驱动实现
    • pl011_probe
      • pl011_find_free_port
      • pl011_setup_port
      • pl011_register_port
    • setup args of dev->dev
      • irq
    • ops
      • read/write
      • pl011_startup
      • pl011_hwinit
      • pl011_allocate_irq
      • pl011_int
      • 读写数据

设备驱动流程

设备驱动中必要的流程函数

init

static int __init pl011_init(void)
{
	printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");

	if (platform_driver_register(&arm_sbsa_uart_platform_driver))
		pr_warn("could not register SBSA UART platform driver\n");
	return amba_driver_register(&pl011_driver);
}

先调用platform_driver_register尝试注册arm_sbsa_uart_platform_driver,(平台总线设备
再调用amba_driver_register注册pl011_driver,(arm总线设备
然而module_init会根据init的返回值判断是否加载成功,所以这里主要是看amba是否加载成功,sbsa只是附加的,因此下面重点关注pl011_driver

关键函数注册

pl011_driver

static struct amba_driver pl011_driver = {
	.drv = {
		.name	= "uart-pl011",
		.pm	= &pl011_dev_pm_ops,
		.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
	},
	.id_table	= pl011_ids,
	.probe		= pl011_probe,
	.remove		= pl011_remove,
};

pl011_probe

probe里注册ops

uap->port.ops = &amba_pl011_pops;

amba_pl011_pops

ops里注册read、write… (不过这里是tx、rx之类的

static const struct uart_ops amba_pl011_pops = {
	.tx_empty	= pl011_tx_empty,
	.set_mctrl	= pl011_set_mctrl,
	.get_mctrl	= pl011_get_mctrl,
	.stop_tx	= pl011_stop_tx,
	.start_tx	= pl011_start_tx,
	.stop_rx	= pl011_stop_rx,
	.throttle	= pl011_throttle_rx,
	.unthrottle	= pl011_unthrottle_rx,
	.enable_ms	= pl011_enable_ms,
	.break_ctl	= pl011_break_ctl,
	.startup	= pl011_startup,
	.shutdown	= pl011_shutdown,
	.flush_buffer	= pl011_dma_flush_buffer,
	.set_termios	= pl011_set_termios,
	.type		= pl011_type,
	.config_port	= pl011_config_port,
	.verify_port	= pl011_verify_port
};

pl011设备驱动实现

pl011设备驱动的具体实现,从probe开始

pl011_probe

pl011_probe
	pl011_find_free_port
	setup args of dev->dev
	pl011_setup_port
	amba_set_drvdata
	pl011_register_port

分配、占用uart端口
设置dev结构的一些属性
配置端口数据
注册到驱动框架

pl011_find_free_port

static int pl011_find_free_port(void)
	for (int i = 0; i < ARRAY_SIZE(amba_ports); i++)
		if (amba_ports[i] == NULL)
			return i;
	return -EBUSY;
	
static struct uart_amba_port *amba_ports[UART_NR];

arm soc有固定的uart数量,这个函数的功能是分配并占用一个
(这个amba_ports不是系统层面的,只是这个驱动层面的,也就是只能统计使用这个驱动注册的uart端口

pl011_setup_port

设置struct uart_amba_port 类型的*uap 里port的属性
port是struct uart_port类型
设置了dev、membase等
然后把amba_ports[index] 给占住

pl011_register_port

调用uart_register_driver,将struct uart_driver类型的amba_reg注册到uart驱动框架
执行uart_add_one_port,调用设备驱动框架的serial_core_register_port

setup args of dev->dev

uap->reg_offset = vendor->reg_offset;
uap->vendor = vendor;
uap->fifosize = vendor->get_fifosize(dev);
uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.irq = dev->irq[0];
uap->port.ops = &amba_pl011_pops;
uap->port.rs485_config = pl011_rs485_config;
uap->port.rs485_supported = pl011_rs485_supported;
snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev));

这里有几个需要注意的:
uap->vendor = vendor; 而vendor = id->data,这里就是把id里的void*信息挂上去
uap->port.irq = dev->irq[0]; 看起来像是挂了个中断
uap->port.ops = & amba_pl011_pops; 注册ops

irq

static int pl011_allocate_irq(struct uart_amba_port *uap)
	pl011_write(uap->im, uap, REG_IMSC);
	return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);

dev->irq[0]据说是从设备树里获取的 第一个中断号
uap->port.irq在这里进行注册

ops

ops有点大,得扩展成一个二级目录来讲

static const struct uart_ops amba_pl011_pops = {
	.tx_empty	= pl011_tx_empty,
	.stop_tx	= pl011_stop_tx,
	.start_tx	= pl011_start_tx,
	.stop_rx	= pl011_stop_rx,
	.startup	= pl011_startup,
	.shutdown	= pl011_shutdown,
	.set_termios	= pl011_set_termios
	...
};

这里挑几个来看,全量的ops内容见前段
(uart的异步接收是自动触发的,因此无需start_rx

read/write

pl011_read

static unsigned int pl011_read(const struct uart_amba_port *uap,
	unsigned int reg)
{
	void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg);

	return (uap->port.iotype == UPIO_MEM32) ?
		readl_relaxed(addr) : readw_relaxed(addr);
}

pl011_write

static void pl011_write(unsigned int val, const struct uart_amba_port *uap,
	unsigned int reg)
{
	void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg);

	if (uap->port.iotype == UPIO_MEM32)
		writel_relaxed(val, addr);
	else
		writew_relaxed(val, addr);
}

readl/writel_relaxed是个读写用的函数,w是16位
所以这里只是通过iomem映射,然后读写寄存器的普通操作

pl011_startup

简要:

pl011_startup
	pl011_hwinit
	pl011_allocate_irq
		pl011_int
			pl011_read
			pl011_write
static int pl011_startup(struct uart_port *port)
{
	struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port);
	unsigned int cr;
	int retval;

	retval = pl011_hwinit(port);
	if (retval)
		goto clk_dis;

	retval = pl011_allocate_irq(uap);
	if (retval)
		goto clk_dis;

	pl011_write(uap->vendor->ifls, uap, REG_IFLS);

	spin_lock_irq(&uap->port.lock);

	cr = pl011_read(uap, REG_CR);
	cr &= UART011_CR_RTS | UART011_CR_DTR;
	cr |= UART01x_CR_UARTEN | UART011_CR_RXE;

	if (!(port->rs485.flags & SER_RS485_ENABLED))
		cr |= UART011_CR_TXE;

	pl011_write(cr, uap, REG_CR);

	spin_unlock_irq(&uap->port.lock);

	/*
	 * initialise the old status of the modem signals
	 */
	uap->old_status = pl011_read(uap, REG_FR) & UART01x_FR_MODEM_ANY;

	/* Startup DMA */
	pl011_dma_startup(uap);

	pl011_enable_interrupts(uap);

	return 0;

 clk_dis:
	clk_disable_unprepare(uap->clk);
	return retval;
}

pl011_hwinit

static int pl011_hwinit(struct uart_port *port)
	pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS |
			    UART011_FEIS | UART011_RTIS | UART011_RXIS,
			    uap, REG_ICR);
	uap->im = pl011_read(uap, REG_IMSC);
	pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC);

读写了REG_ICR、REG_IMSC寄存器

pl011_allocate_irq

static int pl011_allocate_irq(struct uart_amba_port *uap)
	pl011_write(uap->im, uap, REG_IMSC);
	return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);
// 中断号,中断处理函数,flag,中断名,void*数据
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

所以allocate_irq里穿进去的pl011_int是中断处理程序

pl011_int

static irqreturn_t pl011_int(int irq, void *dev_id)
{
// 中断上下文初始化
	struct uart_amba_port *uap = dev_id;
	unsigned long flags;
	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
	int handled = 0;

	spin_lock_irqsave(&uap->port.lock, flags);
	
// 读取并过滤中断状态
	status = pl011_read(uap, REG_RIS) & uap->im;
	if (status) {

// 循环处理中断事件(最多AMBA_ISR_PASS_LIMIT次循环
		do {
			check_apply_cts_event_workaround(uap);
				
			// 清除中断标志
			pl011_write(status & ~(UART011_TXIS|UART011_RTIS| UART011_RXIS), uap, REG_ICR);

			// 处理接收中断
			if (status & (UART011_RTIS|UART011_RXIS)) {
				if (pl011_dma_rx_running(uap))
					pl011_dma_rx_irq(uap);
				else
					pl011_rx_chars(uap); // 普通模式读取接收的数据
			}
			
			if (status & (UART011_DSRMIS|UART011_DCDMIS| UART011_CTSMIS|UART011_RIMIS))
				pl011_modem_status(uap);
				
			// 处理发送中断
			if (status & UART011_TXIS)
				pl011_tx_chars(uap, true);

			if (pass_counter-- == 0)
				break;
				
			// 重新检查是否有新中断到达(如果status为1且还没到256轮,就接着循环去了
			status = pl011_read(uap, REG_RIS) & uap->im;
		} while (status != 0);
		handled = 1;
	}

// 退出中断上下文
	spin_unlock_irqrestore(&uap->port.lock, flags);
	return IRQ_RETVAL(handled);  // 返回中断处理结果
}

读写数据

pl011_tx_chars

pl011_rx_chars
pl011_fifo_to_tty
pl011_fifo_to_tty这里设置的一轮读取最多256个字符

你可能感兴趣的:(Linux,linux,数据库,运维)