xref: /dragonfly/sys/dev/powermng/lm/lm78_isa.c (revision 71990c18)
1 /*
2  * Copyright (c) 2005, 2006 Mark Kettenis
3  * Copyright (c) 2007 Constantine A. Murenin, Google Summer of Code
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  * $OpenBSD: lm78_isa.c,v 1.2 2007/07/01 21:48:57 cnst Exp $
18  */
19 
20 #include <sys/param.h>
21 #include <sys/kernel.h>
22 #include <sys/bus.h>
23 #include <sys/module.h>
24 #include <sys/rman.h>
25 
26 #include <bus/isa/isavar.h>
27 
28 #include <sys/systm.h>
29 
30 #include <sys/sensors.h>
31 
32 #include "lm78var.h"
33 #include "../wbsio/wbsioreg.h"
34 #include "../wbsio/wbsiovar.h"
35 
36 /* ISA registers */
37 #define LMC_ADDR	0x05
38 #define LMC_DATA	0x06
39 
40 #if defined(LMDEBUG)
41 #define DPRINTF(x)		do { kprintf x; } while (0)
42 #else
43 #define DPRINTF(x)
44 #endif
45 
46 struct lm_isa_softc {
47 	struct lm_softc sc_lmsc;
48 
49 	struct resource *sc_iores;
50 	int sc_iorid;
51 	bus_space_tag_t sc_iot;
52 	bus_space_handle_t sc_ioh;
53 };
54 
55 static int lm_isa_probe(device_t);
56 static int lm_isa_attach(device_t);
57 static int lm_isa_detach(device_t);
58 u_int8_t lm_isa_readreg(struct lm_softc *, int);
59 void lm_isa_writereg(struct lm_softc *, int, int);
60 
61 static device_method_t lm_isa_methods[] = {
62 	/* Methods from the device interface */
63 	DEVMETHOD(device_probe,         lm_isa_probe),
64 	DEVMETHOD(device_attach,        lm_isa_attach),
65 	DEVMETHOD(device_detach,        lm_isa_detach),
66 
67 	/* Terminate method list */
68 	DEVMETHOD_END
69 };
70 
71 static driver_t lm_isa_driver = {
72 	"lm",
73 	lm_isa_methods,
74 	sizeof (struct lm_isa_softc)
75 };
76 
77 static devclass_t lm_devclass;
78 
79 DRIVER_MODULE(lm, isa, lm_isa_driver, lm_devclass, NULL, NULL);
80 DRIVER_MODULE(lm, wbsio, lm_isa_driver, lm_devclass, NULL, NULL);
81 
82 static int
83 lm_isa_probe(device_t dev)
84 {
85 	struct lm_isa_softc *sc = device_get_softc(dev);
86 	struct wbsio_softc *wbsc = NULL;
87 	struct resource *iores;
88 	struct devclass *parent_devclass;
89 	const char *parent_name;
90 	int iorid = 0;
91 	bus_space_tag_t iot;
92 	bus_space_handle_t ioh;
93 	int banksel, vendid, chipid, addr;
94 
95 	parent_devclass = device_get_devclass(device_get_parent(dev));
96 	parent_name = devclass_get_name(parent_devclass);
97 	if (strcmp("wbsio", parent_name) == 0) {
98 		wbsc = device_get_softc(device_get_parent(dev));
99 		sc->sc_lmsc.sioid = wbsc->sc_devid;
100 	} else {
101 		sc->sc_lmsc.sioid = 0;
102 	}
103 
104 	iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
105 	    0ul, ~0ul, 8, RF_ACTIVE);
106 	if (iores == NULL) {
107 		DPRINTF(("%s: can't map i/o space\n", __func__));
108 		return (1);
109 	}
110 	iot = rman_get_bustag(iores);
111 	ioh = rman_get_bushandle(iores);
112 
113 	/* Probe for Winbond chips. */
114 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
115 	banksel = bus_space_read_1(iot, ioh, LMC_DATA);
116 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
117 	bus_space_write_1(iot, ioh, LMC_DATA, WB_BANKSEL_HBAC);
118 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID);
119 	vendid = bus_space_read_1(iot, ioh, LMC_DATA) << 8;
120 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
121 	bus_space_write_1(iot, ioh, LMC_DATA, 0);
122 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID);
123 	vendid |= bus_space_read_1(iot, ioh, LMC_DATA);
124 	bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
125 	bus_space_write_1(iot, ioh, LMC_DATA, banksel);
126 	if (vendid == WB_VENDID_WINBOND)
127 		goto found;
128 
129 	/* Probe for ITE chips (and don't attach if we find one). */
130 	bus_space_write_1(iot, ioh, LMC_ADDR, 0x58 /*ITD_CHIPID*/);
131 	vendid = bus_space_read_1(iot, ioh, LMC_DATA);
132 	if (vendid == 0x90 /*IT_ID_IT87*/)
133 		goto notfound;
134 
135 	/*
136 	 * Probe for National Semiconductor LM78/79/81.
137 	 *
138 	 * XXX This assumes the address has not been changed from the
139 	 * power up default.  This is probably a reasonable
140 	 * assumption, and if it isn't true, we should be able to
141 	 * access the chip using the serial bus.
142 	 */
143 	bus_space_write_1(iot, ioh, LMC_ADDR, LM_SBUSADDR);
144 	addr = bus_space_read_1(iot, ioh, LMC_DATA);
145 	if ((addr & 0xfc) == 0x2c) {
146 		bus_space_write_1(iot, ioh, LMC_ADDR, LM_CHIPID);
147 		chipid = bus_space_read_1(iot, ioh, LMC_DATA);
148 
149 		switch (chipid & LM_CHIPID_MASK) {
150 		case LM_CHIPID_LM78:
151 		case LM_CHIPID_LM78J:
152 		case LM_CHIPID_LM79:
153 		case LM_CHIPID_LM81:
154 			goto found;
155 		}
156 	}
157 
158  notfound:
159 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
160 
161 	return (1);
162 
163  found:
164 	/* Bus-independent probe */
165 	sc->sc_lmsc.sc_dev = dev;
166 	sc->sc_iot = iot;
167 	sc->sc_ioh = ioh;
168 	sc->sc_lmsc.lm_writereg = lm_isa_writereg;
169 	sc->sc_lmsc.lm_readreg = lm_isa_readreg;
170 	lm_probe(&sc->sc_lmsc);
171 
172 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
173 	sc->sc_iot = 0;
174 	sc->sc_ioh = 0;
175 
176 	return (0);
177 }
178 
179 static int
180 lm_isa_attach(device_t dev)
181 {
182 	struct lm_isa_softc *sc = device_get_softc(dev);
183 #ifdef notyet
184 	struct lm_softc *lmsc;
185 	int i;
186 	u_int8_t sbusaddr;
187 #endif
188 
189 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
190 	    0ul, ~0ul, 8, RF_ACTIVE);
191 	if (sc->sc_iores == NULL) {
192 		device_printf(dev, "can't map i/o space\n");
193 		return (1);
194 	}
195 	sc->sc_iot = rman_get_bustag(sc->sc_iores);
196 	sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
197 
198 	/* Bus-independent attachment */
199 	lm_attach(&sc->sc_lmsc);
200 
201 #ifdef notyet
202 	/*
203 	 * Most devices supported by this driver can attach to iic(4)
204 	 * as well.  However, we prefer to attach them to isa(4) since
205 	 * that causes less overhead and is more reliable.  We look
206 	 * through all previously attached devices, and if we find an
207 	 * identical chip at the same serial bus address, we stop
208 	 * updating its sensors and mark them as invalid.
209 	 */
210 
211 	sbusaddr = lm_isa_readreg(&sc->sc_lmsc, LM_SBUSADDR);
212 	if (sbusaddr == 0)
213 		return (0);
214 
215 	for (i = 0; i < lm_cd.cd_ndevs; i++) {
216 		lmsc = lm_cd.cd_devs[i];
217 		if (lmsc == &sc->sc_lmsc)
218 			continue;
219 		if (lmsc && lmsc->sbusaddr == sbusaddr &&
220 		    lmsc->chipid == sc->sc_lmsc.chipid)
221 			config_detach(&lmsc->sc_dev, 0);
222 	}
223 #endif
224 	return (0);
225 }
226 
227 static int
228 lm_isa_detach(device_t dev)
229 {
230 	struct lm_isa_softc *sc = device_get_softc(dev);
231 	int error;
232 
233 	/* Bus-independent detachment */
234 	error = lm_detach(&sc->sc_lmsc);
235 	if (error)
236 		return (error);
237 
238 	error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
239 	    sc->sc_iores);
240 	if (error)
241 		return (error);
242 
243 	return (0);
244 }
245 
246 u_int8_t
247 lm_isa_readreg(struct lm_softc *lmsc, int reg)
248 {
249 	struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc;
250 
251 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg);
252 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, LMC_DATA));
253 }
254 
255 void
256 lm_isa_writereg(struct lm_softc *lmsc, int reg, int val)
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 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_DATA, val);
262 }
263