1 /* $NetBSD: wbsio.c,v 1.1 2010/02/21 05:16:29 cnst 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 * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * Winbond LPC Super I/O driver. 22 */ 23 24 #include <sys/param.h> 25 #include <sys/bus.h> 26 #include <sys/kernel.h> 27 #include <sys/module.h> 28 #include <sys/rman.h> 29 #include <sys/systm.h> 30 31 #include <bus/isa/isavar.h> 32 33 /* ISA bus registers */ 34 #define WBSIO_INDEX 0x00 /* Configuration Index Register */ 35 #define WBSIO_DATA 0x01 /* Configuration Data Register */ 36 37 #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */ 38 39 #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */ 40 #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */ 41 42 /* Configuration Space Registers */ 43 #define WBSIO_LDN 0x07 /* Logical Device Number */ 44 #define WBSIO_ID 0x20 /* Device ID */ 45 #define WBSIO_REV 0x21 /* Device Revision */ 46 47 #define WBSIO_ID_W83627HF 0x52 48 #define WBSIO_ID_W83627THF 0x82 49 #define WBSIO_ID_W83627EHF 0x88 50 #define WBSIO_ID_W83627DHG 0xa0 51 #define WBSIO_ID_W83627DHGP 0xb0 52 #define WBSIO_ID_W83627SF 0x59 53 #define WBSIO_ID_W83627UHG 0xa2 54 #define WBSIO_ID_W83637HF 0x70 55 #define WBSIO_ID_W83667HG 0xa5 56 #define WBSIO_ID_W83687THF 0x85 57 #define WBSIO_ID_W83697HF 0x60 58 59 /* Logical Device Number (LDN) Assignments */ 60 #define WBSIO_LDN_HM 0x0b 61 62 /* Hardware Monitor Control Registers (LDN B) */ 63 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 64 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 65 66 struct wbsio_softc { 67 struct device *sc_dev; 68 69 struct resource *sc_iores; 70 int sc_iorid; 71 72 bus_space_tag_t sc_iot; 73 bus_space_handle_t sc_ioh; 74 }; 75 76 static void wbsio_identify(driver_t *, struct device *); 77 static int wbsio_probe(struct device *); 78 static int wbsio_attach(struct device *); 79 static int wbsio_detach(struct device *); 80 81 static device_method_t wbsio_methods[] = { 82 DEVMETHOD(device_identify, wbsio_identify), 83 DEVMETHOD(device_probe, wbsio_probe), 84 DEVMETHOD(device_attach, wbsio_attach), 85 DEVMETHOD(device_detach, wbsio_detach), 86 87 { NULL, NULL} 88 }; 89 90 static driver_t wbsio_driver = { 91 "wbsio", 92 wbsio_methods, 93 sizeof(struct wbsio_softc) 94 }; 95 96 static devclass_t wbsio_devclass; 97 98 DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL); 99 100 101 static __inline void 102 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 103 { 104 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 105 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 106 } 107 108 static __inline void 109 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 110 { 111 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 112 } 113 114 static __inline u_int8_t 115 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 116 { 117 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 118 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 119 } 120 121 static __inline void 122 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 123 u_int8_t data) 124 { 125 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 126 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 127 } 128 129 static void 130 wbsio_identify(driver_t *driver, struct device *parent) 131 { 132 #ifdef KLD_MODULE 133 struct device *child[2]; 134 const int port[2] = { 0x2e, 0x4e }; 135 136 for (int i = 0; i < 2; i++) { 137 child[i] = device_find_child(parent, driver->name, i); 138 if (child[i] == NULL) { 139 child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 140 driver->name, i); 141 if (child[i] == NULL) { 142 kprintf("%s: cannot add child[%i]\n", 143 __func__, i); 144 continue; 145 } 146 } else 147 continue; 148 if (bus_set_resource(child[i], SYS_RES_IOPORT, 0, 149 port[i], WBSIO_IOSIZE)) 150 kprintf("%s: cannot set resource for child[%i]\n", 151 __func__, i); 152 } 153 #endif 154 } 155 156 static int 157 wbsio_probe(struct device *dev) 158 { 159 struct resource *iores; 160 int iorid = 0; 161 bus_space_tag_t iot; 162 bus_space_handle_t ioh; 163 uint8_t reg_id, reg_rev; 164 const char *desc = NULL; 165 char fulldesc[64]; 166 167 /* Match by device ID */ 168 169 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 170 0ul, ~0ul, WBSIO_IOSIZE, 171 RF_ACTIVE); 172 if (iores == NULL) 173 return ENXIO; 174 iot = rman_get_bustag(iores); 175 ioh = rman_get_bushandle(iores); 176 177 wbsio_conf_enable(iot, ioh); 178 /* Read device ID */ 179 reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID); 180 /* Read device revision */ 181 reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV); 182 wbsio_conf_disable(iot, ioh); 183 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 184 185 switch (reg_id) { 186 case WBSIO_ID_W83627HF: 187 desc = "W83627HF"; 188 break; 189 case WBSIO_ID_W83627THF: 190 desc = "W83627THF"; 191 break; 192 case WBSIO_ID_W83627EHF: 193 desc = "W83627EHF"; 194 break; 195 case WBSIO_ID_W83627DHG: 196 desc = "W83627DHG"; 197 break; 198 case WBSIO_ID_W83627DHGP: 199 desc = "W83627DHG-P"; 200 break; 201 case WBSIO_ID_W83627UHG: 202 desc = "W83627UHG"; 203 break; 204 case WBSIO_ID_W83637HF: 205 desc = "W83637HF"; 206 break; 207 case WBSIO_ID_W83667HG: 208 desc = "W83667HG"; 209 break; 210 case WBSIO_ID_W83687THF: 211 desc = "W83687THF"; 212 break; 213 case WBSIO_ID_W83697HF: 214 desc = "W83697HF"; 215 break; 216 } 217 218 if (desc == NULL) { 219 #ifndef KLD_MODULE 220 if (bootverbose) 221 #endif 222 if (!(reg_id == 0xff && reg_rev == 0xff)) 223 device_printf(dev, "%s port 0x%02x: " 224 "Device ID 0x%02x, Rev 0x%02x\n", 225 __func__, isa_get_port(dev), 226 reg_id, reg_rev); 227 return ENXIO; 228 } 229 230 ksnprintf(fulldesc, sizeof(fulldesc), 231 "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev); 232 device_set_desc_copy(dev, fulldesc); 233 return 0; 234 } 235 236 static int 237 wbsio_attach(struct device *dev) 238 { 239 struct wbsio_softc *sc = device_get_softc(dev); 240 uint8_t reg0, reg1; 241 uint16_t iobase; 242 struct device *parent = device_get_parent(dev); 243 struct device *child; 244 struct devclass *c_dc; 245 int c_maxunit; 246 247 /* Map ISA I/O space */ 248 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 249 0ul, ~0ul, WBSIO_IOSIZE, 250 RF_ACTIVE); 251 if (sc->sc_iores == NULL) { 252 device_printf(dev, "can't map i/o space\n"); 253 return ENXIO; 254 } 255 sc->sc_iot = rman_get_bustag(sc->sc_iores); 256 sc->sc_ioh = rman_get_bushandle(sc->sc_iores); 257 258 /* Enter configuration mode */ 259 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 260 261 /* Select HM logical device */ 262 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 263 264 /* 265 * The address should be 8-byte aligned, but it seems some 266 * BIOSes ignore this. They get away with it, because 267 * Apparently the hardware simply ignores the lower three 268 * bits. We do the same here. 269 */ 270 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 271 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 272 iobase = (reg1 << 8) | (reg0 & ~0x7); 273 device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase); 274 275 /* Escape from configuration mode */ 276 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 277 278 if (iobase == 0) { 279 device_printf(dev, "no hardware monitor configured\n"); 280 return 0; 281 } 282 283 child = NULL; 284 c_dc = devclass_find("lm"); 285 if (c_dc == NULL) { 286 device_printf(dev, "lm devclass not found\n"); 287 return ENXIO; 288 } 289 c_maxunit = devclass_get_maxunit(c_dc); 290 for (int u = 0; u < c_maxunit; u++) { 291 child = devclass_get_device(c_dc, u); 292 if (child == NULL) 293 continue; 294 if (isa_get_port(child) == iobase) { 295 if (device_is_attached(child)) { 296 device_printf(dev, 297 "%s is already attached at 0x%x\n", 298 device_get_nameunit(child), iobase); 299 return 0; 300 } 301 break; 302 } 303 if (device_is_attached(child)) { 304 child = NULL; 305 continue; 306 } 307 device_printf(dev, 308 "found unused %s at 0x%x with state %i, reusing at 0x%x\n", 309 device_get_nameunit(child), isa_get_port(child), 310 device_get_state(child), iobase); 311 break; 312 } 313 if (child == NULL) 314 child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 315 "lm", -1); 316 // child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 317 // "lm", 3 + device_get_unit(dev)); 318 if (child == NULL) { 319 device_printf(dev, "cannot add child\n"); 320 return ENXIO; 321 } 322 if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8)) { 323 device_printf(dev, "cannot set resource\n"); 324 return ENXIO; 325 } 326 return device_probe_and_attach(child); 327 } 328 329 static int 330 wbsio_detach(struct device *dev) 331 { 332 struct wbsio_softc *sc = device_get_softc(dev); 333 334 return bus_release_resource(dev, SYS_RES_IOPORT, 335 sc->sc_iorid, sc->sc_iores); 336 } 337