18ff9ad32Smacallan /*-
28ff9ad32Smacallan * Copyright (c) 2013 Phileas Fogg
38ff9ad32Smacallan * All rights reserved.
48ff9ad32Smacallan *
58ff9ad32Smacallan * Redistribution and use in source and binary forms, with or without
68ff9ad32Smacallan * modification, are permitted provided that the following conditions
78ff9ad32Smacallan * are met:
88ff9ad32Smacallan * 1. Redistributions of source code must retain the above copyright
98ff9ad32Smacallan * notice, this list of conditions and the following disclaimer.
108ff9ad32Smacallan * 2. Redistributions in binary form must reproduce the above copyright
118ff9ad32Smacallan * notice, this list of conditions and the following disclaimer in the
128ff9ad32Smacallan * documentation and/or other materials provided with the distribution.
138ff9ad32Smacallan *
148ff9ad32Smacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
158ff9ad32Smacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
168ff9ad32Smacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
178ff9ad32Smacallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
188ff9ad32Smacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
198ff9ad32Smacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
208ff9ad32Smacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
218ff9ad32Smacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
228ff9ad32Smacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
238ff9ad32Smacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
248ff9ad32Smacallan * POSSIBILITY OF SUCH DAMAGE.
258ff9ad32Smacallan */
268ff9ad32Smacallan
278ff9ad32Smacallan #include <sys/param.h>
288ff9ad32Smacallan #include <sys/systm.h>
298ff9ad32Smacallan #include <sys/kernel.h>
308ff9ad32Smacallan #include <sys/malloc.h>
318ff9ad32Smacallan #include <sys/device.h>
328ff9ad32Smacallan #include <sys/proc.h>
338ff9ad32Smacallan #include <sys/mutex.h>
348ff9ad32Smacallan #include <sys/time.h>
358ff9ad32Smacallan #include <sys/reboot.h>
368ff9ad32Smacallan #include <sys/sysctl.h>
3777ec75e5Smacallan #include <sys/kthread.h>
388ff9ad32Smacallan
398ff9ad32Smacallan #include <machine/autoconf.h>
408ff9ad32Smacallan
418ff9ad32Smacallan #include <dev/ofw/openfirm.h>
428ff9ad32Smacallan #include <dev/i2c/i2cvar.h>
438ff9ad32Smacallan #include <dev/clock_subr.h>
448ff9ad32Smacallan #include <dev/sysmon/sysmonvar.h>
458ff9ad32Smacallan #include <dev/sysmon/sysmon_taskq.h>
468ff9ad32Smacallan
478ff9ad32Smacallan #include <macppc/dev/obiovar.h>
488ff9ad32Smacallan #include <macppc/dev/smuvar.h>
494717ce6fSmacallan #include <macppc/dev/fancontrolvar.h>
508ff9ad32Smacallan
518ff9ad32Smacallan #include "opt_smu.h"
528ff9ad32Smacallan
538ff9ad32Smacallan struct smu_softc;
548ff9ad32Smacallan
558ff9ad32Smacallan struct smu_cmd {
568ff9ad32Smacallan u_char cmd;
578ff9ad32Smacallan u_char len;
588ff9ad32Smacallan u_char data[254];
598ff9ad32Smacallan };
608ff9ad32Smacallan
618ff9ad32Smacallan struct smu_fan {
628ff9ad32Smacallan struct smu_softc* sc;
638ff9ad32Smacallan
648ff9ad32Smacallan char location[32];
658ff9ad32Smacallan int reg;
668ff9ad32Smacallan int zone;
678ff9ad32Smacallan int rpm_ctl;
688ff9ad32Smacallan int min_rpm;
698ff9ad32Smacallan int max_rpm;
708ff9ad32Smacallan int default_rpm;
714717ce6fSmacallan int wanted_rpm;
728ff9ad32Smacallan int current_rpm;
734717ce6fSmacallan int fault;
748ff9ad32Smacallan time_t last_update;
758ff9ad32Smacallan };
768ff9ad32Smacallan
778ff9ad32Smacallan struct smu_iicbus {
788ff9ad32Smacallan struct smu_softc* sc;
798ff9ad32Smacallan
808ff9ad32Smacallan int reg;
818ff9ad32Smacallan struct i2c_controller i2c;
828ff9ad32Smacallan };
838ff9ad32Smacallan
848ff9ad32Smacallan #define SMU_MAX_FANS 8
858ff9ad32Smacallan #define SMU_MAX_IICBUS 3
86405aa7c5Smacallan #define SMU_MAX_SME_SENSORS (SMU_MAX_FANS + 8)
878ff9ad32Smacallan
8877ec75e5Smacallan
894717ce6fSmacallan #define SMU_ZONE_CPU 0
904717ce6fSmacallan #define SMU_ZONE_CASE 1
914717ce6fSmacallan #define SMU_ZONE_DRIVEBAY 2
9277ec75e5Smacallan #define SMU_ZONES 3
9377ec75e5Smacallan
9477ec75e5Smacallan #define C_TO_uK(n) (n * 1000000 + 273150000)
9577ec75e5Smacallan
968ff9ad32Smacallan struct smu_softc {
978ff9ad32Smacallan device_t sc_dev;
988ff9ad32Smacallan int sc_node;
998ff9ad32Smacallan struct sysctlnode *sc_sysctl_me;
1008ff9ad32Smacallan
1018ff9ad32Smacallan kmutex_t sc_cmd_lock;
10277ec75e5Smacallan kmutex_t sc_msg_lock;
1038ff9ad32Smacallan struct smu_cmd *sc_cmd;
1048ff9ad32Smacallan paddr_t sc_cmd_paddr;
1058ff9ad32Smacallan int sc_dbell_mbox;
1068ff9ad32Smacallan int sc_dbell_gpio;
1078ff9ad32Smacallan
1088ff9ad32Smacallan int sc_num_fans;
1098ff9ad32Smacallan struct smu_fan sc_fans[SMU_MAX_FANS];
1108ff9ad32Smacallan
1118ff9ad32Smacallan int sc_num_iicbus;
1128ff9ad32Smacallan struct smu_iicbus sc_iicbus[SMU_MAX_IICBUS];
1138ff9ad32Smacallan
1148ff9ad32Smacallan struct todr_chip_handle sc_todr;
1158ff9ad32Smacallan
1168ff9ad32Smacallan struct sysmon_envsys *sc_sme;
1178ff9ad32Smacallan envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
118405aa7c5Smacallan uint32_t cpu_m;
119405aa7c5Smacallan int32_t cpu_b;
12077ec75e5Smacallan
1214717ce6fSmacallan fancontrol_zone_t sc_zones[SMU_ZONES];
12277ec75e5Smacallan lwp_t *sc_thread;
12377ec75e5Smacallan bool sc_dying;
1248ff9ad32Smacallan };
1258ff9ad32Smacallan
1268ff9ad32Smacallan #define SMU_CMD_FAN 0x4a
1278ff9ad32Smacallan #define SMU_CMD_RTC 0x8e
1288ff9ad32Smacallan #define SMU_CMD_I2C 0x9a
1298ff9ad32Smacallan #define SMU_CMD_POWER 0xaa
130405aa7c5Smacallan #define SMU_CMD_ADC 0xd8
131aa357ed6Smacallan #define SMU_MISC 0xee
132aa357ed6Smacallan #define SMU_MISC_GET_DATA 0x02
133aa357ed6Smacallan #define SMU_MISC_LED_CTRL 0x04
134aa357ed6Smacallan
135aa357ed6Smacallan #define SMU_CPUTEMP_CAL 0x18
136aa357ed6Smacallan #define SMU_CPUVOLT_CAL 0x21
137aa357ed6Smacallan #define SMU_SLOTPW_CAL 0x78
138aa357ed6Smacallan
139aa357ed6Smacallan #define SMU_PARTITION 0x3e
140aa357ed6Smacallan #define SMU_PARTITION_LATEST 0x01
141aa357ed6Smacallan #define SMU_PARTITION_BASE 0x02
142aa357ed6Smacallan #define SMU_PARTITION_UPDATE 0x03
1438ff9ad32Smacallan
1448ff9ad32Smacallan #ifdef SMU_DEBUG
1458ff9ad32Smacallan #define DPRINTF printf
1468ff9ad32Smacallan #else
1478ff9ad32Smacallan #define DPRINTF while (0) printf
1488ff9ad32Smacallan #endif
1498ff9ad32Smacallan
1508ff9ad32Smacallan static int smu_match(device_t, struct cfdata *, void *);
1518ff9ad32Smacallan static void smu_attach(device_t, device_t, void *);
1528ff9ad32Smacallan static int smu_setup_doorbell(struct smu_softc *);
1538ff9ad32Smacallan static void smu_setup_fans(struct smu_softc *);
1548ff9ad32Smacallan static void smu_setup_iicbus(struct smu_softc *);
1558ff9ad32Smacallan static void smu_setup_sme(struct smu_softc *);
1568ff9ad32Smacallan static int smu_iicbus_print(void *, const char *);
1578ff9ad32Smacallan static void smu_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
1588ff9ad32Smacallan static int smu_do_cmd(struct smu_softc *, struct smu_cmd *, int);
1598ff9ad32Smacallan static int smu_dbell_gpio_intr(void *);
1608ff9ad32Smacallan static int smu_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
1618ff9ad32Smacallan static int smu_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
1628ff9ad32Smacallan static int smu_fan_update_rpm(struct smu_fan *);
163405aa7c5Smacallan static int smu_read_adc(struct smu_softc *, int);
164405aa7c5Smacallan
1658ff9ad32Smacallan static int smu_iicbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
1668ff9ad32Smacallan size_t, void *, size_t, int);
1678ff9ad32Smacallan
16877ec75e5Smacallan static void smu_setup_zones(struct smu_softc *);
16977ec75e5Smacallan static void smu_adjust(void *);
1704717ce6fSmacallan
17177ec75e5Smacallan static bool is_cpu_sensor(const envsys_data_t *);
17277ec75e5Smacallan static bool is_drive_sensor(const envsys_data_t *);
17377ec75e5Smacallan static bool is_slots_sensor(const envsys_data_t *);
1744717ce6fSmacallan static int smu_fan_get_rpm(void *, int);
1754717ce6fSmacallan static int smu_fan_set_rpm(void *, int, int);
17677ec75e5Smacallan
177aa357ed6Smacallan int smu_get_datablock(int, uint8_t *, size_t);
178aa357ed6Smacallan
1798ff9ad32Smacallan CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
1808ff9ad32Smacallan smu_match, smu_attach, NULL, NULL);
1818ff9ad32Smacallan
1828ff9ad32Smacallan static struct smu_softc *smu0 = NULL;
1838ff9ad32Smacallan
1848ff9ad32Smacallan static int
smu_match(device_t parent,struct cfdata * cf,void * aux)1858ff9ad32Smacallan smu_match(device_t parent, struct cfdata *cf, void *aux)
1868ff9ad32Smacallan {
1878ff9ad32Smacallan struct confargs *ca = aux;
1888ff9ad32Smacallan
1898ff9ad32Smacallan if (strcmp(ca->ca_name, "smu") == 0)
1908ff9ad32Smacallan return 5;
1918ff9ad32Smacallan
1928ff9ad32Smacallan return 0;
1938ff9ad32Smacallan }
1948ff9ad32Smacallan
1958ff9ad32Smacallan static void
smu_attach(device_t parent,device_t self,void * aux)1968ff9ad32Smacallan smu_attach(device_t parent, device_t self, void *aux)
1978ff9ad32Smacallan {
1988ff9ad32Smacallan struct confargs *ca = aux;
1998ff9ad32Smacallan struct smu_softc *sc = device_private(self);
200405aa7c5Smacallan uint16_t data[4];
2018ff9ad32Smacallan
2028ff9ad32Smacallan sc->sc_dev = self;
2038ff9ad32Smacallan sc->sc_node = ca->ca_node;
2048ff9ad32Smacallan
205aa357ed6Smacallan if (smu0 == NULL)
206aa357ed6Smacallan smu0 = sc;
207aa357ed6Smacallan
2088ff9ad32Smacallan sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
2098ff9ad32Smacallan CTLFLAG_READWRITE,
2108ff9ad32Smacallan CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
2118ff9ad32Smacallan NULL, 0, NULL, 0,
2128ff9ad32Smacallan CTL_MACHDEP, CTL_CREATE, CTL_EOL);
2138ff9ad32Smacallan
2148ff9ad32Smacallan if (smu_setup_doorbell(sc) != 0) {
2158ff9ad32Smacallan aprint_normal(": unable to set up doorbell\n");
2168ff9ad32Smacallan return;
2178ff9ad32Smacallan }
2188ff9ad32Smacallan
219298a50bdSrin aprint_normal("\n");
220298a50bdSrin
2218ff9ad32Smacallan smu_setup_fans(sc);
2228ff9ad32Smacallan smu_setup_iicbus(sc);
2238ff9ad32Smacallan
2248ff9ad32Smacallan sc->sc_todr.todr_gettime_ymdhms = smu_todr_gettime_ymdhms;
2258ff9ad32Smacallan sc->sc_todr.todr_settime_ymdhms = smu_todr_settime_ymdhms;
2268ff9ad32Smacallan sc->sc_todr.cookie = sc;
2278ff9ad32Smacallan todr_attach(&sc->sc_todr);
2288ff9ad32Smacallan
229405aa7c5Smacallan /* calibration data */
230405aa7c5Smacallan memset(data, 0, 8);
231405aa7c5Smacallan smu_get_datablock(SMU_CPUTEMP_CAL, (void *)data, 8);
232405aa7c5Smacallan DPRINTF("data %04x %04x %04x %04x\n", data[0], data[1], data[2], data[3]);
233405aa7c5Smacallan sc->cpu_m = data[2];
234405aa7c5Smacallan sc->cpu_b = (int16_t)data[3];
235405aa7c5Smacallan
2368ff9ad32Smacallan smu_setup_sme(sc);
2378ff9ad32Smacallan
23877ec75e5Smacallan smu_setup_zones(sc);
2398ff9ad32Smacallan }
2408ff9ad32Smacallan
2418ff9ad32Smacallan static int
smu_setup_doorbell(struct smu_softc * sc)2428ff9ad32Smacallan smu_setup_doorbell(struct smu_softc *sc)
2438ff9ad32Smacallan {
2448ff9ad32Smacallan int node, parent, reg[4], gpio_base, irq;
2458ff9ad32Smacallan
2468ff9ad32Smacallan mutex_init(&sc->sc_cmd_lock, MUTEX_DEFAULT, IPL_NONE);
247d47bcd29Schs sc->sc_cmd = malloc(4096, M_DEVBUF, M_WAITOK);
2488ff9ad32Smacallan sc->sc_cmd_paddr = vtophys((vaddr_t) sc->sc_cmd);
2498ff9ad32Smacallan
2508ff9ad32Smacallan DPRINTF("%s: cmd vaddr 0x%x paddr 0x%x\n",
2518ff9ad32Smacallan __func__, (unsigned int) sc->sc_cmd,
2528ff9ad32Smacallan (unsigned int) sc->sc_cmd_paddr);
2538ff9ad32Smacallan
2548ff9ad32Smacallan if (OF_getprop(sc->sc_node, "platform-doorbell-buff",
2558ff9ad32Smacallan &node, sizeof(node)) <= 0)
2568ff9ad32Smacallan return -1;
2578ff9ad32Smacallan
2588ff9ad32Smacallan if (OF_getprop(node, "platform-do-doorbell-buff",
2598ff9ad32Smacallan reg, sizeof(reg)) < sizeof(reg))
2608ff9ad32Smacallan return -1;
2618ff9ad32Smacallan
2628ff9ad32Smacallan sc->sc_dbell_mbox = reg[3];
2638ff9ad32Smacallan
2648ff9ad32Smacallan if (OF_getprop(sc->sc_node, "platform-doorbell-ack",
2658ff9ad32Smacallan &node, sizeof(node)) <= 0)
2668ff9ad32Smacallan return -1;
2678ff9ad32Smacallan
2688ff9ad32Smacallan parent = OF_parent(node);
2698ff9ad32Smacallan if (parent == 0)
2708ff9ad32Smacallan return -1;
2718ff9ad32Smacallan
2728ff9ad32Smacallan if (OF_getprop(parent, "reg", &gpio_base, sizeof(gpio_base)) <= 0)
2738ff9ad32Smacallan return -1;
2748ff9ad32Smacallan
2758ff9ad32Smacallan if (OF_getprop(node, "reg", reg, sizeof(reg)) <= 0)
2768ff9ad32Smacallan return -1;
2778ff9ad32Smacallan
2788ff9ad32Smacallan if (OF_getprop(node, "interrupts", &irq, sizeof(irq)) <= 0)
2798ff9ad32Smacallan return -1;
2808ff9ad32Smacallan
2818ff9ad32Smacallan sc->sc_dbell_gpio = gpio_base + reg[0];
2828ff9ad32Smacallan
2838ff9ad32Smacallan aprint_normal(" mbox 0x%x gpio 0x%x irq %d",
2848ff9ad32Smacallan sc->sc_dbell_mbox, sc->sc_dbell_gpio, irq);
2858ff9ad32Smacallan
286cfe2093dSrin intr_establish_xname(irq, IST_EDGE_FALLING, IPL_TTY,
287cfe2093dSrin smu_dbell_gpio_intr, sc, device_xname(sc->sc_dev));
2888ff9ad32Smacallan
2898ff9ad32Smacallan return 0;
2908ff9ad32Smacallan }
2918ff9ad32Smacallan
2928ff9ad32Smacallan static void
smu_setup_fans(struct smu_softc * sc)2938ff9ad32Smacallan smu_setup_fans(struct smu_softc *sc)
2948ff9ad32Smacallan {
2958ff9ad32Smacallan struct smu_fan *fan;
2964717ce6fSmacallan char type[32];
2974717ce6fSmacallan int node, i;
298bd5b38cdSmacallan const char *fans[] = { "fans", "rpm-fans", 0 };
299bd5b38cdSmacallan int n = 0;
3008ff9ad32Smacallan
301bd5b38cdSmacallan while (fans[n][0] != 0) {
302bd5b38cdSmacallan node = of_getnode_byname(sc->sc_node, fans[n]);
3038ff9ad32Smacallan for (node = OF_child(node);
3048ff9ad32Smacallan (node != 0) && (sc->sc_num_fans < SMU_MAX_FANS);
3058ff9ad32Smacallan node = OF_peer(node)) {
3068ff9ad32Smacallan fan = &sc->sc_fans[sc->sc_num_fans];
3078ff9ad32Smacallan fan->sc = sc;
3088ff9ad32Smacallan
3098ff9ad32Smacallan memset(fan->location, 0, sizeof(fan->location));
3108ff9ad32Smacallan OF_getprop(node, "location", fan->location,
3118ff9ad32Smacallan sizeof(fan->location));
3128ff9ad32Smacallan
3138ff9ad32Smacallan if (OF_getprop(node, "reg", &fan->reg,
3148ff9ad32Smacallan sizeof(fan->reg)) <= 0)
3158ff9ad32Smacallan continue;
3168ff9ad32Smacallan
3178ff9ad32Smacallan if (OF_getprop(node, "zone", &fan->zone ,
3188ff9ad32Smacallan sizeof(fan->zone)) <= 0)
3198ff9ad32Smacallan continue;
3208ff9ad32Smacallan
3218ff9ad32Smacallan memset(type, 0, sizeof(type));
3228ff9ad32Smacallan OF_getprop(node, "device_type", type, sizeof(type));
3238ff9ad32Smacallan if (strcmp(type, "fan-rpm-control") == 0)
3248ff9ad32Smacallan fan->rpm_ctl = 1;
3258ff9ad32Smacallan else
3268ff9ad32Smacallan fan->rpm_ctl = 0;
3278ff9ad32Smacallan
3288ff9ad32Smacallan if (OF_getprop(node, "min-value", &fan->min_rpm,
3298ff9ad32Smacallan sizeof(fan->min_rpm)) <= 0)
3308ff9ad32Smacallan fan->min_rpm = 0;
3318ff9ad32Smacallan
3328ff9ad32Smacallan if (OF_getprop(node, "max-value", &fan->max_rpm,
3338ff9ad32Smacallan sizeof(fan->max_rpm)) <= 0)
3348ff9ad32Smacallan fan->max_rpm = 0xffff;
3358ff9ad32Smacallan
3368ff9ad32Smacallan if (OF_getprop(node, "unmanage-value", &fan->default_rpm,
3378ff9ad32Smacallan sizeof(fan->default_rpm)) <= 0)
3388ff9ad32Smacallan fan->default_rpm = fan->max_rpm;
3398ff9ad32Smacallan
3408ff9ad32Smacallan DPRINTF("fan: location %s reg %x zone %d rpm_ctl %d "
3418ff9ad32Smacallan "min_rpm %d max_rpm %d default_rpm %d\n",
3428ff9ad32Smacallan fan->location, fan->reg, fan->zone, fan->rpm_ctl,
3438ff9ad32Smacallan fan->min_rpm, fan->max_rpm, fan->default_rpm);
3448ff9ad32Smacallan
3454717ce6fSmacallan fan->wanted_rpm = fan->default_rpm;
3464717ce6fSmacallan fan->fault = 0;
3478ff9ad32Smacallan sc->sc_num_fans++;
3488ff9ad32Smacallan }
349bd5b38cdSmacallan n++;
350bd5b38cdSmacallan }
3518ff9ad32Smacallan
3528ff9ad32Smacallan for (i = 0; i < sc->sc_num_fans; i++) {
3538ff9ad32Smacallan fan = &sc->sc_fans[i];
3544717ce6fSmacallan smu_fan_set_rpm(sc, i, fan->default_rpm);
3554717ce6fSmacallan smu_fan_update_rpm(fan);
3568ff9ad32Smacallan }
3578ff9ad32Smacallan }
3588ff9ad32Smacallan
3598ff9ad32Smacallan static void
smu_setup_iicbus(struct smu_softc * sc)3608ff9ad32Smacallan smu_setup_iicbus(struct smu_softc *sc)
3618ff9ad32Smacallan {
3628ff9ad32Smacallan struct smu_iicbus *iicbus;
3638ff9ad32Smacallan struct i2c_controller *i2c;
3648ff9ad32Smacallan struct smu_iicbus_confargs ca;
3658ff9ad32Smacallan int node;
3668ff9ad32Smacallan char name[32];
3678ff9ad32Smacallan
368*3944ff70Sthorpej devhandle_t selfh = device_handle(sc->sc_dev);
3698ff9ad32Smacallan node = of_getnode_byname(sc->sc_node, "smu-i2c-control");
370bd5b38cdSmacallan if (node == 0) node = sc->sc_node;
3718ff9ad32Smacallan for (node = OF_child(node);
3728ff9ad32Smacallan (node != 0) && (sc->sc_num_iicbus < SMU_MAX_IICBUS);
3738ff9ad32Smacallan node = OF_peer(node)) {
3748ff9ad32Smacallan memset(name, 0, sizeof(name));
3758ff9ad32Smacallan OF_getprop(node, "name", name, sizeof(name));
376bd5b38cdSmacallan if ((strcmp(name, "i2c-bus") != 0) &&
377bd5b38cdSmacallan (strcmp(name, "i2c") != 0))
3788ff9ad32Smacallan continue;
3798ff9ad32Smacallan
3808ff9ad32Smacallan iicbus = &sc->sc_iicbus[sc->sc_num_iicbus];
3818ff9ad32Smacallan iicbus->sc = sc;
3828ff9ad32Smacallan i2c = &iicbus->i2c;
3838ff9ad32Smacallan
3848ff9ad32Smacallan if (OF_getprop(node, "reg", &iicbus->reg, sizeof(iicbus->reg)) <= 0)
3858ff9ad32Smacallan continue;
3868ff9ad32Smacallan
3878ff9ad32Smacallan DPRINTF("iicbus: reg %x\n", iicbus->reg);
3888ff9ad32Smacallan
389601e1783Sthorpej iic_tag_init(i2c);
3908ff9ad32Smacallan i2c->ic_cookie = iicbus;
3918ff9ad32Smacallan i2c->ic_exec = smu_iicbus_exec;
3928ff9ad32Smacallan
3938ff9ad32Smacallan ca.ca_name = name;
3948ff9ad32Smacallan ca.ca_node = node;
3958ff9ad32Smacallan ca.ca_tag = i2c;
3962685996bSthorpej config_found(sc->sc_dev, &ca, smu_iicbus_print,
397*3944ff70Sthorpej CFARGS(.devhandle = devhandle_from_of(selfh, node)));
3988ff9ad32Smacallan
3998ff9ad32Smacallan sc->sc_num_iicbus++;
4008ff9ad32Smacallan }
4018ff9ad32Smacallan }
4028ff9ad32Smacallan
4038ff9ad32Smacallan static void
smu_setup_sme(struct smu_softc * sc)4048ff9ad32Smacallan smu_setup_sme(struct smu_softc *sc)
4058ff9ad32Smacallan {
4068ff9ad32Smacallan struct smu_fan *fan;
4078ff9ad32Smacallan envsys_data_t *sme_sensor;
408405aa7c5Smacallan int i, sensors, child, reg;
409405aa7c5Smacallan char loc[32], type[32];
4108ff9ad32Smacallan
4118ff9ad32Smacallan sc->sc_sme = sysmon_envsys_create();
4128ff9ad32Smacallan
4138ff9ad32Smacallan for (i = 0; i < sc->sc_num_fans; i++) {
4148ff9ad32Smacallan sme_sensor = &sc->sc_sme_sensors[i];
4158ff9ad32Smacallan fan = &sc->sc_fans[i];
4168ff9ad32Smacallan
4178ff9ad32Smacallan sme_sensor->units = ENVSYS_SFANRPM;
4188ff9ad32Smacallan sme_sensor->state = ENVSYS_SINVALID;
4198ff9ad32Smacallan snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
4208ff9ad32Smacallan "%s", fan->location);
4218ff9ad32Smacallan
4228ff9ad32Smacallan if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
4238ff9ad32Smacallan sysmon_envsys_destroy(sc->sc_sme);
4248ff9ad32Smacallan return;
4258ff9ad32Smacallan }
4268ff9ad32Smacallan }
427405aa7c5Smacallan sensors = OF_finddevice("/smu/sensors");
428405aa7c5Smacallan child = OF_child(sensors);
429405aa7c5Smacallan while (child != 0) {
430405aa7c5Smacallan sme_sensor = &sc->sc_sme_sensors[i];
431405aa7c5Smacallan if (OF_getprop(child, "location", loc, 32) == 0) goto next;
432405aa7c5Smacallan if (OF_getprop(child, "device_type", type, 32) == 0) goto next;
433405aa7c5Smacallan if (OF_getprop(child, "reg", ®, 4) == 0) goto next;
434405aa7c5Smacallan if (strcmp(type, "temp-sensor") == 0) {
435405aa7c5Smacallan sme_sensor->units = ENVSYS_STEMP;
436405aa7c5Smacallan sme_sensor->state = ENVSYS_SINVALID;
437405aa7c5Smacallan strncpy(sme_sensor->desc, loc, sizeof(sme_sensor->desc));
438405aa7c5Smacallan sme_sensor->private = reg;
439405aa7c5Smacallan sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor);
440405aa7c5Smacallan i++;
441405aa7c5Smacallan printf("%s: %s@%x\n", loc, type, reg);
442405aa7c5Smacallan }
443405aa7c5Smacallan next:
444405aa7c5Smacallan child = OF_peer(child);
445405aa7c5Smacallan }
4468ff9ad32Smacallan
4478ff9ad32Smacallan sc->sc_sme->sme_name = device_xname(sc->sc_dev);
4488ff9ad32Smacallan sc->sc_sme->sme_cookie = sc;
4498ff9ad32Smacallan sc->sc_sme->sme_refresh = smu_sme_refresh;
4508ff9ad32Smacallan
4518ff9ad32Smacallan if (sysmon_envsys_register(sc->sc_sme)) {
4528ff9ad32Smacallan aprint_error_dev(sc->sc_dev,
4538ff9ad32Smacallan "unable to register with sysmon\n");
4548ff9ad32Smacallan sysmon_envsys_destroy(sc->sc_sme);
4558ff9ad32Smacallan }
4568ff9ad32Smacallan }
4578ff9ad32Smacallan
4588ff9ad32Smacallan static int
smu_iicbus_print(void * aux,const char * smu)4598ff9ad32Smacallan smu_iicbus_print(void *aux, const char *smu)
4608ff9ad32Smacallan {
4618ff9ad32Smacallan struct smu_iicbus_confargs *ca = aux;
4628ff9ad32Smacallan
4638ff9ad32Smacallan if (smu)
4648ff9ad32Smacallan aprint_normal("%s at %s", ca->ca_name, smu);
4658ff9ad32Smacallan
4668ff9ad32Smacallan return UNCONF;
4678ff9ad32Smacallan }
4688ff9ad32Smacallan
4698ff9ad32Smacallan static void
smu_sme_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)4708ff9ad32Smacallan smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
4718ff9ad32Smacallan {
4728ff9ad32Smacallan struct smu_softc *sc = sme->sme_cookie;
4738ff9ad32Smacallan int which = edata->sensor;
4748ff9ad32Smacallan int ret;
4758ff9ad32Smacallan
4768ff9ad32Smacallan edata->state = ENVSYS_SINVALID;
4778ff9ad32Smacallan
4788ff9ad32Smacallan if (which < sc->sc_num_fans) {
4798ff9ad32Smacallan
4804717ce6fSmacallan ret = smu_fan_get_rpm(sc, which);
4814717ce6fSmacallan if (ret != -1) {
4824717ce6fSmacallan sc->sc_fans[which].current_rpm = ret;
4834717ce6fSmacallan edata->value_cur = ret;
4848ff9ad32Smacallan edata->state = ENVSYS_SVALID;
4858ff9ad32Smacallan }
486405aa7c5Smacallan } else if (edata->private > 0) {
487405aa7c5Smacallan /* this works only for the CPU diode */
488405aa7c5Smacallan int64_t r = smu_read_adc(sc, edata->private);
489405aa7c5Smacallan if (r != -1) {
490405aa7c5Smacallan r = r * sc->cpu_m;
491405aa7c5Smacallan r >>= 3;
492405aa7c5Smacallan r += (int64_t)sc->cpu_b << 9;
493405aa7c5Smacallan r <<= 1;
494405aa7c5Smacallan r *= 15625;
495405aa7c5Smacallan r /= 1024;
496405aa7c5Smacallan edata->value_cur = r + 273150000;
497405aa7c5Smacallan edata->state = ENVSYS_SVALID;
498405aa7c5Smacallan }
4998ff9ad32Smacallan }
5008ff9ad32Smacallan }
5018ff9ad32Smacallan
5028ff9ad32Smacallan static int
smu_do_cmd(struct smu_softc * sc,struct smu_cmd * cmd,int timo)5038ff9ad32Smacallan smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
5048ff9ad32Smacallan {
50577ec75e5Smacallan int gpio, ret, bail;
5068ff9ad32Smacallan u_char ack;
5078ff9ad32Smacallan
5088ff9ad32Smacallan mutex_enter(&sc->sc_cmd_lock);
5098ff9ad32Smacallan
5108ff9ad32Smacallan DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
5118ff9ad32Smacallan DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
5128ff9ad32Smacallan cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
5138ff9ad32Smacallan cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
5148ff9ad32Smacallan
5158ff9ad32Smacallan sc->sc_cmd->cmd = cmd->cmd;
5168ff9ad32Smacallan sc->sc_cmd->len = cmd->len;
5178ff9ad32Smacallan memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
5188ff9ad32Smacallan
5198ff9ad32Smacallan __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
5208ff9ad32Smacallan
5218ff9ad32Smacallan obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
5228ff9ad32Smacallan obio_write_1(sc->sc_dbell_gpio, 0x04);
5238ff9ad32Smacallan
52477ec75e5Smacallan bail = 0;
52577ec75e5Smacallan
52677ec75e5Smacallan gpio = obio_read_1(sc->sc_dbell_gpio);
52777ec75e5Smacallan
52877ec75e5Smacallan while (((gpio & 0x07) != 0x07) && (bail < timo)) {
52977ec75e5Smacallan ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
5308ff9ad32Smacallan if (ret != 0) {
53177ec75e5Smacallan bail++;
5328ff9ad32Smacallan }
5338ff9ad32Smacallan gpio = obio_read_1(sc->sc_dbell_gpio);
53477ec75e5Smacallan }
53577ec75e5Smacallan
53677ec75e5Smacallan if ((gpio & 0x07) != 0x07) {
53777ec75e5Smacallan mutex_exit(&sc->sc_cmd_lock);
53877ec75e5Smacallan return EWOULDBLOCK;
53977ec75e5Smacallan }
5408ff9ad32Smacallan
5418ff9ad32Smacallan __asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
5428ff9ad32Smacallan
5438ff9ad32Smacallan ack = (~cmd->cmd) & 0xff;
5448ff9ad32Smacallan if (sc->sc_cmd->cmd != ack) {
5458ff9ad32Smacallan DPRINTF("%s: invalid ack, got %x expected %x\n",
5468ff9ad32Smacallan __func__, sc->sc_cmd->cmd, ack);
5478ff9ad32Smacallan mutex_exit(&sc->sc_cmd_lock);
5488ff9ad32Smacallan return EIO;
5498ff9ad32Smacallan }
5508ff9ad32Smacallan
5518ff9ad32Smacallan cmd->cmd = sc->sc_cmd->cmd;
5528ff9ad32Smacallan cmd->len = sc->sc_cmd->len;
5538ff9ad32Smacallan memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
5548ff9ad32Smacallan
5558ff9ad32Smacallan mutex_exit(&sc->sc_cmd_lock);
5568ff9ad32Smacallan
5578ff9ad32Smacallan return 0;
5588ff9ad32Smacallan }
5598ff9ad32Smacallan
5608ff9ad32Smacallan
5618ff9ad32Smacallan static int
smu_dbell_gpio_intr(void * arg)5628ff9ad32Smacallan smu_dbell_gpio_intr(void *arg)
5638ff9ad32Smacallan {
5648ff9ad32Smacallan struct smu_softc *sc = arg;
5658ff9ad32Smacallan
5668ff9ad32Smacallan DPRINTF("%s\n", __func__);
5678ff9ad32Smacallan
5688ff9ad32Smacallan wakeup(sc->sc_cmd);
5698ff9ad32Smacallan
5708ff9ad32Smacallan return 1;
5718ff9ad32Smacallan }
5728ff9ad32Smacallan
5738ff9ad32Smacallan void
smu_poweroff(void)5748ff9ad32Smacallan smu_poweroff(void)
5758ff9ad32Smacallan {
5768ff9ad32Smacallan struct smu_cmd cmd;
5778ff9ad32Smacallan
5788ff9ad32Smacallan if (smu0 == NULL)
5798ff9ad32Smacallan return;
5808ff9ad32Smacallan
5818ff9ad32Smacallan cmd.cmd = SMU_CMD_POWER;
5828ff9ad32Smacallan strcpy(cmd.data, "SHUTDOWN");
5838ff9ad32Smacallan cmd.len = strlen(cmd.data) + 1;
5848ff9ad32Smacallan smu_do_cmd(smu0, &cmd, 800);
5858ff9ad32Smacallan
5868ff9ad32Smacallan for (;;);
5878ff9ad32Smacallan }
5888ff9ad32Smacallan
5898ff9ad32Smacallan void
smu_restart(void)5908ff9ad32Smacallan smu_restart(void)
5918ff9ad32Smacallan {
5928ff9ad32Smacallan struct smu_cmd cmd;
5938ff9ad32Smacallan
5948ff9ad32Smacallan if (smu0 == NULL)
5958ff9ad32Smacallan return;
5968ff9ad32Smacallan
5978ff9ad32Smacallan cmd.cmd = SMU_CMD_POWER;
5988ff9ad32Smacallan strcpy(cmd.data, "RESTART");
5998ff9ad32Smacallan cmd.len = strlen(cmd.data) + 1;
6008ff9ad32Smacallan smu_do_cmd(smu0, &cmd, 800);
6018ff9ad32Smacallan
6028ff9ad32Smacallan for (;;);
6038ff9ad32Smacallan }
6048ff9ad32Smacallan
6058ff9ad32Smacallan static int
smu_todr_gettime_ymdhms(todr_chip_handle_t tch,struct clock_ymdhms * dt)6068ff9ad32Smacallan smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
6078ff9ad32Smacallan {
6088ff9ad32Smacallan struct smu_softc *sc = tch->cookie;
6098ff9ad32Smacallan struct smu_cmd cmd;
6108ff9ad32Smacallan int ret;
6118ff9ad32Smacallan
6128ff9ad32Smacallan cmd.cmd = SMU_CMD_RTC;
6138ff9ad32Smacallan cmd.len = 1;
6148ff9ad32Smacallan cmd.data[0] = 0x81;
6158ff9ad32Smacallan
6168ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
6178ff9ad32Smacallan if (ret != 0)
6188ff9ad32Smacallan return ret;
6198ff9ad32Smacallan
6208ff9ad32Smacallan dt->dt_sec = bcdtobin(cmd.data[0]);
6218ff9ad32Smacallan dt->dt_min = bcdtobin(cmd.data[1]);
6228ff9ad32Smacallan dt->dt_hour = bcdtobin(cmd.data[2]);
6238ff9ad32Smacallan dt->dt_wday = bcdtobin(cmd.data[3]);
6248ff9ad32Smacallan dt->dt_day = bcdtobin(cmd.data[4]);
6258ff9ad32Smacallan dt->dt_mon = bcdtobin(cmd.data[5]);
6268ff9ad32Smacallan dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
6278ff9ad32Smacallan
6288ff9ad32Smacallan return 0;
6298ff9ad32Smacallan }
6308ff9ad32Smacallan
6318ff9ad32Smacallan static int
smu_todr_settime_ymdhms(todr_chip_handle_t tch,struct clock_ymdhms * dt)6328ff9ad32Smacallan smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
6338ff9ad32Smacallan {
6348ff9ad32Smacallan struct smu_softc *sc = tch->cookie;
6358ff9ad32Smacallan struct smu_cmd cmd;
6368ff9ad32Smacallan
6378ff9ad32Smacallan cmd.cmd = SMU_CMD_RTC;
6388ff9ad32Smacallan cmd.len = 8;
6398ff9ad32Smacallan cmd.data[0] = 0x80;
6408ff9ad32Smacallan cmd.data[1] = bintobcd(dt->dt_sec);
6418ff9ad32Smacallan cmd.data[2] = bintobcd(dt->dt_min);
6428ff9ad32Smacallan cmd.data[3] = bintobcd(dt->dt_hour);
6438ff9ad32Smacallan cmd.data[4] = bintobcd(dt->dt_wday);
6448ff9ad32Smacallan cmd.data[5] = bintobcd(dt->dt_day);
6458ff9ad32Smacallan cmd.data[6] = bintobcd(dt->dt_mon);
6468ff9ad32Smacallan cmd.data[7] = bintobcd(dt->dt_year - 2000);
6478ff9ad32Smacallan
6488ff9ad32Smacallan return smu_do_cmd(sc, &cmd, 800);
6498ff9ad32Smacallan }
6508ff9ad32Smacallan
6518ff9ad32Smacallan static int
smu_fan_update_rpm(struct smu_fan * fan)6528ff9ad32Smacallan smu_fan_update_rpm(struct smu_fan *fan)
6538ff9ad32Smacallan {
6548ff9ad32Smacallan struct smu_softc *sc = fan->sc;
6558ff9ad32Smacallan struct smu_cmd cmd;
6564717ce6fSmacallan int ret, diff;
6578ff9ad32Smacallan
6588ff9ad32Smacallan cmd.cmd = SMU_CMD_FAN;
6598ff9ad32Smacallan cmd.len = 2;
6608ff9ad32Smacallan cmd.data[0] = 0x31;
6618ff9ad32Smacallan cmd.data[1] = fan->reg;
6628ff9ad32Smacallan
6638ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
6648ff9ad32Smacallan if (ret == 0) {
6658ff9ad32Smacallan fan->last_update = time_uptime;
6668ff9ad32Smacallan fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
6678ff9ad32Smacallan } else {
6688ff9ad32Smacallan cmd.cmd = SMU_CMD_FAN;
6698ff9ad32Smacallan cmd.len = 1;
6708ff9ad32Smacallan cmd.data[0] = 0x01;
6718ff9ad32Smacallan
6728ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
6738ff9ad32Smacallan if (ret == 0) {
6748ff9ad32Smacallan fan->last_update = time_uptime;
6758ff9ad32Smacallan fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
6768ff9ad32Smacallan cmd.data[2 + fan->reg * 2];
6778ff9ad32Smacallan }
6788ff9ad32Smacallan }
6794717ce6fSmacallan diff = abs(fan->current_rpm - fan->wanted_rpm);
6804717ce6fSmacallan if (diff > fan->max_rpm >> 3) {
6814717ce6fSmacallan fan->fault++;
6824717ce6fSmacallan } else fan->fault = 0;
6838ff9ad32Smacallan return ret;
6848ff9ad32Smacallan }
6858ff9ad32Smacallan
6868ff9ad32Smacallan static int
smu_fan_get_rpm(void * cookie,int which)6874717ce6fSmacallan smu_fan_get_rpm(void *cookie, int which)
6888ff9ad32Smacallan {
6894717ce6fSmacallan struct smu_softc *sc = cookie;
6904717ce6fSmacallan struct smu_fan *fan = &sc->sc_fans[which];
6918ff9ad32Smacallan int ret;
692796d7c94Ssevan ret = 0;
6938ff9ad32Smacallan
6948ff9ad32Smacallan if (time_uptime - fan->last_update > 1) {
6958ff9ad32Smacallan ret = smu_fan_update_rpm(fan);
6968ff9ad32Smacallan if (ret != 0)
6974717ce6fSmacallan return -1;
6988ff9ad32Smacallan }
6998ff9ad32Smacallan
7004717ce6fSmacallan return fan->current_rpm;
7018ff9ad32Smacallan }
7028ff9ad32Smacallan
7038ff9ad32Smacallan static int
smu_fan_set_rpm(void * cookie,int which,int rpm)7044717ce6fSmacallan smu_fan_set_rpm(void *cookie, int which, int rpm)
7058ff9ad32Smacallan {
7064717ce6fSmacallan struct smu_softc *sc = cookie;
7074717ce6fSmacallan struct smu_fan *fan = &sc->sc_fans[which];
7088ff9ad32Smacallan struct smu_cmd cmd;
7098ff9ad32Smacallan int ret;
7108ff9ad32Smacallan
7118ff9ad32Smacallan DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
7128ff9ad32Smacallan
713d1579b2dSriastradh rpm = uimax(fan->min_rpm, rpm);
714d1579b2dSriastradh rpm = uimin(fan->max_rpm, rpm);
7158ff9ad32Smacallan
7164717ce6fSmacallan fan->wanted_rpm = rpm;
7174717ce6fSmacallan
7188ff9ad32Smacallan cmd.cmd = SMU_CMD_FAN;
7198ff9ad32Smacallan cmd.len = 4;
7208ff9ad32Smacallan cmd.data[0] = 0x30;
7218ff9ad32Smacallan cmd.data[1] = fan->reg;
7228ff9ad32Smacallan cmd.data[2] = (rpm >> 8) & 0xff;
7238ff9ad32Smacallan cmd.data[3] = rpm & 0xff;
7248ff9ad32Smacallan
7258ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
7268ff9ad32Smacallan if (ret != 0) {
7278ff9ad32Smacallan cmd.cmd = SMU_CMD_FAN;
7288ff9ad32Smacallan cmd.len = 14;
7298ff9ad32Smacallan cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
7308ff9ad32Smacallan cmd.data[1] = 1 << fan->reg;
7318ff9ad32Smacallan cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
7328ff9ad32Smacallan cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
7338ff9ad32Smacallan
7348ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
7358ff9ad32Smacallan }
7368ff9ad32Smacallan
7378ff9ad32Smacallan return ret;
7388ff9ad32Smacallan }
7398ff9ad32Smacallan
7408ff9ad32Smacallan static int
smu_read_adc(struct smu_softc * sc,int id)741405aa7c5Smacallan smu_read_adc(struct smu_softc *sc, int id)
742405aa7c5Smacallan {
743405aa7c5Smacallan struct smu_cmd cmd;
744405aa7c5Smacallan int ret;
745405aa7c5Smacallan
746405aa7c5Smacallan cmd.cmd = SMU_CMD_ADC;
747405aa7c5Smacallan cmd.len = 1;
748405aa7c5Smacallan cmd.data[0] = id;
749405aa7c5Smacallan
750405aa7c5Smacallan ret = smu_do_cmd(sc, &cmd, 800);
751405aa7c5Smacallan if (ret == 0) {
752405aa7c5Smacallan return cmd.data[0] << 8 | cmd.data[1];
753405aa7c5Smacallan }
754405aa7c5Smacallan return -1;
755405aa7c5Smacallan }
756405aa7c5Smacallan
757405aa7c5Smacallan static int
smu_iicbus_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * send,size_t send_len,void * recv,size_t recv_len,int flags)7588ff9ad32Smacallan smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
7598ff9ad32Smacallan size_t send_len, void *recv, size_t recv_len, int flags)
7608ff9ad32Smacallan {
7618ff9ad32Smacallan struct smu_iicbus *iicbus = cookie;
7628ff9ad32Smacallan struct smu_softc *sc = iicbus->sc;
7638ff9ad32Smacallan struct smu_cmd cmd;
7648ff9ad32Smacallan int retries, ret;
7658ff9ad32Smacallan
7668ff9ad32Smacallan DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
7678ff9ad32Smacallan __func__, op, addr, send_len, recv_len);
7688ff9ad32Smacallan
7698ff9ad32Smacallan cmd.cmd = SMU_CMD_I2C;
7708ff9ad32Smacallan cmd.len = 9 + recv_len;
7718ff9ad32Smacallan cmd.data[0] = iicbus->reg;
7728ff9ad32Smacallan cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
773aa357ed6Smacallan cmd.data[2] = addr << 1;
7748ff9ad32Smacallan cmd.data[3] = send_len;
7758ff9ad32Smacallan memcpy(&cmd.data[4], send, send_len);
776aa357ed6Smacallan cmd.data[7] = addr << 1;
7778ff9ad32Smacallan if (I2C_OP_READ_P(op))
7788ff9ad32Smacallan cmd.data[7] |= 0x01;
7798ff9ad32Smacallan cmd.data[8] = recv_len;
7808ff9ad32Smacallan memcpy(&cmd.data[9], recv, recv_len);
7818ff9ad32Smacallan
7828ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
7838ff9ad32Smacallan if (ret != 0)
7848ff9ad32Smacallan return (ret);
7858ff9ad32Smacallan
7868ff9ad32Smacallan for (retries = 0; retries < 10; retries++) {
7878ff9ad32Smacallan cmd.cmd = SMU_CMD_I2C;
7888ff9ad32Smacallan cmd.len = 1;
7898ff9ad32Smacallan cmd.data[0] = 0x00;
7908ff9ad32Smacallan memset(&cmd.data[1], 0xff, recv_len);
7918ff9ad32Smacallan
7928ff9ad32Smacallan ret = smu_do_cmd(sc, &cmd, 800);
7938ff9ad32Smacallan
7948ff9ad32Smacallan DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
7958ff9ad32Smacallan
7968ff9ad32Smacallan if (ret == 0 && (cmd.data[0] & 0x80) == 0)
7978ff9ad32Smacallan break;
7988ff9ad32Smacallan
7998ff9ad32Smacallan DELAY(10000);
8008ff9ad32Smacallan }
8018ff9ad32Smacallan
8028ff9ad32Smacallan if (cmd.data[0] & 0x80)
8038ff9ad32Smacallan return EIO;
8048ff9ad32Smacallan
8058ff9ad32Smacallan if (I2C_OP_READ_P(op))
8068ff9ad32Smacallan memcpy(recv, &cmd.data[1], recv_len);
8078ff9ad32Smacallan
8088ff9ad32Smacallan return 0;
8098ff9ad32Smacallan }
8108ff9ad32Smacallan
8118ff9ad32Smacallan SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
8128ff9ad32Smacallan {
8138ff9ad32Smacallan sysctl_createv(NULL, 0, NULL, NULL,
8148ff9ad32Smacallan CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
8158ff9ad32Smacallan NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
8168ff9ad32Smacallan }
81777ec75e5Smacallan
81877ec75e5Smacallan static void
smu_setup_zones(struct smu_softc * sc)81977ec75e5Smacallan smu_setup_zones(struct smu_softc *sc)
82077ec75e5Smacallan {
82177ec75e5Smacallan struct smu_fan *f;
8224717ce6fSmacallan fancontrol_zone_t *z;
82377ec75e5Smacallan int i;
82477ec75e5Smacallan
8254717ce6fSmacallan /* init zones */
8264717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].name = "CPUs";
8274717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].filter = is_cpu_sensor;
8284717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].cookie = sc;
8294717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].get_rpm = smu_fan_get_rpm;
8304717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].set_rpm = smu_fan_set_rpm;
8314717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].Tmin = 45;
8324717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].Tmax = 80;
8334717ce6fSmacallan sc->sc_zones[SMU_ZONE_CPU].nfans = 0;
8344717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].name = "Slots";
8354717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].filter = is_slots_sensor;
8364717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].cookie = sc;
8374717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].Tmin = 50;
8384717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].Tmax = 75;
8394717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].nfans = 0;
8404717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].get_rpm = smu_fan_get_rpm;
8414717ce6fSmacallan sc->sc_zones[SMU_ZONE_CASE].set_rpm = smu_fan_set_rpm;
8424717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].name = "Drivebays";
8434717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].filter = is_drive_sensor;
8444717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].cookie = sc;
8454717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].get_rpm = smu_fan_get_rpm;
8464717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].set_rpm = smu_fan_set_rpm;
8474717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].Tmin = 30;
8484717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].Tmax = 50;
8494717ce6fSmacallan sc->sc_zones[SMU_ZONE_DRIVEBAY].nfans = 0;
8504717ce6fSmacallan
85177ec75e5Smacallan /* find CPU fans */
8524717ce6fSmacallan z = &sc->sc_zones[SMU_ZONE_CPU];
85377ec75e5Smacallan for (i = 0; i < SMU_MAX_FANS; i++) {
85477ec75e5Smacallan f = &sc->sc_fans[i];
855bd5b38cdSmacallan if ((strstr(f->location, "CPU") != NULL) ||
856bd5b38cdSmacallan (strstr(f->location, "System") != NULL)) {
8574717ce6fSmacallan z->fans[z->nfans].num = i;
8584717ce6fSmacallan z->fans[z->nfans].min_rpm = f->min_rpm;
8594717ce6fSmacallan z->fans[z->nfans].max_rpm = f->max_rpm;
8604717ce6fSmacallan z->fans[z->nfans].name = f->location;
86177ec75e5Smacallan z->nfans++;
86277ec75e5Smacallan }
86377ec75e5Smacallan }
864298a50bdSrin aprint_normal_dev(sc->sc_dev,
865298a50bdSrin "using %d fans for CPU zone\n", z->nfans);
86677ec75e5Smacallan
8674717ce6fSmacallan z = &sc->sc_zones[SMU_ZONE_DRIVEBAY];
86877ec75e5Smacallan for (i = 0; i < SMU_MAX_FANS; i++) {
86977ec75e5Smacallan f = &sc->sc_fans[i];
870bd5b38cdSmacallan if ((strstr(f->location, "DRIVE") != NULL) ||
871bd5b38cdSmacallan (strstr(f->location, "Drive") != NULL)) {
8724717ce6fSmacallan z->fans[z->nfans].num = i;
8734717ce6fSmacallan z->fans[z->nfans].min_rpm = f->min_rpm;
8744717ce6fSmacallan z->fans[z->nfans].max_rpm = f->max_rpm;
8754717ce6fSmacallan z->fans[z->nfans].name = f->location;
87677ec75e5Smacallan z->nfans++;
87777ec75e5Smacallan }
87877ec75e5Smacallan }
879298a50bdSrin aprint_normal_dev(sc->sc_dev,
880298a50bdSrin "using %d fans for drive bay zone\n", z->nfans);
88177ec75e5Smacallan
8824717ce6fSmacallan z = &sc->sc_zones[SMU_ZONE_CASE];
88377ec75e5Smacallan for (i = 0; i < SMU_MAX_FANS; i++) {
88477ec75e5Smacallan f = &sc->sc_fans[i];
88577ec75e5Smacallan if ((strstr(f->location, "BACKSIDE") != NULL) ||
88677ec75e5Smacallan (strstr(f->location, "SLOTS") != NULL)) {
8874717ce6fSmacallan z->fans[z->nfans].num = i;
8884717ce6fSmacallan z->fans[z->nfans].min_rpm = f->min_rpm;
8894717ce6fSmacallan z->fans[z->nfans].max_rpm = f->max_rpm;
8904717ce6fSmacallan z->fans[z->nfans].name = f->location;
89177ec75e5Smacallan z->nfans++;
89277ec75e5Smacallan }
89377ec75e5Smacallan }
894298a50bdSrin aprint_normal_dev(sc->sc_dev,
895298a50bdSrin "using %d fans for expansion slots zone\n", z->nfans);
8964717ce6fSmacallan
8974717ce6fSmacallan /* setup sysctls for our zones etc. */
8984717ce6fSmacallan for (i = 0; i < SMU_ZONES; i++) {
8994717ce6fSmacallan fancontrol_init_zone(&sc->sc_zones[i], sc->sc_sysctl_me);
9004717ce6fSmacallan }
90177ec75e5Smacallan
90277ec75e5Smacallan sc->sc_dying = false;
90377ec75e5Smacallan kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
90477ec75e5Smacallan "fan control");
90577ec75e5Smacallan }
90677ec75e5Smacallan
90777ec75e5Smacallan static void
smu_adjust(void * cookie)90877ec75e5Smacallan smu_adjust(void *cookie)
90977ec75e5Smacallan {
91077ec75e5Smacallan struct smu_softc *sc = cookie;
91177ec75e5Smacallan int i;
91277ec75e5Smacallan
91377ec75e5Smacallan while (!sc->sc_dying) {
91477ec75e5Smacallan for (i = 0; i < SMU_ZONES; i++)
9154717ce6fSmacallan if (sc->sc_zones[i].nfans > 0)
9164717ce6fSmacallan fancontrol_adjust_zone(&sc->sc_zones[i]);
9174717ce6fSmacallan kpause("fanctrl", true, mstohz(2000), NULL);
91877ec75e5Smacallan }
91977ec75e5Smacallan kthread_exit(0);
92077ec75e5Smacallan }
92177ec75e5Smacallan
is_cpu_sensor(const envsys_data_t * edata)92277ec75e5Smacallan static bool is_cpu_sensor(const envsys_data_t *edata)
92377ec75e5Smacallan {
92477ec75e5Smacallan if (edata->units != ENVSYS_STEMP)
92577ec75e5Smacallan return false;
926bd5b38cdSmacallan if (strstr(edata->desc, "CPU") != NULL)
92777ec75e5Smacallan return TRUE;
92877ec75e5Smacallan return false;
92977ec75e5Smacallan }
93077ec75e5Smacallan
is_drive_sensor(const envsys_data_t * edata)93177ec75e5Smacallan static bool is_drive_sensor(const envsys_data_t *edata)
93277ec75e5Smacallan {
93377ec75e5Smacallan if (edata->units != ENVSYS_STEMP)
93477ec75e5Smacallan return false;
935bd5b38cdSmacallan if (strstr(edata->desc, "DRIVE") != NULL)
936bd5b38cdSmacallan return TRUE;
937bd5b38cdSmacallan if (strstr(edata->desc, "drive") != NULL)
93877ec75e5Smacallan return TRUE;
93977ec75e5Smacallan return false;
94077ec75e5Smacallan }
94177ec75e5Smacallan
is_slots_sensor(const envsys_data_t * edata)94277ec75e5Smacallan static bool is_slots_sensor(const envsys_data_t *edata)
94377ec75e5Smacallan {
94477ec75e5Smacallan if (edata->units != ENVSYS_STEMP)
94577ec75e5Smacallan return false;
94677ec75e5Smacallan if (strstr(edata->desc, "BACKSIDE") != NULL)
94777ec75e5Smacallan return TRUE;
94877ec75e5Smacallan if (strstr(edata->desc, "INLET") != NULL)
94977ec75e5Smacallan return TRUE;
950bd5b38cdSmacallan if (strstr(edata->desc, "DIODE") != NULL)
951bd5b38cdSmacallan return TRUE;
952bd5b38cdSmacallan if (strstr(edata->desc, "TUNNEL") != NULL)
953bd5b38cdSmacallan return TRUE;
95477ec75e5Smacallan return false;
95577ec75e5Smacallan }
956aa357ed6Smacallan
957aa357ed6Smacallan int
smu_get_datablock(int id,uint8_t * buf,size_t len)958aa357ed6Smacallan smu_get_datablock(int id, uint8_t *buf, size_t len)
959aa357ed6Smacallan {
960aa357ed6Smacallan struct smu_cmd cmd;
961aa357ed6Smacallan
962aa357ed6Smacallan cmd.cmd = SMU_PARTITION;
963aa357ed6Smacallan cmd.len = 2;
964aa357ed6Smacallan cmd.data[0] = SMU_PARTITION_LATEST;
965aa357ed6Smacallan cmd.data[1] = id;
966aa357ed6Smacallan smu_do_cmd(smu0, &cmd, 100);
967aa357ed6Smacallan
968aa357ed6Smacallan cmd.data[4] = cmd.data[0];
969aa357ed6Smacallan cmd.data[5] = cmd.data[1];
970aa357ed6Smacallan
971aa357ed6Smacallan cmd.cmd = SMU_MISC;
972aa357ed6Smacallan cmd.len = 7;
973aa357ed6Smacallan cmd.data[0] = SMU_MISC_GET_DATA;
974aa357ed6Smacallan cmd.data[1] = 4;
975aa357ed6Smacallan cmd.data[2] = 0;
976aa357ed6Smacallan cmd.data[3] = 0;
977aa357ed6Smacallan cmd.data[6] = len;
978aa357ed6Smacallan smu_do_cmd(smu0, &cmd, 100);
979aa357ed6Smacallan
980aa357ed6Smacallan memcpy(buf, cmd.data, len);
981aa357ed6Smacallan return 0;
982aa357ed6Smacallan }
983