在 linux 内核模块中访问 GPIO 的步骤是怎样的?

How are the steps to access GPIOs in linux kernel modules?

我正在努力寻找从 linux 内核模块访问 gpio-pin 需要哪些步骤。 也许有人可以通过一个简单的例子向我解释。我喜欢使用 pin 4(输入)和 33(输出)。到目前为止我的步数:

1.) 设备树 (dts):我保持 dts 文件不变 - 我需要通过引脚控制设置引脚 4 和 33 吗?

2.) 内核模块:一些伪代码

gpio_is_valid(4)
gpio_request_one(4, GPIOF_DIR_IN | GPIOF_EXPORT_DIR_FIXED , "myPin4")
gpio_export(4, false)
gpio_get_value(4)

gpio_is_valid(33)
gpio_request_one(33, GPIOF_DIR_OUT | GPIOF_INIT_LOW | GPIOF_OPEN_SOURCE | GPIOF_EXPORT_DIR_FIXED , "myPin33")
gpio_export(33, false)
gpio_set_value(33, 1)

如何正确操作?

我建议使用自己的设备树文件 + 平台驱动程序 + 字符驱动程序的组合

0.) RTF

检查 device trees(dts) 的工作方式

检查 platform device 的工作原理

检查 character device 的工作原理

获得一些关于gpios和dts的知识

@gpio mappings

@subsystems using gpios

@Specifying GPIO information for devices

阅读您的 SOC 制造商提供的信息。

state-of-the-art 访问 gpios 的方法是通过 struct gpio_desc 变量。它们是从设备树中创建的。

1.) 方法

要在 linux 下切换引脚,您需要确保 3 个单元一起工作。

引脚控制器 (pinctrl) 定义了输出的驱动方式。开源、拉起等

引脚多路复用器 (pinmux) 为引脚定义了不同的功能。

gpio-controller(gpioctrl) 转换 gpio 编号。 p.E.: 44 -> GPIO A 11

这些组件由 SOC 制造商实现。每个平台都有差异。下面是 SAMA5D35xxx 的示例。

@设备树

定义引脚控制器

pinctrl@fffff200 {
    pinctrl_myPins: myPins {
        atmel,pins = <AT91_PIOA 2 AT91_PERIPH_GPIO AT91_PINCTRL_NONE  // pin 1
            AT91_PIOD 19 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; // pin 2
    };
};

创建节点链接自有平台设备:

myPins {
    compatible = "myPlatformDevice";

    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_myPins>;

    pin1 = <&pioA 2 GPIO_ACTIVE_HIGH>;
    pin2 = <&pioD 19 GPIO_ACTIVE_HIGH>;
};

@create platform + char驱动(伪代码):

// ----------------------
// kernel message support(via dmesg)
// ----------------------
#define KMSG_DEBUG(fmt,args...) printk(KERN_DEBUG "myDrv" ": "fmt"\n", ##args)
#define KMSG_PERR(fmt,args...) printk(KERN_ERR "myDrv" ": "fmt"\n", ##args)
#define KMSG_PINFO(fmt,args...) printk(KERN_INFO "myDrv" ": "fmt"\n", ##args)
// ----------------------
// trace support via defining dMyDrvTrace
// ----------------------
#ifndef dMyDrvTrace
    #define TRACE(...)
#else
    #define TRACE(fmt,args...) printk(KERN_INFO "myDrv" ": [%s] "fmt"\n", __FUNCTION__, ##args)
#endif


typedef struct SMyDrvDrvData {
    struct platform_device *pdev; //!< next device
    // here goes the local/private data
    int gpiod_pin1;
    int gpiod_pin2;

    u32 pin1;
    u32 pin2;
} TMyDrvDrvData;

static struct dentry * gmyPlattformDrvDebugfsRootDir; //!< root dir at debugfs

static int myPlattformDrv_probe(struct platform_device *pdev);
static int myPlattformDrv_remove(struct platform_device *pdev);

#if defined(CONFIG_OF)
    //! filter for the device tree class
    static struct of_device_id gMyPlattformDrvdtsFilter[] = {
        {.compatible = "myPlatformDevice"},
        {}
    };
    MODULE_DEVICE_TABLE(of, gMyPlattformDrvdtsFilter);
#else
    #define gmyPlattformDrvdtsFilter (NULL)
#endif

static struct platform_device *MyPlattformDrv_devs[] = {
};

static struct platform_driver myPlattformDrv_driver = {
        .driver = {
            .name   = dMyPlattformDrvdriver,
            .owner  = THIS_MODULE,
            .of_match_table =  of_match_ptr(gMyPlattformDrvdtsFilter),
        },
        .probe      = myPlattformDrv_probe,
        .remove     = myPlattformDrv_remove,
};


// char device

static dev_t gMyCharDev;
static struct class *gMyCharDevClass;
static struct cdev gMyCharDev_cdev;

static int dev_open      (struct inode *, struct file *);
static int dev_release   (struct inode *, struct file *);
static ssize_t dev_read  (struct file *, char *, size_t, loff_t *);
static ssize_t dev_write (struct file *, const char *, size_t, loff_t *);

static const struct file_operations gMyCharDevOps =
{
    .read    = dev_read,
    .open    = dev_open,
    .write   = dev_write,
    .release = dev_release
};


//! looks up for the gpio name and request it
static int get_gpio(struct platform_device *pdev, const char * name, int * pGPIOnum)
{
    int n,i;
    int r;
    struct device_node * pDN;

    TRACE("look at %s for %s ...", pdev->name, name);
    // reset return value
    *pGPIOnum = 0;

    // parse device tree

    // get device tree entries associated with the device
    pDN = of_find_node_by_name(NULL, pdev->name);

    // parse pins
    n = of_gpio_named_count(pDN, name);
    if (n <= 0) {
        TRACE("no gpios found");
        return -1;
    }
    for (i = 0; i < n; i++) {
        // get pin number
        *pGPIOnum = of_get_named_gpio(pDN,name, i);
        if (*pGPIOnum == -EPROBE_DEFER) {
            return r;
        }
        // check if pin number is valid
        if (gpio_is_valid(*pGPIOnum)) {
            // yes
            // request pin
            r = devm_gpio_request(&pdev->dev, *pGPIOnum, name);
            if (r) {
                return r;
            } else {
                r = gpio_direction_output(*pGPIOnum, 0);
                }
                if (r) return r;
            }
        }
    }
    return 0;
}

//! probes the platform driver
static int myPlattformDrv_probe(struct platform_device *pdev)
{
    struct TMyDrvDrvData *priv;
    int i,j,r,gpioNum, ret;

    KMSG_PINFO("probe my driver ...");

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        KMSG_PERR("Failed to allocate memory for the private data structure");
        return -ENOMEM;
    }

    priv->pdev = pdev;
    platform_set_drvdata(pdev, priv);

    TRACE("setup gpios ...");
    r = get_gpio(pdev, "pin1", &gpioNum);
    if (r) {
        KMSG_PERR("Failed to find gpio \"pin1\" in device tree");
    }
    // save number
    priv->gpiod_pin1 = gpioNum;

    // create "pin1" debugfs entry
    debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin1);

    r = get_gpio(pdev, "pin2", &gpioNum);
    if (r) {
        KMSG_PERR("Failed to find gpio \"pin2\" in device tree");
    }
    // save number
    priv->gpiod_pin2 = gpioNum;

    // create "pin2" debugfs entry
    debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin2);


    // create device class
    TRACE("create myCharDev char device class");

    // create char dev region
    ret = alloc_chrdev_region(&gMyCharDev, 0, 1, "myCharDev");
    if( ret < 0) {
        KMSG_PERR("alloc_chrdev_region error %i", ret);
        goto error;
    }

    // create device class
    if((gMyCharDevClass = class_create(THIS_MODULE, dSEK4DevClass)) == NULL)
    {
        KMSG_PERR("class_create error");
        goto error_classCreate;
    }

    if(NULL == device_create(gMyCharDevClass, NULL, gMyCharDev, NULL, "myCharDev"))
    {
        KMSG_PERR("device_create error");
        goto error_deviceCreate;
    }

    cdev_init(&gMyCharDev_cdev, &gMyCharDevOps);
    ret = cdev_add(&gMyCharDev_cdev, gMyCharDev, 1);
    if(-1 == ret) {
        KMSG_PERR("cdev_add error %i", ret);
        goto error_device_add;
        return -1;
    }
    TRACE("added myCharDev char device");

    return 0;
// error handling block
error_std:
error_device_add:
    device_destroy(gMyCharDevClass, gMyCharDev);
error_deviceCreate:
    class_destroy(gMyCharDevClass);
error_classCreate:
    unregister_chrdev_region(gMyCharDev, 1);
error:
    return -1;
}