xref: /openbsd/sys/dev/pci/viapm.c (revision 9e6efb0a)
1 /*	$OpenBSD: viapm.c,v 1.23 2024/05/24 06:02:58 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Mark Kettenis <kettenis@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 /*	$NetBSD: viaenv.c,v 1.9 2002/10/02 16:51:59 thorpej Exp $	*/
20 
21 /*
22  * Copyright (c) 2000 Johan Danielsson
23  * All rights reserved.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  *
29  * 1. Redistributions of source code must retain the above copyright
30  *    notice, this list of conditions and the following disclaimer.
31  *
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  *
36  * 3. Neither the name of author nor the names of any contributors may
37  *    be used to endorse or promote products derived from this
38  *    software without specific prior written permission.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
41  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
45  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
46  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
50  * POSSIBILITY OF SUCH DAMAGE.
51  */
52 
53 /*
54  * Driver for the SMBus controller and power management timer
55  * in the VIA VT82C596[B], VT82C686A, VT8231, VT8233[A], VT8235, VT8237[A,S],
56  * VT8251, CX700, VX800, VX855 and VX900 South Bridges.
57  * Also for the hardware monitoring part of the VIA VT82C686A and VT8231.
58  */
59 
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/device.h>
63 #include <sys/rwlock.h>
64 #include <sys/sensors.h>
65 #include <sys/timeout.h>
66 #include <sys/timetc.h>
67 
68 #include <machine/bus.h>
69 
70 #include <dev/pci/pcidevs.h>
71 #include <dev/pci/pcireg.h>
72 #include <dev/pci/pcivar.h>
73 
74 #include <dev/i2c/i2cvar.h>
75 
76 /*
77  * Register definitions.
78  */
79 
80 /* PCI configuration registers */
81 #define VIAPM_PM_CFG1		0x40	/* general configuration */
82 #define VIAPM_PM_CFG2		0x80
83 #define VIAPM_PM_CFG_TMR32	(1 << 11)	/* 32-bit PM timer */
84 #define VIAPM_PM_CFG_PMEN	(1 << 15)	/* enable PM I/O space */
85 #define VIAPM_PM_BASE1		0x48	/* power management I/O base address */
86 #define VIAPM_PM_BASE2		0x88
87 #define VIAPM_PM_BASE_MASK	0xff80
88 
89 #define VIAPM_HWMON_BASE	0x70	/* HWMon I/O base address */
90 #define VIAPM_HWMON_BASE_MASK	0xff80
91 #define VIAPM_HWMON_CFG		0x74	/* HWMon control register */
92 #define VIAPM_HWMON_CFG_HWEN	(1 << 0)	/* enable HWMon I/O space */
93 
94 #define VIAPM_SMB_BASE1		0x90	/* SMBus I/O base address */
95 #define VIAPM_SMB_BASE2		0x80
96 #define VIAPM_SMB_BASE3		0xd0
97 #define VIAPM_SMB_BASE_MASK	0xfff0
98 #define VIAPM_SMB_CFG1		0xd2	/* host configuration */
99 #define VIAPM_SMB_CFG2		0x84
100 #define VIAPM_SMB_CFG_HSTEN	(1 << 0)	/* enable SMBus I/O space */
101 #define VIAPM_SMB_CFG_INTEN	(1 << 1)	/* enable SCI/SMI */
102 #define VIAPM_SMB_CFG_SCIEN	(1 << 3)	/* interrupt type (SCI/SMI) */
103 
104 #define VIAPM_PM_SIZE		256	/* Power management I/O space size */
105 #define VIAPM_HWMON_SIZE	128	/* HWMon I/O space size */
106 #define VIAPM_SMB_SIZE		16	/* SMBus I/O space size */
107 
108 /* HWMon I/O registers */
109 #define VIAPM_HWMON_TSENS3	0x1f
110 #define VIAPM_HWMON_TSENS1	0x20
111 #define VIAPM_HWMON_TSENS2	0x21
112 #define VIAPM_HWMON_VSENS1	0x22
113 #define VIAPM_HWMON_VSENS2	0x23
114 #define VIAPM_HWMON_VCORE	0x24
115 #define VIAPM_HWMON_VSENS3	0x25
116 #define VIAPM_HWMON_VSENS4	0x26
117 #define VIAPM_HWMON_FAN1	0x29
118 #define VIAPM_HWMON_FAN2	0x2a
119 #define VIAPM_HWMON_FANCONF	0x47	/* fan configuration */
120 #define VIAPM_HWMON_TLOW	0x49	/* temperature low order value */
121 #define VIAPM_HWMON_TIRQ	0x4b	/* temperature interrupt configuration */
122 
123 /* ACPI I/O registers */
124 #define VIAPM_PM_TMR		0x08	/* PM timer */
125 
126 /* SMBus I/O registers */
127 #define VIAPM_SMB_HS		0x00	/* host status */
128 #define VIAPM_SMB_HS_BUSY	(1 << 0)	/* running a command */
129 #define VIAPM_SMB_HS_INTR	(1 << 1)	/* command completed */
130 #define VIAPM_SMB_HS_DEVERR	(1 << 2)	/* command error */
131 #define VIAPM_SMB_HS_BUSERR	(1 << 3)	/* transaction collision */
132 #define VIAPM_SMB_HS_FAILED	(1 << 4)	/* failed bus transaction */
133 #define VIAPM_SMB_HS_INUSE	(1 << 6)	/* bus semaphore */
134 #define VIAPM_SMB_HS_BITS	\
135   "\020\001BUSY\002INTR\003DEVERR\004BUSERR\005FAILED\007INUSE"
136 #define VIAPM_SMB_HC		0x02	/* host control */
137 #define VIAPM_SMB_HC_INTREN	(1 << 0)	/* enable interrupts */
138 #define VIAPM_SMB_HC_KILL	(1 << 1)	/* kill current transaction */
139 #define VIAPM_SMB_HC_CMD_QUICK	(0 << 2)	/* QUICK command */
140 #define VIAPM_SMB_HC_CMD_BYTE	(1 << 2)	/* BYTE command */
141 #define VIAPM_SMB_HC_CMD_BDATA	(2 << 2)	/* BYTE DATA command */
142 #define VIAPM_SMB_HC_CMD_WDATA	(3 << 2)	/* WORD DATA command */
143 #define VIAPM_SMB_HC_CMD_PCALL	(4 << 2)	/* PROCESS CALL command */
144 #define VIAPM_SMB_HC_CMD_BLOCK	(5 << 2)	/* BLOCK command */
145 #define VIAPM_SMB_HC_START	(1 << 6)	/* start transaction */
146 #define VIAPM_SMB_HCMD		0x03	/* host command */
147 #define VIAPM_SMB_TXSLVA	0x04	/* transmit slave address */
148 #define VIAPM_SMB_TXSLVA_READ	(1 << 0)	/* read direction */
149 #define VIAPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
150 #define VIAPM_SMB_HD0		0x05	/* host data 0 */
151 #define VIAPM_SMB_HD1		0x06	/* host data 1 */
152 #define VIAPM_SMB_HBDB		0x07	/* host block data byte */
153 
154 #ifdef VIAPM_DEBUG
155 #define DPRINTF(x...) printf(x)
156 #else
157 #define DPRINTF(x...)
158 #endif
159 
160 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
161 
162 #define VIAPM_SMBUS_DELAY	100
163 #define VIAPM_SMBUS_TIMEOUT	1
164 
165 #define VIAPM_NUM_SENSORS	10	/* three temp, two fan, five voltage */
166 
167 u_int	viapm_get_timecount(struct timecounter *tc);
168 
169 #ifndef VIAPM_FREQUENCY
170 #define VIAPM_FREQUENCY 3579545
171 #endif
172 
173 static struct timecounter viapm_timecounter = {
174 	.tc_get_timecount = viapm_get_timecount,
175 	.tc_counter_mask = 0xffffff,
176 	.tc_frequency = VIAPM_FREQUENCY,
177 	.tc_name = "VIAPM",
178 	.tc_quality = 1000,
179 	.tc_priv = NULL,
180 	.tc_user = 0,
181 };
182 
183 struct timeout viapm_timeout;
184 
185 struct viapm_softc {
186 	struct device		sc_dev;
187 
188 	bus_space_tag_t		sc_iot;
189 	bus_space_handle_t	sc_pm_ioh;
190 	bus_space_handle_t	sc_smbus_ioh;
191 	bus_space_handle_t	sc_hwmon_ioh;
192 	void *			sc_ih;
193 	int			sc_poll;
194 
195 	int			sc_fan_div[2];	/* fan RPM divisor */
196 
197 	struct ksensor		sc_data[VIAPM_NUM_SENSORS];
198 	struct ksensordev	sc_sensordev;
199 
200 	struct i2c_controller	sc_i2c_tag;
201 	struct rwlock		sc_i2c_lock;
202 	struct {
203 		i2c_op_t     op;
204 		void *       buf;
205 		size_t       len;
206 		int          flags;
207 		volatile int error;
208 	}			sc_i2c_xfer;
209 };
210 
211 int	viapm_match(struct device *, void *, void *);
212 void	viapm_attach(struct device *, struct device *, void *);
213 
214 int	viapm_i2c_acquire_bus(void *, int);
215 void	viapm_i2c_release_bus(void *, int);
216 int	viapm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
217 	    void *, size_t, int);
218 
219 int	viapm_intr(void *);
220 
221 int	val_to_uK(unsigned int);
222 int	val_to_rpm(unsigned int, int);
223 long	val_to_uV(unsigned int, int);
224 void	viapm_refresh_sensor_data(struct viapm_softc *);
225 void	viapm_refresh(void *);
226 
227 const struct cfattach viapm_ca = {
228 	sizeof(struct viapm_softc), viapm_match, viapm_attach
229 };
230 
231 struct cfdriver viapm_cd = {
232 	NULL, "viapm", DV_DULL
233 };
234 
235 const struct pci_matchid viapm_ids[] = {
236 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C596 },
237 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C596B_PM },
238 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_SMB },
239 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8231_PWR },
240 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_ISA },
241 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233A_ISA },
242 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8235_ISA },
243 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237_ISA },
244 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237A_ISA },
245 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237S_ISA },
246 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_ISA },
247 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_CX700_ISA },
248 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VX800_ISA },
249 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VX855_ISA },
250 	{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VX900_ISA }
251 };
252 
253 /*
254  * XXX there doesn't seem to exist much hard documentation on how to
255  * convert the raw values to usable units, this code is more or less
256  * stolen from the Linux driver, but changed to suit our conditions
257  */
258 
259 /*
260  * lookup-table to translate raw values to uK, this is the same table
261  * used by the Linux driver (modulo units); there is a fifth degree
262  * polynomial that supposedly been used to generate this table, but I
263  * haven't been able to figure out how -- it doesn't give the same values
264  */
265 
266 static const long val_to_temp[] = {
267 	20225, 20435, 20645, 20855, 21045, 21245, 21425, 21615, 21785, 21955,
268 	22125, 22285, 22445, 22605, 22755, 22895, 23035, 23175, 23315, 23445,
269 	23565, 23695, 23815, 23925, 24045, 24155, 24265, 24365, 24465, 24565,
270 	24665, 24765, 24855, 24945, 25025, 25115, 25195, 25275, 25355, 25435,
271 	25515, 25585, 25655, 25725, 25795, 25865, 25925, 25995, 26055, 26115,
272 	26175, 26235, 26295, 26355, 26405, 26465, 26515, 26575, 26625, 26675,
273 	26725, 26775, 26825, 26875, 26925, 26975, 27025, 27065, 27115, 27165,
274 	27205, 27255, 27295, 27345, 27385, 27435, 27475, 27515, 27565, 27605,
275 	27645, 27685, 27735, 27775, 27815, 27855, 27905, 27945, 27985, 28025,
276 	28065, 28105, 28155, 28195, 28235, 28275, 28315, 28355, 28405, 28445,
277 	28485, 28525, 28565, 28615, 28655, 28695, 28735, 28775, 28825, 28865,
278 	28905, 28945, 28995, 29035, 29075, 29125, 29165, 29205, 29245, 29295,
279 	29335, 29375, 29425, 29465, 29505, 29555, 29595, 29635, 29685, 29725,
280 	29765, 29815, 29855, 29905, 29945, 29985, 30035, 30075, 30125, 30165,
281 	30215, 30255, 30305, 30345, 30385, 30435, 30475, 30525, 30565, 30615,
282 	30655, 30705, 30755, 30795, 30845, 30885, 30935, 30975, 31025, 31075,
283 	31115, 31165, 31215, 31265, 31305, 31355, 31405, 31455, 31505, 31545,
284 	31595, 31645, 31695, 31745, 31805, 31855, 31905, 31955, 32005, 32065,
285 	32115, 32175, 32225, 32285, 32335, 32395, 32455, 32515, 32575, 32635,
286 	32695, 32755, 32825, 32885, 32955, 33025, 33095, 33155, 33235, 33305,
287 	33375, 33455, 33525, 33605, 33685, 33765, 33855, 33935, 34025, 34115,
288 	34205, 34295, 34395, 34495, 34595, 34695, 34805, 34905, 35015, 35135,
289 	35245, 35365, 35495, 35615, 35745, 35875, 36015, 36145, 36295, 36435,
290 	36585, 36745, 36895, 37065, 37225, 37395, 37575, 37755, 37935, 38125,
291 	38325, 38525, 38725, 38935, 39155, 39375, 39605, 39835, 40075, 40325,
292 	40575, 40835, 41095, 41375, 41655, 41935,
293 };
294 
295 int
296 viapm_match(struct device *parent, void *match, void *aux)
297 {
298 	return (pci_matchbyid(aux, viapm_ids, nitems(viapm_ids)));
299 }
300 
301 void
302 viapm_attach(struct device *parent, struct device *self, void *aux)
303 {
304 	struct viapm_softc *sc = (struct viapm_softc *)self;
305 	struct pci_attach_args *pa = aux;
306 	struct i2cbus_attach_args iba;
307 	pcireg_t conf, iobase;
308 	pci_intr_handle_t ih;
309 	const char *intrstr = NULL;
310 	int basereg, cfgreg;
311 	int i, v;
312 
313 	sc->sc_iot = pa->pa_iot;
314 
315 	/* SMBus */
316 	switch (PCI_PRODUCT(pa->pa_id)) {
317 	case PCI_PRODUCT_VIATECH_VT82C596:
318 	case PCI_PRODUCT_VIATECH_VT82C596B_PM:
319 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
320 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
321 		basereg = VIAPM_SMB_BASE1;
322 		break;
323 	default:
324 		basereg = VIAPM_SMB_BASE3;
325 	}
326 
327 	cfgreg = (VIAPM_SMB_CFG1 & (~0x03));	/* XXX 4-byte aligned */
328 
329 	/* Check 2nd address for VT82C596 */
330 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, basereg);
331 	if ((PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT82C596) &&
332 	    ((iobase & 0x0001) == 0)) {
333 		iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_SMB_BASE2);
334 		cfgreg = VIAPM_SMB_CFG2;
335 	}
336 
337 	/* Check if SMBus I/O space is enabled */
338 	conf = pci_conf_read(pa->pa_pc, pa->pa_tag, cfgreg);
339 	if (cfgreg != VIAPM_SMB_CFG2)
340 		conf >>= 16;
341 	DPRINTF(": conf 0x%02x", conf & 0xff);
342 
343 	if ((conf & VIAPM_SMB_CFG_HSTEN) == 0) {
344 		printf(": SMBus disabled\n");
345 		goto nosmb;
346 	}
347 
348 	/* Map SMBus I/O space */
349 	iobase &= VIAPM_SMB_BASE_MASK;
350 	if (iobase == 0 || bus_space_map(sc->sc_iot, iobase,
351 	    VIAPM_SMB_SIZE, 0, &sc->sc_smbus_ioh)) {
352 		printf(": can't map SMBus i/o space\n");
353 		goto nosmb;
354 	}
355 
356 	sc->sc_poll = 1;
357 	if ((conf & VIAPM_SMB_CFG_SCIEN) == 0) {
358 		/* No PCI IRQ */
359 		printf(": SMI");
360 	} else {
361 		/* Install interrupt handler */
362 		if (pci_intr_map(pa, &ih) == 0) {
363 			intrstr = pci_intr_string(pa->pa_pc, ih);
364 			sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
365 			    viapm_intr, sc, DEVNAME(sc));
366 			if (sc->sc_ih != NULL) {
367 				printf(": %s", intrstr);
368 				sc->sc_poll = 0;
369 			}
370 		}
371 		if (sc->sc_poll)
372 			printf(": polling");
373 	}
374 
375 	printf("\n");
376 
377 	/* Attach I2C bus */
378 	rw_init(&sc->sc_i2c_lock, "iiclk");
379 	sc->sc_i2c_tag.ic_cookie = sc;
380 	sc->sc_i2c_tag.ic_acquire_bus = viapm_i2c_acquire_bus;
381 	sc->sc_i2c_tag.ic_release_bus = viapm_i2c_release_bus;
382 	sc->sc_i2c_tag.ic_exec = viapm_i2c_exec;
383 
384 	bzero(&iba, sizeof iba);
385 	iba.iba_name = "iic";
386 	iba.iba_tag = &sc->sc_i2c_tag;
387 	config_found(self, &iba, iicbus_print);
388 
389 nosmb:
390 
391 	/* Power management */
392 	switch (PCI_PRODUCT(pa->pa_id)) {
393 	case PCI_PRODUCT_VIATECH_VT82C596:
394 	case PCI_PRODUCT_VIATECH_VT82C596B_PM:
395 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
396 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
397 		basereg = VIAPM_PM_BASE1;
398 		cfgreg = VIAPM_PM_CFG1;
399 		break;
400 	default:
401 		basereg = VIAPM_PM_BASE2;
402 		cfgreg = VIAPM_PM_CFG2;
403 	}
404 
405 	/* Check if power management I/O space is enabled */
406 	conf = pci_conf_read(pa->pa_pc, pa->pa_tag, cfgreg);
407 	if ((conf & VIAPM_PM_CFG_PMEN) == 0) {
408 		printf("%s: PM disabled\n", DEVNAME(sc));
409 		goto nopm;
410 	}
411 
412 	/* Map power management I/O space */
413 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, basereg);
414 	iobase &= VIAPM_PM_BASE_MASK;
415 	if (iobase == 0 || bus_space_map(sc->sc_iot, iobase,
416 	    VIAPM_PM_SIZE, 0, &sc->sc_pm_ioh)) {
417 		/* XXX can't map PM i/o space if ACPI mode */
418 		DPRINTF("%s: can't map PM i/o space\n", DEVNAME(sc));
419 		goto nopm;
420 	}
421 
422 	/* Check for 32-bit PM timer */
423 	if (conf & VIAPM_PM_CFG_TMR32)
424 		viapm_timecounter.tc_counter_mask = 0xffffffff;
425 
426 	/* Register new timecounter */
427 	viapm_timecounter.tc_priv = sc;
428 	tc_init(&viapm_timecounter);
429 
430 	printf("%s: %s-bit timer at %lluHz\n", DEVNAME(sc),
431 	    (viapm_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
432 	    (unsigned long long)viapm_timecounter.tc_frequency);
433 
434 nopm:
435 
436 	/* HWMon */
437 	switch (PCI_PRODUCT(pa->pa_id)) {
438 	case PCI_PRODUCT_VIATECH_VT82C686A_SMB:
439 	case PCI_PRODUCT_VIATECH_VT8231_PWR:
440 		break;
441 	default:
442 		return;
443 	}
444 
445 	/* Check if HWMon I/O space is enabled */
446 	conf = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_HWMON_CFG);
447 	if ((conf & VIAPM_HWMON_CFG_HWEN) == 0) {
448 		printf("%s: HWM disabled\n", DEVNAME(sc));
449 		return;
450 	}
451 
452 	/* Map HWMon I/O space */
453 	iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_HWMON_BASE);
454 	iobase &= VIAPM_HWMON_BASE_MASK;
455 	if (iobase == 0 || bus_space_map(sc->sc_iot, iobase,
456 	    VIAPM_HWMON_SIZE, 0, &sc->sc_hwmon_ioh)) {
457 		printf("%s: can't map HWM i/o space\n", DEVNAME(sc));
458 		return;
459 	}
460 
461 	v = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_FANCONF);
462 
463 	sc->sc_fan_div[0] = 1 << ((v >> 4) & 0x3);
464 	sc->sc_fan_div[1] = 1 << ((v >> 6) & 0x3);
465 
466 	for (i = 0; i <= 2; i++)
467 		sc->sc_data[i].type = SENSOR_TEMP;
468 	for (i = 3; i <= 4; i++)
469 		sc->sc_data[i].type = SENSOR_FANRPM;
470 	for (i = 5; i <= 9; ++i)
471 		sc->sc_data[i].type = SENSOR_VOLTS_DC;
472 
473 	strlcpy(sc->sc_data[5].desc, "VSENS1",
474 	    sizeof(sc->sc_data[5].desc));	/* CPU core (2V) */
475 	strlcpy(sc->sc_data[6].desc, "VSENS2",
476 	    sizeof(sc->sc_data[6].desc));	/* NB core? (2.5V) */
477 	strlcpy(sc->sc_data[7].desc, "Vcore",
478 	    sizeof(sc->sc_data[7].desc));	/* Vcore (3.3V) */
479 	strlcpy(sc->sc_data[8].desc, "VSENS3",
480 	    sizeof(sc->sc_data[8].desc));	/* VSENS3 (5V) */
481 	strlcpy(sc->sc_data[9].desc, "VSENS4",
482 	    sizeof(sc->sc_data[9].desc));	/* VSENS4 (12V) */
483 
484 	/* Get initial set of sensor values. */
485 	viapm_refresh_sensor_data(sc);
486 
487 	/* Register sensors with sysctl */
488 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
489 	    sizeof(sc->sc_sensordev.xname));
490 	for (i = 0; i < VIAPM_NUM_SENSORS; ++i)
491 		sensor_attach(&sc->sc_sensordev, &sc->sc_data[i]);
492 	sensordev_install(&sc->sc_sensordev);
493 
494 	/* Refresh sensors data every 1.5 seconds */
495 	timeout_set(&viapm_timeout, viapm_refresh, sc);
496 	timeout_add_msec(&viapm_timeout, 1500);
497 }
498 
499 int
500 viapm_i2c_acquire_bus(void *cookie, int flags)
501 {
502 	struct viapm_softc *sc = cookie;
503 
504 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
505 		return (0);
506 
507 	return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
508 }
509 
510 void
511 viapm_i2c_release_bus(void *cookie, int flags)
512 {
513 	struct viapm_softc *sc = cookie;
514 
515 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
516 		return;
517 
518 	rw_exit(&sc->sc_i2c_lock);
519 }
520 
521 int
522 viapm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
523     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
524 {
525 	struct viapm_softc *sc = cookie;
526 	u_int8_t *b;
527 	u_int8_t ctl, st;
528 	int retries;
529 
530 	/* Check if there's a transfer already running */
531 	st = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HS);
532 	DPRINTF("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
533 	    "flags 0x%x, status 0x%b\n", DEVNAME(sc), op, addr,
534 	    cmdlen, len, flags, st, VIAPM_SMB_HS_BITS);
535 	if (st & VIAPM_SMB_HS_BUSY)
536 		return (1);
537 
538 	if (cold || sc->sc_poll)
539 		flags |= I2C_F_POLL;
540 
541 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
542 		return (1);
543 
544 	/* Setup transfer */
545 	sc->sc_i2c_xfer.op = op;
546 	sc->sc_i2c_xfer.buf = buf;
547 	sc->sc_i2c_xfer.len = len;
548 	sc->sc_i2c_xfer.flags = flags;
549 	sc->sc_i2c_xfer.error = 0;
550 
551 	/* Set slave address and transfer direction */
552 	bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_TXSLVA,
553 	    VIAPM_SMB_TXSLVA_ADDR(addr) |
554 	    (I2C_OP_READ_P(op) ? VIAPM_SMB_TXSLVA_READ : 0));
555 
556 	b = (void *)cmdbuf;
557 	if (cmdlen > 0)
558 		/* Set command byte */
559 		bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh,
560 		    VIAPM_SMB_HCMD, b[0]);
561 
562 	if (I2C_OP_WRITE_P(op)) {
563 		/* Write data */
564 		b = buf;
565 		if (len > 0)
566 			bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh,
567 			    VIAPM_SMB_HD0, b[0]);
568 		if (len > 1)
569 			bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh,
570 			    VIAPM_SMB_HD1, b[1]);
571 	}
572 
573 	/* Set SMBus command */
574 	if (len == 0)
575 		ctl = VIAPM_SMB_HC_CMD_BYTE;
576 	else if (len == 1)
577 		ctl = VIAPM_SMB_HC_CMD_BDATA;
578 	else if (len == 2)
579 		ctl = VIAPM_SMB_HC_CMD_WDATA;
580 	else
581 		panic("%s: unexpected len %zd", __func__, len);
582 
583 	if ((flags & I2C_F_POLL) == 0)
584 		ctl |= VIAPM_SMB_HC_INTREN;
585 
586 	/* Start transaction */
587 	ctl |= VIAPM_SMB_HC_START;
588 	bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HC, ctl);
589 
590 	if (flags & I2C_F_POLL) {
591 		/* Poll for completion */
592 		DELAY(VIAPM_SMBUS_DELAY);
593 		for (retries = 1000; retries > 0; retries--) {
594 			st = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh,
595 			    VIAPM_SMB_HS);
596 			if ((st & VIAPM_SMB_HS_BUSY) == 0)
597 				break;
598 			DELAY(VIAPM_SMBUS_DELAY);
599 		}
600 		if (st & VIAPM_SMB_HS_BUSY)
601 			goto timeout;
602 		viapm_intr(sc);
603 	} else {
604 		/* Wait for interrupt */
605 		if (tsleep_nsec(sc, PRIBIO, "iicexec",
606 		    SEC_TO_NSEC(VIAPM_SMBUS_TIMEOUT)))
607 			goto timeout;
608 	}
609 
610 	if (sc->sc_i2c_xfer.error)
611 		return (1);
612 
613 	return (0);
614 
615 timeout:
616 	/*
617 	 * Transfer timeout. Kill the transaction and clear status bits.
618 	 */
619 	printf("%s: timeout, status 0x%b\n", DEVNAME(sc), st,
620 	    VIAPM_SMB_HS_BITS);
621 	bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HC,
622 	    VIAPM_SMB_HC_KILL);
623 	DELAY(VIAPM_SMBUS_DELAY);
624 	st = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HS);
625 	if ((st & VIAPM_SMB_HS_FAILED) == 0)
626 		printf("%s: transaction abort failed, status 0x%b\n",
627 		    DEVNAME(sc), st, VIAPM_SMB_HS_BITS);
628 	bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HS, st);
629 	return (1);
630 }
631 
632 int
633 viapm_intr(void *arg)
634 {
635 	struct viapm_softc *sc = arg;
636 	u_int8_t st;
637 	u_int8_t *b;
638 	size_t len;
639 
640 	/* Read status */
641 	st = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HS);
642 	if ((st & VIAPM_SMB_HS_BUSY) != 0 || (st & (VIAPM_SMB_HS_INTR |
643 	    VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
644 	    VIAPM_SMB_HS_FAILED)) == 0)
645 		/* Interrupt was not for us */
646 		return (0);
647 
648 	DPRINTF("%s: intr st 0x%b\n", DEVNAME(sc), st, VIAPM_SMB_HS_BITS);
649 
650 	/* Clear status bits */
651 	bus_space_write_1(sc->sc_iot, sc->sc_smbus_ioh, VIAPM_SMB_HS, st);
652 
653 	/* Check for errors */
654 	if (st & (VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
655 	    VIAPM_SMB_HS_FAILED)) {
656 		sc->sc_i2c_xfer.error = 1;
657 		goto done;
658 	}
659 
660 	if (st & VIAPM_SMB_HS_INTR) {
661 		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
662 			goto done;
663 
664 		/* Read data */
665 		b = sc->sc_i2c_xfer.buf;
666 		len = sc->sc_i2c_xfer.len;
667 		if (len > 0)
668 			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh,
669 			    VIAPM_SMB_HD0);
670 		if (len > 1)
671 			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_smbus_ioh,
672 			    VIAPM_SMB_HD1);
673 	}
674 
675 done:
676 	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
677 		wakeup(sc);
678 	return (1);
679 }
680 
681 int
682 val_to_uK(unsigned int val)
683 {
684 	int i = val / 4;
685 	int j = val % 4;
686 
687 	KASSERT(i >= 0 && i <= 255);
688 
689 	if (j == 0 || i == 255)
690 		return val_to_temp[i] * 10000;
691 
692 	/* is linear interpolation ok? */
693 	return (val_to_temp[i] * (4 - j) +
694 	    val_to_temp[i + 1] * j) * 2500 /* really: / 4 * 10000 */ ;
695 }
696 
697 int
698 val_to_rpm(unsigned int val, int div)
699 {
700 	if (val == 0)
701 		return 0;
702 
703 	return 1350000 / val / div;
704 }
705 
706 long
707 val_to_uV(unsigned int val, int index)
708 {
709 	static const long mult[] =
710 	    {1250000, 1250000, 1670000, 2600000, 6300000};
711 
712 	KASSERT(index >= 0 && index <= 4);
713 
714 	return (25LL * val + 133) * mult[index] / 2628;
715 }
716 
717 void
718 viapm_refresh_sensor_data(struct viapm_softc *sc)
719 {
720 	int i;
721 	u_int8_t v, v2;
722 
723 	/* temperature */
724 	v = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_TIRQ);
725 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_TSENS1);
726 	DPRINTF("%s: TSENS1 = %d\n", DEVNAME(sc), (v2 << 2) | (v >> 6));
727 	sc->sc_data[0].value = val_to_uK((v2 << 2) | (v >> 6));
728 
729 	v = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_TLOW);
730 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_TSENS2);
731 	DPRINTF("%s: TSENS2 = %d\n", DEVNAME(sc), (v2 << 2) | ((v >> 4) & 0x3));
732 	sc->sc_data[1].value = val_to_uK((v2 << 2) | ((v >> 4) & 0x3));
733 
734 	v2 = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh, VIAPM_HWMON_TSENS3);
735 	DPRINTF("%s: TSENS3 = %d\n", DEVNAME(sc), (v2 << 2) | (v >> 6));
736 	sc->sc_data[2].value = val_to_uK((v2 << 2) | (v >> 6));
737 
738 	/* fan */
739 	for (i = 3; i <= 4; i++) {
740 		v = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh,
741 		    VIAPM_HWMON_FAN1 + i - 3);
742 		DPRINTF("%s: FAN%d = %d / %d\n", DEVNAME(sc), i - 3, v,
743 		    sc->sc_fan_div[i - 3]);
744 		sc->sc_data[i].value = val_to_rpm(v, sc->sc_fan_div[i - 3]);
745 	}
746 
747 	/* voltage */
748 	for (i = 5; i <= 9; i++) {
749 		v = bus_space_read_1(sc->sc_iot, sc->sc_hwmon_ioh,
750 		    VIAPM_HWMON_VSENS1 + i - 5);
751 		DPRINTF("%s: V%d = %d\n", DEVNAME(sc), i - 5, v);
752 		sc->sc_data[i].value = val_to_uV(v, i - 5);
753 	}
754 }
755 
756 void
757 viapm_refresh(void *arg)
758 {
759 	struct viapm_softc *sc = (struct viapm_softc *)arg;
760 
761 	viapm_refresh_sensor_data(sc);
762 	timeout_add_msec(&viapm_timeout, 1500);
763 }
764 
765 u_int
766 viapm_get_timecount(struct timecounter *tc)
767 {
768 	struct viapm_softc *sc = tc->tc_priv;
769 	u_int u1, u2, u3;
770 
771 	u2 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAPM_PM_TMR);
772 	u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAPM_PM_TMR);
773 	do {
774 		u1 = u2;
775 		u2 = u3;
776 		u3 = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, VIAPM_PM_TMR);
777 	} while (u1 > u2 || u2 > u3);
778 
779 	return (u2);
780 }
781