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