Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/i2c/busses/i2c-powermac.c
15111 views
1
/*
2
i2c Support for Apple SMU Controller
3
4
Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp.
5
<[email protected]>
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/module.h>
24
#include <linux/kernel.h>
25
#include <linux/types.h>
26
#include <linux/i2c.h>
27
#include <linux/init.h>
28
#include <linux/device.h>
29
#include <linux/platform_device.h>
30
#include <asm/prom.h>
31
#include <asm/pmac_low_i2c.h>
32
33
MODULE_AUTHOR("Benjamin Herrenschmidt <[email protected]>");
34
MODULE_DESCRIPTION("I2C driver for Apple PowerMac");
35
MODULE_LICENSE("GPL");
36
37
/*
38
* SMBUS-type transfer entrypoint
39
*/
40
static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
41
u16 addr,
42
unsigned short flags,
43
char read_write,
44
u8 command,
45
int size,
46
union i2c_smbus_data* data)
47
{
48
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
49
int rc = 0;
50
int read = (read_write == I2C_SMBUS_READ);
51
int addrdir = (addr << 1) | read;
52
int mode, subsize, len;
53
u32 subaddr;
54
u8 *buf;
55
u8 local[2];
56
57
if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) {
58
mode = pmac_i2c_mode_std;
59
subsize = 0;
60
subaddr = 0;
61
} else {
62
mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub;
63
subsize = 1;
64
subaddr = command;
65
}
66
67
switch (size) {
68
case I2C_SMBUS_QUICK:
69
buf = NULL;
70
len = 0;
71
break;
72
case I2C_SMBUS_BYTE:
73
case I2C_SMBUS_BYTE_DATA:
74
buf = &data->byte;
75
len = 1;
76
break;
77
case I2C_SMBUS_WORD_DATA:
78
if (!read) {
79
local[0] = data->word & 0xff;
80
local[1] = (data->word >> 8) & 0xff;
81
}
82
buf = local;
83
len = 2;
84
break;
85
86
/* Note that these are broken vs. the expected smbus API where
87
* on reads, the length is actually returned from the function,
88
* but I think the current API makes no sense and I don't want
89
* any driver that I haven't verified for correctness to go
90
* anywhere near a pmac i2c bus anyway ...
91
*
92
* I'm also not completely sure what kind of phases to do between
93
* the actual command and the data (what I am _supposed_ to do that
94
* is). For now, I assume writes are a single stream and reads have
95
* a repeat start/addr phase (but not stop in between)
96
*/
97
case I2C_SMBUS_BLOCK_DATA:
98
buf = data->block;
99
len = data->block[0] + 1;
100
break;
101
case I2C_SMBUS_I2C_BLOCK_DATA:
102
buf = &data->block[1];
103
len = data->block[0];
104
break;
105
106
default:
107
return -EINVAL;
108
}
109
110
rc = pmac_i2c_open(bus, 0);
111
if (rc) {
112
dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);
113
return rc;
114
}
115
116
rc = pmac_i2c_setmode(bus, mode);
117
if (rc) {
118
dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",
119
mode, rc);
120
goto bail;
121
}
122
123
rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len);
124
if (rc) {
125
if (rc == -ENXIO)
126
dev_dbg(&adap->dev,
127
"I2C transfer at 0x%02x failed, size %d, "
128
"err %d\n", addrdir >> 1, size, rc);
129
else
130
dev_err(&adap->dev,
131
"I2C transfer at 0x%02x failed, size %d, "
132
"err %d\n", addrdir >> 1, size, rc);
133
goto bail;
134
}
135
136
if (size == I2C_SMBUS_WORD_DATA && read) {
137
data->word = ((u16)local[1]) << 8;
138
data->word |= local[0];
139
}
140
141
bail:
142
pmac_i2c_close(bus);
143
return rc;
144
}
145
146
/*
147
* Generic i2c master transfer entrypoint. This driver only support single
148
* messages (for "lame i2c" transfers). Anything else should use the smbus
149
* entry point
150
*/
151
static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
152
struct i2c_msg *msgs,
153
int num)
154
{
155
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
156
int rc = 0;
157
int read;
158
int addrdir;
159
160
if (num != 1) {
161
dev_err(&adap->dev,
162
"Multi-message I2C transactions not supported\n");
163
return -EOPNOTSUPP;
164
}
165
166
if (msgs->flags & I2C_M_TEN)
167
return -EINVAL;
168
read = (msgs->flags & I2C_M_RD) != 0;
169
addrdir = (msgs->addr << 1) | read;
170
171
rc = pmac_i2c_open(bus, 0);
172
if (rc) {
173
dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);
174
return rc;
175
}
176
rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
177
if (rc) {
178
dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",
179
pmac_i2c_mode_std, rc);
180
goto bail;
181
}
182
rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len);
183
if (rc < 0) {
184
if (rc == -ENXIO)
185
dev_dbg(&adap->dev, "I2C %s 0x%02x failed, err %d\n",
186
addrdir & 1 ? "read from" : "write to",
187
addrdir >> 1, rc);
188
else
189
dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n",
190
addrdir & 1 ? "read from" : "write to",
191
addrdir >> 1, rc);
192
}
193
bail:
194
pmac_i2c_close(bus);
195
return rc < 0 ? rc : 1;
196
}
197
198
static u32 i2c_powermac_func(struct i2c_adapter * adapter)
199
{
200
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
201
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
202
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C;
203
}
204
205
/* For now, we only handle smbus */
206
static const struct i2c_algorithm i2c_powermac_algorithm = {
207
.smbus_xfer = i2c_powermac_smbus_xfer,
208
.master_xfer = i2c_powermac_master_xfer,
209
.functionality = i2c_powermac_func,
210
};
211
212
213
static int __devexit i2c_powermac_remove(struct platform_device *dev)
214
{
215
struct i2c_adapter *adapter = platform_get_drvdata(dev);
216
int rc;
217
218
rc = i2c_del_adapter(adapter);
219
/* We aren't that prepared to deal with this... */
220
if (rc)
221
printk(KERN_WARNING
222
"i2c-powermac.c: Failed to remove bus %s !\n",
223
adapter->name);
224
platform_set_drvdata(dev, NULL);
225
memset(adapter, 0, sizeof(*adapter));
226
227
return 0;
228
}
229
230
231
static int __devinit i2c_powermac_probe(struct platform_device *dev)
232
{
233
struct pmac_i2c_bus *bus = dev->dev.platform_data;
234
struct device_node *parent = NULL;
235
struct i2c_adapter *adapter;
236
const char *basename;
237
int rc;
238
239
if (bus == NULL)
240
return -EINVAL;
241
adapter = pmac_i2c_get_adapter(bus);
242
243
/* Ok, now we need to make up a name for the interface that will
244
* match what we used to do in the past, that is basically the
245
* controller's parent device node for keywest. PMU didn't have a
246
* naming convention and SMU has a different one
247
*/
248
switch(pmac_i2c_get_type(bus)) {
249
case pmac_i2c_bus_keywest:
250
parent = of_get_parent(pmac_i2c_get_controller(bus));
251
if (parent == NULL)
252
return -EINVAL;
253
basename = parent->name;
254
break;
255
case pmac_i2c_bus_pmu:
256
basename = "pmu";
257
break;
258
case pmac_i2c_bus_smu:
259
/* This is not what we used to do but I'm fixing drivers at
260
* the same time as this change
261
*/
262
basename = "smu";
263
break;
264
default:
265
return -EINVAL;
266
}
267
snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename,
268
pmac_i2c_get_channel(bus));
269
of_node_put(parent);
270
271
platform_set_drvdata(dev, adapter);
272
adapter->algo = &i2c_powermac_algorithm;
273
i2c_set_adapdata(adapter, bus);
274
adapter->dev.parent = &dev->dev;
275
rc = i2c_add_adapter(adapter);
276
if (rc) {
277
printk(KERN_ERR "i2c-powermac: Adapter %s registration "
278
"failed\n", adapter->name);
279
memset(adapter, 0, sizeof(*adapter));
280
}
281
282
printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name);
283
284
if (!strncmp(basename, "uni-n", 5)) {
285
struct device_node *np;
286
const u32 *prop;
287
struct i2c_board_info info;
288
289
/* Instantiate I2C motion sensor if present */
290
np = of_find_node_by_name(NULL, "accelerometer");
291
if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") &&
292
(prop = of_get_property(np, "reg", NULL))) {
293
int i2c_bus;
294
const char *tmp_bus;
295
296
/* look for bus either using "reg" or by path */
297
tmp_bus = strstr(np->full_name, "/i2c-bus@");
298
if (tmp_bus)
299
i2c_bus = *(tmp_bus + 9) - '0';
300
else
301
i2c_bus = ((*prop) >> 8) & 0x0f;
302
303
if (pmac_i2c_get_channel(bus) == i2c_bus) {
304
memset(&info, 0, sizeof(struct i2c_board_info));
305
info.addr = ((*prop) & 0xff) >> 1;
306
strlcpy(info.type, "ams", I2C_NAME_SIZE);
307
i2c_new_device(adapter, &info);
308
}
309
}
310
}
311
312
return rc;
313
}
314
315
316
/* work with hotplug and coldplug */
317
MODULE_ALIAS("platform:i2c-powermac");
318
319
static struct platform_driver i2c_powermac_driver = {
320
.probe = i2c_powermac_probe,
321
.remove = __devexit_p(i2c_powermac_remove),
322
.driver = {
323
.name = "i2c-powermac",
324
.bus = &platform_bus_type,
325
},
326
};
327
328
static int __init i2c_powermac_init(void)
329
{
330
platform_driver_register(&i2c_powermac_driver);
331
return 0;
332
}
333
334
335
static void __exit i2c_powermac_cleanup(void)
336
{
337
platform_driver_unregister(&i2c_powermac_driver);
338
}
339
340
module_init(i2c_powermac_init);
341
module_exit(i2c_powermac_cleanup);
342
343