1 /* $OpenBSD: wbsio.c,v 1.10 2015/03/14 03:38:47 jsg 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_NCT6776F: 114 case WBSIO_ID_NCT5104D: 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 case WBSIO_ID_NCT5104D: 177 printf(": NCT5104D"); 178 break; 179 } 180 181 /* Read device revision */ 182 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 183 printf(" rev 0x%02x", reg); 184 185 /* Select HM logical device */ 186 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 187 188 /* 189 * The address should be 8-byte aligned, but it seems some 190 * BIOSes ignore this. They get away with it, because 191 * Apparently the hardware simply ignores the lower three 192 * bits. We do the same here. 193 */ 194 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 195 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 196 iobase = (reg1 << 8) | (reg0 & ~0x7); 197 198 printf("\n"); 199 200 /* Escape from configuration mode */ 201 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 202 203 if (iobase == 0) 204 return; 205 206 nia = *ia; 207 nia.ia_iobase = iobase; 208 nia.ia_aux = (void *)(u_long)devid; /* pass devid down to wb_match() */ 209 210 config_found(self, &nia, wbsio_print); 211 } 212 213 int 214 wbsio_print(void *aux, const char *pnp) 215 { 216 struct isa_attach_args *ia = aux; 217 218 if (pnp) 219 printf("%s", pnp); 220 if (ia->ia_iosize) 221 printf(" port 0x%x", ia->ia_iobase); 222 if (ia->ia_iosize > 1) 223 printf("/%d", ia->ia_iosize); 224 return (UNCONF); 225 } 226