GPIO/PINCTRL

gpio子系统的框架概述pinctrl子系统的框架概述,这两章已经简单说了下gpio是怎么在linux里面工作的,说白了就是为了统一的接口,半导体厂商按照框架在自己的驱动里去在底层做的实现

 gpio我们一般就是指的是通用的具有输入输出高低电平的控制器,这个做的也就是gpio子系统干的事,设置方向,电平等。

但是外接的io引脚,一般不光有gpio的功能,还有其他的复用功能,比如i2c,spi等;这个io怎么输入输出就取决于连接的外设的驱动,怎么定义的了;这个一般有个复用管理的控制器来选择当前跟io连接的是那种功能,当然这个也就是pinctrl的部分工作内容。

从设备树获取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 

按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;

1. 但是不一定所有平台的驱动都是这么做了的,不去通过pinctrl复用成gpio,是不能正常使用的

2. 而且就算会复用成gpio,有时也需要通过pinctrl去设置电气属性来匹配外部的接法,不然拉高拉低操作完全有可能无效

/* 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

Logo

更多推荐