RK806S PMIC电源管理芯片调试

RK806S-5是RK3576配备的PMIC电源管理芯片,SoC的上电启动逻辑由其控制。

上电启动过程中,RK806S-5一般在uboot阶段接管电源控制,所以其对应的驱动在u-boot/drivers/power/pmic/rk8xx.c

关于下电启动,当进入Linux系统以后,则由一个脚本来控制关机逻辑/usr/bin/power-key.sh

在进入系统以后,默认的点按PWRON按钮是进入系统休眠,我们的需求为关机,所以在short_press()里直接加入poweroff关机指令即可

#!/bin/sh

EVENT=${1:-short-press}

LONG_PRESS_TIMEOUT=3 # s
DEBOUNCE=2 # s
PID_FILE=/var/run/power_key.pid
LOCK_FILE=/var/run/.power_key.lock

log()
{
        logger -t $(basename $0) "[$$]: $@"
}

# HACK: Monitoring power state changes and update the modified time
while ! pgrep -x inotifywait >/dev/null; do
        inotifywait -e modify /sys/power/state >/dev/null 2>&1
        # Avoid race with freezing processes
        sleep .2
        touch /sys/power/state
done&

parse_wakeup_time()
{
        LAST_MODIFY="$(stat -c "%Y" /sys/power/state)"
        NOW="$(date "+%s")"
        WAKE_TIME="$(expr "$NOW" - "$LAST_MODIFY")"

        log "Last state changed: $(date -d "@$LAST_MODIFY" "+%D %T")..."
}

short_press()
{
        log "Power key short press..."
        poweroff

        if which systemctl >/dev/null; then
                SUSPEND_CMD="systemctl suspend"
        elif which pm-suspend >/dev/null; then
                SUSPEND_CMD="pm-suspend"
        else
                SUSPEND_CMD="echo -n mem > /sys/power/state"
        fi

        # Debounce
        if [ -f $LOCK_FILE ]; then
                log "Too close to the latest request..."
                return 0
        fi

        if parse_wakeup_time; then
                if [ "$WAKE_TIME" -le $DEBOUNCE ]; then
                        log "We might just resumed!"
                        return 0
                fi
        fi

        log "Prepare to suspend..."

        touch $LOCK_FILE
        sh -c "$SUSPEND_CMD"
        { sleep $DEBOUNCE && rm $LOCK_FILE; }&
}

long_press()
{
        log "Power key long press (${LONG_PRESS_TIMEOUT}s)..."

        log "Prepare to power off..."

        poweroff
}

log "Received power key event: $@..."

case "$EVENT" in
        press)
                # Lock it
                exec 3<$0
                flock -x 3

                start-stop-daemon -K -q -p $PID_FILE || true
                start-stop-daemon -S -q -b -m -p $PID_FILE -x /bin/sh -- \
                        -c "sleep $LONG_PRESS_TIMEOUT; $0 long-press"

                # Unlock
                flock -u 3
                ;;
        release)
                # Avoid race with press event
                sleep .5

                start-stop-daemon -K -q -p $PID_FILE && short_press
                ;;
        short-press)
                short_press
                ;;
        long-press)
                long_press
                ;;
esac

当RK806S-5使用i2c通讯时,对应使用rk8xx.c,当使用spi通讯时,对应同文件目录的rk8xx_spi.c

uboot同样符合device--driver的驱动框架

下面我们以rk8xx.c的驱动为例,测试实现一个开机长按PWRON按键启动

+ #include <boot_rkimg.h>

+ int mode;
 
 /* read Chip variant */
    if (device_is_compatible(dev, "rockchip,rk817") ||
        device_is_compatible(dev, "rockchip,rk809")) {
        id_msb = RK817_ID_MSB;
        id_lsb = RK817_ID_LSB;
    } else if (device_is_compatible(dev, "rockchip,rk806")) {
        id_msb = RK806_CHIP_NAME;
        id_lsb = RK806_CHIP_VER;
    } else {
        id_msb = ID_MSB;
        id_lsb = ID_LSB;
    }

    ret = rk8xx_read(dev, id_msb, &msb, 1);
    if (ret)
        return ret;
    ret = rk8xx_read(dev, id_lsb, &lsb, 1);
    if (ret)
        return ret;

    priv->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK;
    show_variant = priv->variant;
 
+    /* 10*100ms */
+    mode = rockchip_get_boot_mode();
+    printf("rk806s: mode: %d\n", mode);
+    printf("pmic 0x5d = %x\n", pmic_reg_read(dev, 0x5d)); //SYS_STS
+    printf("pmic 0x74 = %x\n", pmic_reg_read(dev, 0x74)); //ON_SOURCE
+    
+    if ((pmic_reg_read(dev, 0x5d) & 0x80) && ((pmic_reg_read(dev, 0x74) & 0x80) | (pmic_reg_read(dev, 0x74) & 0x40)) &&
+        mode == 11) {
+            i = 0;
+            while (i < 10) {
+                value = pmic_reg_read(dev, 0x5d);
+                if (value & 0x80) {
+                    printf("rk806s: power off\n");
+                    rk8xx_shutdown(dev);
+                }
+                mdelay(100);
+                i++;
+            }
+            printf("rk806s: power on\n");
+ }

需要了解几个开机启动相关的寄存器

SYS_STS(0x5d)

系统状态寄存器,反映 RK806S PMIC 的关键硬件状态,所有位为只读(RO)

0x5d的BIT(7)反映的是PWRON按键的状态

ON_SOURCE(0x74)

系统状态寄存器,反映 RK806S PMIC 的关键硬件状态,所有位为只读(RO)

ON_VDC为电源适配器12V插入检测,即当SoC从断电状态接入12V电源适配器时,0x74的值则为0x40

当SoC在12V电源已经接入,且处于关机状态时,使用PWRON按键开机,0x74的值则为0x80

查看PWRON按键开机说明,默认情况下只需要20ms或500ms的点按就能实现开机,然而为了排除误触的情况,我们通过驱动实现长按开机

解析此处的核心代码

当点按PWRON按键时,rk806上电开始启动bootloader和uboot,在执行到此处时检测到是由于按键启动的,所以0x5d的值是0x80,0x74的值是0x80,在一般情况下,mode都默认是none的,所以mode值为11,因此就进入了我们的if判断,若是由于按键启动的,则直接调用rk8xx_shutdown函数关掉,进入这个函数会看到实际是通过i2c给0x72寄存器的BIT(0)写1,使其关机。

所以此操作就实现了点按PWRON按键无法开机,避免了误触情况。

在判断里加上0x74寄存器为0x40的情况,则避免了12V电源插入自动启动

而长按开机时,0x5d实际读到的值是0x00,所以不会进入此处的判断

关于此处按键寄存器检测的值,从逻辑上来看更像是按下为0x00,不按为0x80,这里后续还要再想办法测试一下,即rk806芯片手册与实际描述是相反的。或者与PWRON按键接vcc或GND有关,因为rk806手册按下按键时电平状态是拉低的

总之实现的逻辑是这样的,具体的场景需要微调寄存器判断的值

if ((pmic_reg_read(dev, 0x5d) & 0x80) && ((pmic_reg_read(dev, 0x74) & 0x80) | (pmic_reg_read(dev, 0x74) & 0x40)) &&
        mode == 11) {
    i = 0;
    while (i < 10) {
        value = pmic_reg_read(dev, 0x5d);
        if (value & 0x80) {
            printf("rk806s: power off\n");
            rk8xx_shutdown(dev);
        }
        mdelay(100);
        i++;
    }
    printf("rk806s: power on\n");
}




static int rk8xx_shutdown(struct udevice *dev)
{
	struct rk8xx_priv *priv = dev_get_priv(dev);
	u8 val, dev_off, devctrl_reg;
	int ret = 0;

	switch (priv->variant) {
	case RK806_ID:
		devctrl_reg = RK806_SYS_CFG3;
		dev_off = RK806_DEV_OFF;
		break;
	case RK808_ID:
		devctrl_reg = REG_DEVCTRL;
		dev_off = BIT(3);
		break;
	case RK805_ID:
	case RK816_ID:
	case RK818_ID:
		devctrl_reg = REG_DEVCTRL;
		dev_off = BIT(0);
		break;
	case RK809_ID:
	case RK817_ID:
		devctrl_reg = RK817_REG_SYS_CFG3;
		dev_off = BIT(0);
		break;
	default:
		printf("Unknown PMIC: RK%x\n", priv->variant);
		return -EINVAL;
	}

	ret = rk8xx_read(dev, devctrl_reg, &val, 1);
	if (ret)
		return ret;

	val |= dev_off;
	ret = rk8xx_write(dev, devctrl_reg, &val, 1);
	if (ret)
		return ret;

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值