Linux version: 4.14
Code link: Linux source code (v4.14) - Bootlin
1 devm_gpio_request_one 函数
int devm_gpio_request_one(struct device *dev, unsigned gpio,unsigned long flags, const char *label)
{unsigned *dr;int rc;dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);if (!dr)return -ENOMEM;rc = gpio_request_one(gpio, flags, label);if (rc) {devres_free(dr);return rc;}*dr = gpio;devres_add(dev, dr);return 0;
}
EXPORT_SYMBOL(devm_gpio_request_one);
2 gpio_request_one 函数
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{struct gpio_desc *desc;int err;desc = gpio_to_desc(gpio);/* Compatibility: assume unavailable "valid" GPIOs will appear later */if (!desc && gpio_is_valid(gpio))return -EPROBE_DEFER;err = gpiod_request(desc, label);if (err)return err;if (flags & GPIOF_OPEN_DRAIN)set_bit(FLAG_OPEN_DRAIN, &desc->flags);if (flags & GPIOF_OPEN_SOURCE)set_bit(FLAG_OPEN_SOURCE, &desc->flags);if (flags & GPIOF_ACTIVE_LOW)set_bit(FLAG_ACTIVE_LOW, &desc->flags);if (flags & GPIOF_DIR_IN)err = gpiod_direction_input(desc);elseerr = gpiod_direction_output_raw(desc,(flags & GPIOF_INIT_HIGH) ? 1 : 0);if (err)goto free_gpio;if (flags & GPIOF_EXPORT) {err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE);if (err)goto free_gpio;}return 0;free_gpio:gpiod_free(desc);return err;
}
EXPORT_SYMBOL_GPL(gpio_request_one);
3 gpiod_request
int gpiod_request(struct gpio_desc *desc, const char *label)
{int status = -EPROBE_DEFER;struct gpio_device *gdev;VALIDATE_DESC(desc);gdev = desc->gdev;if (try_module_get(gdev->owner)) {status = __gpiod_request(desc, label);if (status < 0)module_put(gdev->owner);elseget_device(&gdev->dev);}if (status)gpiod_dbg(desc, "%s: status %d\n", __func__, status);return status;
}
4 gpiod_request
static int __gpiod_request(struct gpio_desc *desc, const char *label)
{struct gpio_chip *chip = desc->gdev->chip;int status;unsigned long flags;spin_lock_irqsave(&gpio_lock, flags);/* NOTE: gpio_request() can be called in early boot,* before IRQs are enabled, for non-sleeping (SOC) GPIOs.*/if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {desc_set_label(desc, label ? : "?");status = 0;} else {status = -EBUSY;goto done;}if (chip->request) {/* chip->request may sleep */spin_unlock_irqrestore(&gpio_lock, flags);status = chip->request(chip, gpio_chip_hwgpio(desc));spin_lock_irqsave(&gpio_lock, flags);if (status < 0) {desc_set_label(desc, NULL);clear_bit(FLAG_REQUESTED, &desc->flags);goto done;}}if (chip->get_direction) {/* chip->get_direction may sleep */spin_unlock_irqrestore(&gpio_lock, flags);gpiod_get_direction(desc);spin_lock_irqsave(&gpio_lock, flags);}
done:spin_unlock_irqrestore(&gpio_lock, flags);return status;
}
5 补充:chip->request
(1)zynq 系列芯片中对 gpio_chip 赋值的函数,其中 chip->request 函数赋值为 zynq_gpio_request 函数。
static int zynq_gpio_probe(struct platform_device *pdev)
{int ret, bank_num;struct zynq_gpio *gpio;struct gpio_chip *chip;struct resource *res;const struct of_device_id *match;gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);if (!gpio)return -ENOMEM;match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node);if (!match) {dev_err(&pdev->dev, "of_match_node() failed\n");return -EINVAL;}gpio->p_data = match->data;platform_set_drvdata(pdev, gpio);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);gpio->base_addr = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(gpio->base_addr))return PTR_ERR(gpio->base_addr);gpio->irq = platform_get_irq(pdev, 0);if (gpio->irq < 0) {dev_err(&pdev->dev, "invalid IRQ\n");return gpio->irq;}/* configure the gpio chip */chip = &gpio->chip;chip->label = gpio->p_data->label;chip->owner = THIS_MODULE;chip->parent = &pdev->dev;chip->get = zynq_gpio_get_value;chip->set = zynq_gpio_set_value;chip->request = zynq_gpio_request;chip->free = zynq_gpio_free;chip->direction_input = zynq_gpio_dir_in;chip->direction_output = zynq_gpio_dir_out;chip->base = -1;chip->ngpio = gpio->p_data->ngpio;/* Retrieve GPIO clock */gpio->clk = devm_clk_get(&pdev->dev, NULL);if (IS_ERR(gpio->clk)) {dev_err(&pdev->dev, "input clock not found.\n");return PTR_ERR(gpio->clk);}ret = clk_prepare_enable(gpio->clk);if (ret) {dev_err(&pdev->dev, "Unable to enable clock.\n");return ret;}pm_runtime_set_active(&pdev->dev);pm_runtime_enable(&pdev->dev);ret = pm_runtime_get_sync(&pdev->dev);if (ret < 0)goto err_pm_dis;/* report a bug if gpio chip registration fails */ret = gpiochip_add_data(chip, gpio);if (ret) {dev_err(&pdev->dev, "Failed to add gpio chip\n");goto err_pm_put;}/* disable interrupts for all banks */for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++)writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +ZYNQ_GPIO_INTDIS_OFFSET(bank_num));ret = gpiochip_irqchip_add(chip, &zynq_gpio_edge_irqchip, 0,handle_level_irq, IRQ_TYPE_NONE);if (ret) {dev_err(&pdev->dev, "Failed to add irq chip\n");goto err_rm_gpiochip;}gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq,zynq_gpio_irqhandler);pm_runtime_put(&pdev->dev);return 0;err_rm_gpiochip:gpiochip_remove(chip);
err_pm_put:pm_runtime_put(&pdev->dev);
err_pm_dis:pm_runtime_disable(&pdev->dev);clk_disable_unprepare(gpio->clk);return ret;
}
(2) zynq_gpio_request
static int zynq_gpio_request(struct gpio_chip *chip, unsigned int offset)
{int ret;ret = pm_runtime_get_sync(chip->parent);/** If the device is already active pm_runtime_get() will return 1 on* success, but gpio_request still needs to return 0.*/return ret < 0 ? ret : 0;
}
(3)pm_runtime_get_sync
static inline int pm_runtime_get_sync(struct device *dev)
{return __pm_runtime_resume(dev, RPM_GET_PUT);
}
(4)__pm_runtime_resume
int __pm_runtime_resume(struct device *dev, int rpmflags)
{unsigned long flags;int retval;might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe &&dev->power.runtime_status != RPM_ACTIVE);if (rpmflags & RPM_GET_PUT)atomic_inc(&dev->power.usage_count);spin_lock_irqsave(&dev->power.lock, flags);retval = rpm_resume(dev, rpmflags);spin_unlock_irqrestore(&dev->power.lock, flags);return retval;
}
EXPORT_SYMBOL_GPL(__pm_runtime_resume);
对于 rpm_resume 函数,通过查阅资料知道该函数用于激活 dev 设备,具体实现就不去分析了。
小结:gpio_chip 中的 request 函数用来激活设备模块的电源和时钟。
6 总结
int devm_gpio_request_one(struct device *dev, unsigned gpio,unsigned long flags, const char *label)
devm_gpio_request_one 函数用于激活 dev 设备的电源和时钟,设置 gpio 引脚的方向;并且设置相应的 flag。
7 gpio_to_desc 函数
struct gpio_desc *gpio_to_desc(unsigned gpio)
{struct gpio_device *gdev;unsigned long flags;spin_lock_irqsave(&gpio_lock, flags);list_for_each_entry(gdev, &gpio_devices, list) {if (gdev->base <= gpio &&gdev->base + gdev->ngpio > gpio) {spin_unlock_irqrestore(&gpio_lock, flags);return &gdev->descs[gpio - gdev->base];}}spin_unlock_irqrestore(&gpio_lock, flags);if (!gpio_is_valid(gpio))WARN(1, "invalid GPIO %d\n", gpio);return NULL;
}
EXPORT_SYMBOL_GPL(gpio_to_desc);
该函数通过 gpio 索引返回对应 gpio_desc 类型的引脚描述符。