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/allwinner/aw_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  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  *
   25  * $FreeBSD$
   26  */
   27 
   28 /*
   29  * Allwinner thermal sensor controller
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/eventhandler.h>
   38 #include <sys/bus.h>
   39 #include <sys/rman.h>
   40 #include <sys/kernel.h>
   41 #include <sys/sysctl.h>
   42 #include <sys/reboot.h>
   43 #include <sys/module.h>
   44 #include <sys/cpu.h>
   45 #include <sys/taskqueue.h>
   46 #include <machine/bus.h>
   47 
   48 #include <dev/ofw/ofw_bus.h>
   49 #include <dev/ofw/ofw_bus_subr.h>
   50 
   51 #include <dev/extres/clk/clk.h>
   52 #include <dev/extres/hwreset/hwreset.h>
   53 #include <dev/extres/nvmem/nvmem.h>
   54 
   55 #include <arm/allwinner/aw_sid.h>
   56 
   57 #include "cpufreq_if.h"
   58 #include "nvmem_if.h"
   59 
   60 #define THS_CTRL0               0x00
   61 #define THS_CTRL1               0x04
   62 #define  ADC_CALI_EN            (1 << 17)
   63 #define THS_CTRL2               0x40
   64 #define  SENSOR_ACQ1_SHIFT      16
   65 #define  SENSOR2_EN             (1 << 2)
   66 #define  SENSOR1_EN             (1 << 1)
   67 #define  SENSOR0_EN             (1 << 0)
   68 #define THS_INTC                0x44
   69 #define  THS_THERMAL_PER_SHIFT  12
   70 #define THS_INTS                0x48
   71 #define  THS2_DATA_IRQ_STS      (1 << 10)
   72 #define  THS1_DATA_IRQ_STS      (1 << 9)
   73 #define  THS0_DATA_IRQ_STS      (1 << 8)
   74 #define  SHUT_INT2_STS          (1 << 6)
   75 #define  SHUT_INT1_STS          (1 << 5)
   76 #define  SHUT_INT0_STS          (1 << 4)
   77 #define  ALARM_INT2_STS         (1 << 2)
   78 #define  ALARM_INT1_STS         (1 << 1)
   79 #define  ALARM_INT0_STS         (1 << 0)
   80 #define THS_ALARM0_CTRL         0x50
   81 #define  ALARM_T_HOT_MASK       0xfff
   82 #define  ALARM_T_HOT_SHIFT      16
   83 #define  ALARM_T_HYST_MASK      0xfff
   84 #define  ALARM_T_HYST_SHIFT     0
   85 #define THS_SHUTDOWN0_CTRL      0x60
   86 #define  SHUT_T_HOT_MASK        0xfff
   87 #define  SHUT_T_HOT_SHIFT       16
   88 #define THS_FILTER              0x70
   89 #define THS_CALIB0              0x74
   90 #define THS_CALIB1              0x78
   91 #define THS_DATA0               0x80
   92 #define THS_DATA1               0x84
   93 #define THS_DATA2               0x88
   94 #define  DATA_MASK              0xfff
   95 
   96 #define A83T_CLK_RATE           24000000
   97 #define A83T_ADC_ACQUIRE_TIME   23      /* 24Mhz/(23 + 1) = 1us */
   98 #define A83T_THERMAL_PER        1       /* 4096 * (1 + 1) / 24Mhz = 341 us */
   99 #define A83T_FILTER             0x5     /* Filter enabled, avg of 4 */
  100 #define A83T_TEMP_BASE          2719000
  101 #define A83T_TEMP_MUL           1000
  102 #define A83T_TEMP_DIV           14186
  103 
  104 #define A64_CLK_RATE            4000000
  105 #define A64_ADC_ACQUIRE_TIME    400     /* 4Mhz/(400 + 1) = 100 us */
  106 #define A64_THERMAL_PER         24      /* 4096 * (24 + 1) / 4Mhz = 25.6 ms */
  107 #define A64_FILTER              0x6     /* Filter enabled, avg of 8 */
  108 #define A64_TEMP_BASE           2170000
  109 #define A64_TEMP_MUL            1000
  110 #define A64_TEMP_DIV            8560
  111 
  112 #define H3_CLK_RATE             4000000
  113 #define H3_ADC_ACQUIRE_TIME     0x3f
  114 #define H3_THERMAL_PER          401
  115 #define H3_FILTER               0x6     /* Filter enabled, avg of 8 */
  116 #define H3_TEMP_BASE            217
  117 #define H3_TEMP_MUL             1000
  118 #define H3_TEMP_DIV             8253
  119 #define H3_TEMP_MINUS           1794000
  120 #define H3_INIT_ALARM           90      /* degC */
  121 #define H3_INIT_SHUT            105     /* degC */
  122 
  123 #define H5_CLK_RATE             24000000
  124 #define H5_ADC_ACQUIRE_TIME     479     /* 24Mhz/479 = 20us */
  125 #define H5_THERMAL_PER          58      /* 4096 * (58 + 1) / 24Mhz = 10ms */
  126 #define H5_FILTER               0x6     /* Filter enabled, avg of 8 */
  127 #define H5_TEMP_BASE            233832448
  128 #define H5_TEMP_MUL             124885
  129 #define H5_TEMP_DIV             20
  130 #define H5_TEMP_BASE_CPU        271581184
  131 #define H5_TEMP_MUL_CPU         152253
  132 #define H5_TEMP_BASE_GPU        289406976
  133 #define H5_TEMP_MUL_GPU         166724
  134 #define H5_INIT_CPU_ALARM       80      /* degC */
  135 #define H5_INIT_CPU_SHUT        96      /* degC */
  136 #define H5_INIT_GPU_ALARM       84      /* degC */
  137 #define H5_INIT_GPU_SHUT        100     /* degC */
  138 
  139 #define TEMP_C_TO_K             273
  140 #define SENSOR_ENABLE_ALL       (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
  141 #define SHUT_INT_ALL            (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS)
  142 #define ALARM_INT_ALL           (ALARM_INT0_STS)
  143 
  144 #define MAX_SENSORS     3
  145 #define MAX_CF_LEVELS   64
  146 
  147 #define THROTTLE_ENABLE_DEFAULT 1
  148 
  149 /* Enable thermal throttling */
  150 static int aw_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT;
  151 TUNABLE_INT("hw.aw_thermal.throttle_enable", &aw_thermal_throttle_enable);
  152 
  153 struct aw_thermal_sensor {
  154         const char              *name;
  155         const char              *desc;
  156         int                     init_alarm;
  157         int                     init_shut;
  158 };
  159 
  160 struct aw_thermal_config {
  161         struct aw_thermal_sensor        sensors[MAX_SENSORS];
  162         int                             nsensors;
  163         uint64_t                        clk_rate;
  164         uint32_t                        adc_acquire_time;
  165         int                             adc_cali_en;
  166         uint32_t                        filter;
  167         uint32_t                        thermal_per;
  168         int                             (*to_temp)(uint32_t, int);
  169         uint32_t                        (*to_reg)(int, int);
  170         int                             temp_base;
  171         int                             temp_mul;
  172         int                             temp_div;
  173         int                             calib0, calib1;
  174         uint32_t                        calib0_mask, calib1_mask;
  175 };
  176 
  177 static int
  178 a83t_to_temp(uint32_t val, int sensor)
  179 {
  180         return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV);
  181 }
  182 
  183 static const struct aw_thermal_config a83t_config = {
  184         .nsensors = 3,
  185         .sensors = {
  186                 [0] = {
  187                         .name = "cluster0",
  188                         .desc = "CPU cluster 0 temperature",
  189                 },
  190                 [1] = {
  191                         .name = "cluster1",
  192                         .desc = "CPU cluster 1 temperature",
  193                 },
  194                 [2] = {
  195                         .name = "gpu",
  196                         .desc = "GPU temperature",
  197                 },
  198         },
  199         .clk_rate = A83T_CLK_RATE,
  200         .adc_acquire_time = A83T_ADC_ACQUIRE_TIME,
  201         .adc_cali_en = 1,
  202         .filter = A83T_FILTER,
  203         .thermal_per = A83T_THERMAL_PER,
  204         .to_temp = a83t_to_temp,
  205         .calib0_mask = 0xffffffff,
  206         .calib1_mask = 0xffff,
  207 };
  208 
  209 static int
  210 a64_to_temp(uint32_t val, int sensor)
  211 {
  212         return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV);
  213 }
  214 
  215 static const struct aw_thermal_config a64_config = {
  216         .nsensors = 3,
  217         .sensors = {
  218                 [0] = {
  219                         .name = "cpu",
  220                         .desc = "CPU temperature",
  221                 },
  222                 [1] = {
  223                         .name = "gpu1",
  224                         .desc = "GPU temperature 1",
  225                 },
  226                 [2] = {
  227                         .name = "gpu2",
  228                         .desc = "GPU temperature 2",
  229                 },
  230         },
  231         .clk_rate = A64_CLK_RATE,
  232         .adc_acquire_time = A64_ADC_ACQUIRE_TIME,
  233         .adc_cali_en = 1,
  234         .filter = A64_FILTER,
  235         .thermal_per = A64_THERMAL_PER,
  236         .to_temp = a64_to_temp,
  237         .calib0_mask = 0xffffffff,
  238         .calib1_mask = 0xffff,
  239 };
  240 
  241 static int
  242 h3_to_temp(uint32_t val, int sensor)
  243 {
  244         return (H3_TEMP_BASE - ((val * H3_TEMP_MUL) / H3_TEMP_DIV));
  245 }
  246 
  247 static uint32_t
  248 h3_to_reg(int val, int sensor)
  249 {
  250         return ((H3_TEMP_MINUS - (val * H3_TEMP_DIV)) / H3_TEMP_MUL);
  251 }
  252 
  253 static const struct aw_thermal_config h3_config = {
  254         .nsensors = 1,
  255         .sensors = {
  256                 [0] = {
  257                         .name = "cpu",
  258                         .desc = "CPU temperature",
  259                         .init_alarm = H3_INIT_ALARM,
  260                         .init_shut = H3_INIT_SHUT,
  261                 },
  262         },
  263         .clk_rate = H3_CLK_RATE,
  264         .adc_acquire_time = H3_ADC_ACQUIRE_TIME,
  265         .adc_cali_en = 1,
  266         .filter = H3_FILTER,
  267         .thermal_per = H3_THERMAL_PER,
  268         .to_temp = h3_to_temp,
  269         .to_reg = h3_to_reg,
  270         .calib0_mask = 0xffffffff,
  271 };
  272 
  273 static int
  274 h5_to_temp(uint32_t val, int sensor)
  275 {
  276         int tmp;
  277 
  278         /* Temp is lower than 70 degrees */
  279         if (val > 0x500) {
  280                 tmp = H5_TEMP_BASE - (val * H5_TEMP_MUL);
  281                 tmp >>= H5_TEMP_DIV;
  282                 return (tmp);
  283         }
  284 
  285         if (sensor == 0)
  286                 tmp = H5_TEMP_BASE_CPU - (val * H5_TEMP_MUL_CPU);
  287         else if (sensor == 1)
  288                 tmp = H5_TEMP_BASE_GPU - (val * H5_TEMP_MUL_GPU);
  289         else {
  290                 printf("Unknown sensor %d\n", sensor);
  291                 return (val);
  292         }
  293 
  294         tmp >>= H5_TEMP_DIV;
  295         return (tmp);
  296 }
  297 
  298 static uint32_t
  299 h5_to_reg(int val, int sensor)
  300 {
  301         int tmp;
  302 
  303         if (val < 70) {
  304                 tmp = H5_TEMP_BASE - (val << H5_TEMP_DIV);
  305                 tmp /= H5_TEMP_MUL;
  306         } else {
  307                 if (sensor == 0) {
  308                         tmp = H5_TEMP_BASE_CPU - (val << H5_TEMP_DIV);
  309                         tmp /= H5_TEMP_MUL_CPU;
  310                 } else if (sensor == 1) {
  311                         tmp = H5_TEMP_BASE_GPU - (val << H5_TEMP_DIV);
  312                         tmp /= H5_TEMP_MUL_GPU;
  313                 } else {
  314                         printf("Unknown sensor %d\n", sensor);
  315                         return (val);
  316                 }
  317         }
  318 
  319         return ((uint32_t)tmp);
  320 }
  321 
  322 static const struct aw_thermal_config h5_config = {
  323         .nsensors = 2,
  324         .sensors = {
  325                 [0] = {
  326                         .name = "cpu",
  327                         .desc = "CPU temperature",
  328                         .init_alarm = H5_INIT_CPU_ALARM,
  329                         .init_shut = H5_INIT_CPU_SHUT,
  330                 },
  331                 [1] = {
  332                         .name = "gpu",
  333                         .desc = "GPU temperature",
  334                         .init_alarm = H5_INIT_GPU_ALARM,
  335                         .init_shut = H5_INIT_GPU_SHUT,
  336                 },
  337         },
  338         .clk_rate = H5_CLK_RATE,
  339         .adc_acquire_time = H5_ADC_ACQUIRE_TIME,
  340         .filter = H5_FILTER,
  341         .thermal_per = H5_THERMAL_PER,
  342         .to_temp = h5_to_temp,
  343         .to_reg = h5_to_reg,
  344         .calib0_mask = 0xffffffff,
  345 };
  346 
  347 static struct ofw_compat_data compat_data[] = {
  348         { "allwinner,sun8i-a83t-ths",   (uintptr_t)&a83t_config },
  349         { "allwinner,sun8i-h3-ths",     (uintptr_t)&h3_config },
  350         { "allwinner,sun50i-a64-ths",   (uintptr_t)&a64_config },
  351         { "allwinner,sun50i-h5-ths",    (uintptr_t)&h5_config },
  352         { NULL,                         (uintptr_t)NULL }
  353 };
  354 
  355 #define THS_CONF(d)             \
  356         (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
  357 
  358 struct aw_thermal_softc {
  359         device_t                        dev;
  360         struct resource                 *res[2];
  361         struct aw_thermal_config        *conf;
  362 
  363         struct task                     cf_task;
  364         int                             throttle;
  365         int                             min_freq;
  366         struct cf_level                 levels[MAX_CF_LEVELS];
  367         eventhandler_tag                cf_pre_tag;
  368 
  369         clk_t                           clk_apb;
  370         clk_t                           clk_ths;
  371 };
  372 
  373 static struct resource_spec aw_thermal_spec[] = {
  374         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
  375         { SYS_RES_IRQ,          0,      RF_ACTIVE },
  376         { -1, 0 }
  377 };
  378 
  379 #define RD4(sc, reg)            bus_read_4((sc)->res[0], (reg))
  380 #define WR4(sc, reg, val)       bus_write_4((sc)->res[0], (reg), (val))
  381 
  382 static int
  383 aw_thermal_init(struct aw_thermal_softc *sc)
  384 {
  385         phandle_t node;
  386         uint32_t calib[2];
  387         int error;
  388 
  389         node = ofw_bus_get_node(sc->dev);
  390         if (nvmem_get_cell_len(node, "calibration") > sizeof(calib)) {
  391                 device_printf(sc->dev, "calibration nvmem cell is too large\n");
  392                 return (ENXIO);
  393         }
  394         error = nvmem_read_cell_by_name(node, "calibration",
  395             (void *)&calib, nvmem_get_cell_len(node, "calibration"));
  396         /* Read calibration settings from EFUSE */
  397         if (error != 0) {
  398                 device_printf(sc->dev, "Cannot read THS efuse\n");
  399                 return (error);
  400         }
  401 
  402         calib[0] &= sc->conf->calib0_mask;
  403         calib[1] &= sc->conf->calib1_mask;
  404 
  405         /* Write calibration settings to thermal controller */
  406         if (calib[0] != 0)
  407                 WR4(sc, THS_CALIB0, calib[0]);
  408         if (calib[1] != 0)
  409                 WR4(sc, THS_CALIB1, calib[1]);
  410 
  411         /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
  412         WR4(sc, THS_CTRL1, ADC_CALI_EN);
  413         WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time);
  414         WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT);
  415 
  416         /* Set thermal period */
  417         WR4(sc, THS_INTC, sc->conf->thermal_per << THS_THERMAL_PER_SHIFT);
  418 
  419         /* Enable average filter */
  420         WR4(sc, THS_FILTER, sc->conf->filter);
  421 
  422         /* Enable interrupts */
  423         WR4(sc, THS_INTS, RD4(sc, THS_INTS));
  424         WR4(sc, THS_INTC, RD4(sc, THS_INTC) | SHUT_INT_ALL | ALARM_INT_ALL);
  425 
  426         /* Enable sensors */
  427         WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL);
  428 
  429         return (0);
  430 }
  431 
  432 static int
  433 aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor)
  434 {
  435         uint32_t val;
  436 
  437         val = RD4(sc, THS_DATA0 + (sensor * 4));
  438 
  439         return (sc->conf->to_temp(val, sensor));
  440 }
  441 
  442 static int
  443 aw_thermal_getshut(struct aw_thermal_softc *sc, int sensor)
  444 {
  445         uint32_t val;
  446 
  447         val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
  448         val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK;
  449 
  450         return (sc->conf->to_temp(val, sensor));
  451 }
  452 
  453 static void
  454 aw_thermal_setshut(struct aw_thermal_softc *sc, int sensor, int temp)
  455 {
  456         uint32_t val;
  457 
  458         val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
  459         val &= ~(SHUT_T_HOT_MASK << SHUT_T_HOT_SHIFT);
  460         val |= (sc->conf->to_reg(temp, sensor) << SHUT_T_HOT_SHIFT);
  461         WR4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4), val);
  462 }
  463 
  464 static int
  465 aw_thermal_gethyst(struct aw_thermal_softc *sc, int sensor)
  466 {
  467         uint32_t val;
  468 
  469         val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
  470         val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK;
  471 
  472         return (sc->conf->to_temp(val, sensor));
  473 }
  474 
  475 static int
  476 aw_thermal_getalarm(struct aw_thermal_softc *sc, int sensor)
  477 {
  478         uint32_t val;
  479 
  480         val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
  481         val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK;
  482 
  483         return (sc->conf->to_temp(val, sensor));
  484 }
  485 
  486 static void
  487 aw_thermal_setalarm(struct aw_thermal_softc *sc, int sensor, int temp)
  488 {
  489         uint32_t val;
  490 
  491         val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
  492         val &= ~(ALARM_T_HOT_MASK << ALARM_T_HOT_SHIFT);
  493         val |= (sc->conf->to_reg(temp, sensor) << ALARM_T_HOT_SHIFT);
  494         WR4(sc, THS_ALARM0_CTRL + (sensor * 4), val);
  495 }
  496 
  497 static int
  498 aw_thermal_sysctl(SYSCTL_HANDLER_ARGS)
  499 {
  500         struct aw_thermal_softc *sc;
  501         int sensor, val;
  502 
  503         sc = arg1;
  504         sensor = arg2;
  505 
  506         val = aw_thermal_gettemp(sc, sensor) + TEMP_C_TO_K;
  507 
  508         return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
  509 }
  510 
  511 static void
  512 aw_thermal_throttle(struct aw_thermal_softc *sc, int enable)
  513 {
  514         device_t cf_dev;
  515         int count, error;
  516 
  517         if (enable == sc->throttle)
  518                 return;
  519 
  520         if (enable != 0) {
  521                 /* Set the lowest available frequency */
  522                 cf_dev = devclass_get_device(devclass_find("cpufreq"), 0);
  523                 if (cf_dev == NULL)
  524                         return;
  525                 count = MAX_CF_LEVELS;
  526                 error = CPUFREQ_LEVELS(cf_dev, sc->levels, &count);
  527                 if (error != 0 || count == 0)
  528                         return;
  529                 sc->min_freq = sc->levels[count - 1].total_set.freq;
  530                 error = CPUFREQ_SET(cf_dev, &sc->levels[count - 1],
  531                     CPUFREQ_PRIO_USER);
  532                 if (error != 0)
  533                         return;
  534         }
  535 
  536         sc->throttle = enable;
  537 }
  538 
  539 static void
  540 aw_thermal_cf_task(void *arg, int pending)
  541 {
  542         struct aw_thermal_softc *sc;
  543 
  544         sc = arg;
  545 
  546         aw_thermal_throttle(sc, 1);
  547 }
  548 
  549 static void
  550 aw_thermal_cf_pre_change(void *arg, const struct cf_level *level, int *status)
  551 {
  552         struct aw_thermal_softc *sc;
  553         int temp_cur, temp_alarm;
  554 
  555         sc = arg;
  556 
  557         if (aw_thermal_throttle_enable == 0 || sc->throttle == 0 ||
  558             level->total_set.freq == sc->min_freq)
  559                 return;
  560 
  561         temp_cur = aw_thermal_gettemp(sc, 0);
  562         temp_alarm = aw_thermal_getalarm(sc, 0);
  563 
  564         if (temp_cur < temp_alarm)
  565                 aw_thermal_throttle(sc, 0);
  566         else
  567                 *status = ENXIO;
  568 }
  569 
  570 static void
  571 aw_thermal_intr(void *arg)
  572 {
  573         struct aw_thermal_softc *sc;
  574         device_t dev;
  575         uint32_t ints;
  576 
  577         dev = arg;
  578         sc = device_get_softc(dev);
  579 
  580         ints = RD4(sc, THS_INTS);
  581         WR4(sc, THS_INTS, ints);
  582 
  583         if ((ints & SHUT_INT_ALL) != 0) {
  584                 device_printf(dev,
  585                     "WARNING - current temperature exceeds safe limits\n");
  586                 shutdown_nice(RB_POWEROFF);
  587         }
  588 
  589         if ((ints & ALARM_INT_ALL) != 0)
  590                 taskqueue_enqueue(taskqueue_thread, &sc->cf_task);
  591 }
  592 
  593 static int
  594 aw_thermal_probe(device_t dev)
  595 {
  596         if (!ofw_bus_status_okay(dev))
  597                 return (ENXIO);
  598 
  599         if (THS_CONF(dev) == NULL)
  600                 return (ENXIO);
  601 
  602         device_set_desc(dev, "Allwinner Thermal Sensor Controller");
  603         return (BUS_PROBE_DEFAULT);
  604 }
  605 
  606 static int
  607 aw_thermal_attach(device_t dev)
  608 {
  609         struct aw_thermal_softc *sc;
  610         hwreset_t rst;
  611         int i, error;
  612         void *ih;
  613 
  614         sc = device_get_softc(dev);
  615         sc->dev = dev;
  616         rst = NULL;
  617         ih = NULL;
  618 
  619         sc->conf = THS_CONF(dev);
  620         TASK_INIT(&sc->cf_task, 0, aw_thermal_cf_task, sc);
  621 
  622         if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) {
  623                 device_printf(dev, "cannot allocate resources for device\n");
  624                 return (ENXIO);
  625         }
  626 
  627         if (clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_apb) == 0) {
  628                 error = clk_enable(sc->clk_apb);
  629                 if (error != 0) {
  630                         device_printf(dev, "cannot enable apb clock\n");
  631                         goto fail;
  632                 }
  633         }
  634 
  635         if (clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_ths) == 0) {
  636                 error = clk_set_freq(sc->clk_ths, sc->conf->clk_rate, 0);
  637                 if (error != 0) {
  638                         device_printf(dev, "cannot set ths clock rate\n");
  639                         goto fail;
  640                 }
  641                 error = clk_enable(sc->clk_ths);
  642                 if (error != 0) {
  643                         device_printf(dev, "cannot enable ths clock\n");
  644                         goto fail;
  645                 }
  646         }
  647 
  648         if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
  649                 error = hwreset_deassert(rst);
  650                 if (error != 0) {
  651                         device_printf(dev, "cannot de-assert reset\n");
  652                         goto fail;
  653                 }
  654         }
  655 
  656         error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
  657             NULL, aw_thermal_intr, dev, &ih);
  658         if (error != 0) {
  659                 device_printf(dev, "cannot setup interrupt handler\n");
  660                 goto fail;
  661         }
  662 
  663         for (i = 0; i < sc->conf->nsensors; i++) {
  664                 if (sc->conf->sensors[i].init_alarm > 0)
  665                         aw_thermal_setalarm(sc, i,
  666                             sc->conf->sensors[i].init_alarm);
  667                 if (sc->conf->sensors[i].init_shut > 0)
  668                         aw_thermal_setshut(sc, i,
  669                             sc->conf->sensors[i].init_shut);
  670         }
  671 
  672         if (aw_thermal_init(sc) != 0)
  673                 goto fail;
  674 
  675         for (i = 0; i < sc->conf->nsensors; i++)
  676                 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
  677                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  678                     OID_AUTO, sc->conf->sensors[i].name,
  679                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
  680                     sc, i, aw_thermal_sysctl, "IK0",
  681                     sc->conf->sensors[i].desc);
  682 
  683         if (bootverbose)
  684                 for (i = 0; i < sc->conf->nsensors; i++) {
  685                         device_printf(dev,
  686                             "%s: alarm %dC hyst %dC shut %dC\n",
  687                             sc->conf->sensors[i].name,
  688                             aw_thermal_getalarm(sc, i),
  689                             aw_thermal_gethyst(sc, i),
  690                             aw_thermal_getshut(sc, i));
  691                 }
  692 
  693         sc->cf_pre_tag = EVENTHANDLER_REGISTER(cpufreq_pre_change,
  694             aw_thermal_cf_pre_change, sc, EVENTHANDLER_PRI_FIRST);
  695 
  696         return (0);
  697 
  698 fail:
  699         if (ih != NULL)
  700                 bus_teardown_intr(dev, sc->res[1], ih);
  701         if (rst != NULL)
  702                 hwreset_release(rst);
  703         if (sc->clk_apb != NULL)
  704                 clk_release(sc->clk_apb);
  705         if (sc->clk_ths != NULL)
  706                 clk_release(sc->clk_ths);
  707         bus_release_resources(dev, aw_thermal_spec, sc->res);
  708 
  709         return (ENXIO);
  710 }
  711 
  712 static device_method_t aw_thermal_methods[] = {
  713         /* Device interface */
  714         DEVMETHOD(device_probe,         aw_thermal_probe),
  715         DEVMETHOD(device_attach,        aw_thermal_attach),
  716 
  717         DEVMETHOD_END
  718 };
  719 
  720 static driver_t aw_thermal_driver = {
  721         "aw_thermal",
  722         aw_thermal_methods,
  723         sizeof(struct aw_thermal_softc),
  724 };
  725 
  726 static devclass_t aw_thermal_devclass;
  727 
  728 DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass,
  729     0, 0);
  730 MODULE_VERSION(aw_thermal, 1);
  731 MODULE_DEPEND(aw_thermal, aw_sid, 1, 1, 1);
  732 SIMPLEBUS_PNP_INFO(compat_data);

Cache object: ce164cf783acc47b52eefd57334f7475


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