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