xref: /openbsd-src/sys/dev/isa/wbsio.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: wbsio.c,v 1.6 2010/07/18 12:44:55 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org>
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 
18 /*
19  * Winbond LPC Super I/O driver.
20  */
21 
22 #include <sys/param.h>
23 #include <sys/device.h>
24 #include <sys/kernel.h>
25 #include <sys/systm.h>
26 
27 #include <machine/bus.h>
28 
29 #include <dev/isa/isareg.h>
30 #include <dev/isa/isavar.h>
31 
32 /* ISA bus registers */
33 #define WBSIO_INDEX		0x00	/* Configuration Index Register */
34 #define WBSIO_DATA		0x01	/* Configuration Data Register */
35 
36 #define WBSIO_IOSIZE		0x02	/* ISA I/O space size */
37 
38 #define WBSIO_CONF_EN_MAGIC	0x87	/* enable configuration mode */
39 #define WBSIO_CONF_DS_MAGIC	0xaa	/* disable configuration mode */
40 
41 /* Configuration Space Registers */
42 #define WBSIO_LDN		0x07	/* Logical Device Number */
43 #define WBSIO_ID		0x20	/* Device ID */
44 #define WBSIO_REV		0x21	/* Device Revision */
45 
46 #define WBSIO_ID_W83627HF	0x52
47 #define WBSIO_ID_W83627THF	0x82
48 #define WBSIO_ID_W83627EHF	0x88
49 #define WBSIO_ID_W83627DHG	0xa0
50 #define WBSIO_ID_W83627DHGP	0xb0
51 #define WBSIO_ID_W83627SF	0x59
52 #define WBSIO_ID_W83637HF	0x70
53 #define WBSIO_ID_W83697HF	0x60
54 
55 /* Logical Device Number (LDN) Assignments */
56 #define WBSIO_LDN_HM		0x0b
57 
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] */
61 
62 #ifdef WBSIO_DEBUG
63 #define DPRINTF(x) printf x
64 #else
65 #define DPRINTF(x)
66 #endif
67 
68 struct wbsio_softc {
69 	struct device		sc_dev;
70 
71 	bus_space_tag_t		sc_iot;
72 	bus_space_handle_t	sc_ioh;
73 };
74 
75 int	wbsio_probe(struct device *, void *, void *);
76 void	wbsio_attach(struct device *, struct device *, void *);
77 int	wbsio_print(void *, const char *);
78 
79 struct cfattach wbsio_ca = {
80 	sizeof(struct wbsio_softc),
81 	wbsio_probe,
82 	wbsio_attach
83 };
84 
85 struct cfdriver wbsio_cd = {
86 	NULL, "wbsio", DV_DULL
87 };
88 
89 static __inline void
90 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
91 {
92 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
93 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
94 }
95 
96 static __inline void
97 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
98 {
99 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
100 }
101 
102 static __inline u_int8_t
103 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
104 {
105 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
106 	return (bus_space_read_1(iot, ioh, WBSIO_DATA));
107 }
108 
109 static __inline void
110 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
111     u_int8_t data)
112 {
113 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
114 	bus_space_write_1(iot, ioh, WBSIO_DATA, data);
115 }
116 
117 int
118 wbsio_probe(struct device *parent, void *match, void *aux)
119 {
120 	struct isa_attach_args *ia = aux;
121 	bus_space_tag_t iot;
122 	bus_space_handle_t ioh;
123 	u_int8_t reg;
124 
125 	/* Match by device ID */
126 	iot = ia->ia_iot;
127 	if (bus_space_map(iot, ia->ipa_io[0].base, WBSIO_IOSIZE, 0, &ioh))
128 		return (0);
129 	wbsio_conf_enable(iot, ioh);
130 	reg = wbsio_conf_read(iot, ioh, WBSIO_ID);
131 	DPRINTF(("wbsio_probe: id 0x%02x\n", reg));
132 	wbsio_conf_disable(iot, ioh);
133 	bus_space_unmap(iot, ioh, WBSIO_IOSIZE);
134 	switch (reg) {
135 	case WBSIO_ID_W83627HF:
136 	case WBSIO_ID_W83627THF:
137 	case WBSIO_ID_W83627EHF:
138 	case WBSIO_ID_W83627DHG:
139 	case WBSIO_ID_W83627DHGP:
140 	case WBSIO_ID_W83637HF:
141 	case WBSIO_ID_W83697HF:
142 		ia->ipa_nio = 1;
143 		ia->ipa_io[0].length = WBSIO_IOSIZE;
144 		ia->ipa_nmem = 0;
145 		ia->ipa_nirq = 0;
146 		ia->ipa_ndrq = 0;
147 		return (1);
148 	}
149 
150 	return (0);
151 }
152 
153 void
154 wbsio_attach(struct device *parent, struct device *self, void *aux)
155 {
156 	struct wbsio_softc *sc = (void *)self;
157 	struct isa_attach_args *ia = aux;
158 	struct isa_attach_args nia;
159 	u_int8_t reg, reg0, reg1;
160 	u_int16_t iobase;
161 
162 	/* Map ISA I/O space */
163 	sc->sc_iot = ia->ia_iot;
164 	if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base,
165 	    WBSIO_IOSIZE, 0, &sc->sc_ioh)) {
166 		printf(": can't map i/o space\n");
167 		return;
168 	}
169 
170 	/* Enter configuration mode */
171 	wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
172 
173 	/* Read device ID */
174 	reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID);
175 	switch (reg) {
176 	case WBSIO_ID_W83627HF:
177 		printf(": W83627HF");
178 		break;
179 	case WBSIO_ID_W83627THF:
180 		printf(": W83627THF");
181 		break;
182 	case WBSIO_ID_W83627EHF:
183 		printf(": W83627EHF");
184 		break;
185 	case WBSIO_ID_W83627DHG:
186 		printf(": W83627DHG");
187 		break;
188 	case WBSIO_ID_W83627DHGP:
189 		printf(": W83627DHG-P");
190 		break;
191 	case WBSIO_ID_W83637HF:
192 		printf(": W83637HF");
193 		break;
194 	case WBSIO_ID_W83697HF:
195 		printf(": W83697HF");
196 		break;
197 	}
198 
199 	/* Read device revision */
200 	reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV);
201 	printf(" rev 0x%02x", reg);
202 
203 	/* Select HM logical device */
204 	wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
205 
206 	/*
207 	 * The address should be 8-byte aligned, but it seems some
208 	 * BIOSes ignore this.  They get away with it, because
209 	 * Apparently the hardware simply ignores the lower three
210 	 * bits.  We do the same here.
211 	 */
212 	reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
213 	reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
214 	iobase = (reg1 << 8) | (reg0 & ~0x7);
215 
216 	printf("\n");
217 
218 	/* Escape from configuration mode */
219 	wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
220 
221 	if (iobase == 0)
222 		return;
223 
224 	nia = *ia;
225 	nia.ia_iobase = iobase;
226 	config_found(self, &nia, wbsio_print);
227 }
228 
229 int
230 wbsio_print(void *aux, const char *pnp)
231 {
232 	struct isa_attach_args *ia = aux;
233 
234 	if (pnp)
235 		printf("%s", pnp);
236 	if (ia->ia_iosize)
237 		printf(" port 0x%x", ia->ia_iobase);
238 	if (ia->ia_iosize > 1)
239 		printf("/%d", ia->ia_iosize);
240 	return (UNCONF);
241 }
242