2 i2c Support for Apple SMU Controller
4 Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
5 <benh@kernel.crashing.org>
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.
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.
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.
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>
32 #include <asm/of_device.h>
37 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
38 MODULE_DESCRIPTION("I2C driver for Apple's SMU");
39 MODULE_LICENSE("GPL");
40 module_param(probe
, bool, 0);
43 /* Physical interface */
46 struct i2c_adapter adapter
;
47 struct completion complete
;
51 static void smu_i2c_done(struct smu_i2c_cmd
*cmd
, void *misc
)
53 struct smu_iface
*iface
= misc
;
54 complete(&iface
->complete
);
58 * SMBUS-type transfer entrypoint
60 static s32
smu_smbus_xfer( struct i2c_adapter
* adap
,
66 union i2c_smbus_data
* data
)
68 struct smu_iface
*iface
= i2c_get_adapdata(adap
);
69 struct smu_i2c_cmd cmd
;
71 int read
= (read_write
== I2C_SMBUS_READ
);
73 cmd
.info
.bus
= iface
->busid
;
74 cmd
.info
.devaddr
= (addr
<< 1) | (read
? 0x01 : 0x00);
76 /* Prepare datas & select mode */
79 cmd
.info
.type
= SMU_I2C_TRANSFER_SIMPLE
;
83 cmd
.info
.type
= SMU_I2C_TRANSFER_SIMPLE
;
86 cmd
.info
.data
[0] = data
->byte
;
88 case I2C_SMBUS_BYTE_DATA
:
89 cmd
.info
.type
= SMU_I2C_TRANSFER_STDSUB
;
92 cmd
.info
.subaddr
[0] = command
;
93 cmd
.info
.subaddr
[1] = 0;
94 cmd
.info
.subaddr
[2] = 0;
96 cmd
.info
.data
[0] = data
->byte
;
98 case I2C_SMBUS_WORD_DATA
:
99 cmd
.info
.type
= SMU_I2C_TRANSFER_STDSUB
;
100 cmd
.info
.datalen
= 2;
102 cmd
.info
.subaddr
[0] = command
;
103 cmd
.info
.subaddr
[1] = 0;
104 cmd
.info
.subaddr
[2] = 0;
106 cmd
.info
.data
[0] = data
->byte
& 0xff;
107 cmd
.info
.data
[1] = (data
->byte
>> 8) & 0xff;
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 ...
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
> 6)
122 memcpy(cmd
.info
.data
, data
->block
, cmd
.info
.datalen
);
124 cmd
.info
.subaddr
[0] = command
;
125 cmd
.info
.subaddr
[1] = 0;
126 cmd
.info
.subaddr
[2] = 0;
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)
134 memcpy(cmd
.info
.data
, &data
->block
[1],
137 cmd
.info
.subaddr
[0] = command
;
138 cmd
.info
.subaddr
[1] = 0;
139 cmd
.info
.subaddr
[2] = 0;
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
;
151 /* Finish filling command and submit it */
152 cmd
.done
= smu_i2c_done
;
154 rc
= smu_queue_i2c(&cmd
);
157 wait_for_completion(&iface
->complete
);
165 case I2C_SMBUS_BYTE_DATA
:
166 data
->byte
= cmd
.info
.data
[0];
168 case I2C_SMBUS_WORD_DATA
:
169 data
->word
= ((u16
)cmd
.info
.data
[1]) << 8;
170 data
->word
|= cmd
.info
.data
[0];
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 ...
178 case I2C_SMBUS_BLOCK_DATA
:
179 case I2C_SMBUS_I2C_BLOCK_DATA
:
180 memcpy(&data
->block
[0], cmd
.info
.data
, cmd
.info
.datalen
);
188 smu_smbus_func(struct i2c_adapter
* adapter
)
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
;
195 /* For now, we only handle combined mode (smbus) */
196 static struct i2c_algorithm smu_algorithm
= {
197 .smbus_xfer
= smu_smbus_xfer
,
198 .functionality
= smu_smbus_func
,
201 static int create_iface(struct device_node
*np
, struct device
*dev
)
203 struct smu_iface
* iface
;
207 reg
= (u32
*)get_property(np
, "reg", NULL
);
209 printk(KERN_ERR
"i2c-pmac-smu: can't find bus number !\n");
214 iface
= kmalloc(sizeof(struct smu_iface
), GFP_KERNEL
);
216 printk(KERN_ERR
"i2c-pmac-smu: can't allocate inteface !\n");
219 memset(iface
, 0, sizeof(struct smu_iface
));
220 init_completion(&iface
->complete
);
221 iface
->busid
= busid
;
223 dev_set_drvdata(dev
, iface
);
225 sprintf(iface
->adapter
.name
, "smu-i2c-%02x", busid
);
226 iface
->adapter
.algo
= &smu_algorithm
;
227 iface
->adapter
.algo_data
= NULL
;
228 iface
->adapter
.client_register
= NULL
;
229 iface
->adapter
.client_unregister
= NULL
;
230 i2c_set_adapdata(&iface
->adapter
, iface
);
231 iface
->adapter
.dev
.parent
= dev
;
233 rc
= i2c_add_adapter(&iface
->adapter
);
235 printk(KERN_ERR
"i2c-pamc-smu.c: Adapter %s registration "
236 "failed\n", iface
->adapter
.name
);
237 i2c_set_adapdata(&iface
->adapter
, NULL
);
243 for (addr
= 0x00; addr
<= 0x7f; addr
++) {
244 if (i2c_smbus_xfer(&iface
->adapter
,addr
,
245 0,0,0,I2C_SMBUS_QUICK
,NULL
) >= 0)
246 printk("%02x ", addr
);
251 printk(KERN_INFO
"SMU i2c bus %x registered\n", busid
);
256 static int dispose_iface(struct device
*dev
)
258 struct smu_iface
*iface
= dev_get_drvdata(dev
);
261 rc
= i2c_del_adapter(&iface
->adapter
);
262 i2c_set_adapdata(&iface
->adapter
, NULL
);
263 /* We aren't that prepared to deal with this... */
265 printk("i2c-pmac-smu.c: Failed to remove bus %s !\n",
266 iface
->adapter
.name
);
267 dev_set_drvdata(dev
, NULL
);
274 static int create_iface_of_platform(struct of_device
* dev
,
275 const struct of_device_id
*match
)
277 return create_iface(dev
->node
, &dev
->dev
);
281 static int dispose_iface_of_platform(struct of_device
* dev
)
283 return dispose_iface(&dev
->dev
);
287 static struct of_device_id i2c_smu_match
[] =
290 .compatible
= "smu-i2c",
294 static struct of_platform_driver i2c_smu_of_platform_driver
=
297 .match_table
= i2c_smu_match
,
298 .probe
= create_iface_of_platform
,
299 .remove
= dispose_iface_of_platform
303 static int __init
i2c_pmac_smu_init(void)
305 of_register_driver(&i2c_smu_of_platform_driver
);
310 static void __exit
i2c_pmac_smu_cleanup(void)
312 of_unregister_driver(&i2c_smu_of_platform_driver
);
315 module_init(i2c_pmac_smu_init
);
316 module_exit(i2c_pmac_smu_cleanup
);