diff --git a/sys/dev/iicbus/pmic/rockchip/rk805.c b/sys/dev/iicbus/pmic/rockchip/rk805.c --- a/sys/dev/iicbus/pmic/rockchip/rk805.c +++ b/sys/dev/iicbus/pmic/rockchip/rk805.c @@ -176,6 +176,8 @@ sc->rtc_regs.ctrl_ampm_mask = RK805_RTC_AMPM_MODE; sc->rtc_regs.ctrl_gettime_mask = RK805_RTC_GET_TIME; sc->rtc_regs.ctrl_readsel_mask = RK805_RTC_READSEL; + sc->dev_ctrl.dev_ctrl_reg = RK805_DEV_CTRL; + sc->dev_ctrl.pwr_off_mask = RK805_DEV_CTRL_OFF; return (rk8xx_attach(sc)); } diff --git a/sys/dev/iicbus/pmic/rockchip/rk808.c b/sys/dev/iicbus/pmic/rockchip/rk808.c --- a/sys/dev/iicbus/pmic/rockchip/rk808.c +++ b/sys/dev/iicbus/pmic/rockchip/rk808.c @@ -251,6 +251,8 @@ sc->rtc_regs.ctrl_ampm_mask = RK808_RTC_AMPM_MODE; sc->rtc_regs.ctrl_gettime_mask = RK808_RTC_GET_TIME; sc->rtc_regs.ctrl_readsel_mask = RK808_RTC_READSEL; + sc->dev_ctrl.dev_ctrl_reg = RK808_DEV_CTRL; + sc->dev_ctrl.pwr_off_mask = RK808_DEV_CTRL_OFF; return (rk8xx_attach(sc)); } diff --git a/sys/dev/iicbus/pmic/rockchip/rk808reg.h b/sys/dev/iicbus/pmic/rockchip/rk808reg.h --- a/sys/dev/iicbus/pmic/rockchip/rk808reg.h +++ b/sys/dev/iicbus/pmic/rockchip/rk808reg.h @@ -107,6 +107,10 @@ #define RK808_LDO8_ON_VSEL 0x49 #define RK808_LDO8_SLEEP_VSEL 0x4A +#define RK808_DEV_CTRL 0x4B +#define RK808_DEV_CTRL_OFF (1 << 0) +#define RK808_DEV_CTRL_SLP (1 << 1) + enum rk808_regulator { RK808_BUCK1 = 0, RK808_BUCK2, diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx.h b/sys/dev/iicbus/pmic/rockchip/rk8xx.h --- a/sys/dev/iicbus/pmic/rockchip/rk8xx.h +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx.h @@ -85,6 +85,11 @@ uint8_t ctrl_readsel_mask; }; +struct rk8xx_dev_ctrl { + uint8_t dev_ctrl_reg; + uint8_t pwr_off_mask; +}; + struct rk8xx_softc { device_t dev; struct mtx mtx; @@ -98,6 +103,7 @@ int nregs; struct rk8xx_rtc_reg rtc_regs; + struct rk8xx_dev_ctrl dev_ctrl; }; int rk8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size); diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx.c b/sys/dev/iicbus/pmic/rockchip/rk8xx.c --- a/sys/dev/iicbus/pmic/rockchip/rk8xx.c +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx.c @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -76,7 +78,6 @@ dev = pdev; sc = device_get_softc(dev); - sc->dev = dev; /* No version register in RK808 */ if (bootverbose && sc->type == RK805) { @@ -101,6 +102,30 @@ config_intrhook_disestablish(&sc->intr_hook); } +static void +rk8xx_poweroff(void *arg, int howto) +{ + struct rk8xx_softc *sc = arg; + int error; + uint8_t val; + + if ((howto & RB_POWEROFF) == 0) + return; + + device_printf(sc->dev, "Powering off...\n"); + error = rk8xx_read(sc->dev, sc->dev_ctrl.dev_ctrl_reg, &val, 1); + if (error == 0) { + val |= sc->dev_ctrl.pwr_off_mask; + error = rk8xx_write(sc->dev, sc->dev_ctrl.dev_ctrl_reg, + &val, 1); + + /* Wait a bit for the command to take effect. */ + if (error == 0) + DELAY(100); + } + device_printf(sc->dev, "Power off failed\n"); +} + int rk8xx_attach(struct rk8xx_softc *sc) { @@ -117,6 +142,17 @@ rk8xx_attach_regulators(sc); + if (OF_hasprop(ofw_bus_get_node(sc->dev), + "rockchip,system-power-controller")) { + /* + * The priority is chosen to override PSCI and EFI shutdown + * methods as those two just hang without powering off on Rock64 + * at least. + */ + EVENTHANDLER_REGISTER(shutdown_final, rk8xx_poweroff, sc, + SHUTDOWN_PRI_LAST - 2); + } + return (0); }