static int sbsa_uart_probe(struct platform_device *pdev) { struct uart_amba_port *uap; struct resource r; int portnr, ret; int baudrate; / * Check the mandatory baud rate parameter in the DT node early * so that we can easily exit with the error. */ if (pdev->dev.of_node) { struct device_node *np = pdev->dev.of_node; ret = of_property_read_u32(np, "current-speed", &baudrate); if (ret) return ret; } else { baudrate = 115200; } portnr = pl011_find_free_port(); if (portnr < 0) return portnr; uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), GFP_KERNEL); if (!uap) return -ENOMEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } uap->port.irq = ret; #ifdef CONFIG_ACPI_SPCR_TABLE if (qdf2400_e44_present) { dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); uap->vendor = &vendor_qdt_qdf2400_e44; } else #endif uap->vendor = &vendor_sbsa; uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; snprintf(uap->type, sizeof(uap->type), "SBSA"); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ret = pl011_setup_port(&pdev->dev, uap, r, portnr); if (ret) return ret; platform_set_drvdata(pdev, uap); return pl011_register_port(uap); }linux内核uart驱动在设备注册时,使用acpi表定义的波特率来初始化串口,请根据我的要求和上述代码,在代码中添加这一功能
时间: 2024-03-08 18:47:22 浏览: 172
在该代码的if (pdev->dev.of_node)分支中,我们可以添加以下代码来使用ACPI表定义的波特率来初始化串口:
```
#ifdef CONFIG_ACPI
if (!pdev->dev.of_node) {
int acpi_baudrate = 0;
if (acpi_get_integer(uap->port.dev->fwnode, "current-speed", &acpi_baudrate) == 0) {
baudrate = acpi_baudrate;
}
}
#endif
```
这里我们使用了ACPI库中的acpi_get_integer()函数来获取ACPI表中定义的波特率。如果成功获取到了波特率,则将其赋值给baudrate变量,否则使用默认值115200。注意,我们需要在代码中包含CONFIG_ACPI宏的判断,以确保ACPI支持已经被启用。
相关问题
static int sbsa_uart_probe(struct platform_device *pdev) { struct uart_amba_port *uap; struct resource *r; int portnr, ret; int baudrate; /* * Check the mandatory baud rate parameter in the DT node early * so that we can easily exit with the error. */ if (pdev->dev.of_node) { struct device_node *np = pdev->dev.of_node; ret = of_property_read_u32(np, "current-speed", &baudrate); if (ret) return ret; } else { baudrate = 115200; } portnr = pl011_find_free_port(); if (portnr < 0) return portnr; uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), GFP_KERNEL); if (!uap) return -ENOMEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } uap->port.irq = ret; #ifdef CONFIG_ACPI_SPCR_TABLE if (qdf2400_e44_present) { dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); uap->vendor = &vendor_qdt_qdf2400_e44; } else #endif uap->vendor = &vendor_sbsa; uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; snprintf(uap->type, sizeof(uap->type), "SBSA"); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ret = pl011_setup_port(&pdev->dev, uap, r, portnr); if (ret) return ret; platform_set_drvdata(pdev, uap); return pl011_register_port(uap); }linux内核uart驱动在设备注册时,使用acpi表定义的波特率来初始化串口,请根据我的要求和上述代码,在代码中添加这一功能
好的,根据你的要求,我可以在该函数中添加获取ACPI表中定义的波特率的功能,并使用该波特率来初始化串口。代码如下:
```
static int sbsa_uart_probe(struct platform_device *pdev) {
struct uart_amba_port *uap;
struct resource *r;
int portnr, ret;
int baudrate;
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
/* Check if ACPI companion device exists, and if so, read the current-speed property */
if (adev) {
struct acpi_device_node *adev_node = acpi_node_get_device_node(adev->handle);
if (adev_node) {
struct fwnode_handle *fwnode = &adev_node->fwnode;
ret = fwnode_property_read_u32(fwnode, "current-speed", &baudrate);
if (ret)
return ret;
}
} else if (pdev->dev.of_node) {
/* If there is no ACPI companion device, fall back to reading the current-speed property from DT node */
struct device_node *np = pdev->dev.of_node;
ret = of_property_read_u32(np, "current-speed", &baudrate);
if (ret)
return ret;
} else {
baudrate = 115200;
}
portnr = pl011_find_free_port();
if (portnr < 0)
return portnr;
uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), GFP_KERNEL);
if (!uap)
return -ENOMEM;
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "cannot obtain irq\n");
return ret;
}
uap->port.irq = ret;
#ifdef CONFIG_ACPI_SPCR_TABLE
if (qdf2400_e44_present) {
dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n");
uap->vendor = &vendor_qdt_qdf2400_e44;
} else
#endif
uap->vendor = &vendor_sbsa;
uap->reg_offset = uap->vendor->reg_offset;
uap->fifosize = 32;
uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.ops = &sbsa_uart_pops;
uap->fixed_baud = baudrate;
snprintf(uap->type, sizeof(uap->type), "SBSA");
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
if (ret)
return ret;
platform_set_drvdata(pdev, uap);
return pl011_register_port(uap);
}
```
我添加的代码在函数开头处,首先判断是否存在ACPI companion device,如果存在则从其属性中读取波特率,否则从DT node中读取。读取到的波特率将被用于初始化串口。
static int pl011_get_poll_char(struct uart_port *port) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned int status; /* * The caller might need IRQs lowered, e.g. if used with KDB NMI * debugger. */ pl011_quiesce_irqs(port); status = pl011_read(uap, REG_FR); if (status & UART01x_FR_RXFE) return NO_POLL_CHAR; return pl011_read(uap, REG_DR); } static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) cpu_relax(); pl011_write(ch, uap, REG_DR); } static const struct uart_ops amba_pl011_pops = { #ifdef CONFIG_CONSOLE_POLL .poll_init = pl011_hwinit, .poll_get_char = pl011_get_poll_char, .poll_put_char = pl011_put_poll_char, #endif }; static const struct uart_ops sbsa_uart_pops = { #ifdef CONFIG_CONSOLE_POLL .poll_init = pl011_hwinit, .poll_get_char = pl011_get_poll_char, .poll_put_char = pl011_put_poll_char, #endif };
<think>好的,我现在要分析用户提供的关于Linux内核中PL011 UART驱动的代码片段,具体是`pl011_get_poll_char`、`pl011_put_poll_char`函数以及它们在`uart_ops`结构体中的注册。用户希望了解这些代码的功能和应用场景。
首先,我需要确认这些代码的作用。根据函数名`poll_get_char`和`poll_put_char`,这应该与轮询模式的串口控制台有关。在Linux内核中,`CONSOLE_POLL`配置通常用于在没有中断支持的情况下进行控制台输入输出,比如系统崩溃时的调试(KDB、NMI调试等)。这时候系统可能无法处理中断,因此需要通过轮询的方式来读取和发送字符。
接下来看`pl011_get_poll_char`函数。函数内部调用了`pl011_quiesce_irqs(port)`,这可能是为了禁用中断,确保在轮询过程中不会被中断干扰。然后读取状态寄存器`REG_FR`,检查接收FIFO是否为空(`UART01x_FR_RXFE`标志)。如果为空,返回`NO_POLL_CHAR`,表示没有字符可读;否则读取数据寄存器`REG_DR`返回接收到的字符。这里需要注意的是,轮询模式需要主动检查状态,而不是等待中断,因此需要不断查询状态位。
然后是`pl011_put_poll_char`函数,它负责发送字符。这里同样检查发送FIFO是否满(`UART01x_FR_TXFF`标志),如果满了就等待(`cpu_relax()`),直到有空闲位置后写入数据寄存器发送字符。这个过程是阻塞的,直到数据可以发送出去。
接着,在`uart_ops`结构体`amba_pl011_pops`和`sbsa_uart_pops`中注册了这三个函数:`poll_init`、`poll_get_char`和`poll_put_char`。这说明这两种UART驱动都支持轮询模式的操作,且使用了相同的函数实现。`CONFIG_CONSOLE_POLL`宏控制这些功能的编译,确保只有在需要轮询控制台时才包含这些代码。
用户可能想了解这些函数在内核中的具体应用场景,比如系统调试、崩溃恢复时的使用,或者在没有中断支持的环境下如何工作。此外,可能需要解释为什么需要禁用中断(`pl011_quiesce_irqs`),以及轮询模式与普通中断驱动模式的区别。
需要验证这些代码是否属于Linux内核的标准PL011驱动,以及SBSA(Server Base System Architecture)UART的具体情况。例如,SBSA是ARM服务器的一种标准,其UART可能与PL011兼容或有特定扩展,但在这里似乎共享了相同的轮询函数。
另外,用户提供的代码片段中有两个不同的`uart_ops`结构体,分别是`amba_pl011_pops`和`sbsa_uart_pops`,但它们都使用了相同的轮询函数。这可能意味着两种驱动在轮询处理上是相同的,或者SBSA UART基于PL011实现,共享部分代码。
总结来说,这些函数实现了在轮询模式下UART的字符读写,用于内核调试场景,如KDB、NMI调试或系统崩溃时的控制台输出。确保在分析时覆盖轮询模式的作用、函数的具体步骤,以及它们在内核中的注册和使用方式。</think>以下是关于PL011 UART驱动中轮询模式实现代码的详细解析:
---
### 一、代码功能概述
这段代码实现了**UART轮询模式下的字符收发功能**,属于Linux内核中PL011/SBSA UART驱动的核心组件。主要用于以下场景:
1. **内核调试器(KDB/KGDB)**:系统崩溃时中断不可用,需轮询收发调试信息
2. **NMI(不可屏蔽中断)处理**:高优先级中断环境中避免嵌套中断
3. **早期控制台初始化**:在中断系统未就绪前的串口输出
---
### 二、关键函数解析
#### 1. `pl011_get_poll_char`
```c
static int pl011_get_poll_char(struct uart_port *port)
{
struct uart_amba_port *uap = container_of(...); // 获取设备实例
pl011_quiesce_irqs(port); // 禁用中断(关键!)
status = pl011_read(uap, REG_FR); // 读取FR(Flag Register)
if (status & UART01x_FR_RXFE) // 检查接收FIFO空标志
return NO_POLL_CHAR; // 返回无数据标记
return pl011_read(uap, REG_DR); // 读取数据寄存器
}
```
**技术要点**:
- `pl011_quiesce_irqs()`:强制关闭UART中断(避免与轮询模式冲突)
- `UART01x_FR_RXFE`:接收FIFO空标志(Bit 4)
- `REG_DR`:数据寄存器地址(偏移0x00)
---
#### 2. `pl011_put_poll_char`
```c
static void pl011_put_poll_char(...)
{
while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) // 检测发送FIFO满
cpu_relax(); // 主动让出CPU(相当于忙等待)
pl011_write(ch, uap, REG_DR); // 写入数据寄存器触发发送
}
```
**关键设计**:
- `UART01x_FR_TXFF`:发送FIFO满标志(Bit 5)
- `cpu_relax()`:在ARM架构中扩展为`yield()`指令,优化功耗
---
### 三、`uart_ops`结构体注册
```c
static const struct uart_ops amba_pl011_pops = {
#ifdef CONFIG_CONSOLE_POLL
.poll_init = pl011_hwinit, // 硬件初始化
.poll_get_char = pl011_get_poll_char,
.poll_put_char = pl011_put_poll_char,
#endif
};
```
**架构差异**:
| 驱动类型 | 适用场景 | 特性差异 |
|----------------|-----------------------------|-------------------------|
| `amba_pl011` | 标准ARM PL011 UART | 支持完整FIFO操作 |
| `sbsa_uart` | ARM服务器基础系统架构(SBSA) | 仅支持单字节收发 |
---
### 四、轮询模式与中断模式对比
| 特性 | 轮询模式 | 中断模式 |
|---------------------|----------------------------------|-------------------------|
| 触发机制 | 主动查询状态寄存器 | 硬件中断触发 |
| CPU利用率 | 高(忙等待) | 低(事件驱动) |
| 延迟确定性 | 更好(无上下文切换) | 依赖中断响应时间 |
| 典型应用场景 | 调试、崩溃恢复、早期引导 | 常规串口通信 |
---
### 五、性能优化细节
1. **FIFO深度处理**:
- PL011的TX/RX FIFO深度为16字节
- 但在`poll_put_char`中未批量写入,因调试场景多为单字符输出
2. **时钟域管理**:
```c
pl011_quiesce_irqs(port); // 实际执行:
writew(UART011_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE,
uap->port.membase + UART011_CR);
```
保持UART使能的同时关闭中断源
3. **ARM内存屏障**:
在`pl011_read`/`pl011_write`中隐含`dsb`指令,确保寄存器访问顺序
---
### 六、典型调用路径
**内核打印崩溃信息**:
```
panic() -> kmsg_dump()
-> console_flush_on_panic()
-> call_console_drivers()
-> uart_console_write()
-> ops->poll_put_char()
```
**KDB调试器输入**:
```
kdb_getchar() -> kdb_poll()
-> tty_poll_get_char()
-> uart_poll_get_char()
-> ops->poll_get_char()
```
---
**注**:该实现需要与`CONFIG_SERIAL_AMBA_PL011_CONSOLE`配置配合使用,确保控制台在轮询模式下正确初始化硬件。
阅读全文
相关推荐

















