1/*	$NetBSD: dbcool.c,v 1.34 2011/08/18 02:08:06 christos Exp $ */
2
3/*-
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Goyette
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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * a driver for the dbCool(tm) family of environmental controllers
34 *
35 * Data sheets for the various supported chips are available at
36 *
37 *	http://www.onsemi.com/pub/Collateral/ADM1027-D.PDF
38 *	http://www.onsemi.com/pub/Collateral/ADM1030-D.PDF
39 *	http://www.onsemi.com/pub/Collateral/ADT7463-D.PDF
40 *	http://www.onsemi.com/pub/Collateral/ADT7466.PDF
41 *	http://www.onsemi.com/pub/Collateral/ADT7467-D.PDF
42 *	http://www.onsemi.com/pub/Collateral/ADT7468-D.PDF
43 *	http://www.onsemi.com/pub/Collateral/ADT7473-D.PDF
44 *	http://www.onsemi.com/pub/Collateral/ADT7475-D.PDF
45 *	http://www.onsemi.com/pub/Collateral/ADT7476-D.PDF
46 *	http://www.onsemi.com/pub/Collateral/ADT7490-D.PDF
47 *	http://www.smsc.com/media/Downloads_Public/Data_Sheets/6d103s.pdf
48 *
49 * (URLs are correct as of October 5, 2008)
50 */
51
52#include <sys/cdefs.h>
53__KERNEL_RCSID(0, "$NetBSD: dbcool.c,v 1.34 2011/08/18 02:08:06 christos Exp $");
54
55#include <sys/param.h>
56#include <sys/systm.h>
57#include <sys/kernel.h>
58#include <sys/device.h>
59#include <sys/malloc.h>
60#include <sys/sysctl.h>
61#include <sys/module.h>
62
63#include <dev/i2c/dbcool_var.h>
64#include <dev/i2c/dbcool_reg.h>
65
66/* Config interface */
67static int dbcool_match(device_t, cfdata_t, void *);
68static void dbcool_attach(device_t, device_t, void *);
69static int dbcool_detach(device_t, int);
70
71/* Device attributes */
72static int dbcool_supply_voltage(struct dbcool_softc *);
73static bool dbcool_islocked(struct dbcool_softc *);
74
75/* Sensor read functions */
76static void dbcool_refresh(struct sysmon_envsys *, envsys_data_t *);
77static int dbcool_read_rpm(struct dbcool_softc *, uint8_t);
78static int dbcool_read_temp(struct dbcool_softc *, uint8_t, bool);
79static int dbcool_read_volt(struct dbcool_softc *, uint8_t, int, bool);
80
81/* Sensor get/set limit functions */
82static void dbcool_get_limits(struct sysmon_envsys *, envsys_data_t *,
83			      sysmon_envsys_lim_t *, uint32_t *);
84static void dbcool_get_temp_limits(struct dbcool_softc *, int,
85				   sysmon_envsys_lim_t *, uint32_t *);
86static void dbcool_get_volt_limits(struct dbcool_softc *, int,
87				   sysmon_envsys_lim_t *, uint32_t *);
88static void dbcool_get_fan_limits(struct dbcool_softc *, int,
89				  sysmon_envsys_lim_t *, uint32_t *);
90
91static void dbcool_set_limits(struct sysmon_envsys *, envsys_data_t *,
92			      sysmon_envsys_lim_t *, uint32_t *);
93static void dbcool_set_temp_limits(struct dbcool_softc *, int,
94				   sysmon_envsys_lim_t *, uint32_t *);
95static void dbcool_set_volt_limits(struct dbcool_softc *, int,
96				   sysmon_envsys_lim_t *, uint32_t *);
97static void dbcool_set_fan_limits(struct dbcool_softc *, int,
98				  sysmon_envsys_lim_t *, uint32_t *);
99
100/* SYSCTL Helpers */
101SYSCTL_SETUP_PROTO(sysctl_dbcoolsetup);
102static int sysctl_dbcool_temp(SYSCTLFN_PROTO);
103static int sysctl_adm1030_temp(SYSCTLFN_PROTO);
104static int sysctl_adm1030_trange(SYSCTLFN_PROTO);
105static int sysctl_dbcool_duty(SYSCTLFN_PROTO);
106static int sysctl_dbcool_behavior(SYSCTLFN_PROTO);
107static int sysctl_dbcool_slope(SYSCTLFN_PROTO);
108static int sysctl_dbcool_thyst(SYSCTLFN_PROTO);
109
110/* Set-up subroutines */
111static void dbcool_setup_controllers(struct dbcool_softc *);
112static int  dbcool_setup_sensors(struct dbcool_softc *);
113static int  dbcool_attach_sensor(struct dbcool_softc *, int);
114static int  dbcool_attach_temp_control(struct dbcool_softc *, int,
115	struct chip_id *);
116
117#ifdef DBCOOL_DEBUG
118static int sysctl_dbcool_reg_select(SYSCTLFN_PROTO);
119static int sysctl_dbcool_reg_access(SYSCTLFN_PROTO);
120#endif /* DBCOOL_DEBUG */
121
122/*
123 * Descriptions for SYSCTL entries
124 */
125struct dbc_sysctl_info {
126	const char *name;
127	const char *desc;
128	bool lockable;
129	int (*helper)(SYSCTLFN_PROTO);
130};
131
132static struct dbc_sysctl_info dbc_sysctl_table[] = {
133	/*
134	 * The first several entries must remain in the same order as the
135	 * corresponding entries in enum dbc_pwm_params
136	 */
137	{ "behavior",		"operating behavior and temp selector",
138		true, sysctl_dbcool_behavior },
139	{ "min_duty",		"minimum fan controller PWM duty cycle",
140		true, sysctl_dbcool_duty },
141	{ "max_duty",		"maximum fan controller PWM duty cycle",
142		true, sysctl_dbcool_duty },
143	{ "cur_duty",		"current fan controller PWM duty cycle",
144		false, sysctl_dbcool_duty },
145
146	/*
147	 * The rest of these should be in the order in which they
148	 * are to be stored in the sysctl tree;  the table index is
149	 * used as the high-order bits of the sysctl_num to maintain
150	 * the sequence.
151	 *
152	 * If you rearrange the order of these items, be sure to
153	 * update the sysctl_index in the XXX_sensor_table[] for
154	 * the various chips!
155	 */
156	{ "Trange",		"temp slope/range to reach 100% duty cycle",
157		true, sysctl_dbcool_slope },
158	{ "Tmin",		"temp at which to start fan controller",
159		true, sysctl_dbcool_temp },
160	{ "Ttherm",		"temp at which THERM is asserted",
161		true, sysctl_dbcool_temp },
162	{ "Thyst",		"temp hysteresis for stopping fan controller",
163		true, sysctl_dbcool_thyst },
164	{ "Tmin",		"temp at which to start fan controller",
165		true, sysctl_adm1030_temp },
166	{ "Trange",		"temp slope/range to reach 100% duty cycle",
167		true, sysctl_adm1030_trange },
168};
169
170static const char *dbc_sensor_names[] = {
171	"l_temp",  "r1_temp", "r2_temp", "Vccp",   "Vcc",    "fan1",
172	"fan2",    "fan3",    "fan4",    "AIN1",   "AIN2",   "V2dot5",
173	"V5",      "V12",     "Vtt",     "Imon",   "VID"
174};
175
176/*
177 * Following table derived from product data-sheets
178 */
179static int64_t nominal_voltages[] = {
180	-1,		/* Vcc can be either 3.3 or 5.0V
181			   at 3/4 scale                  */
182	 2249939,	/* Vccp         2.25V 3/4 scale  */
183	 2497436,	/* 2.5VIN       2.5V  3/4 scale  */
184	 5002466,	/* 5VIN         5V    3/4 scale  */
185	12000000,	/* 12VIN       12V    3/4 scale  */
186	 1690809,	/* Vtt, Imon    2.25V full scale */
187	 1689600,	/* AIN1, AIN2   2.25V full scale */
188	       0
189};
190
191/*
192 * Sensor-type, { val-reg, hilim-reg, lolim-reg}, name-idx, sysctl-table-idx,
193 *	nom-voltage-index
194 */
195struct dbcool_sensor ADT7490_sensor_table[] = {
196	{ DBC_TEMP, {	DBCOOL_LOCAL_TEMP,
197			DBCOOL_LOCAL_HIGHLIM,
198			DBCOOL_LOCAL_LOWLIM },		0, 0, 0 },
199	{ DBC_TEMP, {	DBCOOL_REMOTE1_TEMP,
200			DBCOOL_REMOTE1_HIGHLIM,
201			DBCOOL_REMOTE1_LOWLIM },	1, 0, 0 },
202	{ DBC_TEMP, {	DBCOOL_REMOTE2_TEMP,
203			DBCOOL_REMOTE2_HIGHLIM,
204			DBCOOL_REMOTE2_LOWLIM },	2, 0, 0 },
205	{ DBC_VOLT, {	DBCOOL_VCCP,
206			DBCOOL_VCCP_HIGHLIM,
207			DBCOOL_VCCP_LOWLIM },		3, 0, 1 },
208	{ DBC_VOLT, {	DBCOOL_VCC,
209			DBCOOL_VCC_HIGHLIM,
210			DBCOOL_VCC_LOWLIM },		4, 0, 0 },
211	{ DBC_VOLT, {	DBCOOL_25VIN,
212			DBCOOL_25VIN_HIGHLIM,
213			DBCOOL_25VIN_LOWLIM },		11, 0, 2 },
214	{ DBC_VOLT, {	DBCOOL_5VIN,
215			DBCOOL_5VIN_HIGHLIM,
216			DBCOOL_5VIN_LOWLIM },		12, 0, 3 },
217	{ DBC_VOLT, {	DBCOOL_12VIN,
218			DBCOOL_12VIN_HIGHLIM,
219			DBCOOL_12VIN_LOWLIM },		13, 0, 4 },
220	{ DBC_VOLT, {	DBCOOL_VTT,
221			DBCOOL_VTT_HIGHLIM,
222			DBCOOL_VTT_LOWLIM },		14, 0, 5 },
223	{ DBC_VOLT, {	DBCOOL_IMON,
224			DBCOOL_IMON_HIGHLIM,
225			DBCOOL_IMON_LOWLIM },		15, 0, 5 },
226	{ DBC_FAN,  {	DBCOOL_FAN1_TACH_LSB,
227			DBCOOL_NO_REG,
228			DBCOOL_TACH1_MIN_LSB },		5, 0, 0 },
229	{ DBC_FAN,  {	DBCOOL_FAN2_TACH_LSB,
230			DBCOOL_NO_REG,
231			DBCOOL_TACH2_MIN_LSB },		6, 0, 0 },
232	{ DBC_FAN,  {	DBCOOL_FAN3_TACH_LSB,
233			DBCOOL_NO_REG,
234			DBCOOL_TACH3_MIN_LSB },		7, 0, 0 },
235	{ DBC_FAN,  {	DBCOOL_FAN4_TACH_LSB,
236			DBCOOL_NO_REG,
237			DBCOOL_TACH4_MIN_LSB },		8, 0, 0 },
238	{ DBC_VID,  {	DBCOOL_VID_REG,
239			DBCOOL_NO_REG,
240			DBCOOL_NO_REG },		16, 0, 0 },
241	{ DBC_CTL,  {	DBCOOL_LOCAL_TMIN,
242			DBCOOL_NO_REG,
243			DBCOOL_NO_REG },		0, 5, 0 },
244	{ DBC_CTL,  {	DBCOOL_LOCAL_TTHRESH,
245			DBCOOL_NO_REG,
246			DBCOOL_NO_REG },		0, 6, 0 },
247	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST | 0x80,
248			DBCOOL_NO_REG,
249			DBCOOL_NO_REG },		0, 7, 0 },
250	{ DBC_CTL,  {	DBCOOL_REMOTE1_TMIN,
251			DBCOOL_NO_REG,
252			DBCOOL_NO_REG },		1, 5, 0 },
253	{ DBC_CTL,  {	DBCOOL_REMOTE1_TTHRESH,
254			DBCOOL_NO_REG,
255			DBCOOL_NO_REG },		1, 6, 0 },
256	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST,
257			DBCOOL_NO_REG,
258			DBCOOL_NO_REG },		1, 7, 0 },
259	{ DBC_CTL,  {	DBCOOL_REMOTE2_TMIN,
260			DBCOOL_NO_REG,
261			DBCOOL_NO_REG },		2, 5, 0 },
262	{ DBC_CTL,  {	DBCOOL_REMOTE2_TTHRESH,
263			DBCOOL_NO_REG,
264			DBCOOL_NO_REG },		2, 6, 0 },
265	{ DBC_CTL,  {	DBCOOL_R2_TMIN_HYST,
266			DBCOOL_NO_REG,
267			DBCOOL_NO_REG },		2, 7, 0 },
268	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
269};
270
271struct dbcool_sensor ADT7476_sensor_table[] = {
272	{ DBC_TEMP, {	DBCOOL_LOCAL_TEMP,
273			DBCOOL_LOCAL_HIGHLIM,
274			DBCOOL_LOCAL_LOWLIM },		0, 0, 0 },
275	{ DBC_TEMP, {	DBCOOL_REMOTE1_TEMP,
276			DBCOOL_REMOTE1_HIGHLIM,
277			DBCOOL_REMOTE1_LOWLIM },	1, 0, 0 },
278	{ DBC_TEMP, {	DBCOOL_REMOTE2_TEMP,
279			DBCOOL_REMOTE2_HIGHLIM,
280			DBCOOL_REMOTE2_LOWLIM },	2, 0, 0 },
281	{ DBC_VOLT, {	DBCOOL_VCCP,
282			DBCOOL_VCCP_HIGHLIM,
283			DBCOOL_VCCP_LOWLIM },		3, 0, 1 },
284	{ DBC_VOLT, {	DBCOOL_VCC,
285			DBCOOL_VCC_HIGHLIM,
286			DBCOOL_VCC_LOWLIM },		4, 0, 0 },
287	{ DBC_VOLT, {	DBCOOL_25VIN,
288			DBCOOL_25VIN_HIGHLIM,
289			DBCOOL_25VIN_LOWLIM },		11, 0, 2 },
290	{ DBC_VOLT, {	DBCOOL_5VIN,
291			DBCOOL_5VIN_HIGHLIM,
292			DBCOOL_5VIN_LOWLIM },		12, 0, 3 },
293	{ DBC_VOLT, {	DBCOOL_12VIN,
294			DBCOOL_12VIN_HIGHLIM,
295			DBCOOL_12VIN_LOWLIM },		13, 0, 4 },
296	{ DBC_FAN,  {	DBCOOL_FAN1_TACH_LSB,
297			DBCOOL_NO_REG,
298			DBCOOL_TACH1_MIN_LSB },		5, 0, 0 },
299	{ DBC_FAN,  {	DBCOOL_FAN2_TACH_LSB,
300			DBCOOL_NO_REG,
301			DBCOOL_TACH2_MIN_LSB },		6, 0, 0 },
302	{ DBC_FAN,  {	DBCOOL_FAN3_TACH_LSB,
303			DBCOOL_NO_REG,
304			DBCOOL_TACH3_MIN_LSB },		7, 0, 0 },
305	{ DBC_FAN,  {	DBCOOL_FAN4_TACH_LSB,
306			DBCOOL_NO_REG,
307			DBCOOL_TACH4_MIN_LSB },		8, 0, 0 },
308	{ DBC_VID,  {	DBCOOL_VID_REG,
309			DBCOOL_NO_REG,
310			DBCOOL_NO_REG },		16, 0, 0 },
311	{ DBC_CTL,  {	DBCOOL_LOCAL_TMIN,
312			DBCOOL_NO_REG,
313			DBCOOL_NO_REG },		0, 5, 0 },
314	{ DBC_CTL,  {	DBCOOL_LOCAL_TTHRESH,
315			DBCOOL_NO_REG,
316			DBCOOL_NO_REG },		0, 6, 0 },
317	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST | 0x80,
318			DBCOOL_NO_REG,
319			DBCOOL_NO_REG },		0, 7, 0 },
320	{ DBC_CTL,  {	DBCOOL_REMOTE1_TMIN,
321			DBCOOL_NO_REG,
322			DBCOOL_NO_REG },		1, 5, 0 },
323	{ DBC_CTL,  {	DBCOOL_REMOTE1_TTHRESH,
324			DBCOOL_NO_REG,
325			DBCOOL_NO_REG },		1, 6, 0 },
326	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST,
327			DBCOOL_NO_REG,
328			DBCOOL_NO_REG },		1, 7, 0 },
329	{ DBC_CTL,  {	DBCOOL_REMOTE2_TMIN,
330			DBCOOL_NO_REG,
331			DBCOOL_NO_REG },		2, 5, 0 },
332	{ DBC_CTL,  {	DBCOOL_REMOTE2_TTHRESH,
333			DBCOOL_NO_REG,
334			DBCOOL_NO_REG },		2, 6, 0 },
335	{ DBC_CTL,  {	DBCOOL_R2_TMIN_HYST,
336			DBCOOL_NO_REG,
337			DBCOOL_NO_REG },		2, 7, 0 },
338	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
339};
340
341struct dbcool_sensor ADT7475_sensor_table[] = {
342	{ DBC_TEMP, {	DBCOOL_LOCAL_TEMP,
343			DBCOOL_LOCAL_HIGHLIM,
344			DBCOOL_LOCAL_LOWLIM },		0, 0, 0 },
345	{ DBC_TEMP, {	DBCOOL_REMOTE1_TEMP,
346			DBCOOL_REMOTE1_HIGHLIM,
347			DBCOOL_REMOTE1_LOWLIM },	1, 0, 0 },
348	{ DBC_TEMP, {	DBCOOL_REMOTE2_TEMP,
349			DBCOOL_REMOTE2_HIGHLIM,
350			DBCOOL_REMOTE2_LOWLIM },	2, 0, 0 },
351	{ DBC_VOLT, {	DBCOOL_VCCP,
352			DBCOOL_VCCP_HIGHLIM,
353			DBCOOL_VCCP_LOWLIM },		3, 0, 1 },
354	{ DBC_VOLT, {	DBCOOL_VCC,
355			DBCOOL_VCC_HIGHLIM,
356			DBCOOL_VCC_LOWLIM },		4, 0, 0 },
357	{ DBC_FAN,  {	DBCOOL_FAN1_TACH_LSB,
358			DBCOOL_NO_REG,
359			DBCOOL_TACH1_MIN_LSB },		5, 0, 0 },
360	{ DBC_FAN,  {	DBCOOL_FAN2_TACH_LSB,
361			DBCOOL_NO_REG,
362			DBCOOL_TACH2_MIN_LSB },		6, 0, 0 },
363	{ DBC_FAN,  {	DBCOOL_FAN3_TACH_LSB,
364			DBCOOL_NO_REG,
365			DBCOOL_TACH3_MIN_LSB },		7, 0, 0 },
366	{ DBC_FAN,  {	DBCOOL_FAN4_TACH_LSB,
367			DBCOOL_NO_REG,
368			DBCOOL_TACH4_MIN_LSB },		8, 0, 0 },
369	{ DBC_CTL,  {	DBCOOL_LOCAL_TMIN,
370			DBCOOL_NO_REG,
371			DBCOOL_NO_REG },		0, 5, 0 },
372	{ DBC_CTL,  {	DBCOOL_LOCAL_TTHRESH,
373			DBCOOL_NO_REG,
374			DBCOOL_NO_REG },		0, 6, 0 },
375	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST | 0x80,
376			DBCOOL_NO_REG,
377			DBCOOL_NO_REG },		0, 7, 0 },
378	{ DBC_CTL,  {	DBCOOL_REMOTE1_TMIN,
379			DBCOOL_NO_REG,
380			DBCOOL_NO_REG },		1, 5, 0 },
381	{ DBC_CTL,  {	DBCOOL_REMOTE1_TTHRESH,
382			DBCOOL_NO_REG,
383			DBCOOL_NO_REG },		1, 6, 0 },
384	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST,
385			DBCOOL_NO_REG,
386			DBCOOL_NO_REG },		1, 7, 0 },
387	{ DBC_CTL,  {	DBCOOL_REMOTE2_TMIN,
388			DBCOOL_NO_REG,
389			DBCOOL_NO_REG },		2, 5, 0 },
390	{ DBC_CTL,  {	DBCOOL_REMOTE2_TTHRESH,
391			DBCOOL_NO_REG,
392			DBCOOL_NO_REG },		2, 6, 0 },
393	{ DBC_CTL,  {	DBCOOL_R2_TMIN_HYST,
394			DBCOOL_NO_REG,
395			DBCOOL_NO_REG },		2, 7, 0 },
396	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
397};
398
399/*
400 * The registers of dbcool_power_control must be in the same order as
401 * in enum dbc_pwm_params
402 */
403struct dbcool_power_control ADT7475_power_table[] = {
404	{ { DBCOOL_PWM1_CTL, DBCOOL_PWM1_MINDUTY,
405	    DBCOOL_PWM1_MAXDUTY, DBCOOL_PWM1_CURDUTY },
406		"fan_control_1" },
407	{ { DBCOOL_PWM2_CTL, DBCOOL_PWM2_MINDUTY,
408	    DBCOOL_PWM2_MAXDUTY, DBCOOL_PWM2_CURDUTY },
409		"fan_control_2" },
410	{ { DBCOOL_PWM3_CTL, DBCOOL_PWM3_MINDUTY,
411	    DBCOOL_PWM3_MAXDUTY, DBCOOL_PWM3_CURDUTY },
412		"fan_control_3" },
413	{ { 0, 0, 0, 0 }, NULL }
414};
415
416struct dbcool_sensor ADT7466_sensor_table[] = {
417	{ DBC_TEMP, {	DBCOOL_ADT7466_LCL_TEMP_MSB,
418			DBCOOL_ADT7466_LCL_TEMP_HILIM,
419			DBCOOL_ADT7466_LCL_TEMP_LOLIM }, 0,  0, 0 },
420	{ DBC_TEMP, {	DBCOOL_ADT7466_REM_TEMP_MSB,
421			DBCOOL_ADT7466_REM_TEMP_HILIM,
422			DBCOOL_ADT7466_REM_TEMP_LOLIM }, 1,  0, 0 },
423	{ DBC_VOLT, {	DBCOOL_ADT7466_VCC,
424			DBCOOL_ADT7466_VCC_HILIM,
425			DBCOOL_ADT7466_VCC_LOLIM },	4,  0, 0 },
426	{ DBC_VOLT, {	DBCOOL_ADT7466_AIN1,
427			DBCOOL_ADT7466_AIN1_HILIM,
428			DBCOOL_ADT7466_AIN1_LOLIM },	9,  0, 6 },
429	{ DBC_VOLT, {	DBCOOL_ADT7466_AIN2,
430			DBCOOL_ADT7466_AIN2_HILIM,
431			DBCOOL_ADT7466_AIN2_LOLIM },	10, 0, 6 },
432	{ DBC_FAN,  {	DBCOOL_ADT7466_FANA_LSB,
433			DBCOOL_NO_REG,
434			DBCOOL_ADT7466_FANA_LOLIM_LSB }, 5,  0, 0 },
435	{ DBC_FAN,  {	DBCOOL_ADT7466_FANB_LSB,
436			DBCOOL_NO_REG,
437			DBCOOL_ADT7466_FANB_LOLIM_LSB }, 6,  0, 0 },
438	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
439};
440
441struct dbcool_sensor ADM1027_sensor_table[] = {
442	{ DBC_TEMP, {	DBCOOL_LOCAL_TEMP,
443			DBCOOL_LOCAL_HIGHLIM,
444			DBCOOL_LOCAL_LOWLIM },		0, 0, 0 },
445	{ DBC_TEMP, {	DBCOOL_REMOTE1_TEMP,
446			DBCOOL_REMOTE1_HIGHLIM,
447			DBCOOL_REMOTE1_LOWLIM },	1, 0, 0 },
448	{ DBC_TEMP, {	DBCOOL_REMOTE2_TEMP,
449			DBCOOL_REMOTE2_HIGHLIM,
450			DBCOOL_REMOTE2_LOWLIM },	2, 0, 0 },
451	{ DBC_VOLT, {	DBCOOL_VCCP,
452			DBCOOL_VCCP_HIGHLIM,
453			DBCOOL_VCCP_LOWLIM },		3, 0, 1 },
454	{ DBC_VOLT, {	DBCOOL_VCC,
455			DBCOOL_VCC_HIGHLIM,
456			DBCOOL_VCC_LOWLIM },		4, 0, 0 },
457	{ DBC_VOLT, {	DBCOOL_25VIN,
458			DBCOOL_25VIN_HIGHLIM,
459			DBCOOL_25VIN_LOWLIM },		11, 0, 2 },
460	{ DBC_VOLT, {	DBCOOL_5VIN,
461			DBCOOL_5VIN_HIGHLIM,
462			DBCOOL_5VIN_LOWLIM },		12, 0, 3 },
463	{ DBC_VOLT, {	DBCOOL_12VIN,
464			DBCOOL_12VIN_HIGHLIM,
465			DBCOOL_12VIN_LOWLIM },		13, 0, 4 },
466	{ DBC_FAN,  {	DBCOOL_FAN1_TACH_LSB,
467			DBCOOL_NO_REG,
468			DBCOOL_TACH1_MIN_LSB },		5, 0, 0 },
469	{ DBC_FAN,  {	DBCOOL_FAN2_TACH_LSB,
470			DBCOOL_NO_REG,
471			DBCOOL_TACH2_MIN_LSB },		6, 0, 0 },
472	{ DBC_FAN,  {	DBCOOL_FAN3_TACH_LSB,
473			DBCOOL_NO_REG,
474			DBCOOL_TACH3_MIN_LSB },		7, 0, 0 },
475	{ DBC_FAN,  {	DBCOOL_FAN4_TACH_LSB,
476			DBCOOL_NO_REG,
477			DBCOOL_TACH4_MIN_LSB },		8, 0, 0 },
478	{ DBC_VID,  {	DBCOOL_VID_REG,
479			DBCOOL_NO_REG,
480			DBCOOL_NO_REG },		16, 0, 0 },
481	{ DBC_CTL,  {	DBCOOL_LOCAL_TMIN,
482			DBCOOL_NO_REG,
483			DBCOOL_NO_REG },		0, 5, 0 },
484	{ DBC_CTL,  {	DBCOOL_LOCAL_TTHRESH,
485			DBCOOL_NO_REG,
486			DBCOOL_NO_REG },		0, 6, 0 },
487	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST | 0x80,
488			DBCOOL_NO_REG,
489			DBCOOL_NO_REG },		0, 7, 0 },
490	{ DBC_CTL,  {	DBCOOL_REMOTE1_TMIN,
491			DBCOOL_NO_REG,
492			DBCOOL_NO_REG },		1, 5, 0 },
493	{ DBC_CTL,  {	DBCOOL_REMOTE1_TTHRESH,
494			DBCOOL_NO_REG,
495			DBCOOL_NO_REG },		1, 6, 0 },
496	{ DBC_CTL,  {	DBCOOL_R1_LCL_TMIN_HYST,
497			DBCOOL_NO_REG,
498			DBCOOL_NO_REG },		1, 7, 0 },
499	{ DBC_CTL,  {	DBCOOL_REMOTE2_TMIN,
500			DBCOOL_NO_REG,
501			DBCOOL_NO_REG },		2, 5, 0 },
502	{ DBC_CTL,  {	DBCOOL_REMOTE2_TTHRESH,
503			DBCOOL_NO_REG,
504			DBCOOL_NO_REG },		2, 6, 0 },
505	{ DBC_CTL,  {	DBCOOL_R2_TMIN_HYST,
506			DBCOOL_NO_REG,
507			DBCOOL_NO_REG },		2, 7, 0 },
508	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
509};
510
511struct dbcool_sensor ADM1030_sensor_table[] = {
512	{ DBC_TEMP, {	DBCOOL_ADM1030_L_TEMP,
513			DBCOOL_ADM1030_L_HI_LIM,
514			DBCOOL_ADM1030_L_LO_LIM },	0,  0, 0 },
515	{ DBC_TEMP, {	DBCOOL_ADM1030_R_TEMP,
516			DBCOOL_ADM1030_R_HI_LIM,
517			DBCOOL_ADM1030_R_LO_LIM },	1,  0, 0 },
518	{ DBC_FAN,  {	DBCOOL_ADM1030_FAN_TACH,
519			DBCOOL_NO_REG,
520			DBCOOL_ADM1030_FAN_LO_LIM },	5,  0, 0 },
521	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TMIN,
522			DBCOOL_NO_REG,
523			DBCOOL_NO_REG },		0,  8, 0 },
524	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TTHRESH,
525			DBCOOL_NO_REG,
526			DBCOOL_NO_REG },		0,  9, 0 },
527	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TTHRESH,
528			DBCOOL_NO_REG,
529			DBCOOL_NO_REG },		0,  6, 0 },
530	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TMIN,
531			DBCOOL_NO_REG,
532			DBCOOL_NO_REG },		1,  8, 0 },
533	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TTHRESH,
534			DBCOOL_NO_REG,
535			DBCOOL_NO_REG },		1,  9, 0 },
536	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TTHRESH,
537			DBCOOL_NO_REG,
538			DBCOOL_NO_REG },		1,  6, 0 },
539	{ DBC_EOF,  {0, 0, 0 }, 0, 0, 0 }
540};
541
542struct dbcool_power_control ADM1030_power_table[] = {
543	{ { DBCOOL_ADM1030_CFG1,  DBCOOL_NO_REG, DBCOOL_NO_REG,
544	    DBCOOL_ADM1030_FAN_SPEED_CFG },
545	  "fan_control_1" },
546	{ { 0, 0, 0, 0 }, NULL }
547};
548
549struct dbcool_sensor ADM1031_sensor_table[] = {
550	{ DBC_TEMP, {	DBCOOL_ADM1030_L_TEMP,
551			DBCOOL_ADM1030_L_HI_LIM,
552			DBCOOL_ADM1030_L_LO_LIM },	0,  0, 0 },
553	{ DBC_TEMP, {	DBCOOL_ADM1030_R_TEMP,
554			DBCOOL_ADM1030_R_HI_LIM,
555			DBCOOL_ADM1030_R_LO_LIM },	1,  0, 0 },
556	{ DBC_TEMP, {	DBCOOL_ADM1031_R2_TEMP,
557			DBCOOL_ADM1031_R2_HI_LIM,
558			DBCOOL_ADM1031_R2_LO_LIM },	2,  0, 0 },
559	{ DBC_FAN,  {	DBCOOL_ADM1030_FAN_TACH,
560			DBCOOL_NO_REG,
561			DBCOOL_ADM1030_FAN_LO_LIM },	5,  0, 0 },
562	{ DBC_FAN,  {	DBCOOL_ADM1031_FAN2_TACH,
563			DBCOOL_NO_REG,
564			DBCOOL_ADM1031_FAN2_LO_LIM },	6,  0, 0 },
565	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TMIN,
566			DBCOOL_NO_REG,
567			DBCOOL_NO_REG },		0,  8, 0 },
568	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TTHRESH,
569			DBCOOL_NO_REG,
570			DBCOOL_NO_REG },		0,  9, 0 },
571	{ DBC_CTL,  {	DBCOOL_ADM1030_L_TTHRESH,
572			DBCOOL_NO_REG,
573			DBCOOL_NO_REG },		0,  6, 0 },
574	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TMIN,
575			DBCOOL_NO_REG,
576			DBCOOL_NO_REG },		1,  8, 0 },
577	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TTHRESH,
578			DBCOOL_NO_REG,
579			DBCOOL_NO_REG },		1,  9, 0 },
580	{ DBC_CTL,  {	DBCOOL_ADM1030_R_TTHRESH,
581			DBCOOL_NO_REG,
582			DBCOOL_NO_REG },		1,  6, 0 },
583	{ DBC_CTL,  {	DBCOOL_ADM1031_R2_TMIN,
584			DBCOOL_NO_REG,
585			DBCOOL_NO_REG },		2,  8, 0 },
586	{ DBC_CTL,  {	DBCOOL_ADM1031_R2_TTHRESH,
587			DBCOOL_NO_REG,
588			DBCOOL_NO_REG },		2,  9, 0 },
589	{ DBC_CTL,  {	DBCOOL_ADM1031_R2_TTHRESH,
590			DBCOOL_NO_REG,
591			DBCOOL_NO_REG },		2,  6, 0 },
592	{ DBC_EOF,  {0, 0, 0 }, 0, 0, 0 }
593};
594
595struct dbcool_power_control ADM1031_power_table[] = {
596	{ { DBCOOL_ADM1030_CFG1,  DBCOOL_NO_REG, DBCOOL_NO_REG,
597	    DBCOOL_ADM1030_FAN_SPEED_CFG },
598	  "fan_control_1" },
599	{ { DBCOOL_ADM1030_CFG1,  DBCOOL_NO_REG, DBCOOL_NO_REG,
600	    DBCOOL_ADM1030_FAN_SPEED_CFG },
601	  "fan_control_2" },
602	{ { 0, 0, 0, 0 }, NULL }
603};
604
605struct dbcool_sensor EMC6D103S_sensor_table[] = {
606	{ DBC_TEMP, {	DBCOOL_LOCAL_TEMP,
607			DBCOOL_LOCAL_HIGHLIM,
608			DBCOOL_LOCAL_LOWLIM },		0, 0, 0 },
609	{ DBC_TEMP, {	DBCOOL_REMOTE1_TEMP,
610			DBCOOL_REMOTE1_HIGHLIM,
611			DBCOOL_REMOTE1_LOWLIM },	1, 0, 0 },
612	{ DBC_TEMP, {	DBCOOL_REMOTE2_TEMP,
613			DBCOOL_REMOTE2_HIGHLIM,
614			DBCOOL_REMOTE2_LOWLIM },	2, 0, 0 },
615	{ DBC_VOLT, {	DBCOOL_VCCP,
616			DBCOOL_VCCP_HIGHLIM,
617			DBCOOL_VCCP_LOWLIM },		3, 0, 1 },
618	{ DBC_VOLT, {	DBCOOL_VCC,
619			DBCOOL_VCC_HIGHLIM,
620			DBCOOL_VCC_LOWLIM },		4, 0, 0 },
621	{ DBC_VOLT, {	DBCOOL_25VIN,
622			DBCOOL_25VIN_HIGHLIM,
623			DBCOOL_25VIN_LOWLIM },		11, 0, 2 },
624	{ DBC_VOLT, {	DBCOOL_5VIN,
625			DBCOOL_5VIN_HIGHLIM,
626			DBCOOL_5VIN_LOWLIM },		12, 0, 3 },
627	{ DBC_VOLT, {	DBCOOL_12VIN,
628			DBCOOL_12VIN_HIGHLIM,
629			DBCOOL_12VIN_LOWLIM },		13, 0, 4 },
630	{ DBC_FAN,  {	DBCOOL_FAN1_TACH_LSB,
631			DBCOOL_NO_REG,
632			DBCOOL_TACH1_MIN_LSB },		5, 0, 0 },
633	{ DBC_FAN,  {	DBCOOL_FAN2_TACH_LSB,
634			DBCOOL_NO_REG,
635			DBCOOL_TACH2_MIN_LSB },		6, 0, 0 },
636	{ DBC_FAN,  {	DBCOOL_FAN3_TACH_LSB,
637			DBCOOL_NO_REG,
638			DBCOOL_TACH3_MIN_LSB },		7, 0, 0 },
639	{ DBC_FAN,  {	DBCOOL_FAN4_TACH_LSB,
640			DBCOOL_NO_REG,
641			DBCOOL_TACH4_MIN_LSB },		8, 0, 0 },
642	{ DBC_VID,  {	DBCOOL_VID_REG,
643			DBCOOL_NO_REG,
644			DBCOOL_NO_REG },		16, 0, 0 },
645	{ DBC_CTL,  {	DBCOOL_LOCAL_TMIN,
646			DBCOOL_NO_REG,
647			DBCOOL_NO_REG },		0, 5, 0 },
648	{ DBC_CTL,  {	DBCOOL_LOCAL_TTHRESH,
649			DBCOOL_NO_REG,
650			DBCOOL_NO_REG },		0, 6, 0 },
651	{ DBC_CTL,  {	DBCOOL_REMOTE1_TMIN,
652			DBCOOL_NO_REG,
653			DBCOOL_NO_REG },		1, 5, 0 },
654	{ DBC_CTL,  {	DBCOOL_REMOTE1_TTHRESH,
655			DBCOOL_NO_REG,
656			DBCOOL_NO_REG },		1, 6, 0 },
657	{ DBC_CTL,  {	DBCOOL_REMOTE2_TMIN,
658			DBCOOL_NO_REG,
659			DBCOOL_NO_REG },		2, 5, 0 },
660	{ DBC_CTL,  {	DBCOOL_REMOTE2_TTHRESH,
661			DBCOOL_NO_REG,
662			DBCOOL_NO_REG },		2, 6, 0 },
663	{ DBC_EOF,  { 0, 0, 0 }, 0, 0, 0 }
664};
665
666struct chip_id chip_table[] = {
667	{ DBCOOL_COMPANYID, ADT7490_DEVICEID, ADT7490_REV_ID,
668		ADT7490_sensor_table, ADT7475_power_table,
669		DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_PECI,
670		90000 * 60, "ADT7490" },
671	{ DBCOOL_COMPANYID, ADT7476_DEVICEID, 0xff,
672		ADT7476_sensor_table, ADT7475_power_table,
673		DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY,
674		90000 * 60, "ADT7476" },
675	{ DBCOOL_COMPANYID, ADT7475_DEVICEID, 0xff,
676		ADT7475_sensor_table, ADT7475_power_table,
677		DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN,
678		90000 * 60, "ADT7475" },
679	{ DBCOOL_COMPANYID, ADT7473_DEVICEID, ADT7473_REV_ID1,
680		ADT7475_sensor_table, ADT7475_power_table,
681		DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN,
682		90000 * 60, "ADT7460/ADT7463" },
683	{ DBCOOL_COMPANYID, ADT7473_DEVICEID, ADT7473_REV_ID2,
684		ADT7475_sensor_table, ADT7475_power_table,
685		DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN,
686		90000 * 60, "ADT7463-1" },
687	{ DBCOOL_COMPANYID, ADT7468_DEVICEID, 0xff,
688		ADT7476_sensor_table, ADT7475_power_table,
689		DBCFLAG_TEMPOFFSET  | DBCFLAG_MULTI_VCC | DBCFLAG_HAS_MAXDUTY |
690		    DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN,
691		90000 * 60, "ADT7467/ADT7468" },
692	{ DBCOOL_COMPANYID, ADT7466_DEVICEID, 0xff,
693		ADT7466_sensor_table, NULL,
694		DBCFLAG_ADT7466 | DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_SHDN,
695		82000 * 60, "ADT7466" },
696	{ DBCOOL_COMPANYID, ADT7463_DEVICEID, ADT7463_REV_ID1,
697		ADM1027_sensor_table, ADT7475_power_table,
698		DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN,
699		90000 * 60, "ADT7463" },
700	{ DBCOOL_COMPANYID, ADT7463_DEVICEID, ADT7463_REV_ID2,
701		ADM1027_sensor_table, ADT7475_power_table,
702		DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN |
703		    DBCFLAG_HAS_VID_SEL,
704		90000 * 60, "ADT7463" },
705	{ DBCOOL_COMPANYID, ADM1027_DEVICEID, ADM1027_REV_ID,
706		ADM1027_sensor_table, ADT7475_power_table,
707		DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER,
708		90000 * 60, "ADM1027" },
709	{ DBCOOL_COMPANYID, ADM1030_DEVICEID, 0xff,
710		ADM1030_sensor_table, ADM1030_power_table,
711		DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE,
712		11250 * 60, "ADM1030" },
713	{ DBCOOL_COMPANYID, ADM1031_DEVICEID, 0xff,
714		ADM1031_sensor_table, ADM1030_power_table,
715		DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE,
716		11250 * 60, "ADM1031" },
717	{ SMSC_COMPANYID, EMC6D103S_DEVICEID, EMC6D103S_REV_ID,
718		EMC6D103S_sensor_table, ADT7475_power_table,
719		DBCFLAG_4BIT_VER,
720		90000 * 60, "EMC6D103S" },
721	{ 0, 0, 0, NULL, NULL, 0, 0, NULL }
722};
723
724static const char *behavior[] = {
725	"remote1",	"local",	"remote2",	"full-speed",
726	"disabled",	"local+remote2","all-temps",	"manual"
727};
728
729static char dbcool_cur_behav[16];
730
731CFATTACH_DECL_NEW(dbcool, sizeof(struct dbcool_softc),
732    dbcool_match, dbcool_attach, dbcool_detach, NULL);
733
734int
735dbcool_match(device_t parent, cfdata_t cf, void *aux)
736{
737	struct i2c_attach_args *ia = aux;
738	struct dbcool_chipset dc;
739	dc.dc_tag = ia->ia_tag;
740	dc.dc_addr = ia->ia_addr;
741	dc.dc_chip = NULL;
742	dc.dc_readreg = dbcool_readreg;
743	dc.dc_writereg = dbcool_writereg;
744
745	/* no probing if we attach to iic, but verify chip id  and address */
746	if ((ia->ia_addr & DBCOOL_ADDRMASK) != DBCOOL_ADDR)
747		return 0;
748	if (dbcool_chip_ident(&dc) >= 0)
749		return 1;
750
751	return 0;
752}
753
754void
755dbcool_attach(device_t parent, device_t self, void *aux)
756{
757	struct dbcool_softc *sc = device_private(self);
758	struct i2c_attach_args *args = aux;
759	uint8_t ver;
760
761	sc->sc_dc.dc_addr = args->ia_addr;
762	sc->sc_dc.dc_tag = args->ia_tag;
763	sc->sc_dc.dc_chip = NULL;
764	sc->sc_dc.dc_readreg = dbcool_readreg;
765	sc->sc_dc.dc_writereg = dbcool_writereg;
766	(void)dbcool_chip_ident(&sc->sc_dc);
767	sc->sc_dev = self;
768
769	aprint_naive("\n");
770	aprint_normal("\n");
771
772	ver = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_REVISION_REG);
773	if (sc->sc_dc.dc_chip->flags & DBCFLAG_4BIT_VER)
774	        if (sc->sc_dc.dc_chip->company == SMSC_COMPANYID)
775	        {
776		        aprint_normal_dev(self, "SMSC %s Controller "
777			        "(rev 0x%02x, stepping 0x%02x)\n", sc->sc_dc.dc_chip->name,
778        			ver >> 4, ver & 0x0f);
779	        } else {
780		        aprint_normal_dev(self, "%s dBCool(tm) Controller "
781			        "(rev 0x%02x, stepping 0x%02x)\n", sc->sc_dc.dc_chip->name,
782        			ver >> 4, ver & 0x0f);
783                }
784	else
785		aprint_normal_dev(self, "%s dBCool(tm) Controller "
786			"(rev 0x%04x)\n", sc->sc_dc.dc_chip->name, ver);
787
788	sc->sc_sysctl_log = NULL;
789
790#ifdef _MODULE
791	sysctl_dbcoolsetup(&sc->sc_sysctl_log);
792#endif
793
794	dbcool_setup(self);
795
796	if (!pmf_device_register(self, dbcool_pmf_suspend, dbcool_pmf_resume))
797		aprint_error_dev(self, "couldn't establish power handler\n");
798}
799
800static int
801dbcool_detach(device_t self, int flags)
802{
803	struct dbcool_softc *sc = device_private(self);
804
805	pmf_device_deregister(self);
806
807	sysmon_envsys_unregister(sc->sc_sme);
808
809	sysctl_teardown(&sc->sc_sysctl_log);
810
811	sc->sc_sme = NULL;
812	return 0;
813}
814
815/* On suspend, we save the state of the SHDN bit, then set it */
816bool dbcool_pmf_suspend(device_t dev, const pmf_qual_t *qual)
817{
818	struct dbcool_softc *sc = device_private(dev);
819	uint8_t reg, bit, cfg;
820
821	if ((sc->sc_dc.dc_chip->flags && DBCFLAG_HAS_SHDN) == 0)
822		return true;
823
824	if (sc->sc_dc.dc_chip->flags && DBCFLAG_ADT7466) {
825		reg = DBCOOL_ADT7466_CONFIG2;
826		bit = DBCOOL_ADT7466_CFG2_SHDN;
827	} else {
828		reg = DBCOOL_CONFIG2_REG;
829		bit = DBCOOL_CFG2_SHDN;
830	}
831	cfg = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
832	sc->sc_suspend = cfg & bit;
833	cfg |= bit;
834	sc->sc_dc.dc_writereg(&sc->sc_dc, reg, cfg);
835
836	return true;
837}
838
839/* On resume, we restore the previous state of the SHDN bit */
840bool dbcool_pmf_resume(device_t dev, const pmf_qual_t *qual)
841{
842	struct dbcool_softc *sc = device_private(dev);
843	uint8_t reg, bit, cfg;
844
845	if ((sc->sc_dc.dc_chip->flags && DBCFLAG_HAS_SHDN) == 0)
846		return true;
847
848	if (sc->sc_dc.dc_chip->flags && DBCFLAG_ADT7466) {
849		reg = DBCOOL_ADT7466_CONFIG2;
850		bit = DBCOOL_ADT7466_CFG2_SHDN;
851	} else {
852		reg = DBCOOL_CONFIG2_REG;
853		bit = DBCOOL_CFG2_SHDN;
854	}
855	cfg = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
856	cfg &= ~sc->sc_suspend;
857	sc->sc_dc.dc_writereg(&sc->sc_dc, reg, cfg);
858
859	return true;
860
861}
862
863uint8_t
864dbcool_readreg(struct dbcool_chipset *dc, uint8_t reg)
865{
866	uint8_t data = 0;
867
868	if (iic_acquire_bus(dc->dc_tag, 0) != 0)
869		return data;
870
871	if (dc->dc_chip == NULL || dc->dc_chip->flags & DBCFLAG_NO_READBYTE) {
872		/* ADM1027 doesn't support i2c read_byte protocol */
873		if (iic_smbus_send_byte(dc->dc_tag, dc->dc_addr, reg, 0) != 0)
874			goto bad;
875		(void)iic_smbus_receive_byte(dc->dc_tag, dc->dc_addr, &data, 0);
876	} else
877		(void)iic_smbus_read_byte(dc->dc_tag, dc->dc_addr, reg, &data,
878					  0);
879
880bad:
881	iic_release_bus(dc->dc_tag, 0);
882	return data;
883}
884
885void
886dbcool_writereg(struct dbcool_chipset *dc, uint8_t reg, uint8_t val)
887{
888	if (iic_acquire_bus(dc->dc_tag, 0) != 0)
889		return;
890
891	(void)iic_smbus_write_byte(dc->dc_tag, dc->dc_addr, reg, val, 0);
892
893	iic_release_bus(dc->dc_tag, 0);
894}
895
896static bool
897dbcool_islocked(struct dbcool_softc *sc)
898{
899	uint8_t cfg_reg;
900
901	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030)
902		return 0;
903
904	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466)
905		cfg_reg = DBCOOL_ADT7466_CONFIG1;
906	else
907		cfg_reg = DBCOOL_CONFIG1_REG;
908
909	if (sc->sc_dc.dc_readreg(&sc->sc_dc, cfg_reg) & DBCOOL_CFG1_LOCK)
910		return 1;
911	else
912		return 0;
913}
914
915static int
916dbcool_read_temp(struct dbcool_softc *sc, uint8_t reg, bool extres)
917{
918	uint8_t	t1, t2, t3, val, ext = 0;
919	int temp;
920
921	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466) {
922		/*
923		 * ADT7466 temps are in strange location
924		 */
925		ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADT7466_CONFIG1);
926		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
927		if (extres)
928			ext = sc->sc_dc.dc_readreg(&sc->sc_dc, reg + 1);
929	} else if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030) {
930		/*
931		 * ADM1030 temps are in their own special place, too
932		 */
933		if (extres) {
934			ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADM1030_TEMP_EXTRES);
935			if (reg == DBCOOL_ADM1030_L_TEMP)
936				ext >>= 6;
937			else if (reg == DBCOOL_ADM1031_R2_TEMP)
938				ext >>= 4;
939			else
940				ext >>= 1;
941			ext &= 0x03;
942		}
943		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
944	} else if (extres) {
945		ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_EXTRES2_REG);
946
947		/* Read all msb regs to unlatch them */
948		t1 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_12VIN);
949		t1 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_REMOTE1_TEMP);
950		t2 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_REMOTE2_TEMP);
951		t3 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_LOCAL_TEMP);
952		switch (reg) {
953		case DBCOOL_REMOTE1_TEMP:
954			val = t1;
955			ext >>= 2;
956			break;
957		case DBCOOL_LOCAL_TEMP:
958			val = t3;
959			ext >>= 4;
960			break;
961		case DBCOOL_REMOTE2_TEMP:
962			val = t2;
963			ext >>= 6;
964			break;
965		default:
966			val = 0;
967			break;
968		}
969		ext &= 0x03;
970	}
971	else
972		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
973
974	/* Check for invalid temp values */
975	if ((sc->sc_temp_offset == 0 && val == 0x80) ||
976	    (sc->sc_temp_offset != 0 && val == 0))
977		return 0;
978
979	/* If using offset mode, adjust, else treat as signed */
980	if (sc->sc_temp_offset) {
981		temp = val;
982		temp -= sc->sc_temp_offset;
983	} else
984		temp = (int8_t)val;
985
986	/* Convert degC to uK and include extended precision bits */
987	temp *= 1000000;
988	temp +=  250000 * (int)ext;
989	temp += 273150000U;
990
991	return temp;
992}
993
994static int
995dbcool_read_rpm(struct dbcool_softc *sc, uint8_t reg)
996{
997	int rpm;
998	uint8_t rpm_lo, rpm_hi;
999
1000	rpm_lo = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
1001	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030)
1002		rpm_hi = (rpm_lo == 0xff)?0xff:0x0;
1003	else
1004		rpm_hi = sc->sc_dc.dc_readreg(&sc->sc_dc, reg + 1);
1005
1006	rpm = (rpm_hi << 8) | rpm_lo;
1007	if (rpm == 0xffff)
1008		return 0;	/* 0xffff indicates stalled/failed fan */
1009
1010	/* don't divide by zero */
1011	return (rpm == 0)? 0 : (sc->sc_dc.dc_chip->rpm_dividend / rpm);
1012}
1013
1014/* Provide chip's supply voltage, in microvolts */
1015static int
1016dbcool_supply_voltage(struct dbcool_softc *sc)
1017{
1018	if (sc->sc_dc.dc_chip->flags & DBCFLAG_MULTI_VCC) {
1019		if (sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_CONFIG1_REG) & DBCOOL_CFG1_Vcc)
1020			return 5002500;
1021		else
1022			return 3300000;
1023	} else if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466) {
1024		if (sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADT7466_CONFIG1) &
1025			    DBCOOL_ADT7466_CFG1_Vcc)
1026			return 5000000;
1027		else
1028			return 3300000;
1029	} else
1030		return 3300000;
1031}
1032
1033/*
1034 * Nominal voltages are calculated in microvolts
1035 */
1036static int
1037dbcool_read_volt(struct dbcool_softc *sc, uint8_t reg, int nom_idx, bool extres)
1038{
1039	uint8_t ext = 0, v1, v2, v3, v4, val;
1040	int64_t ret;
1041	int64_t nom;
1042
1043	nom = nominal_voltages[nom_idx];
1044	if (nom < 0)
1045		nom = sc->sc_supply_voltage;
1046
1047	/* ADT7466 voltages are in strange locations with only 8-bits */
1048	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466)
1049		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
1050	else
1051	/*
1052	 * It's a "normal" dbCool chip - check for regs that
1053	 * share extended resolution bits since we have to
1054	 * read all the MSB registers to unlatch them.
1055	 */
1056	if (!extres)
1057		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
1058	else if (reg == DBCOOL_12VIN) {
1059		ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_EXTRES2_REG) && 0x03;
1060		val = sc->sc_dc.dc_readreg(&sc->sc_dc, reg);
1061		(void)dbcool_read_temp(sc, DBCOOL_LOCAL_TEMP, true);
1062	} else if (reg == DBCOOL_VTT || reg == DBCOOL_IMON) {
1063		ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_EXTRES_VTT_IMON);
1064		v1 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_IMON);
1065		v2 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_VTT);
1066		if (reg == DBCOOL_IMON) {
1067			val = v1;
1068			ext >>= 6;
1069		} else
1070			val = v2;
1071			ext >>= 4;
1072		ext &= 0x0f;
1073	} else {
1074		ext = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_EXTRES1_REG);
1075		v1 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_25VIN);
1076		v2 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_VCCP);
1077		v3 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_VCC);
1078		v4 = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_5VIN);
1079
1080		switch (reg) {
1081		case DBCOOL_25VIN:
1082			val = v1;
1083			break;
1084		case DBCOOL_VCCP:
1085			val = v2;
1086			ext >>= 2;
1087			break;
1088		case DBCOOL_VCC:
1089			val = v3;
1090			ext >>= 4;
1091			break;
1092		case DBCOOL_5VIN:
1093			val = v4;
1094			ext >>= 6;
1095			break;
1096		default:
1097			val = nom = 0;
1098		}
1099		ext &= 0x03;
1100	}
1101
1102	/*
1103	 * Scale the nominal value by the 10-bit fraction
1104	 *
1105	 * Returned value is in microvolts.
1106	 */
1107	ret = val;
1108	ret <<= 2;
1109	ret |= ext;
1110	ret = (ret * nom) / 0x300;
1111
1112	return ret;
1113}
1114
1115SYSCTL_SETUP(sysctl_dbcoolsetup, "sysctl dBCool subtree setup")
1116{
1117	sysctl_createv(clog, 0, NULL, NULL,
1118		       CTLFLAG_PERMANENT,
1119		       CTLTYPE_NODE, "hw", NULL,
1120		       NULL, 0, NULL, 0,
1121		       CTL_HW, CTL_EOL);
1122}
1123
1124static int
1125sysctl_dbcool_temp(SYSCTLFN_ARGS)
1126{
1127	struct sysctlnode node;
1128	struct dbcool_softc *sc;
1129	int reg, error;
1130	uint8_t chipreg;
1131	uint8_t newreg;
1132
1133	node = *rnode;
1134	sc = (struct dbcool_softc *)node.sysctl_data;
1135	chipreg = node.sysctl_num & 0xff;
1136
1137	if (sc->sc_temp_offset) {
1138		reg = sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1139		reg -= sc->sc_temp_offset;
1140	} else
1141		reg = (int8_t)sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1142
1143	node.sysctl_data = &reg;
1144	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1145
1146	if (error || newp == NULL)
1147		return error;
1148
1149	/* We were asked to update the value - sanity check before writing */
1150	if (*(int *)node.sysctl_data < -64 ||
1151	    *(int *)node.sysctl_data > 127 + sc->sc_temp_offset)
1152		return EINVAL;
1153
1154	newreg = *(int *)node.sysctl_data;
1155	newreg += sc->sc_temp_offset;
1156	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1157	return 0;
1158}
1159
1160static int
1161sysctl_adm1030_temp(SYSCTLFN_ARGS)
1162{
1163	struct sysctlnode node;
1164	struct dbcool_softc *sc;
1165	int reg, error;
1166	uint8_t chipreg, oldreg, newreg;
1167
1168	node = *rnode;
1169	sc = (struct dbcool_softc *)node.sysctl_data;
1170	chipreg = node.sysctl_num & 0xff;
1171
1172	oldreg = (int8_t)sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1173	reg = (oldreg >> 1) & ~0x03;
1174
1175	node.sysctl_data = &reg;
1176	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1177
1178	if (error || newp == NULL)
1179		return error;
1180
1181	/* We were asked to update the value - sanity check before writing */
1182	if (*(int *)node.sysctl_data < 0 || *(int *)node.sysctl_data > 127)
1183		return EINVAL;
1184
1185	newreg = *(int *)node.sysctl_data;
1186	newreg &= ~0x03;
1187	newreg <<= 1;
1188	newreg |= (oldreg & 0x07);
1189	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1190	return 0;
1191}
1192
1193static int
1194sysctl_adm1030_trange(SYSCTLFN_ARGS)
1195{
1196	struct sysctlnode node;
1197	struct dbcool_softc *sc;
1198	int reg, error, newval;
1199	uint8_t chipreg, oldreg, newreg;
1200
1201	node = *rnode;
1202	sc = (struct dbcool_softc *)node.sysctl_data;
1203	chipreg = node.sysctl_num & 0xff;
1204
1205	oldreg = (int8_t)sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1206	reg = oldreg & 0x07;
1207
1208	node.sysctl_data = &reg;
1209	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1210
1211	if (error || newp == NULL)
1212		return error;
1213
1214	/* We were asked to update the value - sanity check before writing */
1215	newval = *(int *)node.sysctl_data;
1216
1217	if (newval == 5)
1218		newreg = 0;
1219	else if (newval == 10)
1220		newreg = 1;
1221	else if (newval == 20)
1222		newreg = 2;
1223	else if (newval == 40)
1224		newreg = 3;
1225	else if (newval == 80)
1226		newreg = 4;
1227	else
1228		return EINVAL;
1229
1230	newreg |= (oldreg & ~0x07);
1231	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1232	return 0;
1233}
1234
1235static int
1236sysctl_dbcool_duty(SYSCTLFN_ARGS)
1237{
1238	struct sysctlnode node;
1239	struct dbcool_softc *sc;
1240	int reg, error;
1241	uint8_t chipreg, oldreg, newreg;
1242
1243	node = *rnode;
1244	sc = (struct dbcool_softc *)node.sysctl_data;
1245	chipreg = node.sysctl_num & 0xff;
1246
1247	oldreg = sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1248	reg = (uint32_t)oldreg;
1249	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030)
1250		reg = ((reg & 0x0f) * 100) / 15;
1251	else
1252		reg = (reg * 100) / 255;
1253	node.sysctl_data = &reg;
1254	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1255
1256	if (error || newp == NULL)
1257		return error;
1258
1259	/* We were asked to update the value - sanity check before writing */
1260	if (*(int *)node.sysctl_data < 0 || *(int *)node.sysctl_data > 100)
1261		return EINVAL;
1262
1263	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030) {
1264		newreg = *(uint8_t *)(node.sysctl_data) * 15 / 100;
1265		newreg |= oldreg & 0xf0;
1266	} else
1267		newreg = *(uint8_t *)(node.sysctl_data) * 255 / 100;
1268	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1269	return 0;
1270}
1271
1272static int
1273sysctl_dbcool_behavior(SYSCTLFN_ARGS)
1274{
1275	struct sysctlnode node;
1276	struct dbcool_softc *sc;
1277	int i, reg, error;
1278	uint8_t chipreg, oldreg, newreg;
1279
1280	node = *rnode;
1281	sc = (struct dbcool_softc *)node.sysctl_data;
1282	chipreg = node.sysctl_num & 0xff;
1283
1284	oldreg = sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1285
1286	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030) {
1287		if ((sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADM1030_CFG2) & 1) == 0)
1288			reg = 4;
1289		else if ((oldreg & 0x80) == 0)
1290			reg = 7;
1291		else if ((oldreg & 0x60) == 0)
1292			reg = 4;
1293		else
1294			reg = 6;
1295	} else
1296		reg = (oldreg >> 5) & 0x07;
1297
1298	strlcpy(dbcool_cur_behav, behavior[reg], sizeof(dbcool_cur_behav));
1299	node.sysctl_data = dbcool_cur_behav;
1300	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1301
1302	if (error || newp == NULL)
1303		return error;
1304
1305	/* We were asked to update the value - convert string to value */
1306	newreg = __arraycount(behavior);
1307	for (i = 0; i < __arraycount(behavior); i++)
1308		if (strcmp(node.sysctl_data, behavior[i]) == 0)
1309			break;
1310	if (i >= __arraycount(behavior))
1311		return EINVAL;
1312
1313	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030) {
1314		/*
1315		 * ADM1030 splits fan controller behavior across two
1316		 * registers.  We also do not support Auto-Filter mode
1317		 * nor do we support Manual-RPM-feedback.
1318		 */
1319		if (newreg == 4) {
1320			oldreg = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADM1030_CFG2);
1321			oldreg &= ~0x01;
1322			sc->sc_dc.dc_writereg(&sc->sc_dc, DBCOOL_ADM1030_CFG2, oldreg);
1323		} else {
1324			if (newreg == 0)
1325				newreg = 4;
1326			else if (newreg == 6)
1327				newreg = 7;
1328			else if (newreg == 7)
1329				newreg = 0;
1330			else
1331				return EINVAL;
1332			newreg <<= 5;
1333			newreg |= (oldreg & 0x1f);
1334			sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1335			oldreg = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADM1030_CFG2) | 1;
1336			sc->sc_dc.dc_writereg(&sc->sc_dc, DBCOOL_ADM1030_CFG2, oldreg);
1337		}
1338	} else {
1339		newreg = (sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg) & 0x1f) | (i << 5);
1340		sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1341	}
1342	return 0;
1343}
1344
1345static int
1346sysctl_dbcool_slope(SYSCTLFN_ARGS)
1347{
1348	struct sysctlnode node;
1349	struct dbcool_softc *sc;
1350	int reg, error;
1351	uint8_t chipreg;
1352	uint8_t newreg;
1353
1354	node = *rnode;
1355	sc = (struct dbcool_softc *)node.sysctl_data;
1356	chipreg = node.sysctl_num & 0xff;
1357
1358	reg = (sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg) >> 4) & 0x0f;
1359	node.sysctl_data = &reg;
1360	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1361
1362	if (error || newp == NULL)
1363		return error;
1364
1365	/* We were asked to update the value - sanity check before writing */
1366	if (*(int *)node.sysctl_data < 0 || *(int *)node.sysctl_data > 0x0f)
1367		return EINVAL;
1368
1369	newreg = (sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg) & 0x0f) |
1370		  (*(int *)node.sysctl_data << 4);
1371	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1372	return 0;
1373}
1374
1375static int
1376sysctl_dbcool_thyst(SYSCTLFN_ARGS)
1377{
1378	struct sysctlnode node;
1379	struct dbcool_softc *sc;
1380	int reg, error;
1381	uint8_t chipreg;
1382	uint8_t newreg, newhyst;
1383
1384	node = *rnode;
1385	sc = (struct dbcool_softc *)node.sysctl_data;
1386	chipreg = node.sysctl_num & 0x7f;
1387
1388	/* retrieve 4-bit value */
1389	newreg = sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1390	if ((node.sysctl_num & 0x80) == 0)
1391		reg = newreg >> 4;
1392	else
1393		reg = newreg;
1394	reg = reg & 0x0f;
1395
1396	node.sysctl_data = &reg;
1397	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1398
1399	if (error || newp == NULL)
1400		return error;
1401
1402	/* We were asked to update the value - sanity check before writing */
1403	newhyst = *(int *)node.sysctl_data;
1404	if (newhyst > 0x0f)
1405		return EINVAL;
1406
1407	/* Insert new value into field and update register */
1408	if ((node.sysctl_num & 0x80) == 0) {
1409		newreg &= 0x0f;
1410		newreg |= (newhyst << 4);
1411	} else {
1412		newreg &= 0xf0;
1413		newreg |= newhyst;
1414	}
1415	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1416	return 0;
1417}
1418
1419#ifdef DBCOOL_DEBUG
1420
1421/*
1422 * These routines can be used for debugging.  reg_select is used to
1423 * select any arbitrary register in the device.  reg_access is used
1424 * to read (and optionally update) the selected register.
1425 *
1426 * No attempt is made to validate the data passed.  If you use these
1427 * routines, you are assumed to know what you're doing!
1428 *
1429 * Caveat user
1430 */
1431static int
1432sysctl_dbcool_reg_select(SYSCTLFN_ARGS)
1433{
1434	struct sysctlnode node;
1435	struct dbcool_softc *sc;
1436	int reg, error;
1437
1438	node = *rnode;
1439	sc = (struct dbcool_softc *)node.sysctl_data;
1440
1441	reg = sc->sc_user_reg;
1442	node.sysctl_data = &reg;
1443	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1444
1445	if (error || newp == NULL)
1446		return error;
1447
1448	sc->sc_user_reg = *(int *)node.sysctl_data;
1449	return 0;
1450}
1451
1452static int
1453sysctl_dbcool_reg_access(SYSCTLFN_ARGS)
1454{
1455	struct sysctlnode node;
1456	struct dbcool_softc *sc;
1457	int reg, error;
1458	uint8_t chipreg;
1459	uint8_t newreg;
1460
1461	node = *rnode;
1462	sc = (struct dbcool_softc *)node.sysctl_data;
1463	chipreg = sc->sc_user_reg;
1464
1465	reg = sc->sc_dc.dc_readreg(&sc->sc_dc, chipreg);
1466	node.sysctl_data = &reg;
1467	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1468
1469	if (error || newp == NULL)
1470		return error;
1471
1472	newreg = *(int *)node.sysctl_data;
1473	sc->sc_dc.dc_writereg(&sc->sc_dc, chipreg, newreg);
1474	return 0;
1475}
1476#endif /* DBCOOL_DEBUG */
1477
1478/*
1479 * Encode an index number and register number for use as a sysctl_num
1480 * so we can select the correct device register later.
1481 */
1482#define	DBC_PWM_SYSCTL(seq, reg)	((seq << 8) | reg)
1483
1484void
1485dbcool_setup(device_t self)
1486{
1487	struct dbcool_softc *sc = device_private(self);
1488	const struct sysctlnode *me = NULL;
1489#ifdef DBCOOL_DEBUG
1490	struct sysctlnode *node = NULL;
1491#endif
1492	uint8_t cfg_val, cfg_reg;
1493	int ret, error;
1494
1495	/*
1496	 * Some chips are capable of reporting an extended temperature range
1497	 * by default.  On these models, config register 5 bit 0 can be set
1498	 * to 1 for compatability with other chips that report 2s complement.
1499	 */
1500	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466) {
1501		if (sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_ADT7466_CONFIG1) & 0x80)
1502			sc->sc_temp_offset = 64;
1503		else
1504			sc->sc_temp_offset = 0;
1505	} else if (sc->sc_dc.dc_chip->flags & DBCFLAG_TEMPOFFSET) {
1506		if (sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_CONFIG5_REG) &
1507			    DBCOOL_CFG5_TWOSCOMP)
1508			sc->sc_temp_offset = 0;
1509		else
1510			sc->sc_temp_offset = 64;
1511	} else
1512		sc->sc_temp_offset = 0;
1513
1514	/* Determine Vcc for this chip */
1515	sc->sc_supply_voltage = dbcool_supply_voltage(sc);
1516
1517	ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL, &me,
1518	       CTLFLAG_READWRITE,
1519	       CTLTYPE_NODE, device_xname(self), NULL,
1520	       NULL, 0, NULL, 0,
1521	       CTL_HW, CTL_CREATE, CTL_EOL);
1522	if (ret == 0)
1523		sc->sc_root_sysctl_num = me->sysctl_num;
1524	else
1525		sc->sc_root_sysctl_num = 0;
1526
1527	aprint_debug_dev(self,
1528		"Supply voltage %"PRId64".%06"PRId64"V, %s temp range\n",
1529		sc->sc_supply_voltage / 1000000,
1530		sc->sc_supply_voltage % 1000000,
1531		sc->sc_temp_offset ? "extended" : "normal");
1532
1533	/* Create the sensors for this device */
1534	sc->sc_sme = sysmon_envsys_create();
1535	if (dbcool_setup_sensors(sc))
1536		goto out;
1537
1538	if (sc->sc_root_sysctl_num != 0) {
1539		/* If supported, create sysctl tree for fan PWM controllers */
1540		if (sc->sc_dc.dc_chip->power != NULL)
1541			dbcool_setup_controllers(sc);
1542
1543#ifdef DBCOOL_DEBUG
1544		ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL,
1545			(void *)&node,
1546			CTLFLAG_READWRITE, CTLTYPE_INT, "reg_select", NULL,
1547			sysctl_dbcool_reg_select,
1548			0, sc, sizeof(int),
1549			CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
1550		if (node != NULL)
1551			node->sysctl_data = sc;
1552
1553		ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL,
1554			(void *)&node,
1555			CTLFLAG_READWRITE, CTLTYPE_INT, "reg_access", NULL,
1556			sysctl_dbcool_reg_access,
1557			0, sc, sizeof(int),
1558			CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
1559		if (node != NULL)
1560			node->sysctl_data = sc;
1561#endif /* DBCOOL_DEBUG */
1562	}
1563
1564	/*
1565	 * Read and rewrite config register to activate device
1566	 */
1567	if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030)
1568		cfg_reg = DBCOOL_ADM1030_CFG1;
1569	else if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADT7466)
1570		cfg_reg = DBCOOL_ADT7466_CONFIG1;
1571	else
1572		cfg_reg = DBCOOL_CONFIG1_REG;
1573	cfg_val = sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_CONFIG1_REG);
1574	if ((cfg_val & DBCOOL_CFG1_START) == 0) {
1575		cfg_val |= DBCOOL_CFG1_START;
1576		sc->sc_dc.dc_writereg(&sc->sc_dc, cfg_reg, cfg_val);
1577	}
1578	if (dbcool_islocked(sc))
1579		aprint_normal_dev(self, "configuration locked\n");
1580
1581	sc->sc_sme->sme_name = device_xname(self);
1582	sc->sc_sme->sme_cookie = sc;
1583	sc->sc_sme->sme_refresh = dbcool_refresh;
1584	sc->sc_sme->sme_set_limits = dbcool_set_limits;
1585	sc->sc_sme->sme_get_limits = dbcool_get_limits;
1586
1587	if ((error = sysmon_envsys_register(sc->sc_sme)) != 0) {
1588		aprint_error_dev(self,
1589		    "unable to register with sysmon (%d)\n", error);
1590		goto out;
1591	}
1592
1593	return;
1594
1595out:
1596	sysmon_envsys_destroy(sc->sc_sme);
1597}
1598
1599static int
1600dbcool_setup_sensors(struct dbcool_softc *sc)
1601{
1602	int i;
1603	int error = 0;
1604	uint8_t	vid_reg, vid_val;
1605	struct chip_id *chip = sc->sc_dc.dc_chip;
1606
1607	for (i=0; chip->table[i].type != DBC_EOF; i++) {
1608		if (i < DBCOOL_MAXSENSORS)
1609			sc->sc_sysctl_num[i] = -1;
1610		else if (chip->table[i].type != DBC_CTL) {
1611			aprint_normal_dev(sc->sc_dev, "chip table too big!\n");
1612			break;
1613		}
1614		switch (chip->table[i].type) {
1615		case DBC_TEMP:
1616			sc->sc_sensor[i].units = ENVSYS_STEMP;
1617			sc->sc_sensor[i].state = ENVSYS_SINVALID;
1618			sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS;
1619			error = dbcool_attach_sensor(sc, i);
1620			break;
1621		case DBC_VOLT:
1622			/*
1623			 * If 12V-In pin has been reconfigured as 6th bit
1624			 * of VID code, don't create a 12V-In sensor
1625			 */
1626			if ((chip->flags & DBCFLAG_HAS_VID_SEL) &&
1627			    (chip->table[i].reg.val_reg == DBCOOL_12VIN) &&
1628			    (sc->sc_dc.dc_readreg(&sc->sc_dc, DBCOOL_VID_REG) &
1629					0x80))
1630				break;
1631
1632			sc->sc_sensor[i].units = ENVSYS_SVOLTS_DC;
1633			sc->sc_sensor[i].state = ENVSYS_SINVALID;
1634			sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS;
1635			error = dbcool_attach_sensor(sc, i);
1636			break;
1637		case DBC_FAN:
1638			sc->sc_sensor[i].units = ENVSYS_SFANRPM;
1639			sc->sc_sensor[i].state = ENVSYS_SINVALID;
1640			sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS;
1641			error = dbcool_attach_sensor(sc, i);
1642			break;
1643		case DBC_VID:
1644			sc->sc_sensor[i].units = ENVSYS_INTEGER;
1645			sc->sc_sensor[i].state = ENVSYS_SINVALID;
1646			sc->sc_sensor[i].flags |= ENVSYS_FMONNOTSUPP;
1647
1648			/* retrieve 5- or 6-bit value */
1649			vid_reg = chip->table[i].reg.val_reg;
1650			vid_val = sc->sc_dc.dc_readreg(&sc->sc_dc, vid_reg);
1651			if (chip->flags & DBCFLAG_HAS_VID_SEL)
1652				vid_val &= 0x3f;
1653			else
1654				vid_val &= 0x1f;
1655			sc->sc_sensor[i].value_cur = vid_val;
1656
1657			error = dbcool_attach_sensor(sc, i);
1658			break;
1659		case DBC_CTL:
1660			error = dbcool_attach_temp_control(sc, i, chip);
1661			if (error) {
1662				aprint_error_dev(sc->sc_dev,
1663						"attach index %d failed %d\n",
1664						i, error);
1665				error = 0;
1666			}
1667			break;
1668		default:
1669			aprint_error_dev(sc->sc_dev,
1670				"sensor_table index %d has bad type %d\n",
1671				i, chip->table[i].type);
1672			break;
1673		}
1674		if (error)
1675			break;
1676	}
1677	return error;
1678}
1679
1680static int
1681dbcool_attach_sensor(struct dbcool_softc *sc, int idx)
1682{
1683	int name_index;
1684	int error = 0;
1685
1686	name_index = sc->sc_dc.dc_chip->table[idx].name_index;
1687	strlcpy(sc->sc_sensor[idx].desc, dbc_sensor_names[name_index],
1688		sizeof(sc->sc_sensor[idx].desc));
1689	sc->sc_regs[idx] = &sc->sc_dc.dc_chip->table[idx].reg;
1690	sc->sc_nom_volt[idx] = sc->sc_dc.dc_chip->table[idx].nom_volt_index;
1691
1692	error = sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[idx]);
1693	return error;
1694}
1695
1696static int
1697dbcool_attach_temp_control(struct dbcool_softc *sc, int idx,
1698			   struct chip_id *chip)
1699{
1700	const struct sysctlnode *me2 = NULL, *node;
1701	int j, ret, sysctl_index, rw_flag;
1702	uint8_t	sysctl_reg;
1703	char name[SYSCTL_NAMELEN];
1704
1705	/* Search for the corresponding temp sensor */
1706	for (j = 0; j < idx; j++) {
1707		if (j >= DBCOOL_MAXSENSORS || chip->table[j].type != DBC_TEMP)
1708			continue;
1709		if (chip->table[j].name_index == chip->table[idx].name_index)
1710			break;
1711	}
1712	if (j >= idx)	/* Temp sensor not found */
1713		return ENOENT;
1714
1715	/* create sysctl node for the sensor if not one already there */
1716	if (sc->sc_sysctl_num[j] == -1) {
1717		ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL, &me2,
1718				     CTLFLAG_READWRITE,
1719				     CTLTYPE_NODE, sc->sc_sensor[j].desc, NULL,
1720				     NULL, 0, NULL, 0,
1721				     CTL_HW, sc->sc_root_sysctl_num, CTL_CREATE,
1722					CTL_EOL);
1723		if (me2 != NULL)
1724			sc->sc_sysctl_num[j] = me2->sysctl_num;
1725		else
1726			return ret;
1727	}
1728	/* add sysctl leaf node for this control variable */
1729	sysctl_index = chip->table[idx].sysctl_index;
1730	sysctl_reg = chip->table[idx].reg.val_reg;
1731	strlcpy(name, dbc_sysctl_table[sysctl_index].name, sizeof(name));
1732	if (dbc_sysctl_table[sysctl_index].lockable && dbcool_islocked(sc))
1733		rw_flag = CTLFLAG_READONLY | CTLFLAG_OWNDESC;
1734	else
1735		rw_flag = CTLFLAG_READWRITE | CTLFLAG_OWNDESC;
1736	ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL, &node, rw_flag,
1737			     CTLTYPE_INT, name,
1738			     SYSCTL_DESCR(dbc_sysctl_table[sysctl_index].desc),
1739			     dbc_sysctl_table[sysctl_index].helper,
1740			     0, sc, sizeof(int),
1741			     CTL_HW, sc->sc_root_sysctl_num,
1742				sc->sc_sysctl_num[j],
1743				DBC_PWM_SYSCTL(idx, sysctl_reg), CTL_EOL);
1744
1745	return ret;
1746}
1747
1748static void
1749dbcool_setup_controllers(struct dbcool_softc *sc)
1750{
1751	int i, j, ret, rw_flag;
1752	uint8_t sysctl_reg;
1753	struct chip_id *chip = sc->sc_dc.dc_chip;
1754	const struct sysctlnode *me2 = NULL;
1755	const struct sysctlnode *node = NULL;
1756	char name[SYSCTL_NAMELEN];
1757
1758	for (i = 0; chip->power[i].desc != NULL; i++) {
1759		snprintf(name, sizeof(name), "fan_ctl_%d", i);
1760		ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL, &me2,
1761		       CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
1762		       CTLTYPE_NODE, name, NULL,
1763		       NULL, 0, NULL, 0,
1764		       CTL_HW, sc->sc_root_sysctl_num, CTL_CREATE, CTL_EOL);
1765
1766		for (j = DBC_PWM_BEHAVIOR; j < DBC_PWM_LAST_PARAM; j++) {
1767			if (j == DBC_PWM_MAX_DUTY &&
1768			    (chip->flags & DBCFLAG_HAS_MAXDUTY) == 0)
1769				continue;
1770			sysctl_reg = chip->power[i].power_regs[j];
1771			if (sysctl_reg == DBCOOL_NO_REG)
1772				continue;
1773			strlcpy(name, dbc_sysctl_table[j].name, sizeof(name));
1774			if (dbc_sysctl_table[j].lockable && dbcool_islocked(sc))
1775				rw_flag = CTLFLAG_READONLY | CTLFLAG_OWNDESC;
1776			else
1777				rw_flag = CTLFLAG_READWRITE | CTLFLAG_OWNDESC;
1778			ret = sysctl_createv(&sc->sc_sysctl_log, 0, NULL,
1779				&node, rw_flag,
1780				(j == DBC_PWM_BEHAVIOR)?
1781					CTLTYPE_STRING:CTLTYPE_INT,
1782				name,
1783				SYSCTL_DESCR(dbc_sysctl_table[j].desc),
1784				dbc_sysctl_table[j].helper,
1785				0, sc,
1786				( j == DBC_PWM_BEHAVIOR)?
1787					sizeof(dbcool_cur_behav): sizeof(int),
1788				CTL_HW, sc->sc_root_sysctl_num, me2->sysctl_num,
1789				DBC_PWM_SYSCTL(j, sysctl_reg), CTL_EOL);
1790		}
1791	}
1792}
1793
1794static void
1795dbcool_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
1796{
1797	struct dbcool_softc *sc=sme->sme_cookie;
1798	int i, nom_volt_idx, cur;
1799	struct reg_list *reg;
1800
1801	i = edata->sensor;
1802	reg = sc->sc_regs[i];
1803
1804	edata->state = ENVSYS_SVALID;
1805	switch (edata->units)
1806	{
1807		case ENVSYS_STEMP:
1808			cur = dbcool_read_temp(sc, reg->val_reg, true);
1809			break;
1810		case ENVSYS_SVOLTS_DC:
1811			nom_volt_idx = sc->sc_nom_volt[i];
1812			cur = dbcool_read_volt(sc, reg->val_reg, nom_volt_idx,
1813						true);
1814			break;
1815		case ENVSYS_SFANRPM:
1816			cur = dbcool_read_rpm(sc, reg->val_reg);
1817			break;
1818		case ENVSYS_INTEGER:
1819			return;
1820		default:
1821			edata->state = ENVSYS_SINVALID;
1822			return;
1823	}
1824
1825	if (cur == 0 && (edata->units != ENVSYS_SFANRPM))
1826		edata->state = ENVSYS_SINVALID;
1827
1828	/*
1829	 * If fan is "stalled" but has no low limit, treat
1830	 * it as though the fan is not installed.
1831	 */
1832	else if (edata->units == ENVSYS_SFANRPM && cur == 0 &&
1833			!(edata->upropset & (PROP_CRITMIN | PROP_WARNMIN)))
1834		edata->state = ENVSYS_SINVALID;
1835
1836	edata->value_cur = cur;
1837}
1838
1839int
1840dbcool_chip_ident(struct dbcool_chipset *dc)
1841{
1842	/* verify this is a supported dbCool chip */
1843	uint8_t c_id, d_id, r_id;
1844	int i;
1845
1846	c_id = dc->dc_readreg(dc, DBCOOL_COMPANYID_REG);
1847	d_id = dc->dc_readreg(dc, DBCOOL_DEVICEID_REG);
1848	r_id = dc->dc_readreg(dc, DBCOOL_REVISION_REG);
1849
1850	/* The EMC6D103S only supports read_byte and since dc->dc_chip is
1851	 * NULL when we call dc->dc_readreg above we use
1852	 * send_byte/receive_byte which doesn't work.
1853	 *
1854	 * So if we only get 0's back then try again with dc->dc_chip
1855	 * set to the EMC6D103S_DEVICEID and which doesn't have
1856	 * DBCFLAG_NO_READBYTE set so read_byte will be used
1857	 */
1858	if ((c_id == 0) && (d_id == 0) && (r_id == 0)) {
1859		for (i = 0; chip_table[i].company != 0; i++)
1860			if ((SMSC_COMPANYID == chip_table[i].company) &&
1861			    (EMC6D103S_DEVICEID == chip_table[i].device)) {
1862				dc->dc_chip = &chip_table[i];
1863				break;
1864			}
1865		c_id = dc->dc_readreg(dc, DBCOOL_COMPANYID_REG);
1866 		d_id = dc->dc_readreg(dc, DBCOOL_DEVICEID_REG);
1867 		r_id = dc->dc_readreg(dc, DBCOOL_REVISION_REG);
1868	}
1869
1870	for (i = 0; chip_table[i].company != 0; i++)
1871		if ((c_id == chip_table[i].company) &&
1872		    (d_id == chip_table[i].device ||
1873		    chip_table[i].device == 0xff) &&
1874		    (r_id == chip_table[i].rev ||
1875		    chip_table[i].rev == 0xff)) {
1876			dc->dc_chip = &chip_table[i];
1877			return i;
1878		}
1879
1880	aprint_verbose("dbcool_chip_ident: addr 0x%02x c_id 0x%02x d_id 0x%02x"
1881			" r_id 0x%02x: No match.\n", dc->dc_addr, c_id, d_id,
1882			r_id);
1883
1884	return -1;
1885}
1886
1887/*
1888 * Retrieve sensor limits from the chip registers
1889 */
1890static void
1891dbcool_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
1892		  sysmon_envsys_lim_t *limits, uint32_t *props)
1893{
1894	int index = edata->sensor;
1895	struct dbcool_softc *sc = sme->sme_cookie;
1896
1897	*props &= ~(PROP_CRITMIN | PROP_CRITMAX);
1898	switch (edata->units) {
1899	    case ENVSYS_STEMP:
1900		dbcool_get_temp_limits(sc, index, limits, props);
1901		break;
1902	    case ENVSYS_SVOLTS_DC:
1903		dbcool_get_volt_limits(sc, index, limits, props);
1904		break;
1905	    case ENVSYS_SFANRPM:
1906		dbcool_get_fan_limits(sc, index, limits, props);
1907
1908	    /* FALLTHROUGH */
1909	    default:
1910		break;
1911	}
1912	*props &= ~PROP_DRIVER_LIMITS;
1913
1914	/* If both limits provided, make sure they're sane */
1915	if ((*props & PROP_CRITMIN) &&
1916	    (*props & PROP_CRITMAX) &&
1917	    (limits->sel_critmin >= limits->sel_critmax))
1918		*props &= ~(PROP_CRITMIN | PROP_CRITMAX);
1919
1920	/*
1921	 * If this is the first time through, save these values
1922	 * in case user overrides them and then requests a reset.
1923	 */
1924	if (sc->sc_defprops[index] == 0) {
1925		sc->sc_defprops[index] = *props | PROP_DRIVER_LIMITS;
1926		sc->sc_deflims[index]  = *limits;
1927	}
1928}
1929
1930static void
1931dbcool_get_temp_limits(struct dbcool_softc *sc, int idx,
1932		       sysmon_envsys_lim_t *lims, uint32_t *props)
1933{
1934	struct reg_list *reg = sc->sc_regs[idx];
1935	uint8_t	lo_lim, hi_lim;
1936
1937	lo_lim = sc->sc_dc.dc_readreg(&sc->sc_dc, reg->lo_lim_reg);
1938	hi_lim = sc->sc_dc.dc_readreg(&sc->sc_dc, reg->hi_lim_reg);
1939
1940	if (sc->sc_temp_offset) {
1941		if (lo_lim > 0x01) {
1942			lims->sel_critmin = lo_lim - sc->sc_temp_offset;
1943			*props |= PROP_CRITMIN;
1944		}
1945		if (hi_lim != 0xff) {
1946			lims->sel_critmax = hi_lim - sc->sc_temp_offset;
1947			*props |= PROP_CRITMAX;
1948		}
1949	} else {
1950		if (lo_lim != 0x80 && lo_lim != 0x81) {
1951			lims->sel_critmin = (int8_t)lo_lim;
1952			*props |= PROP_CRITMIN;
1953		}
1954
1955		if (hi_lim != 0x7f) {
1956			lims->sel_critmax = (int8_t)hi_lim;
1957			*props |= PROP_CRITMAX;
1958		}
1959	}
1960
1961	/* Convert temp limits to microKelvin */
1962	lims->sel_critmin *= 1000000;
1963	lims->sel_critmin += 273150000;
1964	lims->sel_critmax *= 1000000;
1965	lims->sel_critmax += 273150000;
1966}
1967
1968static void
1969dbcool_get_volt_limits(struct dbcool_softc *sc, int idx,
1970		       sysmon_envsys_lim_t *lims, uint32_t *props)
1971{
1972	struct reg_list *reg = sc->sc_regs[idx];
1973	int64_t limit;
1974	int nom;
1975
1976	nom = nominal_voltages[sc->sc_dc.dc_chip->table[idx].nom_volt_index];
1977	if (nom < 0)
1978		nom = dbcool_supply_voltage(sc);
1979	nom *= 1000000;		/* scale for microvolts */
1980
1981	limit = sc->sc_dc.dc_readreg(&sc->sc_dc, reg->lo_lim_reg);
1982	if (limit != 0x00 && limit != 0xff) {
1983		limit *= nom;
1984		limit /= 0xc0;
1985		lims->sel_critmin = limit;
1986		*props |= PROP_CRITMIN;
1987	}
1988	limit = sc->sc_dc.dc_readreg(&sc->sc_dc, reg->hi_lim_reg);
1989	if (limit != 0x00 && limit != 0xff) {
1990		limit *= nom;
1991		limit /= 0xc0;
1992		lims->sel_critmax = limit;
1993		*props |= PROP_CRITMAX;
1994	}
1995}
1996
1997static void
1998dbcool_get_fan_limits(struct dbcool_softc *sc, int idx,
1999		      sysmon_envsys_lim_t *lims, uint32_t *props)
2000{
2001	struct reg_list *reg = sc->sc_regs[idx];
2002	int32_t	limit;
2003
2004	limit = dbcool_read_rpm(sc, reg->lo_lim_reg);
2005	if (limit) {
2006		lims->sel_critmin = limit;
2007		*props |= PROP_CRITMIN;
2008	}
2009}
2010
2011/*
2012 * Update sensor limits in the chip registers
2013 */
2014static void
2015dbcool_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
2016		  sysmon_envsys_lim_t *limits, uint32_t *props)
2017{
2018	int index = edata->sensor;
2019	struct dbcool_softc *sc = sme->sme_cookie;
2020
2021	if (limits == NULL) {
2022		limits = &sc->sc_deflims[index];
2023		props  = &sc->sc_defprops[index];
2024	}
2025	switch (edata->units) {
2026	    case ENVSYS_STEMP:
2027		dbcool_set_temp_limits(sc, index, limits, props);
2028		break;
2029	    case ENVSYS_SVOLTS_DC:
2030		dbcool_set_volt_limits(sc, index, limits, props);
2031		break;
2032	    case ENVSYS_SFANRPM:
2033		dbcool_set_fan_limits(sc, index, limits, props);
2034
2035	    /* FALLTHROUGH */
2036	    default:
2037		break;
2038	}
2039	*props &= ~PROP_DRIVER_LIMITS;
2040}
2041
2042static void
2043dbcool_set_temp_limits(struct dbcool_softc *sc, int idx,
2044		       sysmon_envsys_lim_t *lims, uint32_t *props)
2045{
2046	struct reg_list *reg = sc->sc_regs[idx];
2047	int32_t	limit;
2048
2049	if (*props & PROP_CRITMIN) {
2050		limit = lims->sel_critmin - 273150000;
2051		limit /= 1000000;
2052		if (sc->sc_temp_offset) {
2053			limit += sc->sc_temp_offset;
2054			if (limit < 0)
2055				limit = 0;
2056			else if (limit > 255)
2057				limit = 255;
2058		} else {
2059			if (limit < -127)
2060				limit = -127;
2061			else if (limit > 127)
2062				limit = 127;
2063		}
2064		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg,
2065				      (uint8_t)limit);
2066	} else if (*props & PROP_DRIVER_LIMITS) {
2067		if (sc->sc_temp_offset)
2068			limit = 0x00;
2069		else
2070			limit = 0x80;
2071		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg,
2072				      (uint8_t)limit);
2073	}
2074
2075	if (*props & PROP_CRITMAX) {
2076		limit = lims->sel_critmax - 273150000;
2077		limit /= 1000000;
2078		if (sc->sc_temp_offset) {
2079			limit += sc->sc_temp_offset;
2080			if (limit < 0)
2081				limit = 0;
2082			else if (limit > 255)
2083				limit = 255;
2084		} else {
2085			if (limit < -127)
2086				limit = -127;
2087			else if (limit > 127)
2088				limit = 127;
2089		}
2090		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->hi_lim_reg,
2091				      (uint8_t)limit);
2092	} else if (*props & PROP_DRIVER_LIMITS) {
2093		if (sc->sc_temp_offset)
2094			limit = 0xff;
2095		else
2096			limit = 0x7f;
2097		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->hi_lim_reg,
2098				      (uint8_t)limit);
2099	}
2100}
2101
2102static void
2103dbcool_set_volt_limits(struct dbcool_softc *sc, int idx,
2104		       sysmon_envsys_lim_t *lims, uint32_t *props)
2105{
2106	struct reg_list *reg = sc->sc_regs[idx];
2107	int64_t limit;
2108	int nom;
2109
2110	nom = nominal_voltages[sc->sc_dc.dc_chip->table[idx].nom_volt_index];
2111	if (nom < 0)
2112		nom = dbcool_supply_voltage(sc);
2113	nom *= 1000000;		/* scale for microvolts */
2114
2115	if (*props & PROP_CRITMIN) {
2116		limit = lims->sel_critmin;
2117		limit *= 0xc0;
2118		limit /= nom;
2119		if (limit > 0xff)
2120			limit = 0xff;
2121		else if (limit < 0)
2122			limit = 0;
2123		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg, limit);
2124	} else if (*props & PROP_DRIVER_LIMITS)
2125		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg, 0);
2126
2127	if (*props & PROP_CRITMAX) {
2128		limit = lims->sel_critmax;
2129		limit *= 0xc0;
2130		limit /= nom;
2131		if (limit > 0xff)
2132			limit = 0xff;
2133		else if (limit < 0)
2134			limit = 0;
2135		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->hi_lim_reg, limit);
2136	} else if (*props & PROP_DRIVER_LIMITS)
2137		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->hi_lim_reg, 0xff);
2138}
2139
2140static void
2141dbcool_set_fan_limits(struct dbcool_softc *sc, int idx,
2142		      sysmon_envsys_lim_t *lims, uint32_t *props)
2143{
2144	struct reg_list *reg = sc->sc_regs[idx];
2145	int32_t	limit, dividend;
2146
2147	if (*props & PROP_CRITMIN) {
2148		limit = lims->sel_critmin;
2149		if (limit == 0)
2150			limit = 0xffff;
2151		else {
2152			if (sc->sc_dc.dc_chip->flags & DBCFLAG_ADM1030)
2153				dividend = 11250 * 60;
2154			else
2155				dividend = 90000 * 60;
2156			limit = limit / dividend;
2157			if (limit > 0xffff)
2158				limit = 0xffff;
2159		}
2160		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg,
2161				      limit & 0xff);
2162		limit >>= 8;
2163		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg + 1,
2164				      limit & 0xff);
2165	} else if (*props & PROP_DRIVER_LIMITS) {
2166		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg, 0xff);
2167		sc->sc_dc.dc_writereg(&sc->sc_dc, reg->lo_lim_reg + 1, 0xff);
2168	}
2169}
2170
2171MODULE(MODULE_CLASS_DRIVER, dbcool, "iic");
2172
2173#ifdef _MODULE
2174#include "ioconf.c"
2175#endif
2176
2177static int
2178dbcool_modcmd(modcmd_t cmd, void *opaque)
2179{
2180	int error = 0;
2181
2182	switch (cmd) {
2183	case MODULE_CMD_INIT:
2184#ifdef _MODULE
2185		error = config_init_component(cfdriver_ioconf_dbcool,
2186		    cfattach_ioconf_dbcool, cfdata_ioconf_dbcool);
2187#endif
2188		return error;
2189	case MODULE_CMD_FINI:
2190#ifdef _MODULE
2191		error = config_fini_component(cfdriver_ioconf_dbcool,
2192		    cfattach_ioconf_dbcool, cfdata_ioconf_dbcool);
2193#endif
2194		return error;
2195	default:
2196		return ENOTTY;
2197	}
2198}
2199