0001
0002
0003
0004
0005
0006
0007
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
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
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
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
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
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
0122 status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
0123 status = smb_wr16(SECOCEC_STATUS_REG_1, val);
0124
0125
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
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
0155
0156
0157 status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
0158 if (status)
0159 return status;
0160
0161
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
0179 payload_id_len = msg->len - 1;
0180
0181
0182 status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
0183 if (status)
0184 goto err;
0185
0186
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
0193 if (payload_id_len > 1) {
0194
0195 payload_len = msg->len - 2;
0196 payload_msg = &msg->msg[2];
0197
0198
0199 for (i = 0; i < payload_len; i += 2) {
0200
0201 val = payload_msg[i + 1] << 8;
0202
0203
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
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
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
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
0265 status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
0266 if (status)
0267 return;
0268
0269
0270 msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
0271
0272
0273 status = smb_rd16(SECOCEC_READ_BYTE0, &val);
0274 if (status)
0275 return;
0276
0277
0278 msg.msg[0] = val;
0279
0280
0281 status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
0282 if (status)
0283 return;
0284
0285 msg.msg[1] = val;
0286
0287
0288 if (msg.len > 1) {
0289 payload_len = msg.len - 2;
0290 payload_msg = &msg.msg[2];
0291
0292
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
0299 payload_msg[i] = val & 0x00ff;
0300
0301
0302 payload_msg[i + 1] = (val & 0xff00) >> 8;
0303 }
0304 }
0305
0306 cec_received_msg(cec->cec_adap, &msg);
0307
0308
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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");