The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/mv/armada38x/armada38x_rtc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015 Semihalf.
    3  * Copyright (c) 2015 Stormshield.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/lock.h>
   34 #include <sys/time.h>
   35 #include <sys/proc.h>
   36 #include <sys/conf.h>
   37 #include <sys/rman.h>
   38 #include <sys/clock.h>
   39 #include <sys/systm.h>
   40 #include <sys/mutex.h>
   41 #include <sys/types.h>
   42 #include <sys/kernel.h>
   43 #include <sys/module.h>
   44 #include <sys/resource.h>
   45 
   46 #include <machine/bus.h>
   47 #include <machine/resource.h>
   48 
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 
   52 #include "clock_if.h"
   53 
   54 #define RTC_RES_US              1000000
   55 #define HALF_OF_SEC_NS          500000000
   56 
   57 #define RTC_STATUS              0x0
   58 #define RTC_TIME                0xC
   59 #define RTC_TEST_CONFIG         0x1C
   60 #define RTC_IRQ_1_CONFIG        0x4
   61 #define RTC_IRQ_2_CONFIG        0x8
   62 #define RTC_ALARM_1             0x10
   63 #define RTC_ALARM_2             0x14
   64 #define RTC_CLOCK_CORR          0x18
   65 
   66 #define RTC_NOMINAL_TIMING      0x2000
   67 #define RTC_NOMINAL_TIMING_MASK 0x7fff
   68 
   69 #define RTC_STATUS_ALARM1_MASK  0x1
   70 #define RTC_STATUS_ALARM2_MASK  0x2
   71 
   72 #define MV_RTC_LOCK(sc)         mtx_lock_spin(&(sc)->mutex)
   73 #define MV_RTC_UNLOCK(sc)       mtx_unlock_spin(&(sc)->mutex)
   74 
   75 #define A38X_RTC_BRIDGE_TIMING_CTRL             0x0
   76 #define A38X_RTC_WRCLK_PERIOD_SHIFT             0
   77 #define A38X_RTC_WRCLK_PERIOD_MASK              0x00000003FF
   78 #define A38X_RTC_WRCLK_PERIOD_MAX               0x3FF
   79 #define A38X_RTC_READ_OUTPUT_DELAY_SHIFT        26
   80 #define A38X_RTC_READ_OUTPUT_DELAY_MASK         0x007C000000
   81 #define A38X_RTC_READ_OUTPUT_DELAY_MAX          0x1F
   82 
   83 #define A8K_RTC_BRIDGE_TIMING_CTRL0             0x0
   84 #define A8K_RTC_WRCLK_PERIOD_SHIFT              0
   85 #define A8K_RTC_WRCLK_PERIOD_MASK               0x000000FFFF
   86 #define A8K_RTC_WRCLK_PERIOD_VAL                0x3FF
   87 #define A8K_RTC_WRCLK_SETUP_SHIFT               16
   88 #define A8K_RTC_WRCLK_SETUP_MASK                0x00FFFF0000
   89 #define A8K_RTC_WRCLK_SETUP_VAL                 29
   90 #define A8K_RTC_BRIDGE_TIMING_CTRL1             0x4
   91 #define A8K_RTC_READ_OUTPUT_DELAY_SHIFT         0
   92 #define A8K_RTC_READ_OUTPUT_DELAY_MASK          0x000000FFFF
   93 #define A8K_RTC_READ_OUTPUT_DELAY_VAL           0x3F
   94 
   95 #define RTC_RES         0
   96 #define RTC_SOC_RES     1
   97 
   98 static struct resource_spec res_spec[] = {
   99         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
  100         { SYS_RES_MEMORY,       1,      RF_ACTIVE },
  101         { -1, 0 }
  102 };
  103 
  104 struct mv_rtc_softc {
  105         device_t        dev;
  106         struct resource *res[2];
  107         struct mtx      mutex;
  108         int             rtc_type;
  109 };
  110 
  111 static int mv_rtc_probe(device_t dev);
  112 static int mv_rtc_attach(device_t dev);
  113 static int mv_rtc_detach(device_t dev);
  114 
  115 static int mv_rtc_gettime(device_t dev, struct timespec *ts);
  116 static int mv_rtc_settime(device_t dev, struct timespec *ts);
  117 
  118 static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc,
  119     bus_size_t off);
  120 static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
  121     uint32_t val);
  122 static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc);
  123 static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc);
  124 
  125 static device_method_t mv_rtc_methods[] = {
  126         DEVMETHOD(device_probe,         mv_rtc_probe),
  127         DEVMETHOD(device_attach,        mv_rtc_attach),
  128         DEVMETHOD(device_detach,        mv_rtc_detach),
  129 
  130         DEVMETHOD(clock_gettime,        mv_rtc_gettime),
  131         DEVMETHOD(clock_settime,        mv_rtc_settime),
  132 
  133         { 0, 0 },
  134 };
  135 
  136 static driver_t mv_rtc_driver = {
  137         "rtc",
  138         mv_rtc_methods,
  139         sizeof(struct mv_rtc_softc),
  140 };
  141 
  142 #define  RTC_A38X       1
  143 #define  RTC_A8K        2
  144 
  145 static struct ofw_compat_data mv_rtc_compat[] = {
  146         {"marvell,armada-380-rtc",      RTC_A38X},
  147         {"marvell,armada-8k-rtc",       RTC_A8K},
  148         {NULL,                          0},
  149 };
  150 
  151 DRIVER_MODULE(a38x_rtc, simplebus, mv_rtc_driver, 0, 0);
  152 
  153 static void
  154 mv_rtc_reset(device_t dev)
  155 {
  156         struct mv_rtc_softc *sc;
  157 
  158         sc = device_get_softc(dev);
  159 
  160         /* Reset Test register */
  161         mv_rtc_reg_write(sc, RTC_TEST_CONFIG, 0);
  162         DELAY(500000);
  163 
  164         /* Reset Time register */
  165         mv_rtc_reg_write(sc, RTC_TIME, 0);
  166         DELAY(62);
  167 
  168         /* Reset Status register */
  169         mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
  170         DELAY(62);
  171 
  172         /* Turn off Int1 and Int2 sources & clear the Alarm count */
  173         mv_rtc_reg_write(sc, RTC_IRQ_1_CONFIG, 0);
  174         mv_rtc_reg_write(sc, RTC_IRQ_2_CONFIG, 0);
  175         mv_rtc_reg_write(sc, RTC_ALARM_1, 0);
  176         mv_rtc_reg_write(sc, RTC_ALARM_2, 0);
  177 
  178         /* Setup nominal register access timing */
  179         mv_rtc_reg_write(sc, RTC_CLOCK_CORR, RTC_NOMINAL_TIMING);
  180 
  181         /* Reset Time register */
  182         mv_rtc_reg_write(sc, RTC_TIME, 0);
  183         DELAY(10);
  184 
  185         /* Reset Status register */
  186         mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
  187         DELAY(50);
  188 }
  189 
  190 static int
  191 mv_rtc_probe(device_t dev)
  192 {
  193 
  194         if (!ofw_bus_status_okay(dev))
  195                 return (ENXIO);
  196 
  197         if (!ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data)
  198                 return (ENXIO);
  199 
  200         device_set_desc(dev, "Marvell Integrated RTC");
  201 
  202         return (BUS_PROBE_DEFAULT);
  203 }
  204 
  205 static int
  206 mv_rtc_attach(device_t dev)
  207 {
  208         struct mv_rtc_softc *sc;
  209         int ret;
  210 
  211         sc = device_get_softc(dev);
  212         sc->dev = dev;
  213         sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data;
  214 
  215         mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
  216 
  217         ret = bus_alloc_resources(dev, res_spec, sc->res);
  218         if (ret != 0) {
  219                 device_printf(dev, "could not allocate resources\n");
  220                 mtx_destroy(&sc->mutex);
  221                 return (ENXIO);
  222         }
  223 
  224         switch (sc->rtc_type) {
  225         case RTC_A38X:
  226                 mv_rtc_configure_bus_a38x(sc);
  227                 break;
  228         case RTC_A8K:
  229                 mv_rtc_configure_bus_a8k(sc);
  230                 break;
  231         default:
  232                 panic("Unknown RTC type: %d", sc->rtc_type);
  233         }
  234         clock_register(dev, RTC_RES_US);
  235 
  236         return (0);
  237 }
  238 
  239 static int
  240 mv_rtc_detach(device_t dev)
  241 {
  242         struct mv_rtc_softc *sc;
  243 
  244         sc = device_get_softc(dev);
  245 
  246         mtx_destroy(&sc->mutex);
  247 
  248         bus_release_resources(dev, res_spec, sc->res);
  249 
  250         return (0);
  251 }
  252 
  253 static int
  254 mv_rtc_gettime(device_t dev, struct timespec *ts)
  255 {
  256         struct mv_rtc_softc *sc;
  257         uint32_t val, val_check;
  258 
  259         sc = device_get_softc(dev);
  260 
  261         MV_RTC_LOCK(sc);
  262         /*
  263          * According to HW Errata, if more than one second is detected
  264          * between two time reads, then at least one of the reads gave
  265          * an invalid value.
  266          */
  267         do {
  268                 val = mv_rtc_reg_read(sc, RTC_TIME);
  269                 DELAY(100);
  270                 val_check = mv_rtc_reg_read(sc, RTC_TIME);
  271         } while ((val_check - val) > 1);
  272 
  273         MV_RTC_UNLOCK(sc);
  274 
  275         ts->tv_sec = val_check;
  276         /* RTC resolution is 1 sec */
  277         ts->tv_nsec = 0;
  278 
  279         return (0);
  280 }
  281 
  282 static int
  283 mv_rtc_settime(device_t dev, struct timespec *ts)
  284 {
  285         struct mv_rtc_softc *sc;
  286 
  287         sc = device_get_softc(dev);
  288 
  289         /* RTC resolution is 1 sec */
  290         if (ts->tv_nsec >= HALF_OF_SEC_NS)
  291                 ts->tv_sec++;
  292         ts->tv_nsec = 0;
  293 
  294         MV_RTC_LOCK(sc);
  295 
  296         if ((mv_rtc_reg_read(sc, RTC_CLOCK_CORR) & RTC_NOMINAL_TIMING_MASK) !=
  297             RTC_NOMINAL_TIMING) {
  298                 /* RTC was not resetted yet */
  299                 mv_rtc_reset(dev);
  300         }
  301 
  302         /*
  303          * According to errata FE-3124064, Write to RTC TIME register
  304          * may fail. As a workaround, before writing to RTC TIME register,
  305          * issue a dummy write of 0x0 twice to RTC Status register.
  306          */
  307         mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
  308         mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
  309         mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec);
  310         MV_RTC_UNLOCK(sc);
  311 
  312         return (0);
  313 }
  314 
  315 static inline uint32_t
  316 mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off)
  317 {
  318 
  319         return (bus_read_4(sc->res[RTC_RES], off));
  320 }
  321 
  322 /*
  323  * According to the datasheet, the OS should wait 5us after every
  324  * register write to the RTC hard macro so that the required update
  325  * can occur without holding off the system bus
  326  */
  327 static inline int
  328 mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val)
  329 {
  330 
  331         bus_write_4(sc->res[RTC_RES], off, val);
  332         DELAY(5);
  333 
  334         return (0);
  335 }
  336 
  337 static inline void
  338 mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc)
  339 {
  340         int val;
  341 
  342         val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL);
  343         val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK);
  344         val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT;
  345         val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << A38X_RTC_READ_OUTPUT_DELAY_SHIFT;
  346         bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val);
  347 }
  348 
  349 static inline void
  350 mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc)
  351 {
  352         int val;
  353 
  354         val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
  355         val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK);
  356         val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT;
  357         val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT;
  358         bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
  359 
  360         val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
  361         val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK;
  362         val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT;
  363         bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
  364 }

Cache object: ad38c30ddc8334a7d78eb2c0fd78e763


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.