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