1 /*
2  * Windfarm PowerMac thermal control.  SMU "satellite" controller sensors.
3  *
4  * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
5  *
6  * Released under the terms of the GNU GPL v2.
7  */
8 
9 #include <linux/types.h>
10 #include <linux/errno.h>
11 #include <linux/kernel.h>
12 #include <linux/slab.h>
13 #include <linux/init.h>
14 #include <linux/wait.h>
15 #include <linux/i2c.h>
16 #include <linux/mutex.h>
17 #include <asm/prom.h>
18 #include <asm/smu.h>
19 #include <asm/pmac_low_i2c.h>
20 
21 #include "windfarm.h"
22 
23 #define VERSION "0.2"
24 
25 #define DEBUG
26 
27 #ifdef DEBUG
28 #define DBG(args...)	printk(args)
29 #else
30 #define DBG(args...)	do { } while(0)
31 #endif
32 
33 /* If the cache is older than 800ms we'll refetch it */
34 #define MAX_AGE		msecs_to_jiffies(800)
35 
36 struct wf_sat {
37 	int			nr;
38 	atomic_t		refcnt;
39 	struct mutex		mutex;
40 	unsigned long		last_read; /* jiffies when cache last updated */
41 	u8			cache[16];
42 	struct i2c_client	*i2c;
43 	struct device_node	*node;
44 };
45 
46 static struct wf_sat *sats[2];
47 
48 struct wf_sat_sensor {
49 	int		index;
50 	int		index2;		/* used for power sensors */
51 	int		shift;
52 	struct wf_sat	*sat;
53 	struct wf_sensor sens;
54 };
55 
56 #define wf_to_sat(c)	container_of(c, struct wf_sat_sensor, sens)
57 
58 struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
59 						  unsigned int *size)
60 {
61 	struct wf_sat *sat;
62 	int err;
63 	unsigned int i, len;
64 	u8 *buf;
65 	u8 data[4];
66 
67 	/* TODO: Add the resulting partition to the device-tree */
68 
69 	if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
70 		return NULL;
71 
72 	err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
73 	if (err) {
74 		printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
75 		return NULL;
76 	}
77 
78 	err = i2c_smbus_read_word_data(sat->i2c, 9);
79 	if (err < 0) {
80 		printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
81 		return NULL;
82 	}
83 	len = err;
84 	if (len == 0) {
85 		printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
86 		return NULL;
87 	}
88 
89 	len = le16_to_cpu(len);
90 	len = (len + 3) & ~3;
91 	buf = kmalloc(len, GFP_KERNEL);
92 	if (buf == NULL)
93 		return NULL;
94 
95 	for (i = 0; i < len; i += 4) {
96 		err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
97 		if (err < 0) {
98 			printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
99 			       err);
100 			goto fail;
101 		}
102 		buf[i] = data[1];
103 		buf[i+1] = data[0];
104 		buf[i+2] = data[3];
105 		buf[i+3] = data[2];
106 	}
107 #ifdef DEBUG
108 	DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id);
109 	for (i = 0; i < len; ++i)
110 		DBG(" %x", buf[i]);
111 	DBG("\n");
112 #endif
113 
114 	if (size)
115 		*size = len;
116 	return (struct smu_sdbp_header *) buf;
117 
118  fail:
119 	kfree(buf);
120 	return NULL;
121 }
122 EXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition);
123 
124 /* refresh the cache */
125 static int wf_sat_read_cache(struct wf_sat *sat)
126 {
127 	int err;
128 
129 	err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
130 	if (err < 0)
131 		return err;
132 	sat->last_read = jiffies;
133 #ifdef LOTSA_DEBUG
134 	{
135 		int i;
136 		DBG(KERN_DEBUG "wf_sat_get: data is");
137 		for (i = 0; i < 16; ++i)
138 			DBG(" %.2x", sat->cache[i]);
139 		DBG("\n");
140 	}
141 #endif
142 	return 0;
143 }
144 
145 static int wf_sat_get(struct wf_sensor *sr, s32 *value)
146 {
147 	struct wf_sat_sensor *sens = wf_to_sat(sr);
148 	struct wf_sat *sat = sens->sat;
149 	int i, err;
150 	s32 val;
151 
152 	if (sat->i2c == NULL)
153 		return -ENODEV;
154 
155 	mutex_lock(&sat->mutex);
156 	if (time_after(jiffies, (sat->last_read + MAX_AGE))) {
157 		err = wf_sat_read_cache(sat);
158 		if (err)
159 			goto fail;
160 	}
161 
162 	i = sens->index * 2;
163 	val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift;
164 	if (sens->index2 >= 0) {
165 		i = sens->index2 * 2;
166 		/* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */
167 		val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4;
168 	}
169 
170 	*value = val;
171 	err = 0;
172 
173  fail:
174 	mutex_unlock(&sat->mutex);
175 	return err;
176 }
177 
178 static void wf_sat_release(struct wf_sensor *sr)
179 {
180 	struct wf_sat_sensor *sens = wf_to_sat(sr);
181 	struct wf_sat *sat = sens->sat;
182 
183 	if (atomic_dec_and_test(&sat->refcnt)) {
184 		if (sat->nr >= 0)
185 			sats[sat->nr] = NULL;
186 		kfree(sat);
187 	}
188 	kfree(sens);
189 }
190 
191 static struct wf_sensor_ops wf_sat_ops = {
192 	.get_value	= wf_sat_get,
193 	.release	= wf_sat_release,
194 	.owner		= THIS_MODULE,
195 };
196 
197 static struct i2c_driver wf_sat_driver;
198 
199 static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
200 {
201 	struct i2c_board_info info;
202 	struct i2c_client *client;
203 	const u32 *reg;
204 	u8 addr;
205 
206 	reg = of_get_property(dev, "reg", NULL);
207 	if (reg == NULL)
208 		return;
209 	addr = *reg;
210 	DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
211 
212 	memset(&info, 0, sizeof(struct i2c_board_info));
213 	info.addr = (addr >> 1) & 0x7f;
214 	info.platform_data = dev;
215 	strlcpy(info.type, "wf_sat", I2C_NAME_SIZE);
216 
217 	client = i2c_new_device(adapter, &info);
218 	if (client == NULL) {
219 		printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
220 		return;
221 	}
222 
223 	/*
224 	 * Let i2c-core delete that device on driver removal.
225 	 * This is safe because i2c-core holds the core_lock mutex for us.
226 	 */
227 	list_add_tail(&client->detected, &wf_sat_driver.clients);
228 }
229 
230 static int wf_sat_probe(struct i2c_client *client,
231 			const struct i2c_device_id *id)
232 {
233 	struct device_node *dev = client->dev.platform_data;
234 	struct wf_sat *sat;
235 	struct wf_sat_sensor *sens;
236 	const u32 *reg;
237 	const char *loc, *type;
238 	u8 chip, core;
239 	struct device_node *child;
240 	int shift, cpu, index;
241 	char *name;
242 	int vsens[2], isens[2];
243 
244 	sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
245 	if (sat == NULL)
246 		return -ENOMEM;
247 	sat->nr = -1;
248 	sat->node = of_node_get(dev);
249 	atomic_set(&sat->refcnt, 0);
250 	mutex_init(&sat->mutex);
251 	sat->i2c = client;
252 	i2c_set_clientdata(client, sat);
253 
254 	vsens[0] = vsens[1] = -1;
255 	isens[0] = isens[1] = -1;
256 	child = NULL;
257 	while ((child = of_get_next_child(dev, child)) != NULL) {
258 		reg = of_get_property(child, "reg", NULL);
259 		type = of_get_property(child, "device_type", NULL);
260 		loc = of_get_property(child, "location", NULL);
261 		if (reg == NULL || loc == NULL)
262 			continue;
263 
264 		/* the cooked sensors are between 0x30 and 0x37 */
265 		if (*reg < 0x30 || *reg > 0x37)
266 			continue;
267 		index = *reg - 0x30;
268 
269 		/* expect location to be CPU [AB][01] ... */
270 		if (strncmp(loc, "CPU ", 4) != 0)
271 			continue;
272 		chip = loc[4] - 'A';
273 		core = loc[5] - '0';
274 		if (chip > 1 || core > 1) {
275 			printk(KERN_ERR "wf_sat_create: don't understand "
276 			       "location %s for %s\n", loc, child->full_name);
277 			continue;
278 		}
279 		cpu = 2 * chip + core;
280 		if (sat->nr < 0)
281 			sat->nr = chip;
282 		else if (sat->nr != chip) {
283 			printk(KERN_ERR "wf_sat_create: can't cope with "
284 			       "multiple CPU chips on one SAT (%s)\n", loc);
285 			continue;
286 		}
287 
288 		if (strcmp(type, "voltage-sensor") == 0) {
289 			name = "cpu-voltage";
290 			shift = 4;
291 			vsens[core] = index;
292 		} else if (strcmp(type, "current-sensor") == 0) {
293 			name = "cpu-current";
294 			shift = 8;
295 			isens[core] = index;
296 		} else if (strcmp(type, "temp-sensor") == 0) {
297 			name = "cpu-temp";
298 			shift = 10;
299 		} else
300 			continue;	/* hmmm shouldn't happen */
301 
302 		/* the +16 is enough for "cpu-voltage-n" */
303 		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
304 		if (sens == NULL) {
305 			printk(KERN_ERR "wf_sat_create: couldn't create "
306 			       "%s sensor %d (no memory)\n", name, cpu);
307 			continue;
308 		}
309 		sens->index = index;
310 		sens->index2 = -1;
311 		sens->shift = shift;
312 		sens->sat = sat;
313 		atomic_inc(&sat->refcnt);
314 		sens->sens.ops = &wf_sat_ops;
315 		sens->sens.name = (char *) (sens + 1);
316 		snprintf(sens->sens.name, 16, "%s-%d", name, cpu);
317 
318 		if (wf_register_sensor(&sens->sens)) {
319 			atomic_dec(&sat->refcnt);
320 			kfree(sens);
321 		}
322 	}
323 
324 	/* make the power sensors */
325 	for (core = 0; core < 2; ++core) {
326 		if (vsens[core] < 0 || isens[core] < 0)
327 			continue;
328 		cpu = 2 * sat->nr + core;
329 		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
330 		if (sens == NULL) {
331 			printk(KERN_ERR "wf_sat_create: couldn't create power "
332 			       "sensor %d (no memory)\n", cpu);
333 			continue;
334 		}
335 		sens->index = vsens[core];
336 		sens->index2 = isens[core];
337 		sens->shift = 0;
338 		sens->sat = sat;
339 		atomic_inc(&sat->refcnt);
340 		sens->sens.ops = &wf_sat_ops;
341 		sens->sens.name = (char *) (sens + 1);
342 		snprintf(sens->sens.name, 16, "cpu-power-%d", cpu);
343 
344 		if (wf_register_sensor(&sens->sens)) {
345 			atomic_dec(&sat->refcnt);
346 			kfree(sens);
347 		}
348 	}
349 
350 	if (sat->nr >= 0)
351 		sats[sat->nr] = sat;
352 
353 	return 0;
354 }
355 
356 static int wf_sat_attach(struct i2c_adapter *adapter)
357 {
358 	struct device_node *busnode, *dev = NULL;
359 	struct pmac_i2c_bus *bus;
360 
361 	bus = pmac_i2c_adapter_to_bus(adapter);
362 	if (bus == NULL)
363 		return -ENODEV;
364 	busnode = pmac_i2c_get_bus_node(bus);
365 
366 	while ((dev = of_get_next_child(busnode, dev)) != NULL)
367 		if (of_device_is_compatible(dev, "smu-sat"))
368 			wf_sat_create(adapter, dev);
369 	return 0;
370 }
371 
372 static int wf_sat_remove(struct i2c_client *client)
373 {
374 	struct wf_sat *sat = i2c_get_clientdata(client);
375 
376 	/* XXX TODO */
377 
378 	sat->i2c = NULL;
379 	i2c_set_clientdata(client, NULL);
380 	return 0;
381 }
382 
383 static const struct i2c_device_id wf_sat_id[] = {
384 	{ "wf_sat", 0 },
385 	{ }
386 };
387 
388 static struct i2c_driver wf_sat_driver = {
389 	.driver = {
390 		.name		= "wf_smu_sat",
391 	},
392 	.attach_adapter	= wf_sat_attach,
393 	.probe		= wf_sat_probe,
394 	.remove		= wf_sat_remove,
395 	.id_table	= wf_sat_id,
396 };
397 
398 static int __init sat_sensors_init(void)
399 {
400 	return i2c_add_driver(&wf_sat_driver);
401 }
402 
403 #if 0	/* uncomment when module_exit() below is uncommented */
404 static void __exit sat_sensors_exit(void)
405 {
406 	i2c_del_driver(&wf_sat_driver);
407 }
408 #endif
409 
410 module_init(sat_sensors_init);
411 /*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */
412 
413 MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
414 MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
415 MODULE_LICENSE("GPL");
416