[celestica] change to use lm75 standard driver (#19)
This commit is contained in:
parent
ec9dc57704
commit
025dad0e11
@ -36,33 +36,30 @@ chip "dps460-i2c-*-5b"
|
|||||||
set in3_crit 13.0
|
set in3_crit 13.0
|
||||||
set in3_lcrit 11.0
|
set in3_lcrit 11.0
|
||||||
|
|
||||||
# These sensors located on CPU Board.
|
|
||||||
chip "dx010_lm75b-i2c-*-48"
|
|
||||||
label temp1 "CPU_external temp sensor"
|
|
||||||
set temp1_max 43
|
|
||||||
set temp1_max_hyst 28
|
|
||||||
|
|
||||||
chip "dx010_lm75b-i2c-*-4e"
|
|
||||||
label temp1 "Rear_panel temp sensor"
|
|
||||||
set temp1_max 43
|
|
||||||
set temp1_max_hyst 28
|
|
||||||
|
|
||||||
# These sensors located on Main Switch Board.
|
# These sensors located on Main Switch Board.
|
||||||
bus "i2c-5" "i2c-1-mux (chan_id 3)"
|
chip "lm75b-i2c-*-48"
|
||||||
chip "dx010_lm75b-i2c-5-48"
|
label temp1 "Front-panel temp sensor 1"
|
||||||
label temp1 "Front_panel left temp sensor"
|
|
||||||
set temp1_max 43
|
set temp1_max 43
|
||||||
set temp1_max_hyst 28
|
set temp1_max_hyst 28
|
||||||
|
|
||||||
bus "i2c-6" "i2c-1-mux (chan_id 4)"
|
chip "lm75b-i2c-*-49"
|
||||||
chip "dx010_lm75b-i2c-6-49"
|
label temp1 "Front-panel temp sensor 2"
|
||||||
label temp1 "Front_panel right temp sensor"
|
|
||||||
set temp1_max 43
|
set temp1_max 43
|
||||||
set temp1_max_hyst 28
|
set temp1_max_hyst 28
|
||||||
|
|
||||||
bus "i2c-7" "i2c-1-mux (chan_id 5)"
|
chip "lm75b-i2c-*-4a"
|
||||||
chip "dx010_lm75b-i2c-7-4a"
|
label temp1 "ASIC temp sensor"
|
||||||
label temp1 "Switch_external temp sensor"
|
set temp1_max 43
|
||||||
|
set temp1_max_hyst 28
|
||||||
|
|
||||||
|
# These sensors located on CPU Board.
|
||||||
|
chip "lm75b-i2c-*-48"
|
||||||
|
label temp1 "Rear-panel temp sensor 1"
|
||||||
|
set temp1_max 43
|
||||||
|
set temp1_max_hyst 28
|
||||||
|
|
||||||
|
chip "lm75b-i2c-*-4e"
|
||||||
|
label temp1 "Rear-panel temp sensor 2"
|
||||||
set temp1_max 43
|
set temp1_max 43
|
||||||
set temp1_max_hyst 28
|
set temp1_max_hyst 28
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ start)
|
|||||||
modprobe i2c-mux-pca954x
|
modprobe i2c-mux-pca954x
|
||||||
modprobe dx010_wdt
|
modprobe dx010_wdt
|
||||||
modprobe leds-dx010
|
modprobe leds-dx010
|
||||||
|
modprobe lm75
|
||||||
|
|
||||||
found=0
|
found=0
|
||||||
for devnum in 0 1; do
|
for devnum in 0 1; do
|
||||||
@ -56,11 +57,11 @@ start)
|
|||||||
echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-12/new_device
|
echo 24lc64t 0x50 > /sys/bus/i2c/devices/i2c-12/new_device
|
||||||
|
|
||||||
# Attach temperature sensors
|
# Attach temperature sensors
|
||||||
echo dx010_lm75b 0x48 > /sys/bus/i2c/devices/i2c-5/new_device
|
echo lm75b 0x48 > /sys/bus/i2c/devices/i2c-5/new_device
|
||||||
echo dx010_lm75b 0x49 > /sys/bus/i2c/devices/i2c-6/new_device
|
echo lm75b 0x49 > /sys/bus/i2c/devices/i2c-6/new_device
|
||||||
echo dx010_lm75b 0x4a > /sys/bus/i2c/devices/i2c-7/new_device
|
echo lm75b 0x4a > /sys/bus/i2c/devices/i2c-7/new_device
|
||||||
echo dx010_lm75b 0x48 > /sys/bus/i2c/devices/i2c-14/new_device
|
echo lm75b 0x48 > /sys/bus/i2c/devices/i2c-14/new_device
|
||||||
echo dx010_lm75b 0x4e > /sys/bus/i2c/devices/i2c-15/new_device
|
echo lm75b 0x4e > /sys/bus/i2c/devices/i2c-15/new_device
|
||||||
|
|
||||||
# Attach fans
|
# Attach fans
|
||||||
echo emc2305 0x2e > /sys/bus/i2c/devices/i2c-13/new_device
|
echo emc2305 0x2e > /sys/bus/i2c/devices/i2c-13/new_device
|
||||||
|
@ -1,6 +1 @@
|
|||||||
obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o lm75.o
|
obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o
|
||||||
KVERSION = $(shell uname -r)
|
|
||||||
all:
|
|
||||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
|
||||||
clean:
|
|
||||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
|
@ -1,557 +0,0 @@
|
|||||||
/*
|
|
||||||
* lm75.c - Part of lm_sensors, Linux kernel modules for hardware
|
|
||||||
* monitoring
|
|
||||||
* Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/hwmon.h>
|
|
||||||
#include <linux/hwmon-sysfs.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/thermal.h>
|
|
||||||
#include "lm75.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This driver handles the LM75 and compatible digital temperature sensors.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum lm75_type { /* keep sorted in alphabetical order */
|
|
||||||
adt75,
|
|
||||||
ds1775,
|
|
||||||
ds75,
|
|
||||||
ds7505,
|
|
||||||
g751,
|
|
||||||
lm75,
|
|
||||||
lm75a,
|
|
||||||
lm75b,
|
|
||||||
max6625,
|
|
||||||
max6626,
|
|
||||||
mcp980x,
|
|
||||||
stds75,
|
|
||||||
tcn75,
|
|
||||||
tmp100,
|
|
||||||
tmp101,
|
|
||||||
tmp105,
|
|
||||||
tmp112,
|
|
||||||
tmp175,
|
|
||||||
tmp275,
|
|
||||||
tmp75,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Addresses scanned */
|
|
||||||
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
|
|
||||||
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
|
|
||||||
|
|
||||||
|
|
||||||
/* The LM75 registers */
|
|
||||||
#define LM75_REG_CONF 0x01
|
|
||||||
static const u8 LM75_REG_TEMP[3] = {
|
|
||||||
0x00, /* input */
|
|
||||||
0x03, /* max */
|
|
||||||
0x02, /* hyst */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Each client has this additional data */
|
|
||||||
struct lm75_data {
|
|
||||||
struct i2c_client *client;
|
|
||||||
struct device *hwmon_dev;
|
|
||||||
struct thermal_zone_device *tz;
|
|
||||||
struct mutex update_lock;
|
|
||||||
u8 orig_conf;
|
|
||||||
u8 resolution; /* In bits, between 9 and 12 */
|
|
||||||
u8 resolution_limits;
|
|
||||||
char valid; /* !=0 if registers are valid */
|
|
||||||
unsigned long last_updated; /* In jiffies */
|
|
||||||
unsigned long sample_time; /* In jiffies */
|
|
||||||
s16 temp[3]; /* Register values,
|
|
||||||
0 = input
|
|
||||||
1 = max
|
|
||||||
2 = hyst */
|
|
||||||
};
|
|
||||||
|
|
||||||
static int lm75_read_value(struct i2c_client *client, u8 reg);
|
|
||||||
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
|
|
||||||
static struct lm75_data *lm75_update_device(struct device *dev);
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
|
|
||||||
{
|
|
||||||
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sysfs attributes for hwmon */
|
|
||||||
|
|
||||||
static int lm75_read_temp(void *dev, int *temp)
|
|
||||||
{
|
|
||||||
struct lm75_data *data = lm75_update_device(dev);
|
|
||||||
|
|
||||||
if (IS_ERR(data))
|
|
||||||
return PTR_ERR(data);
|
|
||||||
|
|
||||||
*temp = lm75_reg_to_mc(data->temp[0], data->resolution);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
|
||||||
struct lm75_data *data = lm75_update_device(dev);
|
|
||||||
|
|
||||||
if (IS_ERR(data))
|
|
||||||
return PTR_ERR(data);
|
|
||||||
|
|
||||||
return sprintf(buf, "%ld\n", lm75_reg_to_mc(data->temp[attr->index],
|
|
||||||
data->resolution));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t set_temp(struct device *dev, struct device_attribute *da,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
|
||||||
struct lm75_data *data = dev_get_drvdata(dev);
|
|
||||||
struct i2c_client *client = data->client;
|
|
||||||
int nr = attr->index;
|
|
||||||
long temp;
|
|
||||||
int error;
|
|
||||||
u8 resolution;
|
|
||||||
|
|
||||||
error = kstrtol(buf, 10, &temp);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Resolution of limit registers is assumed to be the same as the
|
|
||||||
* temperature input register resolution unless given explicitly.
|
|
||||||
*/
|
|
||||||
if (attr->index && data->resolution_limits)
|
|
||||||
resolution = data->resolution_limits;
|
|
||||||
else
|
|
||||||
resolution = data->resolution;
|
|
||||||
|
|
||||||
mutex_lock(&data->update_lock);
|
|
||||||
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
|
|
||||||
data->temp[nr] = DIV_ROUND_CLOSEST(temp << (resolution - 8),
|
|
||||||
1000) << (16 - resolution);
|
|
||||||
lm75_write_value(client, LM75_REG_TEMP[nr], data->temp[nr]);
|
|
||||||
mutex_unlock(&data->update_lock);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
|
|
||||||
show_temp, set_temp, 1);
|
|
||||||
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
|
|
||||||
show_temp, set_temp, 2);
|
|
||||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
|
|
||||||
|
|
||||||
static struct attribute *lm75_attrs[] = {
|
|
||||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
|
||||||
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
ATTRIBUTE_GROUPS(lm75);
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
|
|
||||||
.get_temp = lm75_read_temp,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* device probe and removal */
|
|
||||||
|
|
||||||
static int
|
|
||||||
lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
||||||
{
|
|
||||||
struct device *dev = &client->dev;
|
|
||||||
struct lm75_data *data;
|
|
||||||
int status;
|
|
||||||
u8 set_mask, clr_mask;
|
|
||||||
int new;
|
|
||||||
enum lm75_type kind = id->driver_data;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter,
|
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL);
|
|
||||||
if (!data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
data->client = client;
|
|
||||||
i2c_set_clientdata(client, data);
|
|
||||||
mutex_init(&data->update_lock);
|
|
||||||
|
|
||||||
/* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
|
|
||||||
* Then tweak to be more precise when appropriate.
|
|
||||||
*/
|
|
||||||
set_mask = 0;
|
|
||||||
clr_mask = LM75_SHUTDOWN; /* continuous conversions */
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case adt75:
|
|
||||||
clr_mask |= 1 << 5; /* not one-shot mode */
|
|
||||||
data->resolution = 12;
|
|
||||||
data->sample_time = HZ / 8;
|
|
||||||
break;
|
|
||||||
case ds1775:
|
|
||||||
case ds75:
|
|
||||||
case stds75:
|
|
||||||
clr_mask |= 3 << 5;
|
|
||||||
set_mask |= 2 << 5; /* 11-bit mode */
|
|
||||||
data->resolution = 11;
|
|
||||||
data->sample_time = HZ;
|
|
||||||
break;
|
|
||||||
case ds7505:
|
|
||||||
set_mask |= 3 << 5; /* 12-bit mode */
|
|
||||||
data->resolution = 12;
|
|
||||||
data->sample_time = HZ / 4;
|
|
||||||
break;
|
|
||||||
case g751:
|
|
||||||
case lm75:
|
|
||||||
case lm75a:
|
|
||||||
data->resolution = 9;
|
|
||||||
data->sample_time = HZ / 2;
|
|
||||||
break;
|
|
||||||
case lm75b:
|
|
||||||
data->resolution = 11;
|
|
||||||
data->sample_time = HZ / 4;
|
|
||||||
break;
|
|
||||||
case max6625:
|
|
||||||
data->resolution = 9;
|
|
||||||
data->sample_time = HZ / 4;
|
|
||||||
break;
|
|
||||||
case max6626:
|
|
||||||
data->resolution = 12;
|
|
||||||
data->resolution_limits = 9;
|
|
||||||
data->sample_time = HZ / 4;
|
|
||||||
break;
|
|
||||||
case tcn75:
|
|
||||||
data->resolution = 9;
|
|
||||||
data->sample_time = HZ / 8;
|
|
||||||
break;
|
|
||||||
case mcp980x:
|
|
||||||
data->resolution_limits = 9;
|
|
||||||
/* fall through */
|
|
||||||
case tmp100:
|
|
||||||
case tmp101:
|
|
||||||
set_mask |= 3 << 5; /* 12-bit mode */
|
|
||||||
data->resolution = 12;
|
|
||||||
data->sample_time = HZ;
|
|
||||||
clr_mask |= 1 << 7; /* not one-shot mode */
|
|
||||||
break;
|
|
||||||
case tmp112:
|
|
||||||
set_mask |= 3 << 5; /* 12-bit mode */
|
|
||||||
clr_mask |= 1 << 7; /* not one-shot mode */
|
|
||||||
data->resolution = 12;
|
|
||||||
data->sample_time = HZ / 4;
|
|
||||||
break;
|
|
||||||
case tmp105:
|
|
||||||
case tmp175:
|
|
||||||
case tmp275:
|
|
||||||
case tmp75:
|
|
||||||
set_mask |= 3 << 5; /* 12-bit mode */
|
|
||||||
clr_mask |= 1 << 7; /* not one-shot mode */
|
|
||||||
data->resolution = 12;
|
|
||||||
data->sample_time = HZ / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* configure as specified */
|
|
||||||
status = lm75_read_value(client, LM75_REG_CONF);
|
|
||||||
if (status < 0) {
|
|
||||||
dev_dbg(dev, "Can't read config? %d\n", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
data->orig_conf = status;
|
|
||||||
new = status & ~clr_mask;
|
|
||||||
new |= set_mask;
|
|
||||||
if (status != new)
|
|
||||||
lm75_write_value(client, LM75_REG_CONF, new);
|
|
||||||
dev_dbg(dev, "Config %02x\n", new);
|
|
||||||
|
|
||||||
data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
|
|
||||||
data, lm75_groups);
|
|
||||||
if (IS_ERR(data->hwmon_dev))
|
|
||||||
return PTR_ERR(data->hwmon_dev);
|
|
||||||
|
|
||||||
data->tz = thermal_zone_of_sensor_register(data->hwmon_dev,
|
|
||||||
0,
|
|
||||||
data->hwmon_dev,
|
|
||||||
&lm75_of_thermal_ops);
|
|
||||||
if (IS_ERR(data->tz))
|
|
||||||
data->tz = NULL;
|
|
||||||
|
|
||||||
dev_info(dev, "%s: sensor '%s'\n",
|
|
||||||
dev_name(data->hwmon_dev), client->name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lm75_remove(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
struct lm75_data *data = i2c_get_clientdata(client);
|
|
||||||
|
|
||||||
thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz);
|
|
||||||
hwmon_device_unregister(data->hwmon_dev);
|
|
||||||
lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id lm75_ids[] = {
|
|
||||||
{ "adt75", adt75, },
|
|
||||||
{ "ds1775", ds1775, },
|
|
||||||
{ "ds75", ds75, },
|
|
||||||
{ "ds7505", ds7505, },
|
|
||||||
{ "g751", g751, },
|
|
||||||
{ "lm75", lm75, },
|
|
||||||
{ "lm75a", lm75a, },
|
|
||||||
{ "dx010_lm75b", lm75b, },
|
|
||||||
{ "max6625", max6625, },
|
|
||||||
{ "max6626", max6626, },
|
|
||||||
{ "mcp980x", mcp980x, },
|
|
||||||
{ "stds75", stds75, },
|
|
||||||
{ "tcn75", tcn75, },
|
|
||||||
{ "tmp100", tmp100, },
|
|
||||||
{ "tmp101", tmp101, },
|
|
||||||
{ "tmp105", tmp105, },
|
|
||||||
{ "tmp112", tmp112, },
|
|
||||||
{ "tmp175", tmp175, },
|
|
||||||
{ "tmp275", tmp275, },
|
|
||||||
{ "tmp75", tmp75, },
|
|
||||||
{ /* LIST END */ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(i2c, lm75_ids);
|
|
||||||
|
|
||||||
#define LM75A_ID 0xA1
|
|
||||||
|
|
||||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
||||||
static int lm75_detect(struct i2c_client *new_client,
|
|
||||||
struct i2c_board_info *info)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adapter = new_client->adapter;
|
|
||||||
int i;
|
|
||||||
int conf, hyst, os;
|
|
||||||
bool is_lm75a = 0;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
|
|
||||||
I2C_FUNC_SMBUS_WORD_DATA))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now, we do the remaining detection. There is no identification-
|
|
||||||
* dedicated register so we have to rely on several tricks:
|
|
||||||
* unused bits, registers cycling over 8-address boundaries,
|
|
||||||
* addresses 0x04-0x07 returning the last read value.
|
|
||||||
* The cycling+unused addresses combination is not tested,
|
|
||||||
* since it would significantly slow the detection down and would
|
|
||||||
* hardly add any value.
|
|
||||||
*
|
|
||||||
* The National Semiconductor LM75A is different than earlier
|
|
||||||
* LM75s. It has an ID byte of 0xaX (where X is the chip
|
|
||||||
* revision, with 1 being the only revision in existence) in
|
|
||||||
* register 7, and unused registers return 0xff rather than the
|
|
||||||
* last read value.
|
|
||||||
*
|
|
||||||
* Note that this function only detects the original National
|
|
||||||
* Semiconductor LM75 and the LM75A. Clones from other vendors
|
|
||||||
* aren't detected, on purpose, because they are typically never
|
|
||||||
* found on PC hardware. They are found on embedded designs where
|
|
||||||
* they can be instantiated explicitly so detection is not needed.
|
|
||||||
* The absence of identification registers on all these clones
|
|
||||||
* would make their exhaustive detection very difficult and weak,
|
|
||||||
* and odds are that the driver would bind to unsupported devices.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Unused bits */
|
|
||||||
conf = i2c_smbus_read_byte_data(new_client, 1);
|
|
||||||
if (conf & 0xe0)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* First check for LM75A */
|
|
||||||
if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) {
|
|
||||||
/* LM75A returns 0xff on unused registers so
|
|
||||||
just to be sure we check for that too. */
|
|
||||||
if (i2c_smbus_read_byte_data(new_client, 4) != 0xff
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 5) != 0xff
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 6) != 0xff)
|
|
||||||
return -ENODEV;
|
|
||||||
is_lm75a = 1;
|
|
||||||
hyst = i2c_smbus_read_byte_data(new_client, 2);
|
|
||||||
os = i2c_smbus_read_byte_data(new_client, 3);
|
|
||||||
} else { /* Traditional style LM75 detection */
|
|
||||||
/* Unused addresses */
|
|
||||||
hyst = i2c_smbus_read_byte_data(new_client, 2);
|
|
||||||
if (i2c_smbus_read_byte_data(new_client, 4) != hyst
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 5) != hyst
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 6) != hyst
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 7) != hyst)
|
|
||||||
return -ENODEV;
|
|
||||||
os = i2c_smbus_read_byte_data(new_client, 3);
|
|
||||||
if (i2c_smbus_read_byte_data(new_client, 4) != os
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 5) != os
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 6) != os
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, 7) != os)
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Addresses cycling */
|
|
||||||
for (i = 8; i <= 248; i += 40) {
|
|
||||||
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, i + 2) != hyst
|
|
||||||
|| i2c_smbus_read_byte_data(new_client, i + 3) != os)
|
|
||||||
return -ENODEV;
|
|
||||||
if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7)
|
|
||||||
!= LM75A_ID)
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
static int lm75_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
status = lm75_read_value(client, LM75_REG_CONF);
|
|
||||||
if (status < 0) {
|
|
||||||
dev_dbg(&client->dev, "Can't read config? %d\n", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
status = status | LM75_SHUTDOWN;
|
|
||||||
lm75_write_value(client, LM75_REG_CONF, status);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lm75_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
status = lm75_read_value(client, LM75_REG_CONF);
|
|
||||||
if (status < 0) {
|
|
||||||
dev_dbg(&client->dev, "Can't read config? %d\n", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
status = status & ~LM75_SHUTDOWN;
|
|
||||||
lm75_write_value(client, LM75_REG_CONF, status);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct dev_pm_ops lm75_dev_pm_ops = {
|
|
||||||
.suspend = lm75_suspend,
|
|
||||||
.resume = lm75_resume,
|
|
||||||
};
|
|
||||||
#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops)
|
|
||||||
#else
|
|
||||||
#define LM75_DEV_PM_OPS NULL
|
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
|
|
||||||
static struct i2c_driver lm75_driver = {
|
|
||||||
.class = I2C_CLASS_HWMON,
|
|
||||||
.driver = {
|
|
||||||
.name = "dx010_lm75",
|
|
||||||
.pm = LM75_DEV_PM_OPS,
|
|
||||||
},
|
|
||||||
.probe = lm75_probe,
|
|
||||||
.remove = lm75_remove,
|
|
||||||
.id_table = lm75_ids,
|
|
||||||
/*
|
|
||||||
.detect = lm75_detect,
|
|
||||||
.address_list = normal_i2c,
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* register access */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All registers are word-sized, except for the configuration register.
|
|
||||||
* LM75 uses a high-byte first convention, which is exactly opposite to
|
|
||||||
* the SMBus standard.
|
|
||||||
*/
|
|
||||||
static int lm75_read_value(struct i2c_client *client, u8 reg)
|
|
||||||
{
|
|
||||||
if (reg == LM75_REG_CONF)
|
|
||||||
return i2c_smbus_read_byte_data(client, reg);
|
|
||||||
else
|
|
||||||
return i2c_smbus_read_word_swapped(client, reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
|
|
||||||
{
|
|
||||||
if (reg == LM75_REG_CONF)
|
|
||||||
return i2c_smbus_write_byte_data(client, reg, value);
|
|
||||||
else
|
|
||||||
return i2c_smbus_write_word_swapped(client, reg, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct lm75_data *lm75_update_device(struct device *dev)
|
|
||||||
{
|
|
||||||
struct lm75_data *data = dev_get_drvdata(dev);
|
|
||||||
struct i2c_client *client = data->client;
|
|
||||||
struct lm75_data *ret = data;
|
|
||||||
|
|
||||||
mutex_lock(&data->update_lock);
|
|
||||||
|
|
||||||
if (time_after(jiffies, data->last_updated + data->sample_time)
|
|
||||||
|| !data->valid) {
|
|
||||||
int i;
|
|
||||||
dev_dbg(&client->dev, "Starting lm75 update\n");
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = lm75_read_value(client, LM75_REG_TEMP[i]);
|
|
||||||
if (unlikely(status < 0)) {
|
|
||||||
dev_dbg(dev,
|
|
||||||
"LM75: Failed to read value: reg %d, error %d\n",
|
|
||||||
LM75_REG_TEMP[i], status);
|
|
||||||
ret = ERR_PTR(status);
|
|
||||||
data->valid = 0;
|
|
||||||
goto abort;
|
|
||||||
}
|
|
||||||
data->temp[i] = status;
|
|
||||||
}
|
|
||||||
data->last_updated = jiffies;
|
|
||||||
data->valid = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
abort:
|
|
||||||
mutex_unlock(&data->update_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_i2c_driver(lm75_driver);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
|
||||||
MODULE_DESCRIPTION("LM75 driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
lm75.h - Part of lm_sensors, Linux kernel modules for hardware
|
|
||||||
monitoring
|
|
||||||
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file contains common code for encoding/decoding LM75 type
|
|
||||||
temperature readings, which are emulated by many of the chips
|
|
||||||
we support. As the user is unlikely to load more than one driver
|
|
||||||
which contains this code, we don't worry about the wasted space.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
|
|
||||||
/* straight from the datasheet */
|
|
||||||
#define LM75_TEMP_MIN (-55000)
|
|
||||||
#define LM75_TEMP_MAX 125000
|
|
||||||
#define LM75_SHUTDOWN 0x01
|
|
||||||
|
|
||||||
/* TEMP: 0.001C/bit (-55C to +125C)
|
|
||||||
REG: (0.5C/bit, two's complement) << 7 */
|
|
||||||
static inline u16 LM75_TEMP_TO_REG(long temp)
|
|
||||||
{
|
|
||||||
int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
|
|
||||||
ntemp += (ntemp < 0 ? -250 : 250);
|
|
||||||
return (u16)((ntemp / 500) << 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int LM75_TEMP_FROM_REG(u16 reg)
|
|
||||||
{
|
|
||||||
/* use integer division instead of equivalent right shift to
|
|
||||||
guarantee arithmetic shift and preserve the sign */
|
|
||||||
return ((s16)reg / 128) * 500;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user