14be5e864SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
24be5e864SMauro Carvalho Chehab /*
34be5e864SMauro Carvalho Chehab  * CEC driver for SECO X86 Boards
44be5e864SMauro Carvalho Chehab  *
54be5e864SMauro Carvalho Chehab  * Author:  Ettore Chimenti <ek5.chimenti@gmail.com>
64be5e864SMauro Carvalho Chehab  * Copyright (C) 2018, SECO SpA.
74be5e864SMauro Carvalho Chehab  * Copyright (C) 2018, Aidilab Srl.
84be5e864SMauro Carvalho Chehab  */
94be5e864SMauro Carvalho Chehab 
104be5e864SMauro Carvalho Chehab #include <linux/module.h>
114be5e864SMauro Carvalho Chehab #include <linux/acpi.h>
124be5e864SMauro Carvalho Chehab #include <linux/delay.h>
134be5e864SMauro Carvalho Chehab #include <linux/dmi.h>
144be5e864SMauro Carvalho Chehab #include <linux/gpio/consumer.h>
154be5e864SMauro Carvalho Chehab #include <linux/interrupt.h>
164be5e864SMauro Carvalho Chehab #include <linux/pci.h>
174be5e864SMauro Carvalho Chehab #include <linux/platform_device.h>
184be5e864SMauro Carvalho Chehab 
194be5e864SMauro Carvalho Chehab /* CEC Framework */
204be5e864SMauro Carvalho Chehab #include <media/cec-notifier.h>
214be5e864SMauro Carvalho Chehab 
224be5e864SMauro Carvalho Chehab #include "seco-cec.h"
234be5e864SMauro Carvalho Chehab 
244be5e864SMauro Carvalho Chehab struct secocec_data {
254be5e864SMauro Carvalho Chehab 	struct device *dev;
264be5e864SMauro Carvalho Chehab 	struct platform_device *pdev;
274be5e864SMauro Carvalho Chehab 	struct cec_adapter *cec_adap;
284be5e864SMauro Carvalho Chehab 	struct cec_notifier *notifier;
294be5e864SMauro Carvalho Chehab 	struct rc_dev *ir;
304be5e864SMauro Carvalho Chehab 	char ir_input_phys[32];
314be5e864SMauro Carvalho Chehab 	int irq;
324be5e864SMauro Carvalho Chehab };
334be5e864SMauro Carvalho Chehab 
3497f05aadSTom Rix #define smb_wr16(cmd, data) smb_word_op(SECOCEC_MICRO_ADDRESS, \
354be5e864SMauro Carvalho Chehab 					cmd, data, SMBUS_WRITE, NULL)
3697f05aadSTom Rix #define smb_rd16(cmd, res) smb_word_op(SECOCEC_MICRO_ADDRESS, \
374be5e864SMauro Carvalho Chehab 				       cmd, 0, SMBUS_READ, res)
384be5e864SMauro Carvalho Chehab 
smb_word_op(u16 slave_addr,u8 cmd,u16 data,u8 operation,u16 * result)3997f05aadSTom Rix static int smb_word_op(u16 slave_addr, u8 cmd, u16 data,
404be5e864SMauro Carvalho Chehab 		       u8 operation, u16 *result)
414be5e864SMauro Carvalho Chehab {
424be5e864SMauro Carvalho Chehab 	unsigned int count;
434be5e864SMauro Carvalho Chehab 	int status = 0;
444be5e864SMauro Carvalho Chehab 
454be5e864SMauro Carvalho Chehab 	/* Active wait until ready */
464be5e864SMauro Carvalho Chehab 	for (count = 0; count <= SMBTIMEOUT; ++count) {
474be5e864SMauro Carvalho Chehab 		if (!(inb(HSTS) & BRA_INUSE_STS))
484be5e864SMauro Carvalho Chehab 			break;
494be5e864SMauro Carvalho Chehab 		udelay(SMB_POLL_UDELAY);
504be5e864SMauro Carvalho Chehab 	}
514be5e864SMauro Carvalho Chehab 
524be5e864SMauro Carvalho Chehab 	if (count > SMBTIMEOUT)
534be5e864SMauro Carvalho Chehab 		/* Reset the lock instead of failing */
544be5e864SMauro Carvalho Chehab 		outb(0xff, HSTS);
554be5e864SMauro Carvalho Chehab 
564be5e864SMauro Carvalho Chehab 	outb(0x00, HCNT);
574be5e864SMauro Carvalho Chehab 	outb((u8)(slave_addr & 0xfe) | operation, XMIT_SLVA);
584be5e864SMauro Carvalho Chehab 	outb(cmd, HCMD);
594be5e864SMauro Carvalho Chehab 	inb(HCNT);
604be5e864SMauro Carvalho Chehab 
614be5e864SMauro Carvalho Chehab 	if (operation == SMBUS_WRITE) {
624be5e864SMauro Carvalho Chehab 		outb((u8)data, HDAT0);
634be5e864SMauro Carvalho Chehab 		outb((u8)(data >> 8), HDAT1);
644be5e864SMauro Carvalho Chehab 	}
654be5e864SMauro Carvalho Chehab 
6697f05aadSTom Rix 	outb(BRA_START + BRA_SMB_CMD_WORD_DATA, HCNT);
674be5e864SMauro Carvalho Chehab 
684be5e864SMauro Carvalho Chehab 	for (count = 0; count <= SMBTIMEOUT; count++) {
694be5e864SMauro Carvalho Chehab 		if (!(inb(HSTS) & BRA_HOST_BUSY))
704be5e864SMauro Carvalho Chehab 			break;
714be5e864SMauro Carvalho Chehab 		udelay(SMB_POLL_UDELAY);
724be5e864SMauro Carvalho Chehab 	}
734be5e864SMauro Carvalho Chehab 
744be5e864SMauro Carvalho Chehab 	if (count > SMBTIMEOUT) {
754be5e864SMauro Carvalho Chehab 		status = -EBUSY;
764be5e864SMauro Carvalho Chehab 		goto err;
774be5e864SMauro Carvalho Chehab 	}
784be5e864SMauro Carvalho Chehab 
794be5e864SMauro Carvalho Chehab 	if (inb(HSTS) & BRA_HSTS_ERR_MASK) {
804be5e864SMauro Carvalho Chehab 		status = -EIO;
814be5e864SMauro Carvalho Chehab 		goto err;
824be5e864SMauro Carvalho Chehab 	}
834be5e864SMauro Carvalho Chehab 
844be5e864SMauro Carvalho Chehab 	if (operation == SMBUS_READ)
854be5e864SMauro Carvalho Chehab 		*result = ((inb(HDAT0) & 0xff) + ((inb(HDAT1) & 0xff) << 8));
864be5e864SMauro Carvalho Chehab 
874be5e864SMauro Carvalho Chehab err:
884be5e864SMauro Carvalho Chehab 	outb(0xff, HSTS);
894be5e864SMauro Carvalho Chehab 	return status;
904be5e864SMauro Carvalho Chehab }
914be5e864SMauro Carvalho Chehab 
secocec_adap_enable(struct cec_adapter * adap,bool enable)924be5e864SMauro Carvalho Chehab static int secocec_adap_enable(struct cec_adapter *adap, bool enable)
934be5e864SMauro Carvalho Chehab {
944be5e864SMauro Carvalho Chehab 	struct secocec_data *cec = cec_get_drvdata(adap);
954be5e864SMauro Carvalho Chehab 	struct device *dev = cec->dev;
964be5e864SMauro Carvalho Chehab 	u16 val = 0;
974be5e864SMauro Carvalho Chehab 	int status;
984be5e864SMauro Carvalho Chehab 
994be5e864SMauro Carvalho Chehab 	if (enable) {
1004be5e864SMauro Carvalho Chehab 		/* Clear the status register */
1014be5e864SMauro Carvalho Chehab 		status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
1024be5e864SMauro Carvalho Chehab 		if (status)
1034be5e864SMauro Carvalho Chehab 			goto err;
1044be5e864SMauro Carvalho Chehab 
1054be5e864SMauro Carvalho Chehab 		status = smb_wr16(SECOCEC_STATUS_REG_1, val);
1064be5e864SMauro Carvalho Chehab 		if (status)
1074be5e864SMauro Carvalho Chehab 			goto err;
1084be5e864SMauro Carvalho Chehab 
1094be5e864SMauro Carvalho Chehab 		/* Enable the interrupts */
1104be5e864SMauro Carvalho Chehab 		status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
1114be5e864SMauro Carvalho Chehab 		if (status)
1124be5e864SMauro Carvalho Chehab 			goto err;
1134be5e864SMauro Carvalho Chehab 
1144be5e864SMauro Carvalho Chehab 		status = smb_wr16(SECOCEC_ENABLE_REG_1,
1154be5e864SMauro Carvalho Chehab 				  val | SECOCEC_ENABLE_REG_1_CEC);
1164be5e864SMauro Carvalho Chehab 		if (status)
1174be5e864SMauro Carvalho Chehab 			goto err;
1184be5e864SMauro Carvalho Chehab 
119cf4a3ab5SEttore Chimenti 		dev_dbg(dev, "Device enabled\n");
1204be5e864SMauro Carvalho Chehab 	} else {
1214be5e864SMauro Carvalho Chehab 		/* Clear the status register */
1224be5e864SMauro Carvalho Chehab 		status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
1234be5e864SMauro Carvalho Chehab 		status = smb_wr16(SECOCEC_STATUS_REG_1, val);
1244be5e864SMauro Carvalho Chehab 
1254be5e864SMauro Carvalho Chehab 		/* Disable the interrupts */
1264be5e864SMauro Carvalho Chehab 		status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
1274be5e864SMauro Carvalho Chehab 		status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
1284be5e864SMauro Carvalho Chehab 				  ~SECOCEC_ENABLE_REG_1_CEC &
1294be5e864SMauro Carvalho Chehab 				  ~SECOCEC_ENABLE_REG_1_IR);
1304be5e864SMauro Carvalho Chehab 
131cf4a3ab5SEttore Chimenti 		dev_dbg(dev, "Device disabled\n");
1324be5e864SMauro Carvalho Chehab 	}
1334be5e864SMauro Carvalho Chehab 
1344be5e864SMauro Carvalho Chehab 	return 0;
1354be5e864SMauro Carvalho Chehab err:
1364be5e864SMauro Carvalho Chehab 	return status;
1374be5e864SMauro Carvalho Chehab }
1384be5e864SMauro Carvalho Chehab 
secocec_adap_log_addr(struct cec_adapter * adap,u8 logical_addr)1394be5e864SMauro Carvalho Chehab static int secocec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
1404be5e864SMauro Carvalho Chehab {
1414be5e864SMauro Carvalho Chehab 	u16 enable_val = 0;
1424be5e864SMauro Carvalho Chehab 	int status;
1434be5e864SMauro Carvalho Chehab 
1444be5e864SMauro Carvalho Chehab 	/* Disable device */
1454be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &enable_val);
1464be5e864SMauro Carvalho Chehab 	if (status)
1474be5e864SMauro Carvalho Chehab 		return status;
1484be5e864SMauro Carvalho Chehab 
1494be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
1504be5e864SMauro Carvalho Chehab 			  enable_val & ~SECOCEC_ENABLE_REG_1_CEC);
1514be5e864SMauro Carvalho Chehab 	if (status)
1524be5e864SMauro Carvalho Chehab 		return status;
1534be5e864SMauro Carvalho Chehab 
1544be5e864SMauro Carvalho Chehab 	/* Write logical address
1554be5e864SMauro Carvalho Chehab 	 * NOTE: CEC_LOG_ADDR_INVALID is mapped to the 'Unregistered' LA
1564be5e864SMauro Carvalho Chehab 	 */
1574be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
1584be5e864SMauro Carvalho Chehab 	if (status)
1594be5e864SMauro Carvalho Chehab 		return status;
1604be5e864SMauro Carvalho Chehab 
1614be5e864SMauro Carvalho Chehab 	/* Re-enable device */
1624be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
1634be5e864SMauro Carvalho Chehab 			  enable_val | SECOCEC_ENABLE_REG_1_CEC);
1644be5e864SMauro Carvalho Chehab 	if (status)
1654be5e864SMauro Carvalho Chehab 		return status;
1664be5e864SMauro Carvalho Chehab 
1674be5e864SMauro Carvalho Chehab 	return 0;
1684be5e864SMauro Carvalho Chehab }
1694be5e864SMauro Carvalho Chehab 
secocec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)1704be5e864SMauro Carvalho Chehab static int secocec_adap_transmit(struct cec_adapter *adap, u8 attempts,
1714be5e864SMauro Carvalho Chehab 				 u32 signal_free_time, struct cec_msg *msg)
1724be5e864SMauro Carvalho Chehab {
1734be5e864SMauro Carvalho Chehab 	u16 payload_len, payload_id_len, destination, val = 0;
1744be5e864SMauro Carvalho Chehab 	u8 *payload_msg;
1754be5e864SMauro Carvalho Chehab 	int status;
1764be5e864SMauro Carvalho Chehab 	u8 i;
1774be5e864SMauro Carvalho Chehab 
1784be5e864SMauro Carvalho Chehab 	/* Device msg len already accounts for header */
1794be5e864SMauro Carvalho Chehab 	payload_id_len = msg->len - 1;
1804be5e864SMauro Carvalho Chehab 
1814be5e864SMauro Carvalho Chehab 	/* Send data length */
1824be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
1834be5e864SMauro Carvalho Chehab 	if (status)
1844be5e864SMauro Carvalho Chehab 		goto err;
1854be5e864SMauro Carvalho Chehab 
1864be5e864SMauro Carvalho Chehab 	/* Send Operation ID if present */
1874be5e864SMauro Carvalho Chehab 	if (payload_id_len > 0) {
1884be5e864SMauro Carvalho Chehab 		status = smb_wr16(SECOCEC_WRITE_OPERATION_ID, msg->msg[1]);
1894be5e864SMauro Carvalho Chehab 		if (status)
1904be5e864SMauro Carvalho Chehab 			goto err;
1914be5e864SMauro Carvalho Chehab 	}
1924be5e864SMauro Carvalho Chehab 	/* Send data if present */
1934be5e864SMauro Carvalho Chehab 	if (payload_id_len > 1) {
1944be5e864SMauro Carvalho Chehab 		/* Only data; */
1954be5e864SMauro Carvalho Chehab 		payload_len = msg->len - 2;
1964be5e864SMauro Carvalho Chehab 		payload_msg = &msg->msg[2];
1974be5e864SMauro Carvalho Chehab 
1984be5e864SMauro Carvalho Chehab 		/* Copy message into registers */
1994be5e864SMauro Carvalho Chehab 		for (i = 0; i < payload_len; i += 2) {
2004be5e864SMauro Carvalho Chehab 			/* hi byte */
2014be5e864SMauro Carvalho Chehab 			val = payload_msg[i + 1] << 8;
2024be5e864SMauro Carvalho Chehab 
2034be5e864SMauro Carvalho Chehab 			/* lo byte */
2044be5e864SMauro Carvalho Chehab 			val |= payload_msg[i];
2054be5e864SMauro Carvalho Chehab 
2064be5e864SMauro Carvalho Chehab 			status = smb_wr16(SECOCEC_WRITE_DATA_00 + i / 2, val);
2074be5e864SMauro Carvalho Chehab 			if (status)
2084be5e864SMauro Carvalho Chehab 				goto err;
2094be5e864SMauro Carvalho Chehab 		}
2104be5e864SMauro Carvalho Chehab 	}
2114be5e864SMauro Carvalho Chehab 	/* Send msg source/destination and fire msg */
2124be5e864SMauro Carvalho Chehab 	destination = msg->msg[0];
2134be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_WRITE_BYTE0, destination);
2144be5e864SMauro Carvalho Chehab 	if (status)
2154be5e864SMauro Carvalho Chehab 		goto err;
2164be5e864SMauro Carvalho Chehab 
2174be5e864SMauro Carvalho Chehab 	return 0;
2184be5e864SMauro Carvalho Chehab 
2194be5e864SMauro Carvalho Chehab err:
2204be5e864SMauro Carvalho Chehab 	return status;
2214be5e864SMauro Carvalho Chehab }
2224be5e864SMauro Carvalho Chehab 
secocec_tx_done(struct cec_adapter * adap,u16 status_val)2234be5e864SMauro Carvalho Chehab static void secocec_tx_done(struct cec_adapter *adap, u16 status_val)
2244be5e864SMauro Carvalho Chehab {
2254be5e864SMauro Carvalho Chehab 	if (status_val & SECOCEC_STATUS_TX_ERROR_MASK) {
2264be5e864SMauro Carvalho Chehab 		if (status_val & SECOCEC_STATUS_TX_NACK_ERROR)
2274be5e864SMauro Carvalho Chehab 			cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK);
2284be5e864SMauro Carvalho Chehab 		else
2294be5e864SMauro Carvalho Chehab 			cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR);
2304be5e864SMauro Carvalho Chehab 	} else {
2314be5e864SMauro Carvalho Chehab 		cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
2324be5e864SMauro Carvalho Chehab 	}
2334be5e864SMauro Carvalho Chehab 
2344be5e864SMauro Carvalho Chehab 	/* Reset status reg */
2354be5e864SMauro Carvalho Chehab 	status_val = SECOCEC_STATUS_TX_ERROR_MASK |
2364be5e864SMauro Carvalho Chehab 		SECOCEC_STATUS_MSG_SENT_MASK |
2374be5e864SMauro Carvalho Chehab 		SECOCEC_STATUS_TX_NACK_ERROR;
2384be5e864SMauro Carvalho Chehab 	smb_wr16(SECOCEC_STATUS, status_val);
2394be5e864SMauro Carvalho Chehab }
2404be5e864SMauro Carvalho Chehab 
secocec_rx_done(struct cec_adapter * adap,u16 status_val)2414be5e864SMauro Carvalho Chehab static void secocec_rx_done(struct cec_adapter *adap, u16 status_val)
2424be5e864SMauro Carvalho Chehab {
2434be5e864SMauro Carvalho Chehab 	struct secocec_data *cec = cec_get_drvdata(adap);
2444be5e864SMauro Carvalho Chehab 	struct device *dev = cec->dev;
2454be5e864SMauro Carvalho Chehab 	struct cec_msg msg = { };
2464be5e864SMauro Carvalho Chehab 	bool flag_overflow = false;
2474be5e864SMauro Carvalho Chehab 	u8 payload_len, i = 0;
2484be5e864SMauro Carvalho Chehab 	u8 *payload_msg;
2494be5e864SMauro Carvalho Chehab 	u16 val = 0;
2504be5e864SMauro Carvalho Chehab 	int status;
2514be5e864SMauro Carvalho Chehab 
2524be5e864SMauro Carvalho Chehab 	if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) {
2534be5e864SMauro Carvalho Chehab 		/* NOTE: Untested, it also might not be necessary */
254cf4a3ab5SEttore Chimenti 		dev_warn(dev, "Received more than 16 bytes. Discarding\n");
2554be5e864SMauro Carvalho Chehab 		flag_overflow = true;
2564be5e864SMauro Carvalho Chehab 	}
2574be5e864SMauro Carvalho Chehab 
2584be5e864SMauro Carvalho Chehab 	if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) {
259cf4a3ab5SEttore Chimenti 		dev_warn(dev, "Message received with errors. Discarding\n");
2604be5e864SMauro Carvalho Chehab 		status = -EIO;
2614be5e864SMauro Carvalho Chehab 		goto rxerr;
2624be5e864SMauro Carvalho Chehab 	}
2634be5e864SMauro Carvalho Chehab 
2644be5e864SMauro Carvalho Chehab 	/* Read message length */
2654be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
2664be5e864SMauro Carvalho Chehab 	if (status)
2674be5e864SMauro Carvalho Chehab 		return;
2684be5e864SMauro Carvalho Chehab 
2694be5e864SMauro Carvalho Chehab 	/* Device msg len already accounts for the header */
2704be5e864SMauro Carvalho Chehab 	msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
2714be5e864SMauro Carvalho Chehab 
2724be5e864SMauro Carvalho Chehab 	/* Read logical address */
2734be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_READ_BYTE0, &val);
2744be5e864SMauro Carvalho Chehab 	if (status)
2754be5e864SMauro Carvalho Chehab 		return;
2764be5e864SMauro Carvalho Chehab 
2774be5e864SMauro Carvalho Chehab 	/* device stores source LA and destination */
2784be5e864SMauro Carvalho Chehab 	msg.msg[0] = val;
2794be5e864SMauro Carvalho Chehab 
2804be5e864SMauro Carvalho Chehab 	/* Read operation ID */
2814be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
2824be5e864SMauro Carvalho Chehab 	if (status)
2834be5e864SMauro Carvalho Chehab 		return;
2844be5e864SMauro Carvalho Chehab 
2854be5e864SMauro Carvalho Chehab 	msg.msg[1] = val;
2864be5e864SMauro Carvalho Chehab 
2874be5e864SMauro Carvalho Chehab 	/* Read data if present */
2884be5e864SMauro Carvalho Chehab 	if (msg.len > 1) {
2894be5e864SMauro Carvalho Chehab 		payload_len = msg.len - 2;
2904be5e864SMauro Carvalho Chehab 		payload_msg = &msg.msg[2];
2914be5e864SMauro Carvalho Chehab 
2924be5e864SMauro Carvalho Chehab 		/* device stores 2 bytes in every 16-bit val */
2934be5e864SMauro Carvalho Chehab 		for (i = 0; i < payload_len; i += 2) {
2944be5e864SMauro Carvalho Chehab 			status = smb_rd16(SECOCEC_READ_DATA_00 + i / 2, &val);
2954be5e864SMauro Carvalho Chehab 			if (status)
2964be5e864SMauro Carvalho Chehab 				return;
2974be5e864SMauro Carvalho Chehab 
2984be5e864SMauro Carvalho Chehab 			/* low byte, skipping header */
2994be5e864SMauro Carvalho Chehab 			payload_msg[i] = val & 0x00ff;
3004be5e864SMauro Carvalho Chehab 
3014be5e864SMauro Carvalho Chehab 			/* hi byte */
3024be5e864SMauro Carvalho Chehab 			payload_msg[i + 1] = (val & 0xff00) >> 8;
3034be5e864SMauro Carvalho Chehab 		}
3044be5e864SMauro Carvalho Chehab 	}
3054be5e864SMauro Carvalho Chehab 
3064be5e864SMauro Carvalho Chehab 	cec_received_msg(cec->cec_adap, &msg);
3074be5e864SMauro Carvalho Chehab 
3084be5e864SMauro Carvalho Chehab 	/* Reset status reg */
3094be5e864SMauro Carvalho Chehab 	status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK;
3104be5e864SMauro Carvalho Chehab 	if (flag_overflow)
3114be5e864SMauro Carvalho Chehab 		status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
3124be5e864SMauro Carvalho Chehab 
3134be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_STATUS, status_val);
3144be5e864SMauro Carvalho Chehab 
3154be5e864SMauro Carvalho Chehab 	return;
3164be5e864SMauro Carvalho Chehab 
3174be5e864SMauro Carvalho Chehab rxerr:
3184be5e864SMauro Carvalho Chehab 	/* Reset error reg */
3194be5e864SMauro Carvalho Chehab 	status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK |
3204be5e864SMauro Carvalho Chehab 		SECOCEC_STATUS_RX_ERROR_MASK;
3214be5e864SMauro Carvalho Chehab 	if (flag_overflow)
3224be5e864SMauro Carvalho Chehab 		status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
3234be5e864SMauro Carvalho Chehab 	smb_wr16(SECOCEC_STATUS, status_val);
3244be5e864SMauro Carvalho Chehab }
3254be5e864SMauro Carvalho Chehab 
3264be5e864SMauro Carvalho Chehab static const struct cec_adap_ops secocec_cec_adap_ops = {
3274be5e864SMauro Carvalho Chehab 	/* Low-level callbacks */
3284be5e864SMauro Carvalho Chehab 	.adap_enable = secocec_adap_enable,
3294be5e864SMauro Carvalho Chehab 	.adap_log_addr = secocec_adap_log_addr,
3304be5e864SMauro Carvalho Chehab 	.adap_transmit = secocec_adap_transmit,
3314be5e864SMauro Carvalho Chehab };
3324be5e864SMauro Carvalho Chehab 
333df823a82SMauro Carvalho Chehab #ifdef CONFIG_CEC_SECO_RC
secocec_ir_probe(void * priv)3344be5e864SMauro Carvalho Chehab static int secocec_ir_probe(void *priv)
3354be5e864SMauro Carvalho Chehab {
3364be5e864SMauro Carvalho Chehab 	struct secocec_data *cec = priv;
3374be5e864SMauro Carvalho Chehab 	struct device *dev = cec->dev;
3384be5e864SMauro Carvalho Chehab 	int status;
3394be5e864SMauro Carvalho Chehab 	u16 val;
3404be5e864SMauro Carvalho Chehab 
3414be5e864SMauro Carvalho Chehab 	/* Prepare the RC input device */
3424be5e864SMauro Carvalho Chehab 	cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
3434be5e864SMauro Carvalho Chehab 	if (!cec->ir)
3444be5e864SMauro Carvalho Chehab 		return -ENOMEM;
3454be5e864SMauro Carvalho Chehab 
3464be5e864SMauro Carvalho Chehab 	snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
3474be5e864SMauro Carvalho Chehab 		 "%s/input0", dev_name(dev));
3484be5e864SMauro Carvalho Chehab 
3494be5e864SMauro Carvalho Chehab 	cec->ir->device_name = dev_name(dev);
3504be5e864SMauro Carvalho Chehab 	cec->ir->input_phys = cec->ir_input_phys;
3514be5e864SMauro Carvalho Chehab 	cec->ir->input_id.bustype = BUS_HOST;
3524be5e864SMauro Carvalho Chehab 	cec->ir->input_id.vendor = 0;
3534be5e864SMauro Carvalho Chehab 	cec->ir->input_id.product = 0;
3544be5e864SMauro Carvalho Chehab 	cec->ir->input_id.version = 1;
3554be5e864SMauro Carvalho Chehab 	cec->ir->driver_name = SECOCEC_DEV_NAME;
3564be5e864SMauro Carvalho Chehab 	cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
3574be5e864SMauro Carvalho Chehab 	cec->ir->priv = cec;
3584be5e864SMauro Carvalho Chehab 	cec->ir->map_name = RC_MAP_HAUPPAUGE;
359528222d8SSean Young 	cec->ir->timeout = MS_TO_US(100);
3604be5e864SMauro Carvalho Chehab 
3614be5e864SMauro Carvalho Chehab 	/* Clear the status register */
3624be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
3634be5e864SMauro Carvalho Chehab 	if (status != 0)
3644be5e864SMauro Carvalho Chehab 		goto err;
3654be5e864SMauro Carvalho Chehab 
3664be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
3674be5e864SMauro Carvalho Chehab 	if (status != 0)
3684be5e864SMauro Carvalho Chehab 		goto err;
3694be5e864SMauro Carvalho Chehab 
3704be5e864SMauro Carvalho Chehab 	/* Enable the interrupts */
3714be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
3724be5e864SMauro Carvalho Chehab 	if (status != 0)
3734be5e864SMauro Carvalho Chehab 		goto err;
3744be5e864SMauro Carvalho Chehab 
3754be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_ENABLE_REG_1,
3764be5e864SMauro Carvalho Chehab 			  val | SECOCEC_ENABLE_REG_1_IR);
3774be5e864SMauro Carvalho Chehab 	if (status != 0)
3784be5e864SMauro Carvalho Chehab 		goto err;
3794be5e864SMauro Carvalho Chehab 
380cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "IR enabled\n");
3814be5e864SMauro Carvalho Chehab 
3824be5e864SMauro Carvalho Chehab 	status = devm_rc_register_device(dev, cec->ir);
3834be5e864SMauro Carvalho Chehab 
3844be5e864SMauro Carvalho Chehab 	if (status) {
385cf4a3ab5SEttore Chimenti 		dev_err(dev, "Failed to prepare input device\n");
3864be5e864SMauro Carvalho Chehab 		cec->ir = NULL;
3874be5e864SMauro Carvalho Chehab 		goto err;
3884be5e864SMauro Carvalho Chehab 	}
3894be5e864SMauro Carvalho Chehab 
3904be5e864SMauro Carvalho Chehab 	return 0;
3914be5e864SMauro Carvalho Chehab 
3924be5e864SMauro Carvalho Chehab err:
3934be5e864SMauro Carvalho Chehab 	smb_rd16(SECOCEC_ENABLE_REG_1, &val);
3944be5e864SMauro Carvalho Chehab 
3954be5e864SMauro Carvalho Chehab 	smb_wr16(SECOCEC_ENABLE_REG_1,
3964be5e864SMauro Carvalho Chehab 		 val & ~SECOCEC_ENABLE_REG_1_IR);
3974be5e864SMauro Carvalho Chehab 
398cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "IR disabled\n");
3994be5e864SMauro Carvalho Chehab 	return status;
4004be5e864SMauro Carvalho Chehab }
4014be5e864SMauro Carvalho Chehab 
secocec_ir_rx(struct secocec_data * priv)4024be5e864SMauro Carvalho Chehab static int secocec_ir_rx(struct secocec_data *priv)
4034be5e864SMauro Carvalho Chehab {
4044be5e864SMauro Carvalho Chehab 	struct secocec_data *cec = priv;
4054be5e864SMauro Carvalho Chehab 	struct device *dev = cec->dev;
4064be5e864SMauro Carvalho Chehab 	u16 val, status, key, addr, toggle;
4074be5e864SMauro Carvalho Chehab 
4084be5e864SMauro Carvalho Chehab 	if (!cec->ir)
4094be5e864SMauro Carvalho Chehab 		return -ENODEV;
4104be5e864SMauro Carvalho Chehab 
4114be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
4124be5e864SMauro Carvalho Chehab 	if (status != 0)
4134be5e864SMauro Carvalho Chehab 		goto err;
4144be5e864SMauro Carvalho Chehab 
4154be5e864SMauro Carvalho Chehab 	key = val & SECOCEC_IR_COMMAND_MASK;
4164be5e864SMauro Carvalho Chehab 	addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
4174be5e864SMauro Carvalho Chehab 	toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
4184be5e864SMauro Carvalho Chehab 
4194be5e864SMauro Carvalho Chehab 	rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
4204be5e864SMauro Carvalho Chehab 
421cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x\n", key,
4224be5e864SMauro Carvalho Chehab 		addr, toggle);
4234be5e864SMauro Carvalho Chehab 
4244be5e864SMauro Carvalho Chehab 	return 0;
4254be5e864SMauro Carvalho Chehab 
4264be5e864SMauro Carvalho Chehab err:
427cf4a3ab5SEttore Chimenti 	dev_err(dev, "IR Receive message failed (%d)\n", status);
4284be5e864SMauro Carvalho Chehab 	return -EIO;
4294be5e864SMauro Carvalho Chehab }
4304be5e864SMauro Carvalho Chehab #else
secocec_ir_rx(struct secocec_data * priv)4314be5e864SMauro Carvalho Chehab static void secocec_ir_rx(struct secocec_data *priv)
4324be5e864SMauro Carvalho Chehab {
4334be5e864SMauro Carvalho Chehab }
4344be5e864SMauro Carvalho Chehab 
secocec_ir_probe(void * priv)4354be5e864SMauro Carvalho Chehab static int secocec_ir_probe(void *priv)
4364be5e864SMauro Carvalho Chehab {
4374be5e864SMauro Carvalho Chehab 	return 0;
4384be5e864SMauro Carvalho Chehab }
4394be5e864SMauro Carvalho Chehab #endif
4404be5e864SMauro Carvalho Chehab 
secocec_irq_handler(int irq,void * priv)4414be5e864SMauro Carvalho Chehab static irqreturn_t secocec_irq_handler(int irq, void *priv)
4424be5e864SMauro Carvalho Chehab {
4434be5e864SMauro Carvalho Chehab 	struct secocec_data *cec = priv;
4444be5e864SMauro Carvalho Chehab 	struct device *dev = cec->dev;
4454be5e864SMauro Carvalho Chehab 	u16 status_val, cec_val, val = 0;
4464be5e864SMauro Carvalho Chehab 	int status;
4474be5e864SMauro Carvalho Chehab 
4484be5e864SMauro Carvalho Chehab 	/*  Read status register */
4494be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_STATUS_REG_1, &status_val);
4504be5e864SMauro Carvalho Chehab 	if (status)
4514be5e864SMauro Carvalho Chehab 		goto err;
4524be5e864SMauro Carvalho Chehab 
4534be5e864SMauro Carvalho Chehab 	if (status_val & SECOCEC_STATUS_REG_1_CEC) {
4544be5e864SMauro Carvalho Chehab 		/* Read CEC status register */
4554be5e864SMauro Carvalho Chehab 		status = smb_rd16(SECOCEC_STATUS, &cec_val);
4564be5e864SMauro Carvalho Chehab 		if (status)
4574be5e864SMauro Carvalho Chehab 			goto err;
4584be5e864SMauro Carvalho Chehab 
4594be5e864SMauro Carvalho Chehab 		if (cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK)
4604be5e864SMauro Carvalho Chehab 			secocec_rx_done(cec->cec_adap, cec_val);
4614be5e864SMauro Carvalho Chehab 
4624be5e864SMauro Carvalho Chehab 		if (cec_val & SECOCEC_STATUS_MSG_SENT_MASK)
4634be5e864SMauro Carvalho Chehab 			secocec_tx_done(cec->cec_adap, cec_val);
4644be5e864SMauro Carvalho Chehab 
4654be5e864SMauro Carvalho Chehab 		if ((~cec_val & SECOCEC_STATUS_MSG_SENT_MASK) &&
4664be5e864SMauro Carvalho Chehab 		    (~cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK))
4674be5e864SMauro Carvalho Chehab 			dev_warn_once(dev,
4684be5e864SMauro Carvalho Chehab 				      "Message not received or sent, but interrupt fired");
4694be5e864SMauro Carvalho Chehab 
4704be5e864SMauro Carvalho Chehab 		val = SECOCEC_STATUS_REG_1_CEC;
4714be5e864SMauro Carvalho Chehab 	}
4724be5e864SMauro Carvalho Chehab 
4734be5e864SMauro Carvalho Chehab 	if (status_val & SECOCEC_STATUS_REG_1_IR) {
4744be5e864SMauro Carvalho Chehab 		val |= SECOCEC_STATUS_REG_1_IR;
4754be5e864SMauro Carvalho Chehab 
4764be5e864SMauro Carvalho Chehab 		secocec_ir_rx(cec);
4774be5e864SMauro Carvalho Chehab 	}
4784be5e864SMauro Carvalho Chehab 
4794be5e864SMauro Carvalho Chehab 	/*  Reset status register */
4804be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
4814be5e864SMauro Carvalho Chehab 	if (status)
4824be5e864SMauro Carvalho Chehab 		goto err;
4834be5e864SMauro Carvalho Chehab 
4844be5e864SMauro Carvalho Chehab 	return IRQ_HANDLED;
4854be5e864SMauro Carvalho Chehab 
4864be5e864SMauro Carvalho Chehab err:
487cf4a3ab5SEttore Chimenti 	dev_err_once(dev, "IRQ: R/W SMBus operation failed %d\n", status);
4884be5e864SMauro Carvalho Chehab 
4894be5e864SMauro Carvalho Chehab 	/*  Reset status register */
4904be5e864SMauro Carvalho Chehab 	val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR;
4914be5e864SMauro Carvalho Chehab 	smb_wr16(SECOCEC_STATUS_REG_1, val);
4924be5e864SMauro Carvalho Chehab 
4934be5e864SMauro Carvalho Chehab 	return IRQ_HANDLED;
4944be5e864SMauro Carvalho Chehab }
4954be5e864SMauro Carvalho Chehab 
4964be5e864SMauro Carvalho Chehab struct cec_dmi_match {
4974be5e864SMauro Carvalho Chehab 	const char *sys_vendor;
4984be5e864SMauro Carvalho Chehab 	const char *product_name;
4994be5e864SMauro Carvalho Chehab 	const char *devname;
5004be5e864SMauro Carvalho Chehab 	const char *conn;
5014be5e864SMauro Carvalho Chehab };
5024be5e864SMauro Carvalho Chehab 
5034be5e864SMauro Carvalho Chehab static const struct cec_dmi_match secocec_dmi_match_table[] = {
5044be5e864SMauro Carvalho Chehab 	/* UDOO X86 */
5054be5e864SMauro Carvalho Chehab 	{ "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
5064be5e864SMauro Carvalho Chehab };
5074be5e864SMauro Carvalho Chehab 
secocec_cec_find_hdmi_dev(struct device * dev,const char ** conn)5084be5e864SMauro Carvalho Chehab static struct device *secocec_cec_find_hdmi_dev(struct device *dev,
5094be5e864SMauro Carvalho Chehab 						const char **conn)
5104be5e864SMauro Carvalho Chehab {
5114be5e864SMauro Carvalho Chehab 	int i;
5124be5e864SMauro Carvalho Chehab 
5134be5e864SMauro Carvalho Chehab 	for (i = 0 ; i < ARRAY_SIZE(secocec_dmi_match_table) ; ++i) {
5144be5e864SMauro Carvalho Chehab 		const struct cec_dmi_match *m = &secocec_dmi_match_table[i];
5154be5e864SMauro Carvalho Chehab 
5164be5e864SMauro Carvalho Chehab 		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
5174be5e864SMauro Carvalho Chehab 		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
5184be5e864SMauro Carvalho Chehab 			struct device *d;
5194be5e864SMauro Carvalho Chehab 
5204be5e864SMauro Carvalho Chehab 			/* Find the device, bail out if not yet registered */
5214be5e864SMauro Carvalho Chehab 			d = bus_find_device_by_name(&pci_bus_type, NULL,
5224be5e864SMauro Carvalho Chehab 						    m->devname);
5234be5e864SMauro Carvalho Chehab 			if (!d)
5244be5e864SMauro Carvalho Chehab 				return ERR_PTR(-EPROBE_DEFER);
5254be5e864SMauro Carvalho Chehab 
5264be5e864SMauro Carvalho Chehab 			put_device(d);
5274be5e864SMauro Carvalho Chehab 			*conn = m->conn;
5284be5e864SMauro Carvalho Chehab 			return d;
5294be5e864SMauro Carvalho Chehab 		}
5304be5e864SMauro Carvalho Chehab 	}
5314be5e864SMauro Carvalho Chehab 
5324be5e864SMauro Carvalho Chehab 	return ERR_PTR(-EINVAL);
5334be5e864SMauro Carvalho Chehab }
5344be5e864SMauro Carvalho Chehab 
secocec_acpi_probe(struct secocec_data * sdev)5354be5e864SMauro Carvalho Chehab static int secocec_acpi_probe(struct secocec_data *sdev)
5364be5e864SMauro Carvalho Chehab {
5374be5e864SMauro Carvalho Chehab 	struct device *dev = sdev->dev;
5384be5e864SMauro Carvalho Chehab 	struct gpio_desc *gpio;
5394be5e864SMauro Carvalho Chehab 	int irq = 0;
5404be5e864SMauro Carvalho Chehab 
5410bff66dcSLinus Walleij 	gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
5424be5e864SMauro Carvalho Chehab 	if (IS_ERR(gpio)) {
543cf4a3ab5SEttore Chimenti 		dev_err(dev, "Cannot request interrupt gpio\n");
5444be5e864SMauro Carvalho Chehab 		return PTR_ERR(gpio);
5454be5e864SMauro Carvalho Chehab 	}
5464be5e864SMauro Carvalho Chehab 
5474be5e864SMauro Carvalho Chehab 	irq = gpiod_to_irq(gpio);
5484be5e864SMauro Carvalho Chehab 	if (irq < 0) {
549cf4a3ab5SEttore Chimenti 		dev_err(dev, "Cannot find valid irq\n");
5504be5e864SMauro Carvalho Chehab 		return -ENODEV;
5514be5e864SMauro Carvalho Chehab 	}
552cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "irq-gpio is bound to IRQ %d\n", irq);
5534be5e864SMauro Carvalho Chehab 
5544be5e864SMauro Carvalho Chehab 	sdev->irq = irq;
5554be5e864SMauro Carvalho Chehab 
5564be5e864SMauro Carvalho Chehab 	return 0;
5574be5e864SMauro Carvalho Chehab }
5584be5e864SMauro Carvalho Chehab 
secocec_probe(struct platform_device * pdev)5594be5e864SMauro Carvalho Chehab static int secocec_probe(struct platform_device *pdev)
5604be5e864SMauro Carvalho Chehab {
5614be5e864SMauro Carvalho Chehab 	struct secocec_data *secocec;
5624be5e864SMauro Carvalho Chehab 	struct device *dev = &pdev->dev;
5634be5e864SMauro Carvalho Chehab 	struct device *hdmi_dev;
5644be5e864SMauro Carvalho Chehab 	const char *conn = NULL;
5654be5e864SMauro Carvalho Chehab 	int ret;
5664be5e864SMauro Carvalho Chehab 	u16 val;
5674be5e864SMauro Carvalho Chehab 
5684be5e864SMauro Carvalho Chehab 	hdmi_dev = secocec_cec_find_hdmi_dev(&pdev->dev, &conn);
5694be5e864SMauro Carvalho Chehab 	if (IS_ERR(hdmi_dev))
5704be5e864SMauro Carvalho Chehab 		return PTR_ERR(hdmi_dev);
5714be5e864SMauro Carvalho Chehab 
5724be5e864SMauro Carvalho Chehab 	secocec = devm_kzalloc(dev, sizeof(*secocec), GFP_KERNEL);
5734be5e864SMauro Carvalho Chehab 	if (!secocec)
5744be5e864SMauro Carvalho Chehab 		return -ENOMEM;
5754be5e864SMauro Carvalho Chehab 
5764be5e864SMauro Carvalho Chehab 	dev_set_drvdata(dev, secocec);
5774be5e864SMauro Carvalho Chehab 
5784be5e864SMauro Carvalho Chehab 	/* Request SMBus regions */
5794be5e864SMauro Carvalho Chehab 	if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) {
580cf4a3ab5SEttore Chimenti 		dev_err(dev, "Request memory region failed\n");
5814be5e864SMauro Carvalho Chehab 		return -ENXIO;
5824be5e864SMauro Carvalho Chehab 	}
5834be5e864SMauro Carvalho Chehab 
5844be5e864SMauro Carvalho Chehab 	secocec->pdev = pdev;
5854be5e864SMauro Carvalho Chehab 	secocec->dev = dev;
5864be5e864SMauro Carvalho Chehab 
5874be5e864SMauro Carvalho Chehab 	if (!has_acpi_companion(dev)) {
588cf4a3ab5SEttore Chimenti 		dev_dbg(dev, "Cannot find any ACPI companion\n");
5894be5e864SMauro Carvalho Chehab 		ret = -ENODEV;
5904be5e864SMauro Carvalho Chehab 		goto err;
5914be5e864SMauro Carvalho Chehab 	}
5924be5e864SMauro Carvalho Chehab 
5934be5e864SMauro Carvalho Chehab 	ret = secocec_acpi_probe(secocec);
5944be5e864SMauro Carvalho Chehab 	if (ret) {
595cf4a3ab5SEttore Chimenti 		dev_err(dev, "Cannot assign gpio to IRQ\n");
5964be5e864SMauro Carvalho Chehab 		ret = -ENODEV;
5974be5e864SMauro Carvalho Chehab 		goto err;
5984be5e864SMauro Carvalho Chehab 	}
5994be5e864SMauro Carvalho Chehab 
6004be5e864SMauro Carvalho Chehab 	/* Firmware version check */
6014be5e864SMauro Carvalho Chehab 	ret = smb_rd16(SECOCEC_VERSION, &val);
6024be5e864SMauro Carvalho Chehab 	if (ret) {
603cf4a3ab5SEttore Chimenti 		dev_err(dev, "Cannot check fw version\n");
6044be5e864SMauro Carvalho Chehab 		goto err;
6054be5e864SMauro Carvalho Chehab 	}
6064be5e864SMauro Carvalho Chehab 	if (val < SECOCEC_LATEST_FW) {
607cf4a3ab5SEttore Chimenti 		dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x\n",
6084be5e864SMauro Carvalho Chehab 			val, SECOCEC_LATEST_FW);
6094be5e864SMauro Carvalho Chehab 		ret = -EINVAL;
6104be5e864SMauro Carvalho Chehab 		goto err;
6114be5e864SMauro Carvalho Chehab 	}
6124be5e864SMauro Carvalho Chehab 
6134be5e864SMauro Carvalho Chehab 	ret = devm_request_threaded_irq(dev,
6144be5e864SMauro Carvalho Chehab 					secocec->irq,
6154be5e864SMauro Carvalho Chehab 					NULL,
6164be5e864SMauro Carvalho Chehab 					secocec_irq_handler,
6174be5e864SMauro Carvalho Chehab 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
6184be5e864SMauro Carvalho Chehab 					dev_name(&pdev->dev), secocec);
6194be5e864SMauro Carvalho Chehab 
6204be5e864SMauro Carvalho Chehab 	if (ret) {
621cf4a3ab5SEttore Chimenti 		dev_err(dev, "Cannot request IRQ %d\n", secocec->irq);
6224be5e864SMauro Carvalho Chehab 		ret = -EIO;
6234be5e864SMauro Carvalho Chehab 		goto err;
6244be5e864SMauro Carvalho Chehab 	}
6254be5e864SMauro Carvalho Chehab 
6264be5e864SMauro Carvalho Chehab 	/* Allocate CEC adapter */
6274be5e864SMauro Carvalho Chehab 	secocec->cec_adap = cec_allocate_adapter(&secocec_cec_adap_ops,
6284be5e864SMauro Carvalho Chehab 						 secocec,
6294be5e864SMauro Carvalho Chehab 						 dev_name(dev),
6304be5e864SMauro Carvalho Chehab 						 CEC_CAP_DEFAULTS |
6314be5e864SMauro Carvalho Chehab 						 CEC_CAP_CONNECTOR_INFO,
6324be5e864SMauro Carvalho Chehab 						 SECOCEC_MAX_ADDRS);
6334be5e864SMauro Carvalho Chehab 
6344be5e864SMauro Carvalho Chehab 	if (IS_ERR(secocec->cec_adap)) {
6354be5e864SMauro Carvalho Chehab 		ret = PTR_ERR(secocec->cec_adap);
6364be5e864SMauro Carvalho Chehab 		goto err;
6374be5e864SMauro Carvalho Chehab 	}
6384be5e864SMauro Carvalho Chehab 
6394be5e864SMauro Carvalho Chehab 	secocec->notifier = cec_notifier_cec_adap_register(hdmi_dev, conn,
6404be5e864SMauro Carvalho Chehab 							   secocec->cec_adap);
6414be5e864SMauro Carvalho Chehab 	if (!secocec->notifier) {
6424be5e864SMauro Carvalho Chehab 		ret = -ENOMEM;
6434be5e864SMauro Carvalho Chehab 		goto err_delete_adapter;
6444be5e864SMauro Carvalho Chehab 	}
6454be5e864SMauro Carvalho Chehab 
6464be5e864SMauro Carvalho Chehab 	ret = cec_register_adapter(secocec->cec_adap, dev);
6474be5e864SMauro Carvalho Chehab 	if (ret)
6484be5e864SMauro Carvalho Chehab 		goto err_notifier;
6494be5e864SMauro Carvalho Chehab 
6504be5e864SMauro Carvalho Chehab 	ret = secocec_ir_probe(secocec);
6514be5e864SMauro Carvalho Chehab 	if (ret)
6524be5e864SMauro Carvalho Chehab 		goto err_notifier;
6534be5e864SMauro Carvalho Chehab 
6544be5e864SMauro Carvalho Chehab 	platform_set_drvdata(pdev, secocec);
6554be5e864SMauro Carvalho Chehab 
656cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "Device registered\n");
6574be5e864SMauro Carvalho Chehab 
6584be5e864SMauro Carvalho Chehab 	return ret;
6594be5e864SMauro Carvalho Chehab 
6604be5e864SMauro Carvalho Chehab err_notifier:
6614be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
6624be5e864SMauro Carvalho Chehab err_delete_adapter:
6634be5e864SMauro Carvalho Chehab 	cec_delete_adapter(secocec->cec_adap);
6644be5e864SMauro Carvalho Chehab err:
6654be5e864SMauro Carvalho Chehab 	release_region(BRA_SMB_BASE_ADDR, 7);
6664be5e864SMauro Carvalho Chehab 	dev_err(dev, "%s device probe failed\n", dev_name(dev));
6674be5e864SMauro Carvalho Chehab 
6684be5e864SMauro Carvalho Chehab 	return ret;
6694be5e864SMauro Carvalho Chehab }
6704be5e864SMauro Carvalho Chehab 
secocec_remove(struct platform_device * pdev)671*ba878edaSUwe Kleine-König static void secocec_remove(struct platform_device *pdev)
6724be5e864SMauro Carvalho Chehab {
6734be5e864SMauro Carvalho Chehab 	struct secocec_data *secocec = platform_get_drvdata(pdev);
6744be5e864SMauro Carvalho Chehab 	u16 val;
6754be5e864SMauro Carvalho Chehab 
6764be5e864SMauro Carvalho Chehab 	if (secocec->ir) {
6774be5e864SMauro Carvalho Chehab 		smb_rd16(SECOCEC_ENABLE_REG_1, &val);
6784be5e864SMauro Carvalho Chehab 
6794be5e864SMauro Carvalho Chehab 		smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
6804be5e864SMauro Carvalho Chehab 
681cf4a3ab5SEttore Chimenti 		dev_dbg(&pdev->dev, "IR disabled\n");
6824be5e864SMauro Carvalho Chehab 	}
6834be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
6844be5e864SMauro Carvalho Chehab 	cec_unregister_adapter(secocec->cec_adap);
6854be5e864SMauro Carvalho Chehab 
6864be5e864SMauro Carvalho Chehab 	release_region(BRA_SMB_BASE_ADDR, 7);
6874be5e864SMauro Carvalho Chehab 
688cf4a3ab5SEttore Chimenti 	dev_dbg(&pdev->dev, "CEC device removed\n");
6894be5e864SMauro Carvalho Chehab }
6904be5e864SMauro Carvalho Chehab 
6914be5e864SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP
secocec_suspend(struct device * dev)6924be5e864SMauro Carvalho Chehab static int secocec_suspend(struct device *dev)
6934be5e864SMauro Carvalho Chehab {
6944be5e864SMauro Carvalho Chehab 	int status;
6954be5e864SMauro Carvalho Chehab 	u16 val;
6964be5e864SMauro Carvalho Chehab 
697cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "Device going to suspend, disabling\n");
6984be5e864SMauro Carvalho Chehab 
6994be5e864SMauro Carvalho Chehab 	/* Clear the status register */
7004be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
7014be5e864SMauro Carvalho Chehab 	if (status)
7024be5e864SMauro Carvalho Chehab 		goto err;
7034be5e864SMauro Carvalho Chehab 
7044be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
7054be5e864SMauro Carvalho Chehab 	if (status)
7064be5e864SMauro Carvalho Chehab 		goto err;
7074be5e864SMauro Carvalho Chehab 
7084be5e864SMauro Carvalho Chehab 	/* Disable the interrupts */
7094be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
7104be5e864SMauro Carvalho Chehab 	if (status)
7114be5e864SMauro Carvalho Chehab 		goto err;
7124be5e864SMauro Carvalho Chehab 
7134be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
7144be5e864SMauro Carvalho Chehab 			  ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
7154be5e864SMauro Carvalho Chehab 	if (status)
7164be5e864SMauro Carvalho Chehab 		goto err;
7174be5e864SMauro Carvalho Chehab 
7184be5e864SMauro Carvalho Chehab 	return 0;
7194be5e864SMauro Carvalho Chehab 
7204be5e864SMauro Carvalho Chehab err:
721cf4a3ab5SEttore Chimenti 	dev_err(dev, "Suspend failed: %d\n", status);
7224be5e864SMauro Carvalho Chehab 	return status;
7234be5e864SMauro Carvalho Chehab }
7244be5e864SMauro Carvalho Chehab 
secocec_resume(struct device * dev)7254be5e864SMauro Carvalho Chehab static int secocec_resume(struct device *dev)
7264be5e864SMauro Carvalho Chehab {
7274be5e864SMauro Carvalho Chehab 	int status;
7284be5e864SMauro Carvalho Chehab 	u16 val;
7294be5e864SMauro Carvalho Chehab 
730cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "Resuming device from suspend\n");
7314be5e864SMauro Carvalho Chehab 
7324be5e864SMauro Carvalho Chehab 	/* Clear the status register */
7334be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
7344be5e864SMauro Carvalho Chehab 	if (status)
7354be5e864SMauro Carvalho Chehab 		goto err;
7364be5e864SMauro Carvalho Chehab 
7374be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_STATUS_REG_1, val);
7384be5e864SMauro Carvalho Chehab 	if (status)
7394be5e864SMauro Carvalho Chehab 		goto err;
7404be5e864SMauro Carvalho Chehab 
7414be5e864SMauro Carvalho Chehab 	/* Enable the interrupts */
7424be5e864SMauro Carvalho Chehab 	status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
7434be5e864SMauro Carvalho Chehab 	if (status)
7444be5e864SMauro Carvalho Chehab 		goto err;
7454be5e864SMauro Carvalho Chehab 
7464be5e864SMauro Carvalho Chehab 	status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
7474be5e864SMauro Carvalho Chehab 	if (status)
7484be5e864SMauro Carvalho Chehab 		goto err;
7494be5e864SMauro Carvalho Chehab 
750cf4a3ab5SEttore Chimenti 	dev_dbg(dev, "Device resumed from suspend\n");
7514be5e864SMauro Carvalho Chehab 
7524be5e864SMauro Carvalho Chehab 	return 0;
7534be5e864SMauro Carvalho Chehab 
7544be5e864SMauro Carvalho Chehab err:
755cf4a3ab5SEttore Chimenti 	dev_err(dev, "Resume failed: %d\n", status);
7564be5e864SMauro Carvalho Chehab 	return status;
7574be5e864SMauro Carvalho Chehab }
7584be5e864SMauro Carvalho Chehab 
7594be5e864SMauro Carvalho Chehab static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
7604be5e864SMauro Carvalho Chehab #define SECOCEC_PM_OPS (&secocec_pm_ops)
7614be5e864SMauro Carvalho Chehab #else
7624be5e864SMauro Carvalho Chehab #define SECOCEC_PM_OPS NULL
7634be5e864SMauro Carvalho Chehab #endif
7644be5e864SMauro Carvalho Chehab 
7654be5e864SMauro Carvalho Chehab #ifdef CONFIG_ACPI
7664be5e864SMauro Carvalho Chehab static const struct acpi_device_id secocec_acpi_match[] = {
7674be5e864SMauro Carvalho Chehab 	{"CEC00001", 0},
7684be5e864SMauro Carvalho Chehab 	{},
7694be5e864SMauro Carvalho Chehab };
7704be5e864SMauro Carvalho Chehab 
7714be5e864SMauro Carvalho Chehab MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
7724be5e864SMauro Carvalho Chehab #endif
7734be5e864SMauro Carvalho Chehab 
7744be5e864SMauro Carvalho Chehab static struct platform_driver secocec_driver = {
7754be5e864SMauro Carvalho Chehab 	.driver = {
7764be5e864SMauro Carvalho Chehab 		   .name = SECOCEC_DEV_NAME,
7774be5e864SMauro Carvalho Chehab 		   .acpi_match_table = ACPI_PTR(secocec_acpi_match),
7784be5e864SMauro Carvalho Chehab 		   .pm = SECOCEC_PM_OPS,
7794be5e864SMauro Carvalho Chehab 	},
7804be5e864SMauro Carvalho Chehab 	.probe = secocec_probe,
781*ba878edaSUwe Kleine-König 	.remove_new = secocec_remove,
7824be5e864SMauro Carvalho Chehab };
7834be5e864SMauro Carvalho Chehab 
7844be5e864SMauro Carvalho Chehab module_platform_driver(secocec_driver);
7854be5e864SMauro Carvalho Chehab 
7864be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("SECO CEC X86 Driver");
7874be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
7884be5e864SMauro Carvalho Chehab MODULE_LICENSE("Dual BSD/GPL");
789