1ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/* 2ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Windfarm PowerMac thermal control. MAX6690 sensor. 3ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * 4ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> 5ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * 6ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Use and redistribute under the terms of the GNU GPL v2. 7ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt */ 8ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/types.h> 9ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/errno.h> 10ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/kernel.h> 11ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/init.h> 12ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/slab.h> 13ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/i2c.h> 14ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <asm/prom.h> 15ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <asm/pmac_low_i2c.h> 16ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 17ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include "windfarm.h" 18ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 1922b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt#define VERSION "1.0" 20ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 21ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/* This currently only exports the external temperature sensor, 22ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt since that's all the control loops need. */ 23ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 24ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/* Some MAX6690 register numbers */ 25ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define MAX6690_INTERNAL_TEMP 0 26ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define MAX6690_EXTERNAL_TEMP 1 27ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 28ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstruct wf_6690_sensor { 29351ca3e31197929535418f5affc761cd9fb07428Jean Delvare struct i2c_client *i2c; 30ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt struct wf_sensor sens; 31ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}; 32ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 33ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens) 34ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 35ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic int wf_max6690_get(struct wf_sensor *sr, s32 *value) 36ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{ 37ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt struct wf_6690_sensor *max = wf_to_6690(sr); 38ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt s32 data; 39ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 40351ca3e31197929535418f5affc761cd9fb07428Jean Delvare if (max->i2c == NULL) 41ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt return -ENODEV; 42ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 43ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt /* chip gets initialized by firmware */ 44351ca3e31197929535418f5affc761cd9fb07428Jean Delvare data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP); 45ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt if (data < 0) 46ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt return data; 47ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt *value = data << 16; 48ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt return 0; 49ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt} 50ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 51ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic void wf_max6690_release(struct wf_sensor *sr) 52ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{ 53ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt struct wf_6690_sensor *max = wf_to_6690(sr); 54ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 55ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt kfree(max); 56ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt} 57ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 58ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic struct wf_sensor_ops wf_max6690_ops = { 59ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt .get_value = wf_max6690_get, 60ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt .release = wf_max6690_release, 61ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt .owner = THIS_MODULE, 62ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}; 63ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 64351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic int wf_max6690_probe(struct i2c_client *client, 65351ca3e31197929535418f5affc761cd9fb07428Jean Delvare const struct i2c_device_id *id) 66ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{ 6722b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt const char *name, *loc; 68ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt struct wf_6690_sensor *max; 69351ca3e31197929535418f5affc761cd9fb07428Jean Delvare int rc; 70ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 7122b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); 7222b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt if (!loc) { 7322b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt dev_warn(&client->dev, "Missing hwsensor-location property!\n"); 7422b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt return -ENXIO; 7522b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt } 7622b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt 77d839ba2ab2f3270fe4f067e082a7233ba06bcf9cBenjamin Herrenschmidt /* 78d839ba2ab2f3270fe4f067e082a7233ba06bcf9cBenjamin Herrenschmidt * We only expose the external temperature register for 79d839ba2ab2f3270fe4f067e082a7233ba06bcf9cBenjamin Herrenschmidt * now as this is all we need for our control loops 80d839ba2ab2f3270fe4f067e082a7233ba06bcf9cBenjamin Herrenschmidt */ 81d839ba2ab2f3270fe4f067e082a7233ba06bcf9cBenjamin Herrenschmidt if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) 8222b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt name = "backside-temp"; 8322b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt else if (!strcmp(loc, "NB Ambient")) 8422b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt name = "north-bridge-temp"; 8522b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt else if (!strcmp(loc, "GPU Ambient")) 8622b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt name = "gpu-temp"; 8722b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt else 8822b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt return -ENXIO; 8922b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt 90ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); 91ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt if (max == NULL) { 92351ca3e31197929535418f5affc761cd9fb07428Jean Delvare printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " 93351ca3e31197929535418f5affc761cd9fb07428Jean Delvare "no memory\n"); 94351ca3e31197929535418f5affc761cd9fb07428Jean Delvare return -ENOMEM; 95351ca3e31197929535418f5affc761cd9fb07428Jean Delvare } 96351ca3e31197929535418f5affc761cd9fb07428Jean Delvare 97351ca3e31197929535418f5affc761cd9fb07428Jean Delvare max->i2c = client; 988bb61fe1d03123a625265d599a22fb12af79e1aeGeert Uytterhoeven max->sens.name = name; 99351ca3e31197929535418f5affc761cd9fb07428Jean Delvare max->sens.ops = &wf_max6690_ops; 100351ca3e31197929535418f5affc761cd9fb07428Jean Delvare i2c_set_clientdata(client, max); 101351ca3e31197929535418f5affc761cd9fb07428Jean Delvare 102351ca3e31197929535418f5affc761cd9fb07428Jean Delvare rc = wf_register_sensor(&max->sens); 10322b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt if (rc) 104351ca3e31197929535418f5affc761cd9fb07428Jean Delvare kfree(max); 105351ca3e31197929535418f5affc761cd9fb07428Jean Delvare return rc; 106351ca3e31197929535418f5affc761cd9fb07428Jean Delvare} 107351ca3e31197929535418f5affc761cd9fb07428Jean Delvare 108351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic int wf_max6690_remove(struct i2c_client *client) 109ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{ 110351ca3e31197929535418f5affc761cd9fb07428Jean Delvare struct wf_6690_sensor *max = i2c_get_clientdata(client); 111ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 112351ca3e31197929535418f5affc761cd9fb07428Jean Delvare max->i2c = NULL; 113ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt wf_unregister_sensor(&max->sens); 114ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 115ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt return 0; 116ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt} 117ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 118351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic const struct i2c_device_id wf_max6690_id[] = { 11922b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin Herrenschmidt { "MAC,max6690", 0 }, 120351ca3e31197929535418f5affc761cd9fb07428Jean Delvare { } 121351ca3e31197929535418f5affc761cd9fb07428Jean Delvare}; 12222b6d2ea355449fc2a9a4dc5c346615af86b876eBenjamin HerrenschmidtMODULE_DEVICE_TABLE(i2c, wf_max6690_id); 123351ca3e31197929535418f5affc761cd9fb07428Jean Delvare 124351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic struct i2c_driver wf_max6690_driver = { 125351ca3e31197929535418f5affc761cd9fb07428Jean Delvare .driver = { 126351ca3e31197929535418f5affc761cd9fb07428Jean Delvare .name = "wf_max6690", 127351ca3e31197929535418f5affc761cd9fb07428Jean Delvare }, 128351ca3e31197929535418f5affc761cd9fb07428Jean Delvare .probe = wf_max6690_probe, 129351ca3e31197929535418f5affc761cd9fb07428Jean Delvare .remove = wf_max6690_remove, 130351ca3e31197929535418f5affc761cd9fb07428Jean Delvare .id_table = wf_max6690_id, 131351ca3e31197929535418f5affc761cd9fb07428Jean Delvare}; 132351ca3e31197929535418f5affc761cd9fb07428Jean Delvare 133f7fb862b843269d02a2fa75e4bbb49603f801b88Wei Yongjunmodule_i2c_driver(wf_max6690_driver); 134ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt 135ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 136ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); 137ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_LICENSE("GPL"); 138