-/* $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 },
{ }
};
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;
};
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
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);
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;
};
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;
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;
}
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) {
}
/*
- * 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]);
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;
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);
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);
}
/* 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;
}