1 /* $OpenBSD: lm78_isa.c,v 1.7 2011/12/06 16:06:07 mpf Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 Mark Kettenis 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 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 #include <sys/workq.h> 24 #include <machine/bus.h> 25 26 #include <dev/isa/isareg.h> 27 #include <dev/isa/isavar.h> 28 29 #include <dev/ic/lm78var.h> 30 31 /* ISA registers */ 32 #define LMC_ADDR 0x05 33 #define LMC_DATA 0x06 34 35 extern struct cfdriver lm_cd; 36 37 #if defined(LMDEBUG) 38 #define DPRINTF(x) do { printf x; } while (0) 39 #else 40 #define DPRINTF(x) 41 #endif 42 43 struct lm_isa_softc { 44 struct lm_softc sc_lmsc; 45 46 bus_space_tag_t sc_iot; 47 bus_space_handle_t sc_ioh; 48 }; 49 50 int lm_isa_match(struct device *, void *, void *); 51 int lm_wbsio_match(struct device *, void *, void *); 52 void lm_isa_attach(struct device *, struct device *, void *); 53 u_int8_t lm_isa_readreg(struct lm_softc *, int); 54 void lm_isa_writereg(struct lm_softc *, int, int); 55 void lm_isa_remove_alias(void *, void *); 56 57 struct cfattach lm_isa_ca = { 58 sizeof(struct lm_isa_softc), 59 lm_isa_match, 60 lm_isa_attach 61 }; 62 63 struct cfattach lm_wbsio_ca = { 64 sizeof(struct lm_isa_softc), 65 lm_wbsio_match, 66 lm_isa_attach 67 }; 68 69 int 70 lm_wbsio_match(struct device *parent, void *match, void *aux) 71 { 72 bus_space_tag_t iot; 73 bus_addr_t iobase; 74 bus_space_handle_t ioh; 75 struct isa_attach_args *ia = aux; 76 int banksel, vendid; 77 78 iot = ia->ia_iot; 79 iobase = ia->ipa_io[0].base; 80 81 if (bus_space_map(iot, iobase, 8, 0, &ioh)) { 82 DPRINTF(("%s: can't map i/o space\n", __func__)); 83 return (0); 84 } 85 86 /* Probe for Winbond chips. */ 87 bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL); 88 banksel = bus_space_read_1(iot, ioh, LMC_DATA); 89 bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL); 90 bus_space_write_1(iot, ioh, LMC_DATA, WB_BANKSEL_HBAC); 91 bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID); 92 vendid = bus_space_read_1(iot, ioh, LMC_DATA) << 8; 93 bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL); 94 bus_space_write_1(iot, ioh, LMC_DATA, 0); 95 bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID); 96 vendid |= bus_space_read_1(iot, ioh, LMC_DATA); 97 bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL); 98 bus_space_write_1(iot, ioh, LMC_DATA, banksel); 99 100 bus_space_unmap(iot, ioh, 8); 101 102 if (vendid != WB_VENDID_WINBOND) 103 return (0); 104 105 ia->ipa_nio = 1; 106 ia->ipa_io[0].length = 8; 107 108 ia->ipa_nmem = 0; 109 ia->ipa_nirq = 0; 110 ia->ipa_ndrq = 0; 111 112 return (1); 113 114 } 115 116 int 117 lm_isa_match(struct device *parent, void *match, void *aux) 118 { 119 bus_space_tag_t iot; 120 bus_addr_t iobase; 121 bus_space_handle_t ioh; 122 struct isa_attach_args *ia = aux; 123 int banksel, vendid, chipid, addr; 124 125 iot = ia->ia_iot; 126 iobase = ia->ipa_io[0].base; 127 128 if (bus_space_map(iot, iobase, 8, 0, &ioh)) { 129 DPRINTF(("%s: can't map i/o space\n", __func__)); 130 return (0); 131 } 132 133 /* Probe for Winbond chips. */ 134 bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL); 135 banksel = bus_space_read_1(iot, ioh, LMC_DATA); 136 bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID); 137 vendid = bus_space_read_1(iot, ioh, LMC_DATA); 138 if (((banksel & 0x80) && vendid == (WB_VENDID_WINBOND >> 8)) || 139 (!(banksel & 0x80) && vendid == (WB_VENDID_WINBOND & 0xff))) 140 goto found; 141 142 /* Probe for ITE chips (and don't attach if we find one). */ 143 bus_space_write_1(iot, ioh, LMC_ADDR, 0x58); 144 if ((vendid = bus_space_read_1(iot, ioh, LMC_DATA)) == 0x90) 145 goto notfound; 146 147 /* 148 * Probe for National Semiconductor LM78/79/81. 149 * 150 * XXX This assumes the address has not been changed from the 151 * power up default. This is probably a reasonable 152 * assumption, and if it isn't true, we should be able to 153 * access the chip using the serial bus. 154 */ 155 bus_space_write_1(iot, ioh, LMC_ADDR, LM_SBUSADDR); 156 addr = bus_space_read_1(iot, ioh, LMC_DATA); 157 if ((addr & 0xfc) == 0x2c) { 158 bus_space_write_1(iot, ioh, LMC_ADDR, LM_CHIPID); 159 chipid = bus_space_read_1(iot, ioh, LMC_DATA); 160 161 switch (chipid & LM_CHIPID_MASK) { 162 case LM_CHIPID_LM78: 163 case LM_CHIPID_LM78J: 164 case LM_CHIPID_LM79: 165 case LM_CHIPID_LM81: 166 goto found; 167 } 168 } 169 170 notfound: 171 bus_space_unmap(iot, ioh, 8); 172 173 return (0); 174 175 found: 176 bus_space_unmap(iot, ioh, 8); 177 178 ia->ipa_nio = 1; 179 ia->ipa_io[0].length = 8; 180 181 ia->ipa_nmem = 0; 182 ia->ipa_nirq = 0; 183 ia->ipa_ndrq = 0; 184 185 return (1); 186 } 187 188 void 189 lm_isa_attach(struct device *parent, struct device *self, void *aux) 190 { 191 struct lm_isa_softc *sc = (struct lm_isa_softc *)self; 192 struct isa_attach_args *ia = aux; 193 struct lm_softc *lmsc; 194 bus_addr_t iobase; 195 int i; 196 u_int8_t sbusaddr; 197 198 sc->sc_iot = ia->ia_iot; 199 iobase = ia->ipa_io[0].base; 200 201 if (bus_space_map(sc->sc_iot, iobase, 8, 0, &sc->sc_ioh)) { 202 printf(": can't map i/o space\n"); 203 return; 204 } 205 206 /* Bus-independant attachment */ 207 sc->sc_lmsc.lm_writereg = lm_isa_writereg; 208 sc->sc_lmsc.lm_readreg = lm_isa_readreg; 209 210 /* pass through wbsio(4) devid */ 211 if (ia->ia_aux) 212 sc->sc_lmsc.sioid = (u_int8_t)(u_long)ia->ia_aux; 213 214 lm_attach(&sc->sc_lmsc); 215 216 /* 217 * Most devices supported by this driver can attach to iic(4) 218 * as well. However, we prefer to attach them to isa(4) since 219 * that causes less overhead and is more reliable. We look 220 * through all previously attached devices, and if we find an 221 * identical chip at the same serial bus address, we stop 222 * updating its sensors and mark them as invalid. 223 */ 224 225 sbusaddr = lm_isa_readreg(&sc->sc_lmsc, LM_SBUSADDR); 226 if (sbusaddr == 0) 227 return; 228 229 for (i = 0; i < lm_cd.cd_ndevs; i++) { 230 lmsc = lm_cd.cd_devs[i]; 231 if (lmsc == &sc->sc_lmsc) 232 continue; 233 if (lmsc && lmsc->sbusaddr == sbusaddr && 234 lmsc->chipid == sc->sc_lmsc.chipid) 235 workq_add_task(NULL, 0, lm_isa_remove_alias, lmsc, 236 sc->sc_lmsc.sc_dev.dv_xname); 237 } 238 } 239 240 /* Remove sensors of the i2c alias, since we prefer to use the isa access */ 241 void 242 lm_isa_remove_alias(void *v, void *arg) 243 { 244 struct lm_softc *sc = v; 245 char *iic = arg; 246 int i; 247 248 printf("%s: disabling sensors due to alias with %s\n", 249 sc->sc_dev.dv_xname, iic); 250 sensordev_deinstall(&sc->sensordev); 251 for (i = 0; i < sc->numsensors; i++) 252 sensor_detach(&sc->sensordev, &sc->sensors[i]); 253 if (sc->sensortask != NULL) 254 sensor_task_unregister(sc->sensortask); 255 sc->sensortask = NULL; 256 } 257 258 u_int8_t 259 lm_isa_readreg(struct lm_softc *lmsc, int reg) 260 { 261 struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc; 262 263 bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg); 264 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, LMC_DATA)); 265 } 266 267 void 268 lm_isa_writereg(struct lm_softc *lmsc, int reg, int val) 269 { 270 struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc; 271 272 bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg); 273 bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_DATA, val); 274 } 275