From d863b24203b0b6af7a6a78bd00c5d3db4b84d699 Mon Sep 17 00:00:00 2001 From: kettenis Date: Sat, 16 Dec 2017 10:22:13 +0000 Subject: [PATCH] Add a driver for the RSB controller found on various Allwinner SoCs. Add a driver for the RTC part of the AC100 chip. Together this turns my Cubieboard4 into a real computer by giving it a proper clock. ok patrick@ --- sys/arch/armv7/conf/GENERIC | 4 +- sys/arch/armv7/conf/RAMDISK | 4 +- sys/dev/fdt/acrtc.c | 186 ++++++++++++++++++++++ sys/dev/fdt/files.fdt | 11 +- sys/dev/fdt/rsbvar.h | 29 ++++ sys/dev/fdt/sxirsb.c | 302 ++++++++++++++++++++++++++++++++++++ 6 files changed, 533 insertions(+), 3 deletions(-) create mode 100644 sys/dev/fdt/acrtc.c create mode 100644 sys/dev/fdt/rsbvar.h create mode 100644 sys/dev/fdt/sxirsb.c diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC index 2377eb31ad6..72d683814d9 100644 --- a/sys/arch/armv7/conf/GENERIC +++ b/sys/arch/armv7/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.99 2017/10/24 17:00:34 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.100 2017/12/16 10:22:13 kettenis Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -91,6 +91,8 @@ sxipio* at fdt? early 1 # GPIO pins for leds & PHYs gpio* at sxipio? sxiccmu* at fdt? early 1 # Clock Control Module/Unit sxitimer* at fdt? early 1 +sxirsb* at fdt? early 1 # Reduced Serial Bus +acrtc* at sxirsb? sxidog* at fdt? # watchdog timer sxirtc* at fdt? # Real Time Clock sxie* at fdt? diff --git a/sys/arch/armv7/conf/RAMDISK b/sys/arch/armv7/conf/RAMDISK index 63561027126..f5a98bd6bea 100644 --- a/sys/arch/armv7/conf/RAMDISK +++ b/sys/arch/armv7/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.92 2017/10/24 17:00:34 kettenis Exp $ +# $OpenBSD: RAMDISK,v 1.93 2017/12/16 10:22:13 kettenis Exp $ machine armv7 arm @@ -87,6 +87,8 @@ sxipio* at fdt? early 1 # GPIO pins for leds & PHYs gpio* at sxipio? sxiccmu* at fdt? early 1 # Clock Control Module/Unit sxitimer* at fdt? early 1 +sxirsb* at fdt? early 1 # Reduced Serial Bus +acrtc* at sxirsb? sxidog* at fdt? # watchdog timer sxirtc* at fdt? # Real Time Clock sxie* at fdt? diff --git a/sys/dev/fdt/acrtc.c b/sys/dev/fdt/acrtc.c new file mode 100644 index 00000000000..a150a433a27 --- /dev/null +++ b/sys/dev/fdt/acrtc.c @@ -0,0 +1,186 @@ +/* $OpenBSD: acrtc.c,v 1.1 2017/12/16 10:22:13 kettenis Exp $ */ +/* + * Copyright (c) 2017 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + +extern todr_chip_handle_t todr_handle; + +#define RTC_CTRL 0xc7 +#define RTC_CTRL_12H_24H_MODE (1 << 0) +#define RTC_SEC 0xc8 +#define RTC_MIN 0xc9 +#define RTC_HOU 0xca +#define RTC_WEE 0xcb +#define RTC_DAY 0xcc +#define RTC_MON 0xcd +#define RTC_YEA 0xce +#define RTC_YEA_LEAP_YEAR (1 << 15) +#define RTC_UPD_TRIG 0xcf +#define RTC_UPD_TRIG_UPDATE (1 << 15) + +struct acrtc_softc { + struct device sc_dev; + void *sc_cookie; + uint16_t sc_rta; + + struct todr_chip_handle sc_todr; +}; + +int acrtc_match(struct device *, void *, void *); +void acrtc_attach(struct device *, struct device *, void *); + +struct cfattach acrtc_ca = { + sizeof(struct acrtc_softc), acrtc_match, acrtc_attach +}; + +struct cfdriver acrtc_cd = { + NULL, "acrtc", DV_DULL +}; + +int acrtc_clock_read(struct acrtc_softc *, struct clock_ymdhms *); +int acrtc_clock_write(struct acrtc_softc *, struct clock_ymdhms *); +int acrtc_gettime(struct todr_chip_handle *, struct timeval *); +int acrtc_settime(struct todr_chip_handle *, struct timeval *); + +int +acrtc_match(struct device *parent, void *match, void *aux) +{ + struct rsb_attach_args *ra = aux; + + if (strcmp(ra->ra_name, "x-powers,ac100") == 0) + return 1; + return 0; +} + +void +acrtc_attach(struct device *parent, struct device *self, void *aux) +{ + struct acrtc_softc *sc = (struct acrtc_softc *)self; + struct rsb_attach_args *ra = aux; + + sc->sc_cookie = ra->ra_cookie; + sc->sc_rta = ra->ra_rta; + + printf("\n"); + + sc->sc_todr.cookie = sc; + sc->sc_todr.todr_gettime = acrtc_gettime; + sc->sc_todr.todr_settime = acrtc_settime; + todr_handle = &sc->sc_todr; +} + +inline uint16_t +acrtc_read_reg(struct acrtc_softc *sc, uint8_t reg) +{ + return rsb_read_2(sc->sc_cookie, sc->sc_rta, reg); +} + +inline void +acrtc_write_reg(struct acrtc_softc *sc, uint8_t reg, uint16_t value) +{ + rsb_write_2(sc->sc_cookie, sc->sc_rta, reg, value); +} + +int +acrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) +{ + struct acrtc_softc *sc = handle->cookie; + struct clock_ymdhms dt; + int error; + + error = acrtc_clock_read(sc, &dt); + if (error) + return error; + + if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || + dt.dt_day > 31 || dt.dt_day == 0 || + dt.dt_mon > 12 || dt.dt_mon == 0 || + dt.dt_year < POSIX_BASE_YEAR) + return EINVAL; + + tv->tv_sec = clock_ymdhms_to_secs(&dt); + tv->tv_usec = 0; + return 0; +} + +int +acrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) +{ + struct acrtc_softc *sc = handle->cookie; + struct clock_ymdhms dt; + + clock_secs_to_ymdhms(tv->tv_sec, &dt); + + return acrtc_clock_write(sc, &dt); +} + +int +acrtc_clock_read(struct acrtc_softc *sc, struct clock_ymdhms *dt) +{ + uint16_t ctrl; + + dt->dt_sec = FROMBCD(acrtc_read_reg(sc, RTC_SEC)); + dt->dt_min = FROMBCD(acrtc_read_reg(sc, RTC_MIN)); + dt->dt_hour = FROMBCD(acrtc_read_reg(sc, RTC_HOU)); + dt->dt_day = FROMBCD(acrtc_read_reg(sc, RTC_DAY)); + dt->dt_mon = FROMBCD(acrtc_read_reg(sc, RTC_MON)); + dt->dt_year = FROMBCD(acrtc_read_reg(sc, RTC_YEA)) + 2000; + +#ifdef DEBUG + printf("%02d/%02d/%04d %02d:%02d:%0d\n", dt->dt_day, dt->dt_mon, + dt->dt_year, dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif + + /* Consider the time to be invalid if the clock is in 12H mode. */ + ctrl = acrtc_read_reg(sc, RTC_CTRL); + if ((ctrl & RTC_CTRL_12H_24H_MODE) == 0) + return EINVAL; + + return 0; +} + +int +acrtc_clock_write(struct acrtc_softc *sc, struct clock_ymdhms *dt) +{ + uint16_t leap = isleap(dt->dt_year) ? RTC_YEA_LEAP_YEAR : 0; + + acrtc_write_reg(sc, RTC_SEC, TOBCD(dt->dt_sec)); + acrtc_write_reg(sc, RTC_MIN, TOBCD(dt->dt_min)); + acrtc_write_reg(sc, RTC_HOU, TOBCD(dt->dt_hour)); + acrtc_write_reg(sc, RTC_WEE, TOBCD(dt->dt_wday)); + acrtc_write_reg(sc, RTC_DAY, TOBCD(dt->dt_day)); + acrtc_write_reg(sc, RTC_MON, TOBCD(dt->dt_mon)); + acrtc_write_reg(sc, RTC_YEA, TOBCD(dt->dt_year - 2000) | leap); + acrtc_write_reg(sc, RTC_UPD_TRIG, RTC_UPD_TRIG_UPDATE); + + /* Switch to 24H mode to indicate the time is now valid. */ + acrtc_write_reg(sc, RTC_CTRL, RTC_CTRL_12H_24H_MODE); + + return 0; +} diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index 6df3c1f28a9..b8b17cb99a4 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.27 2017/09/21 12:01:52 patrick Exp $ +# $OpenBSD: files.fdt,v 1.28 2017/12/16 10:22:13 kettenis Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -15,6 +15,11 @@ device sxipio {}: gpiobus attach sxipio at fdt file dev/fdt/sxipio.c sxipio +define rsb +device sxirsb {}: rsb +attach sxirsb at fdt +file dev/fdt/sxirsb.c sxirsb + device sxirtc attach sxirtc at fdt file dev/fdt/sxirtc.c sxirtc @@ -131,3 +136,7 @@ file dev/fdt/if_mvneta.c mvneta device dwxe: ether, ifnet, mii, ifmedia attach dwxe at fdt file dev/fdt/if_dwxe.c dwxe + +device acrtc +attach acrtc at sxirsb +file dev/fdt/acrtc.c acrtc \ No newline at end of file diff --git a/sys/dev/fdt/rsbvar.h b/sys/dev/fdt/rsbvar.h new file mode 100644 index 00000000000..60060ef5193 --- /dev/null +++ b/sys/dev/fdt/rsbvar.h @@ -0,0 +1,29 @@ +/* $OpenBSD: rsbvar.h,v 1.1 2017/12/16 10:22:13 kettenis Exp $ */ +/* + * Copyright (c) 2017 Mark kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct rsb_attach_args { + void *ra_cookie; + uint16_t ra_da; + uint8_t ra_rta; + char *ra_name; + int ra_node; +}; + +int rsb_print(void *, const char *); + +uint16_t rsb_read_2(void *, uint8_t, uint8_t); +void rsb_write_2(void *, uint8_t, uint8_t, uint16_t); diff --git a/sys/dev/fdt/sxirsb.c b/sys/dev/fdt/sxirsb.c new file mode 100644 index 00000000000..b9809cfa325 --- /dev/null +++ b/sys/dev/fdt/sxirsb.c @@ -0,0 +1,302 @@ +/* $OpenBSD: sxirsb.c,v 1.1 2017/12/16 10:22:13 kettenis Exp $ */ +/* + * Copyright (c) 2017 Mark kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#define RSB_CTRL 0x0000 +#define RSB_CTRL_START_TRANS (1 << 7) +#define RSB_CTRL_ABORT_TRANS (1 << 6) +#define RSB_CTRL_GLOBAL_INT_ENB (1 << 1) +#define RSB_CTRL_SOFT_RESET (1 << 0) +#define RSB_CCR 0x0004 +#define RSB_CCR_CD_ODLY_SHIFT 8 +#define RSB_CCR_CD_ODLY_MAX 0x7 +#define RSB_CCR_CK_DIV_SHIFT 0 +#define RSB_CCR_CK_DIV_MAX 0xff +#define RSB_STAT 0x000c +#define RSB_STAT_TRANS_OVER (1 << 0) +#define RSB_AR 0x0010 +#define RSB_DATA 0x001c +#define RSB_DMCR 0x0028 +#define RSB_DMCR_DEVICE_MODE_START (1U << 31) +#define RSB_DMCR_DEVICE_MODE_DATA 0x7e3e00 +#define RSB_CMD 0x002c +#define RSB_DAR 0x0030 + +#define SRTA 0xe8 +#define RD8 0x8b +#define RD16 0x9c +#define RD32 0xa6 +#define WR8 0x4e +#define WR16 0x59 +#define WR32 0x63 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct sxirsb_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + int sc_addr; +}; + +int sxirsb_match(struct device *, void *, void *); +void sxirsb_attach(struct device *, struct device *, void *); + +struct cfattach sxirsb_ca = { + sizeof(struct sxirsb_softc), sxirsb_match, sxirsb_attach +}; + +struct cfdriver sxirsb_cd = { + NULL, "sxirsb", DV_DULL +}; + +uint8_t sxirsb_rta(uint16_t); + +int +sxirsb_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "allwinner,sun8i-a23-rsb"); +} + +void +sxirsb_attach(struct device *parent, struct device *self, void *aux) +{ + struct sxirsb_softc *sc = (struct sxirsb_softc *)self; + struct fdt_attach_args *faa = aux; + uint32_t freq, parent_freq, div, odly; + struct rsb_attach_args ra; + char name[32]; + uint32_t reg; + uint8_t rta; + int node; + int timo; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + pinctrl_byname(faa->fa_node, "default"); + + clock_enable_all(faa->fa_node); + reset_deassert_all(faa->fa_node); + + HWRITE4(sc, RSB_CTRL, RSB_CTRL_SOFT_RESET); + for (timo = 1000; timo > 0; timo--) { + if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_SOFT_RESET) == 0) + break; + delay(100); + } + if (timo == 0) { + printf(": reset failed\n"); + return; + } + + freq = OF_getpropint(faa->fa_node, "clock-frequency", 3000000); + parent_freq = clock_get_frequency_idx(faa->fa_node, 0); + div = parent_freq / freq / 2; + if (div == 0) + div = 1; + if (div > (RSB_CCR_CK_DIV_MAX + 1)) + div = (RSB_CCR_CK_DIV_MAX + 1); + odly = div >> 1; + if (odly == 0) + odly = 1; + if (odly > RSB_CCR_CD_ODLY_MAX) + odly = RSB_CCR_CD_ODLY_MAX; + HWRITE4(sc, RSB_CCR, (odly << RSB_CCR_CD_ODLY_SHIFT) | + ((div - 1) << RSB_CCR_CK_DIV_SHIFT)); + + HWRITE4(sc, RSB_DMCR, RSB_DMCR_DEVICE_MODE_START | + RSB_DMCR_DEVICE_MODE_DATA); + for (timo = 1000; timo > 0; timo--) { + if ((HREAD4(sc, RSB_DMCR) & RSB_DMCR_DEVICE_MODE_START) == 0) + break; + delay(100); + } + if (timo == 0) { + printf(": mode switch failed\n"); + return; + } + + for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { + reg = OF_getpropint(node, "reg", 0); + if (reg == 0) + continue; + + rta = sxirsb_rta(reg); + HWRITE4(sc, RSB_CMD, SRTA); + HWRITE4(sc, RSB_DAR, (rta << 16 | reg)); + + HSET4(sc, RSB_CTRL, RSB_CTRL_START_TRANS); + for (timo = 1000; timo > 0; timo--) { + if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_START_TRANS) == 0) + break; + delay(10); + } + if (timo == 0) { + printf(": SRTA failed for device 0x%03x\n", reg); + return; + } + } + + printf("\n"); + + for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { + reg = OF_getpropint(node, "reg", 0); + if (reg == 0) + continue; + + memset(name, 0, sizeof(name)); + if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) + continue; + if (name[0] == '\0') + continue; + + memset(&ra, 0, sizeof(ra)); + ra.ra_cookie = sc; + ra.ra_da = reg; + ra.ra_rta = sxirsb_rta(reg); + ra.ra_name = name; + ra.ra_node = node; + config_found(self, &ra, rsb_print); + } +} + +/* + * Use a fixed device address to run-time address mapping. This keeps + * things simple and matches what Linux does. + */ + +struct rsb_addr_map { + uint16_t da; + uint8_t rta; +}; + +struct rsb_addr_map rsb_addr_map[] = { + { 0x3a3, 0x2d }, + { 0x745, 0x3a }, + { 0xe89, 0x4e } +}; + +uint8_t +sxirsb_rta(uint16_t da) +{ + int i; + + for (i = 0; i < nitems(rsb_addr_map); i++) { + if (rsb_addr_map[i].da == da) + return rsb_addr_map[i].rta; + } + + return 0; +} + +uint16_t +rsb_read_2(void *cookie, uint8_t rta, uint8_t addr) +{ + struct sxirsb_softc *sc = cookie; + uint16_t stat; + int timo; + + HWRITE4(sc, RSB_CMD, RD16); + HWRITE4(sc, RSB_DAR, rta << 16); + HWRITE4(sc, RSB_AR, addr); + + HSET4(sc, RSB_CTRL, RSB_CTRL_START_TRANS); + for (timo = 1000; timo > 0; timo--) { + if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_START_TRANS) == 0) + break; + delay(10); + } + stat = HREAD4(sc, RSB_STAT); + HWRITE4(sc, RSB_STAT, stat); + if (timo == 0 || stat != RSB_STAT_TRANS_OVER) { + printf(": RD16 failed for run-time address 0x%02x\n", rta); + return 0xff; + } + + return HREAD4(sc, RSB_DATA); +} + +void +rsb_write_2(void *cookie, uint8_t rta, uint8_t addr, uint16_t data) +{ + struct sxirsb_softc *sc = cookie; + uint16_t stat; + int timo; + + HWRITE4(sc, RSB_CMD, WR16); + HWRITE4(sc, RSB_DAR, rta << 16); + HWRITE4(sc, RSB_AR, addr); + HWRITE4(sc, RSB_DATA, data); + + HSET4(sc, RSB_CTRL, RSB_CTRL_START_TRANS); + for (timo = 1000; timo > 0; timo--) { + if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_START_TRANS) == 0) + break; + delay(10); + } + stat = HREAD4(sc, RSB_STAT); + HWRITE4(sc, RSB_STAT, stat); + if (timo == 0 || stat != RSB_STAT_TRANS_OVER) { + printf(": WR16 failed for run-time address 0x%02x\n", rta); + return; + } +} + +int +rsb_print(void *aux, const char *pnp) +{ + struct rsb_attach_args *ra = aux; + + if (pnp != NULL) + printf("\"%s\" at %s", ra->ra_name, pnp); + printf(" addr 0x%x", ra->ra_da); + + return (UNCONF); +} -- 2.20.1