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