Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/windfarm: Add lm87 sensor

For use by the upcoming windfarm_rm31

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

+201
+201
drivers/macintosh/windfarm_lm87_sensor.c
··· 1 + /* 2 + * Windfarm PowerMac thermal control. LM87 sensor 3 + * 4 + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. 5 + * 6 + * Released under the term of the GNU GPL v2. 7 + * 8 + */ 9 + 10 + #include <linux/types.h> 11 + #include <linux/errno.h> 12 + #include <linux/kernel.h> 13 + #include <linux/delay.h> 14 + #include <linux/slab.h> 15 + #include <linux/init.h> 16 + #include <linux/wait.h> 17 + #include <linux/i2c.h> 18 + #include <asm/prom.h> 19 + #include <asm/machdep.h> 20 + #include <asm/io.h> 21 + #include <asm/sections.h> 22 + #include <asm/pmac_low_i2c.h> 23 + 24 + #include "windfarm.h" 25 + 26 + #define VERSION "1.0" 27 + 28 + #undef DEBUG 29 + 30 + #ifdef DEBUG 31 + #define DBG(args...) printk(args) 32 + #else 33 + #define DBG(args...) do { } while(0) 34 + #endif 35 + 36 + struct wf_lm87_sensor { 37 + struct i2c_client *i2c; 38 + struct wf_sensor sens; 39 + }; 40 + #define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens) 41 + 42 + 43 + static int wf_lm87_read_reg(struct i2c_client *chip, int reg) 44 + { 45 + int rc, tries = 0; 46 + u8 buf; 47 + 48 + for (;;) { 49 + /* Set address */ 50 + buf = (u8)reg; 51 + rc = i2c_master_send(chip, &buf, 1); 52 + if (rc <= 0) 53 + goto error; 54 + rc = i2c_master_recv(chip, &buf, 1); 55 + if (rc <= 0) 56 + goto error; 57 + return (int)buf; 58 + error: 59 + DBG("wf_lm87: Error reading LM87, retrying...\n"); 60 + if (++tries > 10) { 61 + printk(KERN_ERR "wf_lm87: Error reading LM87 !\n"); 62 + return -EIO; 63 + } 64 + msleep(10); 65 + } 66 + } 67 + 68 + static int wf_lm87_get(struct wf_sensor *sr, s32 *value) 69 + { 70 + struct wf_lm87_sensor *lm = sr->priv; 71 + s32 temp; 72 + 73 + if (lm->i2c == NULL) 74 + return -ENODEV; 75 + 76 + #define LM87_INT_TEMP 0x27 77 + 78 + /* Read temperature register */ 79 + temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP); 80 + if (temp < 0) 81 + return temp; 82 + *value = temp << 16; 83 + 84 + return 0; 85 + } 86 + 87 + static void wf_lm87_release(struct wf_sensor *sr) 88 + { 89 + struct wf_lm87_sensor *lm = wf_to_lm87(sr); 90 + 91 + kfree(lm); 92 + } 93 + 94 + static struct wf_sensor_ops wf_lm87_ops = { 95 + .get_value = wf_lm87_get, 96 + .release = wf_lm87_release, 97 + .owner = THIS_MODULE, 98 + }; 99 + 100 + static int wf_lm87_probe(struct i2c_client *client, 101 + const struct i2c_device_id *id) 102 + { 103 + struct wf_lm87_sensor *lm; 104 + const char *name = NULL, *loc; 105 + struct device_node *np = NULL; 106 + int rc; 107 + 108 + /* 109 + * The lm87 contains a whole pile of sensors, additionally, 110 + * the Xserve G5 has several lm87's. However, for now we only 111 + * care about the internal temperature sensor 112 + */ 113 + while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) { 114 + if (strcmp(np->name, "int-temp")) 115 + continue; 116 + loc = of_get_property(np, "location", NULL); 117 + if (!loc) 118 + continue; 119 + if (strstr(loc, "DIMM")) 120 + name = "dimms-temp"; 121 + else if (strstr(loc, "Processors")) 122 + name = "between-cpus-temp"; 123 + if (name) { 124 + of_node_put(np); 125 + break; 126 + } 127 + } 128 + if (!name) { 129 + pr_warning("wf_lm87: Unsupported sensor %s\n", 130 + client->dev.of_node->full_name); 131 + return -ENODEV; 132 + } 133 + 134 + lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL); 135 + if (lm == NULL) 136 + return -ENODEV; 137 + 138 + lm->i2c = client; 139 + lm->sens.name = name; 140 + lm->sens.ops = &wf_lm87_ops; 141 + lm->sens.priv = lm; 142 + i2c_set_clientdata(client, lm); 143 + 144 + rc = wf_register_sensor(&lm->sens); 145 + if (rc) 146 + kfree(lm); 147 + return rc; 148 + } 149 + 150 + static int wf_lm87_remove(struct i2c_client *client) 151 + { 152 + struct wf_lm87_sensor *lm = i2c_get_clientdata(client); 153 + 154 + DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name); 155 + 156 + /* Mark client detached */ 157 + lm->i2c = NULL; 158 + 159 + /* release sensor */ 160 + wf_unregister_sensor(&lm->sens); 161 + 162 + return 0; 163 + } 164 + 165 + static const struct i2c_device_id wf_lm87_id[] = { 166 + { "MAC,lm87cimt", 0 }, 167 + { } 168 + }; 169 + MODULE_DEVICE_TABLE(i2c, wf_lm87_id); 170 + 171 + static struct i2c_driver wf_lm87_driver = { 172 + .driver = { 173 + .name = "wf_lm87", 174 + }, 175 + .probe = wf_lm87_probe, 176 + .remove = wf_lm87_remove, 177 + .id_table = wf_lm87_id, 178 + }; 179 + 180 + static int __init wf_lm87_sensor_init(void) 181 + { 182 + /* We only support this on the Xserve */ 183 + if (!of_machine_is_compatible("RackMac3,1")) 184 + return -ENODEV; 185 + 186 + return i2c_add_driver(&wf_lm87_driver); 187 + } 188 + 189 + static void __exit wf_lm87_sensor_exit(void) 190 + { 191 + i2c_del_driver(&wf_lm87_driver); 192 + } 193 + 194 + 195 + module_init(wf_lm87_sensor_init); 196 + module_exit(wf_lm87_sensor_exit); 197 + 198 + MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 199 + MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control"); 200 + MODULE_LICENSE("GPL"); 201 +