/*- * Copyright (c) 2015 Semihalf. * Copyright (c) 2015 Stormshield. * All rights reserved. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #define RTC_RES_US 1000000 #define HALF_OF_SEC_NS 500000000 #define RTC_STATUS 0x0 #define RTC_TIME 0xC #define RTC_TEST_CONFIG 0x1C #define RTC_IRQ_1_CONFIG 0x4 #define RTC_IRQ_2_CONFIG 0x8 #define RTC_ALARM_1 0x10 #define RTC_ALARM_2 0x14 #define RTC_CLOCK_CORR 0x18 #define RTC_NOMINAL_TIMING 0x2000 #define RTC_NOMINAL_TIMING_MASK 0x7fff #define RTC_STATUS_ALARM1_MASK 0x1 #define RTC_STATUS_ALARM2_MASK 0x2 #define MV_RTC_LOCK(sc) mtx_lock_spin(&(sc)->mutex) #define MV_RTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mutex) #define A38X_RTC_BRIDGE_TIMING_CTRL 0x0 #define A38X_RTC_WRCLK_PERIOD_SHIFT 0 #define A38X_RTC_WRCLK_PERIOD_MASK 0x00000003FF #define A38X_RTC_WRCLK_PERIOD_MAX 0x3FF #define A38X_RTC_READ_OUTPUT_DELAY_SHIFT 26 #define A38X_RTC_READ_OUTPUT_DELAY_MASK 0x007C000000 #define A38X_RTC_READ_OUTPUT_DELAY_MAX 0x1F #define A8K_RTC_BRIDGE_TIMING_CTRL0 0x0 #define A8K_RTC_WRCLK_PERIOD_SHIFT 0 #define A8K_RTC_WRCLK_PERIOD_MASK 0x000000FFFF #define A8K_RTC_WRCLK_PERIOD_VAL 0x3FF #define A8K_RTC_WRCLK_SETUP_SHIFT 16 #define A8K_RTC_WRCLK_SETUP_MASK 0x00FFFF0000 #define A8K_RTC_WRCLK_SETUP_VAL 29 #define A8K_RTC_BRIDGE_TIMING_CTRL1 0x4 #define A8K_RTC_READ_OUTPUT_DELAY_SHIFT 0 #define A8K_RTC_READ_OUTPUT_DELAY_MASK 0x000000FFFF #define A8K_RTC_READ_OUTPUT_DELAY_VAL 0x3F #define RTC_RES 0 #define RTC_SOC_RES 1 static struct resource_spec res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { -1, 0 } }; struct mv_rtc_softc { device_t dev; struct resource *res[2]; struct mtx mutex; int rtc_type; }; static int mv_rtc_probe(device_t dev); static int mv_rtc_attach(device_t dev); static int mv_rtc_detach(device_t dev); static int mv_rtc_gettime(device_t dev, struct timespec *ts); static int mv_rtc_settime(device_t dev, struct timespec *ts); static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off); static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val); static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc); static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc); static device_method_t mv_rtc_methods[] = { DEVMETHOD(device_probe, mv_rtc_probe), DEVMETHOD(device_attach, mv_rtc_attach), DEVMETHOD(device_detach, mv_rtc_detach), DEVMETHOD(clock_gettime, mv_rtc_gettime), DEVMETHOD(clock_settime, mv_rtc_settime), { 0, 0 }, }; static driver_t mv_rtc_driver = { "rtc", mv_rtc_methods, sizeof(struct mv_rtc_softc), }; #define RTC_A38X 1 #define RTC_A8K 2 static struct ofw_compat_data mv_rtc_compat[] = { {"marvell,armada-380-rtc", RTC_A38X}, {"marvell,armada-8k-rtc", RTC_A8K}, {NULL, 0}, }; DRIVER_MODULE(a38x_rtc, simplebus, mv_rtc_driver, 0, 0); static void mv_rtc_reset(device_t dev) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); /* Reset Test register */ mv_rtc_reg_write(sc, RTC_TEST_CONFIG, 0); DELAY(500000); /* Reset Time register */ mv_rtc_reg_write(sc, RTC_TIME, 0); DELAY(62); /* Reset Status register */ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK)); DELAY(62); /* Turn off Int1 and Int2 sources & clear the Alarm count */ mv_rtc_reg_write(sc, RTC_IRQ_1_CONFIG, 0); mv_rtc_reg_write(sc, RTC_IRQ_2_CONFIG, 0); mv_rtc_reg_write(sc, RTC_ALARM_1, 0); mv_rtc_reg_write(sc, RTC_ALARM_2, 0); /* Setup nominal register access timing */ mv_rtc_reg_write(sc, RTC_CLOCK_CORR, RTC_NOMINAL_TIMING); /* Reset Time register */ mv_rtc_reg_write(sc, RTC_TIME, 0); DELAY(10); /* Reset Status register */ mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK)); DELAY(50); } static int mv_rtc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data) return (ENXIO); device_set_desc(dev, "Marvell Integrated RTC"); return (BUS_PROBE_DEFAULT); } static int mv_rtc_attach(device_t dev) { struct mv_rtc_softc *sc; int ret; sc = device_get_softc(dev); sc->dev = dev; sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data; mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN); ret = bus_alloc_resources(dev, res_spec, sc->res); if (ret != 0) { device_printf(dev, "could not allocate resources\n"); mtx_destroy(&sc->mutex); return (ENXIO); } switch (sc->rtc_type) { case RTC_A38X: mv_rtc_configure_bus_a38x(sc); break; case RTC_A8K: mv_rtc_configure_bus_a8k(sc); break; default: panic("Unknown RTC type: %d", sc->rtc_type); } clock_register(dev, RTC_RES_US); return (0); } static int mv_rtc_detach(device_t dev) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); mtx_destroy(&sc->mutex); bus_release_resources(dev, res_spec, sc->res); return (0); } static int mv_rtc_gettime(device_t dev, struct timespec *ts) { struct mv_rtc_softc *sc; uint32_t val, val_check; sc = device_get_softc(dev); MV_RTC_LOCK(sc); /* * According to HW Errata, if more than one second is detected * between two time reads, then at least one of the reads gave * an invalid value. */ do { val = mv_rtc_reg_read(sc, RTC_TIME); DELAY(100); val_check = mv_rtc_reg_read(sc, RTC_TIME); } while ((val_check - val) > 1); MV_RTC_UNLOCK(sc); ts->tv_sec = val_check; /* RTC resolution is 1 sec */ ts->tv_nsec = 0; return (0); } static int mv_rtc_settime(device_t dev, struct timespec *ts) { struct mv_rtc_softc *sc; sc = device_get_softc(dev); /* RTC resolution is 1 sec */ if (ts->tv_nsec >= HALF_OF_SEC_NS) ts->tv_sec++; ts->tv_nsec = 0; MV_RTC_LOCK(sc); if ((mv_rtc_reg_read(sc, RTC_CLOCK_CORR) & RTC_NOMINAL_TIMING_MASK) != RTC_NOMINAL_TIMING) { /* RTC was not resetted yet */ mv_rtc_reset(dev); } /* * According to errata FE-3124064, Write to RTC TIME register * may fail. As a workaround, before writing to RTC TIME register, * issue a dummy write of 0x0 twice to RTC Status register. */ mv_rtc_reg_write(sc, RTC_STATUS, 0x0); mv_rtc_reg_write(sc, RTC_STATUS, 0x0); mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec); MV_RTC_UNLOCK(sc); return (0); } static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off) { return (bus_read_4(sc->res[RTC_RES], off)); } /* * According to the datasheet, the OS should wait 5us after every * register write to the RTC hard macro so that the required update * can occur without holding off the system bus */ static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->res[RTC_RES], off, val); DELAY(5); return (0); } static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc) { int val; val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL); val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK); val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT; val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << A38X_RTC_READ_OUTPUT_DELAY_SHIFT; bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val); } static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc) { int val; val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0); val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK); val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT; val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT; bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val); val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0); val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK; val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT; bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val); }