设备驱动中必要的流程函数
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
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,
};
probe里注册ops
uap->port.ops = &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设备驱动的具体实现,从probe开始
pl011_probe
pl011_find_free_port
setup args of dev->dev
pl011_setup_port
amba_set_drvdata
pl011_register_port
分配、占用uart端口
设置dev结构的一些属性
配置端口数据
注册到驱动框架
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端口
设置struct uart_amba_port 类型的*uap 里port的属性
port是struct uart_port类型
设置了dev、membase等
然后把amba_ports[index] 给占住
调用uart_register_driver,将struct uart_driver类型的amba_reg注册到uart驱动框架
执行uart_add_one_port,调用设备驱动框架的serial_core_register_port
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
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有点大,得扩展成一个二级目录来讲
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
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_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;
}
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寄存器
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是中断处理程序
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个字符