xref: /dragonfly/sys/dev/powermng/wbsio/wbsio.c (revision 3badf6b7)
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  * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * Winbond LPC Super I/O driver.
22  */
23 
24 #include <sys/param.h>
25 #include <sys/bus.h>
26 #include <sys/kernel.h>
27 #include <sys/module.h>
28 #include <sys/rman.h>
29 #include <sys/systm.h>
30 
31 #include <bus/isa/isavar.h>
32 #include <bus/isa/isa_common.h>
33 
34 #include "wbsioreg.h"
35 #include "wbsiovar.h"
36 
37 static void	wbsio_identify(driver_t *, struct device *);
38 static int	wbsio_probe(struct device *);
39 static int	wbsio_attach(struct device *);
40 static int	wbsio_detach(struct device *);
41 
42 static device_method_t wbsio_methods[] = {
43 	/* Device interface */
44 	DEVMETHOD(device_identify,	wbsio_identify),
45 	DEVMETHOD(device_probe,		wbsio_probe),
46 	DEVMETHOD(device_attach, 	wbsio_attach),
47 	DEVMETHOD(device_detach,	wbsio_detach),
48 
49 	/* Bus interface */
50 	DEVMETHOD(bus_add_child,	bus_generic_add_child),
51 	DEVMETHOD(bus_set_resource,	bus_generic_set_resource),
52 	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
53 	DEVMETHOD(bus_release_resource,	isa_release_resource),
54 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
55 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
56 
57 	DEVMETHOD_END
58 };
59 
60 static driver_t wbsio_driver = {
61 	"wbsio",
62 	wbsio_methods,
63 	sizeof(struct wbsio_softc)
64 };
65 
66 static devclass_t wbsio_devclass;
67 
68 DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL);
69 
70 
71 static __inline void
72 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
73 {
74 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
75 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
76 }
77 
78 static __inline void
79 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
80 {
81 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
82 }
83 
84 static __inline u_int8_t
85 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
86 {
87 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
88 	return (bus_space_read_1(iot, ioh, WBSIO_DATA));
89 }
90 
91 static __inline void
92 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
93     u_int8_t data)
94 {
95 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
96 	bus_space_write_1(iot, ioh, WBSIO_DATA, data);
97 }
98 
99 static void
100 wbsio_identify(driver_t *driver, struct device *parent)
101 {
102 #ifdef KLD_MODULE
103 	struct device *child[2];
104 	const int port[2] = { 0x2e, 0x4e };
105 
106 	for (int i = 0; i < 2; i++) {
107 		child[i] = device_find_child(parent, driver->name, i);
108 		if (child[i] == NULL) {
109 			child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
110 			    driver->name, i);
111 			if (child[i] == NULL) {
112 				kprintf("%s: cannot add child[%i]\n",
113 				    __func__, i);
114 				continue;
115 			}
116 		} else
117 			continue;
118 		if (bus_set_resource(child[i], SYS_RES_IOPORT, 0,
119 			port[i], WBSIO_IOSIZE, -1))
120 			kprintf("%s: cannot set resource for child[%i]\n",
121 			    __func__, i);
122 	}
123 #endif
124 }
125 
126 static int
127 wbsio_probe(struct device *dev)
128 {
129 	struct resource *iores;
130 	int iorid = 0;
131 	bus_space_tag_t iot;
132 	bus_space_handle_t ioh;
133 	uint8_t reg_id, reg_rev;
134 	const char *desc = NULL;
135 	char fulldesc[64];
136 
137 	/* Match by device ID */
138 
139 	iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
140 	    0ul, ~0ul, WBSIO_IOSIZE,
141 	    RF_ACTIVE);
142 	if (iores == NULL)
143 		return ENXIO;
144 	iot = rman_get_bustag(iores);
145 	ioh = rman_get_bushandle(iores);
146 
147 	wbsio_conf_enable(iot, ioh);
148 	/* Read device ID */
149 	reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID);
150 	/* Read device revision */
151 	reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV);
152 	wbsio_conf_disable(iot, ioh);
153 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
154 
155 	switch (reg_id) {
156 	case WBSIO_ID_W83627HF:
157 		desc = "W83627HF";
158 		break;
159 	case WBSIO_ID_W83627THF:
160 		desc = "W83627THF";
161 		break;
162 	case WBSIO_ID_W83627EHF:
163 		desc = "W83627EHF";
164 		break;
165 	case WBSIO_ID_W83627DHG:
166 		desc = "W83627DHG";
167 		break;
168 	case WBSIO_ID_W83627DHGP:
169 		desc = "W83627DHG-P";
170 		break;
171 	case WBSIO_ID_W83627UHG:
172 		desc = "W83627UHG";
173 		break;
174 	case WBSIO_ID_W83637HF:
175 		desc = "W83637HF";
176 		break;
177 	case WBSIO_ID_W83667HG:
178 		desc = "W83667HG";
179 		break;
180 	case WBSIO_ID_W83687THF:
181 		desc = "W83687THF";
182 		break;
183 	case WBSIO_ID_W83697HF:
184 		desc = "W83697HF";
185 		break;
186 	case WBSIO_ID_NCT6776F:
187 		desc = "NCT6776F";
188 		break;
189 	}
190 
191 	if (desc == NULL) {
192 #ifndef KLD_MODULE
193 		if (bootverbose)
194 #endif
195 			if (!(reg_id == 0xff && reg_rev == 0xff))
196 				device_printf(dev, "%s port 0x%02x: "
197 				    "Device ID 0x%02x, Rev 0x%02x\n",
198 				    __func__, isa_get_port(dev),
199 				    reg_id, reg_rev);
200 		return ENXIO;
201 	}
202 
203 	ksnprintf(fulldesc, sizeof(fulldesc),
204 	    "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev);
205 	device_set_desc_copy(dev, fulldesc);
206 	return 0;
207 }
208 
209 static int
210 wbsio_attach(struct device *dev)
211 {
212 	struct wbsio_softc *sc = device_get_softc(dev);
213 	uint8_t reg0, reg1;
214 	uint16_t iobase;
215 	struct device *child;
216 
217 	/* Map ISA I/O space */
218 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
219 	    0ul, ~0ul, WBSIO_IOSIZE,
220 	    RF_ACTIVE);
221 	if (sc->sc_iores == NULL) {
222 		device_printf(dev, "can't map i/o space\n");
223 		return ENXIO;
224 	}
225 	sc->sc_iot = rman_get_bustag(sc->sc_iores);
226 	sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
227 
228 	/* Enter configuration mode */
229 	wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
230 
231 	/* Read device ID */
232 	sc->sc_devid = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID);
233 
234 	/* Select HM logical device */
235 	wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
236 
237 	/*
238 	 * The address should be 8-byte aligned, but it seems some
239 	 * BIOSes ignore this.  They get away with it, because
240 	 * Apparently the hardware simply ignores the lower three
241 	 * bits.  We do the same here.
242 	 */
243 	reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
244 	reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
245 	iobase = (reg1 << 8) | (reg0 & ~0x7);
246 	device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase);
247 
248 	/* Escape from configuration mode */
249 	wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
250 
251 	if (iobase == 0) {
252 		device_printf(dev, "no hardware monitor configured\n");
253 		return 0;
254 	}
255 
256 	child = BUS_ADD_CHILD(dev, dev, 0, "lm", -1);
257 	if (child == NULL) {
258 		device_printf(dev, "cannot add child\n");
259 		return ENXIO;
260 	}
261 	if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8, -1)) {
262 		device_printf(dev, "cannot set resource\n");
263 		return ENXIO;
264 	}
265 	return device_probe_and_attach(child);
266 }
267 
268 static int
269 wbsio_detach(struct device *dev)
270 {
271 	struct wbsio_softc *sc = device_get_softc(dev);
272 
273 	return bus_release_resource(dev, SYS_RES_IOPORT,
274 	    sc->sc_iorid, sc->sc_iores);
275 }
276