xref: /openbsd/sys/dev/isa/wbsio.c (revision 7b36286a)
1 /*	$OpenBSD: wbsio.c,v 1.4 2008/04/08 18:48:43 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_W83627SF	0x59
51 #define WBSIO_ID_W83637HF	0x70
52 #define WBSIO_ID_W83697HF	0x60
53 
54 /* Logical Device Number (LDN) Assignments */
55 #define WBSIO_LDN_HM		0x0b
56 
57 /* Hardware Monitor Control Registers (LDN B) */
58 #define WBSIO_HM_ADDR_MSB	0x60	/* Address [15:8] */
59 #define WBSIO_HM_ADDR_LSB	0x61	/* Address [7:0] */
60 
61 #ifdef WBSIO_DEBUG
62 #define DPRINTF(x) printf x
63 #else
64 #define DPRINTF(x)
65 #endif
66 
67 struct wbsio_softc {
68 	struct device		sc_dev;
69 
70 	bus_space_tag_t		sc_iot;
71 	bus_space_handle_t	sc_ioh;
72 };
73 
74 int	wbsio_probe(struct device *, void *, void *);
75 void	wbsio_attach(struct device *, struct device *, void *);
76 int	wbsio_print(void *, const char *);
77 
78 struct cfattach wbsio_ca = {
79 	sizeof(struct wbsio_softc),
80 	wbsio_probe,
81 	wbsio_attach
82 };
83 
84 struct cfdriver wbsio_cd = {
85 	NULL, "wbsio", DV_DULL
86 };
87 
88 static __inline void
89 wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
90 {
91 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
92 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_EN_MAGIC);
93 }
94 
95 static __inline void
96 wbsio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
97 {
98 	bus_space_write_1(iot, ioh, WBSIO_INDEX, WBSIO_CONF_DS_MAGIC);
99 }
100 
101 static __inline u_int8_t
102 wbsio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
103 {
104 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
105 	return (bus_space_read_1(iot, ioh, WBSIO_DATA));
106 }
107 
108 static __inline void
109 wbsio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
110     u_int8_t data)
111 {
112 	bus_space_write_1(iot, ioh, WBSIO_INDEX, index);
113 	bus_space_write_1(iot, ioh, WBSIO_DATA, data);
114 }
115 
116 int
117 wbsio_probe(struct device *parent, void *match, void *aux)
118 {
119 	struct isa_attach_args *ia = aux;
120 	bus_space_tag_t iot;
121 	bus_space_handle_t ioh;
122 	u_int8_t reg;
123 
124 	/* Match by device ID */
125 	iot = ia->ia_iot;
126 	if (bus_space_map(iot, ia->ipa_io[0].base, WBSIO_IOSIZE, 0, &ioh))
127 		return (0);
128 	wbsio_conf_enable(iot, ioh);
129 	reg = wbsio_conf_read(iot, ioh, WBSIO_ID);
130 	DPRINTF(("wbsio_probe: id 0x%02x\n", reg));
131 	wbsio_conf_disable(iot, ioh);
132 	bus_space_unmap(iot, ioh, WBSIO_IOSIZE);
133 	switch (reg) {
134 	case WBSIO_ID_W83627HF:
135 	case WBSIO_ID_W83627THF:
136 	case WBSIO_ID_W83627EHF:
137 	case WBSIO_ID_W83627DHG:
138 	case WBSIO_ID_W83637HF:
139 	case WBSIO_ID_W83697HF:
140 		ia->ipa_nio = 1;
141 		ia->ipa_io[0].length = WBSIO_IOSIZE;
142 		ia->ipa_nmem = 0;
143 		ia->ipa_nirq = 0;
144 		ia->ipa_ndrq = 0;
145 		return (1);
146 	}
147 
148 	return (0);
149 }
150 
151 void
152 wbsio_attach(struct device *parent, struct device *self, void *aux)
153 {
154 	struct wbsio_softc *sc = (void *)self;
155 	struct isa_attach_args *ia = aux;
156 	struct isa_attach_args nia;
157 	u_int8_t reg, reg0, reg1;
158 	u_int16_t iobase;
159 
160 	/* Map ISA I/O space */
161 	sc->sc_iot = ia->ia_iot;
162 	if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base,
163 	    WBSIO_IOSIZE, 0, &sc->sc_ioh)) {
164 		printf(": can't map I/O space\n");
165 		return;
166 	}
167 
168 	/* Enter configuration mode */
169 	wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
170 
171 	/* Read device ID */
172 	reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_ID);
173 	switch (reg) {
174 	case WBSIO_ID_W83627HF:
175 		printf(": W83627HF");
176 		break;
177 	case WBSIO_ID_W83627THF:
178 		printf(": W83627THF");
179 		break;
180 	case WBSIO_ID_W83627EHF:
181 		printf(": W83627EHF");
182 		break;
183 	case WBSIO_ID_W83627DHG:
184 		printf(": W83627DHG");
185 		break;
186 	case WBSIO_ID_W83637HF:
187 		printf(": W83637HF");
188 		break;
189 	case WBSIO_ID_W83697HF:
190 		printf(": W83697HF");
191 		break;
192 	}
193 
194 	/* Read device revision */
195 	reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_REV);
196 	printf(" rev 0x%02x", reg);
197 
198 	/* Select HM logical device */
199 	wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_HM);
200 
201 	/*
202 	 * The address should be 8-byte aligned, but it seems some
203 	 * BIOSes ignore this.  They get away with it, because
204 	 * Apparently the hardware simply ignores the lower three
205 	 * bits.  We do the same here.
206 	 */
207 	reg0 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_LSB);
208 	reg1 = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_HM_ADDR_MSB);
209 	iobase = (reg1 << 8) | (reg0 & ~0x7);
210 
211 	printf("\n");
212 
213 	/* Escape from configuration mode */
214 	wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
215 
216 	if (iobase == 0)
217 		return;
218 
219 	nia = *ia;
220 	nia.ia_iobase = iobase;
221 	config_found(self, &nia, wbsio_print);
222 }
223 
224 int
225 wbsio_print(void *aux, const char *pnp)
226 {
227 	struct isa_attach_args *ia = aux;
228 
229 	if (pnp)
230 		printf("%s", pnp);
231 	if (ia->ia_iosize)
232 		printf(" port 0x%x", ia->ia_iobase);
233 	if (ia->ia_iosize > 1)
234 		printf("/%d", ia->ia_iosize);
235 	return (UNCONF);
236 }
237