⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 adm1025.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * adm1025.c * * Copyright (C) 2000       Chen-Yuan Wu <gwu@esoft.com> * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org> * * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6 * voltages (including its own power source) and up to two temperatures * (its own plus up to one external one). Voltages are scaled internally * (which is not the common way) with ratios such that the nominal value * of each voltage correspond to a register value of 192 (which means a * resolution of about 0.5% of the nominal value). Temperature values are * reported with a 1 deg resolution and a 3 deg accuracy. Complete * datasheet can be obtained from Analog's website at: *   http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html * * This driver also supports the ADM1025A, which differs from the ADM1025 * only in that it has "open-drain VID inputs while the ADM1025 has * on-chip 100k pull-ups on the VID inputs". It doesn't make any * difference for us. * * This driver also supports the NE1619, a sensor chip made by Philips. * That chip is similar to the ADM1025A, with a few differences. The only * difference that matters to us is that the NE1619 has only two possible * addresses while the ADM1025A has a third one. Complete datasheet can be * obtained from Philips's website at: *   http://www.semiconductors.philips.com/pip/NE1619DS.html * * Since the ADM1025 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>#include <linux/i2c-vid.h>/* * Addresses to scan * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e. * NE1619 has two possible addresses: 0x2c and 0x2d. */static unsigned short normal_i2c[] = { I2C_CLIENT_END };static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, 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_2(adm1025, ne1619);/* * The ADM1025 registers */#define ADM1025_REG_MAN_ID		0x3E#define ADM1025_REG_CHIP_ID 		0x3F#define ADM1025_REG_CONFIG		0x40#define ADM1025_REG_STATUS1		0x41#define ADM1025_REG_STATUS2		0x42#define ADM1025_REG_IN(nr)		(0x20 + (nr))#define ADM1025_REG_IN_MAX(nr)		(0x2B + (nr) * 2)#define ADM1025_REG_IN_MIN(nr)		(0x2C + (nr) * 2)#define ADM1025_REG_TEMP(nr)		(0x26 + (nr))#define ADM1025_REG_TEMP_HIGH(nr)	(0x37 + (nr) * 2)#define ADM1025_REG_TEMP_LOW(nr)	(0x38 + (nr) * 2)#define ADM1025_REG_VID			0x47#define ADM1025_REG_VID4		0x49/* * Conversions and various macros * The ADM1025 uses signed 8-bit values for temperatures. */static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };#define IN_FROM_REG(reg,scale)	(((reg) * (scale) + 96) / 192)#define IN_TO_REG(val,scale)	((val) <= 0 ? 0 : \				 (val) * 192 >= (scale) * 255 ? 255 : \				 ((val) * 192 + (scale)/2) / (scale))#define TEMP_FROM_REG(reg)	((reg) * 1000)#define TEMP_TO_REG(val)	((val) <= -127500 ? -128 : \				 (val) >= 126500 ? 127 : \				 (((val) < 0 ? (val)-500 : (val)+500) / 1000))/* * Functions declaration */static int adm1025_attach_adapter(struct i2c_adapter *adapter);static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind);static void adm1025_init_client(struct i2c_client *client);static int adm1025_detach_client(struct i2c_client *client);static struct adm1025_data *adm1025_update_device(struct device *dev);/* * Driver data (common to all clients) */static struct i2c_driver adm1025_driver = {	.owner		= THIS_MODULE,	.name		= "adm1025",	.id		= I2C_DRIVERID_ADM1025,	.flags		= I2C_DF_NOTIFY,	.attach_adapter	= adm1025_attach_adapter,	.detach_client	= adm1025_detach_client,};/* * Client data (each client gets its own) */struct adm1025_data {	struct i2c_client client;	struct semaphore update_lock;	char valid; /* zero until following fields are valid */	unsigned long last_updated; /* in jiffies */	u8 in[6];		/* register value */	u8 in_max[6];		/* register value */	u8 in_min[6];		/* register value */	s8 temp[2];		/* register value */	s8 temp_min[2];		/* register value */	s8 temp_max[2];		/* register value */	u16 alarms;		/* register values, combined */	u8 vid;			/* register values, combined */	u8 vrm;};/* * Internal variables */static int adm1025_id = 0;/* * Sysfs stuff */#define show_in(offset) \static ssize_t show_in##offset(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \		       in_scale[offset])); \} \static ssize_t show_in##offset##_min(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \		       in_scale[offset])); \} \static ssize_t show_in##offset##_max(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \		       in_scale[offset])); \} \static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);show_in(0);show_in(1);show_in(2);show_in(3);show_in(4);show_in(5);#define show_temp(offset) \static ssize_t show_temp##offset(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \} \static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \} \static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \{ \	struct adm1025_data *data = adm1025_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \}\static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL);show_temp(1);show_temp(2);#define set_in(offset) \static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct adm1025_data *data = i2c_get_clientdata(client); \	data->in_min[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \			       in_scale[offset]); \	i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \				  data->in_min[offset]); \	return count; \} \static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct adm1025_data *data = i2c_get_clientdata(client); \	data->in_max[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \			       in_scale[offset]); \	i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \				  data->in_max[offset]); \	return count; \} \static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \	show_in##offset##_min, set_in##offset##_min); \static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \	show_in##offset##_max, set_in##offset##_max);set_in(0);set_in(1);set_in(2);set_in(3);set_in(4);set_in(5);#define set_temp(offset) \static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct adm1025_data *data = i2c_get_clientdata(client); \	data->temp_min[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \	i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \				  data->temp_min[offset-1]); \	return count; \} \static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct adm1025_data *data = i2c_get_clientdata(client); \	data->temp_max[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \	i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \				  data->temp_max[offset-1]); \	return count; \} \static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \	show_temp##offset##_min, set_temp##offset##_min); \static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \	show_temp##offset##_max, set_temp##offset##_max);set_temp(1);set_temp(2);static ssize_t show_alarms(struct device *dev, char *buf){	struct adm1025_data *data = adm1025_update_device(dev);	return sprintf(buf, "%u\n", data->alarms);}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);static ssize_t show_vid(struct device *dev, char *buf){	struct adm1025_data *data = adm1025_update_device(dev);	return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));}static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL);static ssize_t show_vrm(struct device *dev, char *buf){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -