/*-1* Copyright (c) 2017 Ian Lepore <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/cdefs.h>27/*28* Driver for imx6 Secure Non-Volatile Storage system, which really means "all29* the stuff that's powered by a battery when main power is off". This includes30* realtime clock, tamper monitor, and power-management functions. Currently31* this driver provides only realtime clock support.32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/bus.h>37#include <sys/clock.h>38#include <sys/kernel.h>39#include <sys/module.h>40#include <machine/bus.h>4142#include <dev/ofw/ofw_bus_subr.h>4344#include "clock_if.h"45#include "syscon_if.h"4647#define SNVS_LPCR 0x38 /* Control register */48#define LPCR_LPCALB_VAL_SHIFT 10 /* Calibration shift */49#define LPCR_LPCALB_VAL_MASK 0x1f /* Calibration mask */50#define LPCR_LPCALB_EN (1u << 8) /* Calibration enable */51#define LPCR_SRTC_ENV (1u << 0) /* RTC enabled/valid */5253#define SNVS_LPSRTCMR 0x50 /* Counter MSB */54#define SNVS_LPSRTCLR 0x54 /* Counter LSB */5556#define RTC_RESOLUTION_US (1000000 / 32768) /* 32khz clock */5758/*59* The RTC is a 47-bit counter clocked at 32KHz and organized as a 32.1560* fixed-point binary value. Shifting by SBT_LSB bits translates between61* counter and sbintime values.62*/63#define RTC_BITS 4764#define SBT_BITS 6465#define SBT_LSB (SBT_BITS - RTC_BITS)6667struct snvs_softc {68device_t dev;69struct syscon *syscon;70uint32_t lpcr;71};7273static struct ofw_compat_data compat_data[] = {74{"fsl,sec-v4.0-mon-rtc-lp", true},75{NULL, false}76};7778static inline uint32_t79RD4(struct snvs_softc *sc, bus_size_t offset)80{8182return (SYSCON_READ_4(sc->syscon, offset));83}8485static inline void86WR4(struct snvs_softc *sc, bus_size_t offset, uint32_t value)87{8889SYSCON_WRITE_4(sc->syscon, offset, value);90}9192static void93snvs_rtc_enable(struct snvs_softc *sc, bool enable)94{95uint32_t enbit;9697if (enable)98sc->lpcr |= LPCR_SRTC_ENV;99else100sc->lpcr &= ~LPCR_SRTC_ENV;101WR4(sc, SNVS_LPCR, sc->lpcr);102103/* Wait for the hardware to achieve the requested state. */104enbit = sc->lpcr & LPCR_SRTC_ENV;105while ((RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV) != enbit)106continue;107}108109static int110snvs_gettime(device_t dev, struct timespec *ts)111{112struct snvs_softc *sc;113sbintime_t counter1, counter2;114115sc = device_get_softc(dev);116117/* If the clock is not enabled and valid, we can't help. */118if (!(RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV)) {119return (EINVAL);120}121122/*123* The counter is clocked asynchronously to cpu accesses; read and124* assemble the pieces of the counter until we get the same value twice.125* The counter is 47 bits, organized as a 32.15 binary fixed-point126* value. If we shift it up to the high order part of a 64-bit word it127* turns into an sbintime.128*/129do {130counter1 = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);131counter1 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);132counter2 = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);133counter2 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);134} while (counter1 != counter2);135136*ts = sbttots(counter1);137138clock_dbgprint_ts(sc->dev, CLOCK_DBG_READ, ts);139140return (0);141}142143static int144snvs_settime(device_t dev, struct timespec *ts)145{146struct snvs_softc *sc;147sbintime_t sbt;148149sc = device_get_softc(dev);150151/*152* The hardware format is the same as sbt (with fewer fractional bits),153* so first convert the time to sbt. It takes two clock cycles for the154* counter to start after setting the enable bit, so add two SBT_LSBs to155* what we're about to set.156*/157sbt = tstosbt(*ts);158sbt += 2 << SBT_LSB;159snvs_rtc_enable(sc, false);160WR4(sc, SNVS_LPSRTCMR, (uint32_t)(sbt >> (SBT_LSB + 32)));161WR4(sc, SNVS_LPSRTCLR, (uint32_t)(sbt >> (SBT_LSB)));162snvs_rtc_enable(sc, true);163164clock_dbgprint_ts(sc->dev, CLOCK_DBG_WRITE, ts);165166return (0);167}168169static int170snvs_probe(device_t dev)171{172173if (!ofw_bus_status_okay(dev))174return (ENXIO);175176if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)177return (ENXIO);178179device_set_desc(dev, "i.MX6 SNVS RTC");180return (BUS_PROBE_DEFAULT);181}182183static int184snvs_attach(device_t dev)185{186struct snvs_softc *sc;187188sc = device_get_softc(dev);189sc->dev = dev;190191if (syscon_get_handle_default(sc->dev, &sc->syscon) != 0) {192device_printf(sc->dev, "Cannot get syscon handle\n");193return (ENXIO);194}195196clock_register(sc->dev, RTC_RESOLUTION_US);197198return (0);199}200201static int202snvs_detach(device_t dev)203{204struct snvs_softc *sc;205206sc = device_get_softc(dev);207clock_unregister(sc->dev);208return (0);209}210211static device_method_t snvs_methods[] = {212DEVMETHOD(device_probe, snvs_probe),213DEVMETHOD(device_attach, snvs_attach),214DEVMETHOD(device_detach, snvs_detach),215216/* clock_if methods */217DEVMETHOD(clock_gettime, snvs_gettime),218DEVMETHOD(clock_settime, snvs_settime),219220DEVMETHOD_END221};222223static driver_t snvs_driver = {224"snvs",225snvs_methods,226sizeof(struct snvs_softc),227};228229DRIVER_MODULE(snvs, simplebus, snvs_driver, 0, 0);230SIMPLEBUS_PNP_INFO(compat_data);231232233