Changeset View
Standalone View
sys/arm64/rockchip/rk805.c
Show All 24 Lines | |||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/clock.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <dev/iicbus/iiconf.h> | #include <dev/iicbus/iiconf.h> | ||||
#include <dev/iicbus/iicbus.h> | #include <dev/iicbus/iicbus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/extres/regulator/regulator.h> | #include <dev/extres/regulator/regulator.h> | ||||
#include <arm64/rockchip/rk805reg.h> | #include <arm64/rockchip/rk805reg.h> | ||||
#include "clock_if.h" | |||||
#include "regdev_if.h" | #include "regdev_if.h" | ||||
MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); | MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); | ||||
/* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ | /* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ | ||||
#define dprintf(sc, format, arg...) | #define dprintf(sc, format, arg...) | ||||
enum rk_pmic_type { | enum rk_pmic_type { | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
int err; | int err; | ||||
err = iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT); | err = iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT); | ||||
return (err); | return (err); | ||||
} | } | ||||
static int | static int | ||||
rk805_write(device_t dev, uint8_t reg, uint8_t data) | rk805_write(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) | ||||
{ | { | ||||
return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT)); | return (iicdev_writeto(dev, reg, data, size, IIC_INTRWAIT)); | ||||
} | } | ||||
static int | static int | ||||
rk805_regnode_init(struct regnode *regnode) | rk805_regnode_init(struct regnode *regnode) | ||||
{ | { | ||||
struct rk805_reg_sc *sc; | struct rk805_reg_sc *sc; | ||||
struct regnode_std_param *param; | struct regnode_std_param *param; | ||||
int rv, udelay, uvolt, status; | int rv, udelay, uvolt, status; | ||||
Show All 39 Lines | rk805_regnode_enable(struct regnode *regnode, bool enable, int *udelay) | ||||
dprintf(sc, "%sabling regulator %s\n", | dprintf(sc, "%sabling regulator %s\n", | ||||
enable ? "En" : "Dis", | enable ? "En" : "Dis", | ||||
sc->def->name); | sc->def->name); | ||||
rk805_read(sc->base_dev, sc->def->enable_reg, &val, 1); | rk805_read(sc->base_dev, sc->def->enable_reg, &val, 1); | ||||
if (enable) | if (enable) | ||||
val |= sc->def->enable_mask; | val |= sc->def->enable_mask; | ||||
else | else | ||||
val &= ~sc->def->enable_mask; | val &= ~sc->def->enable_mask; | ||||
rk805_write(sc->base_dev, sc->def->enable_reg, val); | rk805_write(sc->base_dev, sc->def->enable_reg, &val, 1); | ||||
*udelay = 0; | *udelay = 0; | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
rk805_regnode_reg_to_voltage(struct rk805_reg_sc *sc, uint8_t val, int *uv) | rk805_regnode_reg_to_voltage(struct rk805_reg_sc *sc, uint8_t val, int *uv) | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | rk805_regnode_set_voltage(struct regnode *regnode, int min_uvolt, | ||||
dprintf(sc, "Setting %s to %d<->%d uvolts\n", | dprintf(sc, "Setting %s to %d<->%d uvolts\n", | ||||
sc->def->name, | sc->def->name, | ||||
min_uvolt, | min_uvolt, | ||||
max_uvolt); | max_uvolt); | ||||
rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); | rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); | ||||
if (rk805_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) | if (rk805_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) | ||||
return (ERANGE); | return (ERANGE); | ||||
rk805_write(sc->base_dev, sc->def->voltage_reg, val); | rk805_write(sc->base_dev, sc->def->voltage_reg, &val, 1); | ||||
rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); | rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); | ||||
*udelay = 0; | *udelay = 0; | ||||
rk805_regnode_reg_to_voltage(sc, val, &uvolt); | rk805_regnode_reg_to_voltage(sc, val, &uvolt); | ||||
dprintf(sc, "Regulator %s set to %d uvolt\n", | dprintf(sc, "Regulator %s set to %d uvolt\n", | ||||
sc->def->name, | sc->def->name, | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | if (err != 0) { | ||||
device_printf(dev, "Cannot read chip version reg\n"); | device_printf(dev, "Cannot read chip version reg\n"); | ||||
return; | return; | ||||
} | } | ||||
device_printf(dev, "Chip Name: %x\n", | device_printf(dev, "Chip Name: %x\n", | ||||
data[0] << 4 | ((data[1] >> 4) & 0xf)); | data[0] << 4 | ((data[1] >> 4) & 0xf)); | ||||
device_printf(dev, "Chip Version: %x\n", data[1] & 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); | config_intrhook_disestablish(&sc->intr_hook); | ||||
} | } | ||||
static int | static int | ||||
rk805_gettime(device_t dev, struct timespec *ts) | |||||
{ | |||||
struct bcd_clocktime bct; | |||||
uint8_t data[7]; | |||||
uint8_t ctrl; | |||||
int error; | |||||
/* Latch the RTC value into the shadow registers and set 24hr mode */ | |||||
peterj: This code assumes that the RTC is configured for direct access to the dynamic time registers… | |||||
error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
ctrl |= RK805_RTC_READSEL; | |||||
ctrl &= ~(RK805_RTC_AMPM_MODE | RK805_RTC_GET_TIME); | |||||
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
ctrl |= RK805_RTC_GET_TIME; | |||||
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
ctrl &= ~RK805_RTC_GET_TIME; | |||||
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
/* This works as long as RK805_RTC_SECS = 0 */ | |||||
error = rk805_read(dev, RK805_RTC_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[RK805_RTC_YEARS] < 0x19) | |||||
return (EINVAL); | |||||
memset(&bct, 0, sizeof(bct)); | |||||
bct.year = data[RK805_RTC_YEARS]; | |||||
bct.mon = data[RK805_RTC_MONTHS] & RK805_RTC_MONTHS_MASK; | |||||
bct.day = data[RK805_RTC_DAYS] & RK805_RTC_DAYS_MASK; | |||||
bct.hour = data[RK805_RTC_HOURS] & RK805_RTC_HOURS_MASK; | |||||
bct.min = data[RK805_RTC_MINUTES] & RK805_RTC_MINUTES_MASK; | |||||
bct.sec = data[RK805_RTC_SECS] & RK805_RTC_SECS_MASK; | |||||
bct.dow = data[RK805_RTC_WEEKS] & RK805_RTC_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 (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)); | |||||
Not Done Inline ActionsI think that we should force 24 hours mode here in case some other OS uses it and the user booted it before booting FreeBSD. manu: I think that we should force 24 hours mode here in case some other OS uses it and the user… | |||||
Not Done Inline ActionsActually, the calls to clock_bcd_to_ts() and clock_ts_to_bcd() both assume that the RTC is running in 12h mode. That said, I agree that the code should force the RTC mode to match the time format. peterj: Actually, the calls to clock_bcd_to_ts() and clock_ts_to_bcd() both assume that the RTC is… | |||||
} | |||||
Not Done Inline ActionsTypo 'RRK' val_packett.cool: Typo 'RRK' | |||||
static int | |||||
Not Done Inline ActionsDoes this sequence of CTRL_STOP being set/cleared cause the rtc's internal sub-second counters to reset to zero? If so, then after registering the clock in the attach code, you should call clock_schedule(dev, 1), so that rk805_settime() is scheduled to run right after top-of-second so that the rtc's internal phase matches the kernel clock's phase. Some rtc chips restart their counters at the halfway point (.5 sec), in which case you should use something like clock_schedule(dev, 500000000). ian: Does this sequence of CTRL_STOP being set/cleared cause the rtc's internal sub-second counters… | |||||
Not Done Inline ActionsUnfortunately, the neither the RK805 nor RK808 datasheets that I've found on the web document this. I suspect it will need experimentation. peterj: Unfortunately, the neither the RK805 nor RK808 datasheets that I've found on the web document… | |||||
rk805_settime(device_t dev, struct timespec *ts) | |||||
{ | |||||
struct bcd_clocktime bct; | |||||
uint8_t data[7]; | |||||
int error; | |||||
uint8_t ctrl; | |||||
clock_ts_to_bcd(ts, &bct, false); | |||||
/* This works as long as RK805_RTC_SECS = 0 */ | |||||
data[RK805_RTC_YEARS] = bct.year; | |||||
data[RK805_RTC_MONTHS] = bct.mon; | |||||
data[RK805_RTC_DAYS] = bct.day; | |||||
data[RK805_RTC_HOURS] = bct.hour; | |||||
data[RK805_RTC_MINUTES] = bct.min; | |||||
data[RK805_RTC_SECS] = bct.sec; | |||||
data[RK805_RTC_WEEKS] = bct.dow; | |||||
/* The day of week is reported as 1-7 with 1 = Monday */ | |||||
if (data[RK805_RTC_WEEKS] == 0) | |||||
data[RK805_RTC_WEEKS] = 7; | |||||
error = rk805_read(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
ctrl |= RK805_RTC_CTRL_STOP; | |||||
ctrl &= ~RK805_RTC_AMPM_MODE; | |||||
error = rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (error != 0) | |||||
return (error); | |||||
error = rk805_write(dev, RK805_RTC_SECS, data, 7); | |||||
ctrl &= ~RK805_RTC_CTRL_STOP; | |||||
rk805_write(dev, RK805_RTC_CTRL, &ctrl, 1); | |||||
if (bootverbose) | |||||
device_printf(dev, | |||||
"Set RTC at %04x-%02x-%02x %02x:%02x:%02x[.%09ld]\n", | |||||
bct.year, bct.mon, bct.day, bct.hour, bct.min, bct.sec, | |||||
bct.nsec); | |||||
return (error); | |||||
} | |||||
static int | |||||
rk805_attach(device_t dev) | rk805_attach(device_t dev) | ||||
{ | { | ||||
struct rk805_softc *sc; | struct rk805_softc *sc; | ||||
struct rk805_reg_sc *reg; | struct rk805_reg_sc *reg; | ||||
struct rk805_regdef *regdefs; | struct rk805_regdef *regdefs; | ||||
struct reg_list *regp; | struct reg_list *regp; | ||||
phandle_t rnode, child; | phandle_t rnode, child; | ||||
int i; | int i; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
static device_method_t rk805_methods[] = { | static device_method_t rk805_methods[] = { | ||||
DEVMETHOD(device_probe, rk805_probe), | DEVMETHOD(device_probe, rk805_probe), | ||||
DEVMETHOD(device_attach, rk805_attach), | DEVMETHOD(device_attach, rk805_attach), | ||||
DEVMETHOD(device_detach, rk805_detach), | DEVMETHOD(device_detach, rk805_detach), | ||||
/* regdev interface */ | /* regdev interface */ | ||||
DEVMETHOD(regdev_map, rk805_map), | DEVMETHOD(regdev_map, rk805_map), | ||||
/* Clock interface */ | |||||
DEVMETHOD(clock_gettime, rk805_gettime), | |||||
DEVMETHOD(clock_settime, rk805_settime), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static driver_t rk805_driver = { | static driver_t rk805_driver = { | ||||
"rk805_pmu", | "rk805_pmu", | ||||
rk805_methods, | rk805_methods, | ||||
sizeof(struct rk805_softc), | sizeof(struct rk805_softc), | ||||
}; | }; | ||||
static devclass_t rk805_devclass; | static devclass_t rk805_devclass; | ||||
EARLY_DRIVER_MODULE(rk805, iicbus, rk805_driver, rk805_devclass, 0, 0, | EARLY_DRIVER_MODULE(rk805, iicbus, rk805_driver, rk805_devclass, 0, 0, | ||||
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); | BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); | ||||
MODULE_DEPEND(rk805, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); | MODULE_DEPEND(rk805, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); | ||||
MODULE_VERSION(rk805, 1); | MODULE_VERSION(rk805, 1); |
This code assumes that the RTC is configured for direct access to the dynamic time registers (which might imply that there's a race condition reading the time - the datasheets don't mention one way or the other). Since this function is (normally) only called once on boot (from inittodr()), it would seem preferable to transfer the time to the static shadow registers and read them (this requires an additional 1 I2C read and 3 writes).