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/mv_thermal.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
    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  * $FreeBSD$
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/rman.h>
   40 #include <sys/lock.h>
   41 #include <sys/mutex.h>
   42 #include <sys/sysctl.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/resource.h>
   46 #include <machine/intr.h>
   47 #include <dev/extres/syscon/syscon.h>
   48 
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 
   52 #include "syscon_if.h"
   53 
   54 #define CONTROL0                0       /* Offset in config->regs[] array */
   55 #define  CONTROL0_TSEN_START    (1 << 0)
   56 #define  CONTROL0_TSEN_RESET    (1 << 1)
   57 #define  CONTROL0_TSEN_EN       (1 << 2)
   58 #define  CONTROL0_CHANNEL_SHIFT 13
   59 #define  CONTROL0_CHANNEL_MASK  0xF
   60 #define  CONTROL0_OSR_SHIFT     24
   61 #define  CONTROL0_OSR_MAX       3       /* OSR = 512 * 4uS = ~2mS */
   62 #define  CONTROL0_MODE_SHIFT    30
   63 #define  CONTROL0_MODE_EXTERNAL 0x2
   64 #define  CONTROL0_MODE_MASK     0x3
   65 
   66 #define CONTROL1                1       /* Offset in config->regs[] array */
   67 /* This doesn't seems to work */
   68 #define CONTROL1_TSEN_SENS_SHIFT        21
   69 #define CONTROL1_TSEN_SENS_MASK         0x7
   70 
   71 #define STATUS                  2       /* Offset in config->regs[] array */
   72 #define STATUS_TEMP_MASK        0x3FF
   73 
   74 enum mv_thermal_type {
   75         MV_AP806 = 1,
   76         MV_CP110,
   77 };
   78 
   79 struct mv_thermal_config {
   80         enum mv_thermal_type    type;
   81         int                     regs[3];
   82         int                     ncpus;
   83         int64_t                 calib_mul;
   84         int64_t                 calib_add;
   85         int64_t                 calib_div;
   86         uint32_t                valid_mask;
   87         bool                    signed_value;
   88 };
   89 
   90 struct mv_thermal_softc {
   91         device_t                dev;
   92         struct syscon           *syscon;
   93         struct mtx              mtx;
   94 
   95         struct mv_thermal_config *config;
   96         int                     cur_sensor;
   97 };
   98 
   99 static struct mv_thermal_config mv_ap806_config = {
  100         .type = MV_AP806,
  101         .regs = {0x84, 0x88, 0x8C},
  102         .ncpus = 4,
  103         .calib_mul = 423,
  104         .calib_add = -150000,
  105         .calib_div = 100,
  106         .valid_mask = (1 << 16),
  107         .signed_value = true,
  108 };
  109 
  110 static struct mv_thermal_config mv_cp110_config = {
  111         .type = MV_CP110,
  112         .regs = {0x70, 0x74, 0x78},
  113         .calib_mul = 2000096,
  114         .calib_add = 1172499100,
  115         .calib_div = 420100,
  116         .valid_mask = (1 << 10),
  117         .signed_value = false,
  118 };
  119 
  120 static struct ofw_compat_data compat_data[] = {
  121         {"marvell,armada-ap806-thermal", (uintptr_t) &mv_ap806_config},
  122         {"marvell,armada-cp110-thermal", (uintptr_t) &mv_cp110_config},
  123         {NULL,             0}
  124 };
  125 
  126 #define RD4(sc, reg)                                                    \
  127     SYSCON_READ_4((sc)->syscon, sc->config->regs[reg])
  128 #define WR4(sc, reg, val)                                               \
  129         SYSCON_WRITE_4((sc)->syscon, sc->config->regs[reg], (val))
  130 
  131 static inline int32_t sign_extend(uint32_t value, int index)
  132 {
  133         uint8_t shift;
  134 
  135         shift = 31 - index;
  136         return ((int32_t)(value << shift) >> shift);
  137 }
  138 
  139 static int
  140 mv_thermal_wait_sensor(struct mv_thermal_softc *sc)
  141 {
  142         uint32_t reg;
  143         uint32_t timeout;
  144 
  145         timeout = 100000;
  146         while (--timeout > 0) {
  147                 reg = RD4(sc, STATUS);
  148                 if ((reg & sc->config->valid_mask) == sc->config->valid_mask)
  149                         break;
  150                 DELAY(100);
  151         }
  152         if (timeout == 0) {
  153                 return (ETIMEDOUT);
  154         }
  155 
  156         return (0);
  157 }
  158 
  159 static int
  160 mv_thermal_select_sensor(struct mv_thermal_softc *sc, int sensor)
  161 {
  162         uint32_t reg;
  163 
  164         if (sc->cur_sensor == sensor)
  165                 return (0);
  166 
  167         /* Stop the current reading and reset the module */
  168         reg = RD4(sc, CONTROL0);
  169         reg &= ~(CONTROL0_TSEN_START | CONTROL0_TSEN_EN);
  170         WR4(sc, CONTROL0, reg);
  171 
  172         /* Switch to the selected sensor */
  173         /*
  174          * NOTE : Datasheet says to use CONTROL1 for selecting
  175          * but when doing so the sensors >0 are never ready
  176          * Do what Linux does using undocumented bits in CONTROL0
  177          */
  178         /* This reset automatically to the sensor 0 */
  179         reg &= ~(CONTROL0_MODE_MASK << CONTROL0_MODE_SHIFT);
  180         if (sensor) {
  181                 /* Select external sensor */
  182                 reg |= CONTROL0_MODE_EXTERNAL << CONTROL0_MODE_SHIFT;
  183                 reg &= ~(CONTROL0_CHANNEL_MASK << CONTROL0_CHANNEL_SHIFT);
  184                 reg |= (sensor - 1) << CONTROL0_CHANNEL_SHIFT;
  185         }
  186         WR4(sc, CONTROL0, reg);
  187         sc->cur_sensor = sensor;
  188 
  189         /* Start the reading */
  190         reg = RD4(sc, CONTROL0);
  191         reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN;
  192         WR4(sc, CONTROL0, reg);
  193 
  194         return (mv_thermal_wait_sensor(sc));
  195 }
  196 
  197 static int
  198 mv_thermal_read_sensor(struct mv_thermal_softc *sc, int sensor, int *temp)
  199 {
  200         uint32_t reg;
  201         int64_t sample, rv;
  202 
  203         rv = mv_thermal_select_sensor(sc, sensor);
  204         if (rv != 0)
  205                 return (rv);
  206 
  207         reg = RD4(sc, STATUS) & STATUS_TEMP_MASK;
  208 
  209         if (sc->config->signed_value)
  210                 sample = sign_extend(reg, fls(STATUS_TEMP_MASK) - 1);
  211         else
  212                 sample = reg;
  213 
  214         *temp = ((sample * sc->config->calib_mul) - sc->config->calib_add) /
  215                 sc->config->calib_div;
  216 
  217         return (0);
  218 }
  219 
  220 static int
  221 ap806_init(struct mv_thermal_softc *sc)
  222 {
  223         uint32_t reg;
  224 
  225         /* Start the temp capture/conversion */
  226         reg = RD4(sc, CONTROL0);
  227         reg &= ~CONTROL0_TSEN_RESET;
  228         reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN;
  229 
  230         /* Sample every ~2ms */
  231         reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT;
  232 
  233         WR4(sc, CONTROL0, reg);
  234 
  235         /* Since we just started the module wait for the sensor to be ready */
  236         mv_thermal_wait_sensor(sc);
  237 
  238         return (0);
  239 }
  240 
  241 static int
  242 cp110_init(struct mv_thermal_softc *sc)
  243 {
  244         uint32_t reg;
  245 
  246         reg = RD4(sc, CONTROL1);
  247         reg &= (1 << 7);
  248         reg |= (1 << 8);
  249         WR4(sc, CONTROL1, reg);
  250 
  251         /* Sample every ~2ms */
  252         reg = RD4(sc, CONTROL0);
  253         reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT;
  254         WR4(sc, CONTROL0, reg);
  255 
  256         return (0);
  257 }
  258 
  259 static int
  260 mv_thermal_sysctl(SYSCTL_HANDLER_ARGS)
  261 {
  262         struct mv_thermal_softc *sc;
  263         device_t dev = arg1;
  264         int sensor = arg2;
  265         int val = 0;
  266 
  267         sc = device_get_softc(dev);
  268         mtx_lock(&(sc)->mtx);
  269 
  270         if (mv_thermal_read_sensor(sc, sensor, &val) == 0) {
  271                 /* Convert to Kelvin */
  272                 val = val + 2732;
  273         } else {
  274                 device_printf(dev, "Timeout waiting for sensor\n");
  275         }
  276 
  277         mtx_unlock(&(sc)->mtx);
  278         return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
  279 }
  280 
  281 static int
  282 mv_thermal_probe(device_t dev)
  283 {
  284 
  285         if (!ofw_bus_status_okay(dev))
  286                 return (ENXIO);
  287 
  288         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  289                 return (ENXIO);
  290 
  291         device_set_desc(dev, "Marvell Thermal Sensor Controller");
  292         return (BUS_PROBE_DEFAULT);
  293 }
  294 
  295 static int
  296 mv_thermal_attach(device_t dev)
  297 {
  298         struct mv_thermal_softc *sc;
  299         struct sysctl_ctx_list *ctx;
  300         struct sysctl_oid_list *oid;
  301         char name[255];
  302         char desc[255];
  303         int i;
  304 
  305         sc = device_get_softc(dev);
  306         sc->dev = dev;
  307 
  308         sc->config = (struct mv_thermal_config *)
  309             ofw_bus_search_compatible(dev, compat_data)->ocd_data;
  310 
  311         mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  312 
  313         if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
  314             sc->syscon == NULL) {
  315                 device_printf(dev, "cannot get syscon for device\n");
  316                 return (ENXIO);
  317         }
  318 
  319         sc->cur_sensor = -1;
  320         switch (sc->config->type) {
  321         case MV_AP806:
  322                 ap806_init(sc);
  323                 break;
  324         case MV_CP110:
  325                 cp110_init(sc);
  326                 break;
  327         }
  328 
  329         ctx = device_get_sysctl_ctx(dev);
  330         oid = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
  331         /* There is always at least one sensor */
  332         SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, "internal",
  333             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
  334             dev, 0, mv_thermal_sysctl,
  335             "IK",
  336             "Internal Temperature");
  337 
  338         for (i = 0; i < sc->config->ncpus; i++) {
  339                 snprintf(name, sizeof(name), "cpu%d", i);
  340                 snprintf(desc, sizeof(desc), "CPU%d Temperature", i);
  341                 SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, name,
  342                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
  343                     dev, i + 1, mv_thermal_sysctl,
  344                     "IK",
  345                     desc);
  346         }
  347 
  348         return (0);
  349 }
  350 
  351 static int
  352 mv_thermal_detach(device_t dev)
  353 {
  354         return (0);
  355 }
  356 
  357 static device_method_t mv_thermal_methods[] = {
  358         /* Device interface */
  359         DEVMETHOD(device_probe,         mv_thermal_probe),
  360         DEVMETHOD(device_attach,        mv_thermal_attach),
  361         DEVMETHOD(device_detach,        mv_thermal_detach),
  362 
  363         DEVMETHOD_END
  364 };
  365 
  366 static driver_t mv_thermal_driver = {
  367         "mv_thermal",
  368         mv_thermal_methods,
  369         sizeof(struct mv_thermal_softc),
  370 };
  371 
  372 DRIVER_MODULE(mv_thermal, simplebus, mv_thermal_driver, 0, 0);

Cache object: 6033adb1b77f5c877681f28dbbace8a0


[ 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.