xref: /openbsd-src/sys/dev/pci/amdpm.c (revision 7a7e91161c59b6d05c61f4a06fd7976bad6c1c30)
1 /*	$OpenBSD: amdpm.c,v 1.7 2006/01/05 08:54:27 grange Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Enami Tsugutomo.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/device.h>
42 #include <sys/kernel.h>
43 #include <sys/lock.h>
44 #include <sys/proc.h>
45 #include <sys/timeout.h>
46 #ifdef __HAVE_TIMECOUNTER
47 #include <sys/timetc.h>
48 #endif
49 
50 #include <machine/bus.h>
51 
52 #include <dev/pci/pcivar.h>
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pcidevs.h>
55 
56 #include <dev/pci/amdpmreg.h>
57 
58 #include <dev/rndvar.h>
59 #include <dev/i2c/i2cvar.h>
60 
61 #ifdef AMDPM_DEBUG
62 #define DPRINTF(x) printf x
63 #else
64 #define DPRINTF(x)
65 #endif
66 
67 #define AMDPM_SMBUS_DELAY	100
68 #define AMDPM_SMBUS_TIMEOUT	1
69 
70 #ifdef __HAVE_TIMECOUNTER
71 u_int amdpm_get_timecount(struct timecounter *tc);
72 
73 #ifndef AMDPM_FREQUENCY
74 #define AMDPM_FREQUENCY 3579545
75 #endif
76 
77 static struct timecounter amdpm_timecounter = {
78 	amdpm_get_timecount,	/* get_timecount */
79 	0,			/* no poll_pps */
80 	0xffffff,		/* counter_mask */
81 	AMDPM_FREQUENCY,	/* frequency */
82 	"AMDPM",		/* name */
83 	1000			/* quality */
84 };
85 #endif
86 
87 struct amdpm_softc {
88 	struct device sc_dev;
89 
90 	pci_chipset_tag_t sc_pc;
91 	pcitag_t sc_tag;
92 
93 	bus_space_tag_t sc_iot;
94 	bus_space_handle_t sc_ioh;		/* PMxx space */
95 	int sc_poll;
96 
97 	struct timeout sc_rnd_ch;
98 #ifdef AMDPM_RND_COUNTERS
99 	struct evcnt sc_rnd_hits;
100 	struct evcnt sc_rnd_miss;
101 	struct evcnt sc_rnd_data[256];
102 #endif
103 
104 	struct i2c_controller sc_i2c_tag;
105 	struct lock sc_i2c_lock;
106 	struct {
107 		i2c_op_t op;
108 		void *buf;
109 		size_t len;
110 		int flags;
111 		volatile int error;
112 	} sc_i2c_xfer;
113 };
114 
115 int	amdpm_match(struct device *, void *, void *);
116 void	amdpm_attach(struct device *, struct device *, void *);
117 void	amdpm_rnd_callout(void *);
118 
119 int	amdpm_i2c_acquire_bus(void *, int);
120 void	amdpm_i2c_release_bus(void *, int);
121 int	amdpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
122 	    void *, size_t, int);
123 
124 int	amdpm_intr(void *);
125 
126 struct cfattach amdpm_ca = {
127 	sizeof(struct amdpm_softc), amdpm_match, amdpm_attach
128 };
129 
130 struct cfdriver amdpm_cd = {
131 	NULL, "amdpm", DV_DULL
132 };
133 
134 #ifdef AMDPM_RND_COUNTERS
135 #define	AMDPM_RNDCNT_INCR(ev)	(ev)->ev_count++
136 #else
137 #define	AMDPM_RNDCNT_INCR(ev)	/* nothing */
138 #endif
139 
140 const struct pci_matchid amdpm_ids[] = {
141 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_766_PMC },
142 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC768_PMC }
143 };
144 
145 int
146 amdpm_match(struct device *parent, void *match, void *aux)
147 {
148 	return (pci_matchbyid(aux, amdpm_ids,
149 	    sizeof(amdpm_ids) / sizeof(amdpm_ids[0])));
150 }
151 
152 void
153 amdpm_attach(struct device *parent, struct device *self, void *aux)
154 {
155 	struct amdpm_softc *sc = (struct amdpm_softc *) self;
156 	struct pci_attach_args *pa = aux;
157 	struct i2cbus_attach_args iba;
158 	struct timeval tv1, tv2;
159 	pcireg_t cfg_reg, reg;
160 	int i;
161 
162 	sc->sc_pc = pa->pa_pc;
163 	sc->sc_tag = pa->pa_tag;
164 	sc->sc_iot = pa->pa_iot;
165 	sc->sc_poll = 1; /* XXX */
166 
167 	cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG);
168 	if ((cfg_reg & AMDPM_PMIOEN) == 0) {
169 		printf(": PMxx space isn't enabled\n");
170 		return;
171 	}
172 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_PMPTR);
173 	if (bus_space_map(sc->sc_iot, AMDPM_PMBASE(reg), AMDPM_PMSIZE,
174 	    0, &sc->sc_ioh)) {
175 		printf(": failed to map PMxx space\n");
176 		return;
177 	}
178 
179 #ifdef __HAVE_TIMECOUNTER
180 	if ((cfg_reg & AMDPM_TMRRST) == 0 &&
181 	    (cfg_reg & AMDPM_STOPTMR) == 0 &&
182 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) {
183 		printf(": %d-bit timer at %dHz",
184 		    (cfg_reg & AMDPM_TMR32) ? 32 : 24,
185 		    amdpm_timecounter.tc_frequency);
186 
187 		amdpm_timecounter.tc_priv = sc;
188 		if (cfg_reg & AMDPM_TMR32)
189 			amdpm_timecounter.tc_counter_mask = 0xffffffffu;
190 		tc_init(&amdpm_timecounter);
191 	}
192 #endif
193 
194 	if (cfg_reg & AMDPM_RNGEN) {
195 		/* Check to see if we can read data from the RNG. */
196 		(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh,
197 		    AMDPM_RNGDATA);
198 		/* benchmark the RNG */
199 		microtime(&tv1);
200 		for (i = 2 * 1024; i--; ) {
201 			while(!(bus_space_read_1(sc->sc_iot, sc->sc_ioh,
202 			    AMDPM_RNGSTAT) & AMDPM_RNGDONE))
203 				;
204 			(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh,
205 			    AMDPM_RNGDATA);
206 		}
207 		microtime(&tv2);
208 
209 		timersub(&tv2, &tv1, &tv1);
210 		if (tv1.tv_sec)
211 			tv1.tv_usec += 1000000 * tv1.tv_sec;
212 		printf(": rng active, %dKb/sec", 8 * 1000000 / tv1.tv_usec);
213 
214 #ifdef AMDPM_RND_COUNTERS
215 			evcnt_attach_dynamic(&sc->sc_rnd_hits, EVCNT_TYPE_MISC,
216 			    NULL, sc->sc_dev.dv_xname, "rnd hits");
217 			evcnt_attach_dynamic(&sc->sc_rnd_miss, EVCNT_TYPE_MISC,
218 			    NULL, sc->sc_dev.dv_xname, "rnd miss");
219 			for (i = 0; i < 256; i++) {
220 				evcnt_attach_dynamic(&sc->sc_rnd_data[i],
221 				    EVCNT_TYPE_MISC, NULL, sc->sc_dev.dv_xname,
222 				    "rnd data");
223 			}
224 #endif
225 		timeout_set(&sc->sc_rnd_ch, amdpm_rnd_callout, sc);
226 		amdpm_rnd_callout(sc);
227 	}
228 
229 	/* Attach I2C bus */
230 	lockinit(&sc->sc_i2c_lock, PRIBIO | PCATCH, "iiclk", 0, 0);
231 	sc->sc_i2c_tag.ic_cookie = sc;
232 	sc->sc_i2c_tag.ic_acquire_bus = amdpm_i2c_acquire_bus;
233 	sc->sc_i2c_tag.ic_release_bus = amdpm_i2c_release_bus;
234 	sc->sc_i2c_tag.ic_exec = amdpm_i2c_exec;
235 
236 	bzero(&iba, sizeof iba);
237 	iba.iba_name = "iic";
238 	iba.iba_tag = &sc->sc_i2c_tag;
239 	config_found(self, &iba, iicbus_print);
240 
241 	printf("\n");
242 }
243 
244 void
245 amdpm_rnd_callout(void *v)
246 {
247 	struct amdpm_softc *sc = v;
248 	u_int32_t reg;
249 #ifdef AMDPM_RND_COUNTERS
250 	int i;
251 #endif
252 
253 	if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) &
254 	    AMDPM_RNGDONE) != 0) {
255 		reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA);
256 		add_true_randomness(reg);
257 #ifdef AMDPM_RND_COUNTERS
258 		AMDPM_RNDCNT_INCR(&sc->sc_rnd_hits);
259 		for (i = 0; i < sizeof(reg); i++, reg >>= NBBY)
260 			AMDPM_RNDCNT_INCR(&sc->sc_rnd_data[reg & 0xff]);
261 #endif
262 	} else
263 		AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss);
264 	timeout_add(&sc->sc_rnd_ch, 1);
265 }
266 
267 #ifdef __HAVE_TIMECOUNTER
268 u_int
269 amdpm_get_timecount(struct timecounter *tc)
270 {
271 	struct amdpm_softc *sc = tc->tc_priv;
272 	u_int u2;
273 #if 0
274 	u_int u1, u3;
275 #endif
276 
277 	u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR);
278 #if 0
279 	u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR);
280 	do {
281 		u1 = u2;
282 		u2 = u3;
283 		u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR);
284 	} while (u1 > u2 || u2 > u3);
285 #endif
286 	return (u2);
287 }
288 #endif
289 
290 int
291 amdpm_i2c_acquire_bus(void *cookie, int flags)
292 {
293 	struct amdpm_softc *sc = cookie;
294 
295 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
296 		return (0);
297 
298 	return (lockmgr(&sc->sc_i2c_lock, LK_EXCLUSIVE, NULL));
299 }
300 
301 void
302 amdpm_i2c_release_bus(void *cookie, int flags)
303 {
304 	struct amdpm_softc *sc = cookie;
305 
306 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
307 		return;
308 
309 	lockmgr(&sc->sc_i2c_lock, LK_RELEASE, NULL);
310 }
311 
312 int
313 amdpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
314     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
315 {
316 	struct amdpm_softc *sc = cookie;
317 	u_int8_t *b;
318 	u_int16_t st, ctl, data;
319 	int retries;
320 
321 	DPRINTF(("%s: exec: op %d, addr 0x%x, cmdlen %d, len %d, flags 0x%x\n",
322 	    sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags));
323 
324 	/* Check if there's a transfer already running */
325 	st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT);
326 	DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
327 	    AMDPM_SMBSTAT_BITS));
328 	if (st & AMDPM_SMBSTAT_BSY)
329 		return (1);
330 
331 	if (cold || sc->sc_poll)
332 		flags |= I2C_F_POLL;
333 
334 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
335 		return (1);
336 
337 	/* Setup transfer */
338 	sc->sc_i2c_xfer.op = op;
339 	sc->sc_i2c_xfer.buf = buf;
340 	sc->sc_i2c_xfer.len = len;
341 	sc->sc_i2c_xfer.flags = flags;
342 	sc->sc_i2c_xfer.error = 0;
343 
344 	/* Set slave address and transfer direction */
345 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBADDR,
346 	    AMDPM_SMBADDR_ADDR(addr) |
347 	    (I2C_OP_READ_P(op) ? AMDPM_SMBADDR_READ : 0));
348 
349 	b = (void *)cmdbuf;
350 	if (cmdlen > 0)
351 		/* Set command byte */
352 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCMD, b[0]);
353 
354 	if (I2C_OP_WRITE_P(op)) {
355 		/* Write data */
356 		data = 0;
357 		b = buf;
358 		if (len > 0)
359 			data = b[0];
360 		if (len > 1)
361 			data |= ((u_int16_t)b[1] << 8);
362 		if (len > 0)
363 			bus_space_write_2(sc->sc_iot, sc->sc_ioh,
364 			    AMDPM_SMBDATA, data);
365 	}
366 
367 	/* Set SMBus command */
368 	if (len == 0)
369 		ctl = AMDPM_SMBCTL_CMD_BYTE;
370 	else if (len == 1)
371 		ctl = AMDPM_SMBCTL_CMD_BDATA;
372 	else if (len == 2)
373 		ctl = AMDPM_SMBCTL_CMD_WDATA;
374 
375 	if ((flags & I2C_F_POLL) == 0)
376 		ctl |= AMDPM_SMBCTL_CYCEN;
377 
378 	/* Start transaction */
379 	ctl |= AMDPM_SMBCTL_START;
380 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCTL, ctl);
381 
382 	if (flags & I2C_F_POLL) {
383 		/* Poll for completion */
384 		DELAY(AMDPM_SMBUS_DELAY);
385 		for (retries = 1000; retries > 0; retries--) {
386 			st = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
387 			    AMDPM_SMBSTAT);
388 			if ((st & AMDPM_SMBSTAT_HBSY) == 0)
389 				break;
390 			DELAY(AMDPM_SMBUS_DELAY);
391 		}
392 		if (st & AMDPM_SMBSTAT_HBSY)
393 			goto timeout;
394 		amdpm_intr(sc);
395 	} else {
396 		/* Wait for interrupt */
397 		if (tsleep(sc, PRIBIO, "iicexec", AMDPM_SMBUS_TIMEOUT * hz))
398 			goto timeout;
399 	}
400 
401 	if (sc->sc_i2c_xfer.error)
402 		return (1);
403 
404 	return (0);
405 
406 timeout:
407 	/*
408 	 * Transfer timeout. Kill the transaction and clear status bits.
409 	 */
410 	printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st,
411 	    AMDPM_SMBSTAT_BITS);
412 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBCTL,
413 	    AMDPM_SMBCTL_ABORT);
414 	DELAY(AMDPM_SMBUS_DELAY);
415 	st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT);
416 	if ((st & AMDPM_SMBSTAT_ABRT) == 0)
417 		printf("%s: transaction abort failed, status 0x%b\n",
418 		    sc->sc_dev.dv_xname, st, AMDPM_SMBSTAT_BITS);
419 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT, st);
420 	return (1);
421 }
422 
423 int
424 amdpm_intr(void *arg)
425 {
426 	struct amdpm_softc *sc = arg;
427 	u_int16_t st, data;
428 	u_int8_t *b;
429 	size_t len;
430 
431 	/* Read status */
432 	st = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT);
433 	if ((st & AMDPM_SMBSTAT_HBSY) != 0 || (st & (AMDPM_SMBSTAT_ABRT |
434 	    AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR | AMDPM_SMBSTAT_CYC |
435 	    AMDPM_SMBSTAT_TO | AMDPM_SMBSTAT_SNP | AMDPM_SMBSTAT_SLV |
436 	    AMDPM_SMBSTAT_SMBA)) == 0)
437 		/* Interrupt was not for us */
438 		return (0);
439 
440 	DPRINTF(("%s: intr: st 0x%b\n", sc->sc_dev.dv_xname, st,
441 	    AMDPM_SMBSTAT_BITS));
442 
443 	/* Clear status bits */
444 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_SMBSTAT, st);
445 
446 	/* Check for errors */
447 	if (st & (AMDPM_SMBSTAT_COL | AMDPM_SMBSTAT_PRERR |
448 	    AMDPM_SMBSTAT_TO)) {
449 		sc->sc_i2c_xfer.error = 1;
450 		goto done;
451 	}
452 
453 	if (st & AMDPM_SMBSTAT_CYC) {
454 		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
455 			goto done;
456 
457 		/* Read data */
458 		b = sc->sc_i2c_xfer.buf;
459 		len = sc->sc_i2c_xfer.len;
460 		if (len > 0) {
461 			data = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
462 			    AMDPM_SMBDATA);
463 			b[0] = data & 0xff;
464 		}
465 		if (len > 1)
466 			b[1] = (data >> 8) & 0xff;
467 	}
468 
469 done:
470 	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
471 		wakeup(sc);
472 	return (1);
473 }
474