c - 为什么我在通过内核模块访问 GPIO2 和 GPIO3 时在 Beaglebone Black 上出现段错误?

标签 c module linux-kernel beagleboneblack

我一直试图通过内核模块访问 beaglebone black 上的 GPIO2 和 GPIO3,但没有成功。每次我尝试将输出值分配给 GPIO 2 和 3 时,我都会遇到段错误。

完全相同的代码(具有适当的引脚分配)适用于 GPIO0 和 GPIO1。

我在 P8 和 P9 上尝试了与 GPIO2 和 GPIO3 相关的各种引脚,但没有成功。另一方面,相同的代码适用于具有适当引脚分配的 GPIO0 和 GPIO1。

对于引脚值,我使用的是 BBB 官方手册。为了获得适当的 I/O GPIO 可用性,我正在检查来自 beagleboard.com 的这张图: http://beagleboard.org/support/bone101 65 possible digital I/O

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/tcp.h>

//Macros
#define GPIO1_START_ADDR 0x4804C000
#define GPIO2_START_ADDR 0x481AC000
#define GPIO2_END_ADDR 0x481ACFFF
#define GPIO3_START_ADDR 0x481AE000

#define SIZE (GPIO2_END_ADDR - GPIO2_START_ADDR)
#define GPIO_OE 0x134
#define GPIO_DATAOUT 0x13C

//A couple of standard descriptions
MODULE_LICENSE("GPL");

static int hello_init(void)
{
    volatile void *gpio_addr;
    volatile unsigned int *oe_addr;
    volatile unsigned int *dataout_addr;

    printk(KERN_NOTICE "Module: Initializing module\n");

    printk(KERN_NOTICE "Module: Map GPIO\n");
    gpio_addr = ioremap(GPIO3_START_ADDR,SIZE);

    printk(KERN_NOTICE "Module: Set oe_addr\n");
    oe_addr = gpio_addr + GPIO_OE;

    printk(KERN_NOTICE "Module: Set dataout_addr\n");
    dataout_addr = gpio_addr + GPIO_DATAOUT;

    //Code will work up to here for any GPIO.
    //It crashes on the following for GPIO2 and GPIO3:

    printk(KERN_NOTICE "Module: Set pin to OUTPUT\n");
    *oe_addr &= (0xFFFFFFFF ^ (1<<19));

    printk(KERN_NOTICE "Module: Set pin output to HIGH\n");
    *dataout_addr |= (1<<19);

    return 0;
}

static void hello_exit(void)
{
    printk(KERN_INFO "Exit module.\n");
}

module_init(hello_init);
module_exit(hello_exit);

如果我屏蔽掉这两行 *oe_addr &= (0xFFFFFFFF ^ (1<<19));*dataout_addr |= (1<<19); , 程序运行所有 GPIO 没有故障。

$uname -a: Linux beaglebone 3.8.13-bone79

为什么在访问 GPIO2 和 GPIO3 时出现段错误?

最佳答案

经过大量研究,我发现了一些有用的链接,例如 this onethis one .

需要指出的是,GPIO 寄存器 1、2 和 3 的默认设置是禁用时钟,因此在尝试访问寄存器时会出现段错误。当系统请求导出 GPIO 时,它会启用时钟并且 GPIO 寄存器可供使用。

要解决此问题,我们需要手动启用这些 GPIO 的时钟。我无法使用链接中的代码示例执行此操作。

但是通过使用

echo 5 > /sys/class/gpio/export
echo 65 > /sys/class/gpio/export
echo 105 > /sys/class/gpio/export

在运行插入模组之前,我发现一切正常。通过监控每个 GPIO 上的时钟值,我发现该值从某个值变为“2”。但是,将 2 手动输入到这些值中不足以让 GPIO 工作。

如果我找到通过内存控制正确启用时钟的方法,我将更新此答案。

编辑:

经过更多的关注和研究,我已经获得了可以正常工作的代码。我已经将它写成一个单独的模块,它将在插入问题上发布的模块之前插入:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/tcp.h>

#define CM_PER_ADDR 0x44E00000
#define CM_PER_SIZE 0x3FF
#define CM_PER_GPIO1_ADDR   0xAC
#define CM_PER_GPIO2_ADDR   0xB0
#define CM_PER_GPIO3_ADDR   0xB4

#define GPIO_COUNT 3


//A couple of standard descriptions
MODULE_LICENSE("GPL");

static int hello_init(void)
{
    static volatile void* cm_per;
    static volatile unsigned int* cm_per_gpio[GPIO_COUNT];

    static volatile int cm_per_addr[GPIO_COUNT] = {CM_PER_GPIO1_ADDR, CM_PER_GPIO2_ADDR, CM_PER_GPIO3_ADDR};

    static int i = 0;

    printk(KERN_NOTICE "Module2: Initializing module\n");

    cm_per = ioremap(CM_PER_ADDR, CM_PER_SIZE);
        if(!cm_per){
            printk (KERN_ERR "Error: Failed to map GM_PER.\n");
            return -1;  //Break to avoid segfault
        }

    for(i = 0; i < GPIO_COUNT; i++){
        cm_per_gpio[i] = cm_per + cm_per_addr[i];

        //Check if clock is disabled
        if(*cm_per_gpio[i] != 0x2){
        printk(KERN_NOTICE "Enabling clock on GPIO[%d] bank...\n", (i+1));
            *cm_per_gpio[i] = 0x2;  //Enable clock
            //Wait for enabled clock to be set
            while(*cm_per_gpio[i] != 0x2){}
        }

        //Print hex value of clock
        printk(KERN_NOTICE "cm_per_gpio[%d]: %04x\n", (i+1), *(cm_per_gpio[i]));
    }


    return 0;
}

static void hello_exit(void)
{
    printk(KERN_INFO "Module: Exit module.\n"); //Print exit notice and exit without exploding anythin
}

module_init(hello_init);
module_exit(hello_exit);

来自AM335x and AMIC110 Sitara™ ProcessorsTechnical Reference Manual ,我们可以看到 CM_PER_GPIO#_CLKCTRL Register 是如何组织的(其中 # 代表我们正在查看的 GPIO bank):

表 8-60。 CM_PER_GPIO2_CLKCTRL 寄存器字段说明 Table 8-60. CM_PER_GPIO2_CLKCTRL Register Field Descriptions

还告诉我们寄存器的复位(默认)值为30000h,意思是CLOCK DISABLED,意思是module disabled

关于c - 为什么我在通过内核模块访问 GPIO2 和 GPIO3 时在 Beaglebone Black 上出现段错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48045436/

相关文章:

c - utf-8 字符串的最佳哈希是什么

jboss - Wildfly如何删除Mail等模块

android - 如何为 Sony Xperia ZR 获取用于 Android 内核开发的 mkbootimg?

c - fflush在fork操作之前做了什么?

c - 简单链表 - 无法访问节点

c - C 中的 wchar_t argv -- Unicode

javascript - 如果 requireJS 模块在 $ 上设置了全局变量,那么如何仅在分配了全局变量后才执行模块代码?

file - Typescript 每个类一个文件

linux - NOHZ=ON 如何影响 Linux 内核中的 do_timer()?

linux-kernel - 我怎么知道 Linux 模块初始化上的未成年人