cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

seco-cec.c (17601B)


      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
     24struct 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
     39static 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
     87err:
     88	outb(0xff, HSTS);
     89	return status;
     90}
     91
     92static 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;
    135err:
    136	return status;
    137}
    138
    139static 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
    170static 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
    219err:
    220	return status;
    221}
    222
    223static 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
    241static 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
    317rxerr:
    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
    326static 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
    334static 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
    392err:
    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
    402static 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
    426err:
    427	dev_err(dev, "IR Receive message failed (%d)\n", status);
    428	return -EIO;
    429}
    430#else
    431static void secocec_ir_rx(struct secocec_data *priv)
    432{
    433}
    434
    435static int secocec_ir_probe(void *priv)
    436{
    437	return 0;
    438}
    439#endif
    440
    441static 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
    486err:
    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
    496struct cec_dmi_match {
    497	const char *sys_vendor;
    498	const char *product_name;
    499	const char *devname;
    500	const char *conn;
    501};
    502
    503static const struct cec_dmi_match secocec_dmi_match_table[] = {
    504	/* UDOO X86 */
    505	{ "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
    506};
    507
    508static 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
    535static 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
    559static 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
    660err_notifier:
    661	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
    662err_delete_adapter:
    663	cec_delete_adapter(secocec->cec_adap);
    664err:
    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
    671static 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
    694static 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
    722err:
    723	dev_err(dev, "Suspend failed: %d\n", status);
    724	return status;
    725}
    726
    727static 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
    756err:
    757	dev_err(dev, "Resume failed: %d\n", status);
    758	return status;
    759}
    760
    761static 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
    768static const struct acpi_device_id secocec_acpi_match[] = {
    769	{"CEC00001", 0},
    770	{},
    771};
    772
    773MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
    774#endif
    775
    776static 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
    786module_platform_driver(secocec_driver);
    787
    788MODULE_DESCRIPTION("SECO CEC X86 Driver");
    789MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
    790MODULE_LICENSE("Dual BSD/GPL");