wbsio(4): don't reuse a child that has already been attached
[dragonfly.git] / sys / dev / powermng / wbsio / wbsio.c
blob98ea7c20d0c791519ba01fa150c9af2049da5f35
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>
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.
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.
21 * Winbond LPC Super I/O driver.
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>
31 #include <bus/isa/isavar.h>
33 /* ISA bus registers */
34 #define WBSIO_INDEX 0x00 /* Configuration Index Register */
35 #define WBSIO_DATA 0x01 /* Configuration Data Register */
37 #define WBSIO_IOSIZE 0x02 /* ISA I/O space size */
39 #define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */
40 #define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */
42 /* Configuration Space Registers */
43 #define WBSIO_LDN 0x07 /* Logical Device Number */
44 #define WBSIO_ID 0x20 /* Device ID */
45 #define WBSIO_REV 0x21 /* Device Revision */
47 #define WBSIO_ID_W83627HF 0x52
48 #define WBSIO_ID_W83627THF 0x82
49 #define WBSIO_ID_W83627EHF 0x88
50 #define WBSIO_ID_W83627DHG 0xa0
51 #define WBSIO_ID_W83627SF 0x59
52 #define WBSIO_ID_W83637HF 0x70
53 #define WBSIO_ID_W83697HF 0x60
55 /* Logical Device Number (LDN) Assignments */
56 #define WBSIO_LDN_HM 0x0b
58 /* Hardware Monitor Control Registers (LDN B) */
59 #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */
60 #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */
62 struct wbsio_softc {
63 struct device *sc_dev;
65 struct resource *sc_iores;
66 int sc_iorid;
68 bus_space_tag_t sc_iot;
69 bus_space_handle_t sc_ioh;
72 static int wbsio_probe(struct device *);
73 static int wbsio_attach(struct device *);
74 static int wbsio_detach(struct device *);
76 static device_method_t wbsio_methods[] = {
77 DEVMETHOD(device_probe, wbsio_probe),
78 DEVMETHOD(device_attach, wbsio_attach),
79 DEVMETHOD(device_detach, wbsio_detach),
81 { NULL, NULL}
84 static driver_t wbsio_driver = {
85 "wbsio",
86 wbsio_methods,
87 sizeof(struct wbsio_softc)
90 static devclass_t wbsio_devclass;
92 DRIVER_MODULE(wbsio, isa, wbsio_driver, wbsio_devclass, NULL, NULL);
95 static __inline void
96 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
98 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
99 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
102 static __inline void
103 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
105 bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
108 static __inline u_int8_t
109 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
111 bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
112 return (bus_space_read_1(iot, ioh, WBSIO_DATA));
115 static __inline void
116 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
117 u_int8_t data)
119 bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
120 bus_space_write_1(iot, ioh, WBSIO_DATA, data);
123 static int
124 wbsio_probe(struct device *dev)
126 struct resource *iores;
127 int iorid = 0;
128 bus_space_tag_t iot;
129 bus_space_handle_t ioh;
130 uint8_t reg_id, reg_rev;
131 const char *desc = NULL;
132 char fulldesc[64];
134 /* Match by device ID */
136 iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
137 0ul, ~0ul, WBSIO_IOSIZE,
138 RF_ACTIVE);
139 if (iores == NULL)
140 return ENXIO;
141 iot = rman_get_bustag(iores);
142 ioh = rman_get_bushandle(iores);
144 wbsio_conf_enable(iot, ioh);
145 /* Read device ID */
146 reg_id = wbsio_conf_read(iot, ioh, WBSIO_ID);
147 /* Read device revision */
148 reg_rev = wbsio_conf_read(iot, ioh, WBSIO_REV);
149 wbsio_conf_disable(iot, ioh);
150 bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
152 switch (reg_id) {
153 case WBSIO_ID_W83627HF:
154 desc = "W83627HF";
155 break;
156 case WBSIO_ID_W83627THF:
157 desc = "W83627THF";
158 break;
159 case WBSIO_ID_W83627EHF:
160 desc = "W83627EHF";
161 break;
162 case WBSIO_ID_W83627DHG:
163 desc = "W83627DHG";
164 break;
165 case WBSIO_ID_W83637HF:
166 desc = "W83637HF";
167 break;
168 case WBSIO_ID_W83697HF:
169 desc = "W83697HF";
170 break;
173 if (desc == NULL)
174 return ENXIO;
176 ksnprintf(fulldesc, sizeof(fulldesc),
177 "Winbond LPC Super I/O %s rev 0x%02x", desc, reg_rev);
178 device_set_desc_copy(dev, fulldesc);
179 return 0;
182 static int
183 wbsio_attach(struct device *dev)
185 struct wbsio_softc *sc = device_get_softc(dev);
186 uint8_t reg0, reg1;
187 uint16_t iobase;
188 struct device *parent = device_get_parent(dev);
189 struct device *child;
190 struct devclass *c_dc;
191 int c_maxunit;
193 /* Map ISA I/O space */
194 sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
195 0ul, ~0ul, WBSIO_IOSIZE,
196 RF_ACTIVE);
197 if (sc->sc_iores == NULL) {
198 device_printf(dev, "can't map i/o space\n");
199 return ENXIO;
201 sc->sc_iot = rman_get_bustag(sc->sc_iores);
202 sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
204 /* Enter configuration mode */
205 wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
207 /* Select HM logical device */
208 wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
211 * The address should be 8-byte aligned, but it seems some
212 * BIOSes ignore this. They get away with it, because
213 * Apparently the hardware simply ignores the lower three
214 * bits. We do the same here.
216 reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
217 reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
218 iobase = (reg1 << 8) | (reg0 & ~0x7);
219 device_printf(dev, "hardware monitor iobase is 0x%x\n", iobase);
221 /* Escape from configuration mode */
222 wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
224 if (iobase == 0) {
225 device_printf(dev, "no hardware monitor configured\n");
226 return 0;
229 child = NULL;
230 c_dc = devclass_find("lm");
231 if (c_dc == NULL) {
232 device_printf(dev, "lm devclass not found\n");
233 return ENXIO;
235 c_maxunit = devclass_get_maxunit(c_dc);
236 for (int u = 0; u < c_maxunit; u++) {
237 child = devclass_get_device(c_dc, u);
238 if (child == NULL)
239 continue;
240 if (isa_get_port(child) == iobase) {
241 if (device_is_attached(child)) {
242 device_printf(dev,
243 "%s is already attached at 0x%x\n",
244 device_get_nameunit(child), iobase);
245 return 0;
247 break;
249 if (device_is_attached(child)) {
250 child = NULL;
251 continue;
253 device_printf(dev,
254 "found unused %s at 0x%x with state %i, reusing at 0x%x\n",
255 device_get_nameunit(child), isa_get_port(child),
256 device_get_state(child), iobase);
257 break;
259 if (child == NULL)
260 child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
261 "lm", -1);
262 // child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
263 // "lm", 3 + device_get_unit(dev));
264 if (child == NULL) {
265 device_printf(dev, "cannot add child\n");
266 return ENXIO;
268 if (bus_set_resource(child, SYS_RES_IOPORT, 0, iobase, 8)) {
269 device_printf(dev, "cannot set resource\n");
270 return ENXIO;
272 return device_probe_and_attach(child);
275 static int
276 wbsio_detach(struct device *dev)
278 struct wbsio_softc *sc = device_get_softc(dev);
280 return bus_release_resource(dev, SYS_RES_IOPORT,
281 sc->sc_iorid, sc->sc_iores);