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