1 /* $OpenBSD: acpipwrres.c,v 1.4 2010/07/21 19:35:15 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/param.h> 18 #include <sys/proc.h> 19 #include <sys/signalvar.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <machine/bus.h> 25 26 #include <dev/acpi/acpireg.h> 27 #include <dev/acpi/acpivar.h> 28 #include <dev/acpi/acpidev.h> 29 #include <dev/acpi/amltypes.h> 30 #include <dev/acpi/dsdt.h> 31 32 int acpipwrres_match(struct device *, void *, void *); 33 void acpipwrres_attach(struct device *, struct device *, void *); 34 int acpipwrres_notify(struct aml_node *, int, void *); 35 36 #define NOTIFY_PWRRES_OFF 0 37 #define NOTIFY_PWRRES_ON 1 38 39 #ifdef ACPIPWRRES_DEBUG 40 #define DPRINTF(x) printf x 41 #else 42 #define DPRINTF(x) 43 #endif 44 45 struct acpipwrres_softc { 46 struct device sc_dev; 47 48 bus_space_tag_t sc_iot; 49 bus_space_handle_t sc_ioh; 50 51 struct acpi_softc *sc_acpi; 52 struct aml_node *sc_devnode; 53 54 TAILQ_HEAD(acpipwrres_cons_h, acpipwrres_consumer) sc_cons; 55 56 int sc_level; 57 int sc_order; 58 int sc_ncons; 59 int sc_state; 60 #define ACPIPWRRES_OFF 0 61 #define ACPIPWRRES_ON 1 62 #define ACPIPWRRES_UNK -1 63 }; 64 65 struct acpipwrres_consumer { 66 struct aml_node *cs_node; 67 TAILQ_ENTRY(acpipwrres_consumer) cs_link; 68 }; 69 70 struct cfattach acpipwrres_ca = { 71 sizeof(struct acpipwrres_softc), acpipwrres_match, acpipwrres_attach 72 }; 73 74 struct cfdriver acpipwrres_cd = { 75 NULL, "acpipwrres", DV_DULL 76 }; 77 78 int acpipwrres_foundcons(struct aml_node *, void *); 79 80 int 81 acpipwrres_match(struct device *parent, void *match, void *aux) 82 { 83 struct acpi_attach_args *aaa = aux; 84 struct cfdata *cf = match; 85 86 if (aaa->aaa_name == NULL || strcmp(aaa->aaa_name, 87 cf->cf_driver->cd_name) != 0 || aaa->aaa_table != NULL) 88 return (0); 89 90 return (1); 91 } 92 93 void 94 acpipwrres_attach(struct device *parent, struct device *self, void *aux) 95 { 96 struct acpipwrres_softc *sc = (struct acpipwrres_softc *)self; 97 struct acpi_attach_args *aaa = aux; 98 struct aml_value res; 99 100 extern struct aml_node aml_root; 101 102 sc->sc_acpi = (struct acpi_softc *)parent; 103 sc->sc_devnode = aaa->aaa_node; 104 memset(&res, 0, sizeof res); 105 106 printf(": %s\n", sc->sc_devnode->name); 107 108 aml_register_notify(sc->sc_devnode, aaa->aaa_dev, 109 acpipwrres_notify, sc, ACPIDEV_NOPOLL); 110 111 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) { 112 sc->sc_state = (int)aml_val2int(&res); 113 if (sc->sc_state != ACPIPWRRES_ON && 114 sc->sc_state != ACPIPWRRES_OFF) 115 sc->sc_state = ACPIPWRRES_UNK; 116 } else 117 sc->sc_state = ACPIPWRRES_UNK; 118 DPRINTF(("%s: state = %d\n", DEVNAME(sc), sc->sc_state)); 119 if (aml_evalnode(sc->sc_acpi, aaa->aaa_node, 0, NULL, &res) == 0) { 120 sc->sc_level = res.v_powerrsrc.pwr_level; 121 sc->sc_order = res.v_powerrsrc.pwr_order; 122 DPRINTF(("%s: level = %d, order %d\n", DEVNAME(sc), 123 sc->sc_level, sc->sc_order)); 124 aml_freevalue(&res); 125 } 126 127 /* Get the list of consumers */ 128 TAILQ_INIT(&sc->sc_cons); 129 aml_find_node(&aml_root, "_PRW", acpipwrres_foundcons, sc); 130 aml_find_node(&aml_root, "_PR0", acpipwrres_foundcons, sc); 131 aml_find_node(&aml_root, "_PR1", acpipwrres_foundcons, sc); 132 aml_find_node(&aml_root, "_PR2", acpipwrres_foundcons, sc); 133 } 134 135 int 136 acpipwrres_notify(struct aml_node *node, int notify, void *arg) 137 { 138 int fmatch = 0; 139 struct acpipwrres_consumer *cons; 140 struct acpipwrres_softc *sc = arg; 141 struct aml_value res; 142 143 memset(&res, 0, sizeof res); 144 145 TAILQ_FOREACH(cons, &sc->sc_cons, cs_link) 146 if (cons->cs_node == node) { 147 fmatch = 1; 148 break; 149 } 150 if (!fmatch) 151 return (0); 152 153 switch (notify) { 154 case NOTIFY_PWRRES_ON: 155 DPRINTF(("pwr: on devs %d\n", sc->sc_ncons)); 156 if (sc->sc_ncons++ == 0) 157 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_ON", 0, NULL, 158 &res); 159 aml_freevalue(&res); 160 break; 161 case NOTIFY_PWRRES_OFF: 162 DPRINTF(("pwr: off devs %d\n", sc->sc_ncons)); 163 if (--sc->sc_ncons == 0) 164 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OFF", 0, NULL, 165 &res); 166 aml_freevalue(&res); 167 break; 168 default: 169 printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify); 170 break; 171 } 172 173 return (0); 174 } 175 176 int 177 acpipwrres_foundcons(struct aml_node *node, void *arg) 178 { 179 int i = 0; 180 struct acpipwrres_consumer *cons; 181 struct aml_node *pnode; 182 struct acpipwrres_softc *sc = (struct acpipwrres_softc *)arg; 183 struct aml_value res; 184 185 extern struct aml_node aml_root; 186 187 memset(&res, 0, sizeof res); 188 189 if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) { 190 DPRINTF(("pwr: consumer not found\n")); 191 return (-1); 192 } else { 193 DPRINTF(("%s: node name %s\n", DEVNAME(sc), aml_nodename(node))); 194 if (!strcmp(node->name, "_PRW")) 195 i = 2; /* _PRW first two values are ints */ 196 for (; i < res.length; i++) { 197 pnode = aml_searchname(&aml_root, 198 res.v_package[i]->v_string); 199 if (pnode == sc->sc_devnode) { 200 DPRINTF(("%s: consumer match\n", DEVNAME(sc))); 201 cons = malloc(sizeof *cons, M_DEVBUF, 202 M_WAITOK | M_ZERO); 203 cons->cs_node = pnode; 204 TAILQ_INSERT_HEAD(&sc->sc_cons, cons, cs_link); 205 } 206 } 207 } 208 209 return (0); 210 } 211