aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/i2c/Makefile5
-rw-r--r--drivers/i2c/i2c-acpi.c273
-rw-r--r--drivers/i2c/i2c-core.c2
-rw-r--r--include/linux/acpi.h11
-rw-r--r--include/linux/i2c.h10
5 files changed, 300 insertions, 1 deletions
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 1722f50f2473..80db3073aa84 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -2,8 +2,11 @@
2# Makefile for the i2c core. 2# Makefile for the i2c core.
3# 3#
4 4
5i2ccore-y := i2c-core.o
6i2ccore-$(CONFIG_ACPI) += i2c-acpi.o
7
5obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o 8obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
6obj-$(CONFIG_I2C) += i2c-core.o 9obj-$(CONFIG_I2C) += i2ccore.o
7obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o 10obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
8obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o 11obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
9obj-$(CONFIG_I2C_MUX) += i2c-mux.o 12obj-$(CONFIG_I2C_MUX) += i2c-mux.o
diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c
new file mode 100644
index 000000000000..f7f4c89c09b3
--- /dev/null
+++ b/drivers/i2c/i2c-acpi.c
@@ -0,0 +1,273 @@
1/*
2 * I2C ACPI code
3 *
4 * Copyright (C) 2014 Intel Corp
5 *
6 * Author: Lan Tianyu <tianyu.lan@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17#define pr_fmt(fmt) "I2C/ACPI : " fmt
18
19#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/err.h>
22#include <linux/i2c.h>
23#include <linux/acpi.h>
24
25struct acpi_i2c_handler_data {
26 struct acpi_connection_info info;
27 struct i2c_adapter *adapter;
28};
29
30struct gsb_buffer {
31 u8 status;
32 u8 len;
33 union {
34 u16 wdata;
35 u8 bdata;
36 u8 data[0];
37 };
38} __packed;
39
40static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
41 u8 cmd, u8 *data, u8 data_len)
42{
43
44 struct i2c_msg msgs[2];
45 int ret;
46 u8 *buffer;
47
48 buffer = kzalloc(data_len, GFP_KERNEL);
49 if (!buffer)
50 return AE_NO_MEMORY;
51
52 msgs[0].addr = client->addr;
53 msgs[0].flags = client->flags;
54 msgs[0].len = 1;
55 msgs[0].buf = &cmd;
56
57 msgs[1].addr = client->addr;
58 msgs[1].flags = client->flags | I2C_M_RD;
59 msgs[1].len = data_len;
60 msgs[1].buf = buffer;
61
62 ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
63 if (ret < 0)
64 dev_err(&client->adapter->dev, "i2c read failed\n");
65 else
66 memcpy(data, buffer, data_len);
67
68 kfree(buffer);
69 return ret;
70}
71
72static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
73 u8 cmd, u8 *data, u8 data_len)
74{
75
76 struct i2c_msg msgs[1];
77 u8 *buffer;
78 int ret = AE_OK;
79
80 buffer = kzalloc(data_len + 1, GFP_KERNEL);
81 if (!buffer)
82 return AE_NO_MEMORY;
83
84 buffer[0] = cmd;
85 memcpy(buffer + 1, data, data_len);
86
87 msgs[0].addr = client->addr;
88 msgs[0].flags = client->flags;
89 msgs[0].len = data_len + 1;
90 msgs[0].buf = buffer;
91
92 ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
93 if (ret < 0)
94 dev_err(&client->adapter->dev, "i2c write failed\n");
95
96 kfree(buffer);
97 return ret;
98}
99
100static acpi_status
101acpi_i2c_space_handler(u32 function, acpi_physical_address command,
102 u32 bits, u64 *value64,
103 void *handler_context, void *region_context)
104{
105 struct gsb_buffer *gsb = (struct gsb_buffer *)value64;
106 struct acpi_i2c_handler_data *data = handler_context;
107 struct acpi_connection_info *info = &data->info;
108 struct acpi_resource_i2c_serialbus *sb;
109 struct i2c_adapter *adapter = data->adapter;
110 struct i2c_client client;
111 struct acpi_resource *ares;
112 u32 accessor_type = function >> 16;
113 u8 action = function & ACPI_IO_MASK;
114 acpi_status ret = AE_OK;
115 int status;
116
117 ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
118 if (ACPI_FAILURE(ret))
119 return ret;
120
121 if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
122 ret = AE_BAD_PARAMETER;
123 goto err;
124 }
125
126 sb = &ares->data.i2c_serial_bus;
127 if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
128 ret = AE_BAD_PARAMETER;
129 goto err;
130 }
131
132 memset(&client, 0, sizeof(client));
133 client.adapter = adapter;
134 client.addr = sb->slave_address;
135 client.flags = 0;
136
137 if (sb->access_mode == ACPI_I2C_10BIT_MODE)
138 client.flags |= I2C_CLIENT_TEN;
139
140 switch (accessor_type) {
141 case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV:
142 if (action == ACPI_READ) {
143 status = i2c_smbus_read_byte(&client);
144 if (status >= 0) {
145 gsb->bdata = status;
146 status = 0;
147 }
148 } else {
149 status = i2c_smbus_write_byte(&client, gsb->bdata);
150 }
151 break;
152
153 case ACPI_GSB_ACCESS_ATTRIB_BYTE:
154 if (action == ACPI_READ) {
155 status = i2c_smbus_read_byte_data(&client, command);
156 if (status >= 0) {
157 gsb->bdata = status;
158 status = 0;
159 }
160 } else {
161 status = i2c_smbus_write_byte_data(&client, command,
162 gsb->bdata);
163 }
164 break;
165
166 case ACPI_GSB_ACCESS_ATTRIB_WORD:
167 if (action == ACPI_READ) {
168 status = i2c_smbus_read_word_data(&client, command);
169 if (status >= 0) {
170 gsb->wdata = status;
171 status = 0;
172 }
173 } else {
174 status = i2c_smbus_write_word_data(&client, command,
175 gsb->wdata);
176 }
177 break;
178
179 case ACPI_GSB_ACCESS_ATTRIB_BLOCK:
180 if (action == ACPI_READ) {
181 status = i2c_smbus_read_block_data(&client, command,
182 gsb->data);
183 if (status >= 0) {
184 gsb->len = status;
185 status = 0;
186 }
187 } else {
188 status = i2c_smbus_write_block_data(&client, command,
189 gsb->len, gsb->data);
190 }
191 break;
192
193 case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE:
194 if (action == ACPI_READ) {
195 status = acpi_gsb_i2c_read_bytes(&client, command,
196 gsb->data, info->access_length);
197 if (status > 0)
198 status = 0;
199 } else {
200 status = acpi_gsb_i2c_write_bytes(&client, command,
201 gsb->data, info->access_length);
202 }
203 break;
204
205 default:
206 pr_info("protocol(0x%02x) is not supported.\n", accessor_type);
207 ret = AE_BAD_PARAMETER;
208 goto err;
209 }
210
211 gsb->status = status;
212
213 err:
214 ACPI_FREE(ares);
215 return ret;
216}
217
218
219int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
220{
221 acpi_handle handle = ACPI_HANDLE(adapter->dev.parent);
222 struct acpi_i2c_handler_data *data;
223 acpi_status status;
224
225 if (!handle)
226 return -ENODEV;
227
228 data = kzalloc(sizeof(struct acpi_i2c_handler_data),
229 GFP_KERNEL);
230 if (!data)
231 return -ENOMEM;
232
233 data->adapter = adapter;
234 status = acpi_bus_attach_private_data(handle, (void *)data);
235 if (ACPI_FAILURE(status)) {
236 kfree(data);
237 return -ENOMEM;
238 }
239
240 status = acpi_install_address_space_handler(handle,
241 ACPI_ADR_SPACE_GSBUS,
242 &acpi_i2c_space_handler,
243 NULL,
244 data);
245 if (ACPI_FAILURE(status)) {
246 dev_err(&adapter->dev, "Error installing i2c space handler\n");
247 acpi_bus_detach_private_data(handle);
248 kfree(data);
249 return -ENOMEM;
250 }
251
252 return 0;
253}
254
255void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter)
256{
257 acpi_handle handle = ACPI_HANDLE(adapter->dev.parent);
258 struct acpi_i2c_handler_data *data;
259 acpi_status status;
260
261 if (!handle)
262 return;
263
264 acpi_remove_address_space_handler(handle,
265 ACPI_ADR_SPACE_GSBUS,
266 &acpi_i2c_space_handler);
267
268 status = acpi_bus_get_private_data(handle, (void **)&data);
269 if (ACPI_SUCCESS(status))
270 kfree(data);
271
272 acpi_bus_detach_private_data(handle);
273}
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 7c7f4b856bad..e25cb84cb297 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1293,6 +1293,7 @@ exit_recovery:
1293 /* create pre-declared device nodes */ 1293 /* create pre-declared device nodes */
1294 of_i2c_register_devices(adap); 1294 of_i2c_register_devices(adap);
1295 acpi_i2c_register_devices(adap); 1295 acpi_i2c_register_devices(adap);
1296 acpi_i2c_install_space_handler(adap);
1296 1297
1297 if (adap->nr < __i2c_first_dynamic_bus_num) 1298 if (adap->nr < __i2c_first_dynamic_bus_num)
1298 i2c_scan_static_board_info(adap); 1299 i2c_scan_static_board_info(adap);
@@ -1466,6 +1467,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)
1466 return; 1467 return;
1467 } 1468 }
1468 1469
1470 acpi_i2c_remove_space_handler(adap);
1469 /* Tell drivers about this removal */ 1471 /* Tell drivers about this removal */
1470 mutex_lock(&core_lock); 1472 mutex_lock(&core_lock);
1471 bus_for_each_drv(&i2c_bus_type, NULL, adap, 1473 bus_for_each_drv(&i2c_bus_type, NULL, adap,
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 358c01b971db..40718e91e171 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -364,6 +364,17 @@ extern bool osc_sb_apei_support_acked;
364#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 364#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010
365#define OSC_PCI_CONTROL_MASKS 0x0000001f 365#define OSC_PCI_CONTROL_MASKS 0x0000001f
366 366
367#define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002
368#define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004
369#define ACPI_GSB_ACCESS_ATTRIB_BYTE 0x00000006
370#define ACPI_GSB_ACCESS_ATTRIB_WORD 0x00000008
371#define ACPI_GSB_ACCESS_ATTRIB_BLOCK 0x0000000A
372#define ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE 0x0000000B
373#define ACPI_GSB_ACCESS_ATTRIB_WORD_CALL 0x0000000C
374#define ACPI_GSB_ACCESS_ATTRIB_BLOCK_CALL 0x0000000D
375#define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E
376#define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F
377
367extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, 378extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
368 u32 *mask, u32 req); 379 u32 *mask, u32 req);
369 380
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index b556e0ab946f..f7a939a2cb56 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -577,4 +577,14 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node
577} 577}
578#endif /* CONFIG_OF */ 578#endif /* CONFIG_OF */
579 579
580#ifdef CONFIG_ACPI
581int acpi_i2c_install_space_handler(struct i2c_adapter *adapter);
582void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter);
583#else
584static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter)
585{ }
586static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
587{ return 0; }
588#endif
589
580#endif /* _LINUX_I2C_H */ 590#endif /* _LINUX_I2C_H */