Linux内核4.14版本——pinctrl子系统(2):pinctrl子系统(core)

目录

一、前言

二、pin control subsystem的文件列表

2.1 源文件列表

2.2 和其他内核模块接口头文件

2.3 Low level pin controller driver接口

 三、pin control subsystem的软件框架图

3.1 功能和接口概述

3.2 pin control subsystem在和其他linux内核模块的接口关系

3.3 pin control subsystem内部block diagram

四、pin control subsystem向其他driver提供的接口

4.1 概述

4.2 具体的接口

五、和GPIO subsystem交互

5.1 为何pin control subsystem要和GPIO subsystem交互?

5.2 具体的接口形态

5.2.1 pinctrl_request_gpio

5.2.2 pinctrl_free_gpio

5.2.3 pinctrl_gpio_direction_input/pinctrl_gpio_direction_output

六、和驱动模型的接口

七、和device tree或者machine driver相关的接口

7.1 概述

7.2 通过machine driver静态定义的数据来建立pin mapping database

7.3 通过device tree来建立pin mapping database

八、core driver和low level pin controller driver的接口规格


一、前言

在linux2.6内核上工作的嵌入式软件工程师在pin control上都会遇到这样的状况:

(1)启动一个新的项目后,需要根据硬件平台的设定进行pin control相关的编码。例如:在bootloader中建立一个大的table,描述各个引脚的配置和缺省状态。此外,由于SOC的引脚是可以复用的,因此在各个具体的driver中,也可能会对引脚进行的配置。这些工作都是比较繁琐的工作,需要极大的耐心和细致度。
(2)发现某个driver不能正常工作,辛辛苦苦debug后发现仅仅是因为其他的driver在初始化的过程中修改了引脚的配置,导致自己的driver无法正常工作
(3)即便是主CPU是一样的项目,但是由于外设的不同,我们也不能使用一个kernel image,而是必须要修改代码(这些代码主要是board-specific startup code)
(4)代码不是非常的整洁,cut-and-pasted代码满天飞,linux中的冗余代码太多
作为一个嵌入式软件工程师,项目做多了,接触的CPU就多了,摔的跤就多了,之后自然会去思考,我们是否可以解决上面的问题呢?此外,对于基于ARM core那些SOC,虽然表面上看起来各个SOC各不相同,但是在pin control上还有很多相同的内容的,是否可以把它抽取出来,进行进一步的抽象呢?新版本中的内核(本文以4.14版本内核为例)提出了pin control subsystem来解决这些问题。

二、pin control subsystem的文件列表

2.1 源文件列表

      我们整理linux/drivers/pinctrl目录下pin control subsystem的源文件列表如下:

文件名描述
core.c core.hpin control subsystem的core driver
pinctrl-utils.cpinctrl-utils.h    pin control subsystem的一些utility接口函数
pinmux.c pinmux.hpin control subsystem的core driver(pin muxing部分的代码,也称为pinmux driver)
pinconf.c pinconf.hpin control subsystem的core driver(pin config部分的代码,也称为pin config driver)
device7tree.c devicetree.hpin control subsystem的device tree代码
pinctrl-xxxx.c各种pin controller的low level driver

2.2 和其他内核模块接口头文件

      很多内核的其他模块需要用到pin control subsystem的服务,这些头文件就定义了pin control subsystem的外部接口以及相关的数据结构。我们整理linux/include/linux/pinctrl目录下pin control subsystem的外部接口头文件列表如下:

文件名描述
consumer.h其他的driver要使用pin control subsystem的下列接口:
    a、设置引脚复用功能
    b、配置引脚的电气特性
这时候需要include这个头文件
devinfo.h这是for linux内核的驱动模型模块(driver model)使用的接口。struct device中包括了一个struct dev_pin_info *pins的成员,这个成员描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state
machine.h和machine模块的接口。

2.3 Low level pin controller driver接口

      我们整理linux/include/linux/pinctrl目录下pin control subsystem提供给底层specific pin controller driver的头文件列表如下:

文件名   描述
pinconf-generic.h这个接口主要是提供给各种pin controller driver使用的,不是外部接口。
pinconf.hpin configuration 接口
pinctrl-state.hpin control state状态定义
pinmux.hpin mux function接口

 三、pin control subsystem的软件框架图

3.1 功能和接口概述

      一般而言,学习复杂的软件组件或者软件模块是一个痛苦的过程。我们可以把我们要学习的那个软件block看成一个黑盒子,不论里面有多么复杂,第一步总是先了解其功能和外部接口特性。如果你愿意,你可以不去看其内部实现,先自己思考其内部逻辑,并形成若干问题,然后带着这些问题去看代码,往往事半功倍。

(1)功能规格。pin control subsystem的主要功能包括:

(A)管理系统中所有可以控制的pin。在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。
(B)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }这四个引脚组合形成一个pin group,提供SPI的功能。pin control subsystem要管理所有的pin group。
(C)配置这些pin的特性。例如配置该引脚上的pull-up/down电阻,配置drive strength等.

(2)接口规格。linux内核的某个软件组件必须放回到linux系统中才容易探讨它的接口以及在系统中的位置,因此,在本章的第二节会基于系统block上描述各个pin control subsystem和其他内核模块的接口。

(3)内部逻辑。要研究一个subsystem的内部逻辑,首先要打开黑盒子,细分模块,然后针对每一个模块进行功能分析、外部接口分析、内部逻辑分析。如果模块还是比较大,难于掌握,那么就继续细分,拆成子模块,重复上面的分析过程。在本章的第三节中,我们打开pin control subsystem的黑盒子进行进一步的分析。

3.2 pin control subsystem在和其他linux内核模块的接口关系

        pin control subsystem会向系统中的其他driver提供接口以便进行该driver的pin config和pin mux的设定,这部分的接口在第四章描述。理想的状态是GPIO controll driver也只是象UART,SPI这样driver一样和pin control subsystem进行交互,但是,实际上由于各种源由(后文详述),pin control subsystem和GPIO subsystem必须有交互,这部分的接口在第五章描述。第六章描述了Driver model和pin control subsystem的接口,第七章描述了为Pin control subsystem提供database支持的Device Tree和Machine driver的接口。

3.3 pin control subsystem内部block diagram

       起始理解了接口部分内容,阅读和解析pin control subsystem的内部逻辑已经很简单,本文就不再分析了。

四、pin control subsystem向其他driver提供的接口

       当你准备撰写一个普通的linux driver(例如串口驱动)的时候,你期望pin control subsystem提供的接口是什么样子的?简单,当然最好是简单的,最最好是没有接口,当然这是可能的,具体请参考第六章的接口。

4.1 概述

      普通driver调用pin control subsystem的主要目标是:

(1)设定该设备的功能复用。设定设备的功能复用需要了解两个概念,一个是function,另外一个pin group。function是功能抽象,对应一个HW逻辑block,例如SPI0。虽然给定了具体的gunction name,我们并不能确定其使用的pins的情况。例如:为了设计灵活,芯片内部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一个pin group{ G4, G3, G2, G1 },但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的SPI0的逻辑功能电路只有一个。 因此,只有给出function selector(所谓selector就是一个ID或者index)以及function的pin group selector才能进行function mux的设定。
(2)设定该device对应的那些pin的电气特性。

      此外,由于电源管理的要求,某个device可能处于某个电源管理状态,例如idle或者sleep,这时候,属于该device的所有的pin就会需要处于另外的状态。综合上述的需求,我们把定义了pin control state的概念,也就是说设备可能处于非常多的状态中的一个,device driver可以切换设备处于的状态。为了方便管理pin control state,我们又提出了一个pin control state holder的概念,用来管理一个设备的所有的pin control状态。因此普通driver调用pin control subsystem的接口从逻辑上将主要是:

(1)获取pin control state holder的句柄
(2)设定pin control状态
(3)释放pin control state holder的句柄

pin control state holder的定义如下:

/**
 * struct pinctrl - per-device pin control state holder
 * @node: global list node
 * @dev: the device using this pin control handle
 * @states: a list of states for this device
 * @state: the current state
 * @dt_maps: the mapping table chunks dynamically parsed from device tree for
 *	this device, if any
 * @users: reference count
 */
struct pinctrl {
    struct list_head node;			// 系统中的所有device的pin control state holder
                                       被挂入到了一个全局链表中
    struct device *dev;				// 该pin control state holder对应的device
    struct list_head states;		// 该设备的所有的状态被挂入到这个链表中
    struct pinctrl_state *state;	// 当前的pin control state
    struct list_head dt_maps;		// mapping table
    struct kref users;				// reference count
};

      系统中的每一个需要和pin control subsystem进行交互的设备在进行设定之前都需要首先获取这个句柄。而属于该设备的所有的状态都是挂入到一个链表中,链表头就是pin control state holder的states成员,一个state的定义如下:

/**
 * struct pinctrl_state - a pinctrl state for a device
 * @node: list node for struct pinctrl's @states field
 * @name: the name of this state
 * @settings: a list of settings for this state
 */
struct pinctrl_state {
    struct list_head node;		// 挂入链表头的节点
    const char *name;			// 该state的名字
    struct list_head settings;	// 属于该状态的所有的settings
};

      一个pin state包含若干个setting,所有的settings被挂入一个链表中,链表头就是pin state中的settings成员,定义如下:

/**
 * struct pinctrl_setting - an individual mux or config setting
 * @node: list node for struct pinctrl_settings's @settings field
 * @type: the type of setting
 * @pctldev: pin control device handling to be programmed. Not used for
 *   PIN_MAP_TYPE_DUMMY_STATE.
 * @dev_name: the name of the device using this state
 * @data: Data specific to the setting type
 */
struct pinctrl_setting {
	struct list_head node;
	enum pinctrl_map_type type;
	struct pinctrl_dev *pctldev;
	const char *dev_name;
	union {
		struct pinctrl_setting_mux mux;
		struct pinctrl_setting_configs configs;
	} data;
};

      当driver设定一个pin state的时候,pin control subsystem内部会遍历该state的settings链表,将一个一个的setting进行设定。这些settings有各种类型,定义如下:

enum pinctrl_map_type {
    PIN_MAP_TYPE_INVALID,
    PIN_MAP_TYPE_DUMMY_STATE,
    PIN_MAP_TYPE_MUX_GROUP,				// 功能复用的setting
    PIN_MAP_TYPE_CONFIGS_PIN,			// 设定单一一个pin的电气特性
    PIN_MAP_TYPE_CONFIGS_GROUP,			// 设定单pin group的电气特性
};

      有pin mux相关的设定(PIN_MAP_TYPE_MUX_GROUP),定义如下:

/**
 * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP
 * @group: the group selector to program
 * @func: the function selector to program
 */
struct pinctrl_setting_mux {
    unsigned group;						// 该setting所对应的group selector
    unsigned func;						// 该setting所对应的function selector
};

      有了function selector以及属于该functiong的roup selector就可以进行该device和pin mux相关的设定了。设定电气特性的settings定义如下:

/**
 * struct pinctrl_map_configs - mapping table content for MAP_TYPE_CONFIGS_*
 * @group_or_pin: the name of the pin or group whose configuration parameters
 *	are to be configured.
 * @configs: a pointer to an array of config parameters/values to program into
 *	hardware. Each individual pin controller defines the format and meaning
 *	of config parameters.
 * @num_configs: the number of entries in array @configs
 */
struct pinctrl_map_configs {
    const char *group_or_pin		// 该pin或者pin group的名字
    unsigned long *configs;			// 要设定的值的列表。这个值被用来写入HW
    unsigned num_configs;			// 列表中值的个数
};

4.2 具体的接口

       (1) devm_pinctrl_get和pinctrl_get。devm_pinctrl_get是Resource managed版本的pinctrl_get,核心还是pinctrl_get函数。这两个接口都是获取设备(设备模型中的struct device)的pin control state holder(struct pinctrl)。pin control state holder不是静态定义的,一般在第一次调用该函数的时候会动态创建。创建一个pin control state holder是一个大工程,我们分析一下这段代码。

static struct pinctrl *create_pinctrl(struct device *dev,
				      struct pinctrl_dev *pctldev)
{
	struct pinctrl *p;
	const char *devname;
	struct pinctrl_maps *maps_node;
	int i;
	const struct pinctrl_map *map;
	int ret;

	/*
	 * create the state cookie holder struct pinctrl for each
	 * mapping, this is what consumers will get when requesting
	 * a pin control handle with pinctrl_get()
	 */
	p = kzalloc(sizeof(*p), GFP_KERNEL);   // 分配pin control state holde
r占用的内存并初始化
	if (!p)
		return ERR_PTR(-ENOMEM);
	p->dev = dev;
	INIT_LIST_HEAD(&p->states);
	INIT_LIST_HEAD(&p->dt_maps);

	/*mapping table这个database的建立也是动态的,当第一次调用pin control state 
holder的get函数的时候,就会通过调用pinctrl_dt_to_map来建立该device需要的mapping 
entry。具体请参考第七章。*/
	ret = pinctrl_dt_to_map(p, pctldev);
	if (ret < 0) {
		kfree(p);
		return ERR_PTR(ret);
	}

	devname = dev_name(dev);

	mutex_lock(&pinctrl_maps_mutex);
	/* Iterate over the pin control maps to locate the right ones */
	for_each_maps(maps_node, i, map) {
		/* Map must be for this device */
		if (strcmp(map->dev_name, devname))
			continue;
		/*
		 * If pctldev is not null, we are claiming hog for it,
		 * that means, setting that is served by pctldev by itself.
		 *
		 * Thus we must skip map that is for this device but is served
		 * by other device.
		 */
		if (pctldev &&
		    strcmp(dev_name(pctldev->dev), map->ctrl_dev_name))
			continue;

		ret = add_setting(p, pctldev, map);  //分析一个mapping entry,
把这个setting的代码加入到holder中
		/*
		 * At this point the adding of a setting may:
		 *
		 * - Defer, if the pinctrl device is not yet available
		 * - Fail, if the pinctrl device is not yet available,
		 *   AND the setting is a hog. We cannot defer that, since
		 *   the hog will kick in immediately after the device
		 *   is registered.
		 *
		 * If the error returned was not -EPROBE_DEFER then we
		 * accumulate the errors to see if we end up with
		 * an -EPROBE_DEFER later, as that is the worst case.
		 */
		if (ret == -EPROBE_DEFER) {
			pinctrl_free(p, false);
			mutex_unlock(&pinctrl_maps_mutex);
			return ERR_PTR(ret);
		}
	}
	mutex_unlock(&pinctrl_maps_mutex);

	if (ret < 0) {
		/* If some other error than deferral occurred, return here */
		pinctrl_free(p, false);
		return ERR_PTR(ret);
	}

	kref_init(&p->users);

	/* Add the pinctrl handle to the global list */
	/* 把这个新增加的pin control state holder加入到全局链表中 */
	mutex_lock(&pinctrl_list_mutex);
	list_add_tail(&p->node, &pinctrl_list);
	mutex_unlock(&pinctrl_list_mutex);

	return p;
}

      (2)devm_pinctrl_put和pinctrl_put。是(1)接口中的逆函数。devm_pinctrl_get和pinctrl_get获取句柄的时候申请了很多资源,在devm_pinctrl_put和pinctrl_put可以释放。需要注意的是多次调用get函数不会重复分配资源,只会reference count加一,在put中referrenct count减一,当count==0的时候才释放该device的pin control state holder持有的所有资源。
      (3)pinctrl_lookup_state。根据state name在pin control state holder找到对应的pin control state。具体的state是各个device自己定义的,不过pin control subsystem自己定义了一些标准的pin control state,定义在pinctrl-state.h文件中

/**
 * @PINCTRL_STATE_DEFAULT: the state the pinctrl handle shall be put
 *	into as default, usually this means the pins are up and ready to
 *	be used by the device driver. This state is commonly used by
 *	hogs to configure muxing and pins at boot, and also as a state
 *	to go into when returning from sleep and idle in
 *	.pm_runtime_resume() or ordinary .resume() for example.
 * @PINCTRL_STATE_INIT: normally the pinctrl will be set to "default"
 *	before the driver's probe() function is called.  There are some
 *	drivers where that is not appropriate becausing doing so would
 *	glitch the pins.  In those cases you can add an "init" pinctrl
 *	which is the state of the pins before drive probe.  After probe
 *	if the pins are still in "init" state they'll be moved to
 *	"default".
 * @PINCTRL_STATE_IDLE: the state the pinctrl handle shall be put into
 *	when the pins are idle. This is a state where the system is relaxed
 *	but not fully sleeping - some power may be on but clocks gated for
 *	example. Could typically be set from a pm_runtime_suspend() or
 *	pm_runtime_idle() operation.
 * @PINCTRL_STATE_SLEEP: the state the pinctrl handle shall be put into
 *	when the pins are sleeping. This is a state where the system is in
 *	its lowest sleep state. Could typically be set from an
 *	ordinary .suspend() function.
 */
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_INIT "init"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"

      (4)pinctrl_select_state。设定一个具体的pin control state接口。

五、和GPIO subsystem交互

5.1 为何pin control subsystem要和GPIO subsystem交互?

作为软件工程师,我们期望的硬件设计应该如下图所示:

      GPIO的HW block应该和其他功能复用的block是对等关系的,它们共同输入到一个复用器block,这个block的寄存器控制哪一个功能电路目前是active的。pin configuration是全局的,不论哪种功能是active的,都可以针对pin进行电气特性的设定。这样的架构下,上图中红色边框的三个block是完全独立的HW block,其控制寄存器在SOC datasheet中应该是分成三个章节描述,同时,这些block的寄存器应该分别处于不同的地址区间。

      对于软件工程师,我们可以让pin control subsystem和GPIO subsystem完全独立,各自进行初始化,各自映射自己的寄存器地址空间,对于pin control subsystem而言,GPIO和其他的HW block没有什么不同,都是使用自己提供服务的一个软件模块而已。然而实际上SOC的设计并非总是向软件工程师期望的那样,有的SOC的设计框架图如下:

       这时候,GPIO block是alway active的,而红色边框的三个block是紧密的捆绑在一起,它们的寄存器占据了一个memory range(datasheet中用一个章节描述这三个block)。这时候,对于软件工程师来说就有些纠结了,本来不属于我的GPIO控制也被迫要参与进来。这时候,硬件寄存器的控制都是pin controller来处理,GPIO相关的操作都要经过pin controller driver,这时候,pin controller driver要作为GPIO driver的back-end出现。

5.2 具体的接口形态

5.2.1 pinctrl_request_gpio

      该接口主要用来申请GPIO。GPIO也是一种资源,使用前应该request,使用完毕后释放。具体的代码如下:

/**
 * pinctrl_request_gpio() - request a single pin to be used as GPIO
 * @gpio: the GPIO pin number from the GPIO subsystem number space
 *
 * This function should *ONLY* be used from gpiolib-based GPIO drivers,
 * as part of their gpio_request() semantics, platforms and individual drivers
 * shall *NOT* request GPIO pins to be muxed in.
 */
int pinctrl_request_gpio(unsigned gpio)
{
	struct pinctrl_dev *pctldev;
	struct pinctrl_gpio_range *range;
	int ret;
	int pin;

	ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);    // A
	if (ret) {
		if (pinctrl_ready_for_gpio_range(gpio))
			ret = 0;
		return ret;
	}

	mutex_lock(&pctldev->mutex);

	/* Convert to the pin controllers number space */
	pin = gpio_to_pin(range, gpio);				// 将GPIO ID转换成pin ID

	ret = pinmux_request_gpio(pctldev, range, pin, gpio);			// B

	mutex_unlock(&pctldev->mutex);

	return ret;
}

      毫无疑问,申请GPIO资源本应该是GPIO subsystem的责任,但是由于上一节描述的源由,pin control subsystem提供了这样一个接口函数供GPIO driver使用(其他的内核driver不应该调用,它们应该使用GPIO subsystem提供的接口)。多么丑陋的代码,作为pin control subsystem,除了维护pin space中的ID,还要维护GPIO 的ID以及pin ID和GPIO ID的关系。
      A:根据GPIO ID找到该ID对应的pin control device(struct pinctrl_dev)和GPIO rang(pinctrl_gpio_range)。在core driver中,每个low level的pin controller device都被映射成一个struct pinctrl_dev,并形成链表,链表头就是pinctrldev_list。由于实际的硬件设计(例如GPIO block被分成若干个GPIO 的bank,每个bank就对应一个HW GPIO Controller Block),一个pin control device要管理的GPIO ID是分成区域的,每个区域用struct pinctrl_gpio_range来抽象,在low level 的pin controller初始化的时候(具体参考samsung_pinctrl_register的代码),会调用pinctrl_add_gpio_range将每个GPIO bank表示的gpio range挂入到pin control device的range list中(gpio_ranges成员)。

      pinctrl_gpio_range 的定义如下:

/**
 * struct pinctrl_gpio_range - each pin controller can provide subranges of
 * the GPIO number space to be handled by the controller
 * @node: list node for internal use
 * @name: a name for the chip in this range
 * @id: an ID number for the chip in this range
 * @base: base offset of the GPIO range
 * @pin_base: base pin number of the GPIO range if pins == NULL
 * @pins: enumeration of pins in GPIO range or NULL
 * @npins: number of pins in the GPIO range, including the base number
 * @gc: an optional pointer to a gpio_chip
 */
struct pinctrl_gpio_range {
    struct list_head node;
    const char *name;
    unsigned int id;				// GPIO chip ID
    unsigned int base;				// 该range中的起始GPIO IDD
    unsigned int pin_base;			// 在线性映射的情况下,这是起始的pin base
    unsigned const *pins;			// 在非线性映射的时候,这是table是pin到GPIO的lookup table
    unsigned int npins;				// 这个range有多少个GPIO引脚
    struct gpio_chip *gc;			// 每个GPIO bank都是一个gpio chip,对应一个GPIO range
};

      pin ID和GPIO ID有两种映射关系,一种是线性映射(这时候pin_base有效),也就是说,对于这个GPIO range,GPIO base ID是a,pin ID base是b,那么a<--->b,a+1<--->b+1,a+2<--->b+2,以此类推。对于非线性映射(pin_base无效,pins是有效的),我们需要建立一个lookup table,以GPIO ID为索引,可以找到对于的pin ID。
      B:这里主要是进行复用功能的设定,毕竟GPIO也是引脚的一个特定的功能。        pinmux_request_gpio函数的作用主要有两个,一个是在core driver中标记该pin已经用作GPIO了,这样,如果有模块后续request该资源,那么core driver可以拒绝不合理的要求。第二步就是调用底层pin controller driver的callback函数,进行底层寄存器相关的操作。

5.2.2 pinctrl_free_gpio

      有申请就有释放,这是pinctrl_request_gpio的逆函数

5.2.3 pinctrl_gpio_direction_input/pinctrl_gpio_direction_output

      为已经指定为GPIO功能的引脚设定方向,输入或者输出。代码很简单,不再赘述。

六、和驱动模型的接口

      前文已经表述过,最好是让统一设备驱动模型(Driver model)来处理pin 的各种设定。与其自己写代码调用devm_pinctrl_get、pinctrl_lookup_state、pinctrl_select_state等pin control subsystem的接口函数,为何不让linux内核自己的框架处理呢。本章将分析具体的代码,这些代码实例对自己driver调用pin control subsystem的接口函数来设定本device的pin control的相关设定也是有指导意义的。 linux kernel中的驱动模型提供了driver和device的绑定机制,一旦匹配会调用probe函数如下:

static int really_probe(struct device *dev, struct device_driver *drv)
{
    ……
    ret = pinctrl_bind_pins(dev); 		// 对该device涉及的pin进行pin control相关设定
    ……

    if (dev->bus->probe) {				// 下面是真正的probe过程
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
	……
}

pinctrl_bind_pins的代码如下:

/**
 * pinctrl_bind_pins() - called by the device core before probe
 * @dev: the device that is just about to probe
 */
int pinctrl_bind_pins(struct device *dev)
{
	int ret;

	if (dev->of_node_reused)
		return 0;

	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);      // (1)
	if (!dev->pins)
		return -ENOMEM;

	dev->pins->p = devm_pinctrl_get(dev);								  // (2)
	if (IS_ERR(dev->pins->p)) {
		dev_dbg(dev, "no pinctrl handle\n");
		ret = PTR_ERR(dev->pins->p);
		goto cleanup_alloc;
	}

	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,			// (3)
					PINCTRL_STATE_DEFAULT);
	if (IS_ERR(dev->pins->default_state)) {
		dev_dbg(dev, "no default pinctrl state\n");
		ret = 0;
		goto cleanup_get;
	}

	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");

		ret = pinctrl_select_state(dev->pins->p,
					   dev->pins->default_state);
	} else {
		ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);	// (4)
	}

	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
	 * <linux/pinctrl/pinctrl-state.h>
	 */
	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;
}

      (1)struct device数据结构有一个pins的成员,它描述了和该设备相关的pin control的信息,定义如下:

/**
 * struct dev_pin_info - pin state container for devices
 * @p: pinctrl handle for the containing device
 * @default_state: the default state for the handle, if found
 * @init_state: the state at probe time, if found
 * @sleep_state: the state at suspend time, if found
 * @idle_state: the state at idle (runtime suspend) time, if found
 */
struct dev_pin_info {
	struct pinctrl *p;
	struct pinctrl_state *default_state;
	struct pinctrl_state *init_state;
#ifdef CONFIG_PM
	struct pinctrl_state *sleep_state;
	struct pinctrl_state *idle_state;
#endif
};

      (2)调用devm_pinctrl_get获取该device对应的 pin control state holder句柄。
      (3)搜索default state,sleep state,idle state并记录在本device中
      (4)将该设备设定为pin default/init state

七、和device tree或者machine driver相关的接口

7.1 概述

       device tree或者machine driver这两个模块主要是为 pin control subsystem提供pin mapping database的支持。这个database的每个entry用下面的数据结构表示:

/**
 * struct pinctrl_map - boards/machines shall provide this map for devices
 * @dev_name: the name of the device using this specific mapping, the name
 *	must be the same as in your struct device*. If this name is set to the
 *	same name as the pin controllers own dev_name(), the map entry will be
 *	hogged by the driver itself upon registration
 * @name: the name of this specific map entry for the particular machine.
 *	This is the parameter passed to pinmux_lookup_state()
 * @type: the type of mapping table entry
 * @ctrl_dev_name: the name of the device controlling this specific mapping,
 *	the name must be the same as in your struct device*. This field is not
 *	used for PIN_MAP_TYPE_DUMMY_STATE
 * @data: Data specific to the mapping type
 */
struct pinctrl_map {
    const char *dev_name;					// 使用这个mapping entry的设备名
    const char *name;						// 该名字表示了该mapping entry
    enum pinctrl_map_type type;				// 这个entry的mapping type
    const char *ctrl_dev_name; 				// pin controller这个设备的名字
	union {
		struct pinctrl_map_mux mux;
		struct pinctrl_map_configs configs;
	} data;
};

7.2 通过machine driver静态定义的数据来建立pin mapping database

      machine driver定义一个巨大的mapping table,描述,然后在machine初始化的时候,调用pinctrl_register_mappings将该table注册到pin control subsystem中。

7.3 通过device tree来建立pin mapping database

      pin mapping信息定义在dts中,主要包括两个部分,一个是定义在各个具体的device node中,另外一处是定义在pin controller的device node中。
      一个典型的device tree中的外设node定义如下(建议先看看pin controller driver的第二章关于dts的描述):

device-node-name { 
        // 定义该device自己的属性  
        pinctrl-names = "sleep", "default";
        pinctrl-0 = ;
        pinctrl-1 = ;        
    };

      对普通device的dts分析在函数pinctrl_dt_to_map中,代码如下:

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;
	.........
	/* For each defined state ID */
	for (state = 0; ; state++) {								// (1)
		/* 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);									// (2)

		/* Determine whether pinctrl-names property names the state */
		ret = of_property_read_string_index(np, "pinctrl-names",			// (3)
						    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) {
			/* strlen("pinctrl-") == 8 */
			statename = prop->name + 8;						// (4)
		}

		/* For every referenced pin configuration node in it */
		for (config = 0; config < size; config++) {			// (5)
			phandle = be32_to_cpup(list++);

			/* Look up the pin configuration node */
			np_config = of_find_node_by_phandle(phandle);	// (6)
			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,	// (7)
						   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);		// (8)
			if (ret < 0)
				goto err;
		}
	}

	return 0;

err:
	pinctrl_dt_free_maps(p);
	return ret;
}

      (1)pinctrl-0 pinctrl-1 pinctrl-2……表示了该设备的一个个的状态,这里我们定义了两个pinctrl-0和pinctrl-1分别对应sleep和default状态。这里每次循环分析一个pin state。
      (2)代码执行到这里,size和list分别保存了该pin state中所涉及pin configuration phandle的数目以及phandle的列表
      (3)读取从pinctrl-names属性中获取state name
      (4)如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1 pinctrl-2……中的那个ID取出来作为state name
      (5)遍历一个pin state中的pin configuration list,这里的pin configuration实际应该是pin controler device node中的sub node,用phandle标识。
      (6)用phandle作为索引,在device tree中找他该phandle表示的那个pin configuration
      (7)分析一个pin configuration,具体下面会仔细分析
      (8)如果该设备没有定义pin configuration,那么也要创建一个dummy的pin state。

      这里我们已经进入对pin controller node下面的子节点的分析过程了。分析一个pin configuration的代码如下:

static int dt_to_map_one_config(struct pinctrl *p,
				struct pinctrl_dev *hog_pctldev,
				const char *statename,
				struct device_node *np_config)
{
	struct pinctrl_dev *pctldev = NULL;
	struct device_node *np_pctldev;
	const struct pinctrl_ops *ops;
	int ret;
	struct pinctrl_map *map;
	unsigned num_maps;

	/* Find the pin controller containing np_config */
	np_pctldev = of_node_get(np_config);
	for (;;) {									
		np_pctldev = of_get_next_parent(np_pctldev);						//(1)
		.......
		pctldev = get_pinctrl_dev_from_of_node(np_pctldev);					//(2)
		if (pctldev)
			break;															//(3)
		/* Do not defer probing of hogs (circular loop) */
		if (np_pctldev == p->dev->of_node) {
			of_node_put(np_pctldev);
			return -ENODEV;
		}
	}
	of_node_put(np_pctldev);

	/*
	 * Call pinctrl driver to parse device tree node, and
	 * generate mapping table entries
	 */
	ops = pctldev->desc->pctlops;
	.......
	ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);			//(4)
	if (ret < 0)
		return ret;

	/* Stash the mapping table chunk away for later use */
	return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);   // (5)
}

       (1)首先找到该pin configuration node对应的parent node(也就是pin controler对应的node),如果找不到或者是root node,则进入出错处理。
       (2)获取pin control class device
       (3)一旦找到pin control class device则跳出for循环
       (4)调用底层的callback函数处理pin configuration node。这也是合理的,毕竟很多的pin controller bindings是需要自己解析的。
       (5)将该pin configuration node的mapping entry信息注册到系统中.

八、core driver和low level pin controller driver的接口规格

      pin controller描述符。每一个特定的pin controller都用一个struct pinctrl_desc来抽象,具体如下:

/**
 * struct pinctrl_desc - pin controller descriptor, register this to pin
 * control subsystem
 * @name: name for the pin controller
 * @pins: an array of pin descriptors describing all the pins handled by
 *	this pin controller
 * @npins: number of descriptors in the array, usually just ARRAY_SIZE()
 *	of the pins field above
 * @pctlops: pin control operation vtable, to support global concepts like
 *	grouping of pins, this is optional.
 * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
 * @confops: pin config operations vtable, if you support pin configuration in
 *	your driver
 * @owner: module providing the pin controller, used for refcounting
 * @num_custom_params: Number of driver-specific custom parameters to be parsed
 *	from the hardware description
 * @custom_params: List of driver_specific custom parameters to be parsed from
 *	the hardware description
 * @custom_conf_items: Information how to print @params in debugfs, must be
 *	the same size as the @custom_params, i.e. @num_custom_params
 */
struct pinctrl_desc {
	const char *name;
	const struct pinctrl_pin_desc *pins;
	unsigned int npins;
	const struct pinctrl_ops *pctlops;
	const struct pinmux_ops *pmxops;
	const struct pinconf_ops *confops;
	struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
	unsigned int num_custom_params;
	const struct pinconf_generic_params *custom_params;
	const struct pin_config_item *custom_conf_items;
#endif
};

      pin controller描述符需要描述它可以控制多少个pin(成员npins),每一个pin的信息为何?(成员pins)。这两个成员就确定了一个pin controller所能控制的引脚的信息。
      pin controller描述符中包括了三类操作函数:pctlops是一些全局的控制函数,pmxops是复用引脚相关的操作函数,confops操作函数是用来配置引脚的特性(例如:pull-up/down)。struct pinctrl_ops中各个callback函数的具体的解释如下:
      pin controller描述符中包括了三类操作函数:pctlops是一些全局的控制函数,pmxops是复用引脚相关的操作函数,confops操作函数是用来配置引脚的特性(例如:pull-up/down)。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值