1 /* $NetBSD: wbsio.c,v 1.10 2016/06/01 02:37:47 pgoyette 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 #define WBSIO_ID_NCT6776F 0xc3 57 58 /* Logical Device Number (LDN) Assignments */ 59 #define WBSIO_LDN_HM 0x0b 60 61 /* Hardware Monitor Control Registers (LDN B) */ 62 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 63 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 64 65 struct wbsio_softc { 66 device_t sc_dev; 67 device_t sc_lm_dev; 68 69 bus_space_tag_t sc_iot; 70 bus_space_handle_t sc_ioh; 71 72 struct isa_attach_args sc_ia; 73 struct isa_io sc_io; 74 }; 75 76 int wbsio_probe(device_t, cfdata_t, void *); 77 void wbsio_attach(device_t, device_t, void *); 78 int wbsio_detach(device_t, int); 79 int wbsio_rescan(device_t, const char *, const int *); 80 void wbsio_childdet(device_t, device_t); 81 int wbsio_print(void *, const char *); 82 83 static int wbsio_search(device_t, cfdata_t, const int *, void *); 84 85 CFATTACH_DECL2_NEW(wbsio, sizeof(struct wbsio_softc), 86 wbsio_probe, wbsio_attach, wbsio_detach, NULL, 87 wbsio_rescan, wbsio_childdet); 88 89 static __inline void 90 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 91 { 92 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 93 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 94 } 95 96 static __inline void 97 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 98 { 99 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 100 } 101 102 static __inline uint8_t 103 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, uint8_t index) 104 { 105 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 106 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 107 } 108 109 static __inline void 110 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, uint8_t index, 111 uint8_t data) 112 { 113 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 114 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 115 } 116 117 int 118 wbsio_probe(device_t parent, cfdata_t match, void *aux) 119 { 120 struct isa_attach_args *ia = aux; 121 bus_space_tag_t iot; 122 bus_space_handle_t ioh; 123 uint8_t reg; 124 125 /* Must supply an address */ 126 if (ia->ia_nio < 1) 127 return 0; 128 129 if (ISA_DIRECT_CONFIG(ia)) 130 return 0; 131 132 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 133 return 0; 134 135 /* Match by device ID */ 136 iot = ia->ia_iot; 137 if (bus_space_map(iot, ia->ia_io[0].ir_addr, WBSIO_IOSIZE, 0, &ioh)) 138 return 0; 139 wbsio_conf_enable(iot, ioh); 140 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 141 aprint_debug("wbsio_probe: id 0x%02x\n", reg); 142 wbsio_conf_disable(iot, ioh); 143 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 144 switch (reg) { 145 case WBSIO_ID_W83627HF: 146 case WBSIO_ID_W83627THF: 147 case WBSIO_ID_W83627EHF: 148 case WBSIO_ID_W83627DHG: 149 case WBSIO_ID_W83637HF: 150 case WBSIO_ID_W83697HF: 151 case WBSIO_ID_NCT6776F: 152 ia->ia_nio = 1; 153 ia->ia_io[0].ir_size = WBSIO_IOSIZE; 154 ia->ia_niomem = 0; 155 ia->ia_nirq = 0; 156 ia->ia_ndrq = 0; 157 return 1; 158 } 159 160 return 0; 161 } 162 163 void 164 wbsio_attach(device_t parent, device_t self, void *aux) 165 { 166 struct wbsio_softc *sc = device_private(self); 167 struct isa_attach_args *ia = aux; 168 const char *desc = NULL; 169 const char *vendor = "Winbond"; 170 uint8_t reg; 171 172 sc->sc_dev = self; 173 174 sc->sc_ia = *ia; 175 176 /* Map ISA I/O space */ 177 sc->sc_iot = ia->ia_iot; 178 if (bus_space_map(sc->sc_iot, ia->ia_io[0].ir_addr, 179 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 180 aprint_error(": can't map i/o space\n"); 181 return; 182 } 183 184 /* Enter configuration mode */ 185 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 186 187 /* Read device ID */ 188 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 189 switch (reg) { 190 case WBSIO_ID_W83627HF: 191 desc = "W83627HF"; 192 break; 193 case WBSIO_ID_W83627THF: 194 desc = "W83627THF"; 195 break; 196 case WBSIO_ID_W83627EHF: 197 desc = "W83627EHF"; 198 break; 199 case WBSIO_ID_W83627DHG: 200 desc = "W83627DHG"; 201 break; 202 case WBSIO_ID_W83637HF: 203 desc = "W83637HF"; 204 break; 205 case WBSIO_ID_W83667HG: 206 desc = "W83667HG"; 207 break; 208 case WBSIO_ID_W83697HF: 209 desc = "W83697HF"; 210 break; 211 case WBSIO_ID_NCT6776F: 212 vendor = "Nuvoton"; 213 desc = "NCT6776F"; 214 break; 215 } 216 /* Read device revision */ 217 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 218 219 aprint_naive("\n"); 220 aprint_normal(": %s LPC Super I/O %s rev 0x%02x\n", vendor, desc, reg); 221 222 /* Escape from configuration mode */ 223 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 224 225 if (!pmf_device_register(self, NULL, NULL)) 226 aprint_error_dev(self, "couldn't establish power handler\n"); 227 wbsio_rescan(self, "wbsio", NULL); 228 } 229 230 int 231 wbsio_detach(device_t self, int flags) 232 { 233 struct wbsio_softc *sc = device_private(self); 234 int rc; 235 236 if ((rc = config_detach_children(self, flags)) != 0) 237 return rc; 238 bus_space_unmap(sc->sc_iot, sc->sc_ioh, WBSIO_IOSIZE); 239 pmf_device_deregister(self); 240 return 0; 241 } 242 243 int 244 wbsio_rescan(device_t self, const char *ifattr, const int *locators) 245 { 246 247 config_search_loc(wbsio_search, self, ifattr, locators, NULL); 248 249 return 0; 250 } 251 252 void 253 wbsio_childdet(device_t self, device_t child) 254 { 255 struct wbsio_softc *sc = device_private(self); 256 257 if (sc->sc_lm_dev == child) 258 sc->sc_lm_dev = NULL; 259 } 260 261 static int 262 wbsio_search(device_t parent, cfdata_t cf, const int *slocs, void *aux) 263 { 264 struct wbsio_softc *sc = device_private(parent); 265 uint16_t iobase; 266 uint8_t reg0, reg1, devid; 267 268 /* Enter configuration mode */ 269 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 270 271 /* Select HM logical device */ 272 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 273 274 /* 275 * The address should be 8-byte aligned, but it seems some 276 * BIOSes ignore this. They get away with it, because 277 * Apparently the hardware simply ignores the lower three 278 * bits. We do the same here. 279 */ 280 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 281 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 282 283 /* Escape from configuration mode */ 284 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 285 286 iobase = (reg1 << 8) | (reg0 & ~0x7); 287 288 if (iobase == 0) 289 return -1; 290 291 /* Enter configuration mode */ 292 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 293 /* Read device ID */ 294 devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 295 /* Escape from configuration mode */ 296 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 297 298 sc->sc_ia.ia_nio = 1; 299 sc->sc_ia.ia_io = &sc->sc_io; 300 sc->sc_ia.ia_io[0].ir_addr = iobase; 301 sc->sc_ia.ia_io[0].ir_size = 8; 302 sc->sc_ia.ia_niomem = 0; 303 sc->sc_ia.ia_nirq = 0; 304 sc->sc_ia.ia_ndrq = 0; 305 /* Store device-id to ia_aux */ 306 sc->sc_ia.ia_aux = (void *)(uintptr_t)devid; 307 sc->sc_lm_dev = config_attach(parent, cf, &sc->sc_ia, wbsio_print); 308 309 return 0; 310 } 311 312 int 313 wbsio_print(void *aux, const char *pnp) 314 { 315 struct isa_attach_args *ia = aux; 316 317 if (pnp) 318 aprint_normal("%s", pnp); 319 if (ia->ia_io[0].ir_size) 320 aprint_normal(" port 0x%x", ia->ia_io[0].ir_addr); 321 if (ia->ia_io[0].ir_size > 1) 322 aprint_normal("-0x%x", ia->ia_io[0].ir_addr + 323 ia->ia_io[0].ir_size - 1); 324 return (UNCONF); 325 } 326 327 MODULE(MODULE_CLASS_DRIVER, wbsio, NULL); 328 329 #ifdef _MODULE 330 #include "ioconf.c" 331 #endif 332 333 static int 334 wbsio_modcmd(modcmd_t cmd, void *opaque) 335 { 336 switch (cmd) { 337 case MODULE_CMD_INIT: 338 #ifdef _MODULE 339 return config_init_component(cfdriver_ioconf_wbsio, 340 cfattach_ioconf_wbsio, cfdata_ioconf_wbsio); 341 #else 342 return 0; 343 #endif 344 case MODULE_CMD_FINI: 345 #ifdef _MODULE 346 return config_fini_component(cfdriver_ioconf_wbsio, 347 cfattach_ioconf_wbsio, cfdata_ioconf_wbsio); 348 #else 349 return 0; 350 #endif 351 default: 352 return ENOTTY; 353 } 354 } 355