diff --git a/sys/dev/iicbus/pmic/rockchip/rk817.c b/sys/dev/iicbus/pmic/rockchip/rk817.c new file mode 100644 index 000000000000..54431aaa4735 --- /dev/null +++ b/sys/dev/iicbus/pmic/rockchip/rk817.c @@ -0,0 +1,532 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSS + * + * Copyright (c) 2021, 2022 Soren Schmidt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + + +static struct ofw_compat_data compat_data[] = { + {"rockchip,rk809", RK809}, + {"rockchip,rk817", RK817}, + {NULL, 0} +}; + +static struct rk8xx_regdef rk809_regdefs[] = { + { + .id = RK809_DCDC1, + .name = "DCDC_REG1", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x11, + .voltage_reg = RK817_DCDC1_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK809_DCDC2, + .name = "DCDC_REG2", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x22, + .voltage_reg = RK817_DCDC2_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK809_DCDC3, + .name = "DCDC_REG3", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x44, + .voltage_reg = RK817_DCDC3_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK809_DCDC4, + .name = "DCDC_REG4", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x88, + .voltage_reg = RK817_DCDC4_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 3400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 195, + }, + { + .id = RK809_DCDC5, + .name = "DCDC_REG5", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x22, + .voltage_reg = RK817_BOOST_ON_VSEL, + .voltage_mask = 0x07, + .voltage_min = 1600000, /* cheat is 1.5V */ + .voltage_max = 3400000, + .voltage_min2 = 3500000, + .voltage_max2 = 3600000, + .voltage_step = 200000, + .voltage_step2 = 300000, + .voltage_nstep = 8, + }, + { + .id = RK809_LDO1, + .name = "LDO_REG1", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO1_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO2, + .name = "LDO_REG2", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x22, + .voltage_reg = RK817_LDO2_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO3, + .name = "LDO_REG3", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x44, + .voltage_reg = RK817_LDO3_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO4, + .name = "LDO_REG4", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x88, + .voltage_reg = RK817_LDO4_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO5, + .name = "LDO_REG5", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO5_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO6, + .name = "LDO_REG6", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x22, + .voltage_reg = RK817_LDO6_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO7, + .name = "LDO_REG7", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x44, + .voltage_reg = RK817_LDO7_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO8, + .name = "LDO_REG8", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x88, + .voltage_reg = RK817_LDO8_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_LDO9, + .name = "LDO_REG9", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO9_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK809_SWITCH1, + .name = "SWITCH_REG1", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x44, + .voltage_min = 3300000, + .voltage_max = 3300000, + .voltage_nstep = 0, + }, + { + .id = RK809_SWITCH2, + .name = "SWITCH_REG2", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x88, + .voltage_min = 3300000, + .voltage_max = 3300000, + .voltage_nstep = 0, + }, +}; + +static struct rk8xx_regdef rk817_regdefs[] = { + { + .id = RK817_DCDC1, + .name = "DCDC_REG1", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x11, + .voltage_reg = RK817_DCDC1_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK817_DCDC2, + .name = "DCDC_REG2", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x22, + .voltage_reg = RK817_DCDC2_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK817_DCDC3, + .name = "DCDC_REG3", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x44, + .voltage_reg = RK817_DCDC3_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 2400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 177, + }, + { + .id = RK817_DCDC4, + .name = "DCDC_REG4", + .enable_reg = RK817_DCDC_EN, + .enable_mask = 0x88, + .voltage_reg = RK817_DCDC4_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 500000, + .voltage_max = 1487500, + .voltage_min2 = 1500000, + .voltage_max2 = 3400000, + .voltage_step = 12500, + .voltage_step2 = 100000, + .voltage_nstep = 195, + }, + { + .id = RK817_LDO1, + .name = "LDO_REG1", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO1_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO2, + .name = "LDO_REG2", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x22, + .voltage_reg = RK817_LDO2_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO3, + .name = "LDO_REG3", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x44, + .voltage_reg = RK817_LDO3_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO4, + .name = "LDO_REG4", + .enable_reg = RK817_LDO_EN1, + .enable_mask = 0x88, + .voltage_reg = RK817_LDO4_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO5, + .name = "LDO_REG5", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO5_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO6, + .name = "LDO_REG6", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x22, + .voltage_reg = RK817_LDO6_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO7, + .name = "LDO_REG7", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x44, + .voltage_reg = RK817_LDO7_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO8, + .name = "LDO_REG8", + .enable_reg = RK817_LDO_EN2, + .enable_mask = 0x88, + .voltage_reg = RK817_LDO8_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_LDO9, + .name = "LDO_REG9", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x11, + .voltage_reg = RK817_LDO9_ON_VSEL, + .voltage_mask = 0x7f, + .voltage_min = 600000, + .voltage_max = 3400000, + .voltage_step = 25000, + .voltage_nstep = 112, + }, + { + .id = RK817_BOOST, + .name = "BOOST", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x22, + .voltage_reg = RK817_BOOST_ON_VSEL, + .voltage_mask = 0x07, + .voltage_min = 4700000, + .voltage_max = 5400000, + .voltage_step = 100000, + .voltage_nstep = 8, + }, + { + .id = RK817_OTG_SWITCH, + .name = "OTG_SWITCH", + .enable_reg = RK817_LDO_EN3, + .enable_mask = 0x44, + .voltage_nstep = 0, + }, +}; + +static int +rk817_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + case RK809: + device_set_desc(dev, "RockChip RK809 PMIC"); + break; + case RK817: + device_set_desc(dev, "RockChip RK817 PMIC"); + break; + default: + return (ENXIO); + } + + return (BUS_PROBE_DEFAULT); +} + +static int +rk817_attach(device_t dev) +{ + struct rk8xx_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->type) { + case RK809: + sc->regdefs = rk809_regdefs; + sc->nregs = nitems(rk809_regdefs); + break; + case RK817: + sc->regdefs = rk817_regdefs; + sc->nregs = nitems(rk817_regdefs); + break; + default: + device_printf(dev, "Unknown type %d\n", sc->type); + return (ENXIO); + } + sc->rtc_regs.secs = RK817_RTC_SECONDS; + sc->rtc_regs.secs_mask = RK817_RTC_SECONDS_MASK; + sc->rtc_regs.minutes = RK817_RTC_MINUTES; + sc->rtc_regs.minutes_mask = RK817_RTC_MINUTES_MASK; + sc->rtc_regs.hours = RK817_RTC_HOURS; + sc->rtc_regs.hours_mask = RK817_RTC_HOURS_MASK; + sc->rtc_regs.days = RK817_RTC_DAYS; + sc->rtc_regs.days_mask = RK817_RTC_DAYS_MASK; + sc->rtc_regs.months = RK817_RTC_MONTHS; + sc->rtc_regs.months_mask = RK817_RTC_MONTHS_MASK; + sc->rtc_regs.years = RK817_RTC_YEARS; + sc->rtc_regs.weeks = RK817_RTC_WEEKS_MASK; + sc->rtc_regs.ctrl = RK817_RTC_CTRL; + sc->rtc_regs.ctrl_stop_mask = RK817_RTC_CTRL_STOP; + sc->rtc_regs.ctrl_ampm_mask = RK817_RTC_AMPM_MODE; + sc->rtc_regs.ctrl_gettime_mask = RK817_RTC_GET_TIME; + sc->rtc_regs.ctrl_readsel_mask = RK817_RTC_READSEL; + sc->dev_ctrl.dev_ctrl_reg = RK817_SYS_CFG3; + sc->dev_ctrl.pwr_off_mask = RK817_SYS_CFG3_OFF; + sc->dev_ctrl.pwr_rst_mask = RK817_SYS_CFG3_RST; + + return (rk8xx_attach(sc)); +} + +static device_method_t rk817_methods[] = { + DEVMETHOD(device_probe, rk817_probe), + DEVMETHOD(device_attach, rk817_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(rk817_pmu, rk817_driver, rk817_methods, + sizeof(struct rk8xx_softc), rk8xx_driver); + +EARLY_DRIVER_MODULE(rk817_pmu, iicbus, rk817_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); +EARLY_DRIVER_MODULE(iicbus, rk817_pmu, iicbus_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); +MODULE_DEPEND(rk817_pmu, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_VERSION(rk817_pmu, 1); diff --git a/sys/dev/iicbus/pmic/rockchip/rk817reg.h b/sys/dev/iicbus/pmic/rockchip/rk817reg.h new file mode 100644 index 000000000000..607243826d04 --- /dev/null +++ b/sys/dev/iicbus/pmic/rockchip/rk817reg.h @@ -0,0 +1,121 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBS + * + * Copyright (c) 2021, 2022 Soren Schmidt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + + +#ifndef _RK817REG_H_ +#define _RK817REG_H_ + +#define RK817_RTC_SECONDS 0x00 +#define RK817_RTC_SECONDS_MASK 0x7f +#define RK817_RTC_MINUTES 0x01 +#define RK817_RTC_MINUTES_MASK 0x7f +#define RK817_RTC_HOURS 0x02 +#define RK817_RTC_HOURS_MASK 0x3f +#define RK817_RTC_DAYS 0x03 +#define RK817_RTC_DAYS_MASK 0x3f +#define RK817_RTC_MONTHS 0x04 +#define RK817_RTC_MONTHS_MASK 0x1f +#define RK817_RTC_YEARS 0x05 +#define RK817_RTC_WEEKS 0x06 +#define RK817_RTC_WEEKS_MASK 0x07 +#define RK817_ALARM_SECONDS 0x7 +#define RK817_ALARM_MINUTES 0x8 +#define RK817_ALARM_HOURS 0x9 +#define RK817_ALARM_DAYS 0xA +#define RK817_ALARM_MONTHS 0xB +#define RK817_ALARM_YEARS 0xC +#define RK817_RTC_CTRL 0x0d +#define RK817_RTC_CTRL_STOP (1 << 0) +#define RK817_RTC_AMPM_MODE (1 << 3) +#define RK817_RTC_GET_TIME (1 << 6) +#define RK817_RTC_READSEL (1 << 7) +#define RK817_RTC_STATUS 0x0e +#define RK817_RTC_INT 0x0f +#define RK817_RTC_COMP_LSB 0x10 +#define RK817_RTC_COMP_MSB 0x11 + +#define RK817_DCDC_EN 0xb1 +#define RK817_LDO_EN1 0xb2 +#define RK817_LDO_EN2 0xb3 +#define RK817_LDO_EN3 0xb4 +#define RK817_DCDC1_ON_VSEL 0xbb +#define RK817_DCDC2_ON_VSEL 0xbe +#define RK817_DCDC3_ON_VSEL 0xc1 +#define RK817_DCDC4_ON_VSEL 0xc4 +#define RK817_LDO1_ON_VSEL 0xcc +#define RK817_LDO2_ON_VSEL 0xce +#define RK817_LDO3_ON_VSEL 0xd0 +#define RK817_LDO4_ON_VSEL 0xd2 +#define RK817_LDO5_ON_VSEL 0xd4 +#define RK817_LDO6_ON_VSEL 0xd6 +#define RK817_LDO7_ON_VSEL 0xd8 +#define RK817_LDO8_ON_VSEL 0xda +#define RK817_LDO9_ON_VSEL 0xdc +#define RK817_BOOST_ON_VSEL 0xde +#define RK817_SYS_CFG3 0xf4 +#define RK817_SYS_CFG3_OFF (1 << 0) +#define RK817_SYS_CFG3_SLP (1 << 1) +#define RK817_SYS_CFG3_RST (1 << 2) + +enum rk809_regulator { + RK809_DCDC1 = 0, + RK809_DCDC2, + RK809_DCDC3, + RK809_DCDC4, + RK809_DCDC5, + RK809_LDO1, + RK809_LDO2, + RK809_LDO3, + RK809_LDO4, + RK809_LDO5, + RK809_LDO6, + RK809_LDO7, + RK809_LDO8, + RK809_LDO9, + RK809_SWITCH1, + RK809_SWITCH2, +}; + +enum rk817_regulator { + RK817_DCDC1 = 0, + RK817_DCDC2, + RK817_DCDC3, + RK817_DCDC4, + RK817_LDO1, + RK817_LDO2, + RK817_LDO3, + RK817_LDO4, + RK817_LDO5, + RK817_LDO6, + RK817_LDO7, + RK817_LDO8, + RK817_LDO9, + RK817_BOOST, + RK817_OTG_SWITCH, +}; +#endif /* _RK817REG_H_ */ diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx.c b/sys/dev/iicbus/pmic/rockchip/rk8xx.c index 0e6839ff0152..bf6e5833665a 100644 --- a/sys/dev/iicbus/pmic/rockchip/rk8xx.c +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx.c @@ -1,181 +1,194 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018-2021 Emmanuel Vadot * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "regdev_if.h" int rk8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { int err; err = iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT); return (err); } int rk8xx_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { return (iicdev_writeto(dev, reg, data, size, IIC_INTRWAIT)); } static void rk8xx_start(void *pdev) { struct rk8xx_softc *sc; device_t dev; uint8_t data[2]; int err; dev = pdev; sc = device_get_softc(dev); /* No version register in RK808 */ if (bootverbose && sc->type == RK805) { err = rk8xx_read(dev, RK805_CHIP_NAME, data, 1); if (err != 0) { device_printf(dev, "Cannot read chip name reg\n"); return; } err = rk8xx_read(dev, RK805_CHIP_VER, data + 1, 1); if (err != 0) { device_printf(dev, "Cannot read chip version reg\n"); return; } device_printf(dev, "Chip Name: %x\n", data[0] << 4 | ((data[1] >> 4) & 0xf)); device_printf(dev, "Chip Version: %x\n", data[1] & 0xf); } /* Register this as a 1Hz clock */ clock_register(dev, 1000000); 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; + if (howto & RB_POWEROFF) + val |= sc->dev_ctrl.pwr_off_mask; + else if (howto & RB_POWERCYCLE) { + if (sc->type == RK809 || sc->type == RK817) { + if (bootverbose) { + device_printf(sc->dev, + "Powercycle PMIC\n"); + } + val |= sc->dev_ctrl.pwr_rst_mask;; + } else { + /* Poweroff PMIC that can't powercycle */ + 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) { int error; error = rk8xx_attach_clocks(sc); if (error != 0) return (error); sc->intr_hook.ich_func = rk8xx_start; sc->intr_hook.ich_arg = sc->dev; if (config_intrhook_establish(&sc->intr_hook) != 0) return (ENOMEM); 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); } static int rk8xx_detach(device_t dev) { /* We cannot detach regulators */ return (EBUSY); } static device_method_t rk8xx_methods[] = { DEVMETHOD(device_detach, rk8xx_detach), /* regdev interface */ DEVMETHOD(regdev_map, rk8xx_map), /* Clock interface */ DEVMETHOD(clock_gettime, rk8xx_gettime), DEVMETHOD(clock_settime, rk8xx_settime), DEVMETHOD_END }; DEFINE_CLASS_0(rk8xx, rk8xx_driver, rk8xx_methods, sizeof(struct rk8xx_softc)); diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx.h b/sys/dev/iicbus/pmic/rockchip/rk8xx.h index 739b57c5f0bb..0edd0fd7fe24 100644 --- a/sys/dev/iicbus/pmic/rockchip/rk8xx.h +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx.h @@ -1,128 +1,134 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2021 Emmanuel Vadot * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _RK8XX_H_ #define _RK8XX_H_ #include #include #include enum rk_pmic_type { RK805 = 1, RK808, + RK809, + RK817, }; struct rk8xx_regdef { intptr_t id; char *name; uint8_t enable_reg; uint8_t enable_mask; uint8_t voltage_reg; uint8_t voltage_mask; int voltage_min; int voltage_max; + int voltage_min2; + int voltage_max2; int voltage_step; + int voltage_step2; int voltage_nstep; }; struct rk8xx_reg_sc { struct regnode *regnode; device_t base_dev; struct rk8xx_regdef *def; phandle_t xref; struct regnode_std_param *param; }; struct reg_list { TAILQ_ENTRY(reg_list) next; struct rk8xx_reg_sc *reg; }; struct rk8xx_rtc_reg { uint8_t secs; uint8_t secs_mask; uint8_t minutes; uint8_t minutes_mask; uint8_t hours; uint8_t hours_mask; uint8_t days; uint8_t days_mask; uint8_t months; uint8_t months_mask; uint8_t years; uint8_t weeks; uint8_t weeks_mask; uint8_t ctrl; uint8_t ctrl_stop_mask; uint8_t ctrl_ampm_mask; uint8_t ctrl_gettime_mask; uint8_t ctrl_readsel_mask; }; struct rk8xx_dev_ctrl { uint8_t dev_ctrl_reg; uint8_t pwr_off_mask; + uint8_t pwr_rst_mask; }; struct rk8xx_softc { device_t dev; struct mtx mtx; struct resource * res[1]; void * intrcookie; struct intr_config_hook intr_hook; enum rk_pmic_type type; struct rk8xx_regdef *regdefs; TAILQ_HEAD(, reg_list) regs; 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); int rk8xx_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size); DECLARE_CLASS(rk8xx_driver); int rk8xx_attach(struct rk8xx_softc *sc); /* rk8xx_clocks.c */ int rk8xx_attach_clocks(struct rk8xx_softc *sc); /* rk8xx_regulators.c */ void rk8xx_attach_regulators(struct rk8xx_softc *sc); int rk8xx_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id); /* rk8xx_rtc.c */ int rk8xx_gettime(device_t dev, struct timespec *ts); int rk8xx_settime(device_t dev, struct timespec *ts); #endif /* _RK8XX_H_ */ diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx_regulators.c b/sys/dev/iicbus/pmic/rockchip/rk8xx_regulators.c index 74bf2ab6ff73..be214228e817 100644 --- a/sys/dev/iicbus/pmic/rockchip/rk8xx_regulators.c +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx_regulators.c @@ -1,326 +1,370 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018-2021 Emmanuel Vadot * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "regdev_if.h" static int rk8xx_regnode_status(struct regnode *regnode, int *status); static int rk8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay); static int rk8xx_regnode_get_voltage(struct regnode *regnode, int *uvolt); /* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ #define dprintf(sc, format, arg...) (sc = sc) static int rk8xx_regnode_init(struct regnode *regnode) { struct rk8xx_reg_sc *sc; struct regnode_std_param *param; int rv, udelay, uvolt, status; sc = regnode_get_softc(regnode); dprintf(sc, "Regulator %s init called\n", sc->def->name); param = regnode_get_stdparam(regnode); if (param->min_uvolt == 0) return (0); /* Check that the regulator is preset to the correct voltage */ rv = rk8xx_regnode_get_voltage(regnode, &uvolt); if (rv != 0) return(rv); if (uvolt >= param->min_uvolt && uvolt <= param->max_uvolt) return(0); /* * Set the regulator at the correct voltage if it is not enabled. * Do not enable it, this is will be done either by a * consumer or by regnode_set_constraint if boot_on is true */ rv = rk8xx_regnode_status(regnode, &status); if (rv != 0 || status == REGULATOR_STATUS_ENABLED) return (rv); rv = rk8xx_regnode_set_voltage(regnode, param->min_uvolt, param->max_uvolt, &udelay); if (udelay != 0) DELAY(udelay); return (rv); } static int rk8xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct rk8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); dprintf(sc, "%sabling regulator %s\n", enable ? "En" : "Dis", sc->def->name); rk8xx_read(sc->base_dev, sc->def->enable_reg, &val, 1); if (enable) val |= sc->def->enable_mask; else val &= ~sc->def->enable_mask; rk8xx_write(sc->base_dev, sc->def->enable_reg, &val, 1); *udelay = 0; return (0); } static void rk8xx_regnode_reg_to_voltage(struct rk8xx_reg_sc *sc, uint8_t val, int *uv) { - if (val < sc->def->voltage_nstep) - *uv = sc->def->voltage_min + val * sc->def->voltage_step; - else - *uv = sc->def->voltage_min + - (sc->def->voltage_nstep * sc->def->voltage_step); + struct rk8xx_softc *sc1; + + sc1 = device_get_softc(sc->base_dev); + if (sc1->type == RK809 || sc1->type == RK817) { + if (sc->def->voltage_step2) { + int change; + + change = + ((sc->def->voltage_min2 - sc->def->voltage_min) / + sc->def->voltage_step); + if (val > change) { + if (val < sc->def->voltage_nstep) { + *uv = sc->def->voltage_min2 + + (val - change) * + sc->def->voltage_step2; + } else + *uv = sc->def->voltage_max2; + return; + } + } + if (val < sc->def->voltage_nstep) + *uv = sc->def->voltage_min + val * sc->def->voltage_step; + else + *uv = sc->def->voltage_max; + + } else { + if (val < sc->def->voltage_nstep) + *uv = sc->def->voltage_min + val * sc->def->voltage_step; + else + *uv = sc->def->voltage_min + + (sc->def->voltage_nstep * sc->def->voltage_step); + } } static int rk8xx_regnode_voltage_to_reg(struct rk8xx_reg_sc *sc, int min_uvolt, int max_uvolt, uint8_t *val) { uint8_t nval; int nstep, uvolt; + struct rk8xx_softc *sc1; + sc1 = device_get_softc(sc->base_dev); nval = 0; uvolt = sc->def->voltage_min; for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt; nstep++) { ++nval; - uvolt += sc->def->voltage_step; + if (sc1->type == RK809 || sc1->type == RK817) { + if (sc->def->voltage_step2) { + if (uvolt < sc->def->voltage_min2) + uvolt += sc->def->voltage_step; + else + uvolt += sc->def->voltage_step2; + } else + uvolt += sc->def->voltage_step; + } else + uvolt += sc->def->voltage_step; } if (uvolt > max_uvolt) return (EINVAL); *val = nval; return (0); } static int rk8xx_regnode_status(struct regnode *regnode, int *status) { struct rk8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); *status = 0; rk8xx_read(sc->base_dev, sc->def->enable_reg, &val, 1); if (val & sc->def->enable_mask) *status = REGULATOR_STATUS_ENABLED; return (0); } static int rk8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct rk8xx_reg_sc *sc; - uint8_t val; + uint8_t val, old; int uvolt; + struct rk8xx_softc *sc1; sc = regnode_get_softc(regnode); + sc1 = device_get_softc(sc->base_dev); if (!sc->def->voltage_step) return (ENXIO); dprintf(sc, "Setting %s to %d<->%d uvolts\n", sc->def->name, min_uvolt, max_uvolt); rk8xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); + old = val; if (rk8xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) return (ERANGE); + if (sc1->type == RK809 || sc1->type == RK817) + val |= (old &= ~sc->def->voltage_mask); + rk8xx_write(sc->base_dev, sc->def->voltage_reg, &val, 1); rk8xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); *udelay = 0; rk8xx_regnode_reg_to_voltage(sc, val, &uvolt); dprintf(sc, "Regulator %s set to %d uvolt\n", sc->def->name, uvolt); return (0); } static int rk8xx_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct rk8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (sc->def->voltage_min == sc->def->voltage_max) { *uvolt = sc->def->voltage_min; return (0); } if (!sc->def->voltage_step) return (ENXIO); rk8xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); rk8xx_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt); dprintf(sc, "Regulator %s is at %d uvolt\n", sc->def->name, *uvolt); return (0); } static regnode_method_t rk8xx_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, rk8xx_regnode_init), REGNODEMETHOD(regnode_enable, rk8xx_regnode_enable), REGNODEMETHOD(regnode_status, rk8xx_regnode_status), REGNODEMETHOD(regnode_set_voltage, rk8xx_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, rk8xx_regnode_get_voltage), REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(rk8xx_regnode, rk8xx_regnode_class, rk8xx_regnode_methods, sizeof(struct rk8xx_reg_sc), regnode_class); static struct rk8xx_reg_sc * rk8xx_reg_attach(device_t dev, phandle_t node, struct rk8xx_regdef *def) { struct rk8xx_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; memset(&initdef, 0, sizeof(initdef)); if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) { device_printf(dev, "cannot create regulator\n"); return (NULL); } if (initdef.std_param.min_uvolt == 0) initdef.std_param.min_uvolt = def->voltage_min; if (initdef.std_param.max_uvolt == 0) initdef.std_param.max_uvolt = def->voltage_max; initdef.id = def->id; initdef.ofw_node = node; regnode = regnode_create(dev, &rk8xx_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); regnode_register(regnode); return (reg_sc); } void rk8xx_attach_regulators(struct rk8xx_softc *sc) { struct rk8xx_reg_sc *reg; struct reg_list *regp; phandle_t rnode, child; int i; TAILQ_INIT(&sc->regs); rnode = ofw_bus_find_child(ofw_bus_get_node(sc->dev), "regulators"); if (rnode > 0) { for (i = 0; i < sc->nregs; i++) { child = ofw_bus_find_child(rnode, sc->regdefs[i].name); if (child == 0) continue; if (OF_hasprop(child, "regulator-name") != 1) continue; reg = rk8xx_reg_attach(sc->dev, child, &sc->regdefs[i]); if (reg == NULL) { device_printf(sc->dev, "cannot attach regulator %s\n", sc->regdefs[i].name); continue; } regp = malloc(sizeof(*regp), M_DEVBUF, M_WAITOK | M_ZERO); regp->reg = reg; TAILQ_INSERT_TAIL(&sc->regs, regp, next); if (bootverbose) device_printf(sc->dev, "Regulator %s attached\n", sc->regdefs[i].name); } } } int rk8xx_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { struct rk8xx_softc *sc; struct reg_list *regp; sc = device_get_softc(dev); TAILQ_FOREACH(regp, &sc->regs, next) { if (regp->reg->xref == xref) { *id = regp->reg->def->id; return (0); } } return (ERANGE); } diff --git a/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c b/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c index 2f755d16b164..91991b4f41be 100644 --- a/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c +++ b/sys/dev/iicbus/pmic/rockchip/rk8xx_rtc.c @@ -1,142 +1,156 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2021 Emmanuel Vadot * Copyright (c) 2021 Peter Jeremy * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include int rk8xx_gettime(device_t dev, struct timespec *ts) { struct rk8xx_softc *sc; struct bcd_clocktime bct; uint8_t data[7]; uint8_t ctrl; int error; sc = device_get_softc(dev); /* Latch the RTC value into the shadow registers and set 24hr mode */ error = rk8xx_read(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); ctrl |= sc->rtc_regs.ctrl_readsel_mask; ctrl &= ~(sc->rtc_regs.ctrl_ampm_mask | sc->rtc_regs.ctrl_gettime_mask); error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); ctrl |= sc->rtc_regs.ctrl_gettime_mask; error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); + if (sc->type == RK809 || sc->type == RK817) { + /* wait one 32khz cycle for clock shadow registers to latch */ + DELAY(1000000 / 32000); + } ctrl &= ~sc->rtc_regs.ctrl_gettime_mask; error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); /* This works as long as sc->rtc_regs.secs = 0 */ error = rk8xx_read(dev, sc->rtc_regs.secs, data, 7); if (error != 0) return (error); /* * If the reported year is earlier than 2019, assume the clock is unset. * This is both later than the reset value for the RK805 and RK808 as * well as being prior to the current time. */ if (data[sc->rtc_regs.years] < 0x19) return (EINVAL); memset(&bct, 0, sizeof(bct)); bct.year = data[sc->rtc_regs.years]; bct.mon = data[sc->rtc_regs.months] & sc->rtc_regs.months_mask; bct.day = data[sc->rtc_regs.days] & sc->rtc_regs.days_mask; bct.hour = data[sc->rtc_regs.hours] & sc->rtc_regs.hours_mask; bct.min = data[sc->rtc_regs.minutes] & sc->rtc_regs.minutes_mask; bct.sec = data[sc->rtc_regs.secs] & sc->rtc_regs.secs_mask; bct.dow = data[sc->rtc_regs.weeks] & sc->rtc_regs.weeks_mask; /* The day of week is reported as 1-7 with 1 = Monday */ if (bct.dow == 7) bct.dow = 0; bct.ispm = 0; + if (sc->type == RK809 || sc->type == RK817) + bct.year += 0x2000; /* valid for 2000-2099 only */ if (bootverbose) device_printf(dev, "Read RTC: %02x-%02x-%02x %02x:%02x:%02x\n", bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec); return (clock_bcd_to_ts(&bct, ts, false)); } int rk8xx_settime(device_t dev, struct timespec *ts) { struct rk8xx_softc *sc; struct bcd_clocktime bct; uint8_t data[7]; int error; uint8_t ctrl; sc = device_get_softc(dev); clock_ts_to_bcd(ts, &bct, false); /* This works as long as RK805_RTC_SECS = 0 */ + if (sc->type == RK809 || sc->type == RK817) { + /* valid for 2000-2099 only */ + if ((bct.year & 0xff00) != 0x2000) { + device_printf(dev, "year out of range\n"); + return (EINVAL); + } + bct.year &= 0x00ff; + } data[sc->rtc_regs.years] = bct.year; data[sc->rtc_regs.months] = bct.mon; data[sc->rtc_regs.days] = bct.day; data[sc->rtc_regs.hours] = bct.hour; data[sc->rtc_regs.minutes] = bct.min; data[sc->rtc_regs.secs] = bct.sec; data[sc->rtc_regs.weeks] = bct.dow; /* The day of week is reported as 1-7 with 1 = Monday */ if (data[sc->rtc_regs.weeks] == 0) data[sc->rtc_regs.weeks] = 7; error = rk8xx_read(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); ctrl |= sc->rtc_regs.ctrl_stop_mask; ctrl &= ~sc->rtc_regs.ctrl_ampm_mask; error = rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1); if (error != 0) return (error); error = rk8xx_write(dev, sc->rtc_regs.secs, data, 7); ctrl &= ~sc->rtc_regs.ctrl_stop_mask; rk8xx_write(dev, sc->rtc_regs.ctrl, &ctrl, 1); return (error); }