1 /* $NetBSD: acpi_tz.c,v 1.2 2003/01/05 12:16:22 jdolecek Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * ACPI Thermal Zone driver 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.2 2003/01/05 12:16:22 jdolecek Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/errno.h> 39 #include <sys/ioctl.h> 40 #include <sys/syslog.h> 41 #include <sys/device.h> 42 #include <sys/callout.h> 43 #include <sys/proc.h> 44 #include <sys/lock.h> 45 #include <dev/sysmon/sysmonvar.h> 46 47 #include <dev/acpi/acpica.h> 48 #include <dev/acpi/acpireg.h> 49 #include <dev/acpi/acpivar.h> 50 51 /* flags */ 52 #define ATZ_F_VERBOSE 0x01 /* show events to console */ 53 54 /* constants */ 55 #define ATZ_TZP_RATE 300 /* default if no _TZP CM present (30 secs) */ 56 #define ATZ_NLEVELS 10 /* number of cooling levels, from ACPI spec */ 57 58 /* sensor indexes */ 59 #define ATZ_SENSOR_TEMP 0 /* thermal zone temperature */ 60 #define ATZ_NUMSENSORS 1 /* number of sensors */ 61 62 const struct envsys_range acpitz_ranges[] = { 63 { 0, 1, ATZ_SENSOR_TEMP }, 64 }; 65 66 int acpitz_match(struct device *, struct cfdata *, void *); 67 void acpitz_attach(struct device *, struct device *, void *); 68 69 /* 70 * ACPI Temperature Zone information. Note all temperatures are reported 71 * in tenths of degrees Kelvin 72 */ 73 struct acpitz_zone { 74 /* Active cooling temperature threshold */ 75 UINT32 ac[ATZ_NLEVELS]; 76 /* Package of references to all active cooling devices for a level */ 77 ACPI_BUFFER al[ATZ_NLEVELS]; 78 /* Critical temperature threshold for system shutdown */ 79 UINT32 crt; 80 /* Critical temperature threshold for S4 sleep */ 81 UINT32 hot; 82 /* Package of references to processor objects for passive cooling */ 83 ACPI_BUFFER psl; 84 /* Passive cooling temperature threshold */ 85 UINT32 psv; 86 /* Thermal constants for use in passive cooling formulas */ 87 UINT32 tc1, tc2; 88 /* Current temperature of the thermal zone */ 89 UINT32 tmp; 90 /* Thermal sampling period for passive cooling, in tenths of seconds */ 91 UINT32 tsp; 92 /* Package of references to devices in this TZ (optional) */ 93 ACPI_BUFFER tzd; 94 /* Recommended TZ polling frequency, in tenths of seconds */ 95 UINT32 tzp; 96 }; 97 98 struct acpitz_softc { 99 struct device sc_dev; 100 struct acpi_devnode *sc_devnode; 101 struct acpitz_zone sc_zone; 102 struct callout sc_callout; 103 struct envsys_tre_data sc_data[ATZ_NUMSENSORS]; 104 struct envsys_basic_info sc_info[ATZ_NUMSENSORS]; 105 struct sysmon_envsys sc_sysmon; 106 struct simplelock sc_slock; 107 int sc_flags; 108 int sc_rate; /* tz poll rate */ 109 }; 110 111 void acpitz_get_status(void *); 112 static void acpitz_print_status(struct acpitz_softc *); 113 void acpitz_notify_handler(ACPI_HANDLE, UINT32, void *); 114 int acpitz_get_integer(struct acpitz_softc *, char *, UINT32 *); 115 static void acpitz_tick(void *); 116 static void acpitz_init_envsys(struct acpitz_softc *); 117 static int acpitz_gtredata(struct sysmon_envsys *, 118 struct envsys_tre_data *); 119 static int acpitz_streinfo(struct sysmon_envsys *, 120 struct envsys_basic_info *); 121 122 CFATTACH_DECL(acpitz, sizeof(struct acpitz_softc), acpitz_match, 123 acpitz_attach, NULL, NULL); 124 125 /* 126 * acpitz_match: autoconf(9) match routine 127 */ 128 int 129 acpitz_match(struct device *parent, struct cfdata *match, void *aux) 130 { 131 struct acpi_attach_args *aa = aux; 132 133 if (aa->aa_node->ad_type != ACPI_TYPE_THERMAL) 134 return 0; 135 136 return 1; 137 } 138 139 /* 140 * acpitz_attach: autoconf(9) attach routine 141 */ 142 void 143 acpitz_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct acpitz_softc *sc = (struct acpitz_softc *)self; 146 struct acpi_attach_args *aa = aux; 147 ACPI_STATUS rv; 148 149 #if 0 150 sc->sc_flags = ATZ_F_VERBOSE; 151 #endif 152 sc->sc_devnode = aa->aa_node; 153 154 printf(": ACPI Thermal Zone\n"); 155 156 if (acpitz_get_integer(sc, "_TZP", &sc->sc_zone.tzp)) { 157 #if 0 158 printf("%s: unable to get poll rate, using default\n", 159 sc->sc_dev.dv_xname); 160 #endif 161 sc->sc_zone.tzp = ATZ_TZP_RATE; 162 } 163 /* XXX a value of 0 means "polling is not necessary" */ 164 if (sc->sc_zone.tzp == 0) 165 sc->sc_zone.tzp = ATZ_TZP_RATE; 166 167 acpitz_get_status(sc); 168 169 rv = AcpiInstallNotifyHandler(sc->sc_devnode->ad_handle, 170 ACPI_SYSTEM_NOTIFY, acpitz_notify_handler, sc); 171 if (rv != AE_OK) { 172 printf("%s: unable to install system notify handler\n", 173 sc->sc_dev.dv_xname); 174 return; 175 } 176 177 callout_init(&sc->sc_callout); 178 callout_reset(&sc->sc_callout, (sc->sc_zone.tzp / 10) * hz, 179 acpitz_tick, sc); 180 181 acpitz_init_envsys(sc); 182 } 183 184 void 185 acpitz_get_status(void *opaque) 186 { 187 struct acpitz_softc *sc = opaque; 188 189 acpitz_get_integer(sc, "_TMP", &sc->sc_zone.tmp); 190 sc->sc_data[ATZ_SENSOR_TEMP].cur.data_us = sc->sc_zone.tmp * 100000; 191 192 if (sc->sc_flags & ATZ_F_VERBOSE) 193 acpitz_print_status(sc); 194 195 return; 196 } 197 198 static void 199 acpitz_print_status(struct acpitz_softc *sc) 200 { 201 202 printf("%s: zone temperature is now %d K\n", sc->sc_dev.dv_xname, 203 sc->sc_zone.tmp / 10); 204 205 return; 206 } 207 208 void 209 acpitz_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque) 210 { 211 struct acpitz_softc *sc = opaque; 212 int rv; 213 214 switch (notify) { 215 case ACPI_NOTIFY_ThermalZoneStatusChanged: 216 case ACPI_NOTIFY_ThermalZoneTripPointsChanged: 217 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 218 acpitz_get_status, sc); 219 if (rv != AE_OK) { 220 printf("%s: unable to queue status check\n", 221 sc->sc_dev.dv_xname); 222 } 223 break; 224 default: 225 printf("%s: received unhandled notify message %d\n", 226 sc->sc_dev.dv_xname, notify); 227 break; 228 } 229 230 return; 231 } 232 233 int 234 acpitz_get_integer(struct acpitz_softc *sc, char *cm, UINT32 *rv) 235 { 236 ACPI_STATUS status; 237 238 status = acpi_eval_integer(sc->sc_devnode->ad_handle, cm, rv); 239 if (status != AE_OK) { 240 #ifdef ACPI_DEBUG 241 printf("%s: failed to evaluate %s: %x\n", sc->sc_dev.dv_xname, 242 cm, status); 243 #endif 244 return 1; 245 } 246 247 return 0; 248 } 249 250 static void 251 acpitz_tick(void *opaque) 252 { 253 struct acpitz_softc *sc = opaque; 254 255 callout_reset(&sc->sc_callout, (sc->sc_zone.tzp / 10) * hz, 256 acpitz_tick, opaque); 257 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpitz_get_status, sc); 258 259 return; 260 } 261 262 static void 263 acpitz_init_envsys(struct acpitz_softc *sc) 264 { 265 int i; 266 267 simple_lock_init(&sc->sc_slock); 268 269 for (i = 0; i < ATZ_NUMSENSORS; i++) { 270 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 271 sc->sc_data[i].validflags = (ENVSYS_FVALID | ENVSYS_FCURVALID); 272 sc->sc_info[i].validflags = ENVSYS_FVALID; 273 sc->sc_data[i].warnflags = ENVSYS_WARN_OK; 274 } 275 #define INITDATA(index, unit, string) \ 276 sc->sc_data[index].units = unit; \ 277 sc->sc_info[index].units = unit; \ 278 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info[index].desc), \ 279 "%s %s", sc->sc_dev.dv_xname, string); 280 281 INITDATA(ATZ_SENSOR_TEMP, ENVSYS_STEMP, "temperature"); 282 283 /* hook into sysmon */ 284 sc->sc_sysmon.sme_ranges = acpitz_ranges; 285 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 286 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 287 sc->sc_sysmon.sme_cookie = sc; 288 sc->sc_sysmon.sme_gtredata = acpitz_gtredata; 289 sc->sc_sysmon.sme_streinfo = acpitz_streinfo; 290 sc->sc_sysmon.sme_nsensors = ATZ_NUMSENSORS; 291 sc->sc_sysmon.sme_envsys_version = 1000; 292 293 if (sysmon_envsys_register(&sc->sc_sysmon)) 294 printf("%s: unable to register with sysmon\n", 295 sc->sc_dev.dv_xname); 296 } 297 298 int 299 acpitz_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 300 { 301 struct acpitz_softc *sc = sme->sme_cookie; 302 303 simple_lock(&sc->sc_slock); 304 305 *tred = sc->sc_data[tred->sensor]; 306 307 simple_unlock(&sc->sc_slock); 308 309 return 0; 310 } 311 312 int 313 acpitz_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 314 { 315 316 /* XXX not implemented */ 317 binfo->validflags = 0; 318 319 return 0; 320 } 321