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
viapm_match(struct device * parent,void * match,void * aux)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
viapm_attach(struct device * parent,struct device * self,void * aux)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
viapm_i2c_acquire_bus(void * cookie,int flags)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
viapm_i2c_release_bus(void * cookie,int flags)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
viapm_i2c_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)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
viapm_intr(void * arg)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
val_to_uK(unsigned int val)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
val_to_rpm(unsigned int val,int div)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
val_to_uV(unsigned int val,int index)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
viapm_refresh_sensor_data(struct viapm_softc * sc)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
viapm_refresh(void * arg)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
viapm_get_timecount(struct timecounter * tc)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