1 /* $NetBSD: wbsio.c,v 1.9 2012/01/18 00:23:30 jakllsch Exp $ */ 2 /* $OpenBSD: wbsio.c,v 1.5 2009/03/29 21:53:52 sthen Exp $ */ 3 /* 4 * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Winbond LPC Super I/O driver. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/device.h> 25 #include <sys/kernel.h> 26 #include <sys/module.h> 27 #include <sys/systm.h> 28 29 #include <sys/bus.h> 30 31 #include <dev/isa/isareg.h> 32 #include <dev/isa/isavar.h> 33 34 /* ISA bus registers */ 35 #define WBSIO_INDEX 0x00 /* Configuration Index Register */ 36 #define WBSIO_DATA 0x01 /* Configuration Data Register */ 37 38 #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */ 39 40 #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */ 41 #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */ 42 43 /* Configuration Space Registers */ 44 #define WBSIO_LDN 0x07 /* Logical Device Number */ 45 #define WBSIO_ID 0x20 /* Device ID */ 46 #define WBSIO_REV 0x21 /* Device Revision */ 47 48 #define WBSIO_ID_W83627HF 0x52 49 #define WBSIO_ID_W83627THF 0x82 50 #define WBSIO_ID_W83627EHF 0x88 51 #define WBSIO_ID_W83627DHG 0xa0 52 #define WBSIO_ID_W83627SF 0x59 53 #define WBSIO_ID_W83637HF 0x70 54 #define WBSIO_ID_W83667HG 0xa5 55 #define WBSIO_ID_W83697HF 0x60 56 57 /* Logical Device Number (LDN) Assignments */ 58 #define WBSIO_LDN_HM 0x0b 59 60 /* Hardware Monitor Control Registers (LDN B) */ 61 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 62 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 63 64 struct wbsio_softc { 65 device_t sc_dev; 66 device_t sc_lm_dev; 67 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_ioh; 70 71 struct isa_attach_args sc_ia; 72 struct isa_io sc_io; 73 }; 74 75 int wbsio_probe(device_t, cfdata_t, void *); 76 void wbsio_attach(device_t, device_t, void *); 77 int wbsio_detach(device_t, int); 78 int wbsio_rescan(device_t, const char *, const int *); 79 void wbsio_childdet(device_t, device_t); 80 int wbsio_print(void *, const char *); 81 82 static int wbsio_search(device_t, cfdata_t, const int *, void *); 83 84 CFATTACH_DECL2_NEW(wbsio, sizeof(struct wbsio_softc), 85 wbsio_probe, wbsio_attach, wbsio_detach, NULL, 86 wbsio_rescan, wbsio_childdet); 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 uint8_t 102 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, uint8_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, uint8_t index, 110 uint8_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(device_t parent, cfdata_t match, void *aux) 118 { 119 struct isa_attach_args *ia = aux; 120 bus_space_tag_t iot; 121 bus_space_handle_t ioh; 122 uint8_t reg; 123 124 /* Must supply an address */ 125 if (ia->ia_nio < 1) 126 return 0; 127 128 if (ISA_DIRECT_CONFIG(ia)) 129 return 0; 130 131 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 132 return 0; 133 134 /* Match by device ID */ 135 iot = ia->ia_iot; 136 if (bus_space_map(iot, ia->ia_io[0].ir_addr, WBSIO_IOSIZE, 0, &ioh)) 137 return 0; 138 wbsio_conf_enable(iot, ioh); 139 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 140 aprint_debug("wbsio_probe: id 0x%02x\n", reg); 141 wbsio_conf_disable(iot, ioh); 142 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 143 switch (reg) { 144 case WBSIO_ID_W83627HF: 145 case WBSIO_ID_W83627THF: 146 case WBSIO_ID_W83627EHF: 147 case WBSIO_ID_W83627DHG: 148 case WBSIO_ID_W83637HF: 149 case WBSIO_ID_W83697HF: 150 ia->ia_nio = 1; 151 ia->ia_io[0].ir_size = WBSIO_IOSIZE; 152 ia->ia_niomem = 0; 153 ia->ia_nirq = 0; 154 ia->ia_ndrq = 0; 155 return 1; 156 } 157 158 return 0; 159 } 160 161 void 162 wbsio_attach(device_t parent, device_t self, void *aux) 163 { 164 struct wbsio_softc *sc = device_private(self); 165 struct isa_attach_args *ia = aux; 166 const char *desc = NULL; 167 uint8_t reg; 168 169 sc->sc_dev = self; 170 171 sc->sc_ia = *ia; 172 173 /* Map ISA I/O space */ 174 sc->sc_iot = ia->ia_iot; 175 if (bus_space_map(sc->sc_iot, ia->ia_io[0].ir_addr, 176 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 177 aprint_error(": can't map i/o space\n"); 178 return; 179 } 180 181 /* Enter configuration mode */ 182 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 183 184 /* Read device ID */ 185 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 186 switch (reg) { 187 case WBSIO_ID_W83627HF: 188 desc = "W83627HF"; 189 break; 190 case WBSIO_ID_W83627THF: 191 desc = "W83627THF"; 192 break; 193 case WBSIO_ID_W83627EHF: 194 desc = "W83627EHF"; 195 break; 196 case WBSIO_ID_W83627DHG: 197 desc = "W83627DHG"; 198 break; 199 case WBSIO_ID_W83637HF: 200 desc = "W83637HF"; 201 break; 202 case WBSIO_ID_W83667HG: 203 desc = "W83667HG"; 204 break; 205 case WBSIO_ID_W83697HF: 206 desc = "W83697HF"; 207 break; 208 } 209 210 /* Read device revision */ 211 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 212 213 aprint_naive("\n"); 214 aprint_normal(": Winbond LPC Super I/O %s rev 0x%02x\n", desc, reg); 215 216 /* Escape from configuration mode */ 217 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 218 219 if (!pmf_device_register(self, NULL, NULL)) 220 aprint_error_dev(self, "couldn't establish power handler\n"); 221 222 wbsio_rescan(self, "wbsio", NULL); 223 } 224 225 int 226 wbsio_detach(device_t self, int flags) 227 { 228 struct wbsio_softc *sc = device_private(self); 229 int rc; 230 231 if ((rc = config_detach_children(self, flags)) != 0) 232 return rc; 233 bus_space_unmap(sc->sc_iot, sc->sc_ioh, WBSIO_IOSIZE); 234 pmf_device_deregister(self); 235 return 0; 236 } 237 238 int 239 wbsio_rescan(device_t self, const char *ifattr, const int *locators) 240 { 241 242 config_search_loc(wbsio_search, self, ifattr, locators, NULL); 243 244 return 0; 245 } 246 247 void 248 wbsio_childdet(device_t self, device_t child) 249 { 250 struct wbsio_softc *sc = device_private(self); 251 252 if (sc->sc_lm_dev == child) 253 sc->sc_lm_dev = NULL; 254 } 255 256 static int 257 wbsio_search(device_t parent, cfdata_t cf, const int *slocs, void *aux) 258 { 259 struct wbsio_softc *sc = device_private(parent); 260 uint16_t iobase; 261 uint8_t reg0, reg1; 262 263 /* Enter configuration mode */ 264 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 265 266 /* Select HM logical device */ 267 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 268 269 /* 270 * The address should be 8-byte aligned, but it seems some 271 * BIOSes ignore this. They get away with it, because 272 * Apparently the hardware simply ignores the lower three 273 * bits. We do the same here. 274 */ 275 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 276 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 277 278 /* Escape from configuration mode */ 279 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 280 281 iobase = (reg1 << 8) | (reg0 & ~0x7); 282 283 if (iobase == 0) 284 return -1; 285 286 sc->sc_ia.ia_nio = 1; 287 sc->sc_ia.ia_io = &sc->sc_io; 288 sc->sc_ia.ia_io[0].ir_addr = iobase; 289 sc->sc_ia.ia_io[0].ir_size = 8; 290 sc->sc_ia.ia_niomem = 0; 291 sc->sc_ia.ia_nirq = 0; 292 sc->sc_ia.ia_ndrq = 0; 293 sc->sc_lm_dev = config_attach(parent, cf, &sc->sc_ia, wbsio_print); 294 295 return 0; 296 } 297 298 int 299 wbsio_print(void *aux, const char *pnp) 300 { 301 struct isa_attach_args *ia = aux; 302 303 if (pnp) 304 aprint_normal("%s", pnp); 305 if (ia->ia_io[0].ir_size) 306 aprint_normal(" port 0x%x", ia->ia_io[0].ir_addr); 307 if (ia->ia_io[0].ir_size > 1) 308 aprint_normal("-0x%x", ia->ia_io[0].ir_addr + 309 ia->ia_io[0].ir_size - 1); 310 return (UNCONF); 311 } 312 313 MODULE(MODULE_CLASS_DRIVER, wbsio, NULL); 314 315 #ifdef _MODULE 316 #include "ioconf.c" 317 #endif 318 319 static int 320 wbsio_modcmd(modcmd_t cmd, void *opaque) 321 { 322 switch (cmd) { 323 case MODULE_CMD_INIT: 324 #ifdef _MODULE 325 return config_init_component(cfdriver_ioconf_wbsio, 326 cfattach_ioconf_wbsio, cfdata_ioconf_wbsio); 327 #else 328 return 0; 329 #endif 330 case MODULE_CMD_FINI: 331 #ifdef _MODULE 332 return config_fini_component(cfdriver_ioconf_wbsio, 333 cfattach_ioconf_wbsio, cfdata_ioconf_wbsio); 334 #else 335 return 0; 336 #endif 337 default: 338 return ENOTTY; 339 } 340 } 341