Added support and documentation for:

* reading #VRDHOT inputs
 * reading GPIO pins (input only for now)
 * reading alarms status
 * #PROCHOT override control
 * #PROCHOT sampling interval control
 * Manual PWM output control
 * PWM output frequency control

Also, some whitespace cleanup in the driver.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@2660 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Mark M. Hoffman committed 2004-08-13 19:29:33 +00:00
1 parent e5ee8c4b03
commit 34f34e64ca
2 files changed
+628 -106

No files matched your search

+66 -2
View File
@@ -19,7 +19,8 @@ Module Parameters
* init: integer
Set to zero to disable some initializations (default is 1)
* disable_block: integer
Set to non-zero to disable SMBus block data transactions (default is 0)
A "0" allows SMBus block data transactions if the host supports them. A "1"
disables SMBus block data transactions. The default is 0.
* vccp_limit_type: integer array (2)
Configures in7 and in8 limit type, where 0 means absolute and non-zero
means relative. "Relative" here refers to "Dynamic Vccp Monitoring using
@@ -86,11 +87,35 @@ most recent complete time interval. The third value is something like a
Note that this third value is calculated by the chip itself. All values
range from 0-255 where 0 indicates no throttling, and 255 indicates > 99.6%.
The monitoring intervals for the two #PROCHOT signals is also configurable.
These intervals can be found in the /proc file prochot_interval. There are
two values in this file: one each for #P1_PROCHOT and #P2_PROCHOT, res-
pectively. Selecting a value not in this list will cause the driver to
use the next largest interval. The available intervals are:
#PROCHOT intervals: 0.73, 1.46, 2.9, 5.8, 11.7, 23.3, 46.6, 93.2, 186, 372
It is possible to configure the LM93 to logically short the two #PROCHOT
signals. I.e. when #P1_PROCHOT is asserted, the LM93 will automatically
assert #P2_PROCHOT, and vice-versa. This mode is enabled by writing a
non-zero integer to the /proc file prochot_short.
The LM93 can also override the #PROCHOT pins by driving a PWM signal onto
one or both of them. When overridden, the signal has a period of 3.56 mS,
a minimum pulse width of 5 clocks (at 22.5kHz => 6.25% duty cycle), and
a maximum pulse width of 80 clocks (at 22.5kHz => 99.88% duty cycle).
The /proc file prochot_override has three values. The first two are boolean
integers which enable/disable the override funtion for #P1_PROCHOT and
#P2_PROCHOT, the third value is 0-15 where 0 indicates minimum duty cycle
and 15 indicates maximum.
#VRD_HOT:
The LM93 can monitor two #VRD_HOT signals. The results are found in the
/proc files vrdhot1 and vrdhot2. There is one value per file: a boolean for
which 1 indicates #VRD_HOT is asserted and 0 indicates it is negated. These
files are read-only.
Smart Tach Mode:
@@ -104,7 +129,38 @@ Smart Tach Mode:
Smart tach mode is enabled by the driver by writing 1 or 2 (associating the
the fan tachometer with a pwm) to the /proc file fan<n>_smart_tach. A zero
will disable the function for that fan.
will disable the function for that fan. Note that Smart tach mode cannot be
enabled if the PWM output frequency is 22500 Hz (see below).
Manual PWM:
The LM93 has a fixed or override mode for the two PWM outputs (although, there
are still some conditions that will override even this mode - see section
15.10.6 of the datasheet for details.) The /proc files pwm1 and pwm2 are
used to enable and control this mode. The first value of the file is an
integer (0-255) where 0 is 0% duty cycle, and 255 is 100%. The second value
is a boolean integer where 0 disables and 1 enables the manual control mode.
Note that the duty cycle values are constrained by the hardware. Selecting
a value which is not available will cause the driver to use the next largest
value. Also note: when manual PWM mode is disabled, the first value of the
/proc files indicates the current duty cycle chosen by the h/w.
PWM Output Frequency:
The LM93 supports several different frequencies for the PWM output channels.
The /proc files pwm1_freq and pwm2_freq are used to select the frequency. The
frequency values are constrained by the hardware. Selecting a value which is
not available will cause the driver to use the next largest value. Also note
that this parameter has implications for the Smart Tach Mode (see above).
PWM Output Frequencies: 12, 36, 48, 60, 72, 84, 96, 22500 (h/w default)
GPIO Pins:
The LM93 can monitor the logic level of four dedicated GPIO pins as well as the
four tach input pins. GPIO0-GPIO3 correspond to (fan) tach 1-4, respectively.
All eight GPIOs are read by reading the bitmask in the /proc file gpio. The
LSB is GPIO0, and the MSB is GPIO7.
LM93 Unique /proc Files
-----------------------
@@ -116,8 +172,16 @@ LM93 Unique /proc Files
prochot_short enable or disable logical #PROCHOT pin short
prochot_override force #PROCHOT assertion as PWM
prochot_interval #PROCHOT PWM sampling interval
vrdhot<n> 0 means negated, 1 means asserted
fan<n>_smart_tach enable or disable smart tach mode
gpio input state of 8 GPIO pins; read-only
Sample Configuration File
-------------------------
+562 -104
View File
@@ -96,6 +96,9 @@ MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes");
#define LM93_REG_TEMP_MIN(nr) (0x78 + (nr) * 2)
#define LM93_REG_TEMP_MAX(nr) (0x79 + (nr) * 2)
/* temp[1-4]_auto_boost (nr => 0-3) */
#define LM93_REG_TEMP_FAN_BOOST(nr) (0x80 + (nr))
/* #PROCHOT inputs: prochot1-prochot2 (nr => 0-1) */
#define LM93_REG_PROCHOT_CUR(nr) (0x67 + (nr) * 2)
#define LM93_REG_PROCHOT_AVG(nr) (0x68 + (nr) * 2)
@@ -105,11 +108,15 @@ MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes");
#define LM93_REG_FAN(nr) (0x6e + (nr) * 2)
#define LM93_REG_FAN_MIN(nr) (0xb4 + (nr) * 2)
/* pwm outputs: pwm1-pwm2 (nr => 0-1) */
#define LM93_REG_PWM_CTL1(nr) (0xc8 + (nr) * 4)
#define LM93_REG_PWM_CTL2(nr) (0xc9 + (nr) * 4)
#define LM93_REG_PWM_CTL3(nr) (0xca + (nr) * 4)
#define LM93_REG_PWM_CTL4(nr) (0xcb + (nr) * 4)
/* pwm outputs: pwm1-pwm2 (nr => 0-1, reg => 0-3) */
#define LM93_REG_PWM_CTL(nr,reg) (0xc8 + (reg) + (nr) * 4)
#define LM93_PWM_CTL1 0
#define LM93_PWM_CTL2 1
#define LM93_PWM_CTL3 2
#define LM93_PWM_CTL4 3
/* GPIO input state */
#define LM93_REG_GPI 0x6b
/* vid inputs: vid1-vid2 (nr => 0-1) */
#define LM93_REG_VID(nr) (0x6c + (nr))
@@ -117,6 +124,27 @@ MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes");
/* vccp1 & vccp2: VID relative inputs (nr => 0-1) */
#define LM93_REG_VCCP_LIMIT_OFF(nr) (0xb2 + (nr))
/* temp[1-4]_auto_boost_hyst */
#define LM93_REG_BOOST_HYST_12 0xc0
#define LM93_REG_BOOST_HYST_34 0xc1
/* temp[1-4]_auto_pwm_[min|hyst] */
#define LM93_REG_MIN_PWM_HYST_12 0xc3
#define LM93_REG_MIN_PWM_HYST_34 0xc4
/* prochot_override & prochot_interval */
#define LM93_REG_PROCHOT_OVERRIDE 0xc6
#define LM93_REG_PROCHOT_INTERVAL 0xc7
/* temp[1-4]_auto_base (nr => 0-3) */
#define LM93_REG_TEMP_BASE(nr) (0xd0 + (nr))
/* temp[1-4]_auto_offsets (step => 0-11) */
#define LM93_REG_TEMP_OFFSET(step) (0xd4 + (step))
/* #PROCHOT & #VRDHOT PWM ramp control */
#define LM93_REG_PWM_RAMP_CTL 0xbf
/* miscellaneous */
#define LM93_REG_SFC1 0xbc
#define LM93_REG_SFC2 0xbd
@@ -300,7 +328,138 @@ static u8 LM93_PROCHOT_TO_REG(long prochot)
return (u8)prochot;
}
/* <TODO> add conversion routines here */
/* PROCHOT-OVERRIDE; 0-15, 0 is 6.25%, 15 is 99.88%
* REG: (same) */
static u8 LM93_PROCHOT_OVERRIDE_TO_REG(int force1, int force2, long prochot)
{
u8 result = 0;
result |= force1 ? 0x80 : 0x00;
result |= force2 ? 0x40 : 0x00;
prochot = SENSORS_LIMIT(prochot, 0, 15);
result |= prochot;
return result;
}
/* PROCHOT-INTERVAL: 73 - 37200 (1/100 seconds)
* REG: 0-9 as mapped below */
static int lm93_interval_map[10] = {
73, 146, 290, 580, 1170, 2330, 4660, 9320, 18600, 37200,
};
static int LM93_INTERVAL_FROM_REG(u8 reg)
{
return lm93_interval_map[reg & 0x0f];
}
/* round up to nearest match */
static u8 LM93_INTERVAL_TO_REG(long interval)
{
int i;
for (i = 0; i < 9; i++)
if (interval <= lm93_interval_map[i])
break;
/* can fall through with i==9 */
return (u8)i;
}
/* PWM: 0-255 per sensors documentation
REG: 0-13 as mapped below... *left* justified */
typedef enum { LM93_PWM_MAP_HI_FREQ, LM93_PWM_MAP_LO_FREQ } pwm_freq_t;
static int lm93_pwm_map[2][14] = {
{
0x00, /* 0.00% */ 0x40, /* 25.00% */
0x50, /* 31.25% */ 0x60, /* 37.50% */
0x70, /* 43.75% */ 0x80, /* 50.00% */
0x90, /* 56.25% */ 0xa0, /* 62.50% */
0xb0, /* 68.75% */ 0xc0, /* 75.00% */
0xd0, /* 81.25% */ 0xe0, /* 87.50% */
0xf0, /* 93.75% */ 0xff, /* 100.00% */
},
{
0x00, /* 0.00% */ 0x40, /* 25.00% */
0x49, /* 28.57% */ 0x52, /* 32.14% */
0x5b, /* 35.71% */ 0x64, /* 39.29% */
0x6d, /* 42.86% */ 0x76, /* 46.43% */
0x80, /* 50.00% */ 0x89, /* 53.57% */
0x92, /* 57.14% */ 0xb6, /* 71.43% */
0xdb, /* 85.71% */ 0xff, /* 100.00% */
},
};
static int LM93_PWM_FROM_REG(u8 reg, pwm_freq_t freq)
{
return lm93_pwm_map[freq][reg >> 4 & 0x0f];
}
/* round up to nearest match */
static u8 LM93_PWM_TO_REG(int pwm, pwm_freq_t freq)
{
int i;
for (i = 0; i < 13; i++)
if (pwm <= lm93_pwm_map[freq][i])
break;
/* can fall through with i==13 */
return (u8)(i << 4);
}
/* PWM FREQ: HZ
REG: 0-7 as mapped below */
static int lm93_pwm_freq_map[8] = {
22500, 96, 84, 72, 60, 48, 36, 12
};
static int LM93_PWM_FREQ_FROM_REG(u8 reg)
{
return lm93_pwm_freq_map[reg & 0x07];
}
/* round up to nearest match */
static u8 LM93_PWM_FREQ_TO_REG(int freq)
{
int i;
for (i = 7; i > 0; i--)
if (freq <= lm93_pwm_freq_map[i])
break;
/* can fall through with i==0 */
return (u8)i;
}
/* GPIO: 0-255, GPIO0 is LSB
* REG: inverted */
static unsigned LM93_GPI_FROM_REG(u8 reg)
{
return ~reg & 0xff;
}
/* ALARMS: SYSCTL format described further below
REG: 64 bits in 8 registers, as immediately below */
struct block1_t {
u8 host_status_1;
u8 host_status_2;
u8 host_status_3;
u8 host_status_4;
u8 p1_prochot_status;
u8 p2_prochot_status;
u8 gpi_status;
u8 fan_status;
};
static unsigned LM93_ALARMS_FROM_REG(struct block1_t b1)
{
unsigned result;
result = b1.host_status_2;
result |= b1.host_status_3 << 8;
result |= (b1.fan_status & 0x04) << 16;
result |= (b1.p1_prochot_status & 0x80) << 13;
result |= (b1.p2_prochot_status & 0x80) << 14;
result |= (b1.host_status_4 & 0xfc) << 20;
result |= (b1.host_status_1 & 0x07) << 28;
return result;
}
/* For each registered client, we need to keep some data in memory. That
data is pointed to by client->data. The structure itself is dynamically
@@ -321,16 +480,7 @@ struct lm93_data {
char valid; /* !=0 if following fields are valid */
/* register values, arranged by block read groups */
struct {
u8 host_status_1;
u8 host_status_2;
u8 host_status_3;
u8 host_status_4;
u8 p1_prochot_status;
u8 p2_prochot_status;
u8 gpi_status;
u8 fan_status;
} block1;
struct block1_t block1;
/* temp1 - temp4: unfiltered readings
temp1 - temp2: filtered readings */
@@ -354,7 +504,8 @@ struct lm93_data {
/* fan count limits 1-4 => same format as block5 */
u16 block8[4];
/* more register values */
/* pwm control registers (2 pwms, 4 regs) */
u8 block9[2][4];
/* master config register */
u8 config;
@@ -368,18 +519,30 @@ struct lm93_data {
/* vccp1 & vccp2 (in7 & in8): VID relative limits (register format) */
u8 vccp_limits[2];
/* GPIO input state (register format, i.e. inverted) */
u8 gpi;
/* #PROCHOT override (register format) */
u8 prochot_override;
/* #PROCHOT intervals (register format) */
u8 prochot_interval;
/* miscellaneous setup regs */
u8 sfc1;
u8 sfc2;
u8 sf_tach_to_pwm;
/* <TODO> add members here */
/* The two PWM CTL2 registers can read something other than what was
last written for the OVR_DC field (duty cycle override). So, we
save the user-commanded value here. */
u8 pwm_override[2];
};
static int lm93_attach_adapter(struct i2c_adapter *adapter);
static int lm93_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
unsigned short flags, int kind);
static int lm93_detach_client(struct i2c_client *client);
static u8 lm93_read_byte(struct i2c_client *client, u8 register);
@@ -396,27 +559,33 @@ static void lm93_init_client(struct i2c_client *client);
static void lm93_in(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results);
int *nrels_mag, long *results);
static void lm93_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
int ctl_name, int *nrels_mag, long *results);
static void lm93_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
#if 0
int ctl_name, int *nrels_mag, long *results);
static void lm93_pwm(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
#endif
int ctl_name, int *nrels_mag, long *results);
static void lm93_pwm_freq(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void lm93_fan_smart_tach(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
int ctl_name, int *nrels_mag, long *results);
static void lm93_vid(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
int ctl_name, int *nrels_mag, long *results);
static void lm93_prochot(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
int ctl_name, int *nrels_mag, long *results);
static void lm93_prochot_short(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
#if 0
int ctl_name, int *nrels_mag, long *results);
static void lm93_prochot_override(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void lm93_prochot_interval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void lm93_vrdhot(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void lm93_gpio(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void lm93_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
#endif
int ctl_name, int *nrels_mag, long *results);
static struct i2c_driver lm93_driver = {
.owner = THIS_MODULE,
@@ -474,6 +643,10 @@ static int lm93_id = 0;
#define LM93_SYSCTL_PWM1 1401
#define LM93_SYSCTL_PWM2 1402
/* Hz */
#define LM93_SYSCTL_PWM1_FREQ 1403
#define LM93_SYSCTL_PWM2_FREQ 1404
/* 0 => 0%, 255 => > 99.6% */
#define LM93_SYSCTL_PROCHOT1 1501
#define LM93_SYSCTL_PROCHOT2 1502
@@ -481,50 +654,62 @@ static int lm93_id = 0;
/* !0 => enable #PROCHOT logical short */
#define LM93_SYSCTL_PROCHOT_SHORT 1503
/* bitmask of alarms */
/* 2 boolean enable/disable, 3rd value indicates duty cycle */
#define LM93_SYSCTL_PROCHOT_OVERRIDE 1504
/* 2 values, 0-9 */
#define LM93_SYSCTL_PROCHOT_INTERVAL 1505
/* GPIO input (bitmask) */
#define LM93_SYSCTL_GPIO 1601
/* #VRDHOT input (boolean) */
#define LM93_SYSCTL_VRDHOT1 1701
#define LM93_SYSCTL_VRDHOT2 1702
/* alarms (bitmask) */
#define LM93_SYSCTL_ALARMS 2001 /* bitvector */
/*
<TODO> alarm bitmask definitions
/* alarm bitmask definitions
The LM93 has nearly 64 bits of error status... I've pared that down to
what I think is a useful subset in order to fit it into 32 bits.
This is what would happen if you treated the entire 8 bytes of host
error status registers as a single big-endian integer. Trouble is,
the handler i2c_proc_real() only does 32-bit values. What to do?
Especially note that the #VRD_HOT alarms are missing because we provide
that information as values in another /proc file.
If libsensors is extended to support 64 bit values, this could be revisited.
*/
#define LM93_ALARM_FAN1 0x0000000000000001ull
#define LM93_ALARM_FAN2 0x0000000000000002ull
#define LM93_ALARM_FAN3 0x0000000000000004ull
#define LM93_ALARM_FAN4 0x0000000000000008ull
#define LM93_ALARM_PH2_ERR 0x0000000000800000ull
#define LM93_ALARM_PH1_ERR 0x0000000080000000ull
#define LM93_ALARM_SCSI1_ERR 0x0000000400000000ull
#define LM93_ALARM_SCSI2_ERR 0x0000000800000000ull
#define LM93_ALARM_DVDDP1_ERR 0x0000001000000000ull
#define LM93_ALARM_DVDDP2_ERR 0x0000002000000000ull
#define LM93_ALARM_D1_ERR 0x0000004000000000ull
#define LM93_ALARM_D2_ERR 0x0000008000000000ull
#define LM93_ALARM_IN1 0x0000010000000000ull
#define LM93_ALARM_IN2 0x0000020000000000ull
#define LM93_ALARM_IN3 0x0000040000000000ull
#define LM93_ALARM_IN4 0x0000080000000000ull
#define LM93_ALARM_IN5 0x0000100000000000ull
#define LM93_ALARM_IN6 0x0000200000000000ull
#define LM93_ALARM_IN7 0x0000400000000000ull
#define LM93_ALARM_IN8 0x0000800000000000ull
#define LM93_ALARM_IN9 0x0001000000000000ull
#define LM93_ALARM_IN10 0x0002000000000000ull
#define LM93_ALARM_IN11 0x0004000000000000ull
#define LM93_ALARM_IN12 0x0008000000000000ull
#define LM93_ALARM_IN13 0x0010000000000000ull
#define LM93_ALARM_IN14 0x0020000000000000ull
#define LM93_ALARM_IN15 0x0040000000000000ull
#define LM93_ALARM_IN16 0x0080000000000000ull
#define LM93_ALARM_TEMP1 0x0100000000000000ull
#define LM93_ALARM_TEMP2 0x0200000000000000ull
#define LM93_ALARM_TEMP3 0x0400000000000000ull
#define LM93_ALARM_VRD1_ERR 0x1000000000000000ull
#define LM93_ALARM_VRD2_ERR 0x2000000000000000ull
#define LM93_ALARM_IN1 0x00000001
#define LM93_ALARM_IN2 0x00000002
#define LM93_ALARM_IN3 0x00000004
#define LM93_ALARM_IN4 0x00000008
#define LM93_ALARM_IN5 0x00000010
#define LM93_ALARM_IN6 0x00000020
#define LM93_ALARM_IN7 0x00000040
#define LM93_ALARM_IN8 0x00000080
#define LM93_ALARM_IN9 0x00000100
#define LM93_ALARM_IN10 0x00000200
#define LM93_ALARM_IN11 0x00000400
#define LM93_ALARM_IN12 0x00000800
#define LM93_ALARM_IN13 0x00001000
#define LM93_ALARM_IN14 0x00002000
#define LM93_ALARM_IN15 0x00004000
#define LM93_ALARM_IN16 0x00008000
#define LM93_ALARM_FAN1 0x00010000
#define LM93_ALARM_FAN2 0x00020000
#define LM93_ALARM_FAN3 0x00040000
#define LM93_ALARM_FAN4 0x00080000
#define LM93_ALARM_PH1_ERR 0x00100000
#define LM93_ALARM_PH2_ERR 0x00200000
#define LM93_ALARM_SCSI1_ERR 0x00400000
#define LM93_ALARM_SCSI2_ERR 0x00800000
#define LM93_ALARM_DVDDP1_ERR 0x01000000
#define LM93_ALARM_DVDDP2_ERR 0x02000000
#define LM93_ALARM_D1_ERR 0x04000000
#define LM93_ALARM_D2_ERR 0x08000000
#define LM93_ALARM_TEMP1 0x10000000
#define LM93_ALARM_TEMP2 0x20000000
#define LM93_ALARM_TEMP3 0x40000000
/* -- SENSORS SYSCTL END -- */
@@ -540,12 +725,17 @@ static int lm93_id = 0;
0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_temp}
#define LM93_SYSCTL_PWM(nr) {LM93_SYSCTL_PWM##nr, "pwm" #nr, NULL, 0, \
0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_pwm}
#define LM93_SYSCTL_PWM_FREQ(nr) {LM93_SYSCTL_PWM##nr##_FREQ, \
"pwm" #nr "_freq", NULL, 0, 0644, NULL, &i2c_proc_real, \
&i2c_sysctl_real, NULL, &lm93_pwm_freq}
#define LM93_SYSCTL_FAN(nr) {LM93_SYSCTL_FAN##nr, "fan" #nr, NULL, 0, \
0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_fan}
#define LM93_SYSCTL_VID(nr) {LM93_SYSCTL_VID##nr, "vid" #nr, NULL, 0, \
0444, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_vid}
#define LM93_SYSCTL_PROCHOT(nr) {LM93_SYSCTL_PROCHOT##nr, "prochot" #nr, NULL, \
0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_prochot}
#define LM93_SYSCTL_VRDHOT(nr) {LM93_SYSCTL_VRDHOT##nr, "vrdhot" #nr, NULL, \
0, 0444, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_vrdhot}
#define LM93_SYSCTL_FAN_SMART_TACH(nr) {LM93_SYSCTL_FAN##nr##_SMART_TACH, \
"fan" #nr "_smart_tach", NULL, 0, 0644, NULL, &i2c_proc_real, \
&i2c_sysctl_real, NULL, &lm93_fan_smart_tach}
@@ -581,10 +771,11 @@ static ctl_table lm93_dir_table_template[] = {
LM93_SYSCTL_FAN_SMART_TACH(3),
LM93_SYSCTL_FAN_SMART_TACH(4),
#if 0
LM93_SYSCTL_PWM(1),
LM93_SYSCTL_PWM(2),
#endif
LM93_SYSCTL_PWM_FREQ(1),
LM93_SYSCTL_PWM_FREQ(2),
LM93_SYSCTL_VID(1),
LM93_SYSCTL_VID(2),
@@ -594,10 +785,21 @@ static ctl_table lm93_dir_table_template[] = {
{LM93_SYSCTL_PROCHOT_SHORT, "prochot_short", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_prochot_short},
#if 0
{LM93_SYSCTL_PROCHOT_OVERRIDE, "prochot_override", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_prochot_override},
{LM93_SYSCTL_PROCHOT_INTERVAL, "prochot_interval", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_prochot_interval},
LM93_SYSCTL_VRDHOT(1),
LM93_SYSCTL_VRDHOT(2),
{LM93_SYSCTL_GPIO, "gpio", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &lm93_gpio},
{LM93_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &lm93_alarms},
#endif
{0}
};
@@ -924,6 +1126,17 @@ static void lm93_update_client_common(struct lm93_data *data,
data->vccp_limits[i] = lm93_read_byte(client,
LM93_REG_VCCP_LIMIT_OFF(i));
/* GPIO input state */
data->gpi = lm93_read_byte(client, LM93_REG_GPI);
/* #PROCHOT override state */
data->prochot_override = lm93_read_byte(client,
LM93_REG_PROCHOT_OVERRIDE);
/* #PROCHOT intervals */
data->prochot_interval = lm93_read_byte(client,
LM93_REG_PROCHOT_INTERVAL);
/* misc setup registers */
data->sfc1 = lm93_read_byte(client, LM93_REG_SFC1);
data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
@@ -951,7 +1164,8 @@ static void lm93_update_client_full(struct lm93_data *data,
lm93_read_block(client, 5, (u8 *)(data->block5));
lm93_read_block(client, 8, (u8 *)(data->block8));
/* <TODO> add code here */
/* pmw control registers */
lm93_read_block(client, 9, (u8 *)(data->block9));
lm93_update_client_common(data, client);
}
@@ -960,7 +1174,7 @@ static void lm93_update_client_full(struct lm93_data *data,
static void lm93_update_client_min(struct lm93_data *data,
struct i2c_client *client)
{
int i;
int i,j;
pr_debug("lm93.o: starting device update (block data disabled)\n");
@@ -995,8 +1209,14 @@ static void lm93_update_client_min(struct lm93_data *data,
data->block8[i] =
lm93_read_word(client, LM93_REG_FAN_MIN(i));
}
/* <TODO> add code here */
/* pwm control registers */
for (i = 0; i < 2; i++) {
for (j = 0; j < 4; j++) {
data->block9[i][j] =
lm93_read_word(client, LM93_REG_PWM_CTL(i,j));
}
}
lm93_update_client_common(data, client);
}
@@ -1178,6 +1398,49 @@ void lm93_vid(struct i2c_client *client, int operation, int ctl_name,
bit | 3 | 2 | 1 | 0
T4 T3 T2 T1
*/
/* helper function - must grab data->update_lock before calling
fan is 0-3, indicating fan1-fan4 */
static void lm93_write_fan_smart_tach(struct i2c_client *client,
struct lm93_data *data, int fan, long value)
{
/* insert the new mapping and write it out */
data->sf_tach_to_pwm = lm93_read_byte(client, LM93_REG_SF_TACH_TO_PWM);
data->sf_tach_to_pwm &= ~0x3 << fan * 2;
data->sf_tach_to_pwm |= value << fan * 2;
lm93_write_byte(client, LM93_REG_SF_TACH_TO_PWM, data->sf_tach_to_pwm);
/* insert the enable bit and write it out */
data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
if (value)
data->sfc2 |= 1 << fan;
else
data->sfc2 &= ~1 << fan;
lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
}
/* helper function - must grab data->update_lock before calling
pwm is 0-1, indicating pwm1-pwm2
this disables smart tach for all tach channels bound to the given pwm */
static void lm93_disable_fan_smart_tach(struct i2c_client *client,
struct lm93_data *data, int pwm)
{
int mapping = lm93_read_byte(client, LM93_REG_SF_TACH_TO_PWM);
int mask;
/* collapse the mapping into a mask of enable bits */
mapping = (mapping >> pwm) & 0x55;
mask = mapping & 0x01;
mask |= (mapping & 0x04) >> 1;
mask |= (mapping & 0x10) >> 2;
mask |= (mapping & 0x40) >> 3;
/* disable smart tach according to the mask */
data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
data->sfc2 &= ~mask;
lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
}
void lm93_fan_smart_tach(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
@@ -1210,25 +1473,18 @@ void lm93_fan_smart_tach(struct i2c_client *client, int operation, int ctl_name,
/* sanity test, ignore the write otherwise */
if (0 <= results[0] && results[0] <= 2) {
/* insert the new mapping and write it out */
data->sf_tach_to_pwm = lm93_read_byte(client,
LM93_REG_SF_TACH_TO_PWM);
data->sf_tach_to_pwm &= ~0x3 << nr * 2;
data->sf_tach_to_pwm |= results[0] << nr * 2;
lm93_write_byte(client, LM93_REG_SF_TACH_TO_PWM,
data->sf_tach_to_pwm);
/* can't enable if pwm freq is 22.5KHz */
if (results[0]) {
u8 ctl4 = lm93_read_byte(client,
LM93_REG_PWM_CTL(results[0]-1,
LM93_PWM_CTL4));
if ((ctl4 & 0x07) == 0)
results[0] = 0;
}
/* insert the enable bit and write it out */
data->sfc2 = lm93_read_byte(client,
LM93_REG_SFC2);
if (results[0])
data->sfc2 |= 1 << nr;
else
data->sfc2 &= ~1 << nr;
lm93_write_byte(client, LM93_REG_SFC2,
data->sfc2);
lm93_write_fan_smart_tach(client, data, nr,
results[0]);
}
up(&data->update_lock);
}
}
@@ -1289,21 +1545,223 @@ void lm93_prochot_short(struct i2c_client *client, int operation, int ctl_name,
}
}
#if 0
void lm78_alarms(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
void lm93_prochot_override(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct lm78_data *data = client->data;
struct lm93_data *data = client->data;
if (ctl_name != LM93_SYSCTL_PROCHOT_OVERRIDE)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm78_update_client(client);
results[0] = ALARMS_FROM_REG(data->alarms);
lm93_update_client(client);
results[0] = (data->prochot_override & 0x80) ? 1 : 0;
results[1] = (data->prochot_override & 0x40) ? 1 : 0;
results[2] = data->prochot_override & 0x0f;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
/* grab old values */
int force2 = (data->prochot_override & 0x40) ? 1 : 0;
int prochot = data->prochot_override & 0x0f;
down(&data->update_lock);
if (*nrels_mag >= 3) {
data->prochot_override = LM93_PROCHOT_OVERRIDE_TO_REG(
results[0], results[1], results[2]);
}
if (*nrels_mag == 2) {
data->prochot_override = LM93_PROCHOT_OVERRIDE_TO_REG(
results[0], results[1], prochot);
}
if (*nrels_mag == 1) {
data->prochot_override = LM93_PROCHOT_OVERRIDE_TO_REG(
results[0], force2, prochot);
}
if (*nrels_mag >= 1) {
lm93_write_byte(client, LM93_REG_PROCHOT_OVERRIDE,
data->prochot_override);
}
up(&data->update_lock);
}
}
void lm93_prochot_interval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
if (ctl_name != LM93_SYSCTL_PROCHOT_INTERVAL)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 2;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
results[0] = LM93_INTERVAL_FROM_REG(
data->prochot_interval & 0x0f);
results[1] = LM93_INTERVAL_FROM_REG(
(data->prochot_interval & 0xf0) >> 4);
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
down(&data->update_lock);
if (*nrels_mag >= 2) {
results[1] = SENSORS_LIMIT(results[1], 0, 9);
data->prochot_interval =
(LM93_INTERVAL_TO_REG(results[1] << 4)) |
(data->prochot_interval & 0x0f);
}
if (*nrels_mag >= 1) {
results[0] = SENSORS_LIMIT(results[0], 0, 9);
data->prochot_interval =
(data->prochot_interval & 0xf0) |
LM93_INTERVAL_TO_REG(results[0]);
lm93_write_byte(client, LM93_REG_PROCHOT_INTERVAL,
data->prochot_interval);
}
up(&data->update_lock);
}
}
void lm93_vrdhot(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
int nr = ctl_name - LM93_SYSCTL_VRDHOT1;
if (0 > nr || nr > 1)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
results[0] = data->block1.host_status_1 & (1 << (nr+4)) ? 1 : 0;
*nrels_mag = 1;
}
}
#endif
void lm93_gpio(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
if (ctl_name != LM93_SYSCTL_GPIO)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
results[0] = LM93_GPI_FROM_REG(data->gpi);
*nrels_mag = 1;
}
}
void lm93_pwm(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
int nr = ctl_name - LM93_SYSCTL_PWM1;
u8 ctl2, ctl4;
if (0 > nr || nr > 1)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
ctl2 = data->block9[nr][LM93_PWM_CTL2];
ctl4 = data->block9[nr][LM93_PWM_CTL4];
results[1] = (ctl2 & 0x01) ? 1 : 0;
if (results[1]) /* show user commanded value if enabled */
results[0] = data->pwm_override[nr];
else /* show present h/w value if manual pwm disabled */
results[0] = LM93_PWM_FROM_REG(ctl2, (ctl4 & 0x07) ?
LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ);
*nrels_mag = 2;
}
else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
down(&data->update_lock);
ctl2 = lm93_read_byte(
client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2));
ctl4 = lm93_read_byte(
client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4));
ctl2 = (ctl2 & 0x0f) |
LM93_PWM_TO_REG(results[0], (ctl4 & 0x07) ?
LM93_PWM_MAP_LO_FREQ :
LM93_PWM_MAP_HI_FREQ);
if (*nrels_mag >= 2) {
if (results[1])
ctl2 |= 0x01;
else
ctl2 &= ~0x01;
}
/* save user commanded value */
data->pwm_override[nr] =
LM93_PWM_FROM_REG(ctl2, (ctl4 & 0x07) ?
LM93_PWM_MAP_LO_FREQ :
LM93_PWM_MAP_HI_FREQ);
lm93_write_byte(client,
LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2), ctl2);
up(&data->update_lock);
}
}
}
void lm93_pwm_freq(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
int nr = ctl_name - LM93_SYSCTL_PWM1_FREQ;
u8 ctl4;
if (0 > nr || nr > 1)
return; /* ERROR */
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
ctl4 = data->block9[nr][LM93_PWM_CTL4];
results[0] = LM93_PWM_FREQ_FROM_REG(ctl4);
*nrels_mag = 1;
}
else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
down(&data->update_lock);
ctl4 = lm93_read_byte( client,
LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4));
ctl4 = (ctl4 & 0xf8) | LM93_PWM_FREQ_TO_REG(results[0]);
data->block9[nr][LM93_PWM_CTL4] = ctl4;
/* ctl4 == 0 -> 22.5KHz -> disable smart tach */
if (!ctl4)
lm93_disable_fan_smart_tach(client, data, nr);
lm93_write_byte(client,
LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4), ctl4);
up(&data->update_lock);
}
}
}
void lm93_alarms(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm93_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
lm93_update_client(client);
results[0] = LM93_ALARMS_FROM_REG(data->block1);
*nrels_mag = 1;
}
}
static int __init lm93_init(void)
{