1 /* $NetBSD: wbsio.c,v 1.1 2010/02/21 05:16:29 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_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 struct wbsio_softc { 63 struct device sc_dev; 64 65 bus_space_tag_t sc_iot; 66 bus_space_handle_t sc_ioh; 67 }; 68 69 int wbsio_probe(device_t, cfdata_t, void *); 70 void wbsio_attach(device_t, device_t, void *); 71 int wbsio_print(void *, const char *); 72 73 CFATTACH_DECL_NEW(wbsio, sizeof(struct wbsio_softc), 74 wbsio_probe, wbsio_attach, NULL, NULL); 75 76 static __inline void 77 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh) 78 { 79 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 80 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC); 81 } 82 83 static __inline void 84 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh) 85 { 86 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC); 87 } 88 89 static __inline u_int8_t 90 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index) 91 { 92 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 93 return (bus_space_read_1(iot, ioh, WBSIO_DATA)); 94 } 95 96 static __inline void 97 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index, 98 u_int8_t data) 99 { 100 bus_space_write_1(iot, ioh, WBSIO_INDEX, index); 101 bus_space_write_1(iot, ioh, WBSIO_DATA, data); 102 } 103 104 int 105 wbsio_probe(device_t parent, cfdata_t match, void *aux) 106 { 107 struct isa_attach_args *ia = aux; 108 bus_space_tag_t iot; 109 bus_space_handle_t ioh; 110 u_int8_t reg; 111 112 /* Must supply an address */ 113 if (ia->ia_nio < 1) 114 return 0; 115 116 if (ISA_DIRECT_CONFIG(ia)) 117 return 0; 118 119 if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT) 120 return 0; 121 122 /* Match by device ID */ 123 iot = ia->ia_iot; 124 if (bus_space_map(iot, ia->ia_io[0].ir_addr, WBSIO_IOSIZE, 0, &ioh)) 125 return 0; 126 wbsio_conf_enable(iot, ioh); 127 reg = wbsio_conf_read(iot, ioh, WBSIO_ID); 128 aprint_debug("wbsio_probe: id 0x%02x\n", reg); 129 wbsio_conf_disable(iot, ioh); 130 bus_space_unmap(iot, ioh, WBSIO_IOSIZE); 131 switch (reg) { 132 case WBSIO_ID_W83627HF: 133 case WBSIO_ID_W83627THF: 134 case WBSIO_ID_W83627EHF: 135 case WBSIO_ID_W83627DHG: 136 case WBSIO_ID_W83637HF: 137 case WBSIO_ID_W83697HF: 138 ia->ia_nio = 1; 139 ia->ia_io[0].ir_size = WBSIO_IOSIZE; 140 ia->ia_niomem = 0; 141 ia->ia_nirq = 0; 142 ia->ia_ndrq = 0; 143 return 1; 144 } 145 146 return 0; 147 } 148 149 void 150 wbsio_attach(device_t parent, device_t self, void *aux) 151 { 152 struct wbsio_softc *sc = (void *)self; 153 struct isa_attach_args *ia = aux; 154 struct isa_attach_args nia; 155 const char *desc = NULL; 156 u_int8_t reg, reg0, reg1; 157 u_int16_t iobase; 158 159 /* Map ISA I/O space */ 160 sc->sc_iot = ia->ia_iot; 161 if (bus_space_map(sc->sc_iot, ia->ia_io[0].ir_addr, 162 WBSIO_IOSIZE, 0, &sc->sc_ioh)) { 163 aprint_error(": can't map i/o space\n"); 164 return; 165 } 166 167 /* Enter configuration mode */ 168 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh); 169 170 /* Read device ID */ 171 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID); 172 switch (reg) { 173 case WBSIO_ID_W83627HF: 174 desc = "W83627HF"; 175 break; 176 case WBSIO_ID_W83627THF: 177 desc = "W83627THF"; 178 break; 179 case WBSIO_ID_W83627EHF: 180 desc = "W83627EHF"; 181 break; 182 case WBSIO_ID_W83627DHG: 183 desc = "W83627DHG"; 184 break; 185 case WBSIO_ID_W83637HF: 186 desc = "W83637HF"; 187 break; 188 case WBSIO_ID_W83697HF: 189 desc = "W83697HF"; 190 break; 191 } 192 193 /* Read device revision */ 194 reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV); 195 196 aprint_naive("\n"); 197 aprint_normal(": Winbond LPC Super I/O %s rev 0x%02x\n", desc, reg); 198 199 /* Select HM logical device */ 200 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM); 201 202 /* 203 * The address should be 8-byte aligned, but it seems some 204 * BIOSes ignore this. They get away with it, because 205 * Apparently the hardware simply ignores the lower three 206 * bits. We do the same here. 207 */ 208 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB); 209 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB); 210 iobase = (reg1 << 8) | (reg0 & ~0x7); 211 212 /* Escape from configuration mode */ 213 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh); 214 215 if (iobase == 0) 216 return; 217 218 nia = *ia; 219 nia.ia_io[0].ir_addr = iobase; 220 config_found(self, &nia, wbsio_print); 221 } 222 223 int 224 wbsio_print(void *aux, const char *pnp) 225 { 226 struct isa_attach_args *ia = aux; 227 228 if (pnp) 229 aprint_normal("%s", pnp); 230 if (ia->ia_io[0].ir_size) 231 aprint_normal(" port 0x%x", ia->ia_io[0].ir_addr); 232 if (ia->ia_io[0].ir_size > 1) 233 aprint_normal("-0x%x", ia->ia_io[0].ir_addr + 234 ia->ia_io[0].ir_size - 1); 235 return (UNCONF); 236 } 237