aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorCrane Cai <crane.cai@amd.com>2009-09-18 16:45:51 -0400
committerJean Delvare <khali@linux-fr.org>2009-09-18 16:45:51 -0400
commitdc9854212e0d7318d7133697906d98b78f3088b6 (patch)
treed4a7025f655842e1f7af14aac15af63d28e1ea2b /drivers/i2c
parent449d2c759ddba46a89b698bdc64bfc2f7cc5bb66 (diff)
i2c: Add driver for SMBus Control Method Interface
This driver supports the SMBus Control Method Interface. It needs BIOS declare ACPI control methods which described in SMBus Control Method Interface Spec. http://smbus.org/specs/smbus_cmi10.pdf Signed-off-by: Crane Cai <crane.cai@amd.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig11
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-scmi.c430
3 files changed, 444 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bb216c5fe30e..838bd1e4efb4 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -762,4 +762,15 @@ config SCx200_ACB
762 This support is also available as a module. If so, the module 762 This support is also available as a module. If so, the module
763 will be called scx200_acb. 763 will be called scx200_acb.
764 764
765config I2C_SCMI
766 tristate "SMBus Control Method Interface"
767 depends on ACPI
768 help
769 This driver supports the SMBus Control Method Interface. It needs the
770 BIOS to declare ACPI control methods as described in the SMBus Control
771 Method Interface specification.
772
773 To compile this driver as a module, choose M here:
774 the module will be called i2c-scmi.
775
765endmenu 776endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index e654263bfc01..060da43cf7db 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -2,6 +2,9 @@
2# Makefile for the i2c bus drivers. 2# Makefile for the i2c bus drivers.
3# 3#
4 4
5# SMBus CMI driver
6obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
7
5# PC SMBus host controller drivers 8# PC SMBus host controller drivers
6obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o 9obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
7obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o 10obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
new file mode 100644
index 000000000000..276a046ac93f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -0,0 +1,430 @@
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
22ACPI_MODULE_NAME("smbus_cmi");
23
24struct smbus_methods_t {
25 char *mt_info;
26 char *mt_sbr;
27 char *mt_sbw;
28};
29
30struct 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};
37
38static const struct smbus_methods_t smbus_methods = {
39 .mt_info = "_SBI",
40 .mt_sbr = "_SBR",
41 .mt_sbw = "_SBW",
42};
43
44static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
45 {"SMBUS01", 0},
46 {"", 0}
47};
48
49#define ACPI_SMBUS_STATUS_OK 0x00
50#define ACPI_SMBUS_STATUS_FAIL 0x07
51#define ACPI_SMBUS_STATUS_DNAK 0x10
52#define ACPI_SMBUS_STATUS_DERR 0x11
53#define ACPI_SMBUS_STATUS_CMD_DENY 0x12
54#define ACPI_SMBUS_STATUS_UNKNOWN 0x13
55#define ACPI_SMBUS_STATUS_ACC_DENY 0x17
56#define ACPI_SMBUS_STATUS_TIMEOUT 0x18
57#define ACPI_SMBUS_STATUS_NOTSUP 0x19
58#define ACPI_SMBUS_STATUS_BUSY 0x1a
59#define ACPI_SMBUS_STATUS_PEC 0x1f
60
61#define ACPI_SMBUS_PRTCL_WRITE 0x00
62#define ACPI_SMBUS_PRTCL_READ 0x01
63#define ACPI_SMBUS_PRTCL_QUICK 0x02
64#define ACPI_SMBUS_PRTCL_BYTE 0x04
65#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06
66#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08
67#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a
68
69
70static int
71acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
72 char read_write, u8 command, int size,
73 union i2c_smbus_data *data)
74{
75 int result = 0;
76 struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;
77 unsigned char protocol;
78 acpi_status status = 0;
79 struct acpi_object_list input;
80 union acpi_object mt_params[5];
81 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
82 union acpi_object *obj;
83 union acpi_object *pkg;
84 char *method;
85 int len = 0;
86
87 dev_dbg(&adap->dev, "access size: %d %s\n", size,
88 (read_write) ? "READ" : "WRITE");
89 switch (size) {
90 case I2C_SMBUS_QUICK:
91 protocol = ACPI_SMBUS_PRTCL_QUICK;
92 command = 0;
93 if (read_write == I2C_SMBUS_WRITE) {
94 mt_params[3].type = ACPI_TYPE_INTEGER;
95 mt_params[3].integer.value = 0;
96 mt_params[4].type = ACPI_TYPE_INTEGER;
97 mt_params[4].integer.value = 0;
98 }
99 break;
100
101 case I2C_SMBUS_BYTE:
102 protocol = ACPI_SMBUS_PRTCL_BYTE;
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 } else {
109 command = 0;
110 }
111 break;
112
113 case I2C_SMBUS_BYTE_DATA:
114 protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;
115 if (read_write == I2C_SMBUS_WRITE) {
116 mt_params[3].type = ACPI_TYPE_INTEGER;
117 mt_params[3].integer.value = 1;
118 mt_params[4].type = ACPI_TYPE_INTEGER;
119 mt_params[4].integer.value = data->byte;
120 }
121 break;
122
123 case I2C_SMBUS_WORD_DATA:
124 protocol = ACPI_SMBUS_PRTCL_WORD_DATA;
125 if (read_write == I2C_SMBUS_WRITE) {
126 mt_params[3].type = ACPI_TYPE_INTEGER;
127 mt_params[3].integer.value = 2;
128 mt_params[4].type = ACPI_TYPE_INTEGER;
129 mt_params[4].integer.value = data->word;
130 }
131 break;
132
133 case I2C_SMBUS_BLOCK_DATA:
134 protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;
135 if (read_write == I2C_SMBUS_WRITE) {
136 len = data->block[0];
137 if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
138 return -EINVAL;
139 mt_params[3].type = ACPI_TYPE_INTEGER;
140 mt_params[3].integer.value = len;
141 mt_params[4].type = ACPI_TYPE_BUFFER;
142 mt_params[4].buffer.pointer = data->block + 1;
143 }
144 break;
145
146 default:
147 dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
148 return -EOPNOTSUPP;
149 }
150
151 if (read_write == I2C_SMBUS_READ) {
152 protocol |= ACPI_SMBUS_PRTCL_READ;
153 method = smbus_methods.mt_sbr;
154 input.count = 3;
155 } else {
156 protocol |= ACPI_SMBUS_PRTCL_WRITE;
157 method = smbus_methods.mt_sbw;
158 input.count = 5;
159 }
160
161 input.pointer = mt_params;
162 mt_params[0].type = ACPI_TYPE_INTEGER;
163 mt_params[0].integer.value = protocol;
164 mt_params[1].type = ACPI_TYPE_INTEGER;
165 mt_params[1].integer.value = addr;
166 mt_params[2].type = ACPI_TYPE_INTEGER;
167 mt_params[2].integer.value = command;
168
169 status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
170 &buffer);
171 if (ACPI_FAILURE(status)) {
172 ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
173 return -EIO;
174 }
175
176 pkg = buffer.pointer;
177 if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
178 obj = pkg->package.elements;
179 else {
180 ACPI_ERROR((AE_INFO, "Invalid argument type"));
181 result = -EIO;
182 goto out;
183 }
184 if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
185 ACPI_ERROR((AE_INFO, "Invalid argument type"));
186 result = -EIO;
187 goto out;
188 }
189
190 result = obj->integer.value;
191 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
192 method, result));
193
194 switch (result) {
195 case ACPI_SMBUS_STATUS_OK:
196 result = 0;
197 break;
198 case ACPI_SMBUS_STATUS_BUSY:
199 result = -EBUSY;
200 goto out;
201 case ACPI_SMBUS_STATUS_TIMEOUT:
202 result = -ETIMEDOUT;
203 goto out;
204 case ACPI_SMBUS_STATUS_DNAK:
205 result = -ENXIO;
206 goto out;
207 default:
208 result = -EIO;
209 goto out;
210 }
211
212 if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK)
213 goto out;
214
215 obj = pkg->package.elements + 1;
216 if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
217 ACPI_ERROR((AE_INFO, "Invalid argument type"));
218 result = -EIO;
219 goto out;
220 }
221
222 len = obj->integer.value;
223 obj = pkg->package.elements + 2;
224 switch (size) {
225 case I2C_SMBUS_BYTE:
226 case I2C_SMBUS_BYTE_DATA:
227 case I2C_SMBUS_WORD_DATA:
228 if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
229 ACPI_ERROR((AE_INFO, "Invalid argument type"));
230 result = -EIO;
231 goto out;
232 }
233 if (len == 2)
234 data->word = obj->integer.value;
235 else
236 data->byte = obj->integer.value;
237 break;
238 case I2C_SMBUS_BLOCK_DATA:
239 if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) {
240 ACPI_ERROR((AE_INFO, "Invalid argument type"));
241 result = -EIO;
242 goto out;
243 }
244 if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
245 return -EPROTO;
246 data->block[0] = len;
247 memcpy(data->block + 1, obj->buffer.pointer, len);
248 break;
249 }
250
251out:
252 kfree(buffer.pointer);
253 dev_dbg(&adap->dev, "Transaction status: %i\n", result);
254 return result;
255}
256
257static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)
258{
259 struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;
260 u32 ret;
261
262 ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?
263 I2C_FUNC_SMBUS_QUICK : 0;
264
265 ret |= smbus_cmi->cap_read ?
266 (I2C_FUNC_SMBUS_READ_BYTE |
267 I2C_FUNC_SMBUS_READ_BYTE_DATA |
268 I2C_FUNC_SMBUS_READ_WORD_DATA |
269 I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;
270
271 ret |= smbus_cmi->cap_write ?
272 (I2C_FUNC_SMBUS_WRITE_BYTE |
273 I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
274 I2C_FUNC_SMBUS_WRITE_WORD_DATA |
275 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;
276
277 return ret;
278}
279
280static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
281 .smbus_xfer = acpi_smbus_cmi_access,
282 .functionality = acpi_smbus_cmi_func,
283};
284
285
286static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
287 const char *name)
288{
289 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
290 union acpi_object *obj;
291 acpi_status status;
292
293 if (!strcmp(name, smbus_methods.mt_info)) {
294 status = acpi_evaluate_object(smbus_cmi->handle,
295 smbus_methods.mt_info,
296 NULL, &buffer);
297 if (ACPI_FAILURE(status)) {
298 ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
299 smbus_methods.mt_info, status));
300 return -EIO;
301 }
302
303 obj = buffer.pointer;
304 if (obj && obj->type == ACPI_TYPE_PACKAGE)
305 obj = obj->package.elements;
306 else {
307 ACPI_ERROR((AE_INFO, "Invalid argument type"));
308 kfree(buffer.pointer);
309 return -EIO;
310 }
311
312 if (obj->type != ACPI_TYPE_INTEGER) {
313 ACPI_ERROR((AE_INFO, "Invalid argument type"));
314 kfree(buffer.pointer);
315 return -EIO;
316 } else
317 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
318 "\n", (int)obj->integer.value));
319
320 kfree(buffer.pointer);
321 smbus_cmi->cap_info = 1;
322 } else if (!strcmp(name, smbus_methods.mt_sbr))
323 smbus_cmi->cap_read = 1;
324 else if (!strcmp(name, smbus_methods.mt_sbw))
325 smbus_cmi->cap_write = 1;
326 else
327 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
328 name));
329
330 return 0;
331}
332
333static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
334 void *context, void **return_value)
335{
336 char node_name[5];
337 struct acpi_buffer buffer = { sizeof(node_name), node_name };
338 struct acpi_smbus_cmi *smbus_cmi = context;
339 acpi_status status;
340
341 status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
342
343 if (ACPI_SUCCESS(status))
344 acpi_smbus_cmi_add_cap(smbus_cmi, node_name);
345
346 return AE_OK;
347}
348
349static int acpi_smbus_cmi_add(struct acpi_device *device)
350{
351 struct acpi_smbus_cmi *smbus_cmi;
352
353 smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
354 if (!smbus_cmi)
355 return -ENOMEM;
356
357 smbus_cmi->handle = device->handle;
358 strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
359 strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
360 device->driver_data = smbus_cmi;
361 smbus_cmi->cap_info = 0;
362 smbus_cmi->cap_read = 0;
363 smbus_cmi->cap_write = 0;
364
365 acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
366 acpi_smbus_cmi_query_methods, smbus_cmi, NULL);
367
368 if (smbus_cmi->cap_info == 0)
369 goto err;
370
371 snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
372 "SMBus CMI adapter %s (%s)",
373 acpi_device_name(device),
374 acpi_device_uid(device));
375 smbus_cmi->adapter.owner = THIS_MODULE;
376 smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
377 smbus_cmi->adapter.algo_data = smbus_cmi;
378 smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
379 smbus_cmi->adapter.dev.parent = &device->dev;
380
381 if (i2c_add_adapter(&smbus_cmi->adapter)) {
382 dev_err(&device->dev, "Couldn't register adapter!\n");
383 goto err;
384 }
385
386 return 0;
387
388err:
389 kfree(smbus_cmi);
390 device->driver_data = NULL;
391 return -EIO;
392}
393
394static int acpi_smbus_cmi_remove(struct acpi_device *device, int type)
395{
396 struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device);
397
398 i2c_del_adapter(&smbus_cmi->adapter);
399 kfree(smbus_cmi);
400 device->driver_data = NULL;
401
402 return 0;
403}
404
405static struct acpi_driver acpi_smbus_cmi_driver = {
406 .name = ACPI_SMBUS_HC_DEVICE_NAME,
407 .class = ACPI_SMBUS_HC_CLASS,
408 .ids = acpi_smbus_cmi_ids,
409 .ops = {
410 .add = acpi_smbus_cmi_add,
411 .remove = acpi_smbus_cmi_remove,
412 },
413};
414
415static int __init acpi_smbus_cmi_init(void)
416{
417 return acpi_bus_register_driver(&acpi_smbus_cmi_driver);
418}
419
420static void __exit acpi_smbus_cmi_exit(void)
421{
422 acpi_bus_unregister_driver(&acpi_smbus_cmi_driver);
423}
424
425module_init(acpi_smbus_cmi_init);
426module_exit(acpi_smbus_cmi_exit);
427
428MODULE_LICENSE("GPL");
429MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
430MODULE_DESCRIPTION("ACPI SMBus CMI driver");