xref: /openbmc/linux/drivers/acpi/sbshc.c (revision e8e0929d)
1 /*
2  * SMBus driver for ACPI Embedded Controller (v0.1)
3  *
4  * Copyright (c) 2007 Alexey Starikovskiy
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 <acpi/acpi_bus.h>
12 #include <acpi/acpi_drivers.h>
13 #include <linux/wait.h>
14 #include <linux/delay.h>
15 #include <linux/interrupt.h>
16 #include "sbshc.h"
17 
18 #define PREFIX "ACPI: "
19 
20 #define ACPI_SMB_HC_CLASS	"smbus_host_controller"
21 #define ACPI_SMB_HC_DEVICE_NAME	"ACPI SMBus HC"
22 
23 struct acpi_smb_hc {
24 	struct acpi_ec *ec;
25 	struct mutex lock;
26 	wait_queue_head_t wait;
27 	u8 offset;
28 	u8 query_bit;
29 	smbus_alarm_callback callback;
30 	void *context;
31 };
32 
33 static int acpi_smbus_hc_add(struct acpi_device *device);
34 static int acpi_smbus_hc_remove(struct acpi_device *device, int type);
35 
36 static const struct acpi_device_id sbs_device_ids[] = {
37 	{"ACPI0001", 0},
38 	{"ACPI0005", 0},
39 	{"", 0},
40 };
41 
42 MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
43 
44 static struct acpi_driver acpi_smb_hc_driver = {
45 	.name = "smbus_hc",
46 	.class = ACPI_SMB_HC_CLASS,
47 	.ids = sbs_device_ids,
48 	.ops = {
49 		.add = acpi_smbus_hc_add,
50 		.remove = acpi_smbus_hc_remove,
51 		},
52 };
53 
54 union acpi_smb_status {
55 	u8 raw;
56 	struct {
57 		u8 status:5;
58 		u8 reserved:1;
59 		u8 alarm:1;
60 		u8 done:1;
61 	} fields;
62 };
63 
64 enum acpi_smb_status_codes {
65 	SMBUS_OK = 0,
66 	SMBUS_UNKNOWN_FAILURE = 0x07,
67 	SMBUS_DEVICE_ADDRESS_NACK = 0x10,
68 	SMBUS_DEVICE_ERROR = 0x11,
69 	SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12,
70 	SMBUS_UNKNOWN_ERROR = 0x13,
71 	SMBUS_DEVICE_ACCESS_DENIED = 0x17,
72 	SMBUS_TIMEOUT = 0x18,
73 	SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19,
74 	SMBUS_BUSY = 0x1a,
75 	SMBUS_PEC_ERROR = 0x1f,
76 };
77 
78 enum acpi_smb_offset {
79 	ACPI_SMB_PROTOCOL = 0,	/* protocol, PEC */
80 	ACPI_SMB_STATUS = 1,	/* status */
81 	ACPI_SMB_ADDRESS = 2,	/* address */
82 	ACPI_SMB_COMMAND = 3,	/* command */
83 	ACPI_SMB_DATA = 4,	/* 32 data registers */
84 	ACPI_SMB_BLOCK_COUNT = 0x24,	/* number of data bytes */
85 	ACPI_SMB_ALARM_ADDRESS = 0x25,	/* alarm address */
86 	ACPI_SMB_ALARM_DATA = 0x26,	/* 2 bytes alarm data */
87 };
88 
89 static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
90 {
91 	return ec_read(hc->offset + address, data);
92 }
93 
94 static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data)
95 {
96 	return ec_write(hc->offset + address, data);
97 }
98 
99 static inline int smb_check_done(struct acpi_smb_hc *hc)
100 {
101 	union acpi_smb_status status = {.raw = 0};
102 	smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw);
103 	return status.fields.done && (status.fields.status == SMBUS_OK);
104 }
105 
106 static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
107 {
108 	if (wait_event_timeout(hc->wait, smb_check_done(hc),
109 			       msecs_to_jiffies(timeout)))
110 		return 0;
111 	/*
112 	 * After the timeout happens, OS will try to check the status of SMbus.
113 	 * If the status is what OS expected, it will be regarded as the bogus
114 	 * timeout.
115 	 */
116 	if (smb_check_done(hc))
117 		return 0;
118 	else
119 		return -ETIME;
120 }
121 
122 static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
123 				  u8 address, u8 command, u8 *data, u8 length)
124 {
125 	int ret = -EFAULT, i;
126 	u8 temp, sz = 0;
127 
128 	if (!hc) {
129 		printk(KERN_ERR PREFIX "host controller is not configured\n");
130 		return ret;
131 	}
132 
133 	mutex_lock(&hc->lock);
134 	if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
135 		goto end;
136 	if (temp) {
137 		ret = -EBUSY;
138 		goto end;
139 	}
140 	smb_hc_write(hc, ACPI_SMB_COMMAND, command);
141 	if (!(protocol & 0x01)) {
142 		smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length);
143 		for (i = 0; i < length; ++i)
144 			smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]);
145 	}
146 	smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1);
147 	smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol);
148 	/*
149 	 * Wait for completion. Save the status code, data size,
150 	 * and data into the return package (if required by the protocol).
151 	 */
152 	ret = wait_transaction_complete(hc, 1000);
153 	if (ret || !(protocol & 0x01))
154 		goto end;
155 	switch (protocol) {
156 	case SMBUS_RECEIVE_BYTE:
157 	case SMBUS_READ_BYTE:
158 		sz = 1;
159 		break;
160 	case SMBUS_READ_WORD:
161 		sz = 2;
162 		break;
163 	case SMBUS_READ_BLOCK:
164 		if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) {
165 			ret = -EFAULT;
166 			goto end;
167 		}
168 		sz &= 0x1f;
169 		break;
170 	}
171 	for (i = 0; i < sz; ++i)
172 		smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]);
173       end:
174 	mutex_unlock(&hc->lock);
175 	return ret;
176 }
177 
178 int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
179 		    u8 command, u8 *data)
180 {
181 	return acpi_smbus_transaction(hc, protocol, address, command, data, 0);
182 }
183 
184 EXPORT_SYMBOL_GPL(acpi_smbus_read);
185 
186 int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address,
187 		     u8 command, u8 *data, u8 length)
188 {
189 	return acpi_smbus_transaction(hc, protocol, address, command, data, length);
190 }
191 
192 EXPORT_SYMBOL_GPL(acpi_smbus_write);
193 
194 int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
195 			         smbus_alarm_callback callback, void *context)
196 {
197 	mutex_lock(&hc->lock);
198 	hc->callback = callback;
199 	hc->context = context;
200 	mutex_unlock(&hc->lock);
201 	return 0;
202 }
203 
204 EXPORT_SYMBOL_GPL(acpi_smbus_register_callback);
205 
206 int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
207 {
208 	mutex_lock(&hc->lock);
209 	hc->callback = NULL;
210 	hc->context = NULL;
211 	mutex_unlock(&hc->lock);
212 	return 0;
213 }
214 
215 EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback);
216 
217 static inline void acpi_smbus_callback(void *context)
218 {
219 	struct acpi_smb_hc *hc = context;
220 	if (hc->callback)
221 		hc->callback(hc->context);
222 }
223 
224 static int smbus_alarm(void *context)
225 {
226 	struct acpi_smb_hc *hc = context;
227 	union acpi_smb_status status;
228 	u8 address;
229 	if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw))
230 		return 0;
231 	/* Check if it is only a completion notify */
232 	if (status.fields.done)
233 		wake_up(&hc->wait);
234 	if (!status.fields.alarm)
235 		return 0;
236 	mutex_lock(&hc->lock);
237 	smb_hc_read(hc, ACPI_SMB_ALARM_ADDRESS, &address);
238 	status.fields.alarm = 0;
239 	smb_hc_write(hc, ACPI_SMB_STATUS, status.raw);
240 	/* We are only interested in events coming from known devices */
241 	switch (address >> 1) {
242 		case ACPI_SBS_CHARGER:
243 		case ACPI_SBS_MANAGER:
244 		case ACPI_SBS_BATTERY:
245 			acpi_os_execute(OSL_GPE_HANDLER,
246 					acpi_smbus_callback, hc);
247 		default:;
248 	}
249 	mutex_unlock(&hc->lock);
250 	return 0;
251 }
252 
253 typedef int (*acpi_ec_query_func) (void *data);
254 
255 extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
256 			      acpi_handle handle, acpi_ec_query_func func,
257 			      void *data);
258 
259 static int acpi_smbus_hc_add(struct acpi_device *device)
260 {
261 	int status;
262 	unsigned long long val;
263 	struct acpi_smb_hc *hc;
264 
265 	if (!device)
266 		return -EINVAL;
267 
268 	status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
269 	if (ACPI_FAILURE(status)) {
270 		printk(KERN_ERR PREFIX "error obtaining _EC.\n");
271 		return -EIO;
272 	}
273 
274 	strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
275 	strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
276 
277 	hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
278 	if (!hc)
279 		return -ENOMEM;
280 	mutex_init(&hc->lock);
281 	init_waitqueue_head(&hc->wait);
282 
283 	hc->ec = acpi_driver_data(device->parent);
284 	hc->offset = (val >> 8) & 0xff;
285 	hc->query_bit = val & 0xff;
286 	device->driver_data = hc;
287 
288 	acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
289 	printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n",
290 		hc->ec, hc->offset, hc->query_bit);
291 
292 	return 0;
293 }
294 
295 extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
296 
297 static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
298 {
299 	struct acpi_smb_hc *hc;
300 
301 	if (!device)
302 		return -EINVAL;
303 
304 	hc = acpi_driver_data(device);
305 	acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
306 	kfree(hc);
307 	device->driver_data = NULL;
308 	return 0;
309 }
310 
311 static int __init acpi_smb_hc_init(void)
312 {
313 	int result;
314 
315 	result = acpi_bus_register_driver(&acpi_smb_hc_driver);
316 	if (result < 0)
317 		return -ENODEV;
318 	return 0;
319 }
320 
321 static void __exit acpi_smb_hc_exit(void)
322 {
323 	acpi_bus_unregister_driver(&acpi_smb_hc_driver);
324 }
325 
326 module_init(acpi_smb_hc_init);
327 module_exit(acpi_smb_hc_exit);
328 
329 MODULE_LICENSE("GPL");
330 MODULE_AUTHOR("Alexey Starikovskiy");
331 MODULE_DESCRIPTION("ACPI SMBus HC driver");
332