1 /* $OpenBSD: wbsio.c,v 1.4 2008/04/08 18:48:43 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2008 Mark Kettenis <kettenis@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 18 /* 19 * Winbond LPC Super I/O driver. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/device.h> 24 #include <sys/kernel.h> 25 #include <sys/systm.h> 26 27 #include <machine/bus.h> 28 29 #include <dev/isa/isareg.h> 30 #include <dev/isa/isavar.h> 31 32 /* ISA bus registers */ 33 #define WBSIO_INDEX 0x00 /* Configuration Index Register */ 34 #define WBSIO_DATA 0x01 /* Configuration Data Register */ 35 36 #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */ 37 38 #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */ 39 #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */ 40 41 /* Configuration Space Registers */ 42 #define WBSIO_LDN 0x07 /* Logical Device Number */ 43 #define WBSIO_ID 0x20 /* Device ID */ 44 #define WBSIO_REV 0x21 /* Device Revision */ 45 46 #define WBSIO_ID_W83627HF 0x52 47 #define WBSIO_ID_W83627THF 0x82 48 #define WBSIO_ID_W83627EHF 0x88 49 #define WBSIO_ID_W83627DHG 0xa0 50 #define WBSIO_ID_W83627SF 0x59 51 #define WBSIO_ID_W83637HF 0x70 52 #define WBSIO_ID_W83697HF 0x60 53 54 /* Logical Device Number (LDN) Assignments */ 55 #define WBSIO_LDN_HM 0x0b 56 57 /* Hardware Monitor Control Registers (LDN B) */ 58 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 59 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 60 61 #ifdef WBSIO_DEBUG 62 #define DPRINTF(x) printf x 63 #else 64 #define DPRINTF(x) 65 #endif 66 67 struct wbsio_softc { 68 struct device sc_dev; 69 70 bus_space_tag_t sc_iot; 71 bus_space_handle_t sc_ioh; 72 }; 73 74 int wbsio_probe(struct device *, void *, void *); 75 void wbsio_attach(struct device *, struct device *, void *); 76 int wbsio_print(void *, const char *); 77 78 struct cfattach wbsio_ca = { 79 sizeof(struct wbsio_softc), 80 wbsio_probe, 81 wbsio_attach 82 }; 83 84 struct cfdriver wbsio_cd = { 85 NULL, "wbsio", DV_DULL 86 }; 87 88 static __inline void 89 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 90 { 91 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 92 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 93 } 94 95 static __inline void 96 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 97 { 98 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 99 } 100 101 static __inline u_int8_t 102 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 103 { 104 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 105 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 106 } 107 108 static __inline void 109 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 110 u_int8_t data) 111 { 112 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 113 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 114 } 115 116 int 117 wbsio_probe(struct device *parent, void *match, void *aux) 118 { 119 struct isa_attach_args *ia = aux; 120 bus_space_tag_t iot; 121 bus_space_handle_t ioh; 122 u_int8_t reg; 123 124 /* Match by device ID */ 125 iot = ia->ia_iot; 126 if (bus_space_map(iot, ia->ipa_io[0].base, WBSIO_IOSIZE, 0, &ioh)) 127 return (0); 128 wbsio_conf_enable(iot, ioh); 129 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 130 DPRINTF(("wbsio_probe: id 0x%02x\n", reg)); 131 wbsio_conf_disable(iot, ioh); 132 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 133 switch (reg) { 134 case WBSIO_ID_W83627HF: 135 case WBSIO_ID_W83627THF: 136 case WBSIO_ID_W83627EHF: 137 case WBSIO_ID_W83627DHG: 138 case WBSIO_ID_W83637HF: 139 case WBSIO_ID_W83697HF: 140 ia->ipa_nio = 1; 141 ia->ipa_io[0].length = WBSIO_IOSIZE; 142 ia->ipa_nmem = 0; 143 ia->ipa_nirq = 0; 144 ia->ipa_ndrq = 0; 145 return (1); 146 } 147 148 return (0); 149 } 150 151 void 152 wbsio_attach(struct device *parent, struct device *self, void *aux) 153 { 154 struct wbsio_softc *sc = (void *)self; 155 struct isa_attach_args *ia = aux; 156 struct isa_attach_args nia; 157 u_int8_t reg, reg0, reg1; 158 u_int16_t iobase; 159 160 /* Map ISA I/O space */ 161 sc->sc_iot = ia->ia_iot; 162 if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, 163 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 164 printf(": can't map I/O space\n"); 165 return; 166 } 167 168 /* Enter configuration mode */ 169 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 170 171 /* Read device ID */ 172 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 173 switch (reg) { 174 case WBSIO_ID_W83627HF: 175 printf(": W83627HF"); 176 break; 177 case WBSIO_ID_W83627THF: 178 printf(": W83627THF"); 179 break; 180 case WBSIO_ID_W83627EHF: 181 printf(": W83627EHF"); 182 break; 183 case WBSIO_ID_W83627DHG: 184 printf(": W83627DHG"); 185 break; 186 case WBSIO_ID_W83637HF: 187 printf(": W83637HF"); 188 break; 189 case WBSIO_ID_W83697HF: 190 printf(": W83697HF"); 191 break; 192 } 193 194 /* Read device revision */ 195 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 196 printf(" rev 0x%02x", reg); 197 198 /* Select HM logical device */ 199 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 200 201 /* 202 * The address should be 8-byte aligned, but it seems some 203 * BIOSes ignore this. They get away with it, because 204 * Apparently the hardware simply ignores the lower three 205 * bits. We do the same here. 206 */ 207 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 208 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 209 iobase = (reg1 << 8) | (reg0 & ~0x7); 210 211 printf("\n"); 212 213 /* Escape from configuration mode */ 214 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 215 216 if (iobase == 0) 217 return; 218 219 nia = *ia; 220 nia.ia_iobase = iobase; 221 config_found(self, &nia, wbsio_print); 222 } 223 224 int 225 wbsio_print(void *aux, const char *pnp) 226 { 227 struct isa_attach_args *ia = aux; 228 229 if (pnp) 230 printf("%s", pnp); 231 if (ia->ia_iosize) 232 printf(" port 0x%x", ia->ia_iobase); 233 if (ia->ia_iosize > 1) 234 printf("/%d", ia->ia_iosize); 235 return (UNCONF); 236 } 237