首页
关于
友链
其它
统计
壁纸
更多
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,710 阅读
2
修改Linux Kernel defconfig的标准方法
6,561 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
6,154 阅读
4
使用git生成patch和应用patch
3,700 阅读
5
c语言的__attribute__
3,203 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
Rust
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
adtxl
累计撰写
381
篇文章
累计收到
16
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
页面
关于
友链
其它
统计
壁纸
留言
搜索到
381
篇与
的结果
2023-01-03
此内容被密码保护
加密文章,请前往内页查看详情
2023年01月03日
302 阅读
0 评论
1 点赞
2022-12-08
从memblock中释放已经reserved的内存
1. 目的在有些应用场景中,有些reserved memory只在开机时使用,如开机logo之类的,在使用完成后可以选择释放这些reserved_memory,增大系统可用内存。如下面这样的reserved_memory,系统是无法使用的。2. 方法从memblock中的reserved memory中释放内存--memblock_free()将内存释放会buddy system管理-- free_reserved_page()3. 测试针对上面的logo_reserved,一个简单的测试驱动如下#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/memblock.h> static int __init logo_init(void) { unsigned long mem_addr = 0xe6c00000; unsigned long size = 0x1000000; unsigned long pfn_start, pfn_end, pfn_idx; pfn_start = mem_addr >> PAGE_SHIFT; pfn_end = (mem_addr + size) >> PAGE_SHIFT; memblock_free(mem_addr, size); for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++) free_reserved_page(pfn_to_page(pfn_idx)); printk("logo_init!\n"); return 0; } static void __exit logo_exit(void) { } module_init(logo_init); module_exit(logo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("adtxl");测试结果如下,可以看到在加载驱动后,可用内存增大了16M,也就是logo_reserved这段内存的大小。
2022年12月08日
518 阅读
0 评论
0 点赞
2022-12-01
Linux获取虚拟地址对应的物理地址
暂无简介
2022年12月01日
429 阅读
0 评论
0 点赞
2022-11-17
Android Native调试--lldb on Vscode
暂无简介
2022年11月17日
1,199 阅读
0 评论
0 点赞
2022-11-10
[转载]结合early_param/__setup/__setup_param来学习cmdline的解析(基于kernel-4.9)
版权声明:本文为CSDN博主「程序猿Ricky的日常干货」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/rikeyone/article/details/79979887————————————————
2022年11月10日
353 阅读
0 评论
0 点赞
2022-11-10
[转载]由_OF_DECLARE引发对内核SECTION段解析的思考(基于kernel-4.9)
版权声明:本文为CSDN博主「程序猿Ricky的日常干货」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/rikeyone/article/details/79975138
2022年11月10日
426 阅读
0 评论
0 点赞
2022-11-10
"removed-dma-pool" and "shared-dma-pool"
暂无简介
2022年11月10日
1,136 阅读
0 评论
0 点赞
2022-11-09
使用AddressSanitzier后0xbebebebe值是什么意思
暂无简介
2022年11月09日
414 阅读
0 评论
0 点赞
2022-10-22
[正点原子]Linux驱动学习笔记--33.Regmap API实验
暂无简介
2022年10月22日
376 阅读
0 评论
0 点赞
2022-10-22
[正点原子]Linux驱动学习笔记--22.串口实验
串口是很常用的一个外设,在 Linux 下通常通过串口和其他设备或传感器进行通信,根据电平的不同,串口分为 TTL 和 RS232。不管是什么样的接口电平,其驱动程序都是一样的,通过外接 RS485 这样的芯片就可以将串口转换为 RS485 信号,正点原子的 I.MX6U-ALPHA 开发板就是这么做的。对于正点原子的 I.MX6U-ALPHA 开发板而言, RS232、 RS485 以及 GPS 模块接口通通连接到了 I.MX6U 的 UART3 接口上,因此这些外设最终都归结为 UART3 的串口驱动。本章我们就来学习一下如何驱动 I.MX6U-ALPHA 开发板上的 UART3 串口,进而实现 RS232、RS485 以及 GPS 驱动。1. Linux 下 UART 驱动框架1.1 uart_driver 注册与注销同 I2C、 SPI 一样, Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也已经由 NXP 官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttymxcX(X=0….n)文件。虽然串口驱动不需要我们去写,但是串口驱动框架我们还是需要了解的, uart_driver 结构体表示 UART 驱动, uart_driver 定义在 include/linux/serial_core.h 文件中,内容如下:// 示例代码 63.1.1 uart_driver 结构体 295 struct uart_driver { 296 struct module *owner; /* 模块所属者 */ 297 const char *driver_name; /* 驱动名字 */ 298 const char *dev_name; /* 设备名字 */ 299 int major; /* 主设备号 */ 300 int minor; /* 次设备号 */ 301 int nr; /* 设备数 */ 302 struct console *cons; /* 控制台 */ 303 304 /* 305 * these are private; the low level driver should not 306 * touch these; they should be initialised to NULL 307 */ 308 struct uart_state *state; 309 struct tty_driver *tty_driver; 310 };每个串口驱动都需要定义一个 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。返回值: 无。1.2 uart_port 的添加与移除uart_port 表示一个具体的 port, uart_port 定义在 include/linux/serial_core.h 文件,内容如下(有省略):// 示例代码 63.1.2 uart_port 结构体 117 struct uart_port { 118 spinlock_t lock; /* port lock */ 119 unsigned long iobase; /* in/out[bwl] */ 120 unsigned char __iomem *membase; /* read/write[bwl] */ ...... 235 const struct uart_ops *ops; 236 unsigned int custom_divisor; 237 unsigned int line; /* port index */ 238 unsigned int minor; 239 resource_size_t mapbase; /* for ioremap */ 240 resource_size_t mapsize; 241 struct device *dev; /* parent device */ ...... 250 };uart_port 中最主要的就是第 235 行的 ops, ops 包含了串口的具体驱动函数,这个我们稍后再看。每个 UART 都有一个 uart_port,那么 uart_port 是怎么和 uart_driver 结合起来的呢?这里要用到 uart_add_one_port 函数,函数原型如下: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 实现在上面讲解 uart_port 的时候说过, uart_port 中的 ops 成员变量很重要,因为 ops 包含了针对 UART 具体的驱动函数, Linux 系统收发数据最终调用的都是 ops 中的函数。 ops 是 uart_ops类型的结构体指针变量, uart_ops 定义在 include/linux/serial_core.h 文件中,内容如下:// 示例代码 63.1.3 uart_ops 结构体 49 struct uart_ops { 50 unsigned int (*tx_empty)(struct uart_port *); 51 void (*set_mctrl)(struct uart_port *, unsigned int mctrl); 52 unsigned int (*get_mctrl)(struct uart_port *); 53 void (*stop_tx)(struct uart_port *); 54 void (*start_tx)(struct uart_port *); 55 void (*throttle)(struct uart_port *); 56 void (*unthrottle)(struct uart_port *); 57 void (*send_xchar)(struct uart_port *, char ch); 58 void (*stop_rx)(struct uart_port *); 59 void (*enable_ms)(struct uart_port *); 60 void (*break_ctl)(struct uart_port *, int ctl); 61 int (*startup)(struct uart_port *); 62 void (*shutdown)(struct uart_port *); 63 void (*flush_buffer)(struct uart_port *); 64 void (*set_termios)(struct uart_port *, struct ktermios *new, 65 struct ktermios *old); 66 void (*set_ldisc)(struct uart_port *, struct ktermios *); 67 void (*pm)(struct uart_port *, unsigned int state, 68 unsigned int oldstate); 69 70 /* 71 * Return a string describing the type of the port 72 */ 73 const char *(*type)(struct uart_port *); 74 75 /* 76 * Release IO and memory resources used by the port. 77 * This includes iounmap if necessary. 78 */ 79 void (*release_port)(struct uart_port *); 80 81 /* 82 * Request IO and memory resources used by the port. 83 * This includes iomapping the port if necessary. 84 */ 85 int (*request_port)(struct uart_port *); 86 void (*config_port)(struct uart_port *, int); 87 int (*verify_port)(struct uart_port *, struct serial_struct *); 88 int (*ioctl)(struct uart_port *, unsigned int, unsigned long); 89 #ifdef CONFIG_CONSOLE_POLL 90 int (*poll_init)(struct uart_port *); 91 void (*poll_put_char)(struct uart_port *, unsigned char); 92 int (*poll_get_char)(struct uart_port *); 93 #endif 94 };UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,是实实在在的和 UART 寄存器打交道的。关于 uart_ops 结构体中的这些函数的具体含义请参考Documentation/serial/driver 这个文档。UART 驱动框架大概就是这些,接下来我们理论联系实际,看一下 NXP 官方的 UART 驱动文件是如何编写的。2. I.MX6U UART 驱动分析2.1 UART 的 platform 驱动框架打开 imx6ull.dtsi 文件,找到 UART3 对应的子节点,子节点内容如下所示:// 示例代码 63.2.1 uart3 设备节点 1 uart3: serial@021ec000 { 2 compatible = "fsl,imx6ul-uart", 3 "fsl,imx6q-uart", "fsl,imx21-uart"; 4 reg = <0x021ec000 0x4000>; 5 interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; 6 clocks = <&clks IMX6UL_CLK_UART3_IPG>, 7 <&clks IMX6UL_CLK_UART3_SERIAL>; 8 clock-names = "ipg", "per"; 9 dmas = <&sdma 29 4 0>, <&sdma 30 4 0>; 10 dma-names = "rx", "tx"; 11 status = "disabled"; 12 };重点看一下第 2, 3 行的 compatible 属性,这里一共有三个值:“fsl,imx6ul-uart”、“fsl,imx6quar”和“fsl,imx21-uart”。在 linux 源码中搜索这三个值即可找到对应的 UART 驱动文件,此文件为 drivers/tty/serial/imx.c,在此文件中可以找到如下内容:// 示例代码 63.2.2 UART platform 驱动框架 267 static struct platform_device_id imx_uart_devtype[] = { 268 { 269 .name = "imx1-uart", 270 .driver_data = (kernel_ulong_t)&imx_uart_devdata[IMX1_UART], 271 }, { 272 .name = "imx21-uart", 273 .driver_data = (kernel_ulong_t)&imx_uart_devdata[IMX21_UART], 274 }, { 275 .name = "imx6q-uart", 276 .driver_data = (kernel_ulong_t)&imx_uart_devdata[IMX6Q_UART], 277 }, { 278 /* sentinel */ 279 } 280 }; 281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype); 282 283 static const struct of_device_id imx_uart_dt_ids[] = { 284 { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, 285 { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, 286 { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, 287 { /* sentinel */ } 288 }; ...... 2071 static struct platform_driver serial_imx_driver = { 2072 .probe = serial_imx_probe, 2073 .remove = serial_imx_remove, 2074 2075 .suspend = serial_imx_suspend, 2076 .resume = serial_imx_resume, 2077 .id_table = imx_uart_devtype, 2078 .driver = { 2079 .name = "imx-uart", 2080 .of_match_table = imx_uart_dt_ids, 2081 }, 2082 }; 2083 2084 static int __init imx_serial_init(void) 2085 { 2086 int ret = uart_register_driver(&imx_reg); 2087 2088 if (ret) 2089 return ret; 2090 2091 ret = platform_driver_register(&serial_imx_driver); 2092 if (ret != 0) 2093 uart_unregister_driver(&imx_reg); 2094 2095 return ret; 2096 } 2097 2098 static void __exit imx_serial_exit(void) 2099 { 2100 platform_driver_unregister(&serial_imx_driver); 2101 uart_unregister_driver(&imx_reg); 2102 } 2103 2104 module_init(imx_serial_init); 2105 module_exit(imx_serial_exit);可以看出 I.MX6U 的 UART 本质上是一个 platform 驱动,第 267~280 行, imx_uart_devtype为传统匹配表。第 283~288 行,设备树所使用的匹配表,第 284 行的 compatible 属性值为“fsl,imx6q-uart”。第 2071~2082 行, platform 驱动框架结构体 serial_imx_driver。第 2084~2096 行,驱动入口函数,第 2086 行调用 uart_register_driver 函数向 Linux 内核注册 uart_driver,在这里就是 imx_reg。第 2098~2102 行,驱动出口函数,第 2101 行调用 uart_unregister_driver 函数注销掉前面注册的 uart_driver,也就是 imx_reg。2.2 uart_driver 初始化在 imx_serial_init 函数中向 Linux 内核注册了 imx_reg, imx_reg 就是 uart_driver 类型的结构体变量, imx_reg 定义如下:// 示例代码 63.2.3 imx_reg 结构体变量 1836 static struct uart_driver imx_reg = { 1837 .owner = THIS_MODULE, 1838 .driver_name = DRIVER_NAME, 1839 .dev_name = DEV_NAME, 1840 .major = SERIAL_IMX_MAJOR, 1841 .minor = MINOR_START, 1842 .nr = ARRAY_SIZE(imx_ports), 1843 .cons = IMX_CONSOLE, 1844 };2.3 uart_port 初始化与添加当 UART 设备和驱动匹配成功以后 serial_imx_probe 函数就会执行,此函数的重点工作就是初始化 uart_port,然后将其添加到对应的 uart_driver 中。在看 serial_imx_probe 函数之前先来看一下 imx_port 结构体, imx_port 是 NXP 为 I.MX 系列 SOC 定义的一个设备结构体,此结构体内部就包含了 uart_port 成员变量, imx_port 结构体内容如下所示(有缩减):// 示例代码 63.2.4 imx_port 结构体 216 struct imx_port { 217 struct uart_port port; 218 struct timer_list timer; 219 unsigned int old_status; 220 unsigned int have_rtscts:1; 221 unsigned int dte_mode:1; 222 unsigned int irda_inv_rx:1; 223 unsigned int irda_inv_tx:1; 224 unsigned short trcv_delay; /* transceiver delay */ ...... 243 unsigned long flags; 245 };第 217 行, uart_port 成员变量 port。接下来看一下 serial_imx_probe 函数,函数内容如下:// 示例代码 63.2.5 serial_imx_probe 函数 1969 static int serial_imx_probe(struct platform_device *pdev) 1970 { 1971 struct imx_port *sport; 1972 void __iomem *base; 1973 int ret = 0; 1974 struct resource *res; 1975 int txirq, rxirq, rtsirq; 1976 1977 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); 1978 if (!sport) 1979 return -ENOMEM; 1980 1981 ret = serial_imx_probe_dt(sport, pdev); 1982 if (ret > 0) 1983 serial_imx_probe_pdata(sport, pdev); 1984 else if (ret < 0) 1985 return ret; 1986 1987 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1988 base = devm_ioremap_resource(&pdev->dev, res); 1989 if (IS_ERR(base)) 1990 return PTR_ERR(base); 1991 1992 rxirq = platform_get_irq(pdev, 0); 1993 txirq = platform_get_irq(pdev, 1); 1994 rtsirq = platform_get_irq(pdev, 2); 1995 1996 sport->port.dev = &pdev->dev; 1997 sport->port.mapbase = res->start; 1998 sport->port.membase = base; 1999 sport->port.type = PORT_IMX, 2000 sport->port.iotype = UPIO_MEM; 2001 sport->port.irq = rxirq; 2002 sport->port.fifosize = 32; 2003 sport->port.ops = &imx_pops; 2004 sport->port.rs485_config = imx_rs485_config; 2005 sport->port.rs485.flags = 2006 SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX; 2007 sport->port.flags = UPF_BOOT_AUTOCONF; 2008 init_timer(&sport->timer); 2009 sport->timer.function = imx_timeout; 2010 sport->timer.data = (unsigned long)sport; 2011 2012 sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 2013 if (IS_ERR(sport->clk_ipg)) { 2014 ret = PTR_ERR(sport->clk_ipg); 2015 dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); 2016 return ret; 2017 } 2018 2019 sport->clk_per = devm_clk_get(&pdev->dev, "per"); 2020 if (IS_ERR(sport->clk_per)) { 2021 ret = PTR_ERR(sport->clk_per); 2022 dev_err(&pdev->dev, "failed to get per clk: %d\n", ret); 2023 return ret; 2024 } 2025 2026 sport->port.uartclk = clk_get_rate(sport->clk_per); 2027 if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) { 2028 ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE); 2029 if (ret < 0) { 2030 dev_err(&pdev->dev, "clk_set_rate() failed\n"); 2031 return ret; 2032 } 2033 } 2034 sport->port.uartclk = clk_get_rate(sport->clk_per); 2035 2036 /* 2037 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later 2038 * chips only have one interrupt. 2039 */ 2040 if (txirq > 0) { 2041 ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, 2042 dev_name(&pdev->dev), sport); 2043 if (ret) 2044 return ret; 2045 2046 ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, 2047 dev_name(&pdev->dev), sport); 2048 if (ret) 2049 return ret; 2050 } else { 2051 ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, 2052 dev_name(&pdev->dev), sport); 2053 if (ret) 2054 return ret; 2055 } 2056 2057 imx_ports[sport->port.line] = sport; 2058 2059 platform_set_drvdata(pdev, sport); 2060 2061 return uart_add_one_port(&imx_reg, &sport->port); 2062 }第 1971 行,定义一个 imx_port 类型的结构体指针变量 sport。第 1977 行,为 sport 申请内存。第 1987~1988 行,从设备树中获取 I.MX 系列 SOC UART 外设寄存器首地址,对于I.MX6ULL 的 UART3 来说就是 0X021EC000。得到寄存器首地址以后对其进行内存映射,得到对应的虚拟地址。第 1992~1994 行,获取中断信息。第 1996~2034 行,初始化 sport,我们重点关注的就是第 2003 行初始化 sport 的 port 成员变量,也就是设置 uart_ops 为 imx_pops, imx_pops 就是 I.MX6ULL 最底层的驱动函数集合,稍后再来看。第 2040~2055 行,申请中断。第 2061 行,使用 uart_add_one_port 向 uart_driver 添加 uart_port,在这里就是向 imx_reg 添加 sport->port。2.4 imx_pops 结构体变量imx_pops 就是 uart_ops 类型的结构体变量,保存了 I.MX6ULL 串口最底层的操作函数,imx_pops 定义如下:// 示例代码 63.2.6 imx_pops 结构体 1611 static struct uart_ops imx_pops = { 1612 .tx_empty = imx_tx_empty, 1613 .set_mctrl = imx_set_mctrl, 1614 .get_mctrl = imx_get_mctrl, 1615 .stop_tx = imx_stop_tx, 1616 .start_tx = imx_start_tx, 1617 .stop_rx = imx_stop_rx, 1618 .enable_ms = imx_enable_ms, 1619 .break_ctl = imx_break_ctl, 1620 .startup = imx_startup, 1621 .shutdown = imx_shutdown, 1622 .flush_buffer = imx_flush_buffer, 1623 .set_termios = imx_set_termios, 1624 .type = imx_type, 1625 .config_port = imx_config_port, 1626 .verify_port = imx_verify_port, 1627 #if defined(CONFIG_CONSOLE_POLL) 1628 .poll_init = imx_poll_init, 1629 .poll_get_char = imx_poll_get_char, 1630 .poll_put_char = imx_poll_put_char, 1631 #endif 1632 };imx_pops 中的函数基本都是和 I.MX6ULL 的 UART 寄存器打交道的,这里就不去详细的分析了。简单的了解了 I.MX6U 的 UART 驱动以后我们再来学习一下,如何驱动正点原子I.MX6U-ALPHA 开发板上的 UART3 接口。
2022年10月22日
596 阅读
0 评论
0 点赞
1
...
9
10
11
...
39