在dts中:
serial_parallel {
compatible = "robot,74HC595D,sn74hc165";
hc595_DATA_OUT = <&gpio3 4 GPIO_ACTIVE_HIGH>;
hc595_SHCP = <&gpio3 6 GPIO_ACTIVE_HIGH>;
hc595_STCP = <&gpio3 7 GPIO_ACTIVE_HIGH>;
hc165_DATA_IN = <&gpio3 9 GPIO_ACTIVE_HIGH>;
hc165_SH = <&gpio3 13 GPIO_ACTIVE_HIGH>;
hc165_CLK = <&gpio3 14 GPIO_ACTIVE_HIGH>;
};
在dts中创建了platform device “serial_parallel”,我们也需要注册对应的platform driver:
static const struct of_device_id serial_parallel_match[] = {
{ .compatible = "robot,74HC595D,sn74hc165", },
{}
};
static struct platform_driver serial_parallel_driver = {
.probe = serial_parallel_probe,
.remove = serial_parallel_remove,
.driver = {
.name = "robot_serial_parallel_",
.owner = THIS_MODULE,
.of_match_table = serial_parallel_match,
},
};
static __init int serial_parallel_init(void)
{
ret = platform_driver_register(&serial_parallel_driver);
if (ret == 0)
}
module_init(serial_parallel_init);
在platform driver的probe函数中,我们解析出gpio property对应的gpio id:
struct sp_drv_data
{
int gpio_595_data_out;
int gpio_595_shcp;
int gpio_595_stcp;
int gpio_165_data_in;
int gpio_165_sh;
int gpio_165_clk;
};
static int serial_parallel_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sp_drv_data *pdata = NULL;
/* 分配驱动的私有结构数据 */
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
f (!pdata) {
dev_err(dev, "failed to allocate sp_drv_data\n");
return -ENOMEM;
}
/* 获取 GP3_04的 ID号 ,然后申请 一个GPIO 操作对象。 */
pdata->gpio_595_data_out = of_get_named_gpio(np,"hc595_DATA_OUT", 0);
pdata->gpio_595_shcp = of_get_named_gpio(np,"hc595_SHCP", 0);
pdata->gpio_595_stcp = of_get_named_gpio(np,"hc595_STCP", 0);
pdata->gpio_165_data_in = of_get_named_gpio(np,"hc165_DATA_IN", 0);
pdata->gpio_165_sh = of_get_named_gpio(np,"hc165_SH", 0);
pdata->gpio_165_clk = of_get_named_gpio(np,"hc165_CLK", 0);
/* 设置驱动私有数据到pdev->dev->driver_data指针当中 */
platform_set_drvdata(pdev, pdata);
/* 使用的时候,通过相反方式取出 */
pdata = (struct sp_drv_data *)platform_get_drvdata(pdev);
}
gpio实例操作:
/* 在 driver 代码中 , 需要包含 #include 使用 : */
/* 获取 GP3_04的 ID号 ,然后申请 一个GPIO 操作对象。 */
gpio_id = of_get_named_gpio(your_driver->dev->of_node,"hc595_DATA_OUT", 0);
if (gpio_is_valid(gpio_id)) //判断一个IO是否合法
devm_gpio_request_one(&platform_device->dev, gpio_id,GPIOF_OUT_INIT_LOW, name);
/* 设置GPIO的方向,如果是输出同时设置电平: */
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
/* 获取输入引脚的电平: */
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* 设置输出: */
void gpio_set_value(unsigned gpio, int value);
/* 释放申请的GPIO对象 */
void gpio_free(unsigned gpio);
/* 将GPIO映射为IRQ中断: */
/* map GPIO numbers to IRQ numbers */
int gpio_to_irq(unsigned gpio);
/* map IRQ numbers to GPIO numbers (avoid using this) */
int irq_to_gpio(unsigned irq);
/* 设置GPIO IRQ中断类型: */
set_irq_type(gpio_to_irq( gpio), IRQ_TYPE_EDGE_FALLING);
include/linux/gpio.h:
static inline bool gpio_is_valid(int number)
static inline int gpio_request(unsigned gpio, const char *label)
static inline int gpio_request_one(unsigned gpio,
unsigned long flags, const char *label)
static inline int gpio_request_array(const struct gpio *array, size_t num)
static inline void gpio_free(unsigned gpio)
static inline void gpio_free_array(const struct gpio *array, size_t num)
static inline int gpio_direction_input(unsigned gpio)
static inline int gpio_direction_output(unsigned gpio, int value)
static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)
static inline int gpio_get_value(unsigned gpio)
static inline void gpio_set_value(unsigned gpio, int value)
static inline int devm_gpio_request(struct device *dev, unsigned gpio,
const char *label)
static inline int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label)
static inline void devm_gpio_free(struct device *dev, unsigned int gpio)
...
在dts中增加以下配置:
&dra7_pmx_core {
serial_parallel_pins_default: serial_parallel_pins_default {
pinctrl-single,pins = <
DRA7XX_CORE_IOPAD(0x34F4, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D0.gpio3_4 */
DRA7XX_CORE_IOPAD(0x34FC, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D2.gpio3_6 */
DRA7XX_CORE_IOPAD(0x3500, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D3.gpio3_7 */
DRA7XX_CORE_IOPAD(0x3504, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D4.gpio3_8 */
DRA7XX_CORE_IOPAD(0x3508, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D5.gpio3_9 */
DRA7XX_CORE_IOPAD(0x3518, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D9.gpio3_13 */
DRA7XX_CORE_IOPAD(0x351C, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D10.gpio3_14 */
>;
};
};
serial_parallel {
compatible = "robot,74HC595D,sn74hc165";
hc595_DATA_OUT = <&gpio3 4 GPIO_ACTIVE_HIGH>;
hc595_SHCP = <&gpio3 6 GPIO_ACTIVE_HIGH>;
hc595_STCP = <&gpio3 7 GPIO_ACTIVE_HIGH>;
hc165_DATA_IN = <&gpio3 9 GPIO_ACTIVE_HIGH>;
hc165_SH = <&gpio3 13 GPIO_ACTIVE_HIGH>;
hc165_CLK = <&gpio3 14 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&serial_parallel_pins_default>;
};
在驱动probe时,会自动配置pin ctrl:
driver_probe_device() -> really_probe() -> pinctrl_bind_pins():
int pinctrl_bind_pins(struct device *dev)
{
int ret;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;
/* (1) 获取到device的pinctrl配置
根据dts中的配置创建
*/
dev->pins->p = devm_pinctrl_get(dev);
if (IS_ERR(dev->pins->p)) {
dev_dbg(dev, "no pinctrl handle\n");
ret = PTR_ERR(dev->pins->p);
goto cleanup_alloc;
}
/* (2) 获取"default"配置 */
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(dev->pins->default_state)) {
dev_dbg(dev, "no default pinctrl state\n");
ret = 0;
goto cleanup_get;
}
/* (3) 获取"init"配置 */
dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_INIT);
if (IS_ERR(dev->pins->init_state)) {
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no init pinctrl state\n");
/* (4.1) 配置"default"配置 */
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state);
} else {
/* (4.2) 配置"init"配置 */
ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
}
if (ret) {
dev_dbg(dev, "failed to activate initial pinctrl state\n");
goto cleanup_get;
}
#ifdef CONFIG_PM
/*
* If power management is enabled, we also look for the optional
* sleep and idle pin states, with semantics as defined in
*
*/
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_SLEEP);
if (IS_ERR(dev->pins->sleep_state))
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no sleep pinctrl state\n");
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_IDLE);
if (IS_ERR(dev->pins->idle_state))
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no idle pinctrl state\n");
#endif
return 0;
/*
* If no pinctrl handle or default state was found for this device,
* let's explicitly free the pin container in the device, there is
* no point in keeping it around.
*/
cleanup_get:
devm_pinctrl_put(dev->pins->p);
cleanup_alloc:
devm_kfree(dev, dev->pins);
dev->pins = NULL;
/* Only return deferrals */
if (ret != -EPROBE_DEFER)
ret = 0;
return ret;
}
也可以在驱动的代码中自定义修改这些配置:
#include
struct pinctrl *gp_pinctrl;
struct pinctrl_state *gp_gpio;//gpio
static int serial_parallel_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sp_drv_data *pdata = NULL;
/* 获取当前device的pinctrl操作句柄 */
gp_pinctrl = devm_pinctrl_get(&pdata->dev);
if (IS_ERR_OR_NULL(gp_pinctrl)) {
printk("Failed to get pin ctrl\n");
return PTR_ERR(gp_pinctrl);
}
/* 获取pinctrl的"default"配置 */
gp_gpio = pinctrl_lookup_state(gp_pinctrl, "default");
if (IS_ERR_OR_NULL(gp_gpio)) {
printk("Failed to lookup pinctrl gpio state\n");
return PTR_ERR(gp_gpio);
}
/* 把pinctrl配置成"default"模式 */
result = pinctrl_select_state(gp_pinctrl, gp_gpio);
if (result) {
printk("%s: Can not set %s pins\n", __func__, "default");
}
}
把寄存器映射出来,打印出具体的值来调试:
void *base = NULL;
void *base1 = NULL;
/* (1) 打印出control module对应寄存器的值 */
base = devm_ioremap(&pdev->dev, 0x4a003400, 0x500);
printk(" VIN1A_D0.gpio3_4 reg 0x%x: 0x%x \n", 0x34F4, *(unsigned int *)((0x34F4-0x3400)+base));
printk(" VIN1A_D2.gpio3_6 reg 0x%x: 0x%x \n", 0x34FC, *(unsigned int *)((0x34FC-0x3400)+base));
printk(" VIN1A_D3.gpio3_7 reg 0x%x: 0x%x \n", 0x3500, *(unsigned int *)((0x3500-0x3400)+base));
printk(" VIN1A_D5.gpio3_9 reg 0x%x: 0x%x \n", 0x3508, *(unsigned int *)((0x3508-0x3400)+base));
printk(" VIN1A_D9.gpio3_13 reg 0x%x: 0x%x \n", 0x3518, *(unsigned int *)((0x3518-0x3400)+base));
printk(" VIN1A_D10.gpio3_14 reg 0x%x: 0x%x \n", 0x351C, *(unsigned int *)((0x351C-0x3400)+base));
/* (2) 打印出gpio3对应寄存器的值 */
base1 = devm_ioremap(&pdev->dev, 0x48057000, 0x200);
printk(" GPIO3 GPIO_OE reg 0x%x: 0x%x \n", 0x00000134, *(unsigned int *)(0x00000134+base1));
在/sys/class/gpio目录下存在三种文件:
export/unexport
文件
gpioN
指代具体的gpio引脚
gpio_chipN
指代gpio控制器
1、export/unexport文件接口
/sys/class/gpio/export
,该接口只能写不能读
用户程序通过写入gpio的编号来向内核申请将某个gpio的控制权导出到用户空间当然前提是没有内核代码申请这个gpio端口
比如 echo 19 > export
上述操作会为19号gpio创建一个节点gpio19,此时/sys/class/gpio目录下边生成一个gpio19的目录
/sys/class/gpio/unexport
和导出的效果相反。
比如 echo 19 > unexport
上述操作将会移除gpio19这个节点。
2、/sys/class/gpio/gpioN
指代某个具体的gpio端口,里边有如下属性文件:
3、/sys/class/gpio/gpiochipN
gpiochipN表示的就是一个gpio_chip,用来管理和控制一组gpio端口的控制器,该目录下存在一下属性文件:
4、具体实例:
root@am57xx-evm:~# ls /sys/class/gpio/
export gpiochip128/ gpiochip192/ gpiochip32/ gpiochip96/
gpiochip0/ gpiochip160/ gpiochip224/ gpiochip64/ unexport
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/base
0
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/
base device/ label ngpio power/ subsystem/ uevent
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip0/ngpio
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip32/base
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip64/base
64
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip96/base
96
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip32/ngpio
32
root@am57xx-evm:~# cat /sys/class/gpio/gpiochip64/ngpio
32
root@am57xx-evm:~#
root@am335x-evm:~# ls /sys/class/gpio/
export gpiochip0/ gpiochip32/ gpiochip64/ gpiochip96/ unexport
root@am335x-evm:~# ls /sys/class/gpio/gpiochip0/
base label ngpio power/ subsystem/ uevent
root@am335x-evm:~# cat /sys/class/gpio/gpiochip0/base
0
root@am335x-evm:~# cat /sys/class/gpio/gpiochip32/base
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip64/base
64
root@am335x-evm:~# cat /sys/class/gpio/gpiochip96/base
96
root@am335x-evm:~# cat /sys/class/gpio/gpiochip0/ngpio
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip32/ngpio
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip64/ngpio
32
root@am335x-evm:~# cat /sys/class/gpio/gpiochip96/ngpio
32
根据gpio编号,找到对应gpio描述符。
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_device *gdev;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
/* (1) 遍历gdev链表,查找符合 gdev->base <= gpio < (gdev->base + gdev->ngpio) 的gdev
每个gdev对应一个gpio chip,在gpiochip_add时创建
注意gdev->base的值,不一定是从0开始的
*/
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->base <= gpio &&
gdev->base + gdev->ngpio > gpio) {
spin_unlock_irqrestore(&gpio_lock, flags);
/* (2) 根据gpio在chip中的偏移,返回gdev中的对应描述符descs[offset] */
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;
}
gpio controller的驱动。
dts中gpio controller的定义实例:
gpio0: gpio@****addr {
compatible = "**********";
reg = <0 0x****addr 0 0x50>;
interrupts = ;
#gpio-cells = <2>;
gpio-controller;
gpio-ranges = <&pfc 0 0 16>;
#interrupt-cells = <2>;
interrupt-controller;
clocks = <&cpg CPG_MOD 912>;
power-domains = <*****>;
};
gpio驱动的核心是调用gpiochip_add()函数:
static inline int gpiochip_add(struct gpio_chip *chip)
{
return gpiochip_add_data(chip, NULL);
}
↓
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
unsigned long flags;
int status = 0;
unsigned i;
int base = chip->base;
struct gpio_device *gdev;
/*
* First: allocate and populate the internal stat container, and
* set up the struct device.
*/
/* (1) 分配gpio chip对应的gdev结构 */
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
if (chip->parent) {
gdev->dev.parent = chip->parent;
gdev->dev.of_node = chip->parent->of_node;
}
#ifdef CONFIG_OF_GPIO
/* If the gpiochip has an assigned OF node this takes precedence */
if (chip->of_node)
gdev->dev.of_node = chip->of_node;
#endif
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
if (gdev->id < 0) {
status = gdev->id;
goto err_free_gdev;
}
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev);
if (chip->parent && chip->parent->driver)
gdev->owner = chip->parent->driver->owner;
else if (chip->owner)
/* TODO: remove chip->owner */
gdev->owner = chip->owner;
else
gdev->owner = THIS_MODULE;
/* (2) 根据gpio chip中gpio的数量,分配gpio描述符空间
每个gpio pin对应一个独立的描述符gdev->descs[offset]
*/
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_ida;
}
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
status = -EINVAL;
goto err_free_descs;
}
if (chip->label)
gdev->label = kstrdup(chip->label, GFP_KERNEL);
else
gdev->label = kstrdup("unknown", GFP_KERNEL);
if (!gdev->label) {
status = -ENOMEM;
goto err_free_descs;
}
gdev->ngpio = chip->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags);
/*
* TODO: this allocates a Linux GPIO number base in the global
* GPIO numberspace for this chip. In the long run we want to
* get *rid* of this numberspace and use only descriptors, but
* it may be a pipe dream. It will not happen before we get rid
* of the sysfs interface anyways.
*/
/* (3) 根据chip->base给gdev->base赋值 */
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
/*
* TODO: it should not be necessary to reflect the assigned
* base outside of the GPIO subsystem. Go over drivers and
* see if anyone makes use of this, else drop this and assign
* a poison instead.
*/
chip->base = base;
}
gdev->base = base;
/* (4) 将gdev加入到全局链表中 */
status = gpiodev_add_to_list(gdev);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags);
/* (5) 初始化gpio描述符gdev->descs[i] */
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
desc->gdev = gdev;
/*
* 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.
*/
if (chip->get_direction) {
/*
* If we have .get_direction, set up the initial
* direction flag from the hardware.
*/
int dir = chip->get_direction(chip, i);
if (!dir)
set_bit(FLAG_IS_OUT, &desc->flags);
} else if (!chip->direction_input) {
/*
* If the chip lacks the .direction_input callback
* we logically assume all lines are outputs.
*/
set_bit(FLAG_IS_OUT, &desc->flags);
}
}
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
status = gpiochip_set_desc_names(chip);
if (status)
goto err_remove_from_list;
status = gpiochip_irqchip_init_valid_mask(chip);
if (status)
goto err_remove_from_list;
/* (6.1) chip注册 */
status = of_gpiochip_add(chip);
if (status)
goto err_remove_chip;
/* (6.2) chip注册 */
acpi_gpiochip_add(chip);
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
* We can do this only if gpiolib has been initialized.
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
status = gpiochip_setup_dev(gdev);
if (status)
goto err_remove_chip;
}
return 0;
...
}
EXPORT_SYMBOL_GPL(gpiochip_add_data);
根据gpio描述符,设置gpio为ouput和初始化值
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
else
value = !!value;
return _gpiod_direction_output_raw(desc, value);
}
↓
static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
/* (1) 根据gpio描述符,找到对应的gpio chip结构 */
struct gpio_chip *gc = desc->gdev->chip;
int val = !!value;
int ret;
/* GPIOs used for IRQs shall not be set as output */
if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
gpiod_err(desc,
"%s: tried to set a GPIO tied to an IRQ as output\n",
__func__);
return -EIO;
}
/* (2) gpio_chip_hwgpio(desc)的含义是根据描述符的偏移,计算gpio在chip中的偏移 */
/* (3) 调用chip的实际执行函数,设置gpio的其他模式 */
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
/* First see if we can enable open drain in hardware */
ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
PIN_CONFIG_DRIVE_OPEN_DRAIN);
if (!ret)
goto set_output_value;
/* Emulate open drain by not actively driving the line high */
if (val)
return gpiod_direction_input(desc);
}
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
/* Emulate open source by not actively driving the line low */
if (!val)
return gpiod_direction_input(desc);
} else {
gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
PIN_CONFIG_DRIVE_PUSH_PULL);
}
set_output_value:
if (!gc->set || !gc->direction_output) {
gpiod_warn(desc,
"%s: missing set() or direction_output() operations\n",
__func__);
return -EIO;
}
/* (4) 调用chip的实际执行函数,设置gpio的output模式及其初始值 */
ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(desc_to_gpio(desc), 0, val);
trace_gpio_direction(desc_to_gpio(desc), 0, ret);
return ret;
}
在dts中使用如下格式定义一个gpio:
serial_parallel {
pwd-gpios = <&gpio3 4 GPIO_ACTIVE_HIGH>;
};
在代码中使用of_get_named_gpio()函数来获取到dts中关于gpio的定义:
/* 获取 GP0_07的 ID号 ,然后申请 一个GPIO 操作对象。 */
gpio_id = of_get_named_gpio(your_driver->dev->of_node,"pwd-gpios", 0);
of_get_named_gpio()函数的具体实现:
static inline int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
{
/* (1) 各个参数含义为:
np: "pwd-gpios" property的父node,例如"serial_parallel"对应的device node
propname: property的name,例如"pwd-gpios"
index: 在pwd-gpios属性中的index,一般为0。
如果pwd-gpios属性包含多个<&gpio3 4 GPIO_ACTIVE_HIGH &gpio3 6 GPIO_ACTIVE_HIGH>,index指定具体哪一个。
*/
return of_get_named_gpio_flags(np, propname, index, NULL);
}
↓
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
int index, enum of_gpio_flags *flags)
{
struct gpio_desc *desc;
desc = of_get_named_gpiod_flags(np, list_name, index, flags);
if (IS_ERR(desc))
return PTR_ERR(desc);
else
return desc_to_gpio(desc);
}
↓
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
struct of_phandle_args gpiospec;
struct gpio_chip *chip;
struct gpio_desc *desc;
int ret;
/* (1.1) 根据父device node、gpio的property、index找到对应的 gpiospec结构,
对<&gpio3 4 GPIO_ACTIVE_HIGH>的解析的话,首先根据'&gpio3'找到对应的gpio controller,然后'gpio3' controller中的"#gpio-cells"属性会指定gpio参数的个数,一般是2,然后根据长度2来解析'4 GPIO_ACTIVE_HIGH'
最终得到的gpiospec各子成员如下:
struct of_phandle_args {
struct device_node *np; // '&gpio3'对应的device node
int args_count; // "#gpio-cells"对应的值,一般情况 = 2
uint32_t args[MAX_PHANDLE_ARGS]; // 参数值,例如'4 GPIO_ACTIVE_HIGH'
};
*/
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gpiospec);
if (ret) {
pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n",
__func__, propname, np, index);
return ERR_PTR(ret);
}
/* (1.2) 根据gpio controller的device node,遍历gdev链表,找到对应的chip结构 */
chip = of_find_gpiochip_by_xlate(&gpiospec);
if (!chip) {
desc = ERR_PTR(-EPROBE_DEFER);
goto out;
}
/* (1.3) 使用chip提供的of_xlate()函数,和参数'4 GPIO_ACTIVE_HIGH'
得到gpio对应的描述符
*/
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
if (IS_ERR(desc))
goto out;
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
__func__, propname, np, index,
PTR_ERR_OR_ZERO(desc));
out:
of_node_put(gpiospec.np);
return desc;
}
一个gpio端口被多个功能所复用,设置使用哪个功能需要用到pinctrl。5728的pinctrl控制器在dts中定义为dra7_pmx_core:
dra7.dtsi:
l4_cfg: l4@4a000000 {
compatible = "ti,dra7-l4-cfg", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x4a000000 0x22c000>;
scm: scm@2000 {
compatible = "ti,dra7-scm-core", "simple-bus";
reg = <0x2000 0x2000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x2000 0x2000>;
/* 经过两层父节点的地址转换,实际地址为:0x1400 + 0x2000 + 0x4a000000 = 0x4a003400 */
dra7_pmx_core: pinmux@1400 {
compatible = "ti,dra7-padconf",
"pinctrl-single";
reg = <0x1400 0x0468>;
#address-cells = <1>;
#size-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x3fffffff>;
};
...
}
...
}
pinctrl对应AM572x芯片手册“CONTROL MODULE”一章,提供了端口多功能配置等一系列功能:
The CTRL_MODULE_CORE submodule has registers for the following features:
• Pad configuration with following controls:
– Pad I/O multiplexing
– Pad pullup and pulldown configuration
– Pad wake-up detection enabling
– Pad wake-up event status
– Pad input buffer enable
– Pad slew rate control
• Device thermal management control and status registers
• PBIAS cell and MMC1 I/O cells control
• IRQ_CROSSBAR and DMA_CROSSBAR control
• Control the priority of initiator accesses to the external SDRAM
• Control the priority of initiators connected to L3_MAIN interconnect
• Memory region lock registers
• Mapping of the device non-maskable interrupt (NMI) to respective cores
• Controls for the DDR2/DDR3 I/O Cells
• Controls for the DDR2/DDR3 associated vref-generation cells
• AVS Class 0 associated registers
• ABB associated registers
• PCIe related registers
• Standard eFuse logic
• Other miscellaneous functions:
– Status of the system boot settings
– DSP1 and DSP2 reset vector address
– Settings assoaciated with USB, SATA and HDMI PHYs
– DSS PLLs multiplexing and enabling
– Force MPU write nonposted transactions
– Firewalls error status
– Settings related to different peripheral modules
– Others
The CTRL_MODULE_WKUP submodule has registers for the following features:
• Basic EMIF configuration settings
• XTAL Oscilator control
• Efuse I/O compensation
• Other functions
dts中增加的pinctrl配置,也都是从0x3400的偏移开始的:
&dra7_pmx_core {
serial_parallel_pins_default: serial_parallel_pins_default {
pinctrl-single,pins = <
DRA7XX_CORE_IOPAD(0x34F4, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D0.gpio3_4 */
DRA7XX_CORE_IOPAD(0x34FC, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D2.gpio3_6 */
DRA7XX_CORE_IOPAD(0x3500, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D3.gpio3_7 */
DRA7XX_CORE_IOPAD(0x3508, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D5.gpio3_9 */
DRA7XX_CORE_IOPAD(0x3518, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D9.gpio3_13 */
DRA7XX_CORE_IOPAD(0x351C, (PIN_INPUT_PULLUP | MUX_MODE14)) /* VIN1A_D10.gpio3_14 */
>;
};
};