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

📄 therm_adt746x.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Device driver for the i2c thermostat found on the iBook G4, Albook G4 * * Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt * * Documentation from * http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf * http://www.analog.com/UploadedFiles/Data_Sheets/3686221171167ADT7460_b.pdf * */#include <linux/types.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/i2c.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/wait.h>#include <linux/suspend.h>#include <linux/kthread.h>#include <linux/moduleparam.h>#include <linux/freezer.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/io.h>#include <asm/system.h>#include <asm/sections.h>#include <asm/of_platform.h>#undef DEBUG#define CONFIG_REG   0x40#define MANUAL_MASK  0xe0#define AUTO_MASK    0x20static u8 TEMP_REG[3]    = {0x26, 0x25, 0x27}; /* local, sensor1, sensor2 */static u8 LIMIT_REG[3]   = {0x6b, 0x6a, 0x6c}; /* local, sensor1, sensor2 */static u8 MANUAL_MODE[2] = {0x5c, 0x5d};       static u8 REM_CONTROL[2] = {0x00, 0x40};static u8 FAN_SPEED[2]   = {0x28, 0x2a};static u8 FAN_SPD_SET[2] = {0x30, 0x31};static u8 default_limits_local[3] = {70, 50, 70};    /* local, sensor1, sensor2 */static u8 default_limits_chip[3] = {80, 65, 80};    /* local, sensor1, sensor2 */static const char *sensor_location[3];static int limit_adjust;static int fan_speed = -1;static int verbose;MODULE_AUTHOR("Colin Leroy <colin@colino.net>");MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "		   "Powerbook G4 Alu");MODULE_LICENSE("GPL");module_param(limit_adjust, int, 0644);MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 sensor1, 70 sensor2) "		 "by N degrees.");module_param(fan_speed, int, 0644);MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "		 "(default 64)");module_param(verbose, bool, 0);MODULE_PARM_DESC(verbose,"Verbose log operations "		 "(default 0)");struct thermostat {	struct i2c_client	clt;	u8			temps[3];	u8			cached_temp[3];	u8			initial_limits[3];	u8			limits[3];	int			last_speed[2];	int			last_var[2];};static enum {ADT7460, ADT7467} therm_type;static int therm_bus, therm_address;static struct of_device * of_dev;static struct thermostat* thermostat;static struct task_struct *thread_therm = NULL;static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,				 int busno);static void write_both_fan_speed(struct thermostat *th, int speed);static void write_fan_speed(struct thermostat *th, int speed, int fan);static intwrite_reg(struct thermostat* th, int reg, u8 data){	u8 tmp[2];	int rc;		tmp[0] = reg;	tmp[1] = data;	rc = i2c_master_send(&th->clt, (const char *)tmp, 2);	if (rc < 0)		return rc;	if (rc != 2)		return -ENODEV;	return 0;}static intread_reg(struct thermostat* th, int reg){	u8 reg_addr, data;	int rc;	reg_addr = (u8)reg;	rc = i2c_master_send(&th->clt, &reg_addr, 1);	if (rc < 0)		return rc;	if (rc != 1)		return -ENODEV;	rc = i2c_master_recv(&th->clt, (char *)&data, 1);	if (rc < 0)		return rc;	return data;}static intattach_thermostat(struct i2c_adapter *adapter){	unsigned long bus_no;	if (strncmp(adapter->name, "uni-n", 5))		return -ENODEV;	bus_no = simple_strtoul(adapter->name + 6, NULL, 10);	if (bus_no != therm_bus)		return -ENODEV;	return attach_one_thermostat(adapter, therm_address, bus_no);}static intdetach_thermostat(struct i2c_adapter *adapter){	struct thermostat* th;	int i;		if (thermostat == NULL)		return 0;	th = thermostat;	if (thread_therm != NULL) {		kthread_stop(thread_therm);	}	printk(KERN_INFO "adt746x: Putting max temperatures back from "			 "%d, %d, %d to %d, %d, %d\n",		th->limits[0], th->limits[1], th->limits[2],		th->initial_limits[0], th->initial_limits[1],		th->initial_limits[2]);	for (i = 0; i < 3; i++)		write_reg(th, LIMIT_REG[i], th->initial_limits[i]);	write_both_fan_speed(th, -1);	i2c_detach_client(&th->clt);	thermostat = NULL;	kfree(th);	return 0;}static struct i2c_driver thermostat_driver = {  	.driver = {		.name	= "therm_adt746x",	},	.attach_adapter	= attach_thermostat,	.detach_adapter	= detach_thermostat,};static int read_fan_speed(struct thermostat *th, u8 addr){	u8 tmp[2];	u16 res;		/* should start with low byte */	tmp[1] = read_reg(th, addr);	tmp[0] = read_reg(th, addr + 1);		res = tmp[1] + (tmp[0] << 8);	/* "a value of 0xffff means that the fan has stopped" */	return (res == 0xffff ? 0 : (90000*60)/res);}static void write_both_fan_speed(struct thermostat *th, int speed){	write_fan_speed(th, speed, 0);	if (therm_type == ADT7460)		write_fan_speed(th, speed, 1);}static void write_fan_speed(struct thermostat *th, int speed, int fan){	u8 manual;		if (speed > 0xff) 		speed = 0xff;	else if (speed < -1) 		speed = 0;		if (therm_type == ADT7467 && fan == 1)		return;		if (th->last_speed[fan] != speed) {		if (verbose) {			if (speed == -1)				printk(KERN_DEBUG "adt746x: Setting speed to automatic "					"for %s fan.\n", sensor_location[fan+1]);			else				printk(KERN_DEBUG "adt746x: Setting speed to %d "					"for %s fan.\n", speed, sensor_location[fan+1]);		}	} else		return;		if (speed >= 0) {		manual = read_reg(th, MANUAL_MODE[fan]);		write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);		write_reg(th, FAN_SPD_SET[fan], speed);	} else {		/* back to automatic */		if(therm_type == ADT7460) {			manual = read_reg(th,				MANUAL_MODE[fan]) & (~MANUAL_MASK);			write_reg(th,				MANUAL_MODE[fan], manual|REM_CONTROL[fan]);		} else {			manual = read_reg(th, MANUAL_MODE[fan]);			write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));		}	}		th->last_speed[fan] = speed;			}static void read_sensors(struct thermostat *th){	int i = 0;	for (i = 0; i < 3; i++)		th->temps[i]  = read_reg(th, TEMP_REG[i]);}#ifdef DEBUGstatic void display_stats(struct thermostat *th){	if (th->temps[0] != th->cached_temp[0]	||  th->temps[1] != th->cached_temp[1]	||  th->temps[2] != th->cached_temp[2]) {		printk(KERN_INFO "adt746x: Temperature infos:"				 " thermostats: %d,%d,%d;"				 " limits: %d,%d,%d;"				 " fan speed: %d RPM\n",				 th->temps[0], th->temps[1], th->temps[2],				 th->limits[0],  th->limits[1],  th->limits[2],				 read_fan_speed(th, FAN_SPEED[0]));	}	th->cached_temp[0] = th->temps[0];	th->cached_temp[1] = th->temps[1];	th->cached_temp[2] = th->temps[2];}#endifstatic void update_fans_speed (struct thermostat *th){	int lastvar = 0; /* last variation, for iBook */	int i = 0;	/* we don't care about local sensor, so we start at sensor 1 */	for (i = 1; i < 3; i++) {		int started = 0;		int fan_number = (therm_type == ADT7460 && i == 2);		int var = th->temps[i] - th->limits[i];		if (var > -1) {			int step = (255 - fan_speed) / 7;			int new_speed = 0;			/* hysteresis : change fan speed only if variation is			 * more than two degrees */			if (abs(var - th->last_var[fan_number]) < 2)				continue;			started = 1;			new_speed = fan_speed + ((var-1)*step);			if (new_speed < fan_speed)				new_speed = fan_speed;			if (new_speed > 255)				new_speed = 255;			if (verbose)				printk(KERN_DEBUG "adt746x: Setting fans speed to %d "						 "(limit exceeded by %d on %s) \n",						new_speed, var,						sensor_location[fan_number+1]);			write_both_fan_speed(th, new_speed);			th->last_var[fan_number] = var;		} else if (var < -2) {			/* don't stop fan if sensor2 is cold and sensor1 is not			 * so cold (lastvar >= -1) */			if (i == 2 && lastvar < -1) {				if (th->last_speed[fan_number] != 0)					if (verbose)						printk(KERN_DEBUG "adt746x: Stopping "							"fans.\n");				write_both_fan_speed(th, 0);			}		}		lastvar = var;		if (started)			return; /* we don't want to re-stop the fan				* if sensor1 is heating and sensor2 is not */

⌨️ 快捷键说明

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