一、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));