1222686Sandreast/*- 2222686Sandreast * Copyright (c) 2011 Justin Hibbits 3222686Sandreast * Copyright (c) 2010 Andreas Tobler 4222686Sandreast * All rights reserved. 5222686Sandreast * 6222686Sandreast * Redistribution and use in source and binary forms, with or without 7222686Sandreast * modification, are permitted provided that the following conditions 8222686Sandreast * are met: 9222686Sandreast * 1. Redistributions of source code must retain the above copyright 10222686Sandreast * notice, this list of conditions and the following disclaimer. 11222686Sandreast * 2. Redistributions in binary form must reproduce the above copyright 12222686Sandreast * notice, this list of conditions and the following disclaimer in the 13222686Sandreast * documentation and/or other materials provided with the distribution. 14222686Sandreast * 15222686Sandreast * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16222686Sandreast * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17222686Sandreast * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18222686Sandreast * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19222686Sandreast * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20222686Sandreast * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21222686Sandreast * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22222686Sandreast * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23222686Sandreast * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24222686Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25222686Sandreast * SUCH DAMAGE. 26222686Sandreast */ 27222686Sandreast 28222686Sandreast#include <sys/cdefs.h> 29222686Sandreast__FBSDID("$FreeBSD$"); 30222686Sandreast 31222686Sandreast#include <sys/param.h> 32222686Sandreast#include <sys/bus.h> 33222686Sandreast#include <sys/systm.h> 34222686Sandreast#include <sys/module.h> 35222686Sandreast#include <sys/callout.h> 36222686Sandreast#include <sys/conf.h> 37222686Sandreast#include <sys/cpu.h> 38222686Sandreast#include <sys/ctype.h> 39222686Sandreast#include <sys/kernel.h> 40222686Sandreast#include <sys/kthread.h> 41222686Sandreast#include <sys/limits.h> 42222686Sandreast#include <sys/reboot.h> 43222686Sandreast#include <sys/rman.h> 44222686Sandreast#include <sys/sysctl.h> 45222686Sandreast#include <sys/unistd.h> 46222686Sandreast 47222686Sandreast#include <machine/bus.h> 48222686Sandreast#include <machine/md_var.h> 49222686Sandreast 50222686Sandreast#include <dev/iicbus/iicbus.h> 51222686Sandreast#include <dev/iicbus/iiconf.h> 52222686Sandreast 53222686Sandreast#include <dev/ofw/openfirm.h> 54222686Sandreast#include <dev/ofw/ofw_bus.h> 55222686Sandreast#include <powerpc/powermac/powermac_thermal.h> 56222686Sandreast 57222686Sandreaststruct adm1030_softc { 58222686Sandreast struct pmac_fan fan; 59222686Sandreast device_t sc_dev; 60222686Sandreast struct intr_config_hook enum_hook; 61222686Sandreast uint32_t sc_addr; 62230035Sjhibbits int sc_pwm; 63222686Sandreast}; 64222686Sandreast 65222686Sandreast/* Regular bus attachment functions */ 66222686Sandreaststatic int adm1030_probe(device_t); 67222686Sandreaststatic int adm1030_attach(device_t); 68222686Sandreast 69222686Sandreast/* Utility functions */ 70222686Sandreaststatic void adm1030_start(void *xdev); 71222686Sandreaststatic int adm1030_write_byte(device_t dev, uint32_t addr, uint8_t reg, uint8_t buf); 72230035Sjhibbitsstatic int adm1030_set(struct adm1030_softc *fan, int pwm); 73230035Sjhibbitsstatic int adm1030_sysctl(SYSCTL_HANDLER_ARGS); 74222686Sandreast 75222686Sandreaststatic device_method_t adm1030_methods[] = { 76222686Sandreast /* Device interface */ 77222686Sandreast DEVMETHOD(device_probe, adm1030_probe), 78222686Sandreast DEVMETHOD(device_attach, adm1030_attach), 79222686Sandreast {0, 0}, 80222686Sandreast}; 81222686Sandreast 82222686Sandreaststatic driver_t adm1030_driver = { 83222686Sandreast "adm1030", 84222686Sandreast adm1030_methods, 85222686Sandreast sizeof(struct adm1030_softc) 86222686Sandreast}; 87222686Sandreast 88222686Sandreaststatic devclass_t adm1030_devclass; 89222686Sandreast 90222686SandreastDRIVER_MODULE(adm1030, iicbus, adm1030_driver, adm1030_devclass, 0, 0); 91222686Sandreast 92222686Sandreaststatic int 93222686Sandreastadm1030_write_byte(device_t dev, uint32_t addr, uint8_t reg, uint8_t byte) 94222686Sandreast{ 95222686Sandreast unsigned char buf[4]; 96222686Sandreast int try = 0; 97222686Sandreast 98222686Sandreast struct iic_msg msg[] = { 99222686Sandreast {addr, IIC_M_WR, 0, buf} 100222686Sandreast }; 101222686Sandreast 102222686Sandreast msg[0].len = 2; 103222686Sandreast buf[0] = reg; 104222686Sandreast buf[1] = byte; 105222686Sandreast 106222686Sandreast for (;;) 107222686Sandreast { 108222686Sandreast if (iicbus_transfer(dev, msg, 1) == 0) 109222686Sandreast return (0); 110222686Sandreast 111222686Sandreast if (++try > 5) { 112222686Sandreast device_printf(dev, "iicbus write failed\n"); 113222686Sandreast return (-1); 114222686Sandreast } 115222686Sandreast pause("adm1030_write_byte", hz); 116222686Sandreast } 117222686Sandreast} 118222686Sandreast 119222686Sandreaststatic int 120222686Sandreastadm1030_probe(device_t dev) 121222686Sandreast{ 122222686Sandreast const char *name, *compatible; 123222686Sandreast struct adm1030_softc *sc; 124222686Sandreast phandle_t handle; 125222686Sandreast phandle_t thermostat; 126222686Sandreast 127222686Sandreast name = ofw_bus_get_name(dev); 128222686Sandreast compatible = ofw_bus_get_compat(dev); 129222686Sandreast handle = ofw_bus_get_node(dev); 130222686Sandreast 131222686Sandreast if (!name) 132222686Sandreast return (ENXIO); 133222686Sandreast 134222686Sandreast if (strcmp(name, "fan") != 0 || strcmp(compatible, "adm1030") != 0) 135222686Sandreast return (ENXIO); 136222686Sandreast 137222686Sandreast /* This driver can only be used if there's an associated temp sensor. */ 138222686Sandreast if (OF_getprop(handle, "platform-getTemp", &thermostat, sizeof(thermostat)) < 0) 139222686Sandreast return (ENXIO); 140222686Sandreast 141222686Sandreast sc = device_get_softc(dev); 142222686Sandreast sc->sc_dev = dev; 143222686Sandreast sc->sc_addr = iicbus_get_addr(dev); 144222686Sandreast 145222686Sandreast device_set_desc(dev, "G4 MDD Fan driver"); 146222686Sandreast 147222686Sandreast return (0); 148222686Sandreast} 149222686Sandreast 150222686Sandreaststatic int 151222686Sandreastadm1030_attach(device_t dev) 152222686Sandreast{ 153222686Sandreast struct adm1030_softc *sc; 154230035Sjhibbits struct sysctl_ctx_list *ctx; 155230035Sjhibbits struct sysctl_oid *tree; 156222686Sandreast 157222686Sandreast sc = device_get_softc(dev); 158222686Sandreast 159222686Sandreast sc->enum_hook.ich_func = adm1030_start; 160222686Sandreast sc->enum_hook.ich_arg = dev; 161222686Sandreast 162222686Sandreast /* 163230035Sjhibbits * Wait until interrupts are available, which won't be until the openpic is 164230035Sjhibbits * intialized. 165222686Sandreast */ 166222686Sandreast 167222686Sandreast if (config_intrhook_establish(&sc->enum_hook) != 0) 168222686Sandreast return (ENOMEM); 169222686Sandreast 170230035Sjhibbits ctx = device_get_sysctl_ctx(dev); 171230035Sjhibbits tree = device_get_sysctl_tree(dev); 172230035Sjhibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pwm", 173230035Sjhibbits CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, 174230035Sjhibbits 0, adm1030_sysctl, "I", "Fan PWM Rate"); 175230035Sjhibbits 176222686Sandreast return (0); 177222686Sandreast} 178222686Sandreast 179222686Sandreaststatic void 180222686Sandreastadm1030_start(void *xdev) 181222686Sandreast{ 182222686Sandreast struct adm1030_softc *sc; 183222686Sandreast 184222686Sandreast device_t dev = (device_t) xdev; 185222686Sandreast 186222686Sandreast sc = device_get_softc(dev); 187222686Sandreast 188222686Sandreast /* Start the adm1030 device. */ 189222686Sandreast adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x1, 0x1); 190222686Sandreast adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x0, 0x95); 191222686Sandreast adm1030_write_byte(sc->sc_dev, sc->sc_addr, 0x23, 0x91); 192222686Sandreast 193222686Sandreast /* Use the RPM fields as PWM duty cycles. */ 194222686Sandreast sc->fan.min_rpm = 0; 195230035Sjhibbits sc->fan.max_rpm = 0x0F; 196222686Sandreast sc->fan.default_rpm = 2; 197222686Sandreast 198222686Sandreast strcpy(sc->fan.name, "MDD Case fan"); 199222686Sandreast sc->fan.zone = 0; 200222686Sandreast sc->fan.read = NULL; 201222686Sandreast sc->fan.set = (int (*)(struct pmac_fan *, int))adm1030_set; 202222686Sandreast config_intrhook_disestablish(&sc->enum_hook); 203222686Sandreast 204222686Sandreast pmac_thermal_fan_register(&sc->fan); 205222686Sandreast} 206222686Sandreast 207222686Sandreaststatic int adm1030_set(struct adm1030_softc *fan, int pwm) 208222686Sandreast{ 209222686Sandreast /* Clamp the PWM to 0-0xF, one nibble. */ 210222686Sandreast if (pwm > 0xF) 211222686Sandreast pwm = 0xF; 212222686Sandreast if (pwm < 0) 213222686Sandreast pwm = 0; 214222686Sandreast 215222686Sandreast if (adm1030_write_byte(fan->sc_dev, fan->sc_addr, 0x22, pwm) < 0) 216222686Sandreast return (-1); 217222686Sandreast 218230035Sjhibbits fan->sc_pwm = pwm; 219222686Sandreast return (0); 220222686Sandreast} 221222686Sandreast 222230035Sjhibbitsstatic int 223230035Sjhibbitsadm1030_sysctl(SYSCTL_HANDLER_ARGS) 224230035Sjhibbits{ 225230035Sjhibbits device_t adm1030; 226230035Sjhibbits struct adm1030_softc *sc; 227230035Sjhibbits int pwm, error; 228230035Sjhibbits 229230035Sjhibbits adm1030 = arg1; 230230035Sjhibbits sc = device_get_softc(adm1030); 231230035Sjhibbits 232230035Sjhibbits pwm = sc->sc_pwm; 233230035Sjhibbits 234230035Sjhibbits error = sysctl_handle_int(oidp, &pwm, 0, req); 235230035Sjhibbits 236230035Sjhibbits if (error || !req->newptr) 237230035Sjhibbits return (error); 238230035Sjhibbits 239230035Sjhibbits return (adm1030_set(sc, pwm)); 240230035Sjhibbits} 241