📄 lm90.c
字号:
/* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring * Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org> * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to * one external one) with a 0.125 deg resolution (1 deg for local * temperature) and a 3-4 deg accuracy. Complete datasheet can be * obtained from National's website at: * http://www.national.com/pf/LM/LM90.html * * This driver also supports the LM89 and LM99, two other sensor chips * made by National Semiconductor. Both have an increased remote * temperature measurement accuracy (1 degree), and the LM99 * additionally shifts remote temperatures (measured and limits) by 16 * degrees, which allows for higher temperatures measurement. The * driver doesn't handle it since it can be done easily in user-space. * Complete datasheets can be obtained from National's website at: * http://www.national.com/pf/LM/LM89.html * http://www.national.com/pf/LM/LM99.html * Note that there is no way to differenciate between both chips. * * This driver also supports the LM86, another sensor chip made by * National Semiconductor. It is exactly similar to the LM90 except it * has a higher accuracy. * Complete datasheet can be obtained from National's website at: * http://www.national.com/pf/LM/LM86.html * * This driver also supports the ADM1032, a sensor chip made by Analog * Devices. That chip is similar to the LM90, with a few differences * that are not handled by this driver. Complete datasheet can be * obtained from Analog's website at: * http://products.analog.com/products/info.asp?product=ADM1032 * Among others, it has a higher accuracy than the LM90, much like the * LM86 does. * * This driver also supports the MAX6657 and MAX6658, sensor chips made * by Maxim. These chips are similar to the LM86. Complete datasheet * can be obtained at Maxim's website at: * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 * Note that there is no way to differenciate between both chips (but * no need either). * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. * * 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/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/i2c-sensor.h>/* * Addresses to scan * Address is fully defined internally and cannot be changed. * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c. * LM89-1, and LM99-1 have address 0x4d. */static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END };static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };/* * Insmod parameters */SENSORS_INSMOD_5(lm90, adm1032, lm99, lm86, max6657);/* * The LM90 registers */#define LM90_REG_R_MAN_ID 0xFE#define LM90_REG_R_CHIP_ID 0xFF#define LM90_REG_R_CONFIG1 0x03#define LM90_REG_W_CONFIG1 0x09#define LM90_REG_R_CONFIG2 0xBF#define LM90_REG_W_CONFIG2 0xBF#define LM90_REG_R_CONVRATE 0x04#define LM90_REG_W_CONVRATE 0x0A#define LM90_REG_R_STATUS 0x02#define LM90_REG_R_LOCAL_TEMP 0x00#define LM90_REG_R_LOCAL_HIGH 0x05#define LM90_REG_W_LOCAL_HIGH 0x0B#define LM90_REG_R_LOCAL_LOW 0x06#define LM90_REG_W_LOCAL_LOW 0x0C#define LM90_REG_R_LOCAL_CRIT 0x20#define LM90_REG_W_LOCAL_CRIT 0x20#define LM90_REG_R_REMOTE_TEMPH 0x01#define LM90_REG_R_REMOTE_TEMPL 0x10#define LM90_REG_R_REMOTE_OFFSH 0x11#define LM90_REG_W_REMOTE_OFFSH 0x11#define LM90_REG_R_REMOTE_OFFSL 0x12#define LM90_REG_W_REMOTE_OFFSL 0x12#define LM90_REG_R_REMOTE_HIGHH 0x07#define LM90_REG_W_REMOTE_HIGHH 0x0D#define LM90_REG_R_REMOTE_HIGHL 0x13#define LM90_REG_W_REMOTE_HIGHL 0x13#define LM90_REG_R_REMOTE_LOWH 0x08#define LM90_REG_W_REMOTE_LOWH 0x0E#define LM90_REG_R_REMOTE_LOWL 0x14#define LM90_REG_W_REMOTE_LOWL 0x14#define LM90_REG_R_REMOTE_CRIT 0x19#define LM90_REG_W_REMOTE_CRIT 0x19#define LM90_REG_R_TCRIT_HYST 0x21#define LM90_REG_W_TCRIT_HYST 0x21/* * Conversions and various macros * The LM90 uses signed 8-bit values for the local temperatures, * and signed 11-bit values for the remote temperatures (except * T_CRIT). Note that TEMP2_TO_REG does not round values, but * stick to the nearest lower value instead. Fixing it is just * not worth it. */#define TEMP1_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)#define TEMP1_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000)#define TEMP2_FROM_REG(val) (((val & 0x8000 ? val-0x10000 : val) >> 5) * 125)#define TEMP2_TO_REG(val) ((((val / 125) << 5) + (val < 0 ? 0x10000 : 0)) & 0xFFE0)#define HYST_FROM_REG(val) (val * 1000)#define HYST_TO_REG(val) (val <= 0 ? 0 : val >= 31000 ? 31 : val / 1000)/* * Functions declaration */static int lm90_attach_adapter(struct i2c_adapter *adapter);static int lm90_detect(struct i2c_adapter *adapter, int address, int kind);static void lm90_init_client(struct i2c_client *client);static int lm90_detach_client(struct i2c_client *client);static struct lm90_data *lm90_update_device(struct device *dev);/* * Driver data (common to all clients) */static struct i2c_driver lm90_driver = { .owner = THIS_MODULE, .name = "lm90", .id = I2C_DRIVERID_LM90, .flags = I2C_DF_NOTIFY, .attach_adapter = lm90_attach_adapter, .detach_client = lm90_detach_client,};/* * Client data (each client gets its own) */struct lm90_data { struct i2c_client client; struct semaphore update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ u8 temp_input1, temp_low1, temp_high1; /* local */ u16 temp_input2, temp_low2, temp_high2; /* remote, combined */ u8 temp_crit1, temp_crit2; u8 temp_hyst; u16 alarms; /* bitvector, combined */};/* * Internal variables */static int lm90_id = 0;/* * Sysfs stuff */#define show_temp(value, converter) \static ssize_t show_##value(struct device *dev, char *buf) \{ \ struct lm90_data *data = lm90_update_device(dev); \ return sprintf(buf, "%d\n", converter(data->value)); \}show_temp(temp_input1, TEMP1_FROM_REG);show_temp(temp_input2, TEMP2_FROM_REG);show_temp(temp_low1, TEMP1_FROM_REG);show_temp(temp_low2, TEMP2_FROM_REG);show_temp(temp_high1, TEMP1_FROM_REG);show_temp(temp_high2, TEMP2_FROM_REG);show_temp(temp_crit1, TEMP1_FROM_REG);show_temp(temp_crit2, TEMP1_FROM_REG);#define set_temp1(value, reg) \static ssize_t set_##value(struct device *dev, const char *buf, \ size_t count) \{ \ struct i2c_client *client = to_i2c_client(dev); \ struct lm90_data *data = i2c_get_clientdata(client); \ data->value = TEMP1_TO_REG(simple_strtol(buf, NULL, 10)); \ i2c_smbus_write_byte_data(client, reg, data->value); \ return count; \}#define set_temp2(value, regh, regl) \static ssize_t set_##value(struct device *dev, const char *buf, \ size_t count) \{ \ struct i2c_client *client = to_i2c_client(dev); \ struct lm90_data *data = i2c_get_clientdata(client); \ data->value = TEMP2_TO_REG(simple_strtol(buf, NULL, 10)); \ i2c_smbus_write_byte_data(client, regh, data->value >> 8); \ i2c_smbus_write_byte_data(client, regl, data->value & 0xff); \ return count; \}set_temp1(temp_low1, LM90_REG_W_LOCAL_LOW);set_temp2(temp_low2, LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL);set_temp1(temp_high1, LM90_REG_W_LOCAL_HIGH);set_temp2(temp_high2, LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL);set_temp1(temp_crit1, LM90_REG_W_LOCAL_CRIT);set_temp1(temp_crit2, LM90_REG_W_REMOTE_CRIT);#define show_temp_hyst(value, basereg) \static ssize_t show_##value(struct device *dev, char *buf) \{ \ struct lm90_data *data = lm90_update_device(dev); \ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->basereg) \ - HYST_FROM_REG(data->temp_hyst)); \}show_temp_hyst(temp_hyst1, temp_crit1);show_temp_hyst(temp_hyst2, temp_crit2);static ssize_t set_temp_hyst1(struct device *dev, const char *buf, size_t count){ struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); int hyst = TEMP1_FROM_REG(data->temp_crit1) - simple_strtol(buf, NULL, 10); i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, HYST_TO_REG(hyst)); return count;}static ssize_t show_alarms(struct device *dev, char *buf){ struct lm90_data *data = lm90_update_device(dev); return sprintf(buf, "%d\n", data->alarms);}static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_low1, set_temp_low1);static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, set_temp_low2);static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_high1, set_temp_high1);static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, set_temp_high2);static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit1, set_temp_crit1);static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, set_temp_crit2);static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst1, set_temp_hyst1);static DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_hyst2, NULL);static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -