diff options
-rw-r--r-- | drivers/i2c/Makefile | 5 | ||||
-rw-r--r-- | drivers/i2c/i2c-acpi.c | 273 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.c | 2 | ||||
-rw-r--r-- | include/linux/acpi.h | 11 | ||||
-rw-r--r-- | include/linux/i2c.h | 10 |
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 | ||
5 | i2ccore-y := i2c-core.o | ||
6 | i2ccore-$(CONFIG_ACPI) += i2c-acpi.o | ||
7 | |||
5 | obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o | 8 | obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o |
6 | obj-$(CONFIG_I2C) += i2c-core.o | 9 | obj-$(CONFIG_I2C) += i2ccore.o |
7 | obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o | 10 | obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o |
8 | obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o | 11 | obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o |
9 | obj-$(CONFIG_I2C_MUX) += i2c-mux.o | 12 | obj-$(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 | |||
25 | struct acpi_i2c_handler_data { | ||
26 | struct acpi_connection_info info; | ||
27 | struct i2c_adapter *adapter; | ||
28 | }; | ||
29 | |||
30 | struct gsb_buffer { | ||
31 | u8 status; | ||
32 | u8 len; | ||
33 | union { | ||
34 | u16 wdata; | ||
35 | u8 bdata; | ||
36 | u8 data[0]; | ||
37 | }; | ||
38 | } __packed; | ||
39 | |||
40 | static 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 | |||
72 | static 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 | |||
100 | static acpi_status | ||
101 | acpi_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 | |||
219 | int 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 | |||
255 | void 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 | |||
367 | extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, | 378 | extern 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 | ||
581 | int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); | ||
582 | void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); | ||
583 | #else | ||
584 | static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) | ||
585 | { } | ||
586 | static 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 */ |