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