Merge branch 'work.mount3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[sfrench/cifs-2.6.git] / drivers / media / platform / seco-cec / seco-cec.c
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
25 struct 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
40 static 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
100 err:
101         outb(0xff, HSTS);
102         return status;
103 }
104
105 static 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;
148 err:
149         return status;
150 }
151
152 static 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
183 static 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
232 err:
233         return status;
234 }
235
236 static 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
254 static 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
330 rxerr:
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
339 static 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_VIDEO_SECO_RC
347 static 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_NS(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
405 err:
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
415 static 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
439 err:
440         dev_err(dev, "IR Receive message failed (%d)", status);
441         return -EIO;
442 }
443 #else
444 static void secocec_ir_rx(struct secocec_data *priv)
445 {
446 }
447
448 static int secocec_ir_probe(void *priv)
449 {
450         return 0;
451 }
452 #endif
453
454 static 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
499 err:
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
509 struct cec_dmi_match {
510         const char *sys_vendor;
511         const char *product_name;
512         const char *devname;
513         const char *conn;
514 };
515
516 static const struct cec_dmi_match secocec_dmi_match_table[] = {
517         /* UDOO X86 */
518         { "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
519 };
520
521 static 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
548 static 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
572 static 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
673 err_notifier:
674         cec_notifier_cec_adap_unregister(secocec->notifier);
675 err_delete_adapter:
676         cec_delete_adapter(secocec->cec_adap);
677 err:
678         dev_err(dev, "%s device probe failed\n", dev_name(dev));
679
680         return ret;
681 }
682
683 static int secocec_remove(struct platform_device *pdev)
684 {
685         struct secocec_data *secocec = platform_get_drvdata(pdev);
686         u16 val;
687
688         if (secocec->ir) {
689                 smb_rd16(SECOCEC_ENABLE_REG_1, &val);
690
691                 smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
692
693                 dev_dbg(&pdev->dev, "IR disabled");
694         }
695         cec_notifier_cec_adap_unregister(secocec->notifier);
696         cec_unregister_adapter(secocec->cec_adap);
697
698         release_region(BRA_SMB_BASE_ADDR, 7);
699
700         dev_dbg(&pdev->dev, "CEC device removed");
701
702         return 0;
703 }
704
705 #ifdef CONFIG_PM_SLEEP
706 static int secocec_suspend(struct device *dev)
707 {
708         int status;
709         u16 val;
710
711         dev_dbg(dev, "Device going to suspend, disabling");
712
713         /* Clear the status register */
714         status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
715         if (status)
716                 goto err;
717
718         status = smb_wr16(SECOCEC_STATUS_REG_1, val);
719         if (status)
720                 goto err;
721
722         /* Disable the interrupts */
723         status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
724         if (status)
725                 goto err;
726
727         status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
728                           ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
729         if (status)
730                 goto err;
731
732         return 0;
733
734 err:
735         dev_err(dev, "Suspend failed (err: %d)", status);
736         return status;
737 }
738
739 static int secocec_resume(struct device *dev)
740 {
741         int status;
742         u16 val;
743
744         dev_dbg(dev, "Resuming device from suspend");
745
746         /* Clear the status register */
747         status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
748         if (status)
749                 goto err;
750
751         status = smb_wr16(SECOCEC_STATUS_REG_1, val);
752         if (status)
753                 goto err;
754
755         /* Enable the interrupts */
756         status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
757         if (status)
758                 goto err;
759
760         status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
761         if (status)
762                 goto err;
763
764         dev_dbg(dev, "Device resumed from suspend");
765
766         return 0;
767
768 err:
769         dev_err(dev, "Resume failed (err: %d)", status);
770         return status;
771 }
772
773 static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
774 #define SECOCEC_PM_OPS (&secocec_pm_ops)
775 #else
776 #define SECOCEC_PM_OPS NULL
777 #endif
778
779 #ifdef CONFIG_ACPI
780 static const struct acpi_device_id secocec_acpi_match[] = {
781         {"CEC00001", 0},
782         {},
783 };
784
785 MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
786 #endif
787
788 static struct platform_driver secocec_driver = {
789         .driver = {
790                    .name = SECOCEC_DEV_NAME,
791                    .acpi_match_table = ACPI_PTR(secocec_acpi_match),
792                    .pm = SECOCEC_PM_OPS,
793         },
794         .probe = secocec_probe,
795         .remove = secocec_remove,
796 };
797
798 module_platform_driver(secocec_driver);
799
800 MODULE_DESCRIPTION("SECO CEC X86 Driver");
801 MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
802 MODULE_LICENSE("Dual BSD/GPL");