基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(2)
作者:彭东林
邮箱:pengdonglin137@163.com
QQ:405728433
平台
tiny4412 ADK
Linux-4.9
概述
前面一篇博文基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)结合示例分析了一下新版kernel引入设备树和irq domain后中断幕后的一些知识,其中的示例只是使用gpio中断的一种方式,此外,还有一种,就像博文
基於tiny4412的Linux內核移植--- 中斷和GPIO學習(1)中描述的那样,这种实现方式又是如何进行的呢?下面还是结合示例的方式分析。
正文
框图可以参考前一篇博文。
在前一篇博文的第三部分 GPIO控制器驱动中有一个函数我们没有分析,就是samsung_gpiolib_register,把这函数看懂了,后面的分析就顺了,下面的分析最好结合前一篇博文的第三部分 GPIO控制器驱动一块看。
这里还是以pinctrl@11000000这个节点为例分析。
samsung_gpiolib_register
static int samsung_gpiolib_register(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_bank *bank = drvdata->pin_banks;
struct gpio_chip *gc;
int ret;
int i;
for (i = ; i < drvdata->nr_banks; ++i, ++bank) { // 遍历pinctrl@11000000下的所有bank,我们关心的是gpx3这个bank
bank->gpio_chip = samsung_gpiolib_chip; // gpio_chip
gc = &bank->gpio_chip;
// 这个bank的gpio在系统中的逻辑起始号, 其中drvdata->pin_base是pinctrl@11000000的在系统中的逻辑gpio起始号,
// 而bank->pin_base是这个bank在pinctrl@11000000中的逻辑起始号(从0开始)
gc->base = drvdata->pin_base + bank->pin_base;
gc->ngpio = bank->nr_pins; // 这个bank中含有的gpio的个数
gc->parent = &pdev->dev;
gc->of_node = bank->of_node; //对于gpx3来说,就是gpx3那个节点的node
gc->label = bank->name;
ret = gpiochip_add_data(gc, bank);
...
}
return ;
...
}
---> gpiochip_add_data(struct gpio_chip *chip, void *data)
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
unsigned long flags;
int status = ;
unsigned i;
int base = chip->base;
struct gpio_device *gdev;
// 每一个bank都都应一个唯一的gpio_device和gpio_chip
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
... ...
if (chip->of_node)
gdev->dev.of_node = chip->of_node; // 分配一个唯一的id
gdev->id = ida_simple_get(&gpio_ida, , , GFP_KERNEL);
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
... ...
// 为这个chip下的每一个gpio都要分配一个gpio_desc结构体
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[]), GFP_KERNEL);
... ...
// 这个chip中含有的gpio的个数
gdev->ngpio = chip->ngpio;
// gpx3 这个bank
gdev->data = data;
... ...
// base表示的是这个bank在系统中的逻辑gpio号
gdev->base = base;
// 将这个bank对应的gpio_device添加到全局链表gpio_devices中
// 在添加的时候会根据gdev->base和ngpio在gpio_devices链表中找到合适的位置
status = gpiodev_add_to_list(gdev);
... ...
for (i = ; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
desc->gdev = gdev;
... ...
}
... ...
// 默认这个chip下的所有gpio都是可以产生中断
status = gpiochip_irqchip_init_valid_mask(chip);
status = of_gpiochip_add(chip);
... ...
return ;
... ...
}
---> of_gpiochip_add(struct gpio_chip *chip)
int of_gpiochip_add(struct gpio_chip *chip)
{
int status;
... ...
if (!chip->of_xlate) {
chip->of_gpio_n_cells = ;
chip->of_xlate = of_gpio_simple_xlate;
}
... ...
}
这里需要看一下of_gpio_simple_xlate的实现,这个在下面的分析中会被回调
int of_gpio_simple_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags)
{
.. ...
if (flags) // 第二个参数表示的是flag
*flags = gpiospec->args[];
// 第一个参数表示的是gpio号
return gpiospec->args[];
}
从上面的分析中我们知道了如下几点:
1. 每一个bank(如gpx3)都对应一个gpio_chip和gpio_device
2. 这个bank下的每一个gpio都会对应一个唯一的gpio_desc结构体,这些结构提的首地址存放在gpio_device的desc中
3. 上面的gpio_device会加入到全局gpio_devices链表中
4. gpio_chip的of_gpio_n_cells被赋值为2,表示引用一个gpio资源需要两个参数,负责解析这两个参数函数以的of_xlate函数为of_gpio_simple_xlate,其中第一个参数表示gpio号(在对应的bank中),第二个表示flag
这里还是先把设备树中涉及到的节点列在这里:
/ {
interrupt-parent = <&gic>;
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";
model = "FriendlyARM TINY4412 board based on Exynos4412";
aliases {
pinctrl1 = "/pinctrl@11000000";
};
gic: interrupt-controller@ {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <0x3>;
interrupt-controller;
reg = <0x10490000 0x10000>, <0x10480000 0x10000>;
cpu-offset = <0x4000>;
};
pinctrl@ {
compatible = "samsung,exynos4x12-pinctrl";
reg = <0x11000000 0x1000>;
interrupts = <0x0 0x2e 0x0>;
gpx3: gpx3 {
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
};
wakeup-interrupt-controller {
compatible = "samsung,exynos4210-wakeup-eint";
interrupt-parent = <0x1>;
interrupts = <0x0 0x20 0x0>;
};
};
interrupt_xeint26: interrupt_xeint26 {
compatible = "tiny4412,interrupt_xeint26";
int-gpio = <&gpx3 GPIO_ACTIVE_HIGH>;
};
};
上面的节点interrupt_xeint26中引用了gpx3_2,而且在驱动中打算将这个gpio当作中断引脚来使用。
下面是对应的驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
typedef struct
{
int gpio;
int irq;
char name[];
}xeint26_data_t;
static irqreturn_t xeint26_isr(int irq, void *dev_id)
{
xeint26_data_t *data = dev_id;
printk("%s enter, %s: gpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);
return IRQ_HANDLED;
}
static int xeint26_probe(struct platform_device *pdev) {
struct device *dev = &pdev->dev;
int irq_gpio = -;
int irq = -;
int ret = ;
int i = ;
xeint26_data_t *data = NULL;
printk("%s enter.\n", __func__);
if (!dev->of_node) {
dev_err(dev, "no platform data.\n");
goto err1;
}
data = devm_kmalloc(dev, sizeof(*data)*, GFP_KERNEL);
if (!data) {
dev_err(dev, "no memory.\n");
goto err0;
}
for (i = ; i < ; i++) {
sprintf(data[i].name, "int-gpio");
irq_gpio = of_get_named_gpio(dev->of_node,
data[i].name, );
if (irq_gpio < ) {
dev_err(dev, "Looking up %s property in node %s failed %d\n",
data[i].name, dev->of_node->full_name, irq_gpio);
goto err1;
}
data[i].gpio = irq_gpio;
irq = gpio_to_irq(irq_gpio);
if (irq < ) {
dev_err(dev,
"Unable to get irq number for GPIO %d, error %d\n",
irq_gpio, irq);
goto err1;
}
data[i].irq = irq;
printk("%s: gpio: %d ---> irq (%d)\n", __func__, irq_gpio, irq);
ret = devm_request_any_context_irq(dev, irq,
xeint26_isr, IRQF_TRIGGER_FALLING, data[i].name, data+i);
if (ret < ) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
irq, ret);
goto err1;
}
}
return ;
err1:
devm_kfree(dev, data);
err0:
return -EINVAL;
}
static int xeint26_remove(struct platform_device *pdev) {
printk("%s enter.\n", __func__);
return ;
}
static const struct of_device_id xeint26_dt_ids[] = {
{ .compatible = "tiny4412,interrupt_xeint26", },
{},
};
MODULE_DEVICE_TABLE(of, xeint26_dt_ids);
static struct platform_driver xeint26_driver = {
.driver = {
.name = "interrupt_xeint26",
.of_match_table = of_match_ptr(xeint26_dt_ids),
},
.probe = xeint26_probe,
.remove = xeint26_remove,
};
static int __init xeint26_init(void)
{
int ret;
ret = platform_driver_register(&xeint26_driver);
if (ret)
printk(KERN_ERR "xeint26: probe failed: %d\n", ret);
return ret;
}
module_init(xeint26_init);
static void __exit xeint26_exit(void)
{
platform_driver_unregister(&xeint26_driver);
}
module_exit(xeint26_exit);
MODULE_LICENSE("GPL");
其中我们只需要分析两个关键的函数:of_get_named_gpio 和 gpio_to_irq.
of_get_named_gpio
这个函数的作用是根据传递的属性的name和索引号,得到一个gpio号
of_get_named_gpio
---> of_get_named_gpio_flags(np, propname, index, NULL)
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
int index, enum of_gpio_flags *flags)
{
struct gpio_desc *desc;
desc = of_get_named_gpiod_flags(np, list_name, index, flags);
... ...
return desc_to_gpio(desc);
}
---> of_get_named_gpiod_flags
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
struct of_phandle_args gpiospec;
struct gpio_chip *chip;
struct gpio_desc *desc;
int ret;
// 解析"int-gpio"属性中第index字段,将解析结果存放到gpiospec中
/*
struct of_phandle_args {
struct device_node *np; // int-gpio属性所引用的gpio-controller的node,对于'int-gpio'来说就是gpx3
int args_count; // gpx3这个gpio-controller的#gpio-cells属性的值
uint32_t args[MAX_PHANDLE_ARGS]; // 具体描述这个gpio属性的每一个参数
};
*/
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gpiospec); // 上面gpiospec的np存放的索引用的gpio-controller的node,
// 遍历gpio_devices链表,找到对应的gpio_device,也就找到了gpio_chip
chip = of_find_gpiochip_by_xlate(&gpiospec);
// 调用chip->of_xlate解析gpiospec,返回gpiospec的args中的第一个参数args[0],
// 也就是前面分析的在bank中的逻辑gpio号
// 知道了gpio号,就可以在gpio_device->desc中索引到对应的gpio_desc
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
return desc;
}
int desc_to_gpio(const struct gpio_desc *desc)
{
// 获得这个gpio_desc对应的gpio在系统中的逻辑gpio号
return desc->gdev->base + (desc - &desc->gdev->descs[]);
}
将这个gpio转换成对应的virq
gpio_to_irq(irq_gpio)
---> __gpio_to_irq(gpio)
---> gpiod_to_irq(gpio_to_desc(gpio))
这里调用了两个函数,函数gpio_to_desc根据传入的全局逻辑gpio号找到对应的gpio_desc,原理是:遍历gpio_devices链表,根据传入的逻辑gpio号,就可以定位到所属的gpio_device,前面说过,在将gpio_device加入到gpio_devices链表的时候,不是乱加的,而是根据gpio_device的base和ngpio找到一个合适的位置。找到了gpio_device,那么通过索引它的desc成员,就可以找到对应的gpio_desc
gpio_to_desc
struct gpio_desc *gpio_to_desc(unsigned gpio) // 传入的是全局逻辑gpio号
{
struct gpio_device *gdev;
unsigned long flags;
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->base <= gpio &&
gdev->base + gdev->ngpio > gpio) {
return &gdev->descs[gpio - gdev->base]; // 获得gpio_desc
}
}
... ...
}
gpiod_to_irq
int gpiod_to_irq(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
int offset;
... ...
chip = desc->gdev->chip;
// 这个函数通过desc - &desc->gdev->descs[0]就可以计算出,对于gpx3_2,就是2
// 这个gpio_desc在所属的bank中的逻辑gpio号
offset = gpio_chip_hwgpio(desc);
int retirq = chip->to_irq(chip, offset);
... ...
return retirq;
... ...
}
上面的第11行就是前面samsung_gpiolib_register中设置的samsung_gpiolib_chip,其to_irq定义如下
static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct samsung_pin_bank *bank = gpiochip_get_data(gc);
unsigned int virq;
.. ..
virq = irq_create_mapping(bank->irq_domain, offset);
return (virq) ? : -ENXIO;
}
需要注意的是offset,比如对于gpx3_2,那么offset就是2, 结合前一篇的博文,这里的offset就是hwirq,调用irq_create_mapping可以为该hwirq在kernel中分配一个唯一的virq,同时将hwirq和virq的映射关系存放到bank->irq_domain中。
实验
加载驱动
[root@tiny4412 mnt]# insmod xeint26.ko
[ 152.084809] xeint26_probe enter.
[ 152.085104] of_get_named_gpiod_flags: parsed 'int-gpio' property of node '/interrupt_xeint26[0]' - status ()
[ 152.085286] irq: irq_create_mapping(0xef205d00, 0x2)
[ 152.085423] irq: -> using domain @ef205d00
[ 152.085590] __irq_alloc_descs: alloc virq: , cnt:
[ 152.090160] irq: irq on domain /pinctrl@/gpx3 mapped to virtual irq
[ 152.097376] xeint26_probe: gpio: ---> irq ()
可以看到在加载驱动的时候才创建了hwirq和virq之间的映射,分配到的virq是100.此时可以去按下tiny4412上面的key1,可以看到中断处理函数中打印出来的log
[root@tiny4412 mnt]# [ 170.718118] xeint26_isr enter, int-gpio: gpio:, irq:
[ 170.910928] xeint26_isr enter, int-gpio: gpio:, irq:
可以看看当前的中断触发情况:
[root@tiny4412 mnt]# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
: GIC- Edge mct_comp_irq
: GIC- Edge MCT
: GIC- Edge mmc0
: GIC- Edge .hsotg, .hsotg, dwc2_hsotg:usb1
: GIC- Edge ehci_hcd:usb2, ohci_hcd:usb3
: GIC- Edge .serial
: GIC- Edge .pdma
: GIC- Edge .pdma
: GIC- Edge .mdma
: GIC- Edge .sss
: GIC- Edge .pinctrl
: GIC- Edge .pinctrl
: COMBINER Edge .pinctrl
: GIC- Edge 106e0000.pinctrl
100: 2 0 0 0 exynos4210_wkup_irq_chip 2 Edge int-gpio
IPI0: CPU wakeup interrupts
IPI1: Timer broadcast interrupts
IPI2: Rescheduling interrupts
IPI3: Function call interrupts
IPI4: CPU stop interrupts
IPI5: IRQ work interrupts
IPI6: completion interrupts
Err:
完。
基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(2)的更多相关文章
- 基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)
作者:彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台 tiny4412 ADK Linux-4.9 概述 前面几篇博文列举了在有设备树的时候,gpio中断的 ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(3)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(2)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(1)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- 第一章 Linux內核簡介
1. Linux是類Unix系統,但他不是Unix. 儘管Linux借鑑了Unix的許多設計並且實現了Unix的API(由Posix標準和其他Single Unix Specification定義的) ...
- 【转】Linux內核驅動之GPIO子系統(一)GPIO的使用 _蝸牛
原文网址:http://tc.chinawin.net/it/os/article-2512b.html 一 概述 Linux內核中gpio是最簡單,最常用的資源(和interrupt ,dma,ti ...
- 操作系統3-內存管理(Linux內存管理)
操作系統3-內存管理(Linux系統的內存管理方法) 9.Linux系統的內存管理方法 Linux採用"按需調頁"算法,支持三層管理策略.由於Intel CPU在硬件級提供了段式存 ...
- Linux內核中常用的一些延時方法
Linux內核中常用的一些延時方法 這些方法在以下路徑下定義:kernel/include/linux/delay.h #ifndef _LINUX_DELAY_H #define _LINUX_DE ...
- ROS_Kinetic_x 基於ROS和Gazebo的RoboCup中型組仿真系統(多機器人協作)
國防科學技術大學發布了RoboCup中型組仿真平臺,基於ROS和Gazebo設計. 該平臺可以用於多機器人協作研究.參考資料如下: ROS新聞:1 http://www.ros.org/news ...
随机推荐
- Linux内核入门(六)—— __attribute__ 机制【转】
转自:https://blog.csdn.net/yunsongice/article/details/5538020 GNU C的一大特色(却不被初学者所知)就是__attribute__机制.__ ...
- u_boot移植之内存基础知识DDR【转】
转自:http://blog.chinaunix.net/uid-25909619-id-4938411.html
- 初始ASP.NET数据控件【续 ListView】
ListView控件 ListView控件可以用来显示数据,它还提供编辑,删除,插入,分页与排序等功能.ListView是GridView与DataList的融合体,它具有GridView控件编辑 ...
- Linux下的snmpd.conf配置说明
SNMP(Simple Network Management Protocol,简单网络管理协议)的前身是简单网关监控协议(SGMP),用来对通信线路进行管理.在RHEL中,SNMP的配置文件地址是/ ...
- python生成器、装饰器、正则
包子来了[4],被[mayun]吃了! 包子来了[4],被[mahuateng]吃了! 做了两个包子 包子来了[5],被[mayun]吃了! 包子来了[5],被[mahuateng]吃了! 做了两个包 ...
- IIS开多个HTTPS站点
默认情况一个服务器的IIS只能绑定一个HTTPS也就是443端口 要实现多个站点对应HTTPS只能更改IIS配置 地址:C:\Windows\system32\inetsrv\config\appli ...
- svn导入项目和部署方面的相关问题
前一阵子忙于部署项目的事情,在这个过程之中遇到了一些问题,查阅了相关资料解决了问题于是就决定分享给大家,可能会对大家有一定的帮助.我在下面中可能会提到dubbo的一些问题,dubbo是用于分布式的系统 ...
- net mvc webapi 实用
asp.net mvc webapi 实用的接口加密方法 在很多项目中,因为webapi是对外开放的,这个时候,我们就要得考虑接口交换数据的安全性. 安全机制也比较多,如andriod与webap ...
- jquery下载,实时更新jquery1.2到最新3.3.1所有版本下载
描述:jquery下载,实时更新jquery1.2到最新3.3.1所有版本下载 https://www.jb51.net/zt/jquerydown.htm (注意:jquery-2.0以上版本不再支 ...
- Codeforces Round #529 (Div. 3) 题解
生病康复中,心情很不好,下午回苏州. 刷了一套题散散心,Div 3,全部是 1 A,感觉比以前慢了好多好多啊. 这几天也整理了一下自己要做的事情,工作上要努力... ... 晚上还是要认认真真背英语的 ...