4.以RK808为例认识PMIC芯片的驱动逻辑

一、RK808概述

RK808 是一款高性能 PMIC,RK808 集成 4 个大电流 DCDC、8 个 LDO、2个开关SWITCH、1 个
RTC、可调上电时序等功能。
系统中各路电源总体分为两种:DCDC 和 LDO。两种电源的总体特性如下(详细资料请自行搜索):

  • DCDC:输入输出压差大时,效率高,但是存在纹波比较大的问题,成本高,所以大压差,大电流负载时使用。

    一般有两种工作模式。PWM 模式:纹波瞬态响应好,效率低;PFM 模式:效率高,但是负载能力差。

  • LDO:输入输出压差大时,效率低,成本低,为了提高 LDO 的转换效率,系统上会进行相关优化
    如:LDO 输出电压为 1.1V,为了提高效率,其输入电压可以从 VCCIO_3.3V 的 DCDC 给出。所以电路上如果允许尽量将 LDO 接到 DCDC 输出回路,但是要注意上电时序。

二、RK808功能

从使用者的角度看,RK808 的功能概况起来可以分为 4 个部分:

  • regulator 功能:控制各路 DCDC、LDO 电源状态;
  • rtc 功能:提供时钟计时、定时等功能;
  • clk 功能:有两个32.768KHZ时钟输出,一个不可以控常开,一个是软件可控。

三、原理图

在这里插入图片描述

四、概念扩展

调节器(regulator)是一种为其他设备供电的电子设备。由调节器供电的设备被称为消费者。它们消耗调节器提供的电力。大多数调节器可以启用和禁用他们的输出,一些也可以控制他们的输出电压或电流。

提供物理调节的芯片被称为电源管理集成电路(PMIC)

在这里插入图片描述

以PMIC为例,支持多路电压/电流控制功能,支持rtc功能,支持输出clk输出功能等等,功能特别多的外设在Linux里被称为MFD(Multi-function Device:多功能设备)。任何一个功能单独拿出来,感觉都可以单独作为一个外设,然后有相应的单独的驱动。MFD子系统就是为这种复杂设备准备的,MFD子系统的核心思想就是从一个复杂设备中抽象出不同功能,抽象成单独的设备。

五、设备树

&i2c0 {
	status = "okay";
	i2c-scl-rising-time-ns = <168>;
	i2c-scl-falling-time-ns = <4>;
	clock-frequency = <400000>;

	vdd_cpu_b: syr827@40 {
		compatible = "silergy,syr827";
		reg = <0x40>;
		vin-supply = <&vcc5v0_sys>;
		regulator-compatible = "fan53555-reg";
		regulator-name = "vdd_cpu_b";
		pinctrl-0 = <&vsel1_gpio>;
		vsel-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
		regulator-min-microvolt = <712500>;
		regulator-max-microvolt = <1500000>;
		regulator-ramp-delay = <1000>;
		fcs,suspend-voltage-selector = <1>;
		regulator-always-on;
		regulator-boot-on;
		regulator-initial-state = <3>;
			regulator-state-mem {
			regulator-off-in-suspend;
		};
	};

	vdd_gpu: syr828@41 {
		compatible = "silergy,syr828";
		reg = <0x41>;
		vin-supply = <&vcc5v0_sys>;
		regulator-compatible = "fan53555-reg";
		regulator-name = "vdd_gpu";
		pinctrl-0 = <&vsel2_gpio>;
		vsel-gpios = <&gpio1 14 0>;
		regulator-min-microvolt = <712500>;
		regulator-max-microvolt = <1500000>;
		regulator-ramp-delay = <1000>;
		fcs,suspend-voltage-selector = <1>;
		regulator-always-on;
		regulator-boot-on;
		regulator-initial-state = <3>;
			regulator-state-mem {
			regulator-off-in-suspend;
		};
	};

	rk808: pmic@1b {
		compatible = "rockchip,rk808";
		reg = <0x1b>;
		interrupt-parent = <&gpio1>;
		interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
		pmic,stby-gpio = <&gpio1 24 GPIO_ACTIVE_LOW>;
		pmic,hold-gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&pmic_int_l &pmic_dvs2 &pmic_stby &pmic_hold>;
		rockchip,system-power-controller;
		wakeup-source;
		#clock-cells = <1>;
		clock-output-names = "xin32k", "rk808-clkout2";

		vcc1-supply = <&vcc3v3_sys>;
		vcc2-supply = <&vcc3v3_sys>;
		vcc3-supply = <&vcc3v3_sys>;
		vcc4-supply = <&vcc3v3_sys>;
		vcc6-supply = <&vcc3v3_sys>;
		vcc7-supply = <&vcc3v3_sys>;
		vcc8-supply = <&vcc3v3_sys>;
		vcc9-supply = <&vcc3v3_sys>;
		vcc10-supply = <&vcc3v3_sys>;
		vcc11-supply = <&vcc3v3_sys>;
		vcc12-supply = <&vcc3v3_sys>;
		vddio-supply = <&vcc1v8_pmu>;

		regulators {
			vdd_center: DCDC_REG1 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <750000>;
				regulator-max-microvolt = <1350000>;
				regulator-ramp-delay = <6001>;
				regulator-name = "vdd_center";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vdd_cpu_l: DCDC_REG2 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <750000>;
				regulator-max-microvolt = <1350000>;
				regulator-ramp-delay = <6001>;
				regulator-name = "vdd_cpu_l";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc_ddr: DCDC_REG3 {
				regulator-always-on;
				regulator-boot-on;
				regulator-name = "vcc_ddr";
				regulator-state-mem {
					regulator-on-in-suspend;
				};
			};

			vcc_1v8: DCDC_REG4 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1800000>;
				regulator-max-microvolt = <1800000>;
				regulator-name = "vcc_1v8";
				regulator-state-mem {
					regulator-on-in-suspend;
					regulator-suspend-microvolt = <1800000>;
				};
			};

			vcc1v8_dvp: LDO_REG1 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1800000>;
				regulator-max-microvolt = <1800000>;
				regulator-name = "vcc1v8_dvp";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc2v8_dvp: LDO_REG2 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <2800000>;
				regulator-max-microvolt = <2800000>;
				regulator-name = "vcc2v8_dvp";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc1v8_pmu: LDO_REG3 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1800000>;
				regulator-max-microvolt = <1800000>;
				regulator-name = "vcc1v8_pmu";
				regulator-state-mem {
					regulator-on-in-suspend;
					regulator-suspend-microvolt = <1800000>;
				};
			};

			vccio_sd: LDO_REG4 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1800000>;
				regulator-max-microvolt = <3300000>;
				regulator-name = "vccio_sd";
				regulator-state-mem {
					regulator-on-in-suspend;
					regulator-suspend-microvolt = <3300000>;
				};
			};

			vcca3v0_codec: LDO_REG5 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <3000000>;
				regulator-max-microvolt = <3000000>;
				regulator-name = "vcca3v0_codec";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc_1v5: LDO_REG6 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1500000>;
				regulator-max-microvolt = <1500000>;
				regulator-name = "vcc_1v5";
				regulator-state-mem {
					regulator-on-in-suspend;
					regulator-suspend-microvolt = <1500000>;
				};
			};

			vcca1v8_codec: LDO_REG7 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <1800000>;
				regulator-max-microvolt = <1800000>;
				regulator-name = "vcca1v8_codec";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc_3v0: LDO_REG8 {
				regulator-always-on;
				regulator-boot-on;
				regulator-min-microvolt = <3000000>;
				regulator-max-microvolt = <3000000>;
				regulator-name = "vcc_3v0";
				regulator-state-mem {
					regulator-on-in-suspend;
					regulator-suspend-microvolt = <3000000>;
				};
			};

			vcc3v3_s3: SWITCH_REG1 {
				regulator-always-on;
				regulator-boot-on;
				regulator-name = "vcc3v3_s3";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};

			vcc3v3_s0: SWITCH_REG2 {
				regulator-always-on;
				regulator-boot-on;
				regulator-name = "vcc3v3_s0";
				regulator-state-mem {
					regulator-off-in-suspend;
				};
			};
		};
	};
};

六、内核驱动路径与配置

RK808 驱动文件:

drivers/mfd/rk808.c
drivers/rtc/rtc-rk808.c
drivers/regulator/rk808-regulator.c		#这个是重点
drivers/clk/clk-rk808.c

menuconfig 里对应的宏配置:

CONFIG_MFD_RK808
CONFIG_RTC_RK808
CONFIG_REGULATOR_RK808
CONFIG_COMMON_CLK_RK808

不同于一般的驱动,RK808源码都有四个,了解过前面的概念可以看出大致的逻辑为mfd/rk808.c将芯片rk808的功能抽象为三个设备,他们对应的驱动源码为rtc/rtc-rk808.c、regulator/rk808-regulator.c、clk/clk-rk808.c

七、MFD子系统框架

参考传送门:

[以PMIC为例简析Linux MFD/Regmap/Regulator的使用]
https://www.cnblogs.com/arnoldlu/p/17761663.html

MFD子系统是Linux下一种用于管理和控制多功能设备的软件框架。他提供一种统一接口,使得多个设备可以通过一个驱动程序进行管理和控制。MFD是多个具有类似功能设备的集合,每一个功能称为一个cell。MFD为每个cell创建一个platform设备。

整个MFD包括3部分:

  • MFD Core提供MFD设备的注册去注册,以及对Cell的操作。
  • MFD设备驱动。
  • MFD Cell描述符,一个描述符对应一个MFD设备单元。MFD为每个Cell创建一个Platform设备。

在这里插入图片描述

其中userspace下的效果如图:

在这里插入图片描述

源码drivers/mfd/rk808.c所完成的功能就是上面框图中MFD Driver的部分

MFD Core留给MFD设备的设备注册和注销API如下:

extern int mfd_add_devices(struct device *parent, int id,
               const struct mfd_cell *cells, int n_devs,
               struct resource *mem_base,
               int irq_base, struct irq_domain *irq_domain);

extern void mfd_remove_devices(struct device *parent);

其中的参数的含义如下:

mfd_add_devices()注册一个MFD主设备
mfd_remove_devices()是将一个MFD注销的接口。
parent:MFD主设备,后面Cell创建的设备挂载在下面。
id:表示后续创建Cell Platform设备时ID如何创建,包括PLATFORM_DEVID_NONE和PLATFORM_DEVID_AUTO两种方式。
cells:struct mfd_cell结构体数组,每一个成员表示一个Cell。
n_devs:Cell数量。
mem_base:内存地址基准。
irq_base:irq号基准。
irq_domain:当前MFD设备的struct irq_domain。

第二个参数是一个struct mfd_cell结构体数组,这个结构体数组需要我们自己定义,该结构体如下:

struct mfd_cell {
	const char		*name;	// g, 设备名字,会在注册platform设备时使用该名字
	int			id;			// g, 同名设备需要使用id进行区分

	/* refcounting for multiple drivers to use a single cell */
	atomic_t		*usage_count;
	int			(*enable)(struct platform_device *dev);
	int			(*disable)(struct platform_device *dev);

	int			(*suspend)(struct platform_device *dev);
	int			(*resume)(struct platform_device *dev);

	/* platform data passed to the sub devices drivers */
	void			*platform_data;		// g, 设备私有数据
	size_t			pdata_size;			// g, platform_data的大小

	/* device properties passed to the sub devices drivers */
	struct property_entry *properties;	// g, 如果需要为新注册的设备添加一个properties的话,可以使用该域

	/*
	 * Device Tree compatible string
	 * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
	 */
	const char		*of_compatible;		// g, compatible属性,会在添加cell设备时通过该域寻找设备树生成的设备节点

	/* Matches ACPI */
	const struct mfd_cell_acpi_match	*acpi_match;	// g, acpi不管

	/*
	 * These resources can be specified relative to the parent device.
	 * For accessing hardware you should use resources from the platform dev
	 */
	int			num_resources;				// g, 资源数
	const struct resource	*resources;		// g, 设备资源,如果想为platform_device设置resource的话可以使用该域。设备树中的resource一般都是寄存器地址信息。

	/* don't check for resource conflicts */
	bool			ignore_resource_conflicts;

	/*
	 * Disable runtime PM callbacks for this subdevice - see
	 * pm_runtime_no_callbacks().
	 */
	bool			pm_runtime_no_callbacks;

	/* A list of regulator supplies that should be mapped to the MFD
	 * device rather than the child device when requested
	 */
	const char * const	*parent_supplies;
	int			num_parent_supplies;
};

mfd/rk808.c中的调用实例

const struct mfd_cell *cell;

cell = rk808s;

//可以看出分成了三个cell,对应着三个.c源文件里的.name
static const struct mfd_cell rk808s[] = {
	{ .name = "rk808-clkout", },
	{ .name = "rk808-regulator", },
	{
		.name = "rk808-rtc",
		.num_resources = ARRAY_SIZE(rtc_resources),
		.resources = &rtc_resources[0],
	},
};

ret = mfd_add_devices(&client->dev, -1,
			      cell, cell_num,
			      NULL, 0, regmap_irq_get_domain(rk808->irq_data));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值