aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel_pmc_ipc.c
diff options
context:
space:
mode:
authorqipeng.zha <qipeng.zha@intel.com>2015-06-26 12:32:15 -0400
committerDarren Hart <dvhart@linux.intel.com>2015-06-29 18:28:14 -0400
commit0a8b83530b6f67b9a50bd7937d57a5deea187b5b (patch)
tree30851cbb0eb824a75ccc4b7a0212a5403ad5b26f /drivers/platform/x86/intel_pmc_ipc.c
parent0d44b410a01a75daff60ce4cb78313135cdb3649 (diff)
intel_pmc_ipc: Add Intel Apollo Lake PMC IPC driver
This driver provides support for PMC control on Apollo Lake platforms. The PMC is an ARC processor which defines some IPC commands for communication with other entities in the CPU. Signed-off-by: qipeng.zha <qipeng.zha@intel.com> [fengguang.wu@intel.com: Fix Sparse and Cocinelle warnings] Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform/x86/intel_pmc_ipc.c')
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c767
1 files changed, 767 insertions, 0 deletions
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
new file mode 100644
index 000000000000..d734763dab69
--- /dev/null
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -0,0 +1,767 @@
1/*
2 * intel_pmc_ipc.c: Driver for the Intel PMC IPC mechanism
3 *
4 * (C) Copyright 2014-2015 Intel Corporation
5 *
6 * This driver is based on Intel SCU IPC driver(intel_scu_opc.c) by
7 * Sreedhara DS <sreedhara.ds@intel.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2
12 * of the License.
13 *
14 * PMC running in ARC processor communicates with other entity running in IA
15 * core through IPC mechanism which in turn messaging between IA core ad PMC.
16 */
17
18#include <linux/module.h>
19#include <linux/delay.h>
20#include <linux/errno.h>
21#include <linux/init.h>
22#include <linux/device.h>
23#include <linux/pm.h>
24#include <linux/pci.h>
25#include <linux/platform_device.h>
26#include <linux/interrupt.h>
27#include <linux/pm_qos.h>
28#include <linux/kernel.h>
29#include <linux/bitops.h>
30#include <linux/sched.h>
31#include <linux/atomic.h>
32#include <linux/notifier.h>
33#include <linux/suspend.h>
34#include <linux/acpi.h>
35#include <asm/intel_pmc_ipc.h>
36#include <linux/mfd/lpc_ich.h>
37
38/*
39 * IPC registers
40 * The IA write to IPC_CMD command register triggers an interrupt to the ARC,
41 * The ARC handles the interrupt and services it, writing optional data to
42 * the IPC1 registers, updates the IPC_STS response register with the status.
43 */
44#define IPC_CMD 0x0
45#define IPC_CMD_MSI 0x100
46#define IPC_CMD_SIZE 16
47#define IPC_CMD_SUBCMD 12
48#define IPC_STATUS 0x04
49#define IPC_STATUS_IRQ 0x4
50#define IPC_STATUS_ERR 0x2
51#define IPC_STATUS_BUSY 0x1
52#define IPC_SPTR 0x08
53#define IPC_DPTR 0x0C
54#define IPC_WRITE_BUFFER 0x80
55#define IPC_READ_BUFFER 0x90
56
57/*
58 * 16-byte buffer for sending data associated with IPC command.
59 */
60#define IPC_DATA_BUFFER_SIZE 16
61
62#define IPC_LOOP_CNT 3000000
63#define IPC_MAX_SEC 3
64
65#define IPC_TRIGGER_MODE_IRQ true
66
67/* exported resources from IFWI */
68#define PLAT_RESOURCE_IPC_INDEX 0
69#define PLAT_RESOURCE_IPC_SIZE 0x1000
70#define PLAT_RESOURCE_GCR_SIZE 0x1000
71#define PLAT_RESOURCE_PUNIT_DATA_INDEX 1
72#define PLAT_RESOURCE_PUNIT_INTER_INDEX 2
73#define PLAT_RESOURCE_ACPI_IO_INDEX 0
74
75/*
76 * BIOS does not create an ACPI device for each PMC function,
77 * but exports multiple resources from one ACPI device(IPC) for
78 * multiple functions. This driver is responsible to create a
79 * platform device and to export resources for those functions.
80 */
81#define TCO_DEVICE_NAME "iTCO_wdt"
82#define SMI_EN_OFFSET 0x30
83#define SMI_EN_SIZE 4
84#define TCO_BASE_OFFSET 0x60
85#define TCO_REGS_SIZE 16
86#define PUNIT_DEVICE_NAME "intel_punit_ipc"
87
88static const int iTCO_version = 3;
89
90static struct intel_pmc_ipc_dev {
91 struct device *dev;
92 void __iomem *ipc_base;
93 bool irq_mode;
94 int irq;
95 int cmd;
96 struct completion cmd_complete;
97
98 /* The following PMC BARs share the same ACPI device with the IPC */
99 void *acpi_io_base;
100 int acpi_io_size;
101 struct platform_device *tco_dev;
102
103 /* gcr */
104 void *gcr_base;
105 int gcr_size;
106
107 /* punit */
108 void *punit_base;
109 int punit_size;
110 void *punit_base2;
111 int punit_size2;
112 struct platform_device *punit_dev;
113} ipcdev;
114
115static char *ipc_err_sources[] = {
116 [IPC_ERR_NONE] =
117 "no error",
118 [IPC_ERR_CMD_NOT_SUPPORTED] =
119 "command not supported",
120 [IPC_ERR_CMD_NOT_SERVICED] =
121 "command not serviced",
122 [IPC_ERR_UNABLE_TO_SERVICE] =
123 "unable to service",
124 [IPC_ERR_CMD_INVALID] =
125 "command invalid",
126 [IPC_ERR_CMD_FAILED] =
127 "command failed",
128 [IPC_ERR_EMSECURITY] =
129 "Invalid Battery",
130 [IPC_ERR_UNSIGNEDKERNEL] =
131 "Unsigned kernel",
132};
133
134/* Prevent concurrent calls to the PMC */
135static DEFINE_MUTEX(ipclock);
136
137static inline void ipc_send_command(u32 cmd)
138{
139 ipcdev.cmd = cmd;
140 if (ipcdev.irq_mode) {
141 reinit_completion(&ipcdev.cmd_complete);
142 cmd |= IPC_CMD_MSI;
143 }
144 writel(cmd, ipcdev.ipc_base + IPC_CMD);
145}
146
147static inline u32 ipc_read_status(void)
148{
149 return readl(ipcdev.ipc_base + IPC_STATUS);
150}
151
152static inline void ipc_data_writel(u32 data, u32 offset)
153{
154 writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
155}
156
157static inline u8 ipc_data_readb(u32 offset)
158{
159 return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
160}
161
162static inline u32 ipc_data_readl(u32 offset)
163{
164 return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
165}
166
167static int intel_pmc_ipc_check_status(void)
168{
169 int status;
170 int ret = 0;
171
172 if (ipcdev.irq_mode) {
173 if (0 == wait_for_completion_timeout(
174 &ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
175 ret = -ETIMEDOUT;
176 } else {
177 int loop_count = IPC_LOOP_CNT;
178
179 while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
180 udelay(1);
181 if (loop_count == 0)
182 ret = -ETIMEDOUT;
183 }
184
185 status = ipc_read_status();
186 if (ret == -ETIMEDOUT) {
187 dev_err(ipcdev.dev,
188 "IPC timed out, TS=0x%x, CMD=0x%x\n",
189 status, ipcdev.cmd);
190 return ret;
191 }
192
193 if (status & IPC_STATUS_ERR) {
194 int i;
195
196 ret = -EIO;
197 i = (status >> IPC_CMD_SIZE) & 0xFF;
198 if (i < ARRAY_SIZE(ipc_err_sources))
199 dev_err(ipcdev.dev,
200 "IPC failed: %s, STS=0x%x, CMD=0x%x\n",
201 ipc_err_sources[i], status, ipcdev.cmd);
202 else
203 dev_err(ipcdev.dev,
204 "IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
205 status, ipcdev.cmd);
206 if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
207 ret = -EACCES;
208 }
209
210 return ret;
211}
212
213/*
214 * intel_pmc_ipc_simple_command
215 * @cmd: command
216 * @sub: sub type
217 */
218int intel_pmc_ipc_simple_command(int cmd, int sub)
219{
220 int ret;
221
222 mutex_lock(&ipclock);
223 if (ipcdev.dev == NULL) {
224 mutex_unlock(&ipclock);
225 return -ENODEV;
226 }
227 ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
228 ret = intel_pmc_ipc_check_status();
229 mutex_unlock(&ipclock);
230
231 return ret;
232}
233EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
234
235/*
236 * intel_pmc_ipc_raw_cmd
237 * @cmd: command
238 * @sub: sub type
239 * @in: input data
240 * @inlen: input length in bytes
241 * @out: output data
242 * @outlen: output length in dwords
243 * @sptr: data writing to SPTR register
244 * @dptr: data writing to DPTR register
245 */
246int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
247 u32 outlen, u32 dptr, u32 sptr)
248{
249 u32 wbuf[4] = { 0 };
250 int ret;
251 int i;
252
253 if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
254 return -EINVAL;
255
256 mutex_lock(&ipclock);
257 if (ipcdev.dev == NULL) {
258 mutex_unlock(&ipclock);
259 return -ENODEV;
260 }
261 memcpy(wbuf, in, inlen);
262 writel(dptr, ipcdev.ipc_base + IPC_DPTR);
263 writel(sptr, ipcdev.ipc_base + IPC_SPTR);
264 /* The input data register is 32bit register and inlen is in Byte */
265 for (i = 0; i < ((inlen + 3) / 4); i++)
266 ipc_data_writel(wbuf[i], 4 * i);
267 ipc_send_command((inlen << IPC_CMD_SIZE) |
268 (sub << IPC_CMD_SUBCMD) | cmd);
269 ret = intel_pmc_ipc_check_status();
270 if (!ret) {
271 /* out is read from 32bit register and outlen is in 32bit */
272 for (i = 0; i < outlen; i++)
273 *out++ = ipc_data_readl(4 * i);
274 }
275 mutex_unlock(&ipclock);
276
277 return ret;
278}
279EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
280
281/*
282 * intel_pmc_ipc_command
283 * @cmd: command
284 * @sub: sub type
285 * @in: input data
286 * @inlen: input length in bytes
287 * @out: output data
288 * @outlen: output length in dwords
289 */
290int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
291 u32 *out, u32 outlen)
292{
293 return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
294}
295EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
296
297static irqreturn_t ioc(int irq, void *dev_id)
298{
299 int status;
300
301 if (ipcdev.irq_mode) {
302 status = ipc_read_status();
303 writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
304 }
305 complete(&ipcdev.cmd_complete);
306
307 return IRQ_HANDLED;
308}
309
310static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
311{
312 resource_size_t pci_resource;
313 int ret;
314 int len;
315
316 ipcdev.dev = &pci_dev_get(pdev)->dev;
317 ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
318
319 ret = pci_enable_device(pdev);
320 if (ret)
321 return ret;
322
323 ret = pci_request_regions(pdev, "intel_pmc_ipc");
324 if (ret)
325 return ret;
326
327 pci_resource = pci_resource_start(pdev, 0);
328 len = pci_resource_len(pdev, 0);
329 if (!pci_resource || !len) {
330 dev_err(&pdev->dev, "Failed to get resource\n");
331 return -ENOMEM;
332 }
333
334 init_completion(&ipcdev.cmd_complete);
335
336 if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
337 dev_err(&pdev->dev, "Failed to request irq\n");
338 return -EBUSY;
339 }
340
341 ipcdev.ipc_base = ioremap_nocache(pci_resource, len);
342 if (!ipcdev.ipc_base) {
343 dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
344 free_irq(pdev->irq, &ipcdev);
345 ret = -ENOMEM;
346 }
347
348 return ret;
349}
350
351static void ipc_pci_remove(struct pci_dev *pdev)
352{
353 free_irq(pdev->irq, &ipcdev);
354 pci_release_regions(pdev);
355 pci_dev_put(pdev);
356 iounmap(ipcdev.ipc_base);
357 ipcdev.dev = NULL;
358}
359
360static const struct pci_device_id ipc_pci_ids[] = {
361 {PCI_VDEVICE(INTEL, 0x0a94), 0},
362 {PCI_VDEVICE(INTEL, 0x1a94), 0},
363 { 0,}
364};
365MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
366
367static struct pci_driver ipc_pci_driver = {
368 .name = "intel_pmc_ipc",
369 .id_table = ipc_pci_ids,
370 .probe = ipc_pci_probe,
371 .remove = ipc_pci_remove,
372};
373
374static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
375 struct device_attribute *attr,
376 const char *buf, size_t count)
377{
378 int subcmd;
379 int cmd;
380 int ret;
381
382 ret = sscanf(buf, "%d %d", &cmd, &subcmd);
383 if (ret != 2) {
384 dev_err(dev, "Error args\n");
385 return -EINVAL;
386 }
387
388 ret = intel_pmc_ipc_simple_command(cmd, subcmd);
389 if (ret) {
390 dev_err(dev, "command %d error with %d\n", cmd, ret);
391 return ret;
392 }
393 return (ssize_t)count;
394}
395
396static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
397 struct device_attribute *attr,
398 const char *buf, size_t count)
399{
400 unsigned long val;
401 int subcmd;
402 int ret;
403
404 if (kstrtoul(buf, 0, &val))
405 return -EINVAL;
406
407 if (val)
408 subcmd = 1;
409 else
410 subcmd = 0;
411 ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
412 if (ret) {
413 dev_err(dev, "command north %d error with %d\n", subcmd, ret);
414 return ret;
415 }
416 return (ssize_t)count;
417}
418
419static DEVICE_ATTR(simplecmd, S_IWUSR,
420 NULL, intel_pmc_ipc_simple_cmd_store);
421static DEVICE_ATTR(northpeak, S_IWUSR,
422 NULL, intel_pmc_ipc_northpeak_store);
423
424static struct attribute *intel_ipc_attrs[] = {
425 &dev_attr_northpeak.attr,
426 &dev_attr_simplecmd.attr,
427 NULL
428};
429
430static const struct attribute_group intel_ipc_group = {
431 .attrs = intel_ipc_attrs,
432};
433
434#define PUNIT_RESOURCE_INTER 1
435static struct resource punit_res[] = {
436 /* Punit */
437 {
438 .flags = IORESOURCE_MEM,
439 },
440 {
441 .flags = IORESOURCE_MEM,
442 },
443};
444
445#define TCO_RESOURCE_ACPI_IO 0
446#define TCO_RESOURCE_SMI_EN_IO 1
447#define TCO_RESOURCE_GCR_MEM 2
448static struct resource tco_res[] = {
449 /* ACPI - TCO */
450 {
451 .flags = IORESOURCE_IO,
452 },
453 /* ACPI - SMI */
454 {
455 .flags = IORESOURCE_IO,
456 },
457 /* GCS */
458 {
459 .flags = IORESOURCE_MEM,
460 },
461};
462
463static struct lpc_ich_info tco_info = {
464 .name = "Apollo Lake SoC",
465 .iTCO_version = 3,
466};
467
468static int ipc_create_punit_device(void)
469{
470 struct platform_device *pdev;
471 struct resource *res;
472 int ret;
473
474 pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1);
475 if (!pdev) {
476 dev_err(ipcdev.dev, "Failed to alloc punit platform device\n");
477 return -ENOMEM;
478 }
479
480 pdev->dev.parent = ipcdev.dev;
481
482 res = punit_res;
483 res->start = (resource_size_t)ipcdev.punit_base;
484 res->end = res->start + ipcdev.punit_size - 1;
485
486 res = punit_res + PUNIT_RESOURCE_INTER;
487 res->start = (resource_size_t)ipcdev.punit_base2;
488 res->end = res->start + ipcdev.punit_size2 - 1;
489
490 ret = platform_device_add_resources(pdev, punit_res,
491 ARRAY_SIZE(punit_res));
492 if (ret) {
493 dev_err(ipcdev.dev, "Failed to add platform punit resources\n");
494 goto err;
495 }
496
497 ret = platform_device_add(pdev);
498 if (ret) {
499 dev_err(ipcdev.dev, "Failed to add punit platform device\n");
500 goto err;
501 }
502 ipcdev.punit_dev = pdev;
503
504 return 0;
505err:
506 platform_device_put(pdev);
507 return ret;
508}
509
510static int ipc_create_tco_device(void)
511{
512 struct platform_device *pdev;
513 struct resource *res;
514 int ret;
515
516 pdev = platform_device_alloc(TCO_DEVICE_NAME, -1);
517 if (!pdev) {
518 dev_err(ipcdev.dev, "Failed to alloc tco platform device\n");
519 return -ENOMEM;
520 }
521
522 pdev->dev.parent = ipcdev.dev;
523
524 res = tco_res + TCO_RESOURCE_ACPI_IO;
525 res->start = (resource_size_t)ipcdev.acpi_io_base + TCO_BASE_OFFSET;
526 res->end = res->start + TCO_REGS_SIZE - 1;
527
528 res = tco_res + TCO_RESOURCE_SMI_EN_IO;
529 res->start = (resource_size_t)ipcdev.acpi_io_base + SMI_EN_OFFSET;
530 res->end = res->start + SMI_EN_SIZE - 1;
531
532 res = tco_res + TCO_RESOURCE_GCR_MEM;
533 res->start = (resource_size_t)ipcdev.gcr_base;
534 res->end = res->start + ipcdev.gcr_size - 1;
535
536 ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
537 if (ret) {
538 dev_err(ipcdev.dev, "Failed to add tco platform resources\n");
539 goto err;
540 }
541
542 ret = platform_device_add_data(pdev, &tco_info,
543 sizeof(struct lpc_ich_info));
544 if (ret) {
545 dev_err(ipcdev.dev, "Failed to add tco platform data\n");
546 goto err;
547 }
548
549 ret = platform_device_add(pdev);
550 if (ret) {
551 dev_err(ipcdev.dev, "Failed to add tco platform device\n");
552 goto err;
553 }
554 ipcdev.tco_dev = pdev;
555
556 return 0;
557err:
558 platform_device_put(pdev);
559 return ret;
560}
561
562static int ipc_create_pmc_devices(void)
563{
564 int ret;
565
566 ret = ipc_create_tco_device();
567 if (ret) {
568 dev_err(ipcdev.dev, "Failed to add tco platform device\n");
569 return ret;
570 }
571 ret = ipc_create_punit_device();
572 if (ret) {
573 dev_err(ipcdev.dev, "Failed to add punit platform device\n");
574 platform_device_unregister(ipcdev.tco_dev);
575 }
576 return ret;
577}
578
579static int ipc_plat_get_res(struct platform_device *pdev)
580{
581 struct resource *res;
582 void __iomem *addr;
583 int size;
584
585 res = platform_get_resource(pdev, IORESOURCE_IO,
586 PLAT_RESOURCE_ACPI_IO_INDEX);
587 if (!res) {
588 dev_err(&pdev->dev, "Failed to get io resource\n");
589 return -ENXIO;
590 }
591 size = resource_size(res);
592 ipcdev.acpi_io_base = (void *)res->start;
593 ipcdev.acpi_io_size = size;
594 dev_info(&pdev->dev, "io res: %llx %x\n",
595 (long long)res->start, (int)resource_size(res));
596
597 res = platform_get_resource(pdev, IORESOURCE_MEM,
598 PLAT_RESOURCE_PUNIT_DATA_INDEX);
599 if (!res) {
600 dev_err(&pdev->dev, "Failed to get punit resource\n");
601 return -ENXIO;
602 }
603 size = resource_size(res);
604 ipcdev.punit_base = (void *)res->start;
605 ipcdev.punit_size = size;
606 dev_info(&pdev->dev, "punit data res: %llx %x\n",
607 (long long)res->start, (int)resource_size(res));
608
609 res = platform_get_resource(pdev, IORESOURCE_MEM,
610 PLAT_RESOURCE_PUNIT_INTER_INDEX);
611 if (!res) {
612 dev_err(&pdev->dev, "Failed to get punit inter resource\n");
613 return -ENXIO;
614 }
615 size = resource_size(res);
616 ipcdev.punit_base2 = (void *)res->start;
617 ipcdev.punit_size2 = size;
618 dev_info(&pdev->dev, "punit interface res: %llx %x\n",
619 (long long)res->start, (int)resource_size(res));
620
621 res = platform_get_resource(pdev, IORESOURCE_MEM,
622 PLAT_RESOURCE_IPC_INDEX);
623 if (!res) {
624 dev_err(&pdev->dev, "Failed to get ipc resource\n");
625 return -ENXIO;
626 }
627 size = PLAT_RESOURCE_IPC_SIZE;
628 if (!request_mem_region(res->start, size, pdev->name)) {
629 dev_err(&pdev->dev, "Failed to request ipc resource\n");
630 return -EBUSY;
631 }
632 addr = ioremap_nocache(res->start, size);
633 if (!addr) {
634 dev_err(&pdev->dev, "I/O memory remapping failed\n");
635 release_mem_region(res->start, size);
636 return -ENOMEM;
637 }
638 ipcdev.ipc_base = addr;
639
640 ipcdev.gcr_base = (void *)(res->start + size);
641 ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
642 dev_info(&pdev->dev, "ipc res: %llx %x\n",
643 (long long)res->start, (int)resource_size(res));
644
645 return 0;
646}
647
648#ifdef CONFIG_ACPI
649static const struct acpi_device_id ipc_acpi_ids[] = {
650 { "INT34D2", 0},
651 { }
652};
653MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
654#endif
655
656static int ipc_plat_probe(struct platform_device *pdev)
657{
658 struct resource *res;
659 int ret;
660
661 ipcdev.dev = &pdev->dev;
662 ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
663 init_completion(&ipcdev.cmd_complete);
664
665 ipcdev.irq = platform_get_irq(pdev, 0);
666 if (ipcdev.irq < 0) {
667 dev_err(&pdev->dev, "Failed to get irq\n");
668 return -EINVAL;
669 }
670
671 ret = ipc_plat_get_res(pdev);
672 if (ret) {
673 dev_err(&pdev->dev, "Failed to request resource\n");
674 return ret;
675 }
676
677 ret = ipc_create_pmc_devices();
678 if (ret) {
679 dev_err(&pdev->dev, "Failed to create pmc devices\n");
680 goto err_device;
681 }
682
683 if (request_irq(ipcdev.irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
684 dev_err(&pdev->dev, "Failed to request irq\n");
685 ret = -EBUSY;
686 goto err_irq;
687 }
688
689 ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
690 if (ret) {
691 dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
692 ret);
693 goto err_sys;
694 }
695
696 return 0;
697err_sys:
698 free_irq(ipcdev.irq, &ipcdev);
699err_irq:
700 platform_device_unregister(ipcdev.tco_dev);
701 platform_device_unregister(ipcdev.punit_dev);
702err_device:
703 iounmap(ipcdev.ipc_base);
704 res = platform_get_resource(pdev, IORESOURCE_MEM,
705 PLAT_RESOURCE_IPC_INDEX);
706 if (res)
707 release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
708 return ret;
709}
710
711static int ipc_plat_remove(struct platform_device *pdev)
712{
713 struct resource *res;
714
715 sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
716 free_irq(ipcdev.irq, &ipcdev);
717 platform_device_unregister(ipcdev.tco_dev);
718 platform_device_unregister(ipcdev.punit_dev);
719 iounmap(ipcdev.ipc_base);
720 res = platform_get_resource(pdev, IORESOURCE_MEM,
721 PLAT_RESOURCE_IPC_INDEX);
722 if (res)
723 release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE);
724 ipcdev.dev = NULL;
725 return 0;
726}
727
728static struct platform_driver ipc_plat_driver = {
729 .remove = ipc_plat_remove,
730 .probe = ipc_plat_probe,
731 .driver = {
732 .name = "pmc-ipc-plat",
733 .acpi_match_table = ACPI_PTR(ipc_acpi_ids),
734 },
735};
736
737static int __init intel_pmc_ipc_init(void)
738{
739 int ret;
740
741 ret = platform_driver_register(&ipc_plat_driver);
742 if (ret) {
743 pr_err("Failed to register PMC ipc platform driver\n");
744 return ret;
745 }
746 ret = pci_register_driver(&ipc_pci_driver);
747 if (ret) {
748 pr_err("Failed to register PMC ipc pci driver\n");
749 platform_driver_unregister(&ipc_plat_driver);
750 return ret;
751 }
752 return ret;
753}
754
755static void __exit intel_pmc_ipc_exit(void)
756{
757 pci_unregister_driver(&ipc_pci_driver);
758 platform_driver_unregister(&ipc_plat_driver);
759}
760
761MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
762MODULE_DESCRIPTION("Intel PMC IPC driver");
763MODULE_LICENSE("GPL");
764
765/* Some modules are dependent on this, so init earlier */
766fs_initcall(intel_pmc_ipc_init);
767module_exit(intel_pmc_ipc_exit);