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