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