brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · d7af8e0 Raw
425 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * SMBus driver for ACPI SMBus CMI4 *5 * Copyright (C) 2009 Crane Cai <crane.cai@amd.com>6 */7 8#include <linux/module.h>9#include <linux/platform_device.h>10#include <linux/slab.h>11#include <linux/kernel.h>12#include <linux/stddef.h>13#include <linux/i2c.h>14#include <linux/acpi.h>15 16struct smbus_methods_t {17	char *mt_info;18	char *mt_sbr;19	char *mt_sbw;20};21 22struct acpi_smbus_cmi {23	acpi_handle handle;24	struct i2c_adapter adapter;25	u8 cap_info:1;26	u8 cap_read:1;27	u8 cap_write:1;28	const struct smbus_methods_t *methods;29};30 31static const struct smbus_methods_t smbus_methods = {32	.mt_info = "_SBI",33	.mt_sbr  = "_SBR",34	.mt_sbw  = "_SBW",35};36 37/* Some IBM BIOSes omit the leading underscore */38static const struct smbus_methods_t ibm_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", (kernel_ulong_t)&smbus_methods},46	{ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},47	{ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods},48	{"", 0}49};50MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);51 52#define ACPI_SMBUS_STATUS_OK			0x0053#define ACPI_SMBUS_STATUS_FAIL			0x0754#define ACPI_SMBUS_STATUS_DNAK			0x1055#define ACPI_SMBUS_STATUS_DERR			0x1156#define ACPI_SMBUS_STATUS_CMD_DENY		0x1257#define ACPI_SMBUS_STATUS_UNKNOWN		0x1358#define ACPI_SMBUS_STATUS_ACC_DENY		0x1759#define ACPI_SMBUS_STATUS_TIMEOUT		0x1860#define ACPI_SMBUS_STATUS_NOTSUP		0x1961#define ACPI_SMBUS_STATUS_BUSY			0x1a62#define ACPI_SMBUS_STATUS_PEC			0x1f63 64#define ACPI_SMBUS_PRTCL_WRITE			0x0065#define ACPI_SMBUS_PRTCL_READ			0x0166#define ACPI_SMBUS_PRTCL_QUICK			0x0267#define ACPI_SMBUS_PRTCL_BYTE			0x0468#define ACPI_SMBUS_PRTCL_BYTE_DATA		0x0669#define ACPI_SMBUS_PRTCL_WORD_DATA		0x0870#define ACPI_SMBUS_PRTCL_BLOCK_DATA		0x0a71 72 73static int74acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,75		   char read_write, u8 command, int size,76		   union i2c_smbus_data *data)77{78	int result = 0;79	struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;80	unsigned char protocol;81	acpi_status status = 0;82	struct acpi_object_list input;83	union acpi_object mt_params[5];84	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };85	union acpi_object *obj;86	union acpi_object *pkg;87	char *method;88	int len = 0;89 90	dev_dbg(&adap->dev, "access size: %d %s\n", size,91		(read_write) ? "READ" : "WRITE");92	switch (size) {93	case I2C_SMBUS_QUICK:94		protocol = ACPI_SMBUS_PRTCL_QUICK;95		command = 0;96		if (read_write == I2C_SMBUS_WRITE) {97			mt_params[3].type = ACPI_TYPE_INTEGER;98			mt_params[3].integer.value = 0;99			mt_params[4].type = ACPI_TYPE_INTEGER;100			mt_params[4].integer.value = 0;101		}102		break;103 104	case I2C_SMBUS_BYTE:105		protocol = ACPI_SMBUS_PRTCL_BYTE;106		if (read_write == I2C_SMBUS_WRITE) {107			mt_params[3].type = ACPI_TYPE_INTEGER;108			mt_params[3].integer.value = 0;109			mt_params[4].type = ACPI_TYPE_INTEGER;110			mt_params[4].integer.value = 0;111		} else {112			command = 0;113		}114		break;115 116	case I2C_SMBUS_BYTE_DATA:117		protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;118		if (read_write == I2C_SMBUS_WRITE) {119			mt_params[3].type = ACPI_TYPE_INTEGER;120			mt_params[3].integer.value = 1;121			mt_params[4].type = ACPI_TYPE_INTEGER;122			mt_params[4].integer.value = data->byte;123		}124		break;125 126	case I2C_SMBUS_WORD_DATA:127		protocol = ACPI_SMBUS_PRTCL_WORD_DATA;128		if (read_write == I2C_SMBUS_WRITE) {129			mt_params[3].type = ACPI_TYPE_INTEGER;130			mt_params[3].integer.value = 2;131			mt_params[4].type = ACPI_TYPE_INTEGER;132			mt_params[4].integer.value = data->word;133		}134		break;135 136	case I2C_SMBUS_BLOCK_DATA:137		protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;138		if (read_write == I2C_SMBUS_WRITE) {139			len = data->block[0];140			if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)141				return -EINVAL;142			mt_params[3].type = ACPI_TYPE_INTEGER;143			mt_params[3].integer.value = len;144			mt_params[4].type = ACPI_TYPE_BUFFER;145			mt_params[4].buffer.length = len;146			mt_params[4].buffer.pointer = data->block + 1;147		}148		break;149 150	default:151		dev_warn(&adap->dev, "Unsupported transaction %d\n", size);152		return -EOPNOTSUPP;153	}154 155	if (read_write == I2C_SMBUS_READ) {156		protocol |= ACPI_SMBUS_PRTCL_READ;157		method = smbus_cmi->methods->mt_sbr;158		input.count = 3;159	} else {160		protocol |= ACPI_SMBUS_PRTCL_WRITE;161		method = smbus_cmi->methods->mt_sbw;162		input.count = 5;163	}164 165	input.pointer = mt_params;166	mt_params[0].type = ACPI_TYPE_INTEGER;167	mt_params[0].integer.value = protocol;168	mt_params[1].type = ACPI_TYPE_INTEGER;169	mt_params[1].integer.value = addr;170	mt_params[2].type = ACPI_TYPE_INTEGER;171	mt_params[2].integer.value = command;172 173	status = acpi_evaluate_object(smbus_cmi->handle, method, &input,174				      &buffer);175	if (ACPI_FAILURE(status)) {176		acpi_handle_err(smbus_cmi->handle,177				"Failed to evaluate %s: %i\n", method, status);178		return -EIO;179	}180 181	pkg = buffer.pointer;182	if (pkg && pkg->type == ACPI_TYPE_PACKAGE)183		obj = pkg->package.elements;184	else {185		acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");186		result = -EIO;187		goto out;188	}189	if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {190		acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");191		result = -EIO;192		goto out;193	}194 195	result = obj->integer.value;196	acpi_handle_debug(smbus_cmi->handle,  "%s return status: %i\n", method,197			  result);198 199	switch (result) {200	case ACPI_SMBUS_STATUS_OK:201		result = 0;202		break;203	case ACPI_SMBUS_STATUS_BUSY:204		result = -EBUSY;205		goto out;206	case ACPI_SMBUS_STATUS_TIMEOUT:207		result = -ETIMEDOUT;208		goto out;209	case ACPI_SMBUS_STATUS_DNAK:210		result = -ENXIO;211		goto out;212	default:213		result = -EIO;214		goto out;215	}216 217	if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK)218		goto out;219 220	obj = pkg->package.elements + 1;221	if (obj->type != ACPI_TYPE_INTEGER) {222		acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");223		result = -EIO;224		goto out;225	}226 227	len = obj->integer.value;228	obj = pkg->package.elements + 2;229	switch (size) {230	case I2C_SMBUS_BYTE:231	case I2C_SMBUS_BYTE_DATA:232	case I2C_SMBUS_WORD_DATA:233		if (obj->type != ACPI_TYPE_INTEGER) {234			acpi_handle_err(smbus_cmi->handle,235					"Invalid argument type\n");236			result = -EIO;237			goto out;238		}239		if (len == 2)240			data->word = obj->integer.value;241		else242			data->byte = obj->integer.value;243		break;244	case I2C_SMBUS_BLOCK_DATA:245		if (obj->type != ACPI_TYPE_BUFFER) {246			acpi_handle_err(smbus_cmi->handle,247					"Invalid argument type\n");248			result = -EIO;249			goto out;250		}251		if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)252			return -EPROTO;253		data->block[0] = len;254		memcpy(data->block + 1, obj->buffer.pointer, len);255		break;256	}257 258out:259	kfree(buffer.pointer);260	dev_dbg(&adap->dev, "Transaction status: %i\n", result);261	return result;262}263 264static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)265{266	struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;267	u32 ret;268 269	ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?270		I2C_FUNC_SMBUS_QUICK : 0;271 272	ret |= smbus_cmi->cap_read ?273		(I2C_FUNC_SMBUS_READ_BYTE |274		I2C_FUNC_SMBUS_READ_BYTE_DATA |275		I2C_FUNC_SMBUS_READ_WORD_DATA |276		I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;277 278	ret |= smbus_cmi->cap_write ?279		(I2C_FUNC_SMBUS_WRITE_BYTE |280		I2C_FUNC_SMBUS_WRITE_BYTE_DATA |281		I2C_FUNC_SMBUS_WRITE_WORD_DATA |282		I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;283 284	return ret;285}286 287static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {288	.smbus_xfer = acpi_smbus_cmi_access,289	.functionality = acpi_smbus_cmi_func,290};291 292 293static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,294				  const char *name)295{296	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };297	struct acpi_handle *handle = smbus_cmi->handle;298	union acpi_object *obj;299	acpi_status status;300 301	if (!strcmp(name, smbus_cmi->methods->mt_info)) {302		status = acpi_evaluate_object(smbus_cmi->handle,303					smbus_cmi->methods->mt_info,304					NULL, &buffer);305		if (ACPI_FAILURE(status)) {306			acpi_handle_err(handle, "Failed to evaluate %s: %i\n",307					smbus_cmi->methods->mt_info, status);308			return -EIO;309		}310 311		obj = buffer.pointer;312		if (obj && obj->type == ACPI_TYPE_PACKAGE)313			obj = obj->package.elements;314		else {315			acpi_handle_err(handle, "Invalid argument type\n");316			kfree(buffer.pointer);317			return -EIO;318		}319 320		if (obj->type != ACPI_TYPE_INTEGER) {321			acpi_handle_err(handle, "Invalid argument type\n");322			kfree(buffer.pointer);323			return -EIO;324		} else325			acpi_handle_debug(handle, "SMBus CMI Version %x\n",326					  (int)obj->integer.value);327 328		kfree(buffer.pointer);329		smbus_cmi->cap_info = 1;330	} else if (!strcmp(name, smbus_cmi->methods->mt_sbr))331		smbus_cmi->cap_read = 1;332	else if (!strcmp(name, smbus_cmi->methods->mt_sbw))333		smbus_cmi->cap_write = 1;334	else335		acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name);336 337	return 0;338}339 340static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,341			void *context, void **return_value)342{343	char node_name[5];344	struct acpi_buffer buffer = { sizeof(node_name), node_name };345	struct acpi_smbus_cmi *smbus_cmi = context;346	acpi_status status;347 348	status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);349 350	if (ACPI_SUCCESS(status))351		acpi_smbus_cmi_add_cap(smbus_cmi, node_name);352 353	return AE_OK;354}355 356static int smbus_cmi_probe(struct platform_device *device)357{358	struct device *dev = &device->dev;359	struct acpi_smbus_cmi *smbus_cmi;360	int ret;361 362	smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);363	if (!smbus_cmi)364		return -ENOMEM;365 366	smbus_cmi->handle = ACPI_HANDLE(dev);367	smbus_cmi->methods = device_get_match_data(dev);368 369	platform_set_drvdata(device, smbus_cmi);370 371	smbus_cmi->cap_info = 0;372	smbus_cmi->cap_read = 0;373	smbus_cmi->cap_write = 0;374 375	acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,376			    acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);377 378	if (smbus_cmi->cap_info == 0) {379		ret = -ENODEV;380		goto err;381	}382 383	snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),384		 "SMBus CMI adapter %s", dev_name(dev));385	smbus_cmi->adapter.owner = THIS_MODULE;386	smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;387	smbus_cmi->adapter.algo_data = smbus_cmi;388	smbus_cmi->adapter.class = I2C_CLASS_HWMON;389	smbus_cmi->adapter.dev.parent = &device->dev;390 391	ret = i2c_add_adapter(&smbus_cmi->adapter);392	if (ret) {393		dev_err(&device->dev, "Couldn't register adapter!\n");394		goto err;395	}396 397	return 0;398 399err:400	kfree(smbus_cmi);401	return ret;402}403 404static void smbus_cmi_remove(struct platform_device *device)405{406	struct acpi_smbus_cmi *smbus_cmi = platform_get_drvdata(device);407 408	i2c_del_adapter(&smbus_cmi->adapter);409	kfree(smbus_cmi);410}411 412static struct platform_driver smbus_cmi_driver = {413	.probe = smbus_cmi_probe,414	.remove_new = smbus_cmi_remove,415	.driver = {416		.name   = "smbus_cmi",417		.acpi_match_table = acpi_smbus_cmi_ids,418	},419};420module_platform_driver(smbus_cmi_driver);421 422MODULE_LICENSE("GPL");423MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");424MODULE_DESCRIPTION("ACPI SMBus CMI driver");425