Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
1c65dc36
Commit
1c65dc36
authored
Dec 04, 2012
by
Guenter Roeck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
hwmon: (nct6775) Add support for fan speed attributes
Signed-off-by:
Guenter Roeck
<
linux@roeck-us.net
>
parent
aa136e5d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
518 additions
and
3 deletions
+518
-3
Documentation/hwmon/nct6775
Documentation/hwmon/nct6775
+3
-2
drivers/hwmon/nct6775.c
drivers/hwmon/nct6775.c
+515
-1
No files found.
Documentation/hwmon/nct6775
View file @
1c65dc36
...
@@ -53,8 +53,9 @@ triggered if the rotation speed has dropped below a programmable limit. On
...
@@ -53,8 +53,9 @@ triggered if the rotation speed has dropped below a programmable limit. On
NCT6775F, fan readings can be divided by a programmable divider (1, 2, 4, 8,
NCT6775F, fan readings can be divided by a programmable divider (1, 2, 4, 8,
16, 32, 64 or 128) to give the readings more range or accuracy; the other chips
16, 32, 64 or 128) to give the readings more range or accuracy; the other chips
do not have a fan speed divider. The driver sets the most suitable fan divisor
do not have a fan speed divider. The driver sets the most suitable fan divisor
itself; specifically, it doubles the divider value each time a fan speed reading
itself; specifically, it increases the divider value each time a fan speed
returns an invalid value. Some fans might not be present because they share pins
reading returns an invalid value, and it reduces it if the fan speed reading
is lower than optimal. Some fans might not be present because they share pins
with other functions.
with other functions.
Voltage sensors (also known as IN sensors) report their values in millivolts.
Voltage sensors (also known as IN sensors) report their values in millivolts.
...
...
drivers/hwmon/nct6775.c
View file @
1c65dc36
...
@@ -180,6 +180,9 @@ static const u16 NCT6775_REG_IN[] = {
...
@@ -180,6 +180,9 @@ static const u16 NCT6775_REG_IN[] = {
#define NCT6775_REG_VBAT 0x5D
#define NCT6775_REG_VBAT 0x5D
#define NCT6775_REG_DIODE 0x5E
#define NCT6775_REG_DIODE 0x5E
#define NCT6775_REG_FANDIV1 0x506
#define NCT6775_REG_FANDIV2 0x507
static
const
u16
NCT6775_REG_ALARM
[
NUM_REG_ALARM
]
=
{
0x459
,
0x45A
,
0x45B
};
static
const
u16
NCT6775_REG_ALARM
[
NUM_REG_ALARM
]
=
{
0x459
,
0x45A
,
0x45B
};
/* 0..15 voltages, 16..23 fans, 24..31 temperatures */
/* 0..15 voltages, 16..23 fans, 24..31 temperatures */
...
@@ -193,12 +196,16 @@ static const s8 NCT6775_ALARM_BITS[] = {
...
@@ -193,12 +196,16 @@ static const s8 NCT6775_ALARM_BITS[] = {
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
12
,
-
1
};
/* intrusion0, intrusion1 */
12
,
-
1
};
/* intrusion0, intrusion1 */
#define FAN_ALARM_BASE 16
#define TEMP_ALARM_BASE 24
#define TEMP_ALARM_BASE 24
#define INTRUSION_ALARM_BASE 30
#define INTRUSION_ALARM_BASE 30
static
const
u8
NCT6775_REG_CR_CASEOPEN_CLR
[]
=
{
0xe6
,
0xee
};
static
const
u8
NCT6775_REG_CR_CASEOPEN_CLR
[]
=
{
0xe6
,
0xee
};
static
const
u8
NCT6775_CR_CASEOPEN_CLR_MASK
[]
=
{
0x20
,
0x01
};
static
const
u8
NCT6775_CR_CASEOPEN_CLR_MASK
[]
=
{
0x20
,
0x01
};
static
const
u16
NCT6775_REG_FAN
[]
=
{
0x630
,
0x632
,
0x634
,
0x636
,
0x638
};
static
const
u16
NCT6775_REG_FAN_MIN
[]
=
{
0x3b
,
0x3c
,
0x3d
};
static
const
u16
NCT6775_REG_TEMP
[]
=
{
static
const
u16
NCT6775_REG_TEMP
[]
=
{
0x27
,
0x150
,
0x250
,
0x62b
,
0x62c
,
0x62d
};
0x27
,
0x150
,
0x250
,
0x62b
,
0x62c
,
0x62d
};
...
@@ -256,6 +263,8 @@ static const s8 NCT6776_ALARM_BITS[] = {
...
@@ -256,6 +263,8 @@ static const s8 NCT6776_ALARM_BITS[] = {
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
12
,
9
};
/* intrusion0, intrusion1 */
12
,
9
};
/* intrusion0, intrusion1 */
static
const
u16
NCT6776_REG_FAN_MIN
[]
=
{
0x63a
,
0x63c
,
0x63e
,
0x640
,
0x642
};
static
const
u16
NCT6776_REG_TEMP_CONFIG
[
ARRAY_SIZE
(
NCT6775_REG_TEMP
)]
=
{
static
const
u16
NCT6776_REG_TEMP_CONFIG
[
ARRAY_SIZE
(
NCT6775_REG_TEMP
)]
=
{
0x18
,
0x152
,
0x252
,
0x628
,
0x629
,
0x62A
};
0x18
,
0x152
,
0x252
,
0x628
,
0x629
,
0x62A
};
...
@@ -309,6 +318,8 @@ static const s8 NCT6779_ALARM_BITS[] = {
...
@@ -309,6 +318,8 @@ static const s8 NCT6779_ALARM_BITS[] = {
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
4
,
5
,
13
,
-
1
,
-
1
,
-
1
,
/* temp1..temp6 */
12
,
9
};
/* intrusion0, intrusion1 */
12
,
9
};
/* intrusion0, intrusion1 */
static
const
u16
NCT6779_REG_FAN
[]
=
{
0x4b0
,
0x4b2
,
0x4b4
,
0x4b6
,
0x4b8
};
static
const
u16
NCT6779_REG_TEMP
[]
=
{
0x27
,
0x150
};
static
const
u16
NCT6779_REG_TEMP
[]
=
{
0x27
,
0x150
};
static
const
u16
NCT6779_REG_TEMP_CONFIG
[
ARRAY_SIZE
(
NCT6779_REG_TEMP
)]
=
{
static
const
u16
NCT6779_REG_TEMP_CONFIG
[
ARRAY_SIZE
(
NCT6779_REG_TEMP
)]
=
{
0x18
,
0x152
};
0x18
,
0x152
};
...
@@ -363,6 +374,44 @@ static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
...
@@ -363,6 +374,44 @@ static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
* Conversions
* Conversions
*/
*/
static
unsigned
int
fan_from_reg8
(
u16
reg
,
unsigned
int
divreg
)
{
if
(
reg
==
0
||
reg
==
255
)
return
0
;
return
1350000U
/
(
reg
<<
divreg
);
}
static
unsigned
int
fan_from_reg13
(
u16
reg
,
unsigned
int
divreg
)
{
if
((
reg
&
0xff1f
)
==
0xff1f
)
return
0
;
reg
=
(
reg
&
0x1f
)
|
((
reg
&
0xff00
)
>>
3
);
if
(
reg
==
0
)
return
0
;
return
1350000U
/
reg
;
}
static
unsigned
int
fan_from_reg16
(
u16
reg
,
unsigned
int
divreg
)
{
if
(
reg
==
0
||
reg
==
0xffff
)
return
0
;
/*
* Even though the registers are 16 bit wide, the fan divisor
* still applies.
*/
return
1350000U
/
(
reg
<<
divreg
);
}
static
inline
unsigned
int
div_from_reg
(
u8
reg
)
{
return
1
<<
reg
;
}
/*
/*
* Some of the voltage inputs have internal scaling, the tables below
* Some of the voltage inputs have internal scaling, the tables below
* contain 8 (the ADC LSB in mV) * scaling factor * 100
* contain 8 (the ADC LSB in mV) * scaling factor * 100
...
@@ -411,12 +460,17 @@ struct nct6775_data {
...
@@ -411,12 +460,17 @@ struct nct6775_data {
const
u16
*
REG_VIN
;
const
u16
*
REG_VIN
;
const
u16
*
REG_IN_MINMAX
[
2
];
const
u16
*
REG_IN_MINMAX
[
2
];
const
u16
*
REG_TEMP_SOURCE
;
/* temp register sources */
const
u16
*
REG_FAN
;
const
u16
*
REG_FAN_MIN
;
const
u16
*
REG_TEMP_SOURCE
;
/* temp register sources */
const
u16
*
REG_TEMP_OFFSET
;
const
u16
*
REG_TEMP_OFFSET
;
const
u16
*
REG_ALARM
;
const
u16
*
REG_ALARM
;
unsigned
int
(
*
fan_from_reg
)(
u16
reg
,
unsigned
int
divreg
);
unsigned
int
(
*
fan_from_reg_min
)(
u16
reg
,
unsigned
int
divreg
);
struct
mutex
update_lock
;
struct
mutex
update_lock
;
bool
valid
;
/* true if following fields are valid */
bool
valid
;
/* true if following fields are valid */
unsigned
long
last_updated
;
/* In jiffies */
unsigned
long
last_updated
;
/* In jiffies */
...
@@ -425,6 +479,12 @@ struct nct6775_data {
...
@@ -425,6 +479,12 @@ struct nct6775_data {
u8
bank
;
/* current register bank */
u8
bank
;
/* current register bank */
u8
in_num
;
/* number of in inputs we have */
u8
in_num
;
/* number of in inputs we have */
u8
in
[
15
][
3
];
/* [0]=in, [1]=in_max, [2]=in_min */
u8
in
[
15
][
3
];
/* [0]=in, [1]=in_max, [2]=in_min */
unsigned
int
rpm
[
5
];
u16
fan_min
[
5
];
u8
fan_div
[
5
];
u8
has_fan
;
/* some fan inputs can be disabled */
u8
has_fan_min
;
/* some fans don't have min register */
bool
has_fan_div
;
u8
temp_fixed_num
;
/* 3 or 6 */
u8
temp_fixed_num
;
/* 3 or 6 */
u8
temp_type
[
NUM_TEMP_FIXED
];
u8
temp_type
[
NUM_TEMP_FIXED
];
...
@@ -556,6 +616,153 @@ static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
...
@@ -556,6 +616,153 @@ static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
return
nct6775_write_value
(
data
,
reg
,
value
);
return
nct6775_write_value
(
data
,
reg
,
value
);
}
}
/* This function assumes that the caller holds data->update_lock */
static
void
nct6775_write_fan_div
(
struct
nct6775_data
*
data
,
int
nr
)
{
u8
reg
;
switch
(
nr
)
{
case
0
:
reg
=
(
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV1
)
&
0x70
)
|
(
data
->
fan_div
[
0
]
&
0x7
);
nct6775_write_value
(
data
,
NCT6775_REG_FANDIV1
,
reg
);
break
;
case
1
:
reg
=
(
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV1
)
&
0x7
)
|
((
data
->
fan_div
[
1
]
<<
4
)
&
0x70
);
nct6775_write_value
(
data
,
NCT6775_REG_FANDIV1
,
reg
);
break
;
case
2
:
reg
=
(
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV2
)
&
0x70
)
|
(
data
->
fan_div
[
2
]
&
0x7
);
nct6775_write_value
(
data
,
NCT6775_REG_FANDIV2
,
reg
);
break
;
case
3
:
reg
=
(
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV2
)
&
0x7
)
|
((
data
->
fan_div
[
3
]
<<
4
)
&
0x70
);
nct6775_write_value
(
data
,
NCT6775_REG_FANDIV2
,
reg
);
break
;
}
}
static
void
nct6775_write_fan_div_common
(
struct
nct6775_data
*
data
,
int
nr
)
{
if
(
data
->
kind
==
nct6775
)
nct6775_write_fan_div
(
data
,
nr
);
}
static
void
nct6775_update_fan_div
(
struct
nct6775_data
*
data
)
{
u8
i
;
i
=
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV1
);
data
->
fan_div
[
0
]
=
i
&
0x7
;
data
->
fan_div
[
1
]
=
(
i
&
0x70
)
>>
4
;
i
=
nct6775_read_value
(
data
,
NCT6775_REG_FANDIV2
);
data
->
fan_div
[
2
]
=
i
&
0x7
;
if
(
data
->
has_fan
&
(
1
<<
3
))
data
->
fan_div
[
3
]
=
(
i
&
0x70
)
>>
4
;
}
static
void
nct6775_update_fan_div_common
(
struct
nct6775_data
*
data
)
{
if
(
data
->
kind
==
nct6775
)
nct6775_update_fan_div
(
data
);
}
static
void
nct6775_init_fan_div
(
struct
nct6775_data
*
data
)
{
int
i
;
nct6775_update_fan_div_common
(
data
);
/*
* For all fans, start with highest divider value if the divider
* register is not initialized. This ensures that we get a
* reading from the fan count register, even if it is not optimal.
* We'll compute a better divider later on.
*/
for
(
i
=
0
;
i
<
3
;
i
++
)
{
if
(
!
(
data
->
has_fan
&
(
1
<<
i
)))
continue
;
if
(
data
->
fan_div
[
i
]
==
0
)
{
data
->
fan_div
[
i
]
=
7
;
nct6775_write_fan_div_common
(
data
,
i
);
}
}
}
static
void
nct6775_init_fan_common
(
struct
device
*
dev
,
struct
nct6775_data
*
data
)
{
int
i
;
u8
reg
;
if
(
data
->
has_fan_div
)
nct6775_init_fan_div
(
data
);
/*
* If fan_min is not set (0), set it to 0xff to disable it. This
* prevents the unnecessary warning when fanX_min is reported as 0.
*/
for
(
i
=
0
;
i
<
5
;
i
++
)
{
if
(
data
->
has_fan_min
&
(
1
<<
i
))
{
reg
=
nct6775_read_value
(
data
,
data
->
REG_FAN_MIN
[
i
]);
if
(
!
reg
)
nct6775_write_value
(
data
,
data
->
REG_FAN_MIN
[
i
],
data
->
has_fan_div
?
0xff
:
0xff1f
);
}
}
}
static
void
nct6775_select_fan_div
(
struct
device
*
dev
,
struct
nct6775_data
*
data
,
int
nr
,
u16
reg
)
{
u8
fan_div
=
data
->
fan_div
[
nr
];
u16
fan_min
;
if
(
!
data
->
has_fan_div
)
return
;
/*
* If we failed to measure the fan speed, or the reported value is not
* in the optimal range, and the clock divider can be modified,
* let's try that for next time.
*/
if
(
reg
==
0x00
&&
fan_div
<
0x07
)
fan_div
++
;
else
if
(
reg
!=
0x00
&&
reg
<
0x30
&&
fan_div
>
0
)
fan_div
--
;
if
(
fan_div
!=
data
->
fan_div
[
nr
])
{
dev_dbg
(
dev
,
"Modifying fan%d clock divider from %u to %u
\n
"
,
nr
+
1
,
div_from_reg
(
data
->
fan_div
[
nr
]),
div_from_reg
(
fan_div
));
/* Preserve min limit if possible */
if
(
data
->
has_fan_min
&
(
1
<<
nr
))
{
fan_min
=
data
->
fan_min
[
nr
];
if
(
fan_div
>
data
->
fan_div
[
nr
])
{
if
(
fan_min
!=
255
&&
fan_min
>
1
)
fan_min
>>=
1
;
}
else
{
if
(
fan_min
!=
255
)
{
fan_min
<<=
1
;
if
(
fan_min
>
254
)
fan_min
=
254
;
}
}
if
(
fan_min
!=
data
->
fan_min
[
nr
])
{
data
->
fan_min
[
nr
]
=
fan_min
;
nct6775_write_value
(
data
,
data
->
REG_FAN_MIN
[
nr
],
fan_min
);
}
}
data
->
fan_div
[
nr
]
=
fan_div
;
nct6775_write_fan_div_common
(
data
,
nr
);
}
}
static
struct
nct6775_data
*
nct6775_update_device
(
struct
device
*
dev
)
static
struct
nct6775_data
*
nct6775_update_device
(
struct
device
*
dev
)
{
{
struct
nct6775_data
*
data
=
dev_get_drvdata
(
dev
);
struct
nct6775_data
*
data
=
dev_get_drvdata
(
dev
);
...
@@ -565,6 +772,9 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
...
@@ -565,6 +772,9 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
if
(
time_after
(
jiffies
,
data
->
last_updated
+
HZ
+
HZ
/
2
)
if
(
time_after
(
jiffies
,
data
->
last_updated
+
HZ
+
HZ
/
2
)
||
!
data
->
valid
)
{
||
!
data
->
valid
)
{
/* Fan clock dividers */
nct6775_update_fan_div_common
(
data
);
/* Measured voltages and limits */
/* Measured voltages and limits */
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
{
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
{
if
(
!
(
data
->
have_in
&
(
1
<<
i
)))
if
(
!
(
data
->
have_in
&
(
1
<<
i
)))
...
@@ -578,6 +788,24 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
...
@@ -578,6 +788,24 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
data
->
REG_IN_MINMAX
[
1
][
i
]);
data
->
REG_IN_MINMAX
[
1
][
i
]);
}
}
/* Measured fan speeds and limits */
for
(
i
=
0
;
i
<
5
;
i
++
)
{
u16
reg
;
if
(
!
(
data
->
has_fan
&
(
1
<<
i
)))
continue
;
reg
=
nct6775_read_value
(
data
,
data
->
REG_FAN
[
i
]);
data
->
rpm
[
i
]
=
data
->
fan_from_reg
(
reg
,
data
->
fan_div
[
i
]);
if
(
data
->
has_fan_min
&
(
1
<<
i
))
data
->
fan_min
[
i
]
=
nct6775_read_value
(
data
,
data
->
REG_FAN_MIN
[
i
]);
nct6775_select_fan_div
(
dev
,
data
,
i
,
reg
);
}
/* Measured temperatures and limits */
/* Measured temperatures and limits */
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
...
@@ -874,6 +1102,166 @@ static const struct attribute_group nct6775_group_in[15] = {
...
@@ -874,6 +1102,166 @@ static const struct attribute_group nct6775_group_in[15] = {
{
.
attrs
=
nct6775_attributes_in
[
14
]
},
{
.
attrs
=
nct6775_attributes_in
[
14
]
},
};
};
static
ssize_t
show_fan
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
nct6775_data
*
data
=
nct6775_update_device
(
dev
);
struct
sensor_device_attribute
*
sattr
=
to_sensor_dev_attr
(
attr
);
int
nr
=
sattr
->
index
;
return
sprintf
(
buf
,
"%d
\n
"
,
data
->
rpm
[
nr
]);
}
static
ssize_t
show_fan_min
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
nct6775_data
*
data
=
nct6775_update_device
(
dev
);
struct
sensor_device_attribute
*
sattr
=
to_sensor_dev_attr
(
attr
);
int
nr
=
sattr
->
index
;
return
sprintf
(
buf
,
"%d
\n
"
,
data
->
fan_from_reg_min
(
data
->
fan_min
[
nr
],
data
->
fan_div
[
nr
]));
}
static
ssize_t
show_fan_div
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
nct6775_data
*
data
=
nct6775_update_device
(
dev
);
struct
sensor_device_attribute
*
sattr
=
to_sensor_dev_attr
(
attr
);
int
nr
=
sattr
->
index
;
return
sprintf
(
buf
,
"%u
\n
"
,
div_from_reg
(
data
->
fan_div
[
nr
]));
}
static
ssize_t
store_fan_min
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
nct6775_data
*
data
=
dev_get_drvdata
(
dev
);
struct
sensor_device_attribute
*
sattr
=
to_sensor_dev_attr
(
attr
);
int
nr
=
sattr
->
index
;
unsigned
long
val
;
int
err
;
unsigned
int
reg
;
u8
new_div
;
err
=
kstrtoul
(
buf
,
10
,
&
val
);
if
(
err
<
0
)
return
err
;
mutex_lock
(
&
data
->
update_lock
);
if
(
!
data
->
has_fan_div
)
{
/* NCT6776F or NCT6779D; we know this is a 13 bit register */
if
(
!
val
)
{
val
=
0xff1f
;
}
else
{
if
(
val
>
1350000U
)
val
=
135000U
;
val
=
1350000U
/
val
;
val
=
(
val
&
0x1f
)
|
((
val
<<
3
)
&
0xff00
);
}
data
->
fan_min
[
nr
]
=
val
;
goto
write_min
;
/* Leave fan divider alone */
}
if
(
!
val
)
{
/* No min limit, alarm disabled */
data
->
fan_min
[
nr
]
=
255
;
new_div
=
data
->
fan_div
[
nr
];
/* No change */
dev_info
(
dev
,
"fan%u low limit and alarm disabled
\n
"
,
nr
+
1
);
goto
write_div
;
}
reg
=
1350000U
/
val
;
if
(
reg
>=
128
*
255
)
{
/*
* Speed below this value cannot possibly be represented,
* even with the highest divider (128)
*/
data
->
fan_min
[
nr
]
=
254
;
new_div
=
7
;
/* 128 == (1 << 7) */
dev_warn
(
dev
,
"fan%u low limit %lu below minimum %u, set to minimum
\n
"
,
nr
+
1
,
val
,
data
->
fan_from_reg_min
(
254
,
7
));
}
else
if
(
!
reg
)
{
/*
* Speed above this value cannot possibly be represented,
* even with the lowest divider (1)
*/
data
->
fan_min
[
nr
]
=
1
;
new_div
=
0
;
/* 1 == (1 << 0) */
dev_warn
(
dev
,
"fan%u low limit %lu above maximum %u, set to maximum
\n
"
,
nr
+
1
,
val
,
data
->
fan_from_reg_min
(
1
,
0
));
}
else
{
/*
* Automatically pick the best divider, i.e. the one such
* that the min limit will correspond to a register value
* in the 96..192 range
*/
new_div
=
0
;
while
(
reg
>
192
&&
new_div
<
7
)
{
reg
>>=
1
;
new_div
++
;
}
data
->
fan_min
[
nr
]
=
reg
;
}
write_div:
/*
* Write both the fan clock divider (if it changed) and the new
* fan min (unconditionally)
*/
if
(
new_div
!=
data
->
fan_div
[
nr
])
{
dev_dbg
(
dev
,
"fan%u clock divider changed from %u to %u
\n
"
,
nr
+
1
,
div_from_reg
(
data
->
fan_div
[
nr
]),
div_from_reg
(
new_div
));
data
->
fan_div
[
nr
]
=
new_div
;
nct6775_write_fan_div_common
(
data
,
nr
);
/* Give the chip time to sample a new speed value */
data
->
last_updated
=
jiffies
;
}
write_min:
nct6775_write_value
(
data
,
data
->
REG_FAN_MIN
[
nr
],
data
->
fan_min
[
nr
]);
mutex_unlock
(
&
data
->
update_lock
);
return
count
;
}
static
struct
sensor_device_attribute
sda_fan_input
[]
=
{
SENSOR_ATTR
(
fan1_input
,
S_IRUGO
,
show_fan
,
NULL
,
0
),
SENSOR_ATTR
(
fan2_input
,
S_IRUGO
,
show_fan
,
NULL
,
1
),
SENSOR_ATTR
(
fan3_input
,
S_IRUGO
,
show_fan
,
NULL
,
2
),
SENSOR_ATTR
(
fan4_input
,
S_IRUGO
,
show_fan
,
NULL
,
3
),
SENSOR_ATTR
(
fan5_input
,
S_IRUGO
,
show_fan
,
NULL
,
4
),
};
static
struct
sensor_device_attribute
sda_fan_alarm
[]
=
{
SENSOR_ATTR
(
fan1_alarm
,
S_IRUGO
,
show_alarm
,
NULL
,
FAN_ALARM_BASE
),
SENSOR_ATTR
(
fan2_alarm
,
S_IRUGO
,
show_alarm
,
NULL
,
FAN_ALARM_BASE
+
1
),
SENSOR_ATTR
(
fan3_alarm
,
S_IRUGO
,
show_alarm
,
NULL
,
FAN_ALARM_BASE
+
2
),
SENSOR_ATTR
(
fan4_alarm
,
S_IRUGO
,
show_alarm
,
NULL
,
FAN_ALARM_BASE
+
3
),
SENSOR_ATTR
(
fan5_alarm
,
S_IRUGO
,
show_alarm
,
NULL
,
FAN_ALARM_BASE
+
4
),
};
static
struct
sensor_device_attribute
sda_fan_min
[]
=
{
SENSOR_ATTR
(
fan1_min
,
S_IWUSR
|
S_IRUGO
,
show_fan_min
,
store_fan_min
,
0
),
SENSOR_ATTR
(
fan2_min
,
S_IWUSR
|
S_IRUGO
,
show_fan_min
,
store_fan_min
,
1
),
SENSOR_ATTR
(
fan3_min
,
S_IWUSR
|
S_IRUGO
,
show_fan_min
,
store_fan_min
,
2
),
SENSOR_ATTR
(
fan4_min
,
S_IWUSR
|
S_IRUGO
,
show_fan_min
,
store_fan_min
,
3
),
SENSOR_ATTR
(
fan5_min
,
S_IWUSR
|
S_IRUGO
,
show_fan_min
,
store_fan_min
,
4
),
};
static
struct
sensor_device_attribute
sda_fan_div
[]
=
{
SENSOR_ATTR
(
fan1_div
,
S_IRUGO
,
show_fan_div
,
NULL
,
0
),
SENSOR_ATTR
(
fan2_div
,
S_IRUGO
,
show_fan_div
,
NULL
,
1
),
SENSOR_ATTR
(
fan3_div
,
S_IRUGO
,
show_fan_div
,
NULL
,
2
),
SENSOR_ATTR
(
fan4_div
,
S_IRUGO
,
show_fan_div
,
NULL
,
3
),
SENSOR_ATTR
(
fan5_div
,
S_IRUGO
,
show_fan_div
,
NULL
,
4
),
};
static
ssize_t
static
ssize_t
show_temp_label
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
show_temp_label
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
{
...
@@ -1228,6 +1616,12 @@ static void nct6775_device_remove_files(struct device *dev)
...
@@ -1228,6 +1616,12 @@ static void nct6775_device_remove_files(struct device *dev)
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
sysfs_remove_group
(
&
dev
->
kobj
,
&
nct6775_group_in
[
i
]);
sysfs_remove_group
(
&
dev
->
kobj
,
&
nct6775_group_in
[
i
]);
for
(
i
=
0
;
i
<
5
;
i
++
)
{
device_remove_file
(
dev
,
&
sda_fan_input
[
i
].
dev_attr
);
device_remove_file
(
dev
,
&
sda_fan_alarm
[
i
].
dev_attr
);
device_remove_file
(
dev
,
&
sda_fan_div
[
i
].
dev_attr
);
device_remove_file
(
dev
,
&
sda_fan_min
[
i
].
dev_attr
);
}
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
continue
;
continue
;
...
@@ -1294,6 +1688,75 @@ static inline void nct6775_init_device(struct nct6775_data *data)
...
@@ -1294,6 +1688,75 @@ static inline void nct6775_init_device(struct nct6775_data *data)
}
}
}
}
static
int
nct6775_check_fan_inputs
(
const
struct
nct6775_sio_data
*
sio_data
,
struct
nct6775_data
*
data
)
{
int
regval
;
bool
fan3pin
,
fan3min
,
fan4pin
,
fan4min
,
fan5pin
;
int
ret
;
ret
=
superio_enter
(
sio_data
->
sioreg
);
if
(
ret
)
return
ret
;
/* fan4 and fan5 share some pins with the GPIO and serial flash */
if
(
data
->
kind
==
nct6775
)
{
regval
=
superio_inb
(
sio_data
->
sioreg
,
0x2c
);
fan3pin
=
regval
&
(
1
<<
6
);
fan3min
=
fan3pin
;
/* On NCT6775, fan4 shares pins with the fdc interface */
fan4pin
=
!
(
superio_inb
(
sio_data
->
sioreg
,
0x2A
)
&
0x80
);
fan4min
=
0
;
fan5pin
=
0
;
}
else
if
(
data
->
kind
==
nct6776
)
{
bool
gpok
=
superio_inb
(
sio_data
->
sioreg
,
0x27
)
&
0x80
;
superio_select
(
sio_data
->
sioreg
,
NCT6775_LD_HWM
);
regval
=
superio_inb
(
sio_data
->
sioreg
,
SIO_REG_ENABLE
);
if
(
regval
&
0x80
)
fan3pin
=
gpok
;
else
fan3pin
=
!
(
superio_inb
(
sio_data
->
sioreg
,
0x24
)
&
0x40
);
if
(
regval
&
0x40
)
fan4pin
=
gpok
;
else
fan4pin
=
superio_inb
(
sio_data
->
sioreg
,
0x1C
)
&
0x01
;
if
(
regval
&
0x20
)
fan5pin
=
gpok
;
else
fan5pin
=
superio_inb
(
sio_data
->
sioreg
,
0x1C
)
&
0x02
;
fan4min
=
fan4pin
;
fan3min
=
fan3pin
;
}
else
{
/* NCT6779D */
regval
=
superio_inb
(
sio_data
->
sioreg
,
0x1c
);
fan3pin
=
!
(
regval
&
(
1
<<
5
));
fan4pin
=
!
(
regval
&
(
1
<<
6
));
fan5pin
=
!
(
regval
&
(
1
<<
7
));
fan3min
=
fan3pin
;
fan4min
=
fan4pin
;
}
superio_exit
(
sio_data
->
sioreg
);
data
->
has_fan
=
data
->
has_fan_min
=
0x03
;
/* fan1 and fan2 */
data
->
has_fan
|=
fan3pin
<<
2
;
data
->
has_fan_min
|=
fan3min
<<
2
;
data
->
has_fan
|=
(
fan4pin
<<
3
)
|
(
fan5pin
<<
4
);
data
->
has_fan_min
|=
(
fan4min
<<
3
)
|
(
fan5pin
<<
4
);
return
0
;
}
static
int
nct6775_probe
(
struct
platform_device
*
pdev
)
static
int
nct6775_probe
(
struct
platform_device
*
pdev
)
{
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
device
*
dev
=
&
pdev
->
dev
;
...
@@ -1327,10 +1790,14 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1327,10 +1790,14 @@ static int nct6775_probe(struct platform_device *pdev)
switch
(
data
->
kind
)
{
switch
(
data
->
kind
)
{
case
nct6775
:
case
nct6775
:
data
->
in_num
=
9
;
data
->
in_num
=
9
;
data
->
has_fan_div
=
true
;
data
->
temp_fixed_num
=
3
;
data
->
temp_fixed_num
=
3
;
data
->
ALARM_BITS
=
NCT6775_ALARM_BITS
;
data
->
ALARM_BITS
=
NCT6775_ALARM_BITS
;
data
->
fan_from_reg
=
fan_from_reg16
;
data
->
fan_from_reg_min
=
fan_from_reg8
;
data
->
temp_label
=
nct6775_temp_label
;
data
->
temp_label
=
nct6775_temp_label
;
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6775_temp_label
);
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6775_temp_label
);
...
@@ -1340,6 +1807,8 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1340,6 +1807,8 @@ static int nct6775_probe(struct platform_device *pdev)
data
->
REG_VIN
=
NCT6775_REG_IN
;
data
->
REG_VIN
=
NCT6775_REG_IN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_FAN
=
NCT6775_REG_FAN
;
data
->
REG_FAN_MIN
=
NCT6775_REG_FAN_MIN
;
data
->
REG_TEMP_OFFSET
=
NCT6775_REG_TEMP_OFFSET
;
data
->
REG_TEMP_OFFSET
=
NCT6775_REG_TEMP_OFFSET
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_ALARM
=
NCT6775_REG_ALARM
;
data
->
REG_ALARM
=
NCT6775_REG_ALARM
;
...
@@ -1355,10 +1824,14 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1355,10 +1824,14 @@ static int nct6775_probe(struct platform_device *pdev)
break
;
break
;
case
nct6776
:
case
nct6776
:
data
->
in_num
=
9
;
data
->
in_num
=
9
;
data
->
has_fan_div
=
false
;
data
->
temp_fixed_num
=
3
;
data
->
temp_fixed_num
=
3
;
data
->
ALARM_BITS
=
NCT6776_ALARM_BITS
;
data
->
ALARM_BITS
=
NCT6776_ALARM_BITS
;
data
->
fan_from_reg
=
fan_from_reg13
;
data
->
fan_from_reg_min
=
fan_from_reg13
;
data
->
temp_label
=
nct6776_temp_label
;
data
->
temp_label
=
nct6776_temp_label
;
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6776_temp_label
);
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6776_temp_label
);
...
@@ -1368,6 +1841,8 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1368,6 +1841,8 @@ static int nct6775_probe(struct platform_device *pdev)
data
->
REG_VIN
=
NCT6775_REG_IN
;
data
->
REG_VIN
=
NCT6775_REG_IN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_FAN
=
NCT6775_REG_FAN
;
data
->
REG_FAN_MIN
=
NCT6776_REG_FAN_MIN
;
data
->
REG_TEMP_OFFSET
=
NCT6775_REG_TEMP_OFFSET
;
data
->
REG_TEMP_OFFSET
=
NCT6775_REG_TEMP_OFFSET
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_ALARM
=
NCT6775_REG_ALARM
;
data
->
REG_ALARM
=
NCT6775_REG_ALARM
;
...
@@ -1383,10 +1858,14 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1383,10 +1858,14 @@ static int nct6775_probe(struct platform_device *pdev)
break
;
break
;
case
nct6779
:
case
nct6779
:
data
->
in_num
=
15
;
data
->
in_num
=
15
;
data
->
has_fan_div
=
false
;
data
->
temp_fixed_num
=
6
;
data
->
temp_fixed_num
=
6
;
data
->
ALARM_BITS
=
NCT6779_ALARM_BITS
;
data
->
ALARM_BITS
=
NCT6779_ALARM_BITS
;
data
->
fan_from_reg
=
fan_from_reg13
;
data
->
fan_from_reg_min
=
fan_from_reg13
;
data
->
temp_label
=
nct6779_temp_label
;
data
->
temp_label
=
nct6779_temp_label
;
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6779_temp_label
);
data
->
temp_label_num
=
ARRAY_SIZE
(
nct6779_temp_label
);
...
@@ -1396,6 +1875,8 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1396,6 +1875,8 @@ static int nct6775_probe(struct platform_device *pdev)
data
->
REG_VIN
=
NCT6779_REG_IN
;
data
->
REG_VIN
=
NCT6779_REG_IN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
0
]
=
NCT6775_REG_IN_MIN
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_IN_MINMAX
[
1
]
=
NCT6775_REG_IN_MAX
;
data
->
REG_FAN
=
NCT6779_REG_FAN
;
data
->
REG_FAN_MIN
=
NCT6776_REG_FAN_MIN
;
data
->
REG_TEMP_OFFSET
=
NCT6779_REG_TEMP_OFFSET
;
data
->
REG_TEMP_OFFSET
=
NCT6779_REG_TEMP_OFFSET
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_TEMP_SOURCE
=
NCT6775_REG_TEMP_SOURCE
;
data
->
REG_ALARM
=
NCT6779_REG_ALARM
;
data
->
REG_ALARM
=
NCT6779_REG_ALARM
;
...
@@ -1575,6 +2056,13 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1575,6 +2056,13 @@ static int nct6775_probe(struct platform_device *pdev)
if
(
err
)
if
(
err
)
return
err
;
return
err
;
err
=
nct6775_check_fan_inputs
(
sio_data
,
data
);
if
(
err
)
goto
exit_remove
;
/* Read fan clock dividers immediately */
nct6775_init_fan_common
(
dev
,
data
);
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
{
for
(
i
=
0
;
i
<
data
->
in_num
;
i
++
)
{
if
(
!
(
data
->
have_in
&
(
1
<<
i
)))
if
(
!
(
data
->
have_in
&
(
1
<<
i
)))
continue
;
continue
;
...
@@ -1583,6 +2071,32 @@ static int nct6775_probe(struct platform_device *pdev)
...
@@ -1583,6 +2071,32 @@ static int nct6775_probe(struct platform_device *pdev)
goto
exit_remove
;
goto
exit_remove
;
}
}
for
(
i
=
0
;
i
<
5
;
i
++
)
{
if
(
data
->
has_fan
&
(
1
<<
i
))
{
err
=
device_create_file
(
dev
,
&
sda_fan_input
[
i
].
dev_attr
);
if
(
err
)
goto
exit_remove
;
err
=
device_create_file
(
dev
,
&
sda_fan_alarm
[
i
].
dev_attr
);
if
(
err
)
goto
exit_remove
;
if
(
data
->
kind
!=
nct6776
&&
data
->
kind
!=
nct6779
)
{
err
=
device_create_file
(
dev
,
&
sda_fan_div
[
i
].
dev_attr
);
if
(
err
)
goto
exit_remove
;
}
if
(
data
->
has_fan_min
&
(
1
<<
i
))
{
err
=
device_create_file
(
dev
,
&
sda_fan_min
[
i
].
dev_attr
);
if
(
err
)
goto
exit_remove
;
}
}
}
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
for
(
i
=
0
;
i
<
NUM_TEMP
;
i
++
)
{
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
if
(
!
(
data
->
have_temp
&
(
1
<<
i
)))
continue
;
continue
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment