• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/i2c/busses/
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	struct smbus_methods_t *methods;
37};
38
39static 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 */
46static const struct smbus_methods_t ibm_smbus_methods = {
47	.mt_info = "SBI_",
48	.mt_sbr  = "SBR_",
49	.mt_sbw  = "SBW_",
50};
51
52static 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};
57MODULE_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
80static int
81acpi_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
261out:
262	kfree(buffer.pointer);
263	dev_dbg(&adap->dev, "Transaction status: %i\n", result);
264	return result;
265}
266
267static 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
290static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
291	.smbus_xfer = acpi_smbus_cmi_access,
292	.functionality = acpi_smbus_cmi_func,
293};
294
295
296static 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
343static 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
359static 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
403err:
404	kfree(smbus_cmi);
405	device->driver_data = NULL;
406	return -EIO;
407}
408
409static 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
420static 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
430static int __init acpi_smbus_cmi_init(void)
431{
432	return acpi_bus_register_driver(&acpi_smbus_cmi_driver);
433}
434
435static void __exit acpi_smbus_cmi_exit(void)
436{
437	acpi_bus_unregister_driver(&acpi_smbus_cmi_driver);
438}
439
440module_init(acpi_smbus_cmi_init);
441module_exit(acpi_smbus_cmi_exit);
442
443MODULE_LICENSE("GPL");
444MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
445MODULE_DESCRIPTION("ACPI SMBus CMI driver");
446