aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-pmac-smu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-pmac-smu.c')
-rw-r--r--drivers/i2c/busses/i2c-pmac-smu.c324
1 files changed, 0 insertions, 324 deletions
diff --git a/drivers/i2c/busses/i2c-pmac-smu.c b/drivers/i2c/busses/i2c-pmac-smu.c
deleted file mode 100644
index 7d925be3fd4b..000000000000
--- a/drivers/i2c/busses/i2c-pmac-smu.c
+++ /dev/null
@@ -1,324 +0,0 @@
1/*
2 i2c Support for Apple SMU Controller
3
4 Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
5 <benh@kernel.crashing.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21*/
22
23#include <linux/config.h>
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/types.h>
27#include <linux/i2c.h>
28#include <linux/init.h>
29#include <linux/completion.h>
30#include <linux/device.h>
31#include <asm/prom.h>
32#include <asm/of_device.h>
33#include <asm/smu.h>
34
35static int probe;
36
37MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
38MODULE_DESCRIPTION("I2C driver for Apple's SMU");
39MODULE_LICENSE("GPL");
40module_param(probe, bool, 0);
41
42
43/* Physical interface */
44struct smu_iface
45{
46 struct i2c_adapter adapter;
47 struct completion complete;
48 u32 busid;
49};
50
51static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc)
52{
53 struct smu_iface *iface = misc;
54 complete(&iface->complete);
55}
56
57/*
58 * SMBUS-type transfer entrypoint
59 */
60static s32 smu_smbus_xfer( struct i2c_adapter* adap,
61 u16 addr,
62 unsigned short flags,
63 char read_write,
64 u8 command,
65 int size,
66 union i2c_smbus_data* data)
67{
68 struct smu_iface *iface = i2c_get_adapdata(adap);
69 struct smu_i2c_cmd cmd;
70 int rc = 0;
71 int read = (read_write == I2C_SMBUS_READ);
72
73 cmd.info.bus = iface->busid;
74 cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00);
75
76 /* Prepare datas & select mode */
77 switch (size) {
78 case I2C_SMBUS_QUICK:
79 cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;
80 cmd.info.datalen = 0;
81 break;
82 case I2C_SMBUS_BYTE:
83 cmd.info.type = SMU_I2C_TRANSFER_SIMPLE;
84 cmd.info.datalen = 1;
85 if (!read)
86 cmd.info.data[0] = data->byte;
87 break;
88 case I2C_SMBUS_BYTE_DATA:
89 cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
90 cmd.info.datalen = 1;
91 cmd.info.sublen = 1;
92 cmd.info.subaddr[0] = command;
93 cmd.info.subaddr[1] = 0;
94 cmd.info.subaddr[2] = 0;
95 if (!read)
96 cmd.info.data[0] = data->byte;
97 break;
98 case I2C_SMBUS_WORD_DATA:
99 cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
100 cmd.info.datalen = 2;
101 cmd.info.sublen = 1;
102 cmd.info.subaddr[0] = command;
103 cmd.info.subaddr[1] = 0;
104 cmd.info.subaddr[2] = 0;
105 if (!read) {
106 cmd.info.data[0] = data->word & 0xff;
107 cmd.info.data[1] = (data->word >> 8) & 0xff;
108 }
109 break;
110 /* Note that these are broken vs. the expected smbus API where
111 * on reads, the lenght is actually returned from the function,
112 * but I think the current API makes no sense and I don't want
113 * any driver that I haven't verified for correctness to go
114 * anywhere near a pmac i2c bus anyway ...
115 */
116 case I2C_SMBUS_BLOCK_DATA:
117 cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
118 cmd.info.datalen = data->block[0] + 1;
119 if (cmd.info.datalen > (SMU_I2C_WRITE_MAX + 1))
120 return -EINVAL;
121 if (!read)
122 memcpy(cmd.info.data, data->block, cmd.info.datalen);
123 cmd.info.sublen = 1;
124 cmd.info.subaddr[0] = command;
125 cmd.info.subaddr[1] = 0;
126 cmd.info.subaddr[2] = 0;
127 break;
128 case I2C_SMBUS_I2C_BLOCK_DATA:
129 cmd.info.type = SMU_I2C_TRANSFER_STDSUB;
130 cmd.info.datalen = data->block[0];
131 if (cmd.info.datalen > 7)
132 return -EINVAL;
133 if (!read)
134 memcpy(cmd.info.data, &data->block[1],
135 cmd.info.datalen);
136 cmd.info.sublen = 1;
137 cmd.info.subaddr[0] = command;
138 cmd.info.subaddr[1] = 0;
139 cmd.info.subaddr[2] = 0;
140 break;
141
142 default:
143 return -EINVAL;
144 }
145
146 /* Turn a standardsub read into a combined mode access */
147 if (read_write == I2C_SMBUS_READ &&
148 cmd.info.type == SMU_I2C_TRANSFER_STDSUB)
149 cmd.info.type = SMU_I2C_TRANSFER_COMBINED;
150
151 /* Finish filling command and submit it */
152 cmd.done = smu_i2c_done;
153 cmd.misc = iface;
154 rc = smu_queue_i2c(&cmd);
155 if (rc < 0)
156 return rc;
157 wait_for_completion(&iface->complete);
158 rc = cmd.status;
159
160 if (!read || rc < 0)
161 return rc;
162
163 switch (size) {
164 case I2C_SMBUS_BYTE:
165 case I2C_SMBUS_BYTE_DATA:
166 data->byte = cmd.info.data[0];
167 break;
168 case I2C_SMBUS_WORD_DATA:
169 data->word = ((u16)cmd.info.data[1]) << 8;
170 data->word |= cmd.info.data[0];
171 break;
172 /* Note that these are broken vs. the expected smbus API where
173 * on reads, the lenght is actually returned from the function,
174 * but I think the current API makes no sense and I don't want
175 * any driver that I haven't verified for correctness to go
176 * anywhere near a pmac i2c bus anyway ...
177 */
178 case I2C_SMBUS_BLOCK_DATA:
179 case I2C_SMBUS_I2C_BLOCK_DATA:
180 memcpy(&data->block[0], cmd.info.data, cmd.info.datalen);
181 break;
182 }
183
184 return rc;
185}
186
187static u32
188smu_smbus_func(struct i2c_adapter * adapter)
189{
190 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
191 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
192 I2C_FUNC_SMBUS_BLOCK_DATA;
193}
194
195/* For now, we only handle combined mode (smbus) */
196static struct i2c_algorithm smu_algorithm = {
197 .smbus_xfer = smu_smbus_xfer,
198 .functionality = smu_smbus_func,
199};
200
201static int create_iface(struct device_node *np, struct device *dev)
202{
203 struct smu_iface* iface;
204 u32 *reg, busid;
205 int rc;
206
207 reg = (u32 *)get_property(np, "reg", NULL);
208 if (reg == NULL) {
209 printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n");
210 return -ENXIO;
211 }
212 busid = *reg;
213
214 iface = kzalloc(sizeof(struct smu_iface), GFP_KERNEL);
215 if (iface == NULL) {
216 printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n");
217 return -ENOMEM;
218 }
219 init_completion(&iface->complete);
220 iface->busid = busid;
221
222 dev_set_drvdata(dev, iface);
223
224 sprintf(iface->adapter.name, "smu-i2c-%02x", busid);
225 iface->adapter.algo = &smu_algorithm;
226 iface->adapter.algo_data = NULL;
227 iface->adapter.client_register = NULL;
228 iface->adapter.client_unregister = NULL;
229 i2c_set_adapdata(&iface->adapter, iface);
230 iface->adapter.dev.parent = dev;
231
232 rc = i2c_add_adapter(&iface->adapter);
233 if (rc) {
234 printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration "
235 "failed\n", iface->adapter.name);
236 i2c_set_adapdata(&iface->adapter, NULL);
237 }
238
239 if (probe) {
240 unsigned char addr;
241 printk("Probe: ");
242 for (addr = 0x00; addr <= 0x7f; addr++) {
243 if (i2c_smbus_xfer(&iface->adapter,addr,
244 0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
245 printk("%02x ", addr);
246 }
247 printk("\n");
248 }
249
250 printk(KERN_INFO "SMU i2c bus %x registered\n", busid);
251
252 return 0;
253}
254
255static int dispose_iface(struct device *dev)
256{
257 struct smu_iface *iface = dev_get_drvdata(dev);
258 int rc;
259
260 rc = i2c_del_adapter(&iface->adapter);
261 i2c_set_adapdata(&iface->adapter, NULL);
262 /* We aren't that prepared to deal with this... */
263 if (rc)
264 printk("i2c-pmac-smu.c: Failed to remove bus %s !\n",
265 iface->adapter.name);
266 dev_set_drvdata(dev, NULL);
267 kfree(iface);
268
269 return 0;
270}
271
272
273static int create_iface_of_platform(struct of_device* dev,
274 const struct of_device_id *match)
275{
276 struct device_node *node = dev->node;
277
278 if (device_is_compatible(node, "smu-i2c") ||
279 (node->parent != NULL &&
280 device_is_compatible(node->parent, "smu-i2c-control")))
281 return create_iface(node, &dev->dev);
282 return -ENODEV;
283}
284
285
286static int dispose_iface_of_platform(struct of_device* dev)
287{
288 return dispose_iface(&dev->dev);
289}
290
291
292static struct of_device_id i2c_smu_match[] =
293{
294 {
295 .compatible = "smu-i2c",
296 },
297 {
298 .compatible = "i2c-bus",
299 },
300 {},
301};
302static struct of_platform_driver i2c_smu_of_platform_driver =
303{
304 .name = "i2c-smu",
305 .match_table = i2c_smu_match,
306 .probe = create_iface_of_platform,
307 .remove = dispose_iface_of_platform
308};
309
310
311static int __init i2c_pmac_smu_init(void)
312{
313 of_register_driver(&i2c_smu_of_platform_driver);
314 return 0;
315}
316
317
318static void __exit i2c_pmac_smu_cleanup(void)
319{
320 of_unregister_driver(&i2c_smu_of_platform_driver);
321}
322
323module_init(i2c_pmac_smu_init);
324module_exit(i2c_pmac_smu_cleanup);