在设备驱动中对GPIO的操作是非常普遍的,linux内核为我们提供了GPIO子系统,方便用户使用,它为用户提供了GPIO的统一操作接口,用户不需要关心底层实现,因为这是芯片厂商需要关心的,芯片厂商会去做这一部分工作。
接着我们就从驱动入手来看一下gpio子系统的框架。
gpio_direction_output(gmac->resetgpio, 1); mdelay(30); gpio_direction_output(gmac->resetgpio, 0); mdelay(30); gpio_direction_output(gmac->resetgpio, 1); mdelay(40);
上面几行代码就是从驱动中摘出来的,很简单,就是写指定gpio的值,并设定为输出模式。
gpio_direction_output是内核提供给我们的统一接口,方便我们去操作gpio,那么他是怎么操作到最终的芯片寄存器呢?
带着这个问题我们进入gpio_direction_output函数看一下。
//driver/gpio/gpiolib.c int gpio_direction_output(unsigned gpio, int value) { return gpiod_direction_output(gpio_to_desc(gpio), value); } EXPORT_SYMBOL_GPL(gpio_direction_output);
他直接调用了gpiod_direction_output;
static int gpiod_direction_output(struct gpio_desc *desc, int value) { unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; int offset; .... //前面做了一些各种检查相关的工作,这不是重点 chip = desc->chip; if (!chip || !chip->set || !chip->direction_output) goto fail; status = gpio_ensure_requested(desc); if (status < 0) goto fail; might_sleep_if(chip->can_sleep); offset = gpio_chip_hwgpio(desc); if (status) { status = chip->request(chip, offset); if (status < 0) { pr_debug("GPIO-%d: chip request fail, %d\n", desc_to_gpio(desc), status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ goto lose; } } status = chip->direction_output(chip, offset, value); //重点在这个回调函数,应该是调用的这个函数去设置的CPU的寄存器,最终设置GPIO的。 if (status == 0) set_bit(FLAG_IS_OUT, &desc->flags); trace_gpio_value(desc_to_gpio(desc), 0, value); trace_gpio_direction(desc_to_gpio(desc), 0, status); }
我们猜测应该是调用的chip->direction_output(chip, offset, value);这个回调函数最终设置的寄存器,那么这接下来应该就是跟芯片紧密联系的,这部分工作应该是芯片厂商去完成的。
gpio子系统在kernel/drivers/gpio/目录下面。这里有跟平台相关的文件,这就是厂商提供的。
songchong@srv-artek-pad:~/GS700E/android/kernel/drivers/gpio$ ls devres.c gpio-bt8xx.c gpio-generic.c gpiolib-of.c gpio-mcp23s08.c gpio-mxc.c gpio-pxa.c gpio-sta2x11.c gpio-tps65910.c gpio-wm831x.c gpio-74x164.c gpio-clps711x.c gpio-grgpio.c gpio-lpc32xx.c gpio-ml-ioh.c gpio-mxs.c gpio-rc5t583.c gpio-stmpe.c gpio-tps65912.c gpio-wm8350.c gpio-adnp.c gpio-cs5535.c gpio-ich.c gpio-lynxpoint.c gpio-mm-lantiq.c gpio-omap.c gpio-rcar.c gpio-stp-xway.c gpio-ts5500.c gpio-wm8994.c gpio-adp5520.c gpio-da9052.c gpio-it8761e.c gpio-max7300.c gpio-mpc5200.c gpio-owl.c gpio-rdc321x.c gpio-sx150x.c gpio-twl4030.c gpio-xilinx.c gpio-adp5588.c gpio-da9055.c gpio-janz-ttl.c gpio-max7301.c gpio-mpc8xxx.c gpio-palmas.c gpio-sa1100.c gpio-tc3589x.c gpio-twl6040.c Kconfig gpio-amd8111.c gpio-davinci.c gpio-ks8695.c gpio-max730x.c gpio-msic.c gpio-pca953x.c gpio-samsung.c gpio-tegra.c gpio-ucb1400.c Makefile gpio-arizona.c gpio-em.c gpio-langwell.c gpio-max732x.c gpio-msm-v1.c gpio-pcf857x.c gpio-sch.c gpio-timberdale.c gpio-viperboard.c gpio-atc2603c-sgpio.c gpio-ep93xx.c gpiolib-acpi.c gpio-mc33880.c gpio-msm-v2.c gpio-pch.c gpio-sodaville.c gpio-tnetv107x.c gpio-vr41xx.c gpio-atc260x.c gpio-ge.c gpiolib.c gpio-mc9s08dz60.c gpio-mvebu.c gpio-pl061.c gpio-spear-spics.c gpio-tps6586x.c gpio-vx855.c
各种各样的厂商的都有的。我们看看炬芯的gpio-owl.c文件,直接去看probe函数。
//省略不必要的 static int owl_gpio_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(owl_gpio_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; const struct owl_gpio_bank_data *pdata; struct owl_gpio_bank *bank; int ret; bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); //分配一个bank结构体 if (bank == NULL) return -ENOMEM; bank->id = of_alias_get_id(np, "gpio"); if (bank->id < 0) return bank->id; bank->devid = (enum owl_gpio_id) of_id->data; pr_info("GPIO%c initialization\n", 'A' + bank->id); if (is_s900_gpio(bank)) { BUG_ON(bank->id >= ARRAY_SIZE(s900_banks_data)); pdata = &s900_banks_data[bank->id]; } else if (is_s700_gpio(bank)) { BUG_ON(bank->id >= ARRAY_SIZE(s700_banks_data)); pdata = &s700_banks_data[bank->id]; } else if (is_ats3605_gpio(bank)) { BUG_ON(bank->id >= ARRAY_SIZE(ats3605_banks_data)); pdata = &ats3605_banks_data[bank->id]; }else { return -ENOENT; } bank->base = of_iomap(dev->of_node, 0); if (IS_ERR(bank->base)) return -ENOENT; bank->irq = irq_of_parse_and_map(np, 0); if (bank->irq < 0) { dev_err(dev, "failed to get IRQ"); return -ENOENT; } bank->nr_gpios = pdata->nr_gpios; bank->regs = &pdata->regs; bank->pdev = pdev; spin_lock_init(&bank->lock); owl_gpio_bank_setup(bank); //设置各种bank回调函数 platform_set_drvdata(pdev, bank); ret = owl_gpio_irq_setup(bank); if (ret < 0) { dev_err(dev, "failed to setup irq, ret %d\n", ret); return ret; } ret = gpiochip_add(&bank->gpio_chip); //将设置好的bank结构注册进gpio子系统中去,注册进去之后驱动中就能使用封装好的接口了。 if (ret < 0) { dev_err(dev, "failed to add GPIO chip, ret %d\n", ret); return ret; } return 0; }
上面最重要的两个步骤就是设置回调函数,和将bank结构注册进gpio子系统。
//各种回调函数都是由厂商提供的,这里我们就不细看了,这跟芯片平台紧密相关。 static int owl_gpio_bank_setup(struct owl_gpio_bank *bank) { struct gpio_chip *chip = &bank->gpio_chip; chip->base = bank->id * GPIO_PER_BANK; chip->ngpio = bank->nr_gpios; chip->request = owl_gpio_request; chip->free = owl_gpio_free; chip->get = owl_gpio_get; chip->set = owl_gpio_set; chip->direction_input = owl_gpio_direction_input; chip->direction_output = owl_gpio_direction_output; chip->to_irq = owl_gpio_to_irq; chip->owner = THIS_MODULE; chip->dev = &bank->pdev->dev; chip->label = dev_name(chip->dev); chip->of_node = chip->dev->of_node; chip->owner = THIS_MODULE; return 0; }
接着看一下是怎么注册到gpio子系统的。
扫描二维码关注公众号,回复:
1303851 查看本文章
int gpiochip_add(struct gpio_chip *chip) { unsigned long flags; int status = 0; unsigned id; int base = chip->base; if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) && base >= 0) { status = -EINVAL; goto fail; } spin_lock_irqsave(&gpio_lock, flags); if (base < 0) { base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; goto unlock; } chip->base = base; } status = gpiochip_add_to_list(chip); if (status == 0) { chip->desc = &gpio_desc[chip->base]; for (id = 0; id < chip->ngpio; id++) { struct gpio_desc *desc = &chip->desc[id]; desc->chip = chip; /* REVISIT: most hardware initializes GPIOs as * inputs (often with pullups enabled) so power * usage is minimized. Linux code should set the * gpio direction first thing; but until it does, * and in case chip->get_direction is not set, * we may expose the wrong direction in sysfs. */ desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } } spin_unlock_irqrestore(&gpio_lock, flags); #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif of_gpiochip_add(chip); if (status) goto fail; status = gpiochip_export(chip); if (status) goto fail; pr_debug("gpiochip_add: registered GPIOs %d to %d on device: %s\n", chip->base, chip->base + chip->ngpio - 1, chip->label ? : "generic"); return 0; unlock: spin_unlock_irqrestore(&gpio_lock, flags); fail: /* failures here can mean systems won't boot... */ pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n", chip->base, chip->base + chip->ngpio - 1, chip->label ? : "generic"); return status; } EXPORT_SYMBOL_GPL(gpiochip_add);内核设计真是很美!