对于Linux 内核中的gpio操作,想必大家都很清楚,一般的做法是通过 gpio 的 sysfs文件系统
cd /sys/class/gpio
echo 11 > export
cd gpio11
xxxxxxxxx......
这种方法,可以直接操作GPIO口,但是当我们需要将类似刚刚例子里面的gpio11替换为我们使用引脚的具体定义时,如PWR_EN,这样的方法就行不通了,
所以这里我们要重写 Linux 的gpio_export为 gpio_export_with_name
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index d9fa7985..3d539600 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -604,6 +604,111 @@ fail_unlock:
}
EXPORT_SYMBOL_GPL(gpiod_export);
+/**
+ * gpiod_export_with_name - export a GPIO through sysfs
+ * @gpio: gpio to make available, already requested
+ * @direction_may_change: true if userspace may change gpio direction
+ * @name: gpio name
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine. If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction. A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+ const char *name)
+{
+ struct gpio_chip *chip;
+ unsigned long flags;
+ int status;
+ const char *ioname = NULL;
+ struct device *dev;
+ int offset;
+
+ /* can't export until sysfs is available ... */
+ if (!gpio_class.p) {
+ pr_debug("%s: called too early!\n", __func__);
+ return -ENOENT;
+ }
+
+ if (!desc) {
+ pr_debug("%s: invalid gpio descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ chip = desc->chip;
+
+ mutex_lock(&sysfs_lock);
+
+ /* check if chip is being removed */
+ if (!chip || !chip->exported) {
+ status = -ENODEV;
+ goto fail_unlock;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ test_bit(FLAG_EXPORT, &desc->flags)) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
+ __func__,
+ test_bit(FLAG_REQUESTED, &desc->flags),
+ test_bit(FLAG_EXPORT, &desc->flags));
+ status = -EPERM;
+ goto fail_unlock;
+ }
+
+ if (!desc->chip->direction_input || !desc->chip->direction_output)
+ direction_may_change = false;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ offset = gpio_chip_hwgpio(desc);
+ //if (desc->chip->names && desc->chip->names[offset])
+ // ioname = desc->chip->names[offset];
+ ioname = name; //这里最关键
+
+ dev = device_create_with_groups(&gpio_class, desc->chip->dev,
+ MKDEV(0, 0), desc, gpio_groups,
+ ioname ? ioname : "gpio%u",
+ desc_to_gpio(desc));
+ if (IS_ERR(dev)) {
+ status = PTR_ERR(dev);
+ goto fail_unlock;
+ }
+
+ if (direction_may_change) {
+ status = device_create_file(dev, &dev_attr_direction);
+ if (status)
+ goto fail_unregister_device;
+ }
+
+ if (gpiod_to_irq(desc) >= 0 && (direction_may_change ||
+ !test_bit(FLAG_IS_OUT, &desc->flags))) {
+ status = device_create_file(dev, &dev_attr_edge);
+ if (status)
+ goto fail_remove_attr_direction;
+ }
+
+ set_bit(FLAG_EXPORT, &desc->flags);
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+fail_remove_attr_direction:
+ device_remove_file(dev, &dev_attr_direction);
+fail_unregister_device:
+ device_unregister(dev);
+fail_unlock:
+ mutex_unlock(&sysfs_lock);
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export_with_name);
+
static int match_export(struct device *dev, const void *data)
{
return dev_get_drvdata(dev) == data;
头文件相关
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index d38a2948..ccdbe493 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -113,7 +113,8 @@ static inline int __gpio_to_irq(unsigned gpio)
extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
extern int gpio_request_array(const struct gpio *array, size_t num);
extern void gpio_free_array(const struct gpio *array, size_t num);
-
+extern int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+ const char *name);
/*
* A sysfs interface can be exported by individual drivers if they want,
* but more typically is configured entirely from userspace.
@@ -123,6 +124,12 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
}
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+ const char *name)
+{
+ return gpiod_export_with_name(gpio_to_desc(gpio), direction_may_change, name);
+}
+
static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 85aa5d0b..a43c7e6b 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -188,6 +188,14 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
return -EINVAL;
}
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+ const char *name)
+{
+ /* GPIO can never have been requested or set as {in,out}put */
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{
设备树的方式
dts 修改
+
+ gpio_export {
+ compatible = "gpio-export";
+ #size-cells = <0>;
+
+ kc_ctl1 {
+ gpio-export,name = "KC_CTL1";
+ gpio-export,output = <0>;
+ gpio-export,direction_may_change;
+ gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>;
+ };
+
+ kc_ctl2 {
+ gpio-export,name = "KC_CTL2";
+ gpio-export,output = <0>;
+ gpio-export,direction_may_change;
+ gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;
+ };
+ };
增加驱动解析
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index e0f149bdf98f..a85dd7a9f37f 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -23,6 +23,9 @@
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
#include <linux/gpio/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
#include "gpiolib.h"
@@ -660,3 +663,66 @@ void of_gpiochip_remove(struct gpio_chip *chip)
gpiochip_remove_pin_ranges(chip);
of_node_put(chip->of_node);
}
+
+static struct of_device_id gpio_export_ids[] = {
+ { .compatible = "gpio-export" },
+ { /* sentinel */ }
+};
+
+static int gpio_export_name_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *cnp;
+ u32 val;
+ int nb = 0;
+
+ for_each_child_of_node(np, cnp) {
+ const char *name = NULL;
+ int gpio;
+ bool dmc;
+ int max_gpio = 1;
+ int i;
+
+ of_property_read_string(cnp, "gpio-export,name", &name);
+
+ if (!name)
+ max_gpio = of_gpio_count(cnp);
+
+ for (i = 0; i < max_gpio; i++) {
+ unsigned flags = 0;
+ enum of_gpio_flags of_flags;
+
+ gpio = of_get_gpio_flags(cnp, i, &of_flags);
+
+ if (of_flags == OF_GPIO_ACTIVE_LOW)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ else
+ flags |= GPIOF_IN;
+
+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
+ continue;
+
+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
+ gpio_export_with_name(gpio, dmc, name);
+ nb++;
+ }
+ }
+
+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
+
+ return 0;
+}
+
+static struct platform_driver gpio_export_name_driver = {
+ .probe = gpio_export_name_probe,
+ .driver = {
+ .name = "gpio-export",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpio_export_ids),
+ },
+};
+
+module_platform_driver(gpio_export_name_driver);
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index e0ccc79b239a..28942cfe80cf 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -661,6 +661,109 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
}
EXPORT_SYMBOL_GPL(gpiod_export);
+/**
+ * gpiod_export_with_name - export a GPIO through sysfs
+ * @desc: GPIO to make available, already requested
+ * @direction_may_change: true if userspace may change GPIO direction
+ * @name: gpio name
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine. If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction. A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+ const char *name)
+{
+ struct gpio_chip *chip;
+ struct gpio_device *gdev;
+ struct gpiod_data *data;
+ unsigned long flags;
+ int status;
+ const char *ioname = NULL;
+ struct device *dev;
+ int offset;
+
+ /* can't export until sysfs is available ... */
+ if (!gpio_class.p) {
+ pr_debug("%s: called too early!\n", __func__);
+ return -ENOENT;
+ }
+
+ if (!desc) {
+ pr_debug("%s: invalid gpio descriptor\n", __func__);
+ return -EINVAL;
+ }
+
+ gdev = desc->gdev;
+ chip = gdev->chip;
+
+ mutex_lock(&sysfs_lock);
+
+ /* check if chip is being removed */
+ if (!chip || !gdev->mockdev) {
+ status = -ENODEV;
+ goto err_unlock;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
+ test_bit(FLAG_EXPORT, &desc->flags)) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
+ __func__,
+ test_bit(FLAG_REQUESTED, &desc->flags),
+ test_bit(FLAG_EXPORT, &desc->flags));
+ status = -EPERM;
+ goto err_unlock;
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ status = -ENOMEM;
+ goto err_unlock;
+ }
+
+ data->desc = desc;
+ mutex_init(&data->mutex);
+ if (chip->direction_input && chip->direction_output)
+ data->direction_can_change = direction_may_change;
+ else
+ data->direction_can_change = false;
+
+ offset = gpio_chip_hwgpio(desc);
+ //if (chip->names && chip->names[offset])
+ // ioname = chip->names[offset];
+ ioname = name;
+
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ MKDEV(0, 0), data, gpio_groups,
+ ioname ? ioname : "gpio%u",
+ desc_to_gpio(desc));
+ if (IS_ERR(dev)) {
+ status = PTR_ERR(dev);
+ goto err_free_data;
+ }
+
+ set_bit(FLAG_EXPORT, &desc->flags);
+ mutex_unlock(&sysfs_lock);
+ return 0;
+
+err_free_data:
+ kfree(data);
+err_unlock:
+ mutex_unlock(&sysfs_lock);
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiod_export_with_name);
+
static int match_export(struct device *dev, const void *desc)
{
struct gpiod_data *data = dev_get_drvdata(dev);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 19eadac415c4..84c18bb3dbc6 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -117,7 +117,8 @@ static inline int __gpio_to_irq(unsigned gpio)
extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
extern int gpio_request_array(const struct gpio *array, size_t num);
extern void gpio_free_array(const struct gpio *array, size_t num);
-
+extern int gpiod_export_with_name(struct gpio_desc *desc, bool direction_may_change,
+ const char *name);
/*
* A sysfs interface can be exported by individual drivers if they want,
* but more typically is configured entirely from userspace.
@@ -127,6 +128,13 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
}
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+ const char *name)
+{
+ return gpiod_export_with_name(gpio_to_desc(gpio), direction_may_change, name);
+}
+
+
static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index b3115d1a7d49..96479fe68316 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -199,6 +199,14 @@ static inline int gpio_export(unsigned gpio, bool direction_may_change)
return -EINVAL;
}
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change,
+ const char *name)
+{
+ /* GPIO can never have been requested or set as {in,out}put */
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{