diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-07 00:08:17 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-11-07 19:17:56 -0500 |
commit | 75722d3992f57375c0cc029dcceb2334a45ceff1 (patch) | |
tree | d3f63b3ea80790c2f29ea435781c1331f17d269e /drivers/macintosh/windfarm_lm75_sensor.c | |
parent | 7d49697ef92bd2cf84ab53bd4cea82fefb197fb9 (diff) |
[PATCH] ppc64: Thermal control for SMU based machines
This adds a new thermal control framework for PowerMac, along with the
implementation for PowerMac8,1, PowerMac8,2 (iMac G5 rev 1 and 2), and
PowerMac9,1 (latest single CPU desktop). In the future, I expect to move
the older G5 thermal control to the new framework as well.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'drivers/macintosh/windfarm_lm75_sensor.c')
-rw-r--r-- | drivers/macintosh/windfarm_lm75_sensor.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c new file mode 100644 index 000000000000..a0a41ad0f2b5 --- /dev/null +++ b/drivers/macintosh/windfarm_lm75_sensor.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. LM75 sensor | ||
3 | * | ||
4 | * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * <benh@kernel.crashing.org> | ||
6 | * | ||
7 | * Released under the term of the GNU GPL v2. | ||
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 <linux/i2c-dev.h> | ||
19 | #include <asm/prom.h> | ||
20 | #include <asm/machdep.h> | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/system.h> | ||
23 | #include <asm/sections.h> | ||
24 | |||
25 | #include "windfarm.h" | ||
26 | |||
27 | #define VERSION "0.1" | ||
28 | |||
29 | #undef DEBUG | ||
30 | |||
31 | #ifdef DEBUG | ||
32 | #define DBG(args...) printk(args) | ||
33 | #else | ||
34 | #define DBG(args...) do { } while(0) | ||
35 | #endif | ||
36 | |||
37 | struct wf_lm75_sensor { | ||
38 | int ds1775 : 1; | ||
39 | int inited : 1; | ||
40 | struct i2c_client i2c; | ||
41 | struct wf_sensor sens; | ||
42 | }; | ||
43 | #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) | ||
44 | #define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c) | ||
45 | |||
46 | static int wf_lm75_attach(struct i2c_adapter *adapter); | ||
47 | static int wf_lm75_detach(struct i2c_client *client); | ||
48 | |||
49 | static struct i2c_driver wf_lm75_driver = { | ||
50 | .owner = THIS_MODULE, | ||
51 | .name = "wf_lm75", | ||
52 | .flags = I2C_DF_NOTIFY, | ||
53 | .attach_adapter = wf_lm75_attach, | ||
54 | .detach_client = wf_lm75_detach, | ||
55 | }; | ||
56 | |||
57 | static int wf_lm75_get(struct wf_sensor *sr, s32 *value) | ||
58 | { | ||
59 | struct wf_lm75_sensor *lm = wf_to_lm75(sr); | ||
60 | s32 data; | ||
61 | |||
62 | if (lm->i2c.adapter == NULL) | ||
63 | return -ENODEV; | ||
64 | |||
65 | /* Init chip if necessary */ | ||
66 | if (!lm->inited) { | ||
67 | u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1); | ||
68 | |||
69 | DBG("wf_lm75: Initializing %s, cfg was: %02x\n", | ||
70 | sr->name, cfg); | ||
71 | |||
72 | /* clear shutdown bit, keep other settings as left by | ||
73 | * the firmware for now | ||
74 | */ | ||
75 | cfg_new = cfg & ~0x01; | ||
76 | i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new); | ||
77 | lm->inited = 1; | ||
78 | |||
79 | /* If we just powered it up, let's wait 200 ms */ | ||
80 | msleep(200); | ||
81 | } | ||
82 | |||
83 | /* Read temperature register */ | ||
84 | data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0)); | ||
85 | data <<= 8; | ||
86 | *value = data; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void wf_lm75_release(struct wf_sensor *sr) | ||
92 | { | ||
93 | struct wf_lm75_sensor *lm = wf_to_lm75(sr); | ||
94 | |||
95 | /* check if client is registered and detach from i2c */ | ||
96 | if (lm->i2c.adapter) { | ||
97 | i2c_detach_client(&lm->i2c); | ||
98 | lm->i2c.adapter = NULL; | ||
99 | } | ||
100 | |||
101 | kfree(lm); | ||
102 | } | ||
103 | |||
104 | static struct wf_sensor_ops wf_lm75_ops = { | ||
105 | .get_value = wf_lm75_get, | ||
106 | .release = wf_lm75_release, | ||
107 | .owner = THIS_MODULE, | ||
108 | }; | ||
109 | |||
110 | static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter, | ||
111 | u8 addr, int ds1775, | ||
112 | const char *loc) | ||
113 | { | ||
114 | struct wf_lm75_sensor *lm; | ||
115 | |||
116 | DBG("wf_lm75: creating %s device at address 0x%02x\n", | ||
117 | ds1775 ? "ds1775" : "lm75", addr); | ||
118 | |||
119 | lm = kmalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); | ||
120 | if (lm == NULL) | ||
121 | return NULL; | ||
122 | memset(lm, 0, sizeof(struct wf_lm75_sensor)); | ||
123 | |||
124 | /* Usual rant about sensor names not beeing very consistent in | ||
125 | * the device-tree, oh well ... | ||
126 | * Add more entries below as you deal with more setups | ||
127 | */ | ||
128 | if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY")) | ||
129 | lm->sens.name = "hd-temp"; | ||
130 | else | ||
131 | goto fail; | ||
132 | |||
133 | lm->inited = 0; | ||
134 | lm->sens.ops = &wf_lm75_ops; | ||
135 | lm->ds1775 = ds1775; | ||
136 | lm->i2c.addr = (addr >> 1) & 0x7f; | ||
137 | lm->i2c.adapter = adapter; | ||
138 | lm->i2c.driver = &wf_lm75_driver; | ||
139 | strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1); | ||
140 | |||
141 | if (i2c_attach_client(&lm->i2c)) { | ||
142 | printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", | ||
143 | ds1775 ? "ds1775" : "lm75", lm->i2c.name); | ||
144 | goto fail; | ||
145 | } | ||
146 | |||
147 | if (wf_register_sensor(&lm->sens)) { | ||
148 | i2c_detach_client(&lm->i2c); | ||
149 | goto fail; | ||
150 | } | ||
151 | |||
152 | return lm; | ||
153 | fail: | ||
154 | kfree(lm); | ||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | static int wf_lm75_attach(struct i2c_adapter *adapter) | ||
159 | { | ||
160 | u8 bus_id; | ||
161 | struct device_node *smu, *bus, *dev; | ||
162 | |||
163 | /* We currently only deal with LM75's hanging off the SMU | ||
164 | * i2c busses. If we extend that driver to other/older | ||
165 | * machines, we should split this function into SMU-i2c, | ||
166 | * keywest-i2c, PMU-i2c, ... | ||
167 | */ | ||
168 | |||
169 | DBG("wf_lm75: adapter %s detected\n", adapter->name); | ||
170 | |||
171 | if (strncmp(adapter->name, "smu-i2c-", 8) != 0) | ||
172 | return 0; | ||
173 | smu = of_find_node_by_type(NULL, "smu"); | ||
174 | if (smu == NULL) | ||
175 | return 0; | ||
176 | |||
177 | /* Look for the bus in the device-tree */ | ||
178 | bus_id = (u8)simple_strtoul(adapter->name + 8, NULL, 16); | ||
179 | |||
180 | DBG("wf_lm75: bus ID is %x\n", bus_id); | ||
181 | |||
182 | /* Look for sensors subdir */ | ||
183 | for (bus = NULL; | ||
184 | (bus = of_get_next_child(smu, bus)) != NULL;) { | ||
185 | u32 *reg; | ||
186 | |||
187 | if (strcmp(bus->name, "i2c")) | ||
188 | continue; | ||
189 | reg = (u32 *)get_property(bus, "reg", NULL); | ||
190 | if (reg == NULL) | ||
191 | continue; | ||
192 | if (bus_id == *reg) | ||
193 | break; | ||
194 | } | ||
195 | of_node_put(smu); | ||
196 | if (bus == NULL) { | ||
197 | printk(KERN_WARNING "windfarm: SMU i2c bus 0x%x not found" | ||
198 | " in device-tree !\n", bus_id); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | DBG("wf_lm75: bus found, looking for device...\n"); | ||
203 | |||
204 | /* Now look for lm75(s) in there */ | ||
205 | for (dev = NULL; | ||
206 | (dev = of_get_next_child(bus, dev)) != NULL;) { | ||
207 | const char *loc = | ||
208 | get_property(dev, "hwsensor-location", NULL); | ||
209 | u32 *reg = (u32 *)get_property(dev, "reg", NULL); | ||
210 | DBG(" dev: %s... (loc: %p, reg: %p)\n", dev->name, loc, reg); | ||
211 | if (loc == NULL || reg == NULL) | ||
212 | continue; | ||
213 | /* real lm75 */ | ||
214 | if (device_is_compatible(dev, "lm75")) | ||
215 | wf_lm75_create(adapter, *reg, 0, loc); | ||
216 | /* ds1775 (compatible, better resolution */ | ||
217 | else if (device_is_compatible(dev, "ds1775")) | ||
218 | wf_lm75_create(adapter, *reg, 1, loc); | ||
219 | } | ||
220 | |||
221 | of_node_put(bus); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int wf_lm75_detach(struct i2c_client *client) | ||
227 | { | ||
228 | struct wf_lm75_sensor *lm = i2c_to_lm75(client); | ||
229 | |||
230 | DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name); | ||
231 | |||
232 | /* Mark client detached */ | ||
233 | lm->i2c.adapter = NULL; | ||
234 | |||
235 | /* release sensor */ | ||
236 | wf_unregister_sensor(&lm->sens); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static int __init wf_lm75_sensor_init(void) | ||
242 | { | ||
243 | int rc; | ||
244 | |||
245 | rc = i2c_add_driver(&wf_lm75_driver); | ||
246 | if (rc < 0) | ||
247 | return rc; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static void __exit wf_lm75_sensor_exit(void) | ||
252 | { | ||
253 | i2c_del_driver(&wf_lm75_driver); | ||
254 | } | ||
255 | |||
256 | |||
257 | module_init(wf_lm75_sensor_init); | ||
258 | module_exit(wf_lm75_sensor_exit); | ||
259 | |||
260 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
261 | MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control"); | ||
262 | MODULE_LICENSE("GPL"); | ||
263 | |||