1 /* $NetBSD: wbsio.c,v 1.2 2010/03/19 02:17:41 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 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Winbond LPC Super I/O driver. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/device.h> 25 #include <sys/kernel.h> 26 #include <sys/systm.h> 27 28 #include <machine/bus.h> 29 30 #include <dev/isa/isareg.h> 31 #include <dev/isa/isavar.h> 32 33 /* ISA bus registers */ 34 #define WBSIO_INDEX 0x00 /* Configuration Index Register */ 35 #define WBSIO_DATA 0x01 /* Configuration Data Register */ 36 37 #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */ 38 39 #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */ 40 #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */ 41 42 /* Configuration Space Registers */ 43 #define WBSIO_LDN 0x07 /* Logical Device Number */ 44 #define WBSIO_ID 0x20 /* Device ID */ 45 #define WBSIO_REV 0x21 /* Device Revision */ 46 47 #define WBSIO_ID_W83627HF 0x52 48 #define WBSIO_ID_W83627THF 0x82 49 #define WBSIO_ID_W83627EHF 0x88 50 #define WBSIO_ID_W83627DHG 0xa0 51 #define WBSIO_ID_W83627SF 0x59 52 #define WBSIO_ID_W83637HF 0x70 53 #define WBSIO_ID_W83667HG 0xa5 54 #define WBSIO_ID_W83697HF 0x60 55 56 /* Logical Device Number (LDN) Assignments */ 57 #define WBSIO_LDN_HM 0x0b 58 59 /* Hardware Monitor Control Registers (LDN B) */ 60 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */ 61 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */ 62 63 struct wbsio_softc { 64 struct device sc_dev; 65 66 bus_space_tag_t sc_iot; 67 bus_space_handle_t sc_ioh; 68 }; 69 70 int wbsio_probe(device_t, cfdata_t, void *); 71 void wbsio_attach(device_t, device_t, void *); 72 int wbsio_print(void *, const char *); 73 74 CFATTACH_DECL_NEW(wbsio, sizeof(struct wbsio_softc), 75 wbsio_probe, wbsio_attach, NULL, NULL); 76 77 static __inline void 78 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 79 { 80 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 81 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 82 } 83 84 static __inline void 85 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 86 { 87 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 88 } 89 90 static __inline u_int8_t 91 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 92 { 93 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 94 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 95 } 96 97 static __inline void 98 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 99 u_int8_t data) 100 { 101 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 102 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 103 } 104 105 int 106 wbsio_probe(device_t parent, cfdata_t match, void *aux) 107 { 108 struct isa_attach_args *ia = aux; 109 bus_space_tag_t iot; 110 bus_space_handle_t ioh; 111 u_int8_t reg; 112 113 /* Must supply an address */ 114 if (ia->ia_nio < 1) 115 return 0; 116 117 if (ISA_DIRECT_CONFIG(ia)) 118 return 0; 119 120 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 121 return 0; 122 123 /* Match by device ID */ 124 iot = ia->ia_iot; 125 if (bus_space_map(iot, ia->ia_io[0].ir_addr, WBSIO_IOSIZE, 0, &ioh)) 126 return 0; 127 wbsio_conf_enable(iot, ioh); 128 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 129 aprint_debug("wbsio_probe: id 0x%02x\n", reg); 130 wbsio_conf_disable(iot, ioh); 131 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 132 switch (reg) { 133 case WBSIO_ID_W83627HF: 134 case WBSIO_ID_W83627THF: 135 case WBSIO_ID_W83627EHF: 136 case WBSIO_ID_W83627DHG: 137 case WBSIO_ID_W83637HF: 138 case WBSIO_ID_W83697HF: 139 ia->ia_nio = 1; 140 ia->ia_io[0].ir_size = WBSIO_IOSIZE; 141 ia->ia_niomem = 0; 142 ia->ia_nirq = 0; 143 ia->ia_ndrq = 0; 144 return 1; 145 } 146 147 return 0; 148 } 149 150 void 151 wbsio_attach(device_t parent, device_t self, void *aux) 152 { 153 struct wbsio_softc *sc = (void *)self; 154 struct isa_attach_args *ia = aux; 155 struct isa_attach_args nia; 156 const char *desc = NULL; 157 u_int8_t reg, reg0, reg1; 158 u_int16_t iobase; 159 160 /* Map ISA I/O space */ 161 sc->sc_iot = ia->ia_iot; 162 if (bus_space_map(sc->sc_iot, ia->ia_io[0].ir_addr, 163 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 164 aprint_error(": can't map i/o space\n"); 165 return; 166 } 167 168 /* Enter configuration mode */ 169 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 170 171 /* Read device ID */ 172 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 173 switch (reg) { 174 case WBSIO_ID_W83627HF: 175 desc = "W83627HF"; 176 break; 177 case WBSIO_ID_W83627THF: 178 desc = "W83627THF"; 179 break; 180 case WBSIO_ID_W83627EHF: 181 desc = "W83627EHF"; 182 break; 183 case WBSIO_ID_W83627DHG: 184 desc = "W83627DHG"; 185 break; 186 case WBSIO_ID_W83637HF: 187 desc = "W83637HF"; 188 break; 189 case WBSIO_ID_W83667HG: 190 desc = "W83667HG"; 191 break; 192 case WBSIO_ID_W83697HF: 193 desc = "W83697HF"; 194 break; 195 } 196 197 /* Read device revision */ 198 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 199 200 aprint_naive("\n"); 201 aprint_normal(": Winbond LPC Super I/O %s rev 0x%02x\n", desc, 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 /* Escape from configuration mode */ 217 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 218 219 if (iobase == 0) 220 return; 221 222 nia = *ia; 223 nia.ia_io[0].ir_addr = iobase; 224 config_found(self, &nia, wbsio_print); 225 } 226 227 int 228 wbsio_print(void *aux, const char *pnp) 229 { 230 struct isa_attach_args *ia = aux; 231 232 if (pnp) 233 aprint_normal("%s", pnp); 234 if (ia->ia_io[0].ir_size) 235 aprint_normal(" port 0x%x", ia->ia_io[0].ir_addr); 236 if (ia->ia_io[0].ir_size > 1) 237 aprint_normal("-0x%x", ia->ia_io[0].ir_addr + 238 ia->ia_io[0].ir_size - 1); 239 return (UNCONF); 240 } 241