xref: /linux/drivers/media/cec/platform/seco/seco-cec.c (revision 908fc4c2)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * CEC driver for SECO X86 Boards
4  *
5  * Author:  Ettore Chimenti <ek5.chimenti@gmail.com>
6  * Copyright (C) 2018, SECO SpA.
7  * Copyright (C) 2018, Aidilab Srl.
8  */
9 
10 #include <linux/module.h>
11 #include <linux/acpi.h>
12 #include <linux/delay.h>
13 #include <linux/dmi.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/interrupt.h>
16 #include <linux/pci.h>
17 #include <linux/platform_device.h>
18 
19 /* CEC Framework */
20 #include <media/cec-notifier.h>
21 
22 #include "seco-cec.h"
23 
24 struct secocec_data {
25 	struct device *dev;
26 	struct platform_device *pdev;
27 	struct cec_adapter *cec_adap;
28 	struct cec_notifier *notifier;
29 	struct rc_dev *ir;
30 	char ir_input_phys[32];
31 	int irq;
32 };
33 
34 #define smb_wr16(cmd, data) smb_word_op(SECOCEC_MICRO_ADDRESS, \
35 					cmd, data, SMBUS_WRITE, NULL)
36 #define smb_rd16(cmd, res) smb_word_op(SECOCEC_MICRO_ADDRESS, \
37 				       cmd, 0, SMBUS_READ, res)
38 
39 static int smb_word_op(u16 slave_addr, u8 cmd, u16 data,
40 		       u8 operation, u16 *result)
41 {
42 	unsigned int count;
43 	int status = 0;
44 
45 	/* Active wait until ready */
46 	for (count = 0; count <= SMBTIMEOUT; ++count) {
47 		if (!(inb(HSTS) & BRA_INUSE_STS))
48 			break;
49 		udelay(SMB_POLL_UDELAY);
50 	}
51 
52 	if (count > SMBTIMEOUT)
53 		/* Reset the lock instead of failing */
54 		outb(0xff, HSTS);
55 
56 	outb(0x00, HCNT);
57 	outb((u8)(slave_addr & 0xfe) | operation, XMIT_SLVA);
58 	outb(cmd, HCMD);
59 	inb(HCNT);
60 
61 	if (operation == SMBUS_WRITE) {
62 		outb((u8)data, HDAT0);
63 		outb((u8)(data >> 8), HDAT1);
64 	}
65 
66 	outb(BRA_START + BRA_SMB_CMD_WORD_DATA, HCNT);
67 
68 	for (count = 0; count <= SMBTIMEOUT; count++) {
69 		if (!(inb(HSTS) & BRA_HOST_BUSY))
70 			break;
71 		udelay(SMB_POLL_UDELAY);
72 	}
73 
74 	if (count > SMBTIMEOUT) {
75 		status = -EBUSY;
76 		goto err;
77 	}
78 
79 	if (inb(HSTS) & BRA_HSTS_ERR_MASK) {
80 		status = -EIO;
81 		goto err;
82 	}
83 
84 	if (operation == SMBUS_READ)
85 		*result = ((inb(HDAT0) & 0xff) + ((inb(HDAT1) & 0xff) << 8));
86 
87 err:
88 	outb(0xff, HSTS);
89 	return status;
90 }
91 
92 static int secocec_adap_enable(struct cec_adapter *adap, bool enable)
93 {
94 	struct secocec_data *cec = cec_get_drvdata(adap);
95 	struct device *dev = cec->dev;
96 	u16 val = 0;
97 	int status;
98 
99 	if (enable) {
100 		/* Clear the status register */
101 		status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
102 		if (status)
103 			goto err;
104 
105 		status = smb_wr16(SECOCEC_STATUS_REG_1, val);
106 		if (status)
107 			goto err;
108 
109 		/* Enable the interrupts */
110 		status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
111 		if (status)
112 			goto err;
113 
114 		status = smb_wr16(SECOCEC_ENABLE_REG_1,
115 				  val | SECOCEC_ENABLE_REG_1_CEC);
116 		if (status)
117 			goto err;
118 
119 		dev_dbg(dev, "Device enabled\n");
120 	} else {
121 		/* Clear the status register */
122 		status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
123 		status = smb_wr16(SECOCEC_STATUS_REG_1, val);
124 
125 		/* Disable the interrupts */
126 		status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
127 		status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
128 				  ~SECOCEC_ENABLE_REG_1_CEC &
129 				  ~SECOCEC_ENABLE_REG_1_IR);
130 
131 		dev_dbg(dev, "Device disabled\n");
132 	}
133 
134 	return 0;
135 err:
136 	return status;
137 }
138 
139 static int secocec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
140 {
141 	u16 enable_val = 0;
142 	int status;
143 
144 	/* Disable device */
145 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &enable_val);
146 	if (status)
147 		return status;
148 
149 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
150 			  enable_val & ~SECOCEC_ENABLE_REG_1_CEC);
151 	if (status)
152 		return status;
153 
154 	/* Write logical address
155 	 * NOTE: CEC_LOG_ADDR_INVALID is mapped to the 'Unregistered' LA
156 	 */
157 	status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
158 	if (status)
159 		return status;
160 
161 	/* Re-enable device */
162 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
163 			  enable_val | SECOCEC_ENABLE_REG_1_CEC);
164 	if (status)
165 		return status;
166 
167 	return 0;
168 }
169 
170 static int secocec_adap_transmit(struct cec_adapter *adap, u8 attempts,
171 				 u32 signal_free_time, struct cec_msg *msg)
172 {
173 	u16 payload_len, payload_id_len, destination, val = 0;
174 	u8 *payload_msg;
175 	int status;
176 	u8 i;
177 
178 	/* Device msg len already accounts for header */
179 	payload_id_len = msg->len - 1;
180 
181 	/* Send data length */
182 	status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
183 	if (status)
184 		goto err;
185 
186 	/* Send Operation ID if present */
187 	if (payload_id_len > 0) {
188 		status = smb_wr16(SECOCEC_WRITE_OPERATION_ID, msg->msg[1]);
189 		if (status)
190 			goto err;
191 	}
192 	/* Send data if present */
193 	if (payload_id_len > 1) {
194 		/* Only data; */
195 		payload_len = msg->len - 2;
196 		payload_msg = &msg->msg[2];
197 
198 		/* Copy message into registers */
199 		for (i = 0; i < payload_len; i += 2) {
200 			/* hi byte */
201 			val = payload_msg[i + 1] << 8;
202 
203 			/* lo byte */
204 			val |= payload_msg[i];
205 
206 			status = smb_wr16(SECOCEC_WRITE_DATA_00 + i / 2, val);
207 			if (status)
208 				goto err;
209 		}
210 	}
211 	/* Send msg source/destination and fire msg */
212 	destination = msg->msg[0];
213 	status = smb_wr16(SECOCEC_WRITE_BYTE0, destination);
214 	if (status)
215 		goto err;
216 
217 	return 0;
218 
219 err:
220 	return status;
221 }
222 
223 static void secocec_tx_done(struct cec_adapter *adap, u16 status_val)
224 {
225 	if (status_val & SECOCEC_STATUS_TX_ERROR_MASK) {
226 		if (status_val & SECOCEC_STATUS_TX_NACK_ERROR)
227 			cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK);
228 		else
229 			cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR);
230 	} else {
231 		cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
232 	}
233 
234 	/* Reset status reg */
235 	status_val = SECOCEC_STATUS_TX_ERROR_MASK |
236 		SECOCEC_STATUS_MSG_SENT_MASK |
237 		SECOCEC_STATUS_TX_NACK_ERROR;
238 	smb_wr16(SECOCEC_STATUS, status_val);
239 }
240 
241 static void secocec_rx_done(struct cec_adapter *adap, u16 status_val)
242 {
243 	struct secocec_data *cec = cec_get_drvdata(adap);
244 	struct device *dev = cec->dev;
245 	struct cec_msg msg = { };
246 	bool flag_overflow = false;
247 	u8 payload_len, i = 0;
248 	u8 *payload_msg;
249 	u16 val = 0;
250 	int status;
251 
252 	if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) {
253 		/* NOTE: Untested, it also might not be necessary */
254 		dev_warn(dev, "Received more than 16 bytes. Discarding\n");
255 		flag_overflow = true;
256 	}
257 
258 	if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) {
259 		dev_warn(dev, "Message received with errors. Discarding\n");
260 		status = -EIO;
261 		goto rxerr;
262 	}
263 
264 	/* Read message length */
265 	status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
266 	if (status)
267 		return;
268 
269 	/* Device msg len already accounts for the header */
270 	msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
271 
272 	/* Read logical address */
273 	status = smb_rd16(SECOCEC_READ_BYTE0, &val);
274 	if (status)
275 		return;
276 
277 	/* device stores source LA and destination */
278 	msg.msg[0] = val;
279 
280 	/* Read operation ID */
281 	status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
282 	if (status)
283 		return;
284 
285 	msg.msg[1] = val;
286 
287 	/* Read data if present */
288 	if (msg.len > 1) {
289 		payload_len = msg.len - 2;
290 		payload_msg = &msg.msg[2];
291 
292 		/* device stores 2 bytes in every 16-bit val */
293 		for (i = 0; i < payload_len; i += 2) {
294 			status = smb_rd16(SECOCEC_READ_DATA_00 + i / 2, &val);
295 			if (status)
296 				return;
297 
298 			/* low byte, skipping header */
299 			payload_msg[i] = val & 0x00ff;
300 
301 			/* hi byte */
302 			payload_msg[i + 1] = (val & 0xff00) >> 8;
303 		}
304 	}
305 
306 	cec_received_msg(cec->cec_adap, &msg);
307 
308 	/* Reset status reg */
309 	status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK;
310 	if (flag_overflow)
311 		status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
312 
313 	status = smb_wr16(SECOCEC_STATUS, status_val);
314 
315 	return;
316 
317 rxerr:
318 	/* Reset error reg */
319 	status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK |
320 		SECOCEC_STATUS_RX_ERROR_MASK;
321 	if (flag_overflow)
322 		status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
323 	smb_wr16(SECOCEC_STATUS, status_val);
324 }
325 
326 static const struct cec_adap_ops secocec_cec_adap_ops = {
327 	/* Low-level callbacks */
328 	.adap_enable = secocec_adap_enable,
329 	.adap_log_addr = secocec_adap_log_addr,
330 	.adap_transmit = secocec_adap_transmit,
331 };
332 
333 #ifdef CONFIG_CEC_SECO_RC
334 static int secocec_ir_probe(void *priv)
335 {
336 	struct secocec_data *cec = priv;
337 	struct device *dev = cec->dev;
338 	int status;
339 	u16 val;
340 
341 	/* Prepare the RC input device */
342 	cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
343 	if (!cec->ir)
344 		return -ENOMEM;
345 
346 	snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
347 		 "%s/input0", dev_name(dev));
348 
349 	cec->ir->device_name = dev_name(dev);
350 	cec->ir->input_phys = cec->ir_input_phys;
351 	cec->ir->input_id.bustype = BUS_HOST;
352 	cec->ir->input_id.vendor = 0;
353 	cec->ir->input_id.product = 0;
354 	cec->ir->input_id.version = 1;
355 	cec->ir->driver_name = SECOCEC_DEV_NAME;
356 	cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
357 	cec->ir->priv = cec;
358 	cec->ir->map_name = RC_MAP_HAUPPAUGE;
359 	cec->ir->timeout = MS_TO_US(100);
360 
361 	/* Clear the status register */
362 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
363 	if (status != 0)
364 		goto err;
365 
366 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
367 	if (status != 0)
368 		goto err;
369 
370 	/* Enable the interrupts */
371 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
372 	if (status != 0)
373 		goto err;
374 
375 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
376 			  val | SECOCEC_ENABLE_REG_1_IR);
377 	if (status != 0)
378 		goto err;
379 
380 	dev_dbg(dev, "IR enabled\n");
381 
382 	status = devm_rc_register_device(dev, cec->ir);
383 
384 	if (status) {
385 		dev_err(dev, "Failed to prepare input device\n");
386 		cec->ir = NULL;
387 		goto err;
388 	}
389 
390 	return 0;
391 
392 err:
393 	smb_rd16(SECOCEC_ENABLE_REG_1, &val);
394 
395 	smb_wr16(SECOCEC_ENABLE_REG_1,
396 		 val & ~SECOCEC_ENABLE_REG_1_IR);
397 
398 	dev_dbg(dev, "IR disabled\n");
399 	return status;
400 }
401 
402 static int secocec_ir_rx(struct secocec_data *priv)
403 {
404 	struct secocec_data *cec = priv;
405 	struct device *dev = cec->dev;
406 	u16 val, status, key, addr, toggle;
407 
408 	if (!cec->ir)
409 		return -ENODEV;
410 
411 	status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
412 	if (status != 0)
413 		goto err;
414 
415 	key = val & SECOCEC_IR_COMMAND_MASK;
416 	addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
417 	toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
418 
419 	rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
420 
421 	dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x\n", key,
422 		addr, toggle);
423 
424 	return 0;
425 
426 err:
427 	dev_err(dev, "IR Receive message failed (%d)\n", status);
428 	return -EIO;
429 }
430 #else
431 static void secocec_ir_rx(struct secocec_data *priv)
432 {
433 }
434 
435 static int secocec_ir_probe(void *priv)
436 {
437 	return 0;
438 }
439 #endif
440 
441 static irqreturn_t secocec_irq_handler(int irq, void *priv)
442 {
443 	struct secocec_data *cec = priv;
444 	struct device *dev = cec->dev;
445 	u16 status_val, cec_val, val = 0;
446 	int status;
447 
448 	/*  Read status register */
449 	status = smb_rd16(SECOCEC_STATUS_REG_1, &status_val);
450 	if (status)
451 		goto err;
452 
453 	if (status_val & SECOCEC_STATUS_REG_1_CEC) {
454 		/* Read CEC status register */
455 		status = smb_rd16(SECOCEC_STATUS, &cec_val);
456 		if (status)
457 			goto err;
458 
459 		if (cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK)
460 			secocec_rx_done(cec->cec_adap, cec_val);
461 
462 		if (cec_val & SECOCEC_STATUS_MSG_SENT_MASK)
463 			secocec_tx_done(cec->cec_adap, cec_val);
464 
465 		if ((~cec_val & SECOCEC_STATUS_MSG_SENT_MASK) &&
466 		    (~cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK))
467 			dev_warn_once(dev,
468 				      "Message not received or sent, but interrupt fired");
469 
470 		val = SECOCEC_STATUS_REG_1_CEC;
471 	}
472 
473 	if (status_val & SECOCEC_STATUS_REG_1_IR) {
474 		val |= SECOCEC_STATUS_REG_1_IR;
475 
476 		secocec_ir_rx(cec);
477 	}
478 
479 	/*  Reset status register */
480 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
481 	if (status)
482 		goto err;
483 
484 	return IRQ_HANDLED;
485 
486 err:
487 	dev_err_once(dev, "IRQ: R/W SMBus operation failed %d\n", status);
488 
489 	/*  Reset status register */
490 	val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR;
491 	smb_wr16(SECOCEC_STATUS_REG_1, val);
492 
493 	return IRQ_HANDLED;
494 }
495 
496 struct cec_dmi_match {
497 	const char *sys_vendor;
498 	const char *product_name;
499 	const char *devname;
500 	const char *conn;
501 };
502 
503 static const struct cec_dmi_match secocec_dmi_match_table[] = {
504 	/* UDOO X86 */
505 	{ "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
506 };
507 
508 static struct device *secocec_cec_find_hdmi_dev(struct device *dev,
509 						const char **conn)
510 {
511 	int i;
512 
513 	for (i = 0 ; i < ARRAY_SIZE(secocec_dmi_match_table) ; ++i) {
514 		const struct cec_dmi_match *m = &secocec_dmi_match_table[i];
515 
516 		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
517 		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
518 			struct device *d;
519 
520 			/* Find the device, bail out if not yet registered */
521 			d = bus_find_device_by_name(&pci_bus_type, NULL,
522 						    m->devname);
523 			if (!d)
524 				return ERR_PTR(-EPROBE_DEFER);
525 
526 			put_device(d);
527 			*conn = m->conn;
528 			return d;
529 		}
530 	}
531 
532 	return ERR_PTR(-EINVAL);
533 }
534 
535 static int secocec_acpi_probe(struct secocec_data *sdev)
536 {
537 	struct device *dev = sdev->dev;
538 	struct gpio_desc *gpio;
539 	int irq = 0;
540 
541 	gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
542 	if (IS_ERR(gpio)) {
543 		dev_err(dev, "Cannot request interrupt gpio\n");
544 		return PTR_ERR(gpio);
545 	}
546 
547 	irq = gpiod_to_irq(gpio);
548 	if (irq < 0) {
549 		dev_err(dev, "Cannot find valid irq\n");
550 		return -ENODEV;
551 	}
552 	dev_dbg(dev, "irq-gpio is bound to IRQ %d\n", irq);
553 
554 	sdev->irq = irq;
555 
556 	return 0;
557 }
558 
559 static int secocec_probe(struct platform_device *pdev)
560 {
561 	struct secocec_data *secocec;
562 	struct device *dev = &pdev->dev;
563 	struct device *hdmi_dev;
564 	const char *conn = NULL;
565 	int ret;
566 	u16 val;
567 
568 	hdmi_dev = secocec_cec_find_hdmi_dev(&pdev->dev, &conn);
569 	if (IS_ERR(hdmi_dev))
570 		return PTR_ERR(hdmi_dev);
571 
572 	secocec = devm_kzalloc(dev, sizeof(*secocec), GFP_KERNEL);
573 	if (!secocec)
574 		return -ENOMEM;
575 
576 	dev_set_drvdata(dev, secocec);
577 
578 	/* Request SMBus regions */
579 	if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) {
580 		dev_err(dev, "Request memory region failed\n");
581 		return -ENXIO;
582 	}
583 
584 	secocec->pdev = pdev;
585 	secocec->dev = dev;
586 
587 	if (!has_acpi_companion(dev)) {
588 		dev_dbg(dev, "Cannot find any ACPI companion\n");
589 		ret = -ENODEV;
590 		goto err;
591 	}
592 
593 	ret = secocec_acpi_probe(secocec);
594 	if (ret) {
595 		dev_err(dev, "Cannot assign gpio to IRQ\n");
596 		ret = -ENODEV;
597 		goto err;
598 	}
599 
600 	/* Firmware version check */
601 	ret = smb_rd16(SECOCEC_VERSION, &val);
602 	if (ret) {
603 		dev_err(dev, "Cannot check fw version\n");
604 		goto err;
605 	}
606 	if (val < SECOCEC_LATEST_FW) {
607 		dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x\n",
608 			val, SECOCEC_LATEST_FW);
609 		ret = -EINVAL;
610 		goto err;
611 	}
612 
613 	ret = devm_request_threaded_irq(dev,
614 					secocec->irq,
615 					NULL,
616 					secocec_irq_handler,
617 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
618 					dev_name(&pdev->dev), secocec);
619 
620 	if (ret) {
621 		dev_err(dev, "Cannot request IRQ %d\n", secocec->irq);
622 		ret = -EIO;
623 		goto err;
624 	}
625 
626 	/* Allocate CEC adapter */
627 	secocec->cec_adap = cec_allocate_adapter(&secocec_cec_adap_ops,
628 						 secocec,
629 						 dev_name(dev),
630 						 CEC_CAP_DEFAULTS |
631 						 CEC_CAP_CONNECTOR_INFO,
632 						 SECOCEC_MAX_ADDRS);
633 
634 	if (IS_ERR(secocec->cec_adap)) {
635 		ret = PTR_ERR(secocec->cec_adap);
636 		goto err;
637 	}
638 
639 	secocec->notifier = cec_notifier_cec_adap_register(hdmi_dev, conn,
640 							   secocec->cec_adap);
641 	if (!secocec->notifier) {
642 		ret = -ENOMEM;
643 		goto err_delete_adapter;
644 	}
645 
646 	ret = cec_register_adapter(secocec->cec_adap, dev);
647 	if (ret)
648 		goto err_notifier;
649 
650 	ret = secocec_ir_probe(secocec);
651 	if (ret)
652 		goto err_notifier;
653 
654 	platform_set_drvdata(pdev, secocec);
655 
656 	dev_dbg(dev, "Device registered\n");
657 
658 	return ret;
659 
660 err_notifier:
661 	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
662 err_delete_adapter:
663 	cec_delete_adapter(secocec->cec_adap);
664 err:
665 	release_region(BRA_SMB_BASE_ADDR, 7);
666 	dev_err(dev, "%s device probe failed\n", dev_name(dev));
667 
668 	return ret;
669 }
670 
671 static int secocec_remove(struct platform_device *pdev)
672 {
673 	struct secocec_data *secocec = platform_get_drvdata(pdev);
674 	u16 val;
675 
676 	if (secocec->ir) {
677 		smb_rd16(SECOCEC_ENABLE_REG_1, &val);
678 
679 		smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
680 
681 		dev_dbg(&pdev->dev, "IR disabled\n");
682 	}
683 	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
684 	cec_unregister_adapter(secocec->cec_adap);
685 
686 	release_region(BRA_SMB_BASE_ADDR, 7);
687 
688 	dev_dbg(&pdev->dev, "CEC device removed\n");
689 
690 	return 0;
691 }
692 
693 #ifdef CONFIG_PM_SLEEP
694 static int secocec_suspend(struct device *dev)
695 {
696 	int status;
697 	u16 val;
698 
699 	dev_dbg(dev, "Device going to suspend, disabling\n");
700 
701 	/* Clear the status register */
702 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
703 	if (status)
704 		goto err;
705 
706 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
707 	if (status)
708 		goto err;
709 
710 	/* Disable the interrupts */
711 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
712 	if (status)
713 		goto err;
714 
715 	status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
716 			  ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
717 	if (status)
718 		goto err;
719 
720 	return 0;
721 
722 err:
723 	dev_err(dev, "Suspend failed: %d\n", status);
724 	return status;
725 }
726 
727 static int secocec_resume(struct device *dev)
728 {
729 	int status;
730 	u16 val;
731 
732 	dev_dbg(dev, "Resuming device from suspend\n");
733 
734 	/* Clear the status register */
735 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
736 	if (status)
737 		goto err;
738 
739 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
740 	if (status)
741 		goto err;
742 
743 	/* Enable the interrupts */
744 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
745 	if (status)
746 		goto err;
747 
748 	status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
749 	if (status)
750 		goto err;
751 
752 	dev_dbg(dev, "Device resumed from suspend\n");
753 
754 	return 0;
755 
756 err:
757 	dev_err(dev, "Resume failed: %d\n", status);
758 	return status;
759 }
760 
761 static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
762 #define SECOCEC_PM_OPS (&secocec_pm_ops)
763 #else
764 #define SECOCEC_PM_OPS NULL
765 #endif
766 
767 #ifdef CONFIG_ACPI
768 static const struct acpi_device_id secocec_acpi_match[] = {
769 	{"CEC00001", 0},
770 	{},
771 };
772 
773 MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
774 #endif
775 
776 static struct platform_driver secocec_driver = {
777 	.driver = {
778 		   .name = SECOCEC_DEV_NAME,
779 		   .acpi_match_table = ACPI_PTR(secocec_acpi_match),
780 		   .pm = SECOCEC_PM_OPS,
781 	},
782 	.probe = secocec_probe,
783 	.remove = secocec_remove,
784 };
785 
786 module_platform_driver(secocec_driver);
787 
788 MODULE_DESCRIPTION("SECO CEC X86 Driver");
789 MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
790 MODULE_LICENSE("Dual BSD/GPL");
791