Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
1c65dc36
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
1c65dc36
编写于
12月 04, 2012
作者:
G
Guenter Roeck
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
hwmon: (nct6775) Add support for fan speed attributes
Signed-off-by:
N
Guenter Roeck
<
linux@roeck-us.net
>
上级
aa136e5d
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
518 addition
and
3 deletion
+518
-3
Documentation/hwmon/nct6775
Documentation/hwmon/nct6775
+3
-2
drivers/hwmon/nct6775.c
drivers/hwmon/nct6775.c
+515
-1
未找到文件。
Documentation/hwmon/nct6775
浏览文件 @
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
浏览文件 @
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
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录