* Kernel module for iBook G4's fan controller
@ 2003-12-20 13:29 Colin Leroy
2003-12-20 14:16 ` Colin Leroy
0 siblings, 1 reply; 3+ messages in thread
From: Colin Leroy @ 2003-12-20 13:29 UTC (permalink / raw)
To: linuxppc-dev list; +Cc: benh
[-- Attachment #1: Type: text/plain, Size: 334 bytes --]
Hi,
here's a module to allow fine control of the iBook G4's fan controller. The defaults temperature limits are pretty high.
it's also available at http://geekounet.org/powerbook/patches/files/therm_adt7467.c
It's intended to go with the other therm_* modules in drivers/macintosh/ and is for kernel 2.4.
Hope it's fine,
--
Colin
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: therm_adt7467.c --]
[-- Type: text/x-csrc; name="therm_adt7467.c", Size: 9567 bytes --]
/*
* Device driver for the i2c thermostat found on the iBook G4
*
* Copyright (C) 2003 Colin Leroy, Benjamin Herrenschmidt
*
* Documentation from
* http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf
*
*/
#include <linux/config.h>
#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/smp_lock.h>
#include <linux/wait.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
#undef DEBUG
#define TEMP_LOCAL 0x26
#define TEMP_REMOTE1 0x25
#define TEMP_REMOTE2 0x27
#define LIM_LOCAL 0x6a
#define LIM_REMOTE1 0x6b
#define LIM_REMOTE2 0x6c
#define FAN0_SPEED 0x28
#define HYST0 0x6d
#define HYST1 0x6e
#define MANUAL_MODE 0x5c
#define MANUAL_MASK 0xe0
#define AUTO_MASK 0x20
static int limit_decrease = 15;
static int fan_speed = -1;
MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
MODULE_DESCRIPTION("Driver for ADT7467 thermostat in iBook G4");
MODULE_LICENSE("GPL");
MODULE_PARM(limit_decrease,"i");
MODULE_PARM_DESC(limit_decrease,"Decrease standard maximum temperatures by N °C, default is 15");
MODULE_PARM(fan_speed,"i");
MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (dangerous !), default is automatic");
EXPORT_NO_SYMBOLS;
struct thermostat {
struct i2c_client clt;
u8 cached_temp[3];
u8 initial_limits[3];
u8 limits[3];
int last_speed;
int overriding;
};
static int therm_bus, therm_address;
static struct thermostat* thermostat;
static pid_t monitor_thread_id;
static int monitor_running;
static DECLARE_COMPLETION(monitor_dead);
static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno);
static void write_fan_speed(struct thermostat *th, int speed);
static int
write_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 int
read_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 int
attach_thermostat(struct i2c_adapter *adapter)
{
unsigned long bus_no;
if (strncmp(adapter->name, "uni-n", 5))
return 0;
bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
if (bus_no != therm_bus)
return 0;
return attach_one_thermostat(adapter, therm_address, bus_no);
}
static int
detach_thermostat(struct i2c_client *client)
{
struct thermostat* th;
if (thermostat == NULL)
return 0;
th = thermostat;
printk(KERN_INFO "adt7467: Putting max temperatures back from %d, %d, %d,"
" to %d, %d, %d, (°C)\n",
th->limits[0], th->limits[1], th->limits[2],
th->initial_limits[0], th->initial_limits[1], th->initial_limits[2]);
write_reg(th, LIM_LOCAL, th->initial_limits[0]);
write_reg(th, LIM_REMOTE1, th->initial_limits[1]);
write_reg(th, LIM_REMOTE2, th->initial_limits[2]);
write_fan_speed(th, -1);
thermostat = NULL;
if (monitor_running) {
(void)kill_proc(monitor_thread_id, SIGTERM, 1);
wait_for_completion(&monitor_dead);
}
i2c_detach_client(&th->clt);
kfree(th);
return 0;
}
/* What is this supposed to be ? registered ? I hate
* magic numbers like that ...
*/
#define I2C_DRIVERID_THERMOSTAT (0xDEAD)
static struct i2c_driver thermostat_driver = {
name: "Apple Thermostat",
id: I2C_DRIVERID_THERMOSTAT,
flags: I2C_DF_NOTIFY,
attach_adapter: &attach_thermostat,
detach_client: &detach_thermostat,
command: NULL,
inc_use: NULL,
dec_use: NULL
};
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);
return (90000*60)/res;
}
static void write_fan_speed(struct thermostat *th, int speed)
{
u8 manual;
if (speed > 0xff)
speed = 0xff;
else if (speed < -1)
speed = 0;
if (speed >= 0) {
manual = read_reg(th, MANUAL_MODE);
write_reg(th, 0x5c, manual|MANUAL_MASK);
if (th->last_speed != speed)
printk(KERN_INFO "adt7467: Setting speed to: %d\n", speed);
th->last_speed = speed;
write_reg(th, 0x30, speed);
} else {
/* back to automatic */
manual = read_reg(th, MANUAL_MODE);
if (th->last_speed != -1)
printk(KERN_INFO "adt7467: Setting speed to: automatic\n");
th->last_speed = -1;
write_reg(th, 0x5c, manual&(~AUTO_MASK));
}
}
static int monitor_task(void *arg)
{
struct thermostat* th = arg;
u8 temps[3];
u8 lims[3];
u8 hyst[2];
#ifdef DEBUG
int mfan_speed;
#endif
lock_kernel();
daemonize();
unlock_kernel();
strcpy(current->comm, "thermostat");
monitor_running = 1;
while(1)
{
set_task_state(current, TASK_INTERRUPTIBLE);
schedule_timeout(1*HZ);
if (signal_pending(current))
break;
/* Check status */
/* remote 1: CPU ?*/
/* remote 2: GPU ?*/
temps[0] = read_reg(th, TEMP_LOCAL);
temps[1] = read_reg(th, TEMP_REMOTE1);
temps[2] = read_reg(th, TEMP_REMOTE2);
lims[0] = read_reg(th, LIM_LOCAL);
lims[1] = read_reg(th, LIM_REMOTE1);
lims[2] = read_reg(th, LIM_REMOTE2);
hyst[0] = read_reg(th, HYST0);
hyst[1] = read_reg(th, HYST1);
hyst[0] = (hyst[0] & 0xf0) >> 4;
hyst[1] = (hyst[1] & 0xf0) >> 4;
if (fan_speed != -1) {
if (temps[0] > lims[0]
|| temps[1] > lims[1]
|| temps[2] > lims[2]) {
int var = 0;
var = (temps[0] - lims[0] > var) ? temps[0] - lims[0] : var;
var = (temps[1] - lims[1] > var) ? temps[1] - lims[1] : var;
var = (temps[2] - lims[2] > var) ? temps[2] - lims[2] : var;
if (var > 8) {
if (th->overriding == 0)
printk(KERN_INFO "adt7467: Limit exceeded by %d°C, overriding specified fan speed.\n",
var);
th->overriding = 1;
write_fan_speed(th, -1);
} else {
if (th->overriding == 1)
printk(KERN_INFO "adt7467: Limit excess is over, setting speed to specified.\n"); th->overriding = 0;
write_fan_speed(th, fan_speed);
}
} else {
write_fan_speed(th, 0);
}
}
#ifdef DEBUG
mfan_speed = read_fan_speed(th, FAN0_SPEED);
/* only one fan in the iBook G4 */
if (temps[0] != th->cached_temp[0]
|| temps[1] != th->cached_temp[1]
|| temps[2] != th->cached_temp[2]) {
printk(KERN_INFO "adt7467: Temperature infos:"
" thermostats: %d,%d,%d °C;"
" limits: %d,%d,%d °C;"
" fan speed: %d RPM;"
" hysteresis: %d,%d\n",
temps[0], temps[1], temps[2],
lims[0], lims[1], lims[2],
mfan_speed,hyst[0], hyst[1]);
}
#endif
th->cached_temp[0] = temps[0];
th->cached_temp[1] = temps[1];
th->cached_temp[2] = temps[2];
}
monitor_running = 0;
complete_and_exit(&monitor_dead, 0);
return 0;
}
static int
attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno)
{
struct thermostat* th;
int rc;
if (thermostat)
return 0;
th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
if (!th)
return -ENOMEM;
memset(th, 0, sizeof(*th));
th->clt.addr = addr;
th->clt.adapter = adapter;
th->clt.driver = &thermostat_driver;
th->clt.flags = 0;
th->clt.data = NULL;
strcpy(th->clt.name, "thermostat");
rc = read_reg(th, 0);
if (rc < 0) {
printk(KERN_ERR "adt7467: Thermostat failed to read config from bus %d !\n",
busno);
kfree(th);
return -ENODEV;
}
printk(KERN_INFO "adt7467: ADT7467 initializing\n");
th->initial_limits[0] = read_reg(th, LIM_LOCAL);
th->initial_limits[1] = read_reg(th, LIM_REMOTE1);
th->initial_limits[2] = read_reg(th, LIM_REMOTE2);
th->limits[0] = th->initial_limits[0] - limit_decrease;
th->limits[1] = th->initial_limits[1] - limit_decrease;
th->limits[2] = th->initial_limits[2] - limit_decrease;
printk(KERN_INFO "adt7467: Lowering max temperatures from %d, %d, %d"
" to %d, %d, %d (°C)\n",
th->initial_limits[0], th->initial_limits[1], th->initial_limits[2],
th->limits[0], th->limits[1], th->limits[2]);
write_reg(th, LIM_LOCAL, th->limits[0]);
write_reg(th, LIM_REMOTE1, th->limits[1]);
write_reg(th, LIM_REMOTE2, th->limits[2]);
thermostat = th;
if (i2c_attach_client(&th->clt)) {
printk("adt7467: Thermostat failed to attach client !\n");
thermostat = NULL;
kfree(th);
return -ENODEV;
}
if (fan_speed != -1) {
write_fan_speed(th, 0);
} else {
write_fan_speed(th, -1);
}
monitor_thread_id = kernel_thread(monitor_task, th,
SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
return 0;
}
static int __init
thermostat_init(void)
{
struct device_node* np;
u32 *prop;
np = find_devices("fan");
if (!np)
return -ENODEV;
if (!device_is_compatible(np, "adt7467"))
return -ENODEV;
prop = (u32 *)get_property(np, "reg", NULL);
if (!prop)
return -ENODEV;
therm_bus = ((*prop) >> 8) & 0x0f;
therm_address = ((*prop) & 0xff) >> 1;
printk(KERN_INFO "adt7467: Thermostat bus: %d, address: 0x%02x, limit_decrease: %d, fan_speed: %d\n",
therm_bus, therm_address, limit_decrease, fan_speed);
return i2c_add_driver(&thermostat_driver);
}
static void __exit
thermostat_exit(void)
{
i2c_del_driver(&thermostat_driver);
}
module_init(thermostat_init);
module_exit(thermostat_exit);
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Kernel module for iBook G4's fan controller
2003-12-20 13:29 Kernel module for iBook G4's fan controller Colin Leroy
@ 2003-12-20 14:16 ` Colin Leroy
2003-12-20 23:25 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 3+ messages in thread
From: Colin Leroy @ 2003-12-20 14:16 UTC (permalink / raw)
To: linuxppc-dev list; +Cc: benh
[-- Attachment #1: Type: text/plain, Size: 193 bytes --]
On 20 Dec 2003 at 14h12, Colin Leroy wrote:
Hi,
> here's a module to allow fine control of the iBook G4's fan controller.
This one's better (hysteresis when forcing fan speed too).
--
Colin
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: therm_adt7467.c --]
[-- Type: text/x-csrc; name="therm_adt7467.c", Size: 9870 bytes --]
/*
* Device driver for the i2c thermostat found on the iBook G4
*
* Copyright (C) 2003 Colin Leroy, Benjamin Herrenschmidt
*
* Documentation from
* http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf
*
*/
#include <linux/config.h>
#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/smp_lock.h>
#include <linux/wait.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
#undef DEBUG
#define TEMP_LOCAL 0x26
#define TEMP_REMOTE1 0x25
#define TEMP_REMOTE2 0x27
#define LIM_LOCAL 0x6a
#define LIM_REMOTE1 0x6b
#define LIM_REMOTE2 0x6c
#define FAN0_SPEED 0x28
#define HYST0 0x6d
#define HYST1 0x6e
#define MANUAL_MODE 0x5c
#define MANUAL_MASK 0xe0
#define AUTO_MASK 0x20
#define FAN_SPD_SET 0x30
static int limit_decrease = 15;
static int fan_speed = -1;
MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
MODULE_DESCRIPTION("Driver for ADT7467 thermostat in iBook G4");
MODULE_LICENSE("GPL");
MODULE_PARM(limit_decrease,"i");
MODULE_PARM_DESC(limit_decrease,"Decrease standard maximum temperatures by N °C, default is 15");
MODULE_PARM(fan_speed,"i");
MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (dangerous !), default is automatic");
EXPORT_NO_SYMBOLS;
struct thermostat {
struct i2c_client clt;
u8 cached_temp[3];
u8 initial_limits[3];
u8 limits[3];
int last_speed;
int overriding;
};
static int therm_bus, therm_address;
static struct thermostat* thermostat;
static pid_t monitor_thread_id;
static int monitor_running;
static DECLARE_COMPLETION(monitor_dead);
static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno);
static void write_fan_speed(struct thermostat *th, int speed);
static int
write_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 int
read_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 int
attach_thermostat(struct i2c_adapter *adapter)
{
unsigned long bus_no;
if (strncmp(adapter->name, "uni-n", 5))
return 0;
bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
if (bus_no != therm_bus)
return 0;
return attach_one_thermostat(adapter, therm_address, bus_no);
}
static int
detach_thermostat(struct i2c_client *client)
{
struct thermostat* th;
if (thermostat == NULL)
return 0;
th = thermostat;
printk(KERN_INFO "adt7467: Putting max temperatures back from %d, %d, %d,"
" to %d, %d, %d, (°C)\n",
th->limits[0], th->limits[1], th->limits[2],
th->initial_limits[0], th->initial_limits[1], th->initial_limits[2]);
write_reg(th, LIM_LOCAL, th->initial_limits[0]);
write_reg(th, LIM_REMOTE1, th->initial_limits[1]);
write_reg(th, LIM_REMOTE2, th->initial_limits[2]);
write_fan_speed(th, -1);
thermostat = NULL;
if (monitor_running) {
(void)kill_proc(monitor_thread_id, SIGTERM, 1);
wait_for_completion(&monitor_dead);
}
i2c_detach_client(&th->clt);
kfree(th);
return 0;
}
/* What is this supposed to be ? registered ? I hate
* magic numbers like that ...
*/
#define I2C_DRIVERID_THERMOSTAT (0xDEAD)
static struct i2c_driver thermostat_driver = {
name: "Apple Thermostat",
id: I2C_DRIVERID_THERMOSTAT,
flags: I2C_DF_NOTIFY,
attach_adapter: &attach_thermostat,
detach_client: &detach_thermostat,
command: NULL,
inc_use: NULL,
dec_use: NULL
};
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);
return (90000*60)/res;
}
static void write_fan_speed(struct thermostat *th, int speed)
{
u8 manual;
if (speed > 0xff)
speed = 0xff;
else if (speed < -1)
speed = 0;
if (speed >= 0) {
manual = read_reg(th, MANUAL_MODE);
write_reg(th, MANUAL_MODE, manual|MANUAL_MASK);
if (th->last_speed != speed)
printk(KERN_INFO "adt7467: Setting speed to: %d\n", speed);
th->last_speed = speed;
write_reg(th, FAN_SPD_SET, speed);
} else {
/* back to automatic */
manual = read_reg(th, MANUAL_MODE);
if (th->last_speed != -1)
printk(KERN_INFO "adt7467: Setting speed to: automatic\n");
th->last_speed = -1;
write_reg(th, MANUAL_MODE, manual&(~AUTO_MASK));
}
}
static int monitor_task(void *arg)
{
struct thermostat* th = arg;
u8 temps[3];
u8 lims[3];
u8 hyst[2];
#ifdef DEBUG
int mfan_speed;
#endif
lock_kernel();
daemonize();
unlock_kernel();
strcpy(current->comm, "thermostat");
monitor_running = 1;
while(1)
{
set_task_state(current, TASK_INTERRUPTIBLE);
schedule_timeout(1*HZ);
if (signal_pending(current))
break;
/* Check status */
/* remote 1: CPU ?*/
/* remote 2: GPU ?*/
temps[0] = read_reg(th, TEMP_LOCAL);
temps[1] = read_reg(th, TEMP_REMOTE1);
temps[2] = read_reg(th, TEMP_REMOTE2);
lims[0] = read_reg(th, LIM_LOCAL);
lims[1] = read_reg(th, LIM_REMOTE1);
lims[2] = read_reg(th, LIM_REMOTE2);
hyst[0] = read_reg(th, HYST0);
hyst[1] = read_reg(th, HYST1);
hyst[0] = (hyst[0] & 0xf0) >> 4;
hyst[1] = (hyst[1] & 0xf0) >> 4;
if (fan_speed != -1) {
if (temps[0] > lims[0]
|| temps[1] > lims[1]
|| temps[2] > lims[2]) {
int var = 0;
var = (temps[0] - lims[0] > var) ? temps[0] - lims[0] : var;
var = (temps[1] - lims[1] > var) ? temps[1] - lims[1] : var;
var = (temps[2] - lims[2] > var) ? temps[2] - lims[2] : var;
if (var > 8) {
if (th->overriding == 0)
printk(KERN_INFO "adt7467: Limit exceeded by %d°C, overriding specified fan speed.\n",
var);
th->overriding = 1;
write_fan_speed(th, -1);
} else {
if (th->overriding == 1)
printk(KERN_INFO "adt7467: Limit excess is over, setting speed to specified.\n"); th->overriding = 0;
write_fan_speed(th, fan_speed);
}
} else {
int var = 10;
var = (lims[0] - temps[0] < var) ? lims[0] - temps[0] : var;
var = (lims[1] - temps[1] < var) ? lims[1] - temps[1] : var;
var = (lims[2] - temps[2] < var) ? lims[2] - temps[2] : var;
if (var >= 2) /* pseudo hysteresis */
write_fan_speed(th, 0);
}
}
#ifdef DEBUG
mfan_speed = read_fan_speed(th, FAN0_SPEED);
/* only one fan in the iBook G4 */
if (temps[0] != th->cached_temp[0]
|| temps[1] != th->cached_temp[1]
|| temps[2] != th->cached_temp[2]) {
printk(KERN_INFO "adt7467: Temperature infos:"
" thermostats: %d,%d,%d °C;"
" limits: %d,%d,%d °C;"
" fan speed: %d RPM;"
" hysteresis: %d,%d\n",
temps[0], temps[1], temps[2],
lims[0], lims[1], lims[2],
mfan_speed,hyst[0], hyst[1]);
}
#endif
th->cached_temp[0] = temps[0];
th->cached_temp[1] = temps[1];
th->cached_temp[2] = temps[2];
}
monitor_running = 0;
complete_and_exit(&monitor_dead, 0);
return 0;
}
static int
attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno)
{
struct thermostat* th;
int rc;
if (thermostat)
return 0;
th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
if (!th)
return -ENOMEM;
memset(th, 0, sizeof(*th));
th->clt.addr = addr;
th->clt.adapter = adapter;
th->clt.driver = &thermostat_driver;
th->clt.flags = 0;
th->clt.data = NULL;
strcpy(th->clt.name, "thermostat");
rc = read_reg(th, 0);
if (rc < 0) {
printk(KERN_ERR "adt7467: Thermostat failed to read config from bus %d !\n",
busno);
kfree(th);
return -ENODEV;
}
printk(KERN_INFO "adt7467: ADT7467 initializing\n");
th->initial_limits[0] = read_reg(th, LIM_LOCAL);
th->initial_limits[1] = read_reg(th, LIM_REMOTE1);
th->initial_limits[2] = read_reg(th, LIM_REMOTE2);
th->limits[0] = th->initial_limits[0] - limit_decrease;
th->limits[1] = th->initial_limits[1] - limit_decrease;
th->limits[2] = th->initial_limits[2] - limit_decrease;
printk(KERN_INFO "adt7467: Lowering max temperatures from %d, %d, %d"
" to %d, %d, %d (°C)\n",
th->initial_limits[0], th->initial_limits[1], th->initial_limits[2],
th->limits[0], th->limits[1], th->limits[2]);
write_reg(th, LIM_LOCAL, th->limits[0]);
write_reg(th, LIM_REMOTE1, th->limits[1]);
write_reg(th, LIM_REMOTE2, th->limits[2]);
thermostat = th;
if (i2c_attach_client(&th->clt)) {
printk("adt7467: Thermostat failed to attach client !\n");
thermostat = NULL;
kfree(th);
return -ENODEV;
}
if (fan_speed != -1) {
write_fan_speed(th, 0);
} else {
write_fan_speed(th, -1);
}
monitor_thread_id = kernel_thread(monitor_task, th,
SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
return 0;
}
static int __init
thermostat_init(void)
{
struct device_node* np;
u32 *prop;
np = find_devices("fan");
if (!np)
return -ENODEV;
if (!device_is_compatible(np, "adt7467"))
return -ENODEV;
prop = (u32 *)get_property(np, "reg", NULL);
if (!prop)
return -ENODEV;
therm_bus = ((*prop) >> 8) & 0x0f;
therm_address = ((*prop) & 0xff) >> 1;
printk(KERN_INFO "adt7467: Thermostat bus: %d, address: 0x%02x, limit_decrease: %d, fan_speed: %d\n",
therm_bus, therm_address, limit_decrease, fan_speed);
return i2c_add_driver(&thermostat_driver);
}
static void __exit
thermostat_exit(void)
{
i2c_del_driver(&thermostat_driver);
}
module_init(thermostat_init);
module_exit(thermostat_exit);
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Kernel module for iBook G4's fan controller
2003-12-20 14:16 ` Colin Leroy
@ 2003-12-20 23:25 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 3+ messages in thread
From: Benjamin Herrenschmidt @ 2003-12-20 23:25 UTC (permalink / raw)
To: Colin Leroy; +Cc: linuxppc-dev list
On Sun, 2003-12-21 at 01:16, Colin Leroy wrote:
> On 20 Dec 2003 at 14h12, Colin Leroy wrote:
>
> Hi,
>
> > here's a module to allow fine control of the iBook G4's fan controller.
>
> This one's better (hysteresis when forcing fan speed too).
Great !
I'll look into this tonight or tomorrow.
Ben.
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2003-12-20 23:25 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-12-20 13:29 Kernel module for iBook G4's fan controller Colin Leroy
2003-12-20 14:16 ` Colin Leroy
2003-12-20 23:25 ` Benjamin Herrenschmidt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).