Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * CEC driver for SECO X86 Boards
0004  *
0005  * Author:  Ettore Chimenti <ek5.chimenti@gmail.com>
0006  * Copyright (C) 2018, SECO SpA.
0007  * Copyright (C) 2018, Aidilab Srl.
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/acpi.h>
0012 #include <linux/delay.h>
0013 #include <linux/dmi.h>
0014 #include <linux/gpio/consumer.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/pci.h>
0017 #include <linux/platform_device.h>
0018 
0019 /* CEC Framework */
0020 #include <media/cec-notifier.h>
0021 
0022 #include "seco-cec.h"
0023 
0024 struct secocec_data {
0025     struct device *dev;
0026     struct platform_device *pdev;
0027     struct cec_adapter *cec_adap;
0028     struct cec_notifier *notifier;
0029     struct rc_dev *ir;
0030     char ir_input_phys[32];
0031     int irq;
0032 };
0033 
0034 #define smb_wr16(cmd, data) smb_word_op(SECOCEC_MICRO_ADDRESS, \
0035                     cmd, data, SMBUS_WRITE, NULL)
0036 #define smb_rd16(cmd, res) smb_word_op(SECOCEC_MICRO_ADDRESS, \
0037                        cmd, 0, SMBUS_READ, res)
0038 
0039 static int smb_word_op(u16 slave_addr, u8 cmd, u16 data,
0040                u8 operation, u16 *result)
0041 {
0042     unsigned int count;
0043     int status = 0;
0044 
0045     /* Active wait until ready */
0046     for (count = 0; count <= SMBTIMEOUT; ++count) {
0047         if (!(inb(HSTS) & BRA_INUSE_STS))
0048             break;
0049         udelay(SMB_POLL_UDELAY);
0050     }
0051 
0052     if (count > SMBTIMEOUT)
0053         /* Reset the lock instead of failing */
0054         outb(0xff, HSTS);
0055 
0056     outb(0x00, HCNT);
0057     outb((u8)(slave_addr & 0xfe) | operation, XMIT_SLVA);
0058     outb(cmd, HCMD);
0059     inb(HCNT);
0060 
0061     if (operation == SMBUS_WRITE) {
0062         outb((u8)data, HDAT0);
0063         outb((u8)(data >> 8), HDAT1);
0064     }
0065 
0066     outb(BRA_START + BRA_SMB_CMD_WORD_DATA, HCNT);
0067 
0068     for (count = 0; count <= SMBTIMEOUT; count++) {
0069         if (!(inb(HSTS) & BRA_HOST_BUSY))
0070             break;
0071         udelay(SMB_POLL_UDELAY);
0072     }
0073 
0074     if (count > SMBTIMEOUT) {
0075         status = -EBUSY;
0076         goto err;
0077     }
0078 
0079     if (inb(HSTS) & BRA_HSTS_ERR_MASK) {
0080         status = -EIO;
0081         goto err;
0082     }
0083 
0084     if (operation == SMBUS_READ)
0085         *result = ((inb(HDAT0) & 0xff) + ((inb(HDAT1) & 0xff) << 8));
0086 
0087 err:
0088     outb(0xff, HSTS);
0089     return status;
0090 }
0091 
0092 static int secocec_adap_enable(struct cec_adapter *adap, bool enable)
0093 {
0094     struct secocec_data *cec = cec_get_drvdata(adap);
0095     struct device *dev = cec->dev;
0096     u16 val = 0;
0097     int status;
0098 
0099     if (enable) {
0100         /* Clear the status register */
0101         status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0102         if (status)
0103             goto err;
0104 
0105         status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0106         if (status)
0107             goto err;
0108 
0109         /* Enable the interrupts */
0110         status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0111         if (status)
0112             goto err;
0113 
0114         status = smb_wr16(SECOCEC_ENABLE_REG_1,
0115                   val | SECOCEC_ENABLE_REG_1_CEC);
0116         if (status)
0117             goto err;
0118 
0119         dev_dbg(dev, "Device enabled\n");
0120     } else {
0121         /* Clear the status register */
0122         status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0123         status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0124 
0125         /* Disable the interrupts */
0126         status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0127         status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
0128                   ~SECOCEC_ENABLE_REG_1_CEC &
0129                   ~SECOCEC_ENABLE_REG_1_IR);
0130 
0131         dev_dbg(dev, "Device disabled\n");
0132     }
0133 
0134     return 0;
0135 err:
0136     return status;
0137 }
0138 
0139 static int secocec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
0140 {
0141     u16 enable_val = 0;
0142     int status;
0143 
0144     /* Disable device */
0145     status = smb_rd16(SECOCEC_ENABLE_REG_1, &enable_val);
0146     if (status)
0147         return status;
0148 
0149     status = smb_wr16(SECOCEC_ENABLE_REG_1,
0150               enable_val & ~SECOCEC_ENABLE_REG_1_CEC);
0151     if (status)
0152         return status;
0153 
0154     /* Write logical address
0155      * NOTE: CEC_LOG_ADDR_INVALID is mapped to the 'Unregistered' LA
0156      */
0157     status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
0158     if (status)
0159         return status;
0160 
0161     /* Re-enable device */
0162     status = smb_wr16(SECOCEC_ENABLE_REG_1,
0163               enable_val | SECOCEC_ENABLE_REG_1_CEC);
0164     if (status)
0165         return status;
0166 
0167     return 0;
0168 }
0169 
0170 static int secocec_adap_transmit(struct cec_adapter *adap, u8 attempts,
0171                  u32 signal_free_time, struct cec_msg *msg)
0172 {
0173     u16 payload_len, payload_id_len, destination, val = 0;
0174     u8 *payload_msg;
0175     int status;
0176     u8 i;
0177 
0178     /* Device msg len already accounts for header */
0179     payload_id_len = msg->len - 1;
0180 
0181     /* Send data length */
0182     status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
0183     if (status)
0184         goto err;
0185 
0186     /* Send Operation ID if present */
0187     if (payload_id_len > 0) {
0188         status = smb_wr16(SECOCEC_WRITE_OPERATION_ID, msg->msg[1]);
0189         if (status)
0190             goto err;
0191     }
0192     /* Send data if present */
0193     if (payload_id_len > 1) {
0194         /* Only data; */
0195         payload_len = msg->len - 2;
0196         payload_msg = &msg->msg[2];
0197 
0198         /* Copy message into registers */
0199         for (i = 0; i < payload_len; i += 2) {
0200             /* hi byte */
0201             val = payload_msg[i + 1] << 8;
0202 
0203             /* lo byte */
0204             val |= payload_msg[i];
0205 
0206             status = smb_wr16(SECOCEC_WRITE_DATA_00 + i / 2, val);
0207             if (status)
0208                 goto err;
0209         }
0210     }
0211     /* Send msg source/destination and fire msg */
0212     destination = msg->msg[0];
0213     status = smb_wr16(SECOCEC_WRITE_BYTE0, destination);
0214     if (status)
0215         goto err;
0216 
0217     return 0;
0218 
0219 err:
0220     return status;
0221 }
0222 
0223 static void secocec_tx_done(struct cec_adapter *adap, u16 status_val)
0224 {
0225     if (status_val & SECOCEC_STATUS_TX_ERROR_MASK) {
0226         if (status_val & SECOCEC_STATUS_TX_NACK_ERROR)
0227             cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK);
0228         else
0229             cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR);
0230     } else {
0231         cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
0232     }
0233 
0234     /* Reset status reg */
0235     status_val = SECOCEC_STATUS_TX_ERROR_MASK |
0236         SECOCEC_STATUS_MSG_SENT_MASK |
0237         SECOCEC_STATUS_TX_NACK_ERROR;
0238     smb_wr16(SECOCEC_STATUS, status_val);
0239 }
0240 
0241 static void secocec_rx_done(struct cec_adapter *adap, u16 status_val)
0242 {
0243     struct secocec_data *cec = cec_get_drvdata(adap);
0244     struct device *dev = cec->dev;
0245     struct cec_msg msg = { };
0246     bool flag_overflow = false;
0247     u8 payload_len, i = 0;
0248     u8 *payload_msg;
0249     u16 val = 0;
0250     int status;
0251 
0252     if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) {
0253         /* NOTE: Untested, it also might not be necessary */
0254         dev_warn(dev, "Received more than 16 bytes. Discarding\n");
0255         flag_overflow = true;
0256     }
0257 
0258     if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) {
0259         dev_warn(dev, "Message received with errors. Discarding\n");
0260         status = -EIO;
0261         goto rxerr;
0262     }
0263 
0264     /* Read message length */
0265     status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
0266     if (status)
0267         return;
0268 
0269     /* Device msg len already accounts for the header */
0270     msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
0271 
0272     /* Read logical address */
0273     status = smb_rd16(SECOCEC_READ_BYTE0, &val);
0274     if (status)
0275         return;
0276 
0277     /* device stores source LA and destination */
0278     msg.msg[0] = val;
0279 
0280     /* Read operation ID */
0281     status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
0282     if (status)
0283         return;
0284 
0285     msg.msg[1] = val;
0286 
0287     /* Read data if present */
0288     if (msg.len > 1) {
0289         payload_len = msg.len - 2;
0290         payload_msg = &msg.msg[2];
0291 
0292         /* device stores 2 bytes in every 16-bit val */
0293         for (i = 0; i < payload_len; i += 2) {
0294             status = smb_rd16(SECOCEC_READ_DATA_00 + i / 2, &val);
0295             if (status)
0296                 return;
0297 
0298             /* low byte, skipping header */
0299             payload_msg[i] = val & 0x00ff;
0300 
0301             /* hi byte */
0302             payload_msg[i + 1] = (val & 0xff00) >> 8;
0303         }
0304     }
0305 
0306     cec_received_msg(cec->cec_adap, &msg);
0307 
0308     /* Reset status reg */
0309     status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK;
0310     if (flag_overflow)
0311         status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
0312 
0313     status = smb_wr16(SECOCEC_STATUS, status_val);
0314 
0315     return;
0316 
0317 rxerr:
0318     /* Reset error reg */
0319     status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK |
0320         SECOCEC_STATUS_RX_ERROR_MASK;
0321     if (flag_overflow)
0322         status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
0323     smb_wr16(SECOCEC_STATUS, status_val);
0324 }
0325 
0326 static const struct cec_adap_ops secocec_cec_adap_ops = {
0327     /* Low-level callbacks */
0328     .adap_enable = secocec_adap_enable,
0329     .adap_log_addr = secocec_adap_log_addr,
0330     .adap_transmit = secocec_adap_transmit,
0331 };
0332 
0333 #ifdef CONFIG_CEC_SECO_RC
0334 static int secocec_ir_probe(void *priv)
0335 {
0336     struct secocec_data *cec = priv;
0337     struct device *dev = cec->dev;
0338     int status;
0339     u16 val;
0340 
0341     /* Prepare the RC input device */
0342     cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
0343     if (!cec->ir)
0344         return -ENOMEM;
0345 
0346     snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
0347          "%s/input0", dev_name(dev));
0348 
0349     cec->ir->device_name = dev_name(dev);
0350     cec->ir->input_phys = cec->ir_input_phys;
0351     cec->ir->input_id.bustype = BUS_HOST;
0352     cec->ir->input_id.vendor = 0;
0353     cec->ir->input_id.product = 0;
0354     cec->ir->input_id.version = 1;
0355     cec->ir->driver_name = SECOCEC_DEV_NAME;
0356     cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
0357     cec->ir->priv = cec;
0358     cec->ir->map_name = RC_MAP_HAUPPAUGE;
0359     cec->ir->timeout = MS_TO_US(100);
0360 
0361     /* Clear the status register */
0362     status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0363     if (status != 0)
0364         goto err;
0365 
0366     status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0367     if (status != 0)
0368         goto err;
0369 
0370     /* Enable the interrupts */
0371     status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0372     if (status != 0)
0373         goto err;
0374 
0375     status = smb_wr16(SECOCEC_ENABLE_REG_1,
0376               val | SECOCEC_ENABLE_REG_1_IR);
0377     if (status != 0)
0378         goto err;
0379 
0380     dev_dbg(dev, "IR enabled\n");
0381 
0382     status = devm_rc_register_device(dev, cec->ir);
0383 
0384     if (status) {
0385         dev_err(dev, "Failed to prepare input device\n");
0386         cec->ir = NULL;
0387         goto err;
0388     }
0389 
0390     return 0;
0391 
0392 err:
0393     smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0394 
0395     smb_wr16(SECOCEC_ENABLE_REG_1,
0396          val & ~SECOCEC_ENABLE_REG_1_IR);
0397 
0398     dev_dbg(dev, "IR disabled\n");
0399     return status;
0400 }
0401 
0402 static int secocec_ir_rx(struct secocec_data *priv)
0403 {
0404     struct secocec_data *cec = priv;
0405     struct device *dev = cec->dev;
0406     u16 val, status, key, addr, toggle;
0407 
0408     if (!cec->ir)
0409         return -ENODEV;
0410 
0411     status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
0412     if (status != 0)
0413         goto err;
0414 
0415     key = val & SECOCEC_IR_COMMAND_MASK;
0416     addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
0417     toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
0418 
0419     rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
0420 
0421     dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x\n", key,
0422         addr, toggle);
0423 
0424     return 0;
0425 
0426 err:
0427     dev_err(dev, "IR Receive message failed (%d)\n", status);
0428     return -EIO;
0429 }
0430 #else
0431 static void secocec_ir_rx(struct secocec_data *priv)
0432 {
0433 }
0434 
0435 static int secocec_ir_probe(void *priv)
0436 {
0437     return 0;
0438 }
0439 #endif
0440 
0441 static irqreturn_t secocec_irq_handler(int irq, void *priv)
0442 {
0443     struct secocec_data *cec = priv;
0444     struct device *dev = cec->dev;
0445     u16 status_val, cec_val, val = 0;
0446     int status;
0447 
0448     /*  Read status register */
0449     status = smb_rd16(SECOCEC_STATUS_REG_1, &status_val);
0450     if (status)
0451         goto err;
0452 
0453     if (status_val & SECOCEC_STATUS_REG_1_CEC) {
0454         /* Read CEC status register */
0455         status = smb_rd16(SECOCEC_STATUS, &cec_val);
0456         if (status)
0457             goto err;
0458 
0459         if (cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK)
0460             secocec_rx_done(cec->cec_adap, cec_val);
0461 
0462         if (cec_val & SECOCEC_STATUS_MSG_SENT_MASK)
0463             secocec_tx_done(cec->cec_adap, cec_val);
0464 
0465         if ((~cec_val & SECOCEC_STATUS_MSG_SENT_MASK) &&
0466             (~cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK))
0467             dev_warn_once(dev,
0468                       "Message not received or sent, but interrupt fired");
0469 
0470         val = SECOCEC_STATUS_REG_1_CEC;
0471     }
0472 
0473     if (status_val & SECOCEC_STATUS_REG_1_IR) {
0474         val |= SECOCEC_STATUS_REG_1_IR;
0475 
0476         secocec_ir_rx(cec);
0477     }
0478 
0479     /*  Reset status register */
0480     status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0481     if (status)
0482         goto err;
0483 
0484     return IRQ_HANDLED;
0485 
0486 err:
0487     dev_err_once(dev, "IRQ: R/W SMBus operation failed %d\n", status);
0488 
0489     /*  Reset status register */
0490     val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR;
0491     smb_wr16(SECOCEC_STATUS_REG_1, val);
0492 
0493     return IRQ_HANDLED;
0494 }
0495 
0496 struct cec_dmi_match {
0497     const char *sys_vendor;
0498     const char *product_name;
0499     const char *devname;
0500     const char *conn;
0501 };
0502 
0503 static const struct cec_dmi_match secocec_dmi_match_table[] = {
0504     /* UDOO X86 */
0505     { "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
0506 };
0507 
0508 static struct device *secocec_cec_find_hdmi_dev(struct device *dev,
0509                         const char **conn)
0510 {
0511     int i;
0512 
0513     for (i = 0 ; i < ARRAY_SIZE(secocec_dmi_match_table) ; ++i) {
0514         const struct cec_dmi_match *m = &secocec_dmi_match_table[i];
0515 
0516         if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
0517             dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
0518             struct device *d;
0519 
0520             /* Find the device, bail out if not yet registered */
0521             d = bus_find_device_by_name(&pci_bus_type, NULL,
0522                             m->devname);
0523             if (!d)
0524                 return ERR_PTR(-EPROBE_DEFER);
0525 
0526             put_device(d);
0527             *conn = m->conn;
0528             return d;
0529         }
0530     }
0531 
0532     return ERR_PTR(-EINVAL);
0533 }
0534 
0535 static int secocec_acpi_probe(struct secocec_data *sdev)
0536 {
0537     struct device *dev = sdev->dev;
0538     struct gpio_desc *gpio;
0539     int irq = 0;
0540 
0541     gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
0542     if (IS_ERR(gpio)) {
0543         dev_err(dev, "Cannot request interrupt gpio\n");
0544         return PTR_ERR(gpio);
0545     }
0546 
0547     irq = gpiod_to_irq(gpio);
0548     if (irq < 0) {
0549         dev_err(dev, "Cannot find valid irq\n");
0550         return -ENODEV;
0551     }
0552     dev_dbg(dev, "irq-gpio is bound to IRQ %d\n", irq);
0553 
0554     sdev->irq = irq;
0555 
0556     return 0;
0557 }
0558 
0559 static int secocec_probe(struct platform_device *pdev)
0560 {
0561     struct secocec_data *secocec;
0562     struct device *dev = &pdev->dev;
0563     struct device *hdmi_dev;
0564     const char *conn = NULL;
0565     int ret;
0566     u16 val;
0567 
0568     hdmi_dev = secocec_cec_find_hdmi_dev(&pdev->dev, &conn);
0569     if (IS_ERR(hdmi_dev))
0570         return PTR_ERR(hdmi_dev);
0571 
0572     secocec = devm_kzalloc(dev, sizeof(*secocec), GFP_KERNEL);
0573     if (!secocec)
0574         return -ENOMEM;
0575 
0576     dev_set_drvdata(dev, secocec);
0577 
0578     /* Request SMBus regions */
0579     if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) {
0580         dev_err(dev, "Request memory region failed\n");
0581         return -ENXIO;
0582     }
0583 
0584     secocec->pdev = pdev;
0585     secocec->dev = dev;
0586 
0587     if (!has_acpi_companion(dev)) {
0588         dev_dbg(dev, "Cannot find any ACPI companion\n");
0589         ret = -ENODEV;
0590         goto err;
0591     }
0592 
0593     ret = secocec_acpi_probe(secocec);
0594     if (ret) {
0595         dev_err(dev, "Cannot assign gpio to IRQ\n");
0596         ret = -ENODEV;
0597         goto err;
0598     }
0599 
0600     /* Firmware version check */
0601     ret = smb_rd16(SECOCEC_VERSION, &val);
0602     if (ret) {
0603         dev_err(dev, "Cannot check fw version\n");
0604         goto err;
0605     }
0606     if (val < SECOCEC_LATEST_FW) {
0607         dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x\n",
0608             val, SECOCEC_LATEST_FW);
0609         ret = -EINVAL;
0610         goto err;
0611     }
0612 
0613     ret = devm_request_threaded_irq(dev,
0614                     secocec->irq,
0615                     NULL,
0616                     secocec_irq_handler,
0617                     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
0618                     dev_name(&pdev->dev), secocec);
0619 
0620     if (ret) {
0621         dev_err(dev, "Cannot request IRQ %d\n", secocec->irq);
0622         ret = -EIO;
0623         goto err;
0624     }
0625 
0626     /* Allocate CEC adapter */
0627     secocec->cec_adap = cec_allocate_adapter(&secocec_cec_adap_ops,
0628                          secocec,
0629                          dev_name(dev),
0630                          CEC_CAP_DEFAULTS |
0631                          CEC_CAP_CONNECTOR_INFO,
0632                          SECOCEC_MAX_ADDRS);
0633 
0634     if (IS_ERR(secocec->cec_adap)) {
0635         ret = PTR_ERR(secocec->cec_adap);
0636         goto err;
0637     }
0638 
0639     secocec->notifier = cec_notifier_cec_adap_register(hdmi_dev, conn,
0640                                secocec->cec_adap);
0641     if (!secocec->notifier) {
0642         ret = -ENOMEM;
0643         goto err_delete_adapter;
0644     }
0645 
0646     ret = cec_register_adapter(secocec->cec_adap, dev);
0647     if (ret)
0648         goto err_notifier;
0649 
0650     ret = secocec_ir_probe(secocec);
0651     if (ret)
0652         goto err_notifier;
0653 
0654     platform_set_drvdata(pdev, secocec);
0655 
0656     dev_dbg(dev, "Device registered\n");
0657 
0658     return ret;
0659 
0660 err_notifier:
0661     cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
0662 err_delete_adapter:
0663     cec_delete_adapter(secocec->cec_adap);
0664 err:
0665     release_region(BRA_SMB_BASE_ADDR, 7);
0666     dev_err(dev, "%s device probe failed\n", dev_name(dev));
0667 
0668     return ret;
0669 }
0670 
0671 static int secocec_remove(struct platform_device *pdev)
0672 {
0673     struct secocec_data *secocec = platform_get_drvdata(pdev);
0674     u16 val;
0675 
0676     if (secocec->ir) {
0677         smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0678 
0679         smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
0680 
0681         dev_dbg(&pdev->dev, "IR disabled\n");
0682     }
0683     cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
0684     cec_unregister_adapter(secocec->cec_adap);
0685 
0686     release_region(BRA_SMB_BASE_ADDR, 7);
0687 
0688     dev_dbg(&pdev->dev, "CEC device removed\n");
0689 
0690     return 0;
0691 }
0692 
0693 #ifdef CONFIG_PM_SLEEP
0694 static int secocec_suspend(struct device *dev)
0695 {
0696     int status;
0697     u16 val;
0698 
0699     dev_dbg(dev, "Device going to suspend, disabling\n");
0700 
0701     /* Clear the status register */
0702     status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0703     if (status)
0704         goto err;
0705 
0706     status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0707     if (status)
0708         goto err;
0709 
0710     /* Disable the interrupts */
0711     status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0712     if (status)
0713         goto err;
0714 
0715     status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
0716               ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
0717     if (status)
0718         goto err;
0719 
0720     return 0;
0721 
0722 err:
0723     dev_err(dev, "Suspend failed: %d\n", status);
0724     return status;
0725 }
0726 
0727 static int secocec_resume(struct device *dev)
0728 {
0729     int status;
0730     u16 val;
0731 
0732     dev_dbg(dev, "Resuming device from suspend\n");
0733 
0734     /* Clear the status register */
0735     status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0736     if (status)
0737         goto err;
0738 
0739     status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0740     if (status)
0741         goto err;
0742 
0743     /* Enable the interrupts */
0744     status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
0745     if (status)
0746         goto err;
0747 
0748     status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
0749     if (status)
0750         goto err;
0751 
0752     dev_dbg(dev, "Device resumed from suspend\n");
0753 
0754     return 0;
0755 
0756 err:
0757     dev_err(dev, "Resume failed: %d\n", status);
0758     return status;
0759 }
0760 
0761 static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
0762 #define SECOCEC_PM_OPS (&secocec_pm_ops)
0763 #else
0764 #define SECOCEC_PM_OPS NULL
0765 #endif
0766 
0767 #ifdef CONFIG_ACPI
0768 static const struct acpi_device_id secocec_acpi_match[] = {
0769     {"CEC00001", 0},
0770     {},
0771 };
0772 
0773 MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
0774 #endif
0775 
0776 static struct platform_driver secocec_driver = {
0777     .driver = {
0778            .name = SECOCEC_DEV_NAME,
0779            .acpi_match_table = ACPI_PTR(secocec_acpi_match),
0780            .pm = SECOCEC_PM_OPS,
0781     },
0782     .probe = secocec_probe,
0783     .remove = secocec_remove,
0784 };
0785 
0786 module_platform_driver(secocec_driver);
0787 
0788 MODULE_DESCRIPTION("SECO CEC X86 Driver");
0789 MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
0790 MODULE_LICENSE("Dual BSD/GPL");