Add support for rk809 as seen on the Rock Pi N10 with the rk3399pro. Add
authorkurt <kurt@openbsd.org>
Mon, 8 Mar 2021 12:53:35 +0000 (12:53 +0000)
committerkurt <kurt@openbsd.org>
Mon, 8 Mar 2021 12:53:35 +0000 (12:53 +0000)
support for multiple linear ranges for voltage regulators and use for all
rkpmic ICs.

ok kettenis@

sys/dev/fdt/rkpmic.c

index fd4c8ed..3589f13 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rkpmic.c,v 1.7 2020/11/12 10:47:07 patrick Exp $      */
+/*     $OpenBSD: rkpmic.c,v 1.8 2021/03/08 12:53:35 kurt Exp $ */
 /*
  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
  *
 
 extern todr_chip_handle_t todr_handle;
 
-#define RK808_SECONDS          0x00
-#define RK808_MINUTES          0x01
-#define RK808_HOURS            0x02
-#define RK808_DAYS             0x03
-#define RK808_MONTHS           0x04
-#define RK808_YEARS            0x05
-#define RK808_WEEKS            0x06
+#define RK80X_SECONDS          0x00
+#define RK80X_MINUTES          0x01
+#define RK80X_HOURS            0x02
+#define RK80X_DAYS             0x03
+#define RK80X_MONTHS           0x04
+#define RK80X_YEARS            0x05
+#define RK80X_WEEKS            0x06
+#define RK80X_NRTC_REGS        7
+
+#define RK805_RTC_CTRL         0x10
 #define RK808_RTC_CTRL         0x10
-#define  RK808_RTC_CTRL_STOP_RTC       0x01
+#define RK809_RTC_CTRL         0x0d
+#define  RK80X_RTC_CTRL_STOP_RTC       0x01
+
+#define RK805_RTC_STATUS       0x11
 #define RK808_RTC_STATUS       0x11
-#define  RK808_RTC_STATUS_POWER_UP     0x80
+#define RK809_RTC_STATUS       0x0e
+#define  RK80X_RTC_STATUS_POWER_UP     0x80
 
-#define RK808_NRTC_REGS        7
+struct rkpmic_vsel_range {
+       uint32_t base, delta;
+       uint8_t vsel_min, vsel_max;
+};
 
 struct rkpmic_regdata {
        const char *name;
        uint8_t reg, mask;
-       uint32_t base, delta;
+       struct rkpmic_vsel_range *vsel_range;
+};
+
+/* 
+ * Used by RK805 for BUCK1, BUCK2
+ *  0-59:      0.7125V-1.45V, step=12.5mV
+ *  60-62:     1.8V-2.2V, step=200mV
+ *  63:                2.3V
+ */
+struct rkpmic_vsel_range rk805_vsel_range1[] = {
+       { 712500, 12500, 0, 59 },
+       { 1800000, 200000, 60, 62 },
+       { 2300000, 0, 63, 63 },
+       {}
+};
+
+/*
+ * Used by RK805 for BUCK4 
+ *  0-27:      0.8V-3.5V, step=100mV
+ */
+struct rkpmic_vsel_range rk805_vsel_range2[] = {
+       { 800000, 100000, 0, 27 },
+       {}
+};
+
+/*
+ * Used by RK805 for LDO1-3 
+ *  0-26:      0.8V-3.4V, step=100mV
+ */
+struct rkpmic_vsel_range rk805_vsel_range3[] = {
+       { 800000, 100000, 0, 26 },
+       {}
 };
 
 struct rkpmic_regdata rk805_regdata[] = {
-       { "DCDC_REG1", 0x2f, 0x3f, 712500, 12500 },
-       { "DCDC_REG2", 0x33, 0x3f, 712500, 12500 },
-       { "DCDC_REG4", 0x38, 0x1f, 800000, 100000 },
-       { "LDO_REG1", 0x3b, 0x1f, 800000, 100000 },
-       { "LDO_REG2", 0x3d, 0x1f, 800000, 100000 },
-       { "LDO_REG3", 0x3f, 0x1f, 800000, 100000 },
+       { "DCDC_REG1", 0x2f, 0x3f, rk805_vsel_range1 },
+       { "DCDC_REG2", 0x33, 0x3f, rk805_vsel_range1 },
+       { "DCDC_REG4", 0x38, 0x1f, rk805_vsel_range2 },
+       { "LDO_REG1", 0x3b, 0x1f, rk805_vsel_range3 },
+       { "LDO_REG2", 0x3d, 0x1f, rk805_vsel_range3 },
+       { "LDO_REG3", 0x3f, 0x1f, rk805_vsel_range3 },
        { }
 };
 
+/*
+ * Used by RK808 for BUCK1 & BUCK2
+ *  0-63:      0.7125V-1.5V, step=12.5mV
+ */
+struct rkpmic_vsel_range rk808_vsel_range1[] = {
+       { 712500, 12500, 0, 63 },
+       {}
+};
+
+/*
+ * Used by RK808 for BUCK4
+ *  0-15:      1.8V-3.3V,step=100mV
+ */
+struct rkpmic_vsel_range rk808_vsel_range2[] = {
+       { 1800000, 100000, 0, 15 },
+       {}
+};
+
+/*
+ * Used by RK808 for LDO1-2, 4-5, 8
+ *  0-16:      1.8V-3.4V, step=100mV
+ */
+struct rkpmic_vsel_range rk808_vsel_range3[] = {
+       { 1800000, 100000, 0, 16 },
+       {}
+};
+
+/*
+ * Used by RK808 for LDO3
+ *   0-12:     0.8V~2.0V, step=100mV
+ *   13:       2.2V
+ *   15:       2.5V
+ */
+struct rkpmic_vsel_range rk808_vsel_range4[] = {
+       { 800000, 100000, 0, 12 },
+       { 2200000, 0, 13, 13 },
+       { 2500000, 0, 15, 15 },
+       {}
+};
+
+/*
+ * Used by RK808 for LDO6-7
+ *  0-17:      0.8V-2.5V,step=100mV
+ */
+struct rkpmic_vsel_range rk808_vsel_range5[] = {
+       { 800000, 100000, 0, 17 },
+       {}
+};
+
 struct rkpmic_regdata rk808_regdata[] = {
-       { "DCDC_REG1", 0x2f, 0x3f, 712500, 12500 },
-       { "DCDC_REG2", 0x33, 0x3f, 712500, 12500 },
-       { "DCDC_REG4", 0x38, 0x0f, 1800000, 100000 },
-       { "LDO_REG1", 0x3b, 0x1f, 1800000, 100000 },
-       { "LDO_REG2", 0x3d, 0x1f, 1800000, 100000 },
-       { "LDO_REG3", 0x3f, 0x0f, 800000, 100000 },
-       { "LDO_REG4", 0x41, 0x1f, 1800000, 100000 },
-       { "LDO_REG5", 0x43, 0x1f, 1800000, 100000 },
-       { "LDO_REG6", 0x45, 0x1f, 800000, 100000 },
-       { "LDO_REG7", 0x47, 0x1f, 800000, 100000 },
-       { "LDO_REG8", 0x49, 0x1f, 1800000, 100000 },
+       { "DCDC_REG1", 0x2f, 0x3f, rk808_vsel_range1 },
+       { "DCDC_REG2", 0x33, 0x3f, rk808_vsel_range1 },
+       { "DCDC_REG4", 0x38, 0x0f, rk808_vsel_range2 },
+       { "LDO_REG1", 0x3b, 0x1f, rk808_vsel_range3 },
+       { "LDO_REG2", 0x3d, 0x1f, rk808_vsel_range3 },
+       { "LDO_REG3", 0x3f, 0x0f, rk808_vsel_range4 },
+       { "LDO_REG4", 0x41, 0x1f, rk808_vsel_range3 },
+       { "LDO_REG5", 0x43, 0x1f, rk808_vsel_range3 },
+       { "LDO_REG6", 0x45, 0x1f, rk808_vsel_range5 },
+       { "LDO_REG7", 0x47, 0x1f, rk808_vsel_range5 },
+       { "LDO_REG8", 0x49, 0x1f, rk808_vsel_range3 },
+       { }
+};
+
+/*
+ * Used by RK809 for BUCK1-3
+ *  0-80:      0.5V-1.5V,step=12.5mV
+ *  81-89:     1.6V-2.4V,step=100mV
+ */
+struct rkpmic_vsel_range rk809_vsel_range1[] = {
+       { 500000, 12500, 0, 80 },
+       { 1600000, 100000, 81, 89 },
+       {}
+};
+
+/*
+ * Used by RK809 for BUCK4
+ *  0-80:      0.5V-1.5V,step=12.5mV
+ *  81-99:     1.6V-3.4V,step=100mV
+ */
+struct rkpmic_vsel_range rk809_vsel_range2[] = {
+       { 500000, 12500, 0, 80 },
+       { 1600000, 100000, 81, 99 },
+       {}
+};
+
+/*
+ * Used by RK809 for BUCK5
+ *  0:         1.5V
+ *  1-3:       1.8V-2.2V,step=200mV
+ *  4-5:       2.8V-3.0V,step=200mV
+ *  6-7:       3.3V-3.6V,step=300mV
+ */
+struct rkpmic_vsel_range rk809_vsel_range3[] = {
+       { 1500000, 0, 0, 0 },
+       { 1800000, 200000, 1, 3 },
+       { 2800000, 200000, 4, 5 },
+       { 3300000, 300000, 6, 7 },
+       {}
+};
+
+/*
+ * Used by RK809 for LDO1-7
+ *  0-112: 0.6V-3.4V,step=25mV
+ */
+struct rkpmic_vsel_range rk809_vsel_range4[] = {
+       { 600000, 25000, 0, 112 },
+       {}
+};
+
+struct rkpmic_regdata rk809_regdata[] = {
+       { "DCDC_REG1", 0xbb, 0x7f, rk809_vsel_range1 },
+       { "DCDC_REG2", 0xbe, 0x7f, rk809_vsel_range1 },
+       { "DCDC_REG3", 0xc1, 0x7f, rk809_vsel_range1 },
+       { "DCDC_REG4", 0xc4, 0x7f, rk809_vsel_range2 },
+       { "DCDC_REG5", 0xde, 0x0f, rk809_vsel_range3},
+       { "LDO_REG1", 0xcc, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG2", 0xce, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG3", 0xd0, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG4", 0xd2, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG5", 0xd4, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG6", 0xd6, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG7", 0xd8, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG8", 0xda, 0x7f, rk809_vsel_range4 },
+       { "LDO_REG9", 0xdc, 0x7f, rk809_vsel_range4 },
        { }
 };
 
@@ -80,6 +234,7 @@ struct rkpmic_softc {
        i2c_tag_t sc_tag;
        i2c_addr_t sc_addr;
 
+       int sc_rtc_ctrl_reg, sc_rtc_status_reg;
        struct todr_chip_handle sc_todr;
        struct rkpmic_regdata *sc_regdata;
 };
@@ -109,7 +264,8 @@ rkpmic_match(struct device *parent, void *match, void *aux)
        struct i2c_attach_args *ia = aux;
 
        return (strcmp(ia->ia_name, "rockchip,rk805") == 0 ||
-           strcmp(ia->ia_name, "rockchip,rk808") == 0);
+           strcmp(ia->ia_name, "rockchip,rk808") == 0 ||
+           strcmp(ia->ia_name, "rockchip,rk809") == 0);
 }
 
 void
@@ -131,10 +287,19 @@ rkpmic_attach(struct device *parent, struct device *self, void *aux)
 
        if (OF_is_compatible(node, "rockchip,rk805")) {
                chip = "RK805";
+               sc->sc_rtc_ctrl_reg = RK805_RTC_CTRL;
+               sc->sc_rtc_status_reg = RK805_RTC_STATUS;
                sc->sc_regdata = rk805_regdata;
-       } else {
+       } else if (OF_is_compatible(node, "rockchip,rk808")) {
                chip = "RK808";
+               sc->sc_rtc_ctrl_reg = RK808_RTC_CTRL;
+               sc->sc_rtc_status_reg = RK808_RTC_STATUS;
                sc->sc_regdata = rk808_regdata;
+       } else {
+               chip = "RK809";
+               sc->sc_rtc_ctrl_reg = RK809_RTC_CTRL;
+               sc->sc_rtc_status_reg = RK809_RTC_STATUS;
+               sc->sc_regdata = rk809_regdata;
        }
        printf(": %s\n", chip);
 
@@ -149,7 +314,7 @@ struct rkpmic_regulator {
        struct rkpmic_softc *rr_sc;
 
        uint8_t rr_reg, rr_mask;
-       uint32_t rr_base, rr_delta;
+       struct rkpmic_vsel_range *rr_vsel_range;
 
        struct regulator_device rr_rd;
 };
@@ -179,8 +344,7 @@ rkpmic_attach_regulator(struct rkpmic_softc *sc, int node)
 
        rr->rr_reg = sc->sc_regdata[i].reg;
        rr->rr_mask = sc->sc_regdata[i].mask;
-       rr->rr_base = sc->sc_regdata[i].base;
-       rr->rr_delta = sc->sc_regdata[i].delta;
+       rr->rr_vsel_range = sc->sc_regdata[i].vsel_range;
 
        rr->rr_rd.rd_node = node;
        rr->rr_rd.rd_cookie = rr;
@@ -193,27 +357,68 @@ uint32_t
 rkpmic_get_voltage(void *cookie)
 {
        struct rkpmic_regulator *rr = cookie;
+       struct rkpmic_vsel_range *vsel_range = rr->rr_vsel_range;
        uint8_t vsel;
+       uint32_t ret = 0;
+
+       vsel = rkpmic_reg_read(rr->rr_sc, rr->rr_reg) & rr->rr_mask;
        
-       vsel = rkpmic_reg_read(rr->rr_sc, rr->rr_reg);
-       return rr->rr_base + (vsel & rr->rr_mask) * rr->rr_delta;
+       while (vsel_range->base) {
+               ret = vsel_range->base;
+               if (vsel >= vsel_range->vsel_min &&
+                   vsel <= vsel_range->vsel_max) {
+                       ret += (vsel - vsel_range->vsel_min) *
+                           vsel_range->delta;
+                       break;
+               } else
+                       ret += (vsel_range->vsel_max - vsel_range->vsel_min) *
+                           vsel_range->delta;
+               vsel_range++;
+                       
+       }
+
+       return ret;
 }
 
 int
 rkpmic_set_voltage(void *cookie, uint32_t voltage)
 {
        struct rkpmic_regulator *rr = cookie;
-       uint32_t vmin = rr->rr_base;
-       uint32_t vmax = vmin + rr->rr_mask * rr->rr_delta;
-       uint8_t vsel;
+       struct rkpmic_vsel_range *vsel_range = rr->rr_vsel_range;
+       uint32_t vmin, vmax, volt;
+       uint8_t reg, vsel;
+
+       while (vsel_range->base) {
+               vmin = vsel_range->base;
+               vmax = vmin + (vsel_range->vsel_max - vsel_range->vsel_min) *
+                   vsel_range->delta;
+               if (voltage < vmin)
+                       return EINVAL;
+               if (voltage <= vmax) {
+                       vsel = vsel_range->vsel_min;
+                       volt = vsel_range->base;
+                       while (vsel <= vsel_range->vsel_max) {
+                               if (volt == voltage)
+                                       break;
+                               else {
+                                       vsel++;
+                                       volt += vsel_range->delta;
+                               }
+                       }
+                       if (volt != voltage)
+                               return EINVAL;
+                       break;
+               }
+               vsel_range++;
+       }
 
-       if (voltage < vmin || voltage > vmax)
+       if (vsel_range->base == 0)
                return EINVAL;
 
-       vsel = rkpmic_reg_read(rr->rr_sc, rr->rr_reg);
-       vsel &= ~rr->rr_mask;
-       vsel |= (voltage - rr->rr_base) / rr->rr_delta;
-       rkpmic_reg_write(rr->rr_sc, rr->rr_reg, vsel);
+       reg = rkpmic_reg_read(rr->rr_sc, rr->rr_reg);
+       reg &= ~rr->rr_mask;
+       reg |= vsel;
+       rkpmic_reg_write(rr->rr_sc, rr->rr_reg, reg);
 
        return 0;
 }
@@ -319,14 +524,14 @@ rkpmic_reg_write(struct rkpmic_softc *sc, int reg, uint8_t val)
 int
 rkpmic_clock_read(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
 {
-       uint8_t regs[RK808_NRTC_REGS];
-       uint8_t cmd = RK808_SECONDS;
+       uint8_t regs[RK80X_NRTC_REGS];
+       uint8_t cmd = RK80X_SECONDS;
        uint8_t status;
        int error;
 
        iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
        error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
-           &cmd, sizeof(cmd), regs, RK808_NRTC_REGS, I2C_F_POLL);
+           &cmd, sizeof(cmd), regs, RK80X_NRTC_REGS, I2C_F_POLL);
        iic_release_bus(sc->sc_tag, I2C_F_POLL);
 
        if (error) {
@@ -335,7 +540,7 @@ rkpmic_clock_read(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
        }
 
        /*
-        * Convert the RK808's register values into something useable.
+        * Convert the RK80x's register values into something useable.
         */
        dt->dt_sec = FROMBCD(regs[0]);
        dt->dt_min = FROMBCD(regs[1]);
@@ -345,8 +550,8 @@ rkpmic_clock_read(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
        dt->dt_year = FROMBCD(regs[5]) + 2000;
 
        /* Consider the time to be invalid if the POWER_UP bit is set. */
-       status = rkpmic_reg_read(sc, RK808_RTC_STATUS);
-       if (status & RK808_RTC_STATUS_POWER_UP)
+       status = rkpmic_reg_read(sc, sc->sc_rtc_status_reg);
+       if (status & RK80X_RTC_STATUS_POWER_UP)
                return EINVAL;
 
        return 0;
@@ -355,12 +560,12 @@ rkpmic_clock_read(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
 int
 rkpmic_clock_write(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
 {
-       uint8_t regs[RK808_NRTC_REGS];
-       uint8_t cmd = RK808_SECONDS;
+       uint8_t regs[RK80X_NRTC_REGS];
+       uint8_t cmd = RK80X_SECONDS;
        int error;
 
        /*
-        * Convert our time representation into something the RK808
+        * Convert our time representation into something the RK80x
         * can understand.
         */
        regs[0] = TOBCD(dt->dt_sec);
@@ -372,15 +577,15 @@ rkpmic_clock_write(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
        regs[6] = TOBCD(dt->dt_wday);
 
        /* Stop RTC such that we can write to it. */
-       rkpmic_reg_write(sc, RK808_RTC_CTRL, RK808_RTC_CTRL_STOP_RTC);
+       rkpmic_reg_write(sc, sc->sc_rtc_ctrl_reg, RK80X_RTC_CTRL_STOP_RTC);
 
        iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
        error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
-           &cmd, sizeof(cmd), regs, RK808_NRTC_REGS, I2C_F_POLL);
+           &cmd, sizeof(cmd), regs, RK80X_NRTC_REGS, I2C_F_POLL);
        iic_release_bus(sc->sc_tag, I2C_F_POLL);
 
        /* Restart RTC. */
-       rkpmic_reg_write(sc, RK808_RTC_CTRL, 0);
+       rkpmic_reg_write(sc, sc->sc_rtc_ctrl_reg, 0);
 
        if (error) {
                printf("%s: can't write RTC\n", sc->sc_dev.dv_xname);
@@ -388,7 +593,7 @@ rkpmic_clock_write(struct rkpmic_softc *sc, struct clock_ymdhms *dt)
        }
 
        /* Clear POWER_UP bit to indicate the time is now valid. */
-       rkpmic_reg_write(sc, RK808_RTC_STATUS, RK808_RTC_STATUS_POWER_UP);
+       rkpmic_reg_write(sc, sc->sc_rtc_status_reg, RK80X_RTC_STATUS_POWER_UP);
 
        return 0;
 }