[go: nahoru, domu]

1ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/*
2ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Windfarm PowerMac thermal control.  SMU "satellite" controller sensors.
3ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt *
4ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
5ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt *
6ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt * Released under the terms of the GNU GPL v2.
7ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt */
8ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
9ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/types.h>
10ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/errno.h>
11ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/kernel.h>
12ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/slab.h>
13ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/init.h>
14ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/wait.h>
15ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <linux/i2c.h>
1656783c5e4dd32ca370ad0bdf3a9c6c1aaee94726Daniel Walker#include <linux/mutex.h>
17ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <asm/prom.h>
18ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <asm/smu.h>
19ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include <asm/pmac_low_i2c.h>
20ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
21ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#include "windfarm.h"
22ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
23e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt#define VERSION "1.0"
24ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
25ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define DEBUG
26ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
27ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#ifdef DEBUG
28ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define DBG(args...)	printk(args)
29ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#else
30ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define DBG(args...)	do { } while(0)
31ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#endif
32ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
33ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/* If the cache is older than 800ms we'll refetch it */
34ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define MAX_AGE		msecs_to_jiffies(800)
35ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
36ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstruct wf_sat {
37e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct kref		ref;
38ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int			nr;
3956783c5e4dd32ca370ad0bdf3a9c6c1aaee94726Daniel Walker	struct mutex		mutex;
40ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	unsigned long		last_read; /* jiffies when cache last updated */
41ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	u8			cache[16];
42e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct list_head	sensors;
43351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	struct i2c_client	*i2c;
44ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct device_node	*node;
45ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt};
46ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
47ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic struct wf_sat *sats[2];
48ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
49ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstruct wf_sat_sensor {
50e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct list_head	link;
51e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	int			index;
52e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	int			index2;		/* used for power sensors */
53e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	int			shift;
54e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct wf_sat		*sat;
55e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct wf_sensor 	sens;
56ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt};
57ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
58ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#define wf_to_sat(c)	container_of(c, struct wf_sat_sensor, sens)
59ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
60ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstruct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
61ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt						  unsigned int *size)
62ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{
63ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat *sat;
64ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int err;
65ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	unsigned int i, len;
66ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	u8 *buf;
67ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	u8 data[4];
68ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
69ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	/* TODO: Add the resulting partition to the device-tree */
70ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
71ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
72ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return NULL;
73ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
74351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
75ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (err) {
76ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
77ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return NULL;
78ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
79ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
80351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	err = i2c_smbus_read_word_data(sat->i2c, 9);
812fd091f3eebc5accefa5f77ff04436982765d15croel kluin	if (err < 0) {
82ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
83ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return NULL;
84ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
852fd091f3eebc5accefa5f77ff04436982765d15croel kluin	len = err;
86ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (len == 0) {
87ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
88ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return NULL;
89ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
90ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
91ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	len = le16_to_cpu(len);
92ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	len = (len + 3) & ~3;
93ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	buf = kmalloc(len, GFP_KERNEL);
94ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (buf == NULL)
95ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return NULL;
96ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
97ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	for (i = 0; i < len; i += 4) {
98351ca3e31197929535418f5affc761cd9fb07428Jean Delvare		err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
994b2643d7d9bdcd776749e17f73c168ddf02e93cbJean Delvare		if (err < 0) {
100ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
101ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			       err);
102ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			goto fail;
103ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
104ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		buf[i] = data[1];
105ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		buf[i+1] = data[0];
106ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		buf[i+2] = data[3];
107ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		buf[i+3] = data[2];
108ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
109ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#ifdef DEBUG
110ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id);
111ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	for (i = 0; i < len; ++i)
112ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		DBG(" %x", buf[i]);
113ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	DBG("\n");
114ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#endif
115ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
116ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (size)
117ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		*size = len;
118ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	return (struct smu_sdbp_header *) buf;
119ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
120ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt fail:
121ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	kfree(buf);
122ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	return NULL;
123ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
1249127dd1aace4e89acb48fbcafd0ed27d3869847bJohannes BergEXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition);
125ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
126ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt/* refresh the cache */
127ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic int wf_sat_read_cache(struct wf_sat *sat)
128ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{
129ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int err;
130ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
131351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
1324b2643d7d9bdcd776749e17f73c168ddf02e93cbJean Delvare	if (err < 0)
133ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return err;
134ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	sat->last_read = jiffies;
135ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#ifdef LOTSA_DEBUG
136ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	{
137ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		int i;
138ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		DBG(KERN_DEBUG "wf_sat_get: data is");
139ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		for (i = 0; i < 16; ++i)
140ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			DBG(" %.2x", sat->cache[i]);
141ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		DBG("\n");
142ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
143ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt#endif
144ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	return 0;
145ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
146ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
147e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidtstatic int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value)
148ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{
149ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat_sensor *sens = wf_to_sat(sr);
150ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat *sat = sens->sat;
151ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int i, err;
152ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	s32 val;
153ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
154351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	if (sat->i2c == NULL)
155ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		return -ENODEV;
156ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
15756783c5e4dd32ca370ad0bdf3a9c6c1aaee94726Daniel Walker	mutex_lock(&sat->mutex);
158ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (time_after(jiffies, (sat->last_read + MAX_AGE))) {
159ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		err = wf_sat_read_cache(sat);
160ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (err)
161ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			goto fail;
162ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
163ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
164ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	i = sens->index * 2;
165ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift;
166ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (sens->index2 >= 0) {
167ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		i = sens->index2 * 2;
168ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		/* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */
169ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4;
170ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
171ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
172ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	*value = val;
173ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	err = 0;
174ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
175ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt fail:
17656783c5e4dd32ca370ad0bdf3a9c6c1aaee94726Daniel Walker	mutex_unlock(&sat->mutex);
177ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	return err;
178ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
179ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
180e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidtstatic void wf_sat_release(struct kref *ref)
181e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt{
182e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct wf_sat *sat = container_of(ref, struct wf_sat, ref);
183e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt
184e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	if (sat->nr >= 0)
185e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		sats[sat->nr] = NULL;
186e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	kfree(sat);
187e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt}
188e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt
189e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidtstatic void wf_sat_sensor_release(struct wf_sensor *sr)
190ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{
191ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat_sensor *sens = wf_to_sat(sr);
192ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat *sat = sens->sat;
193ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
194ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	kfree(sens);
195e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	kref_put(&sat->ref, wf_sat_release);
196ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
197ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
198ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidtstatic struct wf_sensor_ops wf_sat_ops = {
199e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	.get_value	= wf_sat_sensor_get,
200e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	.release	= wf_sat_sensor_release,
201ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	.owner		= THIS_MODULE,
202ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt};
203ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
204351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic int wf_sat_probe(struct i2c_client *client,
205351ca3e31197929535418f5affc761cd9fb07428Jean Delvare			const struct i2c_device_id *id)
206351ca3e31197929535418f5affc761cd9fb07428Jean Delvare{
2079525a08b30b1c8e39578938fc3420ac0b56f3a3dBenjamin Herrenschmidt	struct device_node *dev = client->dev.of_node;
208ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat *sat;
209ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct wf_sat_sensor *sens;
210018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr	const u32 *reg;
211018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr	const char *loc, *type;
212351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	u8 chip, core;
213ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	struct device_node *child;
214ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int shift, cpu, index;
215ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	char *name;
216ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	int vsens[2], isens[2];
217ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
218ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
219ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (sat == NULL)
220351ca3e31197929535418f5affc761cd9fb07428Jean Delvare		return -ENOMEM;
221ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	sat->nr = -1;
222ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	sat->node = of_node_get(dev);
223e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	kref_init(&sat->ref);
22456783c5e4dd32ca370ad0bdf3a9c6c1aaee94726Daniel Walker	mutex_init(&sat->mutex);
225351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	sat->i2c = client;
226e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	INIT_LIST_HEAD(&sat->sensors);
227351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	i2c_set_clientdata(client, sat);
228ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
229ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	vsens[0] = vsens[1] = -1;
230ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	isens[0] = isens[1] = -1;
231ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	child = NULL;
232ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	while ((child = of_get_next_child(dev, child)) != NULL) {
23301b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell		reg = of_get_property(child, "reg", NULL);
23401b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell		type = of_get_property(child, "device_type", NULL);
23501b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell		loc = of_get_property(child, "location", NULL);
236ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (reg == NULL || loc == NULL)
237ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
238ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
239ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		/* the cooked sensors are between 0x30 and 0x37 */
240ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (*reg < 0x30 || *reg > 0x37)
241ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
242ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		index = *reg - 0x30;
243ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
244ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		/* expect location to be CPU [AB][01] ... */
245ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (strncmp(loc, "CPU ", 4) != 0)
246ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
247ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		chip = loc[4] - 'A';
248ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		core = loc[5] - '0';
249ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (chip > 1 || core > 1) {
250ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			printk(KERN_ERR "wf_sat_create: don't understand "
251ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			       "location %s for %s\n", loc, child->full_name);
252ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
253ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
254ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		cpu = 2 * chip + core;
255ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (sat->nr < 0)
256ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			sat->nr = chip;
257ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		else if (sat->nr != chip) {
258ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			printk(KERN_ERR "wf_sat_create: can't cope with "
259ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			       "multiple CPU chips on one SAT (%s)\n", loc);
260ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
261ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
262ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
263ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (strcmp(type, "voltage-sensor") == 0) {
264ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			name = "cpu-voltage";
265ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			shift = 4;
266ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			vsens[core] = index;
267ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		} else if (strcmp(type, "current-sensor") == 0) {
268ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			name = "cpu-current";
269ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			shift = 8;
270ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			isens[core] = index;
271ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		} else if (strcmp(type, "temp-sensor") == 0) {
272ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			name = "cpu-temp";
273ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			shift = 10;
274ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		} else
275ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;	/* hmmm shouldn't happen */
276ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
277ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		/* the +16 is enough for "cpu-voltage-n" */
278ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
279ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (sens == NULL) {
280ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			printk(KERN_ERR "wf_sat_create: couldn't create "
281ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			       "%s sensor %d (no memory)\n", name, cpu);
282ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
283ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
284ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->index = index;
285ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->index2 = -1;
286ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->shift = shift;
287ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sat = sat;
288ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sens.ops = &wf_sat_ops;
289ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sens.name = (char *) (sens + 1);
29043671cc96e58458b2711f1e97ff24a4c0e7cd1acStephen Rothwell		snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu);
291ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
292e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		if (wf_register_sensor(&sens->sens))
293ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			kfree(sens);
294e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		else {
295e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt			list_add(&sens->link, &sat->sensors);
296e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt			kref_get(&sat->ref);
297ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
298ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
299ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
300ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	/* make the power sensors */
301ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	for (core = 0; core < 2; ++core) {
302ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (vsens[core] < 0 || isens[core] < 0)
303ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
304ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		cpu = 2 * sat->nr + core;
305ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
306ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		if (sens == NULL) {
307ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			printk(KERN_ERR "wf_sat_create: couldn't create power "
308ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			       "sensor %d (no memory)\n", cpu);
309ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			continue;
310ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
311ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->index = vsens[core];
312ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->index2 = isens[core];
313ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->shift = 0;
314ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sat = sat;
315ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sens.ops = &wf_sat_ops;
316ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sens->sens.name = (char *) (sens + 1);
31743671cc96e58458b2711f1e97ff24a4c0e7cd1acStephen Rothwell		snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu);
318ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
319e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		if (wf_register_sensor(&sens->sens))
320ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt			kfree(sens);
321e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		else {
322e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt			list_add(&sens->link, &sat->sensors);
323e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt			kref_get(&sat->ref);
324ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		}
325ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	}
326ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
327ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	if (sat->nr >= 0)
328ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt		sats[sat->nr] = sat;
329ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
330351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	return 0;
331ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
332ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
333351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic int wf_sat_remove(struct i2c_client *client)
334ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt{
335351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	struct wf_sat *sat = i2c_get_clientdata(client);
336e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	struct wf_sat_sensor *sens;
337ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
338e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	/* release sensors */
339e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	while(!list_empty(&sat->sensors)) {
340e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		sens = list_first_entry(&sat->sensors,
341e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt					struct wf_sat_sensor, link);
342e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		list_del(&sens->link);
343e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt		wf_unregister_sensor(&sens->sens);
344e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	}
345351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	sat->i2c = NULL;
346e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	kref_put(&sat->ref, wf_sat_release);
347e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt
348ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt	return 0;
349ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt}
350ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
351351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic const struct i2c_device_id wf_sat_id[] = {
352e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin Herrenschmidt	{ "MAC,smu-sat", 0 },
353351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	{ }
354351ca3e31197929535418f5affc761cd9fb07428Jean Delvare};
355e326b30fda9a985a2e7fda6fb9212b86bf025c39Benjamin HerrenschmidtMODULE_DEVICE_TABLE(i2c, wf_sat_id);
356351ca3e31197929535418f5affc761cd9fb07428Jean Delvare
357351ca3e31197929535418f5affc761cd9fb07428Jean Delvarestatic struct i2c_driver wf_sat_driver = {
358351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	.driver = {
359351ca3e31197929535418f5affc761cd9fb07428Jean Delvare		.name		= "wf_smu_sat",
360351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	},
361351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	.probe		= wf_sat_probe,
362351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	.remove		= wf_sat_remove,
363351ca3e31197929535418f5affc761cd9fb07428Jean Delvare	.id_table	= wf_sat_id,
364351ca3e31197929535418f5affc761cd9fb07428Jean Delvare};
365351ca3e31197929535418f5affc761cd9fb07428Jean Delvare
366f7fb862b843269d02a2fa75e4bbb49603f801b88Wei Yongjunmodule_i2c_driver(wf_sat_driver);
367ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin Herrenschmidt
368ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
369ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
370ac171c46667c1cb2ee9e22312291df6ed78e1b6eBenjamin HerrenschmidtMODULE_LICENSE("GPL");
371