1 /* $OpenBSD: wbsio.c,v 1.8 2012/07/01 02:15:09 lteo 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 #include <dev/isa/wbsioreg.h> 32 33 #ifdef WBSIO_DEBUG 34 #define DPRINTF(x) printf x 35 #else 36 #define DPRINTF(x) 37 #endif 38 39 struct wbsio_softc { 40 struct device sc_dev; 41 42 bus_space_tag_t sc_iot; 43 bus_space_handle_t sc_ioh; 44 }; 45 46 int wbsio_probe(struct device *, void *, void *); 47 void wbsio_attach(struct device *, struct device *, void *); 48 int wbsio_print(void *, const char *); 49 50 struct cfattach wbsio_ca = { 51 sizeof(struct wbsio_softc), 52 wbsio_probe, 53 wbsio_attach 54 }; 55 56 struct cfdriver wbsio_cd = { 57 NULL, "wbsio", DV_DULL 58 }; 59 60 static __inline void 61 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 62 { 63 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 64 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 65 } 66 67 static __inline void 68 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 69 { 70 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 71 } 72 73 static __inline u_int8_t 74 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 75 { 76 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 77 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 78 } 79 80 static __inline void 81 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 82 u_int8_t data) 83 { 84 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 85 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 86 } 87 88 int 89 wbsio_probe(struct device *parent, void *match, void *aux) 90 { 91 struct isa_attach_args *ia = aux; 92 bus_space_tag_t iot; 93 bus_space_handle_t ioh; 94 u_int8_t reg; 95 96 /* Match by device ID */ 97 iot = ia->ia_iot; 98 if (bus_space_map(iot, ia->ipa_io[0].base, WBSIO_IOSIZE, 0, &ioh)) 99 return (0); 100 wbsio_conf_enable(iot, ioh); 101 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 102 DPRINTF(("wbsio_probe: id 0x%02x\n", reg)); 103 wbsio_conf_disable(iot, ioh); 104 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 105 switch (reg) { 106 case WBSIO_ID_W83627HF: 107 case WBSIO_ID_W83627THF: 108 case WBSIO_ID_W83627EHF: 109 case WBSIO_ID_W83627DHG: 110 case WBSIO_ID_W83627DHGP: 111 case WBSIO_ID_W83627UHG: 112 case WBSIO_ID_W83637HF: 113 case WBSIO_ID_W83697HF: 114 case WBSIO_ID_NCT6776F: 115 ia->ipa_nio = 1; 116 ia->ipa_io[0].length = WBSIO_IOSIZE; 117 ia->ipa_nmem = 0; 118 ia->ipa_nirq = 0; 119 ia->ipa_ndrq = 0; 120 return (1); 121 } 122 123 return (0); 124 } 125 126 void 127 wbsio_attach(struct device *parent, struct device *self, void *aux) 128 { 129 struct wbsio_softc *sc = (void *)self; 130 struct isa_attach_args *ia = aux; 131 struct isa_attach_args nia; 132 u_int8_t devid, reg, reg0, reg1; 133 u_int16_t iobase; 134 135 /* Map ISA I/O space */ 136 sc->sc_iot = ia->ia_iot; 137 if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, 138 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 139 printf(": can't map i/o space\n"); 140 return; 141 } 142 143 /* Enter configuration mode */ 144 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 145 146 /* Read device ID */ 147 devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 148 switch (devid) { 149 case WBSIO_ID_W83627HF: 150 printf(": W83627HF"); 151 break; 152 case WBSIO_ID_W83627THF: 153 printf(": W83627THF"); 154 break; 155 case WBSIO_ID_W83627EHF: 156 printf(": W83627EHF"); 157 break; 158 case WBSIO_ID_W83627DHG: 159 printf(": W83627DHG"); 160 break; 161 case WBSIO_ID_W83627DHGP: 162 printf(": W83627DHG-P"); 163 break; 164 case WBSIO_ID_W83627UHG: 165 printf(": W83627UHG"); 166 break; 167 case WBSIO_ID_W83637HF: 168 printf(": W83637HF"); 169 break; 170 case WBSIO_ID_W83697HF: 171 printf(": W83697HF"); 172 break; 173 case WBSIO_ID_NCT6776F: 174 printf(": NCT6776F"); 175 break; 176 } 177 178 /* Read device revision */ 179 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 180 printf(" rev 0x%02x", reg); 181 182 /* Select HM logical device */ 183 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 184 185 /* 186 * The address should be 8-byte aligned, but it seems some 187 * BIOSes ignore this. They get away with it, because 188 * Apparently the hardware simply ignores the lower three 189 * bits. We do the same here. 190 */ 191 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 192 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 193 iobase = (reg1 << 8) | (reg0 & ~0x7); 194 195 printf("\n"); 196 197 /* Escape from configuration mode */ 198 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 199 200 if (iobase == 0) 201 return; 202 203 nia = *ia; 204 nia.ia_iobase = iobase; 205 nia.ia_aux = (void *)(u_long)devid; /* pass devid down to wb_match() */ 206 207 config_found(self, &nia, wbsio_print); 208 } 209 210 int 211 wbsio_print(void *aux, const char *pnp) 212 { 213 struct isa_attach_args *ia = aux; 214 215 if (pnp) 216 printf("%s", pnp); 217 if (ia->ia_iosize) 218 printf(" port 0x%x", ia->ia_iobase); 219 if (ia->ia_iosize > 1) 220 printf("/%d", ia->ia_iosize); 221 return (UNCONF); 222 } 223