xref: /dflybsd-src/sys/dev/powermng/wbsio/wbsio.c (revision fa130c0230f70153612043d6fbb47d9cf50af78f)
1a5261280SConstantine A. Murenin /*	$NetBSD: wbsio.c,v 1.1 2010/02/21 05:16:29 cnst Exp $	*/
2a5261280SConstantine A. Murenin /*	$OpenBSD: wbsio.c,v 1.5 2009/03/29 21:53:52 sthen Exp $	*/
3a5261280SConstantine A. Murenin /*
4a5261280SConstantine A. Murenin  * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org>
5f81520edSConstantine A. Murenin  * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org>
6a5261280SConstantine A. Murenin  *
7a5261280SConstantine A. Murenin  * Permission to use, copy, modify, and distribute this software for any
8a5261280SConstantine A. Murenin  * purpose with or without fee is hereby granted, provided that the above
9a5261280SConstantine A. Murenin  * copyright notice and this permission notice appear in all copies.
10a5261280SConstantine A. Murenin  *
11a5261280SConstantine A. Murenin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a5261280SConstantine A. Murenin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a5261280SConstantine A. Murenin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14a5261280SConstantine A. Murenin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a5261280SConstantine A. Murenin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a5261280SConstantine A. Murenin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a5261280SConstantine A. Murenin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18a5261280SConstantine A. Murenin  */
19a5261280SConstantine A. Murenin 
20a5261280SConstantine A. Murenin /*
21a5261280SConstantine A. Murenin  * Winbond LPC Super I/O driver.
22a5261280SConstantine A. Murenin  */
23a5261280SConstantine A. Murenin 
24a5261280SConstantine A. Murenin #include <sys/param.h>
25f81520edSConstantine A. Murenin #include <sys/bus.h>
26a5261280SConstantine A. Murenin #include <sys/kernel.h>
27f81520edSConstantine A. Murenin #include <sys/module.h>
28f81520edSConstantine A. Murenin #include <sys/rman.h>
29a5261280SConstantine A. Murenin #include <sys/systm.h>
30a5261280SConstantine A. Murenin 
31f81520edSConstantine A. Murenin #include <bus/isa/isavar.h>
32a5261280SConstantine A. Murenin 
33a5261280SConstantine A. Murenin /* ISA bus registers */
34a5261280SConstantine A. Murenin #define WBSIO_INDEX		0x00	/* Configuration Index Register */
35a5261280SConstantine A. Murenin #define WBSIO_DATA		0x01	/* Configuration Data Register */
36a5261280SConstantine A. Murenin 
37a5261280SConstantine A. Murenin #define WBSIO_IOSIZE		0x02	/* ISA I/O space size */
38a5261280SConstantine A. Murenin 
39a5261280SConstantine A. Murenin #define WBSIO_CONF_EN_MAGIC	0x87	/* enable configuration mode */
40a5261280SConstantine A. Murenin #define WBSIO_CONF_DS_MAGIC	0xaa	/* disable configuration mode */
41a5261280SConstantine A. Murenin 
42a5261280SConstantine A. Murenin /* Configuration Space Registers */
43a5261280SConstantine A. Murenin #define WBSIO_LDN		0x07	/* Logical Device Number */
44a5261280SConstantine A. Murenin #define WBSIO_ID		0x20	/* Device ID */
45a5261280SConstantine A. Murenin #define WBSIO_REV		0x21	/* Device Revision */
46a5261280SConstantine A. Murenin 
47a5261280SConstantine A. Murenin #define WBSIO_ID_W83627HF	0x52
48a5261280SConstantine A. Murenin #define WBSIO_ID_W83627THF	0x82
49a5261280SConstantine A. Murenin #define WBSIO_ID_W83627EHF	0x88
50a5261280SConstantine A. Murenin #define WBSIO_ID_W83627DHG	0xa0
51a5261280SConstantine A. Murenin #define WBSIO_ID_W83627SF	0x59
52a5261280SConstantine A. Murenin #define WBSIO_ID_W83637HF	0x70
531c486d4bSConstantine A. Murenin #define WBSIO_ID_W83667HG	0xa5
54a5261280SConstantine A. Murenin #define WBSIO_ID_W83697HF	0x60
55a5261280SConstantine A. Murenin 
56a5261280SConstantine A. Murenin /* Logical Device Number (LDN) Assignments */
57a5261280SConstantine A. Murenin #define WBSIO_LDN_HM		0x0b
58a5261280SConstantine A. Murenin 
59a5261280SConstantine A. Murenin /* Hardware Monitor Control Registers (LDN B) */
60a5261280SConstantine A. Murenin #define WBSIO_HM_ADDR_MSB	0x60	/* Address [15:8] */
61a5261280SConstantine A. Murenin #define WBSIO_HM_ADDR_LSB	0x61	/* Address [7:0] */
62a5261280SConstantine A. Murenin 
63a5261280SConstantine A. Murenin struct wbsio_softc {
64f81520edSConstantine A. Murenin 	struct device		*sc_dev;
65f81520edSConstantine A. Murenin 
66f81520edSConstantine A. Murenin 	struct resource		*sc_iores;
67f81520edSConstantine A. Murenin 	int			sc_iorid;
68a5261280SConstantine A. Murenin 
69a5261280SConstantine A. Murenin 	bus_space_tag_t		sc_iot;
70a5261280SConstantine A. Murenin 	bus_space_handle_t	sc_ioh;
71a5261280SConstantine A. Murenin };
72a5261280SConstantine A. Murenin 
73*fa130c02SConstantine A. Murenin static void	wbsio_identify(driver_t *, struct device *);
74f81520edSConstantine A. Murenin static int	wbsio_probe(struct device *);
75f81520edSConstantine A. Murenin static int	wbsio_attach(struct device *);
76f81520edSConstantine A. Murenin static int	wbsio_detach(struct device *);
77a5261280SConstantine A. Murenin 
78f81520edSConstantine A. Murenin static device_method_t wbsio_methods[] = {
79*fa130c02SConstantine A. Murenin 	DEVMETHOD(device_identify,	wbsio_identify),
80f81520edSConstantine A. Murenin 	DEVMETHOD(device_probe,		wbsio_probe),
81f81520edSConstantine A. Murenin 	DEVMETHOD(device_attach, 	wbsio_attach),
82f81520edSConstantine A. Murenin 	DEVMETHOD(device_detach,	wbsio_detach),
83f81520edSConstantine A. Murenin 
84f81520edSConstantine A. Murenin 	{ NULL, NULL}
85f81520edSConstantine A. Murenin };
86f81520edSConstantine A. Murenin 
87f81520edSConstantine A. Murenin static driver_t wbsio_driver = {
88f81520edSConstantine A. Murenin 	"wbsio",
89f81520edSConstantine A. Murenin 	wbsio_methods,
90f81520edSConstantine A. Murenin 	sizeof(struct wbsio_softc)
91f81520edSConstantine A. Murenin };
92f81520edSConstantine A. Murenin 
93f81520edSConstantine A. Murenin static devclass_t wbsio_devclass;
94f81520edSConstantine A. Murenin 
95f81520edSConstantine A. Murenin DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL);
96f81520edSConstantine A. Murenin 
97a5261280SConstantine A. Murenin 
98a5261280SConstantine A. Murenin static __inline void
99a5261280SConstantine A. Murenin wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
100a5261280SConstantine A. Murenin {
101a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
102a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
103a5261280SConstantine A. Murenin }
104a5261280SConstantine A. Murenin 
105a5261280SConstantine A. Murenin static __inline void
106a5261280SConstantine A. Murenin wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
107a5261280SConstantine A. Murenin {
108a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
109a5261280SConstantine A. Murenin }
110a5261280SConstantine A. Murenin 
111a5261280SConstantine A. Murenin static __inline u_int8_t
112a5261280SConstantine A. Murenin wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
113a5261280SConstantine A. Murenin {
114a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
115a5261280SConstantine A. Murenin 	return (bus_space_read_1(iot, ioh, WBSIO_DATA));
116a5261280SConstantine A. Murenin }
117a5261280SConstantine A. Murenin 
118a5261280SConstantine A. Murenin static __inline void
119a5261280SConstantine A. Murenin wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
120a5261280SConstantine A. Murenin     u_int8_t data)
121a5261280SConstantine A. Murenin {
122a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
123a5261280SConstantine A. Murenin 	bus_space_write_1(iot, ioh, WBSIO_DATA, data);
124a5261280SConstantine A. Murenin }
125a5261280SConstantine A. Murenin 
126*fa130c02SConstantine A. Murenin static void
127*fa130c02SConstantine A. Murenin wbsio_identify(driver_t *driver, struct device *parent)
128*fa130c02SConstantine A. Murenin {
129*fa130c02SConstantine A. Murenin #ifdef KLD_MODULE
130*fa130c02SConstantine A. Murenin 	struct device *child[2];
131*fa130c02SConstantine A. Murenin 	const int port[2] = { 0x2e, 0x4e };
132*fa130c02SConstantine A. Murenin 
133*fa130c02SConstantine A. Murenin 	for (int i = 0; i < 2; i++) {
134*fa130c02SConstantine A. Murenin 		child[i] = device_find_child(parent, driver->name, i);
135*fa130c02SConstantine A. Murenin 		if (child[i] == NULL) {
136*fa130c02SConstantine A. Murenin 			child[i] = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
137*fa130c02SConstantine A. Murenin 			    driver->name, i);
138*fa130c02SConstantine A. Murenin 			if (child[i] == NULL) {
139*fa130c02SConstantine A. Murenin 				kprintf("%s: cannot add child[%i]\n",
140*fa130c02SConstantine A. Murenin 				    __func__, i);
141*fa130c02SConstantine A. Murenin 				continue;
142*fa130c02SConstantine A. Murenin 			}
143*fa130c02SConstantine A. Murenin 		} else
144*fa130c02SConstantine A. Murenin 			continue;
145*fa130c02SConstantine A. Murenin 		if (bus_set_resource(child[i], SYS_RES_IOPORT, 0,
146*fa130c02SConstantine A. Murenin 			port[i], WBSIO_IOSIZE))
147*fa130c02SConstantine A. Murenin 			kprintf("%s: cannot set resource for child[%i]\n",
148*fa130c02SConstantine A. Murenin 			    __func__, i);
149*fa130c02SConstantine A. Murenin 	}
150*fa130c02SConstantine A. Murenin #endif
151*fa130c02SConstantine A. Murenin }
152*fa130c02SConstantine A. Murenin 
153f81520edSConstantine A. Murenin static int
154f81520edSConstantine A. Murenin wbsio_probe(struct device *dev)
155a5261280SConstantine A. Murenin {
156f81520edSConstantine A. Murenin 	struct resource *iores;
157f81520edSConstantine A. Murenin 	int iorid = 0;
158a5261280SConstantine A. Murenin 	bus_space_tag_t iot;
159a5261280SConstantine A. Murenin 	bus_space_handle_t ioh;
160f81520edSConstantine A. Murenin 	uint8_t reg_id, reg_rev;
161f81520edSConstantine A. Murenin 	const char *desc = NULL;
162f81520edSConstantine A. Murenin 	char fulldesc[64];
163a5261280SConstantine A. Murenin 
164a5261280SConstantine A. Murenin 	/* Match by device ID */
165f81520edSConstantine A. Murenin 
166f81520edSConstantine A. Murenin 	iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
167f81520edSConstantine A. Murenin 	    0ul, ~0ul, WBSIO_IOSIZE,
168f81520edSConstantine A. Murenin 	    RF_ACTIVE);
169f81520edSConstantine A. Murenin 	if (iores == NULL)
170f81520edSConstantine A. Murenin 		return ENXIO;
171f81520edSConstantine A. Murenin 	iot = rman_get_bustag(iores);
172f81520edSConstantine A. Murenin 	ioh = rman_get_bushandle(iores);
173f81520edSConstantine A. Murenin 
174a5261280SConstantine A. Murenin 	wbsio_conf_enable(iot, ioh);
175a5261280SConstantine A. Murenin 	/* Read device ID */
176f81520edSConstantine A. Murenin 	reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID);
177f81520edSConstantine A. Murenin 	/* Read device revision */
178f81520edSConstantine A. Murenin 	reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV);
179f81520edSConstantine A. Murenin 	wbsio_conf_disable(iot, ioh);
180f81520edSConstantine A. Murenin 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
181f81520edSConstantine A. Murenin 
182f81520edSConstantine A. Murenin 	switch (reg_id) {
183a5261280SConstantine A. Murenin 	case WBSIO_ID_W83627HF:
184a5261280SConstantine A. Murenin 		desc = "W83627HF";
185a5261280SConstantine A. Murenin 		break;
186a5261280SConstantine A. Murenin 	case WBSIO_ID_W83627THF:
187a5261280SConstantine A. Murenin 		desc = "W83627THF";
188a5261280SConstantine A. Murenin 		break;
189a5261280SConstantine A. Murenin 	case WBSIO_ID_W83627EHF:
190a5261280SConstantine A. Murenin 		desc = "W83627EHF";
191a5261280SConstantine A. Murenin 		break;
192a5261280SConstantine A. Murenin 	case WBSIO_ID_W83627DHG:
193a5261280SConstantine A. Murenin 		desc = "W83627DHG";
194a5261280SConstantine A. Murenin 		break;
195a5261280SConstantine A. Murenin 	case WBSIO_ID_W83637HF:
196a5261280SConstantine A. Murenin 		desc = "W83637HF";
197a5261280SConstantine A. Murenin 		break;
1981c486d4bSConstantine A. Murenin 	case WBSIO_ID_W83667HG:
1991c486d4bSConstantine A. Murenin 		desc = "W83667HG";
2001c486d4bSConstantine A. Murenin 		break;
201a5261280SConstantine A. Murenin 	case WBSIO_ID_W83697HF:
202a5261280SConstantine A. Murenin 		desc = "W83697HF";
203a5261280SConstantine A. Murenin 		break;
204a5261280SConstantine A. Murenin 	}
205a5261280SConstantine A. Murenin 
206f81520edSConstantine A. Murenin 	if (desc == NULL)
207f81520edSConstantine A. Murenin 		return ENXIO;
208a5261280SConstantine A. Murenin 
209f81520edSConstantine A. Murenin 	ksnprintf(fulldesc, sizeof(fulldesc),
210f81520edSConstantine A. Murenin 	    "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev);
211f81520edSConstantine A. Murenin 	device_set_desc_copy(dev, fulldesc);
212f81520edSConstantine A. Murenin 	return 0;
213f81520edSConstantine A. Murenin }
214f81520edSConstantine A. Murenin 
215f81520edSConstantine A. Murenin static int
216f81520edSConstantine A. Murenin wbsio_attach(struct device *dev)
217f81520edSConstantine A. Murenin {
218f81520edSConstantine A. Murenin 	struct wbsio_softc *sc = device_get_softc(dev);
219f81520edSConstantine A. Murenin 	uint8_t reg0, reg1;
220f81520edSConstantine A. Murenin 	uint16_t iobase;
221f81520edSConstantine A. Murenin 	struct device *parent = device_get_parent(dev);
222f81520edSConstantine A. Murenin 	struct device *child;
223f81520edSConstantine A. Murenin 	struct devclass *c_dc;
224f81520edSConstantine A. Murenin 	int c_maxunit;
225f81520edSConstantine A. Murenin 
226f81520edSConstantine A. Murenin 	/* Map ISA I/O space */
227f81520edSConstantine A. Murenin 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
228f81520edSConstantine A. Murenin 	    0ul, ~0ul, WBSIO_IOSIZE,
229f81520edSConstantine A. Murenin 	    RF_ACTIVE);
230f81520edSConstantine A. Murenin 	if (sc->sc_iores == NULL) {
231f81520edSConstantine A. Murenin 		device_printf(dev, "can't map i/o space\n");
232f81520edSConstantine A. Murenin 		return ENXIO;
233f81520edSConstantine A. Murenin 	}
234f81520edSConstantine A. Murenin 	sc->sc_iot = rman_get_bustag(sc->sc_iores);
235f81520edSConstantine A. Murenin 	sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
236f81520edSConstantine A. Murenin 
237f81520edSConstantine A. Murenin 	/* Enter configuration mode */
238f81520edSConstantine A. Murenin 	wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
239a5261280SConstantine A. Murenin 
240a5261280SConstantine A. Murenin 	/* Select HM logical device */
241a5261280SConstantine A. Murenin 	wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
242a5261280SConstantine A. Murenin 
243a5261280SConstantine A. Murenin 	/*
244a5261280SConstantine A. Murenin 	 * The address should be 8-byte aligned, but it seems some
245a5261280SConstantine A. Murenin 	 * BIOSes ignore this.  They get away with it, because
246a5261280SConstantine A. Murenin 	 * Apparently the hardware simply ignores the lower three
247a5261280SConstantine A. Murenin 	 * bits.  We do the same here.
248a5261280SConstantine A. Murenin 	 */
249a5261280SConstantine A. Murenin 	reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
250a5261280SConstantine A. Murenin 	reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
251a5261280SConstantine A. Murenin 	iobase = (reg1 << 8) | (reg0 & ~0x7);
252f81520edSConstantine A. Murenin 	device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase);
253a5261280SConstantine A. Murenin 
254a5261280SConstantine A. Murenin 	/* Escape from configuration mode */
255a5261280SConstantine A. Murenin 	wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
256a5261280SConstantine A. Murenin 
257f81520edSConstantine A. Murenin 	if (iobase == 0) {
258f81520edSConstantine A. Murenin 		device_printf(dev, "no hardware monitor configured\n");
259f81520edSConstantine A. Murenin 		return 0;
260a5261280SConstantine A. Murenin 	}
261a5261280SConstantine A. Murenin 
262f81520edSConstantine A. Murenin 	child = NULL;
263f81520edSConstantine A. Murenin 	c_dc = devclass_find("lm");
264f81520edSConstantine A. Murenin 	if (c_dc == NULL) {
265f81520edSConstantine A. Murenin 		device_printf(dev, "lm devclass not found\n");
266f81520edSConstantine A. Murenin 		return ENXIO;
267f81520edSConstantine A. Murenin 	}
268f81520edSConstantine A. Murenin 	c_maxunit = devclass_get_maxunit(c_dc);
269f81520edSConstantine A. Murenin 	for (int u = 0; u < c_maxunit; u++) {
270f81520edSConstantine A. Murenin 		child = devclass_get_device(c_dc, u);
271f81520edSConstantine A. Murenin 		if (child == NULL)
272f81520edSConstantine A. Murenin 			continue;
273f81520edSConstantine A. Murenin 		if (isa_get_port(child) == iobase) {
274f81520edSConstantine A. Murenin 			if (device_is_attached(child)) {
275f81520edSConstantine A. Murenin 				device_printf(dev,
276f81520edSConstantine A. Murenin 				    "%s is already attached at 0x%x\n",
277f81520edSConstantine A. Murenin 				    device_get_nameunit(child), iobase);
278f81520edSConstantine A. Murenin 				return 0;
279f81520edSConstantine A. Murenin 			}
280f81520edSConstantine A. Murenin 			break;
281f81520edSConstantine A. Murenin 		}
282647ed4b2SConstantine A. Murenin 		if (device_is_attached(child)) {
283647ed4b2SConstantine A. Murenin 			child = NULL;
284f81520edSConstantine A. Murenin 			continue;
285647ed4b2SConstantine A. Murenin 		}
286f81520edSConstantine A. Murenin 		device_printf(dev,
287f81520edSConstantine A. Murenin 		    "found unused %s at 0x%x with state %i, reusing at 0x%x\n",
288f81520edSConstantine A. Murenin 		    device_get_nameunit(child), isa_get_port(child),
289f81520edSConstantine A. Murenin 		    device_get_state(child), iobase);
290f81520edSConstantine A. Murenin 		break;
291f81520edSConstantine A. Murenin 	}
292f81520edSConstantine A. Murenin 	if (child == NULL)
293f81520edSConstantine A. Murenin 		child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
294f81520edSConstantine A. Murenin 		    "lm", -1);
295f81520edSConstantine A. Murenin //	child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
296f81520edSConstantine A. Murenin //	    "lm", 3 + device_get_unit(dev));
297f81520edSConstantine A. Murenin 	if (child == NULL) {
298f81520edSConstantine A. Murenin 		device_printf(dev, "cannot add child\n");
299f81520edSConstantine A. Murenin 		return ENXIO;
300f81520edSConstantine A. Murenin 	}
301f81520edSConstantine A. Murenin 	if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8)) {
302f81520edSConstantine A. Murenin 		device_printf(dev, "cannot set resource\n");
303f81520edSConstantine A. Murenin 		return ENXIO;
304f81520edSConstantine A. Murenin 	}
305f81520edSConstantine A. Murenin 	return device_probe_and_attach(child);
306f81520edSConstantine A. Murenin }
307a5261280SConstantine A. Murenin 
308f81520edSConstantine A. Murenin static int
309f81520edSConstantine A. Murenin wbsio_detach(struct device *dev)
310f81520edSConstantine A. Murenin {
311f81520edSConstantine A. Murenin 	struct wbsio_softc *sc = device_get_softc(dev);
312f81520edSConstantine A. Murenin 
313f81520edSConstantine A. Murenin 	return bus_release_resource(dev, SYS_RES_IOPORT,
314f81520edSConstantine A. Murenin 	    sc->sc_iorid, sc->sc_iores);
315a5261280SConstantine A. Murenin }
316