gpio子系统的框架概述和pinctrl子系统的框架概述,这两章已经简单说了下gpio是怎么在linux里面工作的,说白了就是为了统一的接口,半导体厂商按照框架在自己的驱动里去在底层做的实现
gpio我们一般就是指的是通用的具有输入输出高低电平的控制器,这个做的也就是gpio子系统干的事,设置方向,电平等。
但是外接的io引脚,一般不光有gpio的功能,还有其他的复用功能,比如i2c,spi等;这个io怎么输入输出就取决于连接的外设的驱动,怎么定义的了;这个一般有个复用管理的控制器来选择当前跟io连接的是那种功能,当然这个也就是pinctrl的部分工作内容。
一般我们通过devm_pinctrl_get_select来获取和设置应该选用的pinctrl,来选择io的复用功能,里面的几个接口就是一般在驱动中常用到的
static inline struct pinctrl * __must_check devm_pinctrl_get_select(
struct device *dev, const char *name)
{
struct pinctrl *p;
struct pinctrl_state *s;
int ret;
p = devm_pinctrl_get(dev);
if (IS_ERR(p))
return p;
s = pinctrl_lookup_state(p, name);
if (IS_ERR(s)) {
devm_pinctrl_put(p);
return ERR_CAST(s);
}
ret = pinctrl_select_state(p, s);
if (ret < 0) {
devm_pinctrl_put(p);
return ERR_PTR(ret);
}
return p;
}
我们只说下怎么在设备树获取pinctrl信息的,最终是通过pinctrl_dt_to_map去映射,名字跟复用功能的
devm_pinctrl_get
pinctrl_get
create_pinctrl
pinctrl_dt_to_map
根据设备树里的pinctrl-names和pinctrl-%d来将名字跟复用功能完成映射,后续就通过pinctrl_lookup_state来通过名字找到复用功能,在通过pinctrl_select_state把这个复用功能设置下去
int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
{
struct device_node *np = p->dev->of_node;
int state, ret;
char *propname;
struct property *prop;
const char *statename;
const __be32 *list;
int size, config;
phandle phandle;
struct device_node *np_config;
/* CONFIG_OF enabled, p->dev not instantiated from DT */
if (!np) {
if (of_have_populated_dt())
dev_dbg(p->dev,
"no of_node; not parsing pinctrl DT\n");
return 0;
}
/* We may store pointers to property names within the node */
of_node_get(np);
/* For each defined state ID */
for (state = 0; ; state++) {
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
prop = of_find_property(np, propname, &size);
kfree(propname);
if (!prop) {
if (state == 0) {
of_node_put(np);
return -ENODEV;
}
break;
}
list = prop->value;
size /= sizeof(*list);
/* Determine whether pinctrl-names property names the state */
ret = of_property_read_string_index(np, "pinctrl-names",
state, &statename);
/*
* If not, statename is just the integer state ID. But rather
* than dynamically allocate it and have to free it later,
* just point part way into the property name for the string.
*/
if (ret < 0)
statename = prop->name + strlen("pinctrl-");
/* For every referenced pin configuration node in it */
for (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */
np_config = of_find_node_by_phandle(phandle);
if (!np_config) {
dev_err(p->dev,
"prop %s index %i invalid phandle\n",
prop->name, config);
ret = -EINVAL;
goto err;
}
/* Parse the node */
ret = dt_to_map_one_config(p, pctldev, statename,
np_config);
of_node_put(np_config);
if (ret < 0)
goto err;
}
/* No entries in DT? Generate a dummy state table entry */
if (!size) {
ret = dt_remember_dummy_state(p, statename);
if (ret < 0)
goto err;
}
}
return 0;
err:
pinctrl_dt_free_maps(p);
return ret;
}
按gpio名字;一般通过of_get_named_gpio来获取gpio号和flags
/* c code */
of_get_named_gpio(np, "rst_gpio", 0);
static inline int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
{
return of_get_named_gpio_flags(np, propname, index, NULL);
}
/* dts code */
rst_gpio = <&gpio 24 0>;
按名字和index;来获取一个属性里的多个gpio
/* c code */
for (i = 0; i < 4; i++) {
pins_ssp = of_get_named_gpio(np, "ssp-gpio", i);
gpio_request(pins_ssp, NULL);
gpio_direction_input(pins_ssp);
gpio_free(pins_ssp);
}
/* dts code */
ssp-gpio = <&gpio 21 0 &gpio 22 0 &gpio 23 0 &gpio 24 0>;
按属性获取,最终是通过__of_find_property来获取设备树节点里的某个属性的
/*dts code */
sim-set-gpio = <1>;
esim-set-gpio = <32>;
/* c code */
of_property_read_u32(pdev->dev.of_node,"sim-set-gpio",&sim_gpio );
of_property_read_u32(pdev->dev.of_node,"esim-set-gpio", &esim_gpio);
gpio_request_one(sim_gpio, GPIOF_OUT_INIT_HIGH,"sim set");
gpio_request_one(esim_gpio, GPIOF_OUT_INIT_LOW,"esim set");
static struct property *__of_find_property(const struct device_node *np,
const char *name, int *lenp)
{
struct property *pp;
if (!np)
return NULL;
for (pp = np->properties; pp; pp = pp->next) {
if (of_prop_cmp(pp->name, name) == 0) {
if (lenp)
*lenp = pp->length;
break;
}
}
return pp;
}
按gpio-flags来回去,在gpio-leds的驱动中一般会这么用
/* dts code */
led@1 {
label = "led_green";
gpios = <&gpio 28 1>;
linux,default-trigger = "none";
default-state = "off";
};
/*c code */
led.gpio = of_get_gpio_flags(child, 0, &flags);
led.active_low = flags & OF_GPIO_ACTIVE_LOW;
led.name = of_get_property(child, "label", NULL) ? : child->name;
led.default_trigger =of_get_property(child, "linux,default-trigger", NULL);
state = of_get_property(child, "default-state", NULL);
static inline int of_get_gpio_flags(struct device_node *np, int index,
enum of_gpio_flags *flags)
{
return of_get_named_gpio_flags(np, "gpios", index, flags);
}
综上,其实就两种:1.按gpio格式去获取;2.按通用属性去获取
再给一个简单的示例,从设备树获取pinctrl和gpio;一般来说平台使用gpio,不需要单独的再去使用pinctrl复用成gpio,request_gpio或者设置gpio输入输出的时候就会去复用成gpio;但是不一定所有平台的驱动都是这么做了的,不去复用成gpio,是不能正常使用的
/* dts code */
pcie0_poweron: pcie0_poweron {
pinctrl-single,pins = <
GPIO10 AF0
>;
DS_MEDIUM;PULL_FLOAT;EDGE_NONE;SL_NORMAL;
};
pcie1_poweron: pcie1_poweron {
pinctrl-single,pins = <
GPIO11 AF0
>;
DS_MEDIUM;PULL_FLOAT;EDGE_NONE;SL_NORMAL;
};
pcie0: pcie@0xd4288000{
pinctrl-names = "default";
pinctrl-0 = <&pcie0_poweron>;
reset-gpios = <&gpio 10 0>;
status = "okay";
};
pcie1: pcie@0xd428c000{
pinctrl-names = "default";
pinctrl-0 = <&pcie1_poweron>;
reset-gpios = <&gpio 11 0>;
status = "okay";
};
/* c code */
static int falcon_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int gpio_reset;
......
devm_pinctrl_get_select_default(dev);
......
gpio_reset = of_get_named_gpio(node, "reset-gpios", 0);
......
if (!gpio_request(gpio_reset, "pcie_perst"))
gpio_direction_output(gpio_reset, 1);
}
sys/kernel/debug/pinctrl里面有pinctrl的range,function,group,pinmux,state等详细调试信息
sys/kernel/debug/gpio可以看目前gpio的使用情况和具体物理电平
devmem可以看读写物理寄存器的值,也可用于调试gpio