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 #include <bus/isa/isa_common.h> 33 34 #include "wbsioreg.h" 35 #include "wbsiovar.h" 36 37 static void wbsio_identify(driver_t *, device_t); 38 static int wbsio_probe(device_t); 39 static int wbsio_attach(device_t); 40 static int wbsio_detach(device_t); 41 42 static device_method_t wbsio_methods[] = { 43 /* Device interface */ 44 DEVMETHOD(device_identify, wbsio_identify), 45 DEVMETHOD(device_probe, wbsio_probe), 46 DEVMETHOD(device_attach, wbsio_attach), 47 DEVMETHOD(device_detach, wbsio_detach), 48 49 /* Bus interface */ 50 DEVMETHOD(bus_add_child, bus_generic_add_child), 51 DEVMETHOD(bus_set_resource, bus_generic_set_resource), 52 DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 53 DEVMETHOD(bus_release_resource, isa_release_resource), 54 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 55 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 56 57 DEVMETHOD_END 58 }; 59 60 static driver_t wbsio_driver = { 61 "wbsio", 62 wbsio_methods, 63 sizeof(struct wbsio_softc) 64 }; 65 66 static devclass_t wbsio_devclass; 67 68 DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL); 69 70 71 static __inline void 72 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 73 { 74 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 75 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 76 } 77 78 static __inline void 79 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 80 { 81 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 82 } 83 84 static __inline u_int8_t 85 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 86 { 87 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 88 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 89 } 90 91 static __inline void 92 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 93 u_int8_t data) 94 { 95 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 96 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 97 } 98 99 static void 100 wbsio_identify(driver_t *driver, device_t parent) 101 { 102 #ifdef KLD_MODULE 103 device_t child[2]; 104 const int port[2] = { 0x2e, 0x4e }; 105 106 for (int i = 0; i < 2; i++) { 107 child[i] = device_find_child(parent, driver->name, i); 108 if (child[i] == NULL) { 109 child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, 110 driver->name, i); 111 if (child[i] == NULL) { 112 kprintf("%s: cannot add child[%i]\n", 113 __func__, i); 114 continue; 115 } 116 } else 117 continue; 118 if (bus_set_resource(child[i], SYS_RES_IOPORT, 0, 119 port[i], WBSIO_IOSIZE, -1)) 120 kprintf("%s: cannot set resource for child[%i]\n", 121 __func__, i); 122 } 123 #endif 124 } 125 126 static int 127 wbsio_probe(device_t dev) 128 { 129 struct resource *iores; 130 int iorid = 0; 131 bus_space_tag_t iot; 132 bus_space_handle_t ioh; 133 uint8_t reg_id, reg_rev; 134 const char *desc = NULL; 135 char fulldesc[64]; 136 137 /* Match by device ID */ 138 139 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid, 140 0ul, ~0ul, WBSIO_IOSIZE, 141 RF_ACTIVE); 142 if (iores == NULL) 143 return ENXIO; 144 iot = rman_get_bustag(iores); 145 ioh = rman_get_bushandle(iores); 146 147 wbsio_conf_enable(iot, ioh); 148 /* Read device ID */ 149 reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID); 150 /* Read device revision */ 151 reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV); 152 wbsio_conf_disable(iot, ioh); 153 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores); 154 155 switch (reg_id) { 156 case WBSIO_ID_W83627HF: 157 desc = "W83627HF"; 158 break; 159 case WBSIO_ID_W83627THF: 160 desc = "W83627THF"; 161 break; 162 case WBSIO_ID_W83627EHF: 163 desc = "W83627EHF"; 164 break; 165 case WBSIO_ID_W83627DHG: 166 desc = "W83627DHG"; 167 break; 168 case WBSIO_ID_W83627DHGP: 169 desc = "W83627DHG-P"; 170 break; 171 case WBSIO_ID_W83627UHG: 172 desc = "W83627UHG"; 173 break; 174 case WBSIO_ID_W83637HF: 175 desc = "W83637HF"; 176 break; 177 case WBSIO_ID_W83667HG: 178 desc = "W83667HG"; 179 break; 180 case WBSIO_ID_W83687THF: 181 desc = "W83687THF"; 182 break; 183 case WBSIO_ID_W83697HF: 184 desc = "W83697HF"; 185 break; 186 case WBSIO_ID_NCT6776F: 187 desc = "NCT6776F"; 188 break; 189 } 190 191 if (desc == NULL) { 192 #ifndef KLD_MODULE 193 if (bootverbose) 194 #endif 195 if (!(reg_id == 0xff && reg_rev == 0xff)) 196 device_printf(dev, "%s port 0x%02x: " 197 "Device ID 0x%02x, Rev 0x%02x\n", 198 __func__, isa_get_port(dev), 199 reg_id, reg_rev); 200 return ENXIO; 201 } 202 203 ksnprintf(fulldesc, sizeof(fulldesc), 204 "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev); 205 device_set_desc_copy(dev, fulldesc); 206 return 0; 207 } 208 209 static int 210 wbsio_attach(device_t dev) 211 { 212 struct wbsio_softc *sc = device_get_softc(dev); 213 uint8_t reg0, reg1; 214 uint16_t iobase; 215 device_t child; 216 217 /* Map ISA I/O space */ 218 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid, 219 0ul, ~0ul, WBSIO_IOSIZE, 220 RF_ACTIVE); 221 if (sc->sc_iores == NULL) { 222 device_printf(dev, "can't map i/o space\n"); 223 return ENXIO; 224 } 225 sc->sc_iot = rman_get_bustag(sc->sc_iores); 226 sc->sc_ioh = rman_get_bushandle(sc->sc_iores); 227 228 /* Enter configuration mode */ 229 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 230 231 /* Read device ID */ 232 sc->sc_devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 233 234 /* Select HM logical device */ 235 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 236 237 /* 238 * The address should be 8-byte aligned, but it seems some 239 * BIOSes ignore this. They get away with it, because 240 * Apparently the hardware simply ignores the lower three 241 * bits. We do the same here. 242 */ 243 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 244 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 245 iobase = (reg1 << 8) | (reg0 & ~0x7); 246 device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase); 247 248 /* Escape from configuration mode */ 249 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 250 251 if (iobase == 0) { 252 device_printf(dev, "no hardware monitor configured\n"); 253 return 0; 254 } 255 256 child = BUS_ADD_CHILD(dev, dev, 0, "lm", -1); 257 if (child == NULL) { 258 device_printf(dev, "cannot add child\n"); 259 return ENXIO; 260 } 261 if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8, -1)) { 262 device_printf(dev, "cannot set resource\n"); 263 return ENXIO; 264 } 265 return device_probe_and_attach(child); 266 } 267 268 static int 269 wbsio_detach(device_t dev) 270 { 271 struct wbsio_softc *sc = device_get_softc(dev); 272 273 return bus_release_resource(dev, SYS_RES_IOPORT, 274 sc->sc_iorid, sc->sc_iores); 275 } 276