[BACK]Return to smu.c CVS log [TXT][DIR] Up to [local] / sys / arch / macppc / dev

Annotation of sys/arch/macppc/dev/smu.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: smu.c,v 1.19 2007/05/20 23:38:52 thib Exp $   */
                      2:
                      3: /*
                      4:  * Copyright (c) 2005 Mark Kettenis
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/param.h>
                     20: #include <sys/systm.h>
                     21: #include <sys/device.h>
                     22: #include <sys/kernel.h>
                     23: #include <sys/rwlock.h>
                     24: #include <sys/proc.h>
                     25: #include <sys/sensors.h>
                     26:
                     27: #include <machine/autoconf.h>
                     28: #include <machine/cpu.h>
                     29:
                     30: #include <dev/clock_subr.h>
                     31: #include <dev/i2c/i2cvar.h>
                     32: #include <dev/ofw/openfirm.h>
                     33:
                     34: #include <arch/macppc/dev/maci2cvar.h>
                     35:
                     36: int     smu_match(struct device *, void *, void *);
                     37: void    smu_attach(struct device *, struct device *, void *);
                     38:
                     39: #define SMU_MAXFANS    3
                     40:
                     41: struct smu_fan {
                     42:        u_int8_t        reg;
                     43:        u_int16_t       min_rpm;
                     44:        u_int16_t       max_rpm;
                     45:        u_int16_t       unmanaged_rpm;
                     46:        struct ksensor  sensor;
                     47: };
                     48:
                     49: #define SMU_MAXSENSORS 3
                     50:
                     51: struct smu_sensor {
                     52:        u_int8_t        reg;
                     53:        struct ksensor  sensor;
                     54: };
                     55:
                     56: struct smu_softc {
                     57:         struct device   sc_dev;
                     58:
                     59:        /* SMU command buffer. */
                     60:         bus_dma_tag_t   sc_dmat;
                     61:         bus_dmamap_t    sc_cmdmap;
                     62:         bus_dma_segment_t sc_cmdseg[1];
                     63:         caddr_t         sc_cmd;
                     64:        struct rwlock   sc_lock;
                     65:
                     66:        /* Doorbell and mailbox. */
                     67:        struct ppc_bus_space sc_mem_bus_space;
                     68:        bus_space_tag_t sc_memt;
                     69:        bus_space_handle_t sc_gpioh;
                     70:        bus_space_handle_t sc_buffh;
                     71:
                     72:        struct smu_fan  sc_fans[SMU_MAXFANS];
                     73:        int             sc_num_fans;
                     74:
                     75:        struct smu_sensor sc_sensors[SMU_MAXSENSORS];
                     76:        int             sc_num_sensors;
                     77:
                     78:        struct ksensordev sc_sensordev;
                     79:
                     80:        u_int16_t       sc_cpu_diode_scale;
                     81:        int16_t         sc_cpu_diode_offset;
                     82:        u_int16_t       sc_cpu_volt_scale;
                     83:        int16_t         sc_cpu_volt_offset;
                     84:        u_int16_t       sc_cpu_curr_scale;
                     85:        int16_t         sc_cpu_curr_offset;
                     86:
                     87:        struct i2c_controller sc_i2c_tag;
                     88: };
                     89:
                     90: struct cfattach smu_ca = {
                     91:         sizeof(struct smu_softc), smu_match, smu_attach
                     92: };
                     93:
                     94: struct cfdriver smu_cd = {
                     95:         NULL, "smu", DV_DULL,
                     96: };
                     97:
                     98: /* SMU command */
                     99: struct smu_cmd {
                    100:         u_int8_t        cmd;
                    101:         u_int8_t        len;
                    102:         u_int8_t        data[254];
                    103: };
                    104: #define SMU_CMDSZ       sizeof(struct smu_cmd)
                    105:
                    106: /* RTC */
                    107: #define SMU_RTC                        0x8e
                    108: #define SMU_RTC_SET_DATETIME   0x80
                    109: #define SMU_RTC_GET_DATETIME   0x81
                    110:
                    111: /* ADC */
                    112: #define SMU_ADC                        0xd8
                    113:
                    114: /* Fan control */
                    115: #define SMU_FAN                        0x4a
                    116:
                    117: /* Data partitions */
                    118: #define SMU_PARTITION          0x3e
                    119: #define SMU_PARTITION_LATEST   0x01
                    120: #define SMU_PARTITION_BASE     0x02
                    121: #define SMU_PARTITION_UPDATE   0x03
                    122:
                    123: /* I2C */
                    124: #define SMU_I2C                        0x9a
                    125: #define SMU_I2C_SIMPLE         0x00
                    126: #define SMU_I2C_NORMAL         0x01
                    127: #define SMU_I2C_COMBINED       0x02
                    128:
                    129: /* Power Management */
                    130: #define SMU_POWER              0xaa
                    131:
                    132: /* Miscellaneous */
                    133: #define SMU_MISC               0xee
                    134: #define SMU_MISC_GET_DATA      0x02
                    135:
                    136: int    smu_intr(void *);
                    137:
                    138: int    smu_do_cmd(struct smu_softc *, int);
                    139: int    smu_time_read(time_t *);
                    140: int    smu_time_write(time_t);
                    141: int    smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
                    142: int    smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
                    143: int    smu_fan_refresh(struct smu_softc *, struct smu_fan *);
                    144: int    smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
                    145: void   smu_refresh_sensors(void *);
                    146:
                    147: int    smu_i2c_acquire_bus(void *, int);
                    148: void   smu_i2c_release_bus(void *, int);
                    149: int    smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
                    150:            const void *, size_t, void *buf, size_t, int);
                    151:
                    152: void   smu_slew_voltage(u_int);
                    153:
                    154: #define GPIO_DDR        0x04    /* Data direction */
                    155: #define GPIO_DDR_OUTPUT 0x04    /* Output */
                    156: #define GPIO_DDR_INPUT  0x00    /* Input */
                    157:
                    158: #define GPIO_LEVEL     0x02    /* Pin level (RO) */
                    159:
                    160: #define GPIO_DATA       0x01    /* Data */
                    161:
                    162: int
                    163: smu_match(struct device *parent, void *cf, void *aux)
                    164: {
                    165:         struct confargs *ca = aux;
                    166:
                    167:         if (strcmp(ca->ca_name, "smu") == 0)
                    168:                 return (1);
                    169:         return (0);
                    170: }
                    171:
                    172: /* XXX */
                    173: extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
                    174:
                    175: void
                    176: smu_attach(struct device *parent, struct device *self, void *aux)
                    177: {
                    178:         struct smu_softc *sc = (struct smu_softc *)self;
                    179:        struct confargs *ca = aux;
                    180:        struct i2cbus_attach_args iba;
                    181:        struct smu_fan *fan;
                    182:        struct smu_sensor *sensor;
                    183:        int nseg, node;
                    184:        char type[32], loc[32];
                    185:        u_int32_t reg, intr, gpio, val;
                    186:        u_int8_t data[12];
                    187:
                    188:        /* XXX */
                    189:        sc->sc_mem_bus_space.bus_base = 0x80000000;
                    190:        sc->sc_mem_bus_space.bus_size = 0;
                    191:        sc->sc_mem_bus_space.bus_io = 0;
                    192:        sc->sc_memt = &sc->sc_mem_bus_space;
                    193:
                    194:        /* Map smu-doorbell gpio. */
                    195:        if (OF_getprop(ca->ca_node, "platform-doorbell-ack",
                    196:                &node, sizeof node) <= 0 ||
                    197:            OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
                    198:            OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 ||
                    199:            OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) {
                    200:                printf(": cannot find smu-doorbell gpio\n");
                    201:                return;
                    202:        }
                    203:        if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) {
                    204:                printf(": cannot map smu-doorbell gpio\n");
                    205:                return;
                    206:        }
                    207:
                    208:        /* XXX Should get this from OF. */
                    209:        if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) {
                    210:                printf(": cannot map smu-doorbell buffer\n");
                    211:                return;
                    212:        }
                    213:
                    214:        /* XXX */
                    215:         sc->sc_dmat = &pci_bus_dma_tag;
                    216:
                    217:        /* Allocate and map SMU command buffer.  */
                    218:        if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0,
                    219:             sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) {
                    220:                 printf(": cannot allocate cmd buffer\n");
                    221:                 return;
                    222:         }
                    223:         if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg,
                    224:             SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) {
                    225:                 printf(": cannot map cmd buffer\n");
                    226:                 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
                    227:                 return;
                    228:         }
                    229:         if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0,
                    230:             BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) {
                    231:                 printf(": cannot create cmd dmamap\n");
                    232:                 bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
                    233:                 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
                    234:                 return;
                    235:         }
                    236:         if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd,
                    237:             SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) {
                    238:                 printf(": cannot load cmd dmamap\n");
                    239:                 bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap);
                    240:                 bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
                    241:                 bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg);
                    242:                 return;
                    243:         }
                    244:
                    245:        rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
                    246:
                    247:        /* Establish smu-doorbell interrupt. */
                    248:        mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
                    249:            smu_intr, sc, sc->sc_dev.dv_xname);
                    250:
                    251:        /* Initialize global variables that control RTC functionality. */
                    252:        time_read = smu_time_read;
                    253:        time_write = smu_time_write;
                    254:
                    255:        /* Fans */
                    256:        node = OF_getnodebyname(ca->ca_node, "rpm-fans");
                    257:        if (node == 0)
                    258:                node = OF_getnodebyname(ca->ca_node, "fans");
                    259:        for (node = OF_child(node); node; node = OF_peer(node)) {
                    260:                if (OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
                    261:                    OF_getprop(node, "device_type", type, sizeof type) <= 0)
                    262:                        continue;
                    263:
                    264:                if (strcmp(type, "fan-rpm-control") != 0) {
                    265:                        printf(": unsupported fan type: %s\n", type);
                    266:                        return;
                    267:                }
                    268:
                    269:                if (sc->sc_num_fans >= SMU_MAXFANS) {
                    270:                        printf(": too many fans\n");
                    271:                        return;
                    272:                }
                    273:
                    274:                fan = &sc->sc_fans[sc->sc_num_fans++];
                    275:                fan->sensor.type = SENSOR_FANRPM;
                    276:                fan->sensor.flags = SENSOR_FINVALID;
                    277:                fan->reg = reg;
                    278:
                    279:                if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
                    280:                        val = 0;
                    281:                fan->min_rpm = val;
                    282:                if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
                    283:                        val = 0xffff;
                    284:                fan->max_rpm = val;
                    285:                if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0)
                    286:                        val = fan->max_rpm;
                    287:                fan->unmanaged_rpm = val;
                    288:
                    289:                if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
                    290:                        strlcpy(loc, "Unknown", sizeof loc);
                    291:                strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
                    292:
                    293:                /* Start running fans at their "unmanaged" speed. */
                    294:                smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);
                    295:
                    296:                sensor_attach(&sc->sc_sensordev, &fan->sensor);
                    297:        }
                    298:
                    299:        /*
                    300:         * Bail out if we didn't find any fans.  If we don't set the
                    301:         * fans to a safe speed, but tickle the SMU periodically by
                    302:         * reading sensors, the fans will never spin up and the
                    303:         * machine might overheat.
                    304:         */
                    305:        if (sc->sc_num_fans == 0) {
                    306:                printf(": no fans\n");
                    307:                return;
                    308:        }
                    309:
                    310:        /* Sensors */
                    311:        node = OF_getnodebyname(ca->ca_node, "sensors");
                    312:        for (node = OF_child(node); node; node = OF_peer(node)) {
                    313:                if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
                    314:                    OF_getprop(node, "device_type", type, sizeof type) <= 0)
                    315:                        continue;
                    316:
                    317:                sensor = &sc->sc_sensors[sc->sc_num_sensors++];
                    318:                sensor->sensor.flags = SENSOR_FINVALID;
                    319:                sensor->reg = val;
                    320:
                    321:                if (strcmp(type, "current-sensor") == 0) {
                    322:                        sensor->sensor.type = SENSOR_AMPS;
                    323:                } else if (strcmp(type, "temp-sensor") == 0) {
                    324:                        sensor->sensor.type = SENSOR_TEMP;
                    325:                } else if (strcmp(type, "voltage-sensor") == 0) {
                    326:                        sensor->sensor.type = SENSOR_VOLTS_DC;
                    327:                } else {
                    328:                        sensor->sensor.type = SENSOR_INTEGER;
                    329:                }
                    330:
                    331:                if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
                    332:                        strlcpy(loc, "Unknown", sizeof loc);
                    333:                strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);
                    334:
                    335:                sensor_attach(&sc->sc_sensordev, &sensor->sensor);
                    336:        }
                    337:
                    338:        /* Register sensor device with sysctl */
                    339:        strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
                    340:            sizeof(sc->sc_sensordev.xname));
                    341:        sensordev_install(&sc->sc_sensordev);
                    342:
                    343:        /* CPU temperature diode calibration */
                    344:        smu_get_datablock(sc, 0x18, data, sizeof data);
                    345:        sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
                    346:        sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];
                    347:
                    348:        /* CPU power (voltage and current) calibration */
                    349:        smu_get_datablock(sc, 0x21, data, sizeof data);
                    350:        sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
                    351:        sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
                    352:        sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
                    353:        sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];
                    354:
                    355:        sensor_task_register(sc, smu_refresh_sensors, 5);
                    356:        printf("\n");
                    357:
                    358:        ppc64_slew_voltage = smu_slew_voltage;
                    359:
                    360:        sc->sc_i2c_tag.ic_cookie = sc;
                    361:        sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
                    362:        sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
                    363:        sc->sc_i2c_tag.ic_exec = smu_i2c_exec;
                    364:
                    365:        node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
                    366:        node = OF_child(node);
                    367:
                    368:        bzero(&iba, sizeof iba);
                    369:        iba.iba_name = "iic";
                    370:        iba.iba_tag = &sc->sc_i2c_tag;
                    371:        iba.iba_bus_scan = maciic_scan;
                    372:        iba.iba_bus_scan_arg = &node;
                    373:        config_found(&sc->sc_dev, &iba, NULL);
                    374: }
                    375:
                    376: int
                    377: smu_intr(void *arg)
                    378: {
                    379:        wakeup(arg);
                    380:        return 1;
                    381: }
                    382:
                    383: int
                    384: smu_do_cmd(struct smu_softc *sc, int timo)
                    385: {
                    386:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    387:        u_int8_t gpio, ack = ~cmd->cmd;
                    388:        int error;
                    389:
                    390:        /* Write to mailbox.  */
                    391:        bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
                    392:            sc->sc_cmdmap->dm_segs->ds_addr);
                    393:
                    394:        /* Flush to RAM. */
                    395:        asm __volatile__ ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");
                    396:
                    397:        /* Ring doorbell.  */
                    398:        bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);
                    399:
                    400:        do {
                    401:                error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000);
                    402:                if (error)
                    403:                        return (error);
                    404:                gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
                    405:        } while (!(gpio & (GPIO_DATA)));
                    406:
                    407:        /* CPU might have brought back the cache line. */
                    408:        asm __volatile__ ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
                    409:
                    410:        if (cmd->cmd != ack)
                    411:                return (EIO);
                    412:        return (0);
                    413: }
                    414:
                    415: int
                    416: smu_time_read(time_t *secs)
                    417: {
                    418:        struct smu_softc *sc = smu_cd.cd_devs[0];
                    419:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    420:        struct clock_ymdhms dt;
                    421:        int error;
                    422:
                    423:        rw_enter_write(&sc->sc_lock);
                    424:
                    425:        cmd->cmd = SMU_RTC;
                    426:        cmd->len = 1;
                    427:        cmd->data[0] = SMU_RTC_GET_DATETIME;
                    428:        error = smu_do_cmd(sc, 800);
                    429:        if (error) {
                    430:                rw_exit_write(&sc->sc_lock);
                    431:
                    432:                *secs = 0;
                    433:                return (error);
                    434:        }
                    435:
                    436:        dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
                    437:        dt.dt_mon = FROMBCD(cmd->data[5]);
                    438:        dt.dt_day = FROMBCD(cmd->data[4]);
                    439:        dt.dt_hour = FROMBCD(cmd->data[2]);
                    440:        dt.dt_min = FROMBCD(cmd->data[1]);
                    441:        dt.dt_sec = FROMBCD(cmd->data[0]);
                    442:
                    443:        rw_exit_write(&sc->sc_lock);
                    444:
                    445:        *secs = clock_ymdhms_to_secs(&dt);
                    446:        return (0);
                    447: }
                    448:
                    449: int
                    450: smu_time_write(time_t secs)
                    451: {
                    452:        struct smu_softc *sc = smu_cd.cd_devs[0];
                    453:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    454:        struct clock_ymdhms dt;
                    455:        int error;
                    456:
                    457:        clock_secs_to_ymdhms(secs, &dt);
                    458:
                    459:        rw_enter_write(&sc->sc_lock);
                    460:
                    461:        cmd->cmd = SMU_RTC;
                    462:        cmd->len = 8;
                    463:        cmd->data[0] = SMU_RTC_SET_DATETIME;
                    464:        cmd->data[1] = TOBCD(dt.dt_sec);
                    465:        cmd->data[2] = TOBCD(dt.dt_min);
                    466:        cmd->data[3] = TOBCD(dt.dt_hour);
                    467:        cmd->data[4] = TOBCD(dt.dt_wday);
                    468:        cmd->data[5] = TOBCD(dt.dt_day);
                    469:        cmd->data[6] = TOBCD(dt.dt_mon);
                    470:        cmd->data[7] = TOBCD(dt.dt_year - 2000);
                    471:        error = smu_do_cmd(sc, 800);
                    472:
                    473:        rw_exit_write(&sc->sc_lock);
                    474:
                    475:        return (error);
                    476: }
                    477:
                    478:
                    479: int
                    480: smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
                    481: {
                    482:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    483:        u_int8_t addr[4];
                    484:        int error;
                    485:
                    486:        cmd->cmd = SMU_PARTITION;
                    487:        cmd->len = 2;
                    488:        cmd->data[0] = SMU_PARTITION_LATEST;
                    489:        cmd->data[1] = id;
                    490:        error = smu_do_cmd(sc, 800);
                    491:        if (error)
                    492:                return (error);
                    493:
                    494:        addr[0] = 0x00;
                    495:        addr[1] = 0x00;
                    496:        addr[2] = cmd->data[0];
                    497:        addr[3] = cmd->data[1];
                    498:
                    499:        cmd->cmd = SMU_MISC;
                    500:        cmd->len = 7;
                    501:        cmd->data[0] = SMU_MISC_GET_DATA;
                    502:        cmd->data[1] = sizeof(u_int32_t);
                    503:        cmd->data[2] = addr[0];
                    504:        cmd->data[3] = addr[1];
                    505:        cmd->data[4] = addr[2];
                    506:        cmd->data[5] = addr[3];
                    507:        cmd->data[6] = len;
                    508:        error = smu_do_cmd(sc, 800);
                    509:        if (error)
                    510:                return (error);
                    511:
                    512:        memcpy(buf, cmd->data, len);
                    513:        return (0);
                    514: }
                    515:
                    516: int
                    517: smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
                    518: {
                    519:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    520:
                    521:        /*
                    522:         * On the PowerMac8,2 this command expects the requested fan
                    523:         * speed at a different location in the command block than on
                    524:         * the PowerMac8,1.  We simply store the value at both
                    525:         * locations.
                    526:         */
                    527:        cmd->cmd = SMU_FAN;
                    528:        cmd->len = 14;
                    529:        cmd->data[0] = 0x00;    /* fan-rpm-control */
                    530:        cmd->data[1] = 0x01 << fan->reg;
                    531:        cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
                    532:        cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff);
                    533:        return smu_do_cmd(sc, 800);
                    534: }
                    535:
                    536: int
                    537: smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
                    538: {
                    539:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    540:        int error;
                    541:
                    542:        cmd->cmd = SMU_FAN;
                    543:        cmd->len = 2;
                    544:        cmd->data[0] = 0x01;    /* fan-rpm-control */
                    545:        cmd->data[1] = 0x01 << fan->reg;
                    546:        error = smu_do_cmd(sc, 800);
                    547:        if (error) {
                    548:                fan->sensor.flags = SENSOR_FINVALID;
                    549:                return (error);
                    550:        }
                    551:        fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2];
                    552:        fan->sensor.flags = 0;
                    553:        return (0);
                    554: }
                    555:
                    556: int
                    557: smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor)
                    558: {
                    559:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    560:        int64_t value;
                    561:        int error;
                    562:
                    563:        cmd->cmd = SMU_ADC;
                    564:        cmd->len = 1;
                    565:        cmd->data[0] = sensor->reg;
                    566:        error = smu_do_cmd(sc, 800);
                    567:        if (error) {
                    568:                sensor->sensor.flags = SENSOR_FINVALID;
                    569:                return (error);
                    570:        }
                    571:        value = (cmd->data[0] << 8) + cmd->data[1];
                    572:        switch (sensor->sensor.type) {
                    573:        case SENSOR_TEMP:
                    574:                value *= sc->sc_cpu_diode_scale;
                    575:                value >>= 3;
                    576:                value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
                    577:                value <<= 1;
                    578:
                    579:                /* Convert from 16.16 fixed point degC into muK. */
                    580:                value *= 15625;
                    581:                value /= 1024;
                    582:                value += 273150000;
                    583:                break;
                    584:
                    585:        case SENSOR_VOLTS_DC:
                    586:                value *= sc->sc_cpu_volt_scale;
                    587:                value += sc->sc_cpu_volt_offset;
                    588:                value <<= 4;
                    589:
                    590:                /* Convert from 16.16 fixed point V into muV. */
                    591:                value *= 15625;
                    592:                value /= 1024;
                    593:                break;
                    594:
                    595:        case SENSOR_AMPS:
                    596:                value *= sc->sc_cpu_curr_scale;
                    597:                value += sc->sc_cpu_curr_offset;
                    598:                value <<= 4;
                    599:
                    600:                /* Convert from 16.16 fixed point A into muA. */
                    601:                value *= 15625;
                    602:                value /= 1024;
                    603:                break;
                    604:
                    605:        default:
                    606:                break;
                    607:        }
                    608:        sensor->sensor.value = value;
                    609:        sensor->sensor.flags = 0;
                    610:        return (0);
                    611: }
                    612:
                    613: void
                    614: smu_refresh_sensors(void *arg)
                    615: {
                    616:        struct smu_softc *sc = arg;
                    617:        int i;
                    618:
                    619:        rw_enter_write(&sc->sc_lock);
                    620:        for (i = 0; i < sc->sc_num_sensors; i++)
                    621:                smu_sensor_refresh(sc, &sc->sc_sensors[i]);
                    622:        for (i = 0; i < sc->sc_num_fans; i++)
                    623:                smu_fan_refresh(sc, &sc->sc_fans[i]);
                    624:        rw_exit_write(&sc->sc_lock);
                    625: }
                    626:
                    627: int
                    628: smu_i2c_acquire_bus(void *cookie, int flags)
                    629: {
                    630:        struct smu_softc *sc = cookie;
                    631:
                    632:        if (flags & I2C_F_POLL)
                    633:                return (0);
                    634:
                    635:        return (rw_enter(&sc->sc_lock, RW_WRITE));
                    636: }
                    637:
                    638: void
                    639: smu_i2c_release_bus(void *cookie, int flags)
                    640: {
                    641:        struct smu_softc *sc = cookie;
                    642:
                    643:         if (flags & I2C_F_POLL)
                    644:                 return;
                    645:
                    646:        rw_exit(&sc->sc_lock);
                    647: }
                    648:
                    649: int
                    650: smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
                    651:     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
                    652: {
                    653:        struct smu_softc *sc = cookie;
                    654:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    655:        u_int8_t smu_op = SMU_I2C_NORMAL;
                    656:        int error, retries = 10;
                    657:
                    658:        if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
                    659:                return (EINVAL);
                    660:
                    661:        if(cmdlen == 0)
                    662:                smu_op = SMU_I2C_SIMPLE;
                    663:        else if (I2C_OP_READ_P(op))
                    664:                smu_op = SMU_I2C_COMBINED;
                    665:
                    666:        cmd->cmd = SMU_I2C;
                    667:        cmd->len = 9 + len;
                    668:        cmd->data[0] = 0xb;
                    669:        cmd->data[1] = smu_op;
                    670:        cmd->data[2] = addr << 1;
                    671:        cmd->data[3] = cmdlen;
                    672:        memcpy (&cmd->data[4], cmdbuf, cmdlen);
                    673:        cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
                    674:        cmd->data[8] = len;
                    675:        memcpy(&cmd->data[9], buf, len);
                    676:
                    677:        error = smu_do_cmd(sc, 250);
                    678:        if (error)
                    679:                return error;
                    680:
                    681:        while (retries--) {
                    682:                cmd->cmd = SMU_I2C;
                    683:                cmd->len = 1;
                    684:                cmd->data[0] = 0;
                    685:                memset(&cmd->data[1], 0xff, len);
                    686:
                    687:                error = smu_do_cmd(sc, 250);
                    688:                if (error)
                    689:                        return error;
                    690:
                    691:                if ((cmd->data[0] & 0x80) == 0)
                    692:                        break;
                    693:                if (cmd->data[0] == 0xfd)
                    694:                        break;
                    695:
                    696:                DELAY(15 * 1000);
                    697:        }
                    698:
                    699:        if (cmd->data[0] & 0x80)
                    700:                return (EIO);
                    701:
                    702:        if (I2C_OP_READ_P(op))
                    703:                memcpy(buf, &cmd->data[1], len);
                    704:        return (0);
                    705: }
                    706:
                    707: void
                    708: smu_slew_voltage(u_int freq_scale)
                    709: {
                    710:        struct smu_softc *sc = smu_cd.cd_devs[0];
                    711:        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
                    712:
                    713:        cmd->cmd = SMU_POWER;
                    714:        cmd->len = 8;
                    715:        memcpy(cmd->data, "VSLEW", 5);
                    716:        cmd->data[5] = 0xff;
                    717:        cmd->data[6] = 1;
                    718:        cmd->data[7] = freq_scale;
                    719:
                    720:        smu_do_cmd(sc, 250);
                    721: }

CVSweb