~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/i2c/busses/i2c-scmi.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * SMBus driver for ACPI SMBus CMI
 
3
 *
 
4
 * Copyright (C) 2009 Crane Cai <crane.cai@amd.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation version 2.
 
9
 */
 
10
 
 
11
#include <linux/module.h>
 
12
#include <linux/slab.h>
 
13
#include <linux/kernel.h>
 
14
#include <linux/stddef.h>
 
15
#include <linux/init.h>
 
16
#include <linux/i2c.h>
 
17
#include <linux/acpi.h>
 
18
 
 
19
#define ACPI_SMBUS_HC_CLASS             "smbus"
 
20
#define ACPI_SMBUS_HC_DEVICE_NAME       "cmi"
 
21
 
 
22
ACPI_MODULE_NAME("smbus_cmi");
 
23
 
 
24
struct smbus_methods_t {
 
25
        char *mt_info;
 
26
        char *mt_sbr;
 
27
        char *mt_sbw;
 
28
};
 
29
 
 
30
struct acpi_smbus_cmi {
 
31
        acpi_handle handle;
 
32
        struct i2c_adapter adapter;
 
33
        u8 cap_info:1;
 
34
        u8 cap_read:1;
 
35
        u8 cap_write:1;
 
36
        struct smbus_methods_t *methods;
 
37
};
 
38
 
 
39
static const struct smbus_methods_t smbus_methods = {
 
40
        .mt_info = "_SBI",
 
41
        .mt_sbr  = "_SBR",
 
42
        .mt_sbw  = "_SBW",
 
43
};
 
44
 
 
45
/* Some IBM BIOSes omit the leading underscore */
 
46
static const struct smbus_methods_t ibm_smbus_methods = {
 
47
        .mt_info = "SBI_",
 
48
        .mt_sbr  = "SBR_",
 
49
        .mt_sbw  = "SBW_",
 
50
};
 
51
 
 
52
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
 
53
        {"SMBUS01", (kernel_ulong_t)&smbus_methods},
 
54
        {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
 
55
        {"", 0}
 
56
};
 
57
MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);
 
58
 
 
59
#define ACPI_SMBUS_STATUS_OK                    0x00
 
60
#define ACPI_SMBUS_STATUS_FAIL                  0x07
 
61
#define ACPI_SMBUS_STATUS_DNAK                  0x10
 
62
#define ACPI_SMBUS_STATUS_DERR                  0x11
 
63
#define ACPI_SMBUS_STATUS_CMD_DENY              0x12
 
64
#define ACPI_SMBUS_STATUS_UNKNOWN               0x13
 
65
#define ACPI_SMBUS_STATUS_ACC_DENY              0x17
 
66
#define ACPI_SMBUS_STATUS_TIMEOUT               0x18
 
67
#define ACPI_SMBUS_STATUS_NOTSUP                0x19
 
68
#define ACPI_SMBUS_STATUS_BUSY                  0x1a
 
69
#define ACPI_SMBUS_STATUS_PEC                   0x1f
 
70
 
 
71
#define ACPI_SMBUS_PRTCL_WRITE                  0x00
 
72
#define ACPI_SMBUS_PRTCL_READ                   0x01
 
73
#define ACPI_SMBUS_PRTCL_QUICK                  0x02
 
74
#define ACPI_SMBUS_PRTCL_BYTE                   0x04
 
75
#define ACPI_SMBUS_PRTCL_BYTE_DATA              0x06
 
76
#define ACPI_SMBUS_PRTCL_WORD_DATA              0x08
 
77
#define ACPI_SMBUS_PRTCL_BLOCK_DATA             0x0a
 
78
 
 
79
 
 
80
static int
 
81
acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
 
82
                   char read_write, u8 command, int size,
 
83
                   union i2c_smbus_data *data)
 
84
{
 
85
        int result = 0;
 
86
        struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;
 
87
        unsigned char protocol;
 
88
        acpi_status status = 0;
 
89
        struct acpi_object_list input;
 
90
        union acpi_object mt_params[5];
 
91
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
92
        union acpi_object *obj;
 
93
        union acpi_object *pkg;
 
94
        char *method;
 
95
        int len = 0;
 
96
 
 
97
        dev_dbg(&adap->dev, "access size: %d %s\n", size,
 
98
                (read_write) ? "READ" : "WRITE");
 
99
        switch (size) {
 
100
        case I2C_SMBUS_QUICK:
 
101
                protocol = ACPI_SMBUS_PRTCL_QUICK;
 
102
                command = 0;
 
103
                if (read_write == I2C_SMBUS_WRITE) {
 
104
                        mt_params[3].type = ACPI_TYPE_INTEGER;
 
105
                        mt_params[3].integer.value = 0;
 
106
                        mt_params[4].type = ACPI_TYPE_INTEGER;
 
107
                        mt_params[4].integer.value = 0;
 
108
                }
 
109
                break;
 
110
 
 
111
        case I2C_SMBUS_BYTE:
 
112
                protocol = ACPI_SMBUS_PRTCL_BYTE;
 
113
                if (read_write == I2C_SMBUS_WRITE) {
 
114
                        mt_params[3].type = ACPI_TYPE_INTEGER;
 
115
                        mt_params[3].integer.value = 0;
 
116
                        mt_params[4].type = ACPI_TYPE_INTEGER;
 
117
                        mt_params[4].integer.value = 0;
 
118
                } else {
 
119
                        command = 0;
 
120
                }
 
121
                break;
 
122
 
 
123
        case I2C_SMBUS_BYTE_DATA:
 
124
                protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;
 
125
                if (read_write == I2C_SMBUS_WRITE) {
 
126
                        mt_params[3].type = ACPI_TYPE_INTEGER;
 
127
                        mt_params[3].integer.value = 1;
 
128
                        mt_params[4].type = ACPI_TYPE_INTEGER;
 
129
                        mt_params[4].integer.value = data->byte;
 
130
                }
 
131
                break;
 
132
 
 
133
        case I2C_SMBUS_WORD_DATA:
 
134
                protocol = ACPI_SMBUS_PRTCL_WORD_DATA;
 
135
                if (read_write == I2C_SMBUS_WRITE) {
 
136
                        mt_params[3].type = ACPI_TYPE_INTEGER;
 
137
                        mt_params[3].integer.value = 2;
 
138
                        mt_params[4].type = ACPI_TYPE_INTEGER;
 
139
                        mt_params[4].integer.value = data->word;
 
140
                }
 
141
                break;
 
142
 
 
143
        case I2C_SMBUS_BLOCK_DATA:
 
144
                protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;
 
145
                if (read_write == I2C_SMBUS_WRITE) {
 
146
                        len = data->block[0];
 
147
                        if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 
148
                                return -EINVAL;
 
149
                        mt_params[3].type = ACPI_TYPE_INTEGER;
 
150
                        mt_params[3].integer.value = len;
 
151
                        mt_params[4].type = ACPI_TYPE_BUFFER;
 
152
                        mt_params[4].buffer.pointer = data->block + 1;
 
153
                }
 
154
                break;
 
155
 
 
156
        default:
 
157
                dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
 
158
                return -EOPNOTSUPP;
 
159
        }
 
160
 
 
161
        if (read_write == I2C_SMBUS_READ) {
 
162
                protocol |= ACPI_SMBUS_PRTCL_READ;
 
163
                method = smbus_cmi->methods->mt_sbr;
 
164
                input.count = 3;
 
165
        } else {
 
166
                protocol |= ACPI_SMBUS_PRTCL_WRITE;
 
167
                method = smbus_cmi->methods->mt_sbw;
 
168
                input.count = 5;
 
169
        }
 
170
 
 
171
        input.pointer = mt_params;
 
172
        mt_params[0].type = ACPI_TYPE_INTEGER;
 
173
        mt_params[0].integer.value = protocol;
 
174
        mt_params[1].type = ACPI_TYPE_INTEGER;
 
175
        mt_params[1].integer.value = addr;
 
176
        mt_params[2].type = ACPI_TYPE_INTEGER;
 
177
        mt_params[2].integer.value = command;
 
178
 
 
179
        status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
 
180
                                      &buffer);
 
181
        if (ACPI_FAILURE(status)) {
 
182
                ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
 
183
                return -EIO;
 
184
        }
 
185
 
 
186
        pkg = buffer.pointer;
 
187
        if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
 
188
                obj = pkg->package.elements;
 
189
        else {
 
190
                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
191
                result = -EIO;
 
192
                goto out;
 
193
        }
 
194
        if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
 
195
                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
196
                result = -EIO;
 
197
                goto out;
 
198
        }
 
199
 
 
200
        result = obj->integer.value;
 
201
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
 
202
                          method, result));
 
203
 
 
204
        switch (result) {
 
205
        case ACPI_SMBUS_STATUS_OK:
 
206
                result = 0;
 
207
                break;
 
208
        case ACPI_SMBUS_STATUS_BUSY:
 
209
                result = -EBUSY;
 
210
                goto out;
 
211
        case ACPI_SMBUS_STATUS_TIMEOUT:
 
212
                result = -ETIMEDOUT;
 
213
                goto out;
 
214
        case ACPI_SMBUS_STATUS_DNAK:
 
215
                result = -ENXIO;
 
216
                goto out;
 
217
        default:
 
218
                result = -EIO;
 
219
                goto out;
 
220
        }
 
221
 
 
222
        if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK)
 
223
                goto out;
 
224
 
 
225
        obj = pkg->package.elements + 1;
 
226
        if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
 
227
                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
228
                result = -EIO;
 
229
                goto out;
 
230
        }
 
231
 
 
232
        len = obj->integer.value;
 
233
        obj = pkg->package.elements + 2;
 
234
        switch (size) {
 
235
        case I2C_SMBUS_BYTE:
 
236
        case I2C_SMBUS_BYTE_DATA:
 
237
        case I2C_SMBUS_WORD_DATA:
 
238
                if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
 
239
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
240
                        result = -EIO;
 
241
                        goto out;
 
242
                }
 
243
                if (len == 2)
 
244
                        data->word = obj->integer.value;
 
245
                else
 
246
                        data->byte = obj->integer.value;
 
247
                break;
 
248
        case I2C_SMBUS_BLOCK_DATA:
 
249
                if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) {
 
250
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
251
                        result = -EIO;
 
252
                        goto out;
 
253
                }
 
254
                if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 
255
                        return -EPROTO;
 
256
                data->block[0] = len;
 
257
                memcpy(data->block + 1, obj->buffer.pointer, len);
 
258
                break;
 
259
        }
 
260
 
 
261
out:
 
262
        kfree(buffer.pointer);
 
263
        dev_dbg(&adap->dev, "Transaction status: %i\n", result);
 
264
        return result;
 
265
}
 
266
 
 
267
static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)
 
268
{
 
269
        struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;
 
270
        u32 ret;
 
271
 
 
272
        ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?
 
273
                I2C_FUNC_SMBUS_QUICK : 0;
 
274
 
 
275
        ret |= smbus_cmi->cap_read ?
 
276
                (I2C_FUNC_SMBUS_READ_BYTE |
 
277
                I2C_FUNC_SMBUS_READ_BYTE_DATA |
 
278
                I2C_FUNC_SMBUS_READ_WORD_DATA |
 
279
                I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;
 
280
 
 
281
        ret |= smbus_cmi->cap_write ?
 
282
                (I2C_FUNC_SMBUS_WRITE_BYTE |
 
283
                I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
 
284
                I2C_FUNC_SMBUS_WRITE_WORD_DATA |
 
285
                I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;
 
286
 
 
287
        return ret;
 
288
}
 
289
 
 
290
static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
 
291
        .smbus_xfer = acpi_smbus_cmi_access,
 
292
        .functionality = acpi_smbus_cmi_func,
 
293
};
 
294
 
 
295
 
 
296
static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
 
297
                                  const char *name)
 
298
{
 
299
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
300
        union acpi_object *obj;
 
301
        acpi_status status;
 
302
 
 
303
        if (!strcmp(name, smbus_cmi->methods->mt_info)) {
 
304
                status = acpi_evaluate_object(smbus_cmi->handle,
 
305
                                        smbus_cmi->methods->mt_info,
 
306
                                        NULL, &buffer);
 
307
                if (ACPI_FAILURE(status)) {
 
308
                        ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
 
309
                                   smbus_cmi->methods->mt_info, status));
 
310
                        return -EIO;
 
311
                }
 
312
 
 
313
                obj = buffer.pointer;
 
314
                if (obj && obj->type == ACPI_TYPE_PACKAGE)
 
315
                        obj = obj->package.elements;
 
316
                else {
 
317
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
318
                        kfree(buffer.pointer);
 
319
                        return -EIO;
 
320
                }
 
321
 
 
322
                if (obj->type != ACPI_TYPE_INTEGER) {
 
323
                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 
324
                        kfree(buffer.pointer);
 
325
                        return -EIO;
 
326
                } else
 
327
                        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
 
328
                                          "\n", (int)obj->integer.value));
 
329
 
 
330
                kfree(buffer.pointer);
 
331
                smbus_cmi->cap_info = 1;
 
332
        } else if (!strcmp(name, smbus_cmi->methods->mt_sbr))
 
333
                smbus_cmi->cap_read = 1;
 
334
        else if (!strcmp(name, smbus_cmi->methods->mt_sbw))
 
335
                smbus_cmi->cap_write = 1;
 
336
        else
 
337
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
 
338
                                 name));
 
339
 
 
340
        return 0;
 
341
}
 
342
 
 
343
static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
 
344
                        void *context, void **return_value)
 
345
{
 
346
        char node_name[5];
 
347
        struct acpi_buffer buffer = { sizeof(node_name), node_name };
 
348
        struct acpi_smbus_cmi *smbus_cmi = context;
 
349
        acpi_status status;
 
350
 
 
351
        status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
 
352
 
 
353
        if (ACPI_SUCCESS(status))
 
354
                acpi_smbus_cmi_add_cap(smbus_cmi, node_name);
 
355
 
 
356
        return AE_OK;
 
357
}
 
358
 
 
359
static int acpi_smbus_cmi_add(struct acpi_device *device)
 
360
{
 
361
        struct acpi_smbus_cmi *smbus_cmi;
 
362
        const struct acpi_device_id *id;
 
363
 
 
364
        smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
 
365
        if (!smbus_cmi)
 
366
                return -ENOMEM;
 
367
 
 
368
        smbus_cmi->handle = device->handle;
 
369
        strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
 
370
        strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
 
371
        device->driver_data = smbus_cmi;
 
372
        smbus_cmi->cap_info = 0;
 
373
        smbus_cmi->cap_read = 0;
 
374
        smbus_cmi->cap_write = 0;
 
375
 
 
376
        for (id = acpi_smbus_cmi_ids; id->id[0]; id++)
 
377
                if (!strcmp(id->id, acpi_device_hid(device)))
 
378
                        smbus_cmi->methods =
 
379
                                (struct smbus_methods_t *) id->driver_data;
 
380
 
 
381
        acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
 
382
                            acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
 
383
 
 
384
        if (smbus_cmi->cap_info == 0)
 
385
                goto err;
 
386
 
 
387
        snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
 
388
                "SMBus CMI adapter %s",
 
389
                acpi_device_name(device));
 
390
        smbus_cmi->adapter.owner = THIS_MODULE;
 
391
        smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
 
392
        smbus_cmi->adapter.algo_data = smbus_cmi;
 
393
        smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 
394
        smbus_cmi->adapter.dev.parent = &device->dev;
 
395
 
 
396
        if (i2c_add_adapter(&smbus_cmi->adapter)) {
 
397
                dev_err(&device->dev, "Couldn't register adapter!\n");
 
398
                goto err;
 
399
        }
 
400
 
 
401
        return 0;
 
402
 
 
403
err:
 
404
        kfree(smbus_cmi);
 
405
        device->driver_data = NULL;
 
406
        return -EIO;
 
407
}
 
408
 
 
409
static int acpi_smbus_cmi_remove(struct acpi_device *device, int type)
 
410
{
 
411
        struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device);
 
412
 
 
413
        i2c_del_adapter(&smbus_cmi->adapter);
 
414
        kfree(smbus_cmi);
 
415
        device->driver_data = NULL;
 
416
 
 
417
        return 0;
 
418
}
 
419
 
 
420
static struct acpi_driver acpi_smbus_cmi_driver = {
 
421
        .name = ACPI_SMBUS_HC_DEVICE_NAME,
 
422
        .class = ACPI_SMBUS_HC_CLASS,
 
423
        .ids = acpi_smbus_cmi_ids,
 
424
        .ops = {
 
425
                .add = acpi_smbus_cmi_add,
 
426
                .remove = acpi_smbus_cmi_remove,
 
427
        },
 
428
};
 
429
 
 
430
static int __init acpi_smbus_cmi_init(void)
 
431
{
 
432
        return acpi_bus_register_driver(&acpi_smbus_cmi_driver);
 
433
}
 
434
 
 
435
static void __exit acpi_smbus_cmi_exit(void)
 
436
{
 
437
        acpi_bus_unregister_driver(&acpi_smbus_cmi_driver);
 
438
}
 
439
 
 
440
module_init(acpi_smbus_cmi_init);
 
441
module_exit(acpi_smbus_cmi_exit);
 
442
 
 
443
MODULE_LICENSE("GPL");
 
444
MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
 
445
MODULE_DESCRIPTION("ACPI SMBus CMI driver");