LinuxGPIO子系统3

LinuxGPIO子系统3(基于Linux6.6)---pin controller driver介绍


一、概述

Linux 中的 Pin Controller(引脚控制器) 是一个用于管理和配置嵌入式系统硬件中引脚(pins)的子系统。它允许开发人员通过驱动程序设置和控制芯片的引脚特性,如输入输出方向、驱动电压、上拉/下拉电阻、复用功能等。该功能对于嵌入式设备和 SoC(System on Chip)非常重要,因为这些设备通常具有多功能的引脚,每个引脚可以在不同的工作模式下执行不同的任务(如 GPIO、UART、SPI、I2C 等)。

1.1、Pin Controller 的作用

Pin Controller 的主要作用是管理 SoC 上的所有物理引脚的配置,包括:

  • 引脚复用(Pin Multiplexing):配置每个引脚的功能,如将引脚设置为 GPIO、UART、SPI、I2C、PWM、ADC、触摸屏等功能。
  • 电气配置:设置引脚的电气特性,例如:
    • 输入、输出模式。
    • 上拉/下拉电阻(用于防止引脚浮空)。
    • 驱动强度(电流驱动能力)。
  • 中断配置:如果引脚支持中断,可以配置中断触发方式(上升沿、下降沿或双边缘触发)。

1.2、Pin Control 在 Linux 内核中的实现

Pin Control 通过内核的 pinctrl 子系统来实现。Linux 使用 pinctrl 驱动程序来为每个硬件平台定义和管理这些引脚配置。

主要模块

  • pinctrl 驱动:负责硬件平台的引脚配置和管理。
  • pinctrl API:提供了在驱动中对引脚进行配置和控制的接口。
  • 设备树(Device Tree):大多数平台使用设备树来描述硬件,Pin Controller 的配置信息通常也存储在设备树中。

1.3、Pin Controller 核心组件

  1. Pin Configurations: 每个引脚都可能具有多个配置选项。这些配置通常由一个或多个寄存器控制。例如,一个引脚可能可以配置为不同的模式(GPIO、UART Tx/Rx、SPI CLK 等)。

  2. Pin Groups: Pin Controller 允许将多个引脚组合在一起进行配置。每个组可以包含多个引脚,这些引脚具有相同的配置需求。例如,一组引脚可能需要配置为 I2C 总线。

  3. Pin Functions: 每个引脚可以支持多种功能。例如,GPIO、UART、I2C、SPI、PWM 等。Pin Controller 允许通过不同的函数切换引脚的工作模式。

  4. Pinmux: 引脚复用是通过 Pinmux 功能来实现的。Pinmux 控制器配置每个引脚的功能和电气属性。

  5. GPIO 和 Pin Control 结合: 在许多平台中,GPIO 控制器与 Pin Controller 配合使用。GPIO 控制器提供基本的数字输入输出功能,而 Pin Controller 提供更高级的引脚功能和配置。

1.4、Pin Controller 的主要 API

在 Linux 内核中,pinctrl 子系统提供了一些 API 来帮助开发人员进行引脚控制。

  • pinctrl_get():获取某个设备的 pinctrl 状态或句柄。
  • pinctrl_select_state():选择一个特定的引脚状态(比如配置为某个模式)。
  • pinctrl_register():用于注册一个新的 Pin Control 驱动。
  • pinctrl_free():释放之前分配的引脚资源。

二、pin controller相关的DTS描述

类似其他的硬件,pin controller这个HW block需要是device tree中的一个节点。此外,各个其他的HW block在驱动之前也需要先配置其引脚复用功能,因此,这些device(称pin controller是host,那么这些使用pin controller进行引脚配置的device叫做client device)也需要在它自己的device tree node中描述pin control的相关内容。

2.1、pin controller DTS结构

DTS结构:

	pinctrl: pinctrl {
		compatible = "rockchip,rk3568-pinctrl";
		rockchip,grf = <&grf>;
		rockchip,pmu = <&pmugrf>;
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
}

每个pin configuration都是pin controller的child node,描述了client device要使用到的一组pin的配置信息。具体如何定义pin configuration是和具体的pin controller相关的。

在pin controller node中定义pin configuration其目的是为了让client device引用。

所谓client device其实就是使用pin control subsystem提供服务的那些设备,例如串口设备。在使用之前,一般会在初始化代码中配置相关的引脚功能是串口功能。

有了device tree,可以通过device tree来传递这样的信息。也就是说,各个device可以通过自己节点的属性来指向pin controller的某个child node,也就是pin configuration了。

2.2、pin configuration定义

两个简单的例子(当然一个是pin bank,另外一个是定义功能复用配置)来理解pin configuration第一个例子是描述pin bank:

	pinctrl: pinctrl {
		compatible = "rockchip,rk3568-pinctrl";
		rockchip,grf = <&grf>;
		rockchip,pmu = <&pmugrf>;
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;

		gpio0: gpio0@fdd60000 {
			compatible = "rockchip,gpio-bank";
			reg = <0x0 0xfdd60000 0x0 0x100>;
			interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;

			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};

		gpio1: gpio1@fe740000 {
			compatible = "rockchip,gpio-bank";
			reg = <0x0 0xfe740000 0x0 0x100>;
			interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&cru PCLK_GPIO1>, <&cru DBCLK_GPIO1>;

			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};

		gpio2: gpio2@fe750000 {
			compatible = "rockchip,gpio-bank";
			reg = <0x0 0xfe750000 0x0 0x100>;
			interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>;

			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};

		gpio3: gpio3@fe760000 {
			compatible = "rockchip,gpio-bank";
			reg = <0x0 0xfe760000 0x0 0x100>;
			interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;

			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};

		gpio4: gpio4@fe770000 {
			compatible = "rockchip,gpio-bank";
			reg = <0x0 0xfe770000 0x0 0x100>;
			interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&cru PCLK_GPIO4>, <&cru DBCLK_GPIO4>;

			gpio-controller;
			#gpio-cells = <2>;
			interrupt-controller;
			#interrupt-cells = <2>;
		};
	};

之所以分成bank,主要是把特性相同的GPIO进行分组,方便控制。例如:这些bank中,只有GPF和GPG这两个bank上的引脚有中断功能,其他的都没有。interrupt-controller这个属性相信大家已经熟悉,就是说明该node是一个interrupt controller。gpio-controller类似,说明该device node是一个GPIO controller。#gpio-cells属性是一个GPIO controller的必须定义的属性,它描述了需要多少个cell来具体描述一个GPIO(这是和具体的GPIO controller相关的)。#interrupt-cells的概念类似,不再赘述。phandle(linux,phandle这个属性和phandle是一样的,只不过linux,phandle是old-style,多定义一个属性是为了兼容)定义了一个句柄,当其他的device node想要引用这个node的时候就可以使用该句柄。具体的例子参考下节client device的DTS的描述。

另外一个例子是uart的pin configuration,代码如下:

	uart0 {
		/omit-if-no-ref/
		uart0_xfer: uart0-xfer {
			rockchip,pins =
				/* uart0_rx */
				<0 RK_PC0 3 &pcfg_pull_up>,
				/* uart0_tx */
				<0 RK_PC1 3 &pcfg_pull_up>;
		};

		/omit-if-no-ref/
		uart0_ctsn: uart0-ctsn {
			rockchip,pins =
				/* uart0_ctsn */
				<0 RK_PC7 3 &pcfg_pull_none>;
		};

		/omit-if-no-ref/
		uart0_rtsn: uart0-rtsn {
			rockchip,pins =
				/* uart0_rtsn */
				<0 RK_PC4 3 &pcfg_pull_none>;
		};
	};

	uart1 {
		/omit-if-no-ref/
		uart1m0_xfer: uart1m0-xfer {
			rockchip,pins =
				/* uart1_rxm0 */
				<2 RK_PB3 2 &pcfg_pull_up>,
				/* uart1_txm0 */
				<2 RK_PB4 2 &pcfg_pull_up>;
		};

		/omit-if-no-ref/
		uart1m0_ctsn: uart1m0-ctsn {
			rockchip,pins =
				/* uart1m0_ctsn */
				<2 RK_PB6 2 &pcfg_pull_none>;
		};

		/omit-if-no-ref/
		uart1m0_rtsn: uart1m0-rtsn {
			rockchip,pins =
				/* uart1m0_rtsn */
				<2 RK_PB5 2 &pcfg_pull_none>;
		};

		/omit-if-no-ref/
		uart1m1_xfer: uart1m1-xfer {
			rockchip,pins =
				/* uart1_rxm1 */
				<3 RK_PD7 4 &pcfg_pull_up>,
				/* uart1_txm1 */
				<3 RK_PD6 4 &pcfg_pull_up>;
		};

		/omit-if-no-ref/
		uart1m1_ctsn: uart1m1-ctsn {
			rockchip,pins =
				/* uart1m1_ctsn */
				<4 RK_PC1 4 &pcfg_pull_none>;
		};

		/omit-if-no-ref/
		uart1m1_rtsn: uart1m1-rtsn {
			rockchip,pins =
				/* uart1m1_rtsn */
				<4 RK_PB6 4 &pcfg_pull_none>;
		};
	};

这段代码是一个 设备树 (Device Tree) 配置,描述了 uart0 的引脚配置和相关设置。它使用了 rockchip 平台的引脚控制(Pin Control)和 UART 配置。

具体来说,uart0_xfer 节点配置了 uart0_rxuart0_tx 这两个引脚,包含了引脚编号、功能设置以及电气特性(如上拉配置)。

2.3、client device的DTS

一个典型的device tree中的外设node定义如下:

device-node-name { 
        定义该device自己的属性  

        pinctrl-names = "sleep", "active";------(1)
        pinctrl-0 = <pin-config-0-a>;--------------(2)
        pinctrl-1 = <pin-config-1-a pin-config-1-b>;        
    };

(1)pinctrl-names定义了一个state列表。那么什么是state呢?具体说应该是pin state,对于一个client device,它使用了一组pin,这一组pin应该同时处于某种状态,毕竟这些pin是属于一个具体的设备功能。state的定义和电源管理关系比较紧密,例如当设备active的时候,我们需要pin controller将相关的一组pin设定为具体的设备功能,而当设备进入sleep状态的时候,需要pin controller将相关的一组pin设定为普通GPIO,并精确的控制GPIO状态以便节省系统的功耗。state有两种,标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。根据例子中的定义,state ID等于0(名字是sleep)的state对应pinctrl-0属性,state ID等于1(名字是active)的state对应pinctrl-1属性。具体设备state的定义和各个设备相关,具体参考在自己的device bind。

(2)pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pin configuration。有时候,一个state对应多个pin configure。例如在active的时候,I2C功能有两种配置,一种是从pin ID{7,8}引出,另外一个是从pin ID{69,103}引出。

例如串口的dts配置:

    uart1: serial@fe650000 {
        compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";
        reg = <0x0 0xfe650000 0x0 0x100>;
        interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
        clock-names = "baudclk", "apb_pclk";
        reg-shift = <2>;
        reg-io-width = <4>;
        dmas = <&dmac0 2>, <&dmac0 3>;
        pinctrl-names = "default";
        pinctrl-0 = <&uart1m0_xfer>;
        status = "disabled";
    };

该serial device只定义了一个state就是default,对应pinctrl-0属性定义。pinctrl-0是一个句柄(phandle)列表,每个句柄指向一个pin configuration。0x2对应上节中的uart0-data节点,0x03对应uart0-fctl 节点,也就是说,这个串口有两种配置,一种是从gph bank中的第一个和第二个GPIO pin引出,另外一个是从gph bank中的第8个和第9个GPIO pin引出。

三、 pin controller driver初始化

3.1、注册pin control device

在系统初始化的时候,dts描述的device node会形成一个树状结构,在machine初始化的过程中,会scan device node的树状结构,将真正的硬件device node变成一个个的设备模型中的device结构(比如struct platform_device)并加入到系统中。

	pinctrl: pinctrl {
		compatible = "rockchip,rk3568-pinctrl";

……省略wakeup的pin configuration

……省略gpio0-4这些pink bank的pin configuration

……省略Pin groups的相关描述

}

compatible属性用来描述pin controller的programming model。该属性的值是string list,定义了一系列的modle(每个string是一个model)。

3.2、注册pin controller driver

pinctrl: pinctrl这个device node也会变成一个platform device加入系统。有了device,那么对应的platform driver是如何注册到系统中的呢?代码如下:

drivers/pinctrl/pinctrl-rockchip.c

static int __init rockchip_pinctrl_drv_register(void)
{
	return platform_driver_register(&rockchip_pinctrl_driver);
}
postcore_initcall(rockchip_pinctrl_drv_register);

系统初始化的时候,该函数会执行,向系统注册了rockchip_pinctrl_driver:

static struct platform_driver rockchip_pinctrl_driver = {
	.probe		= rockchip_pinctrl_probe,
	.remove		= rockchip_pinctrl_remove,
	.driver = {
		.name	= "rockchip-pinctrl",
		.pm = &rockchip_pinctrl_dev_pm_ops,
		.of_match_table = rockchip_pinctrl_dt_match,
	},
};

3.3、probe过程

在Linux kernel引入统一设备模型之后,bus、driver和device形成了设备模型中的铁三角。对于platform这种类型的bus,其铁三角数据是platform_bus_type(表示platform这种类型的bus)、struct platform_device(platform bus上的device)、struct platform_driver(platform bus上的driver)。具体匹配的代码是platform bus上的match函数,如下:

drivers/base/platform.c 

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

旧的的platform的匹配函数就是简单的比较device和driver的名字,多么简单,多么清晰,真是有点怀念过去单纯而美好的生活。当然,事情没有那么糟糕,我们这里只要关注OF style的匹配过程即可,思路很简单,解铃还需系铃人,把匹配过程推给device tree模块,代码如下:

const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if (!matches || !dev->of_node || dev->of_node_reused)
		return NULL;
	return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);

platform driver提供了match table(struct device_driver 中的of_match_table的成员)。platform device提供了device tree node(struct device中的of_node成员)。对于我们这个场景,match table是rockchip_pinctrl_dt_match,代码如下:

static const struct of_device_id rockchip_pinctrl_dt_match[] = {
	{ .compatible = "rockchip,px30-pinctrl",
		.data = &px30_pin_ctrl },
	{ .compatible = "rockchip,rv1108-pinctrl",
		.data = &rv1108_pin_ctrl },
	{ .compatible = "rockchip,rv1126-pinctrl",
		.data = &rv1126_pin_ctrl },
	{ .compatible = "rockchip,rk2928-pinctrl",
		.data = &rk2928_pin_ctrl },
	{ .compatible = "rockchip,rk3036-pinctrl",
		.data = &rk3036_pin_ctrl },
	{ .compatible = "rockchip,rk3066a-pinctrl",
		.data = &rk3066a_pin_ctrl },
	{ .compatible = "rockchip,rk3066b-pinctrl",
		.data = &rk3066b_pin_ctrl },
	{ .compatible = "rockchip,rk3128-pinctrl",
		.data = (void *)&rk3128_pin_ctrl },
	{ .compatible = "rockchip,rk3188-pinctrl",
		.data = &rk3188_pin_ctrl },
	{ .compatible = "rockchip,rk3228-pinctrl",
		.data = &rk3228_pin_ctrl },
	{ .compatible = "rockchip,rk3288-pinctrl",
		.data = &rk3288_pin_ctrl },
	{ .compatible = "rockchip,rk3308-pinctrl",
		.data = &rk3308_pin_ctrl },
	{ .compatible = "rockchip,rk3328-pinctrl",
		.data = &rk3328_pin_ctrl },
	{ .compatible = "rockchip,rk3368-pinctrl",
		.data = &rk3368_pin_ctrl },
	{ .compatible = "rockchip,rk3399-pinctrl",
		.data = &rk3399_pin_ctrl },
	{ .compatible = "rockchip,rk3568-pinctrl",
		.data = &rk3568_pin_ctrl },
	{ .compatible = "rockchip,rk3588-pinctrl",
		.data = &rk3588_pin_ctrl },
	{},
};

一旦pin controller这个device遇到了适当的driver,就会调用probe函数进行具体的driver初始化的动作了,代码如下:

 drivers/pinctrl/pinctrl-rockchip.c

static int rockchip_pinctrl_probe(struct platform_device *pdev)
{
	struct rockchip_pinctrl *info;
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node, *node;
	struct rockchip_pin_ctrl *ctrl;
	struct resource *res;
	void __iomem *base;
	int ret;

	if (!dev->of_node)
		return dev_err_probe(dev, -ENODEV, "device tree node not found\n");

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

	info->dev = dev;

	ctrl = rockchip_pinctrl_get_soc_data(info, pdev); ----------(2)
	if (!ctrl)
		return dev_err_probe(dev, -EINVAL, "driver data not available\n");
	info->ctrl = ctrl;

	node = of_parse_phandle(np, "rockchip,grf", 0);
	if (node) {
		info->regmap_base = syscon_node_to_regmap(node);
		of_node_put(node);
		if (IS_ERR(info->regmap_base))
			return PTR_ERR(info->regmap_base);
	} else {
		base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
		if (IS_ERR(base))
			return PTR_ERR(base);

		rockchip_regmap_config.max_register = resource_size(res) - 4;
		rockchip_regmap_config.name = "rockchip,pinctrl";
		info->regmap_base =
			devm_regmap_init_mmio(dev, base, &rockchip_regmap_config);

		/* to check for the old dt-bindings */
		info->reg_size = resource_size(res);

		/* Honor the old binding, with pull registers as 2nd resource */
		if (ctrl->type == RK3188 && info->reg_size < 0x200) {
			base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
			if (IS_ERR(base))
				return PTR_ERR(base);

			rockchip_regmap_config.max_register = resource_size(res) - 4;
			rockchip_regmap_config.name = "rockchip,pinctrl-pull";
			info->regmap_pull =
				devm_regmap_init_mmio(dev, base, &rockchip_regmap_config);
		}
	}

	/* try to find the optional reference to the pmu syscon */
	node = of_parse_phandle(np, "rockchip,pmu", 0);
	if (node) {
		info->regmap_pmu = syscon_node_to_regmap(node);
		of_node_put(node);
		if (IS_ERR(info->regmap_pmu))
			return PTR_ERR(info->regmap_pmu);
	}

	ret = rockchip_pinctrl_register(pdev, info);-------------(3)
	if (ret)
		return ret;

	platform_set_drvdata(pdev, info);-设定platform device的私有数据

	ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
	if (ret)
		return dev_err_probe(dev, ret, "failed to register gpio device\n");

	return 0;
}

(1)devm_kzalloc函数是为struct rockchip_pinctrl数据结构分配内存。

(2)分配了struct rockchip_pinctrl数据结构的内存,当然下一步就是初始化这个数据结构了。

 drivers/pinctrl/pinctrl-rockchip.h

struct rockchip_pinctrl {
	struct regmap			*regmap_base;
	int				reg_size;
	struct regmap			*regmap_pull;
	struct regmap			*regmap_pmu;
	struct device			*dev;
	struct rockchip_pin_ctrl	*ctrl;
	struct pinctrl_desc		pctl;
	struct pinctrl_dev		*pctl_dev;
	struct rockchip_pin_group	*groups;
	unsigned int			ngroups;
	struct rockchip_pmx_func	*functions;
	unsigned int			nfunctions;
};

struct pinctrl_desc和struct pinctrl_dev 都是pin control subsystem中core driver的概念。各个具体硬件的pin controller可能会各不相同,但是可以抽取其共同的部分来形成一个HW independent的数据结构,这个数据就是pin controller描述符,在core driver中用struct pinctrl_desc表示,具体该数据结构的定义如下:

include/linux/pinctrl/pinctrl.h 

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
	bool link_consumers;
};

其实整个初始化过程的核心思想就是low level的driver定义一个pinctrl_desc ,设定pin相关的定义和callback函数,注册到pin control subsystem中。我们用引脚描述符(pin descriptor)来描述一个pin。在pin control subsystem中,struct pinctrl_pin_desc用来描述一个可以控制的引脚,称之引脚描述符,代码如下:

 include/linux/pinctrl/pinctrl.h

struct pinctrl_pin_desc {
    unsigned number;-------ID,在本pin controller中唯一标识该引脚
    const char *name;-------user friedly name
    void *drv_data;
};

struct pinctrl_dev在pin control subsystem的core driver中抽象一个pin control device。其实可以通过多个层面来定义一个device。在这个场景下,pin control subsystem的core driver关注的是一类pin controller的硬件设备,具体其底层是什么硬件连接方式,使用什么硬件协议它不关心,它关心的是pin controller这类设备所有的通用特性和功能。

struct rockchip_pinctrl数据结构就是rk3568的pin controller driver要驱动rk3568的HW pin controller的私有数据结构。这个数据结构中最重要的就是 rockchip pin controller描述符了。关于pin controller有两个描述符,一个是struct pinctrl_desc,是具体硬件无关的pin controller的描述符。struct rockchip_pin_ctrl描述的具体samsung pin controller硬件相关的信息,比如说:pin bank的信息,不是所有的pin controller都是分bank的,因此pin bank的信息只能封装在low level的 rockchip pin controller driver中。这个数据结构定义如下:

struct rockchip_pin_ctrl {
	struct rockchip_pin_bank	*pin_banks;
	u32				nr_banks;
	u32				nr_pins;
	char				*label;
	enum rockchip_pinctrl_type	type;
	int				grf_mux_offset;
	int				pmu_mux_offset;
	int				grf_drv_offset;
	int				pmu_drv_offset;
	struct rockchip_mux_recalced_data *iomux_recalced;
	u32				niomux_recalced;
	struct rockchip_mux_route_data *iomux_routes;
	u32				niomux_routes;

	int	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
				    int pin_num, struct regmap **regmap,
				    int *reg, u8 *bit);
	int	(*drv_calc_reg)(struct rockchip_pin_bank *bank,
				    int pin_num, struct regmap **regmap,
				    int *reg, u8 *bit);
	int	(*schmitt_calc_reg)(struct rockchip_pin_bank *bank,
				    int pin_num, struct regmap **regmap,
				    int *reg, u8 *bit);
};

关于上面的base可以多说两句。实际上,系统支持多个pin controller设备,这时候,系统要管理多个pin controller控制下的多个pin。每个pin有自己的pin ID,是唯一的,假设系统中有两个pin controller,一个是A,控制32个,另外一个是B,控制64个pin,可以统一编号,对A,pin ID从0~31,对于B,pin ID是从32~95。对于B,其pin base就是32。

rockchip_pinctrl_probe-> rockchip_pinctrl_get_soc_data函数中会根据device tree的信息和静态定义的table来初始化该描述符,具体的代码如下:

 drivers/pinctrl/pinctrl-rockchip.c

static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
						struct rockchip_pinctrl *d,
						struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *node = dev->of_node;---获取device tree中的device node指针
	const struct of_device_id *match;
	struct rockchip_pin_ctrl *ctrl;
	struct rockchip_pin_bank *bank;
	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;

	match = of_match_node(rockchip_pinctrl_dt_match, node);
	ctrl = (struct rockchip_pin_ctrl *)match->data;--------A

	grf_offs = ctrl->grf_mux_offset;
	pmu_offs = ctrl->pmu_mux_offset;
	drv_pmu_offs = ctrl->pmu_drv_offset;
	drv_grf_offs = ctrl->grf_drv_offset;
	bank = ctrl->pin_banks;
	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {------------B 
		int bank_pins = 0;

		raw_spin_lock_init(&bank->slock);
		bank->drvdata = d;
		bank->pin_base = ctrl->nr_pins; ---ctrl->nr_pins初始的时候等于0,最后完成bank初始化后, 该值等于total的pin number。
		ctrl->nr_pins += bank->nr_pins;

		/* calculate iomux and drv offsets */
		for (j = 0; j < 4; j++) {
			struct rockchip_iomux *iom = &bank->iomux[j];
			struct rockchip_drv *drv = &bank->drv[j];
			int inc;

			if (bank_pins >= bank->nr_pins)
				break;

			/* preset iomux offset value, set new start value */
			if (iom->offset >= 0) {
				if ((iom->type & IOMUX_SOURCE_PMU) ||
				    (iom->type & IOMUX_L_SOURCE_PMU))
					pmu_offs = iom->offset;
				else
					grf_offs = iom->offset;
			} else { /* set current iomux offset */
				iom->offset = ((iom->type & IOMUX_SOURCE_PMU) ||
					       (iom->type & IOMUX_L_SOURCE_PMU)) ?
							pmu_offs : grf_offs;
			}

			/* preset drv offset value, set new start value */
			if (drv->offset >= 0) {
				if (iom->type & IOMUX_SOURCE_PMU)
					drv_pmu_offs = drv->offset;
				else
					drv_grf_offs = drv->offset;
			} else { /* set current drv offset */
				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
						drv_pmu_offs : drv_grf_offs;
			}

			dev_dbg(dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
				i, j, iom->offset, drv->offset);

			/*
			 * Increase offset according to iomux width.
			 * 4bit iomux'es are spread over two registers.
			 */
			inc = (iom->type & (IOMUX_WIDTH_4BIT |
					    IOMUX_WIDTH_3BIT |
					    IOMUX_WIDTH_2BIT)) ? 8 : 4;
			if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU))
				pmu_offs += inc;
			else
				grf_offs += inc;

			/*
			 * Increase offset according to drv width.
			 * 3bit drive-strenth'es are spread over two registers.
			 */
			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
				inc = 8;
			else
				inc = 4;

			if (iom->type & IOMUX_SOURCE_PMU)
				drv_pmu_offs += inc;
			else
				drv_grf_offs += inc;

			bank_pins += 8;
		}

		/* calculate the per-bank recalced_mask */
		for (j = 0; j < ctrl->niomux_recalced; j++) {
			int pin = 0;

			if (ctrl->iomux_recalced[j].num == bank->bank_num) {
				pin = ctrl->iomux_recalced[j].pin;
				bank->recalced_mask |= BIT(pin);
			}
		}

		/* calculate the per-bank route_mask */
		for (j = 0; j < ctrl->niomux_routes; j++) {
			int pin = 0;

			if (ctrl->iomux_routes[j].bank_num == bank->bank_num) {
				pin = ctrl->iomux_routes[j].pin;
				bank->route_mask |= BIT(pin);
			}
		}
	}

	return ctrl;
}

rockchip_pinctrl_get_soc_data这个函数名字基本反应了其功能,rk3568是rockchip的一个具体的SOC型号,调用该函数可以返回一个表示rk3568 SOC的rockchip pin controller的描述符。

A:这段代码主要是获取具体的rk3568的HW pin controller的信息,该数据结构在上文中出现过(具体参考pin controller的device tree match table:samsung_pinctrl_dt_match),就是rk3568_pin_ctrl这个变量。这个变量定义了rk3568的pin controller的信息如下:

drivers/pinctrl/pinctrl-rockchip.c

static struct rockchip_pin_ctrl rk3568_pin_ctrl = {
	.pin_banks		= rk3568_pin_banks,------静态定义的rk3568的pin bank的信息
	.nr_banks		= ARRAY_SIZE(rk3568_pin_banks),
	.label			= "RK3568-GPIO",
	.type			= RK3568,
	.grf_mux_offset		= 0x0,
	.pmu_mux_offset		= 0x0,
	.grf_drv_offset		= 0x0200,
	.pmu_drv_offset		= 0x0070,
	.iomux_routes		= rk3568_mux_route_data,
	.niomux_routes		= ARRAY_SIZE(rk3568_mux_route_data),
	.pull_calc_reg		= rk3568_calc_pull_reg_and_bit,
	.drv_calc_reg		= rk3568_calc_drv_reg_and_bit,
	.schmitt_calc_reg	= rk3568_calc_schmitt_reg_and_bit,
};

这个变量中包含了rk3568的pin bank的信息,包括:有多少个pin bank,每个bank中有多少个pin,pin bank的名字是什么,寄存器的offset是多少。这些信息用来初始化pin controller描述符数据结构。

B:初始化rk3568 rockchip pin controller中各个bank的描述符。

(3)rockchip_pinctrl_register函数的主要功能是将本pin controller注册到pin control subsystem。代码如下:

drivers/pinctrl/pinctrl-rockchip.c

static int rockchip_pinctrl_register(struct platform_device *pdev,
					struct rockchip_pinctrl *info)
{
	struct pinctrl_desc *ctrldesc = &info->pctl;
	struct pinctrl_pin_desc *pindesc, *pdesc;
	struct rockchip_pin_bank *pin_bank;
	struct device *dev = &pdev->dev;
	char **pin_names;
	int pin, bank, ret;
	int k;

	ctrldesc->name = "rockchip-pinctrl";--------A
	ctrldesc->owner = THIS_MODULE;
	ctrldesc->pctlops = &rockchip_pctrl_ops;
	ctrldesc->pmxops = &rockchip_pmx_ops;
	ctrldesc->confops = &rockchip_pinconf_ops;

	pindesc = devm_kcalloc(dev, info->ctrl->nr_pins, sizeof(*pindesc), GFP_KERNEL);-------B
	if (!pindesc)
		return -ENOMEM;

	ctrldesc->pins = pindesc;
	ctrldesc->npins = info->ctrl->nr_pins;

	pdesc = pindesc;
	for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {---C
		pin_bank = &info->ctrl->pin_banks[bank];

		pin_names = devm_kasprintf_strarray(dev, pin_bank->name, pin_bank->nr_pins);
		if (IS_ERR(pin_names))
			return PTR_ERR(pin_names);

		for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
			pdesc->number = k;
			pdesc->name = pin_names[pin];
			pdesc++;
		}

		INIT_LIST_HEAD(&pin_bank->deferred_pins);
		mutex_init(&pin_bank->deferred_lock);
	}

	ret = rockchip_pinctrl_parse_dt(pdev, info);------D
	if (ret)
		return ret;

	info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info);---E
	if (IS_ERR(info->pctl_dev))
		return dev_err_probe(dev, PTR_ERR(info->pctl_dev), "could not register pinctrl driver\n");

	return 0;
}

A:初始化硬件无关的pin controller描述符(struct rockchip_pinctrl_drv_data中的pctl成员)。该数据结构中还包含了所有pin的描述符的信息,这些pin descriptor所需要的内存在步骤B中分配

B:初始化过程中涉及不少内存分配,这些内存主要用于描述每一个pin(术语叫做pin descriptor)以及pin name。

C:初始化每一个pin 描述符的名字和ID。对于rockchip的pin描述符,其名字使用pin-bank name + pin ID的形式。 ID的分配是从该pin controller的pin base开始分配ID的,逐个加一。

D:初始化pin group和function。

E:调用pinctrl_register注册到pin control subsystem 。这是pin control subsystem的核心函数。

 

3.4、pin control subsystem如何获取pin group的信息

具体的代码如下:

 drivers/pinctrl/pinctrl-rockchip.c

static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
					      struct rockchip_pinctrl *info)
{
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	struct device_node *child;
	int ret;
	int i;

	rockchip_pinctrl_child_count(info, np);------(1)

	dev_dbg(dev, "nfunctions = %d\n", info->nfunctions);
	dev_dbg(dev, "ngroups = %d\n", info->ngroups);

	info->functions = devm_kcalloc(dev, info->nfunctions, sizeof(*info->functions), GFP_KERNEL); ----(2)
	if (!info->functions)
		return -ENOMEM;

	info->groups = devm_kcalloc(dev, info->ngroups, sizeof(*info->groups), GFP_KERNEL);
	if (!info->groups)
		return -ENOMEM;

	i = 0;

	for_each_child_of_node(np, child) {----遍历pin control的所有的child node
		if (of_match_node(rockchip_bank_match, child))
			continue;

		ret = rockchip_pinctrl_parse_functions(child, info, i++);
		if (ret) {
			dev_err(dev, "failed to parse function\n");
			of_node_put(child);
			return ret;
		}
	}

	return 0;
}


static int rockchip_pinctrl_parse_functions(struct device_node *np,
						struct rockchip_pinctrl *info,
						u32 index)
{
	struct device *dev = info->dev;
	struct device_node *child;
	struct rockchip_pmx_func *func;
	struct rockchip_pin_group *grp;
	int ret;
	static u32 grp_index;
	u32 i = 0;

	dev_dbg(dev, "parse function(%d): %pOFn\n", index, np);

	func = &info->functions[index];

	/* Initialise function */
	func->name = np->name;
	func->ngroups = of_get_child_count(np);
	if (func->ngroups <= 0)
		return 0;

	func->groups = devm_kcalloc(dev, func->ngroups, sizeof(*func->groups), GFP_KERNEL);
	if (!func->groups)
		return -ENOMEM;

	for_each_child_of_node(np, child) {
		func->groups[i] = child->name;
		grp = &info->groups[grp_index++];
		ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
		if (ret) {
			of_node_put(child);
			return ret;
		}
	}

	return 0;
}

(1)pin controller的device node有若干个child node,每个child node都描述了一个pin configuration。of_get_child_count函数可以获取pin configuration的数目。

(2)根据pin configuration的数目分配内存。在这里共计分配了两片内存,一片保存了所有pin group的信息,一片保存了pin mux function的信息。实际上,分配pin configuration的数目的内存有些浪费,因为不是每一个pin control的child node都是和pin group相关(例如pin bank node就是和pin group无关)。对于function,就更浪费了,因为有可能多个pin group对应一个function。

 

四、pin controller driver的操作函数

4.1、操作函数概述

pin controller描述符中包括了三类操作函数:pctlops是一些全局的控制函数,pmxops是复用引脚相关的操作函数,confops操作函数是用来配置引脚的特性(例如:pull-up/down)。这些callback函数都是和具体的底层pin controller的操作相关。

4.2、struct pinctrl_ops中各个callback函数

 drivers/pinctrl/pinctrl-rockchip.c

 

static const struct pinctrl_ops rockchip_pctrl_ops = {
	.get_groups_count	= rockchip_get_groups_count,
	.get_group_name		= rockchip_get_group_name,
	.get_group_pins		= rockchip_get_group_pins,
	.dt_node_to_map		= rockchip_dt_node_to_map,
	.dt_free_map		= rockchip_dt_free_map,
};

具体的解释如下:

(1)rockchip_get_groups_count

该函数的代码如下:

static int rockchip_get_groups_count(struct pinctrl_dev *pctldev)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	return info->ngroups;
}

该函数主要是用来获取指定pin control device的pin group的数目。逻辑很简单,通过pin control的class device的driver_data成员可以获得rockchip pin control driver的私有数据,可以nr_groups成员返回group的数目。

(2)rockchip_get_group_name

该函数的代码如下:

static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev,
							unsigned selector)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	return info->groups[selector].name;
}

该函数主要用来获取指定group selector的pin group信息。

(3)rockchip_get_group_pins

该函数的代码如下:

static int rockchip_get_group_pins(struct pinctrl_dev *pctldev,
				      unsigned selector, const unsigned **pins,
				      unsigned *npins)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	if (selector >= info->ngroups)
		return -EINVAL;

	*pins = info->groups[selector].pins;
	*npins = info->groups[selector].npins;

	return 0;
}

该函数的主要功能是给定一个group selector(index),获取该pin group中pin的信息(该pin group包括多少个pin,每个pin的ID是什么) 。

(4)rockchip_dt_node_to_map

该函数的代码如下:

static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev,
				 struct device_node *np,
				 struct pinctrl_map **map, unsigned *num_maps)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	const struct rockchip_pin_group *grp;
	struct device *dev = info->dev;
	struct pinctrl_map *new_map;
	struct device_node *parent;
	int map_num = 1;
	int i;

	/*
	 * first find the group of this node and check if we need to create
	 * config maps for pins
	 */
	grp = pinctrl_name_to_group(info, np->name);
	if (!grp) {
		dev_err(dev, "unable to find group for node %pOFn\n", np);
		return -EINVAL;
	}

	map_num += grp->npins;

	new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL);
	if (!new_map)
		return -ENOMEM;

	*map = new_map;
	*num_maps = map_num;

	/* create mux map */
	parent = of_get_parent(np);
	if (!parent) {
		kfree(new_map);
		return -EINVAL;
	}
	new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
	new_map[0].data.mux.function = parent->name;
	new_map[0].data.mux.group = np->name;
	of_node_put(parent);

	/* create config map */
	new_map++;
	for (i = 0; i < grp->npins; i++) {
		new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
		new_map[i].data.configs.group_or_pin =
				pin_get_name(pctldev, grp->pins[i]);
		new_map[i].data.configs.configs = grp->data[i].configs;
		new_map[i].data.configs.num_configs = grp->data[i].nconfigs;
	}

	dev_dbg(dev, "maps: function %s group %s num %d\n",
		(*map)->data.mux.function, (*map)->data.mux.group, map_num);

	return 0;
}

(5)rockchip_dt_free_map

该函数的代码如下:

static void rockchip_dt_free_map(struct pinctrl_dev *pctldev,
				    struct pinctrl_map *map, unsigned num_maps)
{
	kfree(map);
}

4.3、复用引脚相关的操作函数struct pinmux_ops

 drivers/pinctrl/pinctrl-rockchip.c

static const struct pinmux_ops rockchip_pmx_ops = {
	.get_functions_count	= rockchip_pmx_get_funcs_count,
	.get_function_name	= rockchip_pmx_get_func_name,
	.get_function_groups	= rockchip_pmx_get_groups,
	.set_mux		= rockchip_pmx_set,
	.gpio_set_direction	= rockchip_pmx_gpio_set_direction,
};

具体解释如下:

(1)rockchip_pmx_get_funcs_count

该函数的代码如下:

static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	return info->nfunctions;
}

 该函数的主要功能是就是返回pin controller device支持的function的数目
(2)rockchip_pmx_get_func_name

该函数的代码如下:

static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev,
					  unsigned selector)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	return info->functions[selector].name;
}

该函数的主要功能是就是:给定一个function selector(index),获取指定function的name
 
(3)rockchip_pmx_get_groups

该函数的代码如下:

static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev,
				unsigned selector, const char * const **groups,
				unsigned * const num_groups)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);

	*groups = info->functions[selector].groups;
	*num_groups = info->functions[selector].ngroups;

	return 0;
}

 该函数的主要功能是就是:给定一个function selector(index),获取指定function的pin groups信息
(4)rockchip_pmx_set

该函数的代码如下:

static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
			    unsigned group)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	const unsigned int *pins = info->groups[group].pins;
	const struct rockchip_pin_config *data = info->groups[group].data;
	struct device *dev = info->dev;
	struct rockchip_pin_bank *bank;
	int cnt, ret = 0;

	dev_dbg(dev, "enable function %s group %s\n",
		info->functions[selector].name, info->groups[group].name);

	/*
	 * for each pin in the pin group selected, program the corresponding
	 * pin function number in the config register.
	 */
	for (cnt = 0; cnt < info->groups[group].npins; cnt++) {
		bank = pin_to_bank(info, pins[cnt]);
		ret = rockchip_set_mux(bank, pins[cnt] - bank->pin_base,
				       data[cnt].func);
		if (ret)
			break;
	}

	if (ret) {
		/* revert the already done pin settings */
		for (cnt--; cnt >= 0; cnt--)
			rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0);

		return ret;
	}

	return 0;
}

该函数主要用来enable一个指定function。具体指定function的时候要给出function selector和pin group的selector 。具体的操作涉及很多底层的寄存器操作。

(5)samsung_pinmux_gpio_set_direction

该函数的代码如下:

static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
					   struct pinctrl_gpio_range *range,
					   unsigned offset,
					   bool input)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	struct rockchip_pin_bank *bank;

	bank = pin_to_bank(info, offset);
	return rockchip_set_mux(bank, offset - bank->pin_base, RK_FUNC_GPIO);
}

该函数用来设定GPIO的方向。
 

4.4、配置引脚的特性的struct pinconf_ops数据结构

各个成员定义如下:

drivers/pinctrl/pinctrl-rockchip.c

static const struct pinconf_ops rockchip_pinconf_ops = {
	.pin_config_get			= rockchip_pinconf_get,
	.pin_config_set			= rockchip_pinconf_set,
	.is_generic			= true,
};

(1)rockchip_pinconf_get该函数代码如下:

/* get the pin config settings for a specified pin */
static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
							unsigned long *config)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
	struct gpio_chip *gpio = &bank->gpio_chip;
	enum pin_config_param param = pinconf_to_config_param(*config);
	u16 arg;
	int rc;

	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
		if (rockchip_get_pull(bank, pin - bank->pin_base) != param)
			return -EINVAL;

		arg = 0;
		break;
	case PIN_CONFIG_BIAS_PULL_UP:
	case PIN_CONFIG_BIAS_PULL_DOWN:
	case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
	case PIN_CONFIG_BIAS_BUS_HOLD:
		if (!rockchip_pinconf_pull_valid(info->ctrl, param))
			return -ENOTSUPP;

		if (rockchip_get_pull(bank, pin - bank->pin_base) != param)
			return -EINVAL;

		arg = 1;
		break;
	case PIN_CONFIG_OUTPUT:
		rc = rockchip_get_mux(bank, pin - bank->pin_base);
		if (rc != RK_FUNC_GPIO)
			return -EINVAL;

		if (!gpio || !gpio->get) {
			arg = 0;
			break;
		}

		rc = gpio->get(gpio, pin - bank->pin_base);
		if (rc < 0)
			return rc;

		arg = rc ? 1 : 0;
		break;
	case PIN_CONFIG_DRIVE_STRENGTH:
		/* rk3288 is the first with per-pin drive-strength */
		if (!info->ctrl->drv_calc_reg)
			return -ENOTSUPP;

		rc = rockchip_get_drive_perpin(bank, pin - bank->pin_base);
		if (rc < 0)
			return rc;

		arg = rc;
		break;
	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
		if (!info->ctrl->schmitt_calc_reg)
			return -ENOTSUPP;

		rc = rockchip_get_schmitt(bank, pin - bank->pin_base);
		if (rc < 0)
			return rc;

		arg = rc;
		break;
	default:
		return -ENOTSUPP;
		break;
	}

	*config = pinconf_to_config_packed(param, arg);

	return 0;
}

(2)rockchip_pinconf_set该函数代码如下:

static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
				unsigned long *configs, unsigned num_configs)
{
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
	struct gpio_chip *gpio = &bank->gpio_chip;
	enum pin_config_param param;
	u32 arg;
	int i;
	int rc;

	for (i = 0; i < num_configs; i++) {
		param = pinconf_to_config_param(configs[i]);
		arg = pinconf_to_config_argument(configs[i]);

		if (param == PIN_CONFIG_OUTPUT || param == PIN_CONFIG_INPUT_ENABLE) {
			/*
			 * Check for gpio driver not being probed yet.
			 * The lock makes sure that either gpio-probe has completed
			 * or the gpio driver hasn't probed yet.
			 */
			mutex_lock(&bank->deferred_lock);
			if (!gpio || !gpio->direction_output) {
				rc = rockchip_pinconf_defer_pin(bank, pin - bank->pin_base, param,
								arg);
				mutex_unlock(&bank->deferred_lock);
				if (rc)
					return rc;

				break;
			}
			mutex_unlock(&bank->deferred_lock);
		}

		switch (param) {
		case PIN_CONFIG_BIAS_DISABLE:
			rc =  rockchip_set_pull(bank, pin - bank->pin_base,
				param);
			if (rc)
				return rc;
			break;
		case PIN_CONFIG_BIAS_PULL_UP:
		case PIN_CONFIG_BIAS_PULL_DOWN:
		case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
		case PIN_CONFIG_BIAS_BUS_HOLD:
			if (!rockchip_pinconf_pull_valid(info->ctrl, param))
				return -ENOTSUPP;

			if (!arg)
				return -EINVAL;

			rc = rockchip_set_pull(bank, pin - bank->pin_base,
				param);
			if (rc)
				return rc;
			break;
		case PIN_CONFIG_OUTPUT:
			rc = rockchip_set_mux(bank, pin - bank->pin_base,
					      RK_FUNC_GPIO);
			if (rc != RK_FUNC_GPIO)
				return -EINVAL;

			rc = gpio->direction_output(gpio, pin - bank->pin_base,
						    arg);
			if (rc)
				return rc;
			break;
		case PIN_CONFIG_INPUT_ENABLE:
			rc = rockchip_set_mux(bank, pin - bank->pin_base,
					      RK_FUNC_GPIO);
			if (rc != RK_FUNC_GPIO)
				return -EINVAL;

			rc = gpio->direction_input(gpio, pin - bank->pin_base);
			if (rc)
				return rc;
			break;
		case PIN_CONFIG_DRIVE_STRENGTH:
			/* rk3288 is the first with per-pin drive-strength */
			if (!info->ctrl->drv_calc_reg)
				return -ENOTSUPP;

			rc = rockchip_set_drive_perpin(bank,
						pin - bank->pin_base, arg);
			if (rc < 0)
				return rc;
			break;
		case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
			if (!info->ctrl->schmitt_calc_reg)
				return -ENOTSUPP;

			rc = rockchip_set_schmitt(bank,
						  pin - bank->pin_base, arg);
			if (rc < 0)
				return rc;
			break;
		default:
			return -ENOTSUPP;
			break;
		}
	} /* for each config */

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值