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/powerpc/powermac/smu.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) 2009 Nathan Whitehorn
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/bus.h>
   35 #include <sys/eventhandler.h>
   36 #include <sys/systm.h>
   37 #include <sys/module.h>
   38 #include <sys/conf.h>
   39 #include <sys/cpu.h>
   40 #include <sys/clock.h>
   41 #include <sys/ctype.h>
   42 #include <sys/kernel.h>
   43 #include <sys/kthread.h>
   44 #include <sys/lock.h>
   45 #include <sys/mutex.h>
   46 #include <sys/reboot.h>
   47 #include <sys/rman.h>
   48 #include <sys/sysctl.h>
   49 #include <sys/unistd.h>
   50 
   51 #include <machine/bus.h>
   52 #include <machine/intr_machdep.h>
   53 #include <machine/md_var.h>
   54 
   55 #include <dev/iicbus/iicbus.h>
   56 #include <dev/iicbus/iiconf.h>
   57 #include <dev/led/led.h>
   58 #include <dev/ofw/openfirm.h>
   59 #include <dev/ofw/ofw_bus.h>
   60 #include <dev/ofw/ofw_bus_subr.h>
   61 #include <powerpc/powermac/macgpiovar.h>
   62 #include <powerpc/powermac/powermac_thermal.h>
   63 
   64 #include "clock_if.h"
   65 #include "iicbus_if.h"
   66 
   67 struct smu_cmd {
   68         volatile uint8_t cmd;
   69         uint8_t         len;
   70         uint8_t         data[254];
   71 
   72         STAILQ_ENTRY(smu_cmd) cmd_q;
   73 };
   74 
   75 STAILQ_HEAD(smu_cmdq, smu_cmd);
   76 
   77 struct smu_fan {
   78         struct pmac_fan fan;
   79         device_t dev;
   80         cell_t  reg;
   81 
   82         enum {
   83                 SMU_FAN_RPM,
   84                 SMU_FAN_PWM
   85         } type;
   86         int     setpoint;
   87         int     old_style;
   88         int     rpm;
   89 };
   90 
   91 /* We can read the PWM and the RPM from a PWM controlled fan.
   92  * Offer both values via sysctl.
   93  */
   94 enum {
   95         SMU_PWM_SYSCTL_PWM   = 1 << 8,
   96         SMU_PWM_SYSCTL_RPM   = 2 << 8
   97 };
   98 
   99 struct smu_sensor {
  100         struct pmac_therm therm;
  101         device_t dev;
  102 
  103         cell_t  reg;
  104         enum {
  105                 SMU_CURRENT_SENSOR,
  106                 SMU_VOLTAGE_SENSOR,
  107                 SMU_POWER_SENSOR,
  108                 SMU_TEMP_SENSOR
  109         } type;
  110 };
  111 
  112 struct smu_softc {
  113         device_t        sc_dev;
  114         struct mtx      sc_mtx;
  115 
  116         struct resource *sc_memr;
  117         int             sc_memrid;
  118         int             sc_u3;
  119 
  120         bus_dma_tag_t   sc_dmatag;
  121         bus_space_tag_t sc_bt;
  122         bus_space_handle_t sc_mailbox;
  123 
  124         struct smu_cmd  *sc_cmd, *sc_cur_cmd;
  125         bus_addr_t      sc_cmd_phys;
  126         bus_dmamap_t    sc_cmd_dmamap;
  127         struct smu_cmdq sc_cmdq;
  128 
  129         struct smu_fan  *sc_fans;
  130         int             sc_nfans;
  131         int             old_style_fans;
  132         struct smu_sensor *sc_sensors;
  133         int             sc_nsensors;
  134 
  135         int             sc_doorbellirqid;
  136         struct resource *sc_doorbellirq;
  137         void            *sc_doorbellirqcookie;
  138 
  139         struct proc     *sc_fanmgt_proc;
  140         time_t          sc_lastuserchange;
  141 
  142         /* Calibration data */
  143         uint16_t        sc_cpu_diode_scale;
  144         int16_t         sc_cpu_diode_offset;
  145 
  146         uint16_t        sc_cpu_volt_scale;
  147         int16_t         sc_cpu_volt_offset;
  148         uint16_t        sc_cpu_curr_scale;
  149         int16_t         sc_cpu_curr_offset;
  150 
  151         uint16_t        sc_slots_pow_scale;
  152         int16_t         sc_slots_pow_offset;
  153 
  154         struct cdev     *sc_leddev;
  155 };
  156 
  157 /* regular bus attachment functions */
  158 
  159 static int      smu_probe(device_t);
  160 static int      smu_attach(device_t);
  161 static const struct ofw_bus_devinfo *
  162     smu_get_devinfo(device_t bus, device_t dev);
  163 
  164 /* cpufreq notification hooks */
  165 
  166 static void     smu_cpufreq_pre_change(device_t, const struct cf_level *level);
  167 static void     smu_cpufreq_post_change(device_t, const struct cf_level *level);
  168 
  169 /* clock interface */
  170 static int      smu_gettime(device_t dev, struct timespec *ts);
  171 static int      smu_settime(device_t dev, struct timespec *ts);
  172 
  173 /* utility functions */
  174 static int      smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait);
  175 static int      smu_get_datablock(device_t dev, int8_t id, uint8_t *buf,
  176                     size_t len);
  177 static void     smu_attach_i2c(device_t dev, phandle_t i2croot);
  178 static void     smu_attach_fans(device_t dev, phandle_t fanroot);
  179 static void     smu_attach_sensors(device_t dev, phandle_t sensroot);
  180 static void     smu_set_sleepled(void *xdev, int onoff);
  181 static int      smu_server_mode(SYSCTL_HANDLER_ARGS);
  182 static void     smu_doorbell_intr(void *xdev);
  183 static void     smu_shutdown(void *xdev, int howto);
  184 
  185 /* where to find the doorbell GPIO */
  186 
  187 static device_t smu_doorbell = NULL;
  188 
  189 static device_method_t  smu_methods[] = {
  190         /* Device interface */
  191         DEVMETHOD(device_probe,         smu_probe),
  192         DEVMETHOD(device_attach,        smu_attach),
  193 
  194         /* Clock interface */
  195         DEVMETHOD(clock_gettime,        smu_gettime),
  196         DEVMETHOD(clock_settime,        smu_settime),
  197 
  198         /* ofw_bus interface */
  199         DEVMETHOD(bus_child_pnpinfo,    ofw_bus_gen_child_pnpinfo),
  200         DEVMETHOD(ofw_bus_get_devinfo,  smu_get_devinfo),
  201         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
  202         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
  203         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
  204         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
  205         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
  206 
  207         { 0, 0 },
  208 };
  209 
  210 static driver_t smu_driver = {
  211         "smu",
  212         smu_methods,
  213         sizeof(struct smu_softc)
  214 };
  215 
  216 DRIVER_MODULE(smu, ofwbus, smu_driver, 0, 0);
  217 static MALLOC_DEFINE(M_SMU, "smu", "SMU Sensor Information");
  218 
  219 #define SMU_MAILBOX             0x8000860c
  220 #define SMU_FANMGT_INTERVAL     1000 /* ms */
  221 
  222 /* Command types */
  223 #define SMU_ADC                 0xd8
  224 #define SMU_FAN                 0x4a
  225 #define SMU_RPM_STATUS          0x01
  226 #define SMU_RPM_SETPOINT        0x02
  227 #define SMU_PWM_STATUS          0x11
  228 #define SMU_PWM_SETPOINT        0x12
  229 #define SMU_I2C                 0x9a
  230 #define  SMU_I2C_SIMPLE         0x00
  231 #define  SMU_I2C_NORMAL         0x01
  232 #define  SMU_I2C_COMBINED       0x02
  233 #define SMU_MISC                0xee
  234 #define  SMU_MISC_GET_DATA      0x02
  235 #define  SMU_MISC_LED_CTRL      0x04
  236 #define SMU_POWER               0xaa
  237 #define SMU_POWER_EVENTS        0x8f
  238 #define  SMU_PWR_GET_POWERUP    0x00
  239 #define  SMU_PWR_SET_POWERUP    0x01
  240 #define  SMU_PWR_CLR_POWERUP    0x02
  241 #define SMU_RTC                 0x8e
  242 #define  SMU_RTC_GET            0x81
  243 #define  SMU_RTC_SET            0x80
  244 
  245 /* Power event types */
  246 #define SMU_WAKEUP_KEYPRESS     0x01
  247 #define SMU_WAKEUP_AC_INSERT    0x02
  248 #define SMU_WAKEUP_AC_CHANGE    0x04
  249 #define SMU_WAKEUP_RING         0x10
  250 
  251 /* Data blocks */
  252 #define SMU_CPUTEMP_CAL         0x18
  253 #define SMU_CPUVOLT_CAL         0x21
  254 #define SMU_SLOTPW_CAL          0x78
  255 
  256 /* Partitions */
  257 #define SMU_PARTITION           0x3e
  258 #define SMU_PARTITION_LATEST    0x01
  259 #define SMU_PARTITION_BASE      0x02
  260 #define SMU_PARTITION_UPDATE    0x03
  261 
  262 static int
  263 smu_probe(device_t dev)
  264 {
  265         const char *name = ofw_bus_get_name(dev);
  266 
  267         if (strcmp(name, "smu") != 0)
  268                 return (ENXIO);
  269 
  270         device_set_desc(dev, "Apple System Management Unit");
  271         return (0);
  272 }
  273 
  274 static void
  275 smu_phys_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
  276 {
  277         struct smu_softc *sc = xsc;
  278 
  279         sc->sc_cmd_phys = segs[0].ds_addr;
  280 }
  281 
  282 static int
  283 smu_attach(device_t dev)
  284 {
  285         struct smu_softc *sc;
  286         phandle_t       node, child;
  287         uint8_t         data[12];
  288 
  289         sc = device_get_softc(dev);
  290 
  291         mtx_init(&sc->sc_mtx, "smu", NULL, MTX_DEF);
  292         sc->sc_cur_cmd = NULL;
  293         sc->sc_doorbellirqid = -1;
  294 
  295         sc->sc_u3 = 0;
  296         if (OF_finddevice("/u3") != -1)
  297                 sc->sc_u3 = 1;
  298 
  299         /*
  300          * Map the mailbox area. This should be determined from firmware,
  301          * but I have not found a simple way to do that.
  302          */
  303         bus_dma_tag_create(NULL, 16, 0, BUS_SPACE_MAXADDR_32BIT,
  304             BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
  305             NULL, &(sc->sc_dmatag));
  306         sc->sc_bt = &bs_le_tag;
  307         bus_space_map(sc->sc_bt, SMU_MAILBOX, 4, 0, &sc->sc_mailbox);
  308 
  309         /*
  310          * Allocate the command buffer. This can be anywhere in the low 4 GB
  311          * of memory.
  312          */
  313         bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_cmd, BUS_DMA_WAITOK | 
  314             BUS_DMA_ZERO, &sc->sc_cmd_dmamap);
  315         bus_dmamap_load(sc->sc_dmatag, sc->sc_cmd_dmamap,
  316             sc->sc_cmd, PAGE_SIZE, smu_phys_callback, sc, 0);
  317         STAILQ_INIT(&sc->sc_cmdq);
  318 
  319         /*
  320          * Set up handlers to change CPU voltage when CPU frequency is changed.
  321          */
  322         EVENTHANDLER_REGISTER(cpufreq_pre_change, smu_cpufreq_pre_change, dev,
  323             EVENTHANDLER_PRI_ANY);
  324         EVENTHANDLER_REGISTER(cpufreq_post_change, smu_cpufreq_post_change, dev,
  325             EVENTHANDLER_PRI_ANY);
  326 
  327         node = ofw_bus_get_node(dev);
  328 
  329         /* Some SMUs have RPM and PWM controlled fans which do not sit
  330          * under the same node. So we have to attach them separately.
  331          */
  332         smu_attach_fans(dev, node);
  333 
  334         /*
  335          * Now detect and attach the other child devices.
  336          */
  337         for (child = OF_child(node); child != 0; child = OF_peer(child)) {
  338                 char name[32];
  339                 memset(name, 0, sizeof(name));
  340                 OF_getprop(child, "name", name, sizeof(name));
  341 
  342                 if (strncmp(name, "sensors", 8) == 0)
  343                         smu_attach_sensors(dev, child);
  344 
  345                 if (strncmp(name, "smu-i2c-control", 15) == 0)
  346                         smu_attach_i2c(dev, child);
  347         }
  348 
  349         /* Some SMUs have the I2C children directly under the bus. */
  350         smu_attach_i2c(dev, node);
  351 
  352         /*
  353          * Collect calibration constants.
  354          */
  355         smu_get_datablock(dev, SMU_CPUTEMP_CAL, data, sizeof(data));
  356         sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
  357         sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
  358 
  359         smu_get_datablock(dev, SMU_CPUVOLT_CAL, data, sizeof(data));
  360         sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
  361         sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
  362         sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
  363         sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
  364 
  365         smu_get_datablock(dev, SMU_SLOTPW_CAL, data, sizeof(data));
  366         sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
  367         sc->sc_slots_pow_offset = (data[6] << 8) + data[7];
  368 
  369         /*
  370          * Set up LED interface
  371          */
  372         sc->sc_leddev = led_create(smu_set_sleepled, dev, "sleepled");
  373 
  374         /*
  375          * Reset on power loss behavior
  376          */
  377 
  378         SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
  379             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
  380             "server_mode", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, dev,
  381             0, smu_server_mode, "I", "Enable reboot after power failure");
  382 
  383         /*
  384          * Set up doorbell interrupt.
  385          */
  386         sc->sc_doorbellirqid = 0;
  387         sc->sc_doorbellirq = bus_alloc_resource_any(smu_doorbell, SYS_RES_IRQ,
  388             &sc->sc_doorbellirqid, RF_ACTIVE);
  389         bus_setup_intr(smu_doorbell, sc->sc_doorbellirq,
  390             INTR_TYPE_MISC | INTR_MPSAFE, NULL, smu_doorbell_intr, dev,
  391             &sc->sc_doorbellirqcookie);
  392         powerpc_config_intr(rman_get_start(sc->sc_doorbellirq),
  393             INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
  394 
  395         /*
  396          * Connect RTC interface.
  397          */
  398         clock_register(dev, 1000);
  399 
  400         /*
  401          * Learn about shutdown events
  402          */
  403         EVENTHANDLER_REGISTER(shutdown_final, smu_shutdown, dev,
  404             SHUTDOWN_PRI_LAST);
  405 
  406         return (bus_generic_attach(dev));
  407 }
  408 
  409 static const struct ofw_bus_devinfo *
  410 smu_get_devinfo(device_t bus, device_t dev)
  411 {
  412 
  413         return (device_get_ivars(dev));
  414 }
  415 
  416 static void
  417 smu_send_cmd(device_t dev, struct smu_cmd *cmd)
  418 {
  419         struct smu_softc *sc;
  420 
  421         sc = device_get_softc(dev);
  422 
  423         mtx_assert(&sc->sc_mtx, MA_OWNED);
  424 
  425         if (sc->sc_u3)
  426                 powerpc_pow_enabled = 0; /* SMU cannot work if we go to NAP */
  427 
  428         sc->sc_cur_cmd = cmd;
  429 
  430         /* Copy the command to the mailbox */
  431         sc->sc_cmd->cmd = cmd->cmd;
  432         sc->sc_cmd->len = cmd->len;
  433         memcpy(sc->sc_cmd->data, cmd->data, sizeof(cmd->data));
  434         bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_PREWRITE);
  435         bus_space_write_4(sc->sc_bt, sc->sc_mailbox, 0, sc->sc_cmd_phys);
  436 
  437         /* Flush the cacheline it is in -- SMU bypasses the cache */
  438         __asm __volatile("sync; dcbf 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
  439 
  440         /* Ring SMU doorbell */
  441         macgpio_write(smu_doorbell, GPIO_DDR_OUTPUT);
  442 }
  443 
  444 static void
  445 smu_doorbell_intr(void *xdev)
  446 {
  447         device_t smu;
  448         struct smu_softc *sc;
  449         int doorbell_ack;
  450 
  451         smu = xdev;
  452         doorbell_ack = macgpio_read(smu_doorbell);
  453         sc = device_get_softc(smu);
  454 
  455         if (doorbell_ack != (GPIO_DDR_OUTPUT | GPIO_LEVEL_RO | GPIO_DATA)) 
  456                 return;
  457 
  458         mtx_lock(&sc->sc_mtx);
  459 
  460         if (sc->sc_cur_cmd == NULL)     /* spurious */
  461                 goto done;
  462 
  463         /* Check result. First invalidate the cache again... */
  464         __asm __volatile("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
  465 
  466         bus_dmamap_sync(sc->sc_dmatag, sc->sc_cmd_dmamap, BUS_DMASYNC_POSTREAD);
  467 
  468         sc->sc_cur_cmd->cmd = sc->sc_cmd->cmd;
  469         sc->sc_cur_cmd->len = sc->sc_cmd->len;
  470         memcpy(sc->sc_cur_cmd->data, sc->sc_cmd->data,
  471             sizeof(sc->sc_cmd->data));
  472         wakeup(sc->sc_cur_cmd);
  473         sc->sc_cur_cmd = NULL;
  474         if (sc->sc_u3)
  475                 powerpc_pow_enabled = 1;
  476 
  477     done:
  478         /* Queue next command if one is pending */
  479         if (STAILQ_FIRST(&sc->sc_cmdq) != NULL) {
  480                 sc->sc_cur_cmd = STAILQ_FIRST(&sc->sc_cmdq);
  481                 STAILQ_REMOVE_HEAD(&sc->sc_cmdq, cmd_q);
  482                 smu_send_cmd(smu, sc->sc_cur_cmd);
  483         }
  484 
  485         mtx_unlock(&sc->sc_mtx);
  486 }
  487 
  488 static int
  489 smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait)
  490 {
  491         struct smu_softc *sc;
  492         uint8_t cmd_code;
  493         int error;
  494 
  495         sc = device_get_softc(dev);
  496         cmd_code = cmd->cmd;
  497 
  498         mtx_lock(&sc->sc_mtx);
  499         if (sc->sc_cur_cmd != NULL) {
  500                 STAILQ_INSERT_TAIL(&sc->sc_cmdq, cmd, cmd_q);
  501         } else
  502                 smu_send_cmd(dev, cmd);
  503         mtx_unlock(&sc->sc_mtx);
  504 
  505         if (!wait)
  506                 return (0);
  507 
  508         if (sc->sc_doorbellirqid < 0) {
  509                 /* Poll if the IRQ has not been set up yet */
  510                 do {
  511                         DELAY(50);
  512                         smu_doorbell_intr(dev);
  513                 } while (sc->sc_cur_cmd != NULL);
  514         } else {
  515                 /* smu_doorbell_intr will wake us when the command is ACK'ed */
  516                 error = tsleep(cmd, 0, "smu", 800 * hz / 1000);
  517                 if (error != 0)
  518                         smu_doorbell_intr(dev); /* One last chance */
  519                 
  520                 if (error != 0) {
  521                     mtx_lock(&sc->sc_mtx);
  522                     if (cmd->cmd == cmd_code) { /* Never processed */
  523                         /* Abort this command if we timed out */
  524                         if (sc->sc_cur_cmd == cmd)
  525                                 sc->sc_cur_cmd = NULL;
  526                         else
  527                                 STAILQ_REMOVE(&sc->sc_cmdq, cmd, smu_cmd,
  528                                     cmd_q);
  529                         mtx_unlock(&sc->sc_mtx);
  530                         return (error);
  531                     }
  532                     error = 0;
  533                     mtx_unlock(&sc->sc_mtx);
  534                 }
  535         }
  536 
  537         /* SMU acks the command by inverting the command bits */
  538         if (cmd->cmd == ((~cmd_code) & 0xff))
  539                 error = 0;
  540         else
  541                 error = EIO;
  542 
  543         return (error);
  544 }
  545 
  546 static int
  547 smu_get_datablock(device_t dev, int8_t id, uint8_t *buf, size_t len)
  548 {
  549         struct smu_cmd cmd;
  550         uint8_t addr[4];
  551 
  552         cmd.cmd = SMU_PARTITION;
  553         cmd.len = 2;
  554         cmd.data[0] = SMU_PARTITION_LATEST;
  555         cmd.data[1] = id; 
  556 
  557         smu_run_cmd(dev, &cmd, 1);
  558 
  559         addr[0] = addr[1] = 0;
  560         addr[2] = cmd.data[0];
  561         addr[3] = cmd.data[1];
  562 
  563         cmd.cmd = SMU_MISC;
  564         cmd.len = 7;
  565         cmd.data[0] = SMU_MISC_GET_DATA;
  566         cmd.data[1] = sizeof(addr);
  567         memcpy(&cmd.data[2], addr, sizeof(addr));
  568         cmd.data[6] = len;
  569 
  570         smu_run_cmd(dev, &cmd, 1);
  571         memcpy(buf, cmd.data, len);
  572         return (0);
  573 }
  574 
  575 static void
  576 smu_slew_cpu_voltage(device_t dev, int to)
  577 {
  578         struct smu_cmd cmd;
  579 
  580         cmd.cmd = SMU_POWER;
  581         cmd.len = 8;
  582         cmd.data[0] = 'V';
  583         cmd.data[1] = 'S'; 
  584         cmd.data[2] = 'L'; 
  585         cmd.data[3] = 'E'; 
  586         cmd.data[4] = 'W'; 
  587         cmd.data[5] = 0xff;
  588         cmd.data[6] = 1;
  589         cmd.data[7] = to;
  590 
  591         smu_run_cmd(dev, &cmd, 1);
  592 }
  593 
  594 static void
  595 smu_cpufreq_pre_change(device_t dev, const struct cf_level *level)
  596 {
  597         /*
  598          * Make sure the CPU voltage is raised before we raise
  599          * the clock.
  600          */
  601                 
  602         if (level->rel_set[0].freq == 10000 /* max */)
  603                 smu_slew_cpu_voltage(dev, 0);
  604 }
  605 
  606 static void
  607 smu_cpufreq_post_change(device_t dev, const struct cf_level *level)
  608 {
  609         /* We are safe to reduce CPU voltage after a downward transition */
  610 
  611         if (level->rel_set[0].freq < 10000 /* max */)
  612                 smu_slew_cpu_voltage(dev, 1); /* XXX: 1/4 voltage for 970MP? */
  613 }
  614 
  615 /* Routines for probing the SMU doorbell GPIO */
  616 static int doorbell_probe(device_t dev);
  617 static int doorbell_attach(device_t dev);
  618 
  619 static device_method_t  doorbell_methods[] = {
  620         /* Device interface */
  621         DEVMETHOD(device_probe,         doorbell_probe),
  622         DEVMETHOD(device_attach,        doorbell_attach),
  623         { 0, 0 },
  624 };
  625 
  626 static driver_t doorbell_driver = {
  627         "smudoorbell",
  628         doorbell_methods,
  629         0
  630 };
  631 
  632 EARLY_DRIVER_MODULE(smudoorbell, macgpio, doorbell_driver, 0, 0,
  633     BUS_PASS_SUPPORTDEV);
  634 
  635 static int
  636 doorbell_probe(device_t dev)
  637 {
  638         const char *name = ofw_bus_get_name(dev);
  639 
  640         if (strcmp(name, "smu-doorbell") != 0)
  641                 return (ENXIO);
  642 
  643         device_set_desc(dev, "SMU Doorbell GPIO");
  644         device_quiet(dev);
  645         return (0);
  646 }
  647 
  648 static int
  649 doorbell_attach(device_t dev)
  650 {
  651         smu_doorbell = dev;
  652         return (0);
  653 }
  654 
  655 /*
  656  * Sensor and fan management
  657  */
  658 
  659 static int
  660 smu_fan_check_old_style(struct smu_fan *fan)
  661 {
  662         device_t smu = fan->dev;
  663         struct smu_softc *sc = device_get_softc(smu);
  664         struct smu_cmd cmd;
  665         int error;
  666 
  667         if (sc->old_style_fans != -1)
  668                 return (sc->old_style_fans);
  669 
  670         /*
  671          * Apple has two fan control mechanisms. We can't distinguish
  672          * them except by seeing if the new one fails. If the new one
  673          * fails, use the old one.
  674          */
  675 
  676         cmd.cmd = SMU_FAN;
  677         cmd.len = 2;
  678         cmd.data[0] = 0x31;
  679         cmd.data[1] = fan->reg;
  680 
  681         do {
  682                 error = smu_run_cmd(smu, &cmd, 1);
  683         } while (error == EWOULDBLOCK);
  684 
  685         sc->old_style_fans = (error != 0);
  686 
  687         return (sc->old_style_fans);
  688 }
  689 
  690 static int
  691 smu_fan_set_rpm(struct smu_fan *fan, int rpm)
  692 {
  693         device_t smu = fan->dev;
  694         struct smu_cmd cmd;
  695         int error;
  696 
  697         cmd.cmd = SMU_FAN;
  698         error = EIO;
  699 
  700         /* Clamp to allowed range */
  701         rpm = max(fan->fan.min_rpm, rpm);
  702         rpm = min(fan->fan.max_rpm, rpm);
  703 
  704         smu_fan_check_old_style(fan);
  705 
  706         if (!fan->old_style) {
  707                 cmd.len = 4;
  708                 cmd.data[0] = 0x30;
  709                 cmd.data[1] = fan->reg;
  710                 cmd.data[2] = (rpm >> 8) & 0xff;
  711                 cmd.data[3] = rpm & 0xff;
  712 
  713                 error = smu_run_cmd(smu, &cmd, 1);
  714                 if (error && error != EWOULDBLOCK)
  715                         fan->old_style = 1;
  716         } else {
  717                 cmd.len = 14;
  718                 cmd.data[0] = 0x00; /* RPM fan. */
  719                 cmd.data[1] = 1 << fan->reg;
  720                 cmd.data[2 + 2*fan->reg] = (rpm >> 8) & 0xff;
  721                 cmd.data[3 + 2*fan->reg] = rpm & 0xff;
  722                 error = smu_run_cmd(smu, &cmd, 1);
  723         }
  724 
  725         if (error == 0)
  726                 fan->setpoint = rpm;
  727 
  728         return (error);
  729 }
  730 
  731 static int
  732 smu_fan_read_rpm(struct smu_fan *fan)
  733 {
  734         device_t smu = fan->dev;
  735         struct smu_cmd cmd;
  736         int rpm, error;
  737 
  738         smu_fan_check_old_style(fan);
  739 
  740         if (!fan->old_style) {
  741                 cmd.cmd = SMU_FAN;
  742                 cmd.len = 2;
  743                 cmd.data[0] = 0x31;
  744                 cmd.data[1] = fan->reg;
  745 
  746                 error = smu_run_cmd(smu, &cmd, 1);
  747                 if (error && error != EWOULDBLOCK)
  748                         fan->old_style = 1;
  749 
  750                 rpm = (cmd.data[0] << 8) | cmd.data[1];
  751         }
  752 
  753         if (fan->old_style) {
  754                 cmd.cmd = SMU_FAN;
  755                 cmd.len = 1;
  756                 cmd.data[0] = SMU_RPM_STATUS;
  757 
  758                 error = smu_run_cmd(smu, &cmd, 1);
  759                 if (error)
  760                         return (error);
  761 
  762                 rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2];
  763         }
  764 
  765         return (rpm);
  766 }
  767 static int
  768 smu_fan_set_pwm(struct smu_fan *fan, int pwm)
  769 {
  770         device_t smu = fan->dev;
  771         struct smu_cmd cmd;
  772         int error;
  773 
  774         cmd.cmd = SMU_FAN;
  775         error = EIO;
  776 
  777         /* Clamp to allowed range */
  778         pwm = max(fan->fan.min_rpm, pwm);
  779         pwm = min(fan->fan.max_rpm, pwm);
  780 
  781         /*
  782          * Apple has two fan control mechanisms. We can't distinguish
  783          * them except by seeing if the new one fails. If the new one
  784          * fails, use the old one.
  785          */
  786 
  787         if (!fan->old_style) {
  788                 cmd.len = 4;
  789                 cmd.data[0] = 0x30;
  790                 cmd.data[1] = fan->reg;
  791                 cmd.data[2] = (pwm >> 8) & 0xff;
  792                 cmd.data[3] = pwm & 0xff;
  793 
  794                 error = smu_run_cmd(smu, &cmd, 1);
  795                 if (error && error != EWOULDBLOCK)
  796                         fan->old_style = 1;
  797         }
  798 
  799         if (fan->old_style) {
  800                 cmd.len = 14;
  801                 cmd.data[0] = 0x10; /* PWM fan. */
  802                 cmd.data[1] = 1 << fan->reg;
  803                 cmd.data[2 + 2*fan->reg] = (pwm >> 8) & 0xff;
  804                 cmd.data[3 + 2*fan->reg] = pwm & 0xff;
  805                 error = smu_run_cmd(smu, &cmd, 1);
  806         }
  807 
  808         if (error == 0)
  809                 fan->setpoint = pwm;
  810 
  811         return (error);
  812 }
  813 
  814 static int
  815 smu_fan_read_pwm(struct smu_fan *fan, int *pwm, int *rpm)
  816 {
  817         device_t smu = fan->dev;
  818         struct smu_cmd cmd;
  819         int error;
  820 
  821         if (!fan->old_style) {
  822                 cmd.cmd = SMU_FAN;
  823                 cmd.len = 2;
  824                 cmd.data[0] = 0x31;
  825                 cmd.data[1] = fan->reg;
  826 
  827                 error = smu_run_cmd(smu, &cmd, 1);
  828                 if (error && error != EWOULDBLOCK)
  829                         fan->old_style = 1;
  830 
  831                 *rpm = (cmd.data[0] << 8) | cmd.data[1];
  832         }
  833 
  834         if (fan->old_style) {
  835                 cmd.cmd = SMU_FAN;
  836                 cmd.len = 1;
  837                 cmd.data[0] = SMU_PWM_STATUS;
  838 
  839                 error = smu_run_cmd(smu, &cmd, 1);
  840                 if (error)
  841                         return (error);
  842 
  843                 *rpm = (cmd.data[fan->reg*2+1] << 8) | cmd.data[fan->reg*2+2];
  844         }
  845         if (fan->old_style) {
  846                 cmd.cmd = SMU_FAN;
  847                 cmd.len = 14;
  848                 cmd.data[0] = SMU_PWM_SETPOINT;
  849                 cmd.data[1] = 1 << fan->reg;
  850 
  851                 error = smu_run_cmd(smu, &cmd, 1);
  852                 if (error)
  853                         return (error);
  854 
  855                 *pwm = cmd.data[fan->reg*2+2];
  856         }
  857         return (0);
  858 }
  859 
  860 static int
  861 smu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
  862 {
  863         device_t smu;
  864         struct smu_softc *sc;
  865         struct smu_fan *fan;
  866         int pwm = 0, rpm, error = 0;
  867 
  868         smu = arg1;
  869         sc = device_get_softc(smu);
  870         fan = &sc->sc_fans[arg2 & 0xff];
  871 
  872         if (fan->type == SMU_FAN_RPM) {
  873                 rpm = smu_fan_read_rpm(fan);
  874                 if (rpm < 0)
  875                         return (rpm);
  876 
  877                 error = sysctl_handle_int(oidp, &rpm, 0, req);
  878         } else {
  879                 error = smu_fan_read_pwm(fan, &pwm, &rpm);
  880                 if (error < 0)
  881                         return (EIO);
  882 
  883                 switch (arg2 & 0xff00) {
  884                 case SMU_PWM_SYSCTL_PWM:
  885                         error = sysctl_handle_int(oidp, &pwm, 0, req);
  886                         break;
  887                 case SMU_PWM_SYSCTL_RPM:
  888                         error = sysctl_handle_int(oidp, &rpm, 0, req);
  889                         break;
  890                 default:
  891                         /* This should never happen */
  892                         return (EINVAL);
  893                 }
  894         }
  895         /* We can only read the RPM from a PWM controlled fan, so return. */
  896         if ((arg2 & 0xff00) == SMU_PWM_SYSCTL_RPM)
  897                 return (0);
  898 
  899         if (error || !req->newptr)
  900                 return (error);
  901 
  902         sc->sc_lastuserchange = time_uptime;
  903 
  904         if (fan->type == SMU_FAN_RPM)
  905                 return (smu_fan_set_rpm(fan, rpm));
  906         else
  907                 return (smu_fan_set_pwm(fan, pwm));
  908 }
  909 
  910 static void
  911 smu_fill_fan_prop(device_t dev, phandle_t child, int id)
  912 {
  913         struct smu_fan *fan;
  914         struct smu_softc *sc;
  915         char type[32];
  916 
  917         sc = device_get_softc(dev);
  918         fan = &sc->sc_fans[id];
  919 
  920         OF_getprop(child, "device_type", type, sizeof(type));
  921         /* We have either RPM or PWM controlled fans. */
  922         if (strcmp(type, "fan-rpm-control") == 0)
  923                 fan->type = SMU_FAN_RPM;
  924         else
  925                 fan->type = SMU_FAN_PWM;
  926 
  927         fan->dev = dev;
  928         fan->old_style = 0;
  929         OF_getprop(child, "reg", &fan->reg,
  930                    sizeof(cell_t));
  931         OF_getprop(child, "min-value", &fan->fan.min_rpm,
  932                    sizeof(int));
  933         OF_getprop(child, "max-value", &fan->fan.max_rpm,
  934                    sizeof(int));
  935         OF_getprop(child, "zone", &fan->fan.zone,
  936                    sizeof(int));
  937 
  938         if (OF_getprop(child, "unmanaged-value",
  939                        &fan->fan.default_rpm,
  940                        sizeof(int)) != sizeof(int))
  941                 fan->fan.default_rpm = fan->fan.max_rpm;
  942 
  943         OF_getprop(child, "location", fan->fan.name,
  944                    sizeof(fan->fan.name));
  945 
  946         if (fan->type == SMU_FAN_RPM)
  947                 fan->setpoint = smu_fan_read_rpm(fan);
  948         else
  949                 smu_fan_read_pwm(fan, &fan->setpoint, &fan->rpm);
  950 }
  951 
  952 /* On the first call count the number of fans. In the second call,
  953  * after allocating the fan struct, fill the properties of the fans.
  954  */
  955 static int
  956 smu_count_fans(device_t dev)
  957 {
  958         struct smu_softc *sc;
  959         phandle_t child, node, root;
  960         int nfans = 0;
  961 
  962         node = ofw_bus_get_node(dev);
  963         sc = device_get_softc(dev);
  964 
  965         /* First find the fanroots and count the number of fans. */
  966         for (root = OF_child(node); root != 0; root = OF_peer(root)) {
  967                 char name[32];
  968                 memset(name, 0, sizeof(name));
  969                 OF_getprop(root, "name", name, sizeof(name));
  970                 if (strncmp(name, "rpm-fans", 9) == 0 ||
  971                     strncmp(name, "pwm-fans", 9) == 0 ||
  972                     strncmp(name, "fans", 5) == 0)
  973                         for (child = OF_child(root); child != 0;
  974                              child = OF_peer(child)) {
  975                                 nfans++;
  976                                 /* When allocated, fill the fan properties. */
  977                                 if (sc->sc_fans != NULL) {
  978                                         smu_fill_fan_prop(dev, child,
  979                                                           nfans - 1);
  980                                 }
  981                         }
  982         }
  983         if (nfans == 0) {
  984                 device_printf(dev, "WARNING: No fans detected!\n");
  985                 return (0);
  986         }
  987         return (nfans);
  988 }
  989 
  990 static void
  991 smu_attach_fans(device_t dev, phandle_t fanroot)
  992 {
  993         struct smu_fan *fan;
  994         struct smu_softc *sc;
  995         struct sysctl_oid *oid, *fanroot_oid;
  996         struct sysctl_ctx_list *ctx;
  997         char sysctl_name[32];
  998         int i, j;
  999 
 1000         sc = device_get_softc(dev);
 1001 
 1002         /* Get the number of fans. */
 1003         sc->sc_nfans = smu_count_fans(dev);
 1004         if (sc->sc_nfans == 0)
 1005                 return;
 1006 
 1007         /* Now we're able to allocate memory for the fans struct. */
 1008         sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct smu_fan), M_SMU,
 1009             M_WAITOK | M_ZERO);
 1010 
 1011         /* Now fill in the properties. */
 1012         smu_count_fans(dev);
 1013 
 1014         /* Register fans with pmac_thermal */
 1015         for (i = 0; i < sc->sc_nfans; i++)
 1016                 pmac_thermal_fan_register(&sc->sc_fans[i].fan);
 1017 
 1018         ctx = device_get_sysctl_ctx(dev);
 1019         fanroot_oid = SYSCTL_ADD_NODE(ctx,
 1020             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
 1021             CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "SMU Fan Information");
 1022 
 1023         /* Add sysctls */
 1024         for (i = 0; i < sc->sc_nfans; i++) {
 1025                 fan = &sc->sc_fans[i];
 1026                 for (j = 0; j < strlen(fan->fan.name); j++) {
 1027                         sysctl_name[j] = tolower(fan->fan.name[j]);
 1028                         if (isspace(sysctl_name[j]))
 1029                                 sysctl_name[j] = '_';
 1030                 }
 1031                 sysctl_name[j] = 0;
 1032                 if (fan->type == SMU_FAN_RPM) {
 1033                         oid = SYSCTL_ADD_NODE(ctx,
 1034                             SYSCTL_CHILDREN(fanroot_oid), OID_AUTO,
 1035                             sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
 1036                             "Fan Information");
 1037                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1038                                        "minrpm", CTLFLAG_RD,
 1039                                        &fan->fan.min_rpm, 0,
 1040                                        "Minimum allowed RPM");
 1041                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1042                                        "maxrpm", CTLFLAG_RD,
 1043                                        &fan->fan.max_rpm, 0,
 1044                                        "Maximum allowed RPM");
 1045                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1046                                         "rpm",CTLTYPE_INT | CTLFLAG_RW |
 1047                                         CTLFLAG_MPSAFE, dev, i,
 1048                                         smu_fanrpm_sysctl, "I", "Fan RPM");
 1049 
 1050                         fan->fan.read = (int (*)(struct pmac_fan *))smu_fan_read_rpm;
 1051                         fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_rpm;
 1052 
 1053                 } else {
 1054                         oid = SYSCTL_ADD_NODE(ctx,
 1055                             SYSCTL_CHILDREN(fanroot_oid), OID_AUTO,
 1056                                 sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
 1057                                 "Fan Information");
 1058                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1059                                        "minpwm", CTLFLAG_RD,
 1060                                        &fan->fan.min_rpm, 0,
 1061                                        "Minimum allowed PWM in %");
 1062                         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1063                                        "maxpwm", CTLFLAG_RD,
 1064                                        &fan->fan.max_rpm, 0,
 1065                                        "Maximum allowed PWM in %");
 1066                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1067                                         "pwm",CTLTYPE_INT | CTLFLAG_RW |
 1068                                         CTLFLAG_MPSAFE, dev,
 1069                                         SMU_PWM_SYSCTL_PWM | i,
 1070                                         smu_fanrpm_sysctl, "I", "Fan PWM in %");
 1071                         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 1072                                         "rpm",CTLTYPE_INT | CTLFLAG_RD |
 1073                                         CTLFLAG_MPSAFE, dev,
 1074                                         SMU_PWM_SYSCTL_RPM | i,
 1075                                         smu_fanrpm_sysctl, "I", "Fan RPM");
 1076                         fan->fan.read = NULL;
 1077                         fan->fan.set = (int (*)(struct pmac_fan *, int))smu_fan_set_pwm;
 1078                 }
 1079                 if (bootverbose)
 1080                         device_printf(dev, "Fan: %s type: %d\n",
 1081                                       fan->fan.name, fan->type);
 1082         }
 1083 }
 1084 
 1085 static int
 1086 smu_sensor_read(struct smu_sensor *sens)
 1087 {
 1088         device_t smu = sens->dev;
 1089         struct smu_cmd cmd;
 1090         struct smu_softc *sc;
 1091         int64_t value;
 1092         int error;
 1093 
 1094         cmd.cmd = SMU_ADC;
 1095         cmd.len = 1;
 1096         cmd.data[0] = sens->reg;
 1097         error = 0;
 1098 
 1099         error = smu_run_cmd(smu, &cmd, 1);
 1100         if (error != 0)
 1101                 return (-1);
 1102 
 1103         sc = device_get_softc(smu);
 1104         value = (cmd.data[0] << 8) | cmd.data[1];
 1105 
 1106         switch (sens->type) {
 1107         case SMU_TEMP_SENSOR:
 1108                 value *= sc->sc_cpu_diode_scale;
 1109                 value >>= 3;
 1110                 value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
 1111                 value <<= 1;
 1112 
 1113                 /* Convert from 16.16 fixed point degC into integer 0.1 K. */
 1114                 value = 10*(value >> 16) + ((10*(value & 0xffff)) >> 16) + 2731;
 1115                 break;
 1116         case SMU_VOLTAGE_SENSOR:
 1117                 value *= sc->sc_cpu_volt_scale;
 1118                 value += sc->sc_cpu_volt_offset;
 1119                 value <<= 4;
 1120 
 1121                 /* Convert from 16.16 fixed point V into mV. */
 1122                 value *= 15625;
 1123                 value /= 1024;
 1124                 value /= 1000;
 1125                 break;
 1126         case SMU_CURRENT_SENSOR:
 1127                 value *= sc->sc_cpu_curr_scale;
 1128                 value += sc->sc_cpu_curr_offset;
 1129                 value <<= 4;
 1130 
 1131                 /* Convert from 16.16 fixed point A into mA. */
 1132                 value *= 15625;
 1133                 value /= 1024;
 1134                 value /= 1000;
 1135                 break;
 1136         case SMU_POWER_SENSOR:
 1137                 value *= sc->sc_slots_pow_scale;
 1138                 value += sc->sc_slots_pow_offset;
 1139                 value <<= 4;
 1140 
 1141                 /* Convert from 16.16 fixed point W into mW. */
 1142                 value *= 15625;
 1143                 value /= 1024;
 1144                 value /= 1000;
 1145                 break;
 1146         }
 1147 
 1148         return (value);
 1149 }
 1150 
 1151 static int
 1152 smu_sensor_sysctl(SYSCTL_HANDLER_ARGS)
 1153 {
 1154         device_t smu;
 1155         struct smu_softc *sc;
 1156         struct smu_sensor *sens;
 1157         int value, error;
 1158 
 1159         smu = arg1;
 1160         sc = device_get_softc(smu);
 1161         sens = &sc->sc_sensors[arg2];
 1162 
 1163         value = smu_sensor_read(sens);
 1164         if (value < 0)
 1165                 return (EBUSY);
 1166 
 1167         error = sysctl_handle_int(oidp, &value, 0, req);
 1168 
 1169         return (error);
 1170 }
 1171 
 1172 static void
 1173 smu_attach_sensors(device_t dev, phandle_t sensroot)
 1174 {
 1175         struct smu_sensor *sens;
 1176         struct smu_softc *sc;
 1177         struct sysctl_oid *sensroot_oid;
 1178         struct sysctl_ctx_list *ctx;
 1179         phandle_t child;
 1180         char type[32];
 1181         int i;
 1182 
 1183         sc = device_get_softc(dev);
 1184         sc->sc_nsensors = 0;
 1185 
 1186         for (child = OF_child(sensroot); child != 0; child = OF_peer(child))
 1187                 sc->sc_nsensors++;
 1188 
 1189         if (sc->sc_nsensors == 0) {
 1190                 device_printf(dev, "WARNING: No sensors detected!\n");
 1191                 return;
 1192         }
 1193 
 1194         sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
 1195             M_SMU, M_WAITOK | M_ZERO);
 1196 
 1197         sens = sc->sc_sensors;
 1198         sc->sc_nsensors = 0;
 1199 
 1200         ctx = device_get_sysctl_ctx(dev);
 1201         sensroot_oid = SYSCTL_ADD_NODE(ctx,
 1202             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensors",
 1203             CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "SMU Sensor Information");
 1204 
 1205         for (child = OF_child(sensroot); child != 0; child = OF_peer(child)) {
 1206                 char sysctl_name[40], sysctl_desc[40];
 1207                 const char *units;
 1208 
 1209                 sens->dev = dev;
 1210                 OF_getprop(child, "device_type", type, sizeof(type));
 1211 
 1212                 if (strcmp(type, "current-sensor") == 0) {
 1213                         sens->type = SMU_CURRENT_SENSOR;
 1214                         units = "mA";
 1215                 } else if (strcmp(type, "temp-sensor") == 0) {
 1216                         sens->type = SMU_TEMP_SENSOR;
 1217                         units = "C";
 1218                 } else if (strcmp(type, "voltage-sensor") == 0) {
 1219                         sens->type = SMU_VOLTAGE_SENSOR;
 1220                         units = "mV";
 1221                 } else if (strcmp(type, "power-sensor") == 0) {
 1222                         sens->type = SMU_POWER_SENSOR;
 1223                         units = "mW";
 1224                 } else {
 1225                         continue;
 1226                 }
 1227 
 1228                 OF_getprop(child, "reg", &sens->reg, sizeof(cell_t));
 1229                 OF_getprop(child, "zone", &sens->therm.zone, sizeof(int));
 1230                 OF_getprop(child, "location", sens->therm.name,
 1231                     sizeof(sens->therm.name));
 1232 
 1233                 for (i = 0; i < strlen(sens->therm.name); i++) {
 1234                         sysctl_name[i] = tolower(sens->therm.name[i]);
 1235                         if (isspace(sysctl_name[i]))
 1236                                 sysctl_name[i] = '_';
 1237                 }
 1238                 sysctl_name[i] = 0;
 1239 
 1240                 sprintf(sysctl_desc,"%s (%s)", sens->therm.name, units);
 1241 
 1242                 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
 1243                     sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
 1244                     dev, sc->sc_nsensors, smu_sensor_sysctl, 
 1245                     (sens->type == SMU_TEMP_SENSOR) ? "IK" : "I", sysctl_desc);
 1246 
 1247                 if (sens->type == SMU_TEMP_SENSOR) {
 1248                         /* Make up some numbers */
 1249                         sens->therm.target_temp = 500 + 2731; /* 50 C */
 1250                         sens->therm.max_temp = 900 + 2731; /* 90 C */
 1251 
 1252                         sens->therm.read =
 1253                             (int (*)(struct pmac_therm *))smu_sensor_read;
 1254                         pmac_thermal_sensor_register(&sens->therm);
 1255                 }
 1256 
 1257                 sens++;
 1258                 sc->sc_nsensors++;
 1259         }
 1260 }
 1261 
 1262 static void
 1263 smu_set_sleepled(void *xdev, int onoff)
 1264 {
 1265         static struct smu_cmd cmd;
 1266         device_t smu = xdev;
 1267 
 1268         cmd.cmd = SMU_MISC;
 1269         cmd.len = 3;
 1270         cmd.data[0] = SMU_MISC_LED_CTRL;
 1271         cmd.data[1] = 0;
 1272         cmd.data[2] = onoff; 
 1273 
 1274         smu_run_cmd(smu, &cmd, 0);
 1275 }
 1276 
 1277 static int
 1278 smu_server_mode(SYSCTL_HANDLER_ARGS)
 1279 {
 1280         struct smu_cmd cmd;
 1281         u_int server_mode;
 1282         device_t smu = arg1;
 1283         int error;
 1284 
 1285         cmd.cmd = SMU_POWER_EVENTS;
 1286         cmd.len = 1;
 1287         cmd.data[0] = SMU_PWR_GET_POWERUP;
 1288 
 1289         error = smu_run_cmd(smu, &cmd, 1);
 1290 
 1291         if (error)
 1292                 return (error);
 1293 
 1294         server_mode = (cmd.data[1] & SMU_WAKEUP_AC_INSERT) ? 1 : 0;
 1295 
 1296         error = sysctl_handle_int(oidp, &server_mode, 0, req);
 1297 
 1298         if (error || !req->newptr)
 1299                 return (error);
 1300 
 1301         if (server_mode == 1)
 1302                 cmd.data[0] = SMU_PWR_SET_POWERUP;
 1303         else if (server_mode == 0)
 1304                 cmd.data[0] = SMU_PWR_CLR_POWERUP;
 1305         else
 1306                 return (EINVAL);
 1307 
 1308         cmd.len = 3;
 1309         cmd.data[1] = 0;
 1310         cmd.data[2] = SMU_WAKEUP_AC_INSERT;
 1311 
 1312         return (smu_run_cmd(smu, &cmd, 1));
 1313 }
 1314 
 1315 static void
 1316 smu_shutdown(void *xdev, int howto)
 1317 {
 1318         device_t smu = xdev;
 1319         struct smu_cmd cmd;
 1320 
 1321         cmd.cmd = SMU_POWER;
 1322         if (howto & RB_HALT)
 1323                 strcpy(cmd.data, "SHUTDOWN");
 1324         else
 1325                 strcpy(cmd.data, "RESTART");
 1326 
 1327         cmd.len = strlen(cmd.data);
 1328 
 1329         smu_run_cmd(smu, &cmd, 1);
 1330 
 1331         for (;;);
 1332 }
 1333 
 1334 static int
 1335 smu_gettime(device_t dev, struct timespec *ts)
 1336 {
 1337         struct smu_cmd cmd;
 1338         struct clocktime ct;
 1339 
 1340         cmd.cmd = SMU_RTC;
 1341         cmd.len = 1;
 1342         cmd.data[0] = SMU_RTC_GET;
 1343 
 1344         if (smu_run_cmd(dev, &cmd, 1) != 0)
 1345                 return (ENXIO);
 1346 
 1347         ct.nsec = 0;
 1348         ct.sec  = bcd2bin(cmd.data[0]);
 1349         ct.min  = bcd2bin(cmd.data[1]);
 1350         ct.hour = bcd2bin(cmd.data[2]);
 1351         ct.dow  = bcd2bin(cmd.data[3]);
 1352         ct.day  = bcd2bin(cmd.data[4]);
 1353         ct.mon  = bcd2bin(cmd.data[5]);
 1354         ct.year = bcd2bin(cmd.data[6]) + 2000;
 1355 
 1356         return (clock_ct_to_ts(&ct, ts));
 1357 }
 1358 
 1359 static int
 1360 smu_settime(device_t dev, struct timespec *ts)
 1361 {
 1362         static struct smu_cmd cmd;
 1363         struct clocktime ct;
 1364 
 1365         cmd.cmd = SMU_RTC;
 1366         cmd.len = 8;
 1367         cmd.data[0] = SMU_RTC_SET;
 1368 
 1369         clock_ts_to_ct(ts, &ct);
 1370 
 1371         cmd.data[1] = bin2bcd(ct.sec);
 1372         cmd.data[2] = bin2bcd(ct.min);
 1373         cmd.data[3] = bin2bcd(ct.hour);
 1374         cmd.data[4] = bin2bcd(ct.dow);
 1375         cmd.data[5] = bin2bcd(ct.day);
 1376         cmd.data[6] = bin2bcd(ct.mon);
 1377         cmd.data[7] = bin2bcd(ct.year - 2000);
 1378 
 1379         return (smu_run_cmd(dev, &cmd, 0));
 1380 }
 1381 
 1382 /* SMU I2C Interface */
 1383 
 1384 static int smuiic_probe(device_t dev);
 1385 static int smuiic_attach(device_t dev);
 1386 static int smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
 1387 static phandle_t smuiic_get_node(device_t bus, device_t dev);
 1388 
 1389 static device_method_t smuiic_methods[] = {
 1390         /* device interface */
 1391         DEVMETHOD(device_probe,         smuiic_probe),
 1392         DEVMETHOD(device_attach,        smuiic_attach),
 1393 
 1394         /* iicbus interface */
 1395         DEVMETHOD(iicbus_callback,      iicbus_null_callback),
 1396         DEVMETHOD(iicbus_transfer,      smuiic_transfer),
 1397 
 1398         /* ofw_bus interface */
 1399         DEVMETHOD(ofw_bus_get_node,     smuiic_get_node),
 1400         { 0, 0 }
 1401 };
 1402 
 1403 struct smuiic_softc {
 1404         struct mtx      sc_mtx;
 1405         volatile int    sc_iic_inuse;
 1406         int             sc_busno;
 1407 };
 1408 
 1409 static driver_t smuiic_driver = {
 1410         "iichb",
 1411         smuiic_methods,
 1412         sizeof(struct smuiic_softc)
 1413 };
 1414 
 1415 DRIVER_MODULE(smuiic, smu, smuiic_driver, 0, 0);
 1416 
 1417 static void
 1418 smu_attach_i2c(device_t smu, phandle_t i2croot)
 1419 {
 1420         phandle_t child;
 1421         device_t cdev;
 1422         struct ofw_bus_devinfo *dinfo;
 1423         char name[32];
 1424 
 1425         for (child = OF_child(i2croot); child != 0; child = OF_peer(child)) {
 1426                 if (OF_getprop(child, "name", name, sizeof(name)) <= 0)
 1427                         continue;
 1428 
 1429                 if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0)
 1430                         continue;
 1431 
 1432                 dinfo = malloc(sizeof(struct ofw_bus_devinfo), M_SMU,
 1433                     M_WAITOK | M_ZERO);
 1434                 if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
 1435                         free(dinfo, M_SMU);
 1436                         continue;
 1437                 }
 1438 
 1439                 cdev = device_add_child(smu, NULL, -1);
 1440                 if (cdev == NULL) {
 1441                         device_printf(smu, "<%s>: device_add_child failed\n",
 1442                             dinfo->obd_name);
 1443                         ofw_bus_gen_destroy_devinfo(dinfo);
 1444                         free(dinfo, M_SMU);
 1445                         continue;
 1446                 }
 1447                 device_set_ivars(cdev, dinfo);
 1448         }
 1449 }
 1450 
 1451 static int
 1452 smuiic_probe(device_t dev)
 1453 {
 1454         const char *name;
 1455 
 1456         name = ofw_bus_get_name(dev);
 1457         if (name == NULL)
 1458                 return (ENXIO);
 1459 
 1460         if (strcmp(name, "i2c-bus") == 0 || strcmp(name, "i2c") == 0) {
 1461                 device_set_desc(dev, "SMU I2C controller");
 1462                 return (0);
 1463         }
 1464 
 1465         return (ENXIO);
 1466 }
 1467 
 1468 static int
 1469 smuiic_attach(device_t dev)
 1470 {
 1471         struct smuiic_softc *sc = device_get_softc(dev);
 1472         mtx_init(&sc->sc_mtx, "smuiic", NULL, MTX_DEF);
 1473         sc->sc_iic_inuse = 0;
 1474 
 1475         /* Get our bus number */
 1476         OF_getprop(ofw_bus_get_node(dev), "reg", &sc->sc_busno,
 1477             sizeof(sc->sc_busno));
 1478 
 1479         /* Add the IIC bus layer */
 1480         device_add_child(dev, "iicbus", -1);
 1481 
 1482         return (bus_generic_attach(dev));
 1483 }
 1484 
 1485 static int
 1486 smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
 1487 {
 1488         struct smuiic_softc *sc = device_get_softc(dev);
 1489         struct smu_cmd cmd;
 1490         int i, j, error;
 1491 
 1492         mtx_lock(&sc->sc_mtx);
 1493         while (sc->sc_iic_inuse)
 1494                 mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 100);
 1495 
 1496         sc->sc_iic_inuse = 1;
 1497         error = 0;
 1498 
 1499         for (i = 0; i < nmsgs; i++) {
 1500                 cmd.cmd = SMU_I2C;
 1501                 cmd.data[0] = sc->sc_busno;
 1502                 if (msgs[i].flags & IIC_M_NOSTOP)
 1503                         cmd.data[1] = SMU_I2C_COMBINED;
 1504                 else
 1505                         cmd.data[1] = SMU_I2C_SIMPLE;
 1506 
 1507                 cmd.data[2] = msgs[i].slave;
 1508                 if (msgs[i].flags & IIC_M_RD)
 1509                         cmd.data[2] |= 1; 
 1510 
 1511                 if (msgs[i].flags & IIC_M_NOSTOP) {
 1512                         KASSERT(msgs[i].len < 4,
 1513                             ("oversize I2C combined message"));
 1514 
 1515                         cmd.data[3] = min(msgs[i].len, 3);
 1516                         memcpy(&cmd.data[4], msgs[i].buf, min(msgs[i].len, 3));
 1517                         i++; /* Advance to next part of message */
 1518                 } else {
 1519                         cmd.data[3] = 0;
 1520                         memset(&cmd.data[4], 0, 3);
 1521                 }
 1522 
 1523                 cmd.data[7] = msgs[i].slave;
 1524                 if (msgs[i].flags & IIC_M_RD)
 1525                         cmd.data[7] |= 1; 
 1526 
 1527                 cmd.data[8] = msgs[i].len;
 1528                 if (msgs[i].flags & IIC_M_RD) {
 1529                         memset(&cmd.data[9], 0xff, msgs[i].len);
 1530                         cmd.len = 9;
 1531                 } else {
 1532                         memcpy(&cmd.data[9], msgs[i].buf, msgs[i].len);
 1533                         cmd.len = 9 + msgs[i].len;
 1534                 }
 1535 
 1536                 mtx_unlock(&sc->sc_mtx);
 1537                 smu_run_cmd(device_get_parent(dev), &cmd, 1);
 1538                 mtx_lock(&sc->sc_mtx);
 1539 
 1540                 for (j = 0; j < 10; j++) {
 1541                         cmd.cmd = SMU_I2C;
 1542                         cmd.len = 1;
 1543                         cmd.data[0] = 0;
 1544                         memset(&cmd.data[1], 0xff, msgs[i].len);
 1545                         
 1546                         mtx_unlock(&sc->sc_mtx);
 1547                         smu_run_cmd(device_get_parent(dev), &cmd, 1);
 1548                         mtx_lock(&sc->sc_mtx);
 1549                         
 1550                         if (!(cmd.data[0] & 0x80))
 1551                                 break;
 1552 
 1553                         mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 10);
 1554                 }
 1555                 
 1556                 if (cmd.data[0] & 0x80) {
 1557                         error = EIO;
 1558                         msgs[i].len = 0;
 1559                         goto exit;
 1560                 }
 1561                 memcpy(msgs[i].buf, &cmd.data[1], msgs[i].len);
 1562                 msgs[i].len = cmd.len - 1;
 1563         }
 1564 
 1565     exit:
 1566         sc->sc_iic_inuse = 0;
 1567         mtx_unlock(&sc->sc_mtx);
 1568         wakeup(sc);
 1569         return (error);
 1570 }
 1571 
 1572 static phandle_t
 1573 smuiic_get_node(device_t bus, device_t dev)
 1574 {
 1575 
 1576         return (ofw_bus_get_node(bus));
 1577 }

Cache object: ed1237f78e520e117acfa5fa0f139a3a


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