📄 therm_adt746x.c
字号:
/* * 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, ®_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 + -