1*22d7dd83SEmmanuel Vadot /*- 2*22d7dd83SEmmanuel Vadot * Copyright (c) 2011 Justin Hibbits 3*22d7dd83SEmmanuel Vadot * Copyright (c) 2010 Andreas Tobler 4*22d7dd83SEmmanuel Vadot * All rights reserved. 5*22d7dd83SEmmanuel Vadot * 6*22d7dd83SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 7*22d7dd83SEmmanuel Vadot * modification, are permitted provided that the following conditions 8*22d7dd83SEmmanuel Vadot * are met: 9*22d7dd83SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 10*22d7dd83SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 11*22d7dd83SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 12*22d7dd83SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 13*22d7dd83SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 14*22d7dd83SEmmanuel Vadot * 15*22d7dd83SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*22d7dd83SEmmanuel Vadot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17*22d7dd83SEmmanuel Vadot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18*22d7dd83SEmmanuel Vadot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19*22d7dd83SEmmanuel Vadot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20*22d7dd83SEmmanuel Vadot * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21*22d7dd83SEmmanuel Vadot * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22*22d7dd83SEmmanuel Vadot * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23*22d7dd83SEmmanuel Vadot * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*22d7dd83SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*22d7dd83SEmmanuel Vadot * SUCH DAMAGE. 26*22d7dd83SEmmanuel Vadot */ 27*22d7dd83SEmmanuel Vadot 28*22d7dd83SEmmanuel Vadot #include <sys/cdefs.h> 29*22d7dd83SEmmanuel Vadot #include <sys/param.h> 30*22d7dd83SEmmanuel Vadot #include <sys/bus.h> 31*22d7dd83SEmmanuel Vadot #include <sys/systm.h> 32*22d7dd83SEmmanuel Vadot #include <sys/module.h> 33*22d7dd83SEmmanuel Vadot #include <sys/callout.h> 34*22d7dd83SEmmanuel Vadot #include <sys/conf.h> 35*22d7dd83SEmmanuel Vadot #include <sys/cpu.h> 36*22d7dd83SEmmanuel Vadot #include <sys/ctype.h> 37*22d7dd83SEmmanuel Vadot #include <sys/kernel.h> 38*22d7dd83SEmmanuel Vadot #include <sys/kthread.h> 39*22d7dd83SEmmanuel Vadot #include <sys/limits.h> 40*22d7dd83SEmmanuel Vadot #include <sys/reboot.h> 41*22d7dd83SEmmanuel Vadot #include <sys/rman.h> 42*22d7dd83SEmmanuel Vadot #include <sys/sysctl.h> 43*22d7dd83SEmmanuel Vadot #include <sys/unistd.h> 44*22d7dd83SEmmanuel Vadot 45*22d7dd83SEmmanuel Vadot #include <machine/bus.h> 46*22d7dd83SEmmanuel Vadot #include <machine/md_var.h> 47*22d7dd83SEmmanuel Vadot 48*22d7dd83SEmmanuel Vadot #include <dev/iicbus/iicbus.h> 49*22d7dd83SEmmanuel Vadot #include <dev/iicbus/iiconf.h> 50*22d7dd83SEmmanuel Vadot 51*22d7dd83SEmmanuel Vadot #include <dev/ofw/openfirm.h> 52*22d7dd83SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 53*22d7dd83SEmmanuel Vadot #include <powerpc/powermac/powermac_thermal.h> 54*22d7dd83SEmmanuel Vadot 55*22d7dd83SEmmanuel Vadot struct adm1030_softc { 56*22d7dd83SEmmanuel Vadot struct pmac_fan fan; 57*22d7dd83SEmmanuel Vadot device_t sc_dev; 58*22d7dd83SEmmanuel Vadot struct intr_config_hook enum_hook; 59*22d7dd83SEmmanuel Vadot uint32_t sc_addr; 60*22d7dd83SEmmanuel Vadot int sc_pwm; 61*22d7dd83SEmmanuel Vadot }; 62*22d7dd83SEmmanuel Vadot 63*22d7dd83SEmmanuel Vadot /* Regular bus attachment functions */ 64*22d7dd83SEmmanuel Vadot static int adm1030_probe(device_t); 65*22d7dd83SEmmanuel Vadot static int adm1030_attach(device_t); 66*22d7dd83SEmmanuel Vadot 67*22d7dd83SEmmanuel Vadot /* Utility functions */ 68*22d7dd83SEmmanuel Vadot static void adm1030_start(void *xdev); 69*22d7dd83SEmmanuel Vadot static int adm1030_write_byte(device_t dev, uint32_t addr, uint8_t reg, uint8_t buf); 70*22d7dd83SEmmanuel Vadot static int adm1030_set(struct adm1030_softc *fan, int pwm); 71*22d7dd83SEmmanuel Vadot static int adm1030_sysctl(SYSCTL_HANDLER_ARGS); 72*22d7dd83SEmmanuel Vadot 73*22d7dd83SEmmanuel Vadot static device_method_t adm1030_methods[] = { 74*22d7dd83SEmmanuel Vadot /* Device interface */ 75*22d7dd83SEmmanuel Vadot DEVMETHOD(device_probe, adm1030_probe), 76*22d7dd83SEmmanuel Vadot DEVMETHOD(device_attach, adm1030_attach), 77*22d7dd83SEmmanuel Vadot {0, 0}, 78*22d7dd83SEmmanuel Vadot }; 79*22d7dd83SEmmanuel Vadot 80*22d7dd83SEmmanuel Vadot static driver_t adm1030_driver = { 81*22d7dd83SEmmanuel Vadot "adm1030", 82*22d7dd83SEmmanuel Vadot adm1030_methods, 83*22d7dd83SEmmanuel Vadot sizeof(struct adm1030_softc) 84*22d7dd83SEmmanuel Vadot }; 85*22d7dd83SEmmanuel Vadot 86*22d7dd83SEmmanuel Vadot DRIVER_MODULE(adm1030, iicbus, adm1030_driver, 0, 0); 87*22d7dd83SEmmanuel Vadot 88*22d7dd83SEmmanuel Vadot static int 89*22d7dd83SEmmanuel Vadot adm1030_write_byte(device_t dev, uint32_t addr, uint8_t reg, uint8_t byte) 90*22d7dd83SEmmanuel Vadot { 91*22d7dd83SEmmanuel Vadot unsigned char buf[4]; 92*22d7dd83SEmmanuel Vadot int try = 0; 93*22d7dd83SEmmanuel Vadot 94*22d7dd83SEmmanuel Vadot struct iic_msg msg[] = { 95*22d7dd83SEmmanuel Vadot {addr, IIC_M_WR, 0, buf} 96*22d7dd83SEmmanuel Vadot }; 97*22d7dd83SEmmanuel Vadot 98*22d7dd83SEmmanuel Vadot msg[0].len = 2; 99*22d7dd83SEmmanuel Vadot buf[0] = reg; 100*22d7dd83SEmmanuel Vadot buf[1] = byte; 101*22d7dd83SEmmanuel Vadot 102*22d7dd83SEmmanuel Vadot for (;;) 103*22d7dd83SEmmanuel Vadot { 104*22d7dd83SEmmanuel Vadot if (iicbus_transfer(dev, msg, 1) == 0) 105*22d7dd83SEmmanuel Vadot return (0); 106*22d7dd83SEmmanuel Vadot 107*22d7dd83SEmmanuel Vadot if (++try > 5) { 108*22d7dd83SEmmanuel Vadot device_printf(dev, "iicbus write failed\n"); 109*22d7dd83SEmmanuel Vadot return (-1); 110*22d7dd83SEmmanuel Vadot } 111*22d7dd83SEmmanuel Vadot pause("adm1030_write_byte", hz); 112*22d7dd83SEmmanuel Vadot } 113*22d7dd83SEmmanuel Vadot } 114*22d7dd83SEmmanuel Vadot 115*22d7dd83SEmmanuel Vadot static int 116*22d7dd83SEmmanuel Vadot adm1030_probe(device_t dev) 117*22d7dd83SEmmanuel Vadot { 118*22d7dd83SEmmanuel Vadot const char *name, *compatible; 119*22d7dd83SEmmanuel Vadot struct adm1030_softc *sc; 120*22d7dd83SEmmanuel Vadot phandle_t handle; 121*22d7dd83SEmmanuel Vadot phandle_t thermostat; 122*22d7dd83SEmmanuel Vadot 123*22d7dd83SEmmanuel Vadot name = ofw_bus_get_name(dev); 124*22d7dd83SEmmanuel Vadot compatible = ofw_bus_get_compat(dev); 125*22d7dd83SEmmanuel Vadot handle = ofw_bus_get_node(dev); 126*22d7dd83SEmmanuel Vadot 127*22d7dd83SEmmanuel Vadot if (!name) 128*22d7dd83SEmmanuel Vadot return (ENXIO); 129*22d7dd83SEmmanuel Vadot 130*22d7dd83SEmmanuel Vadot if (strcmp(name, "fan") != 0 || strcmp(compatible, "adm1030") != 0) 131*22d7dd83SEmmanuel Vadot return (ENXIO); 132*22d7dd83SEmmanuel Vadot 133*22d7dd83SEmmanuel Vadot /* This driver can only be used if there's an associated temp sensor. */ 134*22d7dd83SEmmanuel Vadot if (OF_getprop(handle, "platform-getTemp", &thermostat, sizeof(thermostat)) < 0) 135*22d7dd83SEmmanuel Vadot return (ENXIO); 136*22d7dd83SEmmanuel Vadot 137*22d7dd83SEmmanuel Vadot sc = device_get_softc(dev); 138*22d7dd83SEmmanuel Vadot sc->sc_dev = dev; 139*22d7dd83SEmmanuel Vadot sc->sc_addr = iicbus_get_addr(dev); 140*22d7dd83SEmmanuel Vadot 141*22d7dd83SEmmanuel Vadot device_set_desc(dev, "G4 MDD Fan driver"); 142*22d7dd83SEmmanuel Vadot 143*22d7dd83SEmmanuel Vadot return (0); 144*22d7dd83SEmmanuel Vadot } 145*22d7dd83SEmmanuel Vadot 146*22d7dd83SEmmanuel Vadot static int 147*22d7dd83SEmmanuel Vadot adm1030_attach(device_t dev) 148*22d7dd83SEmmanuel Vadot { 149*22d7dd83SEmmanuel Vadot struct adm1030_softc *sc; 150*22d7dd83SEmmanuel Vadot struct sysctl_ctx_list *ctx; 151*22d7dd83SEmmanuel Vadot struct sysctl_oid *tree; 152*22d7dd83SEmmanuel Vadot 153*22d7dd83SEmmanuel Vadot sc = device_get_softc(dev); 154*22d7dd83SEmmanuel Vadot 155*22d7dd83SEmmanuel Vadot sc->enum_hook.ich_func = adm1030_start; 156*22d7dd83SEmmanuel Vadot sc->enum_hook.ich_arg = dev; 157*22d7dd83SEmmanuel Vadot 158*22d7dd83SEmmanuel Vadot /* 159*22d7dd83SEmmanuel Vadot * Wait until interrupts are available, which won't be until the openpic is 160*22d7dd83SEmmanuel Vadot * intialized. 161*22d7dd83SEmmanuel Vadot */ 162*22d7dd83SEmmanuel Vadot 163*22d7dd83SEmmanuel Vadot if (config_intrhook_establish(&sc->enum_hook) != 0) 164*22d7dd83SEmmanuel Vadot return (ENOMEM); 165*22d7dd83SEmmanuel Vadot 166*22d7dd83SEmmanuel Vadot ctx = device_get_sysctl_ctx(dev); 167*22d7dd83SEmmanuel Vadot tree = device_get_sysctl_tree(dev); 168*22d7dd83SEmmanuel Vadot SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pwm", 169*22d7dd83SEmmanuel Vadot CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, 170*22d7dd83SEmmanuel Vadot 0, adm1030_sysctl, "I", "Fan PWM Rate"); 171*22d7dd83SEmmanuel Vadot 172*22d7dd83SEmmanuel Vadot return (0); 173*22d7dd83SEmmanuel Vadot } 174*22d7dd83SEmmanuel Vadot 175*22d7dd83SEmmanuel Vadot static void 176*22d7dd83SEmmanuel Vadot adm1030_start(void *xdev) 177*22d7dd83SEmmanuel Vadot { 178*22d7dd83SEmmanuel Vadot struct adm1030_softc *sc; 179*22d7dd83SEmmanuel Vadot 180*22d7dd83SEmmanuel Vadot device_t dev = (device_t) xdev; 181*22d7dd83SEmmanuel Vadot 182*22d7dd83SEmmanuel Vadot sc = device_get_softc(dev); 183*22d7dd83SEmmanuel Vadot 184*22d7dd83SEmmanuel Vadot /* Start the adm1030 device. */ 185*22d7dd83SEmmanuel Vadot adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x1, 0x1); 186*22d7dd83SEmmanuel Vadot adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x0, 0x95); 187*22d7dd83SEmmanuel Vadot adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x23, 0x91); 188*22d7dd83SEmmanuel Vadot 189*22d7dd83SEmmanuel Vadot /* Use the RPM fields as PWM duty cycles. */ 190*22d7dd83SEmmanuel Vadot sc->fan.min_rpm = 0; 191*22d7dd83SEmmanuel Vadot sc->fan.max_rpm = 0x0F; 192*22d7dd83SEmmanuel Vadot sc->fan.default_rpm = 2; 193*22d7dd83SEmmanuel Vadot 194*22d7dd83SEmmanuel Vadot strcpy(sc->fan.name, "MDD Case fan"); 195*22d7dd83SEmmanuel Vadot sc->fan.zone = 0; 196*22d7dd83SEmmanuel Vadot sc->fan.read = NULL; 197*22d7dd83SEmmanuel Vadot sc->fan.set = (int (*)(struct pmac_fan *, int))adm1030_set; 198*22d7dd83SEmmanuel Vadot config_intrhook_disestablish(&sc->enum_hook); 199*22d7dd83SEmmanuel Vadot 200*22d7dd83SEmmanuel Vadot pmac_thermal_fan_register(&sc->fan); 201*22d7dd83SEmmanuel Vadot } 202*22d7dd83SEmmanuel Vadot 203*22d7dd83SEmmanuel Vadot static int adm1030_set(struct adm1030_softc *fan, int pwm) 204*22d7dd83SEmmanuel Vadot { 205*22d7dd83SEmmanuel Vadot /* Clamp the PWM to 0-0xF, one nibble. */ 206*22d7dd83SEmmanuel Vadot if (pwm > 0xF) 207*22d7dd83SEmmanuel Vadot pwm = 0xF; 208*22d7dd83SEmmanuel Vadot if (pwm < 0) 209*22d7dd83SEmmanuel Vadot pwm = 0; 210*22d7dd83SEmmanuel Vadot 211*22d7dd83SEmmanuel Vadot if (adm1030_write_byte(fan->sc_dev, fan->sc_addr, 0x22, pwm) < 0) 212*22d7dd83SEmmanuel Vadot return (-1); 213*22d7dd83SEmmanuel Vadot 214*22d7dd83SEmmanuel Vadot fan->sc_pwm = pwm; 215*22d7dd83SEmmanuel Vadot return (0); 216*22d7dd83SEmmanuel Vadot } 217*22d7dd83SEmmanuel Vadot 218*22d7dd83SEmmanuel Vadot static int 219*22d7dd83SEmmanuel Vadot adm1030_sysctl(SYSCTL_HANDLER_ARGS) 220*22d7dd83SEmmanuel Vadot { 221*22d7dd83SEmmanuel Vadot device_t adm1030; 222*22d7dd83SEmmanuel Vadot struct adm1030_softc *sc; 223*22d7dd83SEmmanuel Vadot int pwm, error; 224*22d7dd83SEmmanuel Vadot 225*22d7dd83SEmmanuel Vadot adm1030 = arg1; 226*22d7dd83SEmmanuel Vadot sc = device_get_softc(adm1030); 227*22d7dd83SEmmanuel Vadot 228*22d7dd83SEmmanuel Vadot pwm = sc->sc_pwm; 229*22d7dd83SEmmanuel Vadot 230*22d7dd83SEmmanuel Vadot error = sysctl_handle_int(oidp, &pwm, 0, req); 231*22d7dd83SEmmanuel Vadot 232*22d7dd83SEmmanuel Vadot if (error || !req->newptr) 233*22d7dd83SEmmanuel Vadot return (error); 234*22d7dd83SEmmanuel Vadot 235*22d7dd83SEmmanuel Vadot return (adm1030_set(sc, pwm)); 236*22d7dd83SEmmanuel Vadot } 237