2 * Driver for the Intel P-Unit Mailbox IPC mechanism
4 * (C) Copyright 2015 Intel Corporation
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * The heart of the P-Unit is the Foxton microcontroller and its firmware,
11 * which provide mailbox interface for power management usage.
14 #include <linux/module.h>
15 #include <linux/acpi.h>
16 #include <linux/delay.h>
17 #include <linux/bitops.h>
18 #include <linux/device.h>
19 #include <linux/interrupt.h>
20 #include <linux/platform_device.h>
21 #include <asm/intel_punit_ipc.h>
23 /* IPC Mailbox registers */
24 #define OFFSET_DATA_LOW 0x0
25 #define OFFSET_DATA_HIGH 0x4
26 /* bit field of interface register */
27 #define CMD_RUN BIT(31)
28 #define CMD_ERRCODE_MASK GENMASK(7, 0)
29 #define CMD_PARA1_SHIFT 8
30 #define CMD_PARA2_SHIFT 16
32 #define CMD_TIMEOUT_SECONDS 1
44 struct completion cmd_complete
;
45 /* base of interface and data registers */
46 void __iomem
*base
[RESERVED_IPC
][BASE_MAX
];
50 static IPC_DEV
*punit_ipcdev
;
52 static inline u32
ipc_read_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
54 return readl(ipcdev
->base
[type
][BASE_IFACE
]);
57 static inline void ipc_write_cmd(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 cmd
)
59 writel(cmd
, ipcdev
->base
[type
][BASE_IFACE
]);
62 static inline u32
ipc_read_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
)
64 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
67 static inline u32
ipc_read_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
)
69 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
72 static inline void ipc_write_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
74 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
77 static inline void ipc_write_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
79 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
82 static const char *ipc_err_string(int error
)
84 if (error
== IPC_PUNIT_ERR_SUCCESS
)
86 else if (error
== IPC_PUNIT_ERR_INVALID_CMD
)
87 return "invalid command";
88 else if (error
== IPC_PUNIT_ERR_INVALID_PARAMETER
)
89 return "invalid parameter";
90 else if (error
== IPC_PUNIT_ERR_CMD_TIMEOUT
)
91 return "command timeout";
92 else if (error
== IPC_PUNIT_ERR_CMD_LOCKED
)
93 return "command locked";
94 else if (error
== IPC_PUNIT_ERR_INVALID_VR_ID
)
95 return "invalid vr id";
96 else if (error
== IPC_PUNIT_ERR_VR_ERR
)
99 return "unknown error";
102 static int intel_punit_ipc_check_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
104 int loops
= CMD_TIMEOUT_SECONDS
* USEC_PER_SEC
;
109 if (!wait_for_completion_timeout(&ipcdev
->cmd_complete
,
110 CMD_TIMEOUT_SECONDS
* HZ
)) {
111 dev_err(ipcdev
->dev
, "IPC timed out\n");
115 while ((ipc_read_status(ipcdev
, type
) & CMD_RUN
) && --loops
)
118 dev_err(ipcdev
->dev
, "IPC timed out\n");
123 status
= ipc_read_status(ipcdev
, type
);
124 errcode
= status
& CMD_ERRCODE_MASK
;
126 dev_err(ipcdev
->dev
, "IPC failed: %s, IPC_STS=0x%x\n",
127 ipc_err_string(errcode
), status
);
135 * intel_punit_ipc_simple_command() - Simple IPC command
136 * @cmd: IPC command code.
137 * @para1: First 8bit parameter, set 0 if not used.
138 * @para2: Second 8bit parameter, set 0 if not used.
140 * Send a IPC command to P-Unit when there is no data transaction
142 * Return: IPC error code or 0 on success.
144 int intel_punit_ipc_simple_command(int cmd
, int para1
, int para2
)
146 IPC_DEV
*ipcdev
= punit_ipcdev
;
151 mutex_lock(&ipcdev
->lock
);
153 reinit_completion(&ipcdev
->cmd_complete
);
154 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
156 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
157 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
158 ipc_write_cmd(ipcdev
, type
, val
);
159 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
161 mutex_unlock(&ipcdev
->lock
);
165 EXPORT_SYMBOL(intel_punit_ipc_simple_command
);
168 * intel_punit_ipc_command() - IPC command with data and pointers
169 * @cmd: IPC command code.
170 * @para1: First 8bit parameter, set 0 if not used.
171 * @para2: Second 8bit parameter, set 0 if not used.
172 * @in: Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
175 * Send a IPC command to P-Unit with data transaction
177 * Return: IPC error code or 0 on success.
179 int intel_punit_ipc_command(u32 cmd
, u32 para1
, u32 para2
, u32
*in
, u32
*out
)
181 IPC_DEV
*ipcdev
= punit_ipcdev
;
186 mutex_lock(&ipcdev
->lock
);
188 reinit_completion(&ipcdev
->cmd_complete
);
189 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
192 ipc_write_data_low(ipcdev
, type
, *in
);
193 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
194 ipc_write_data_high(ipcdev
, type
, *++in
);
197 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
198 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
199 ipc_write_cmd(ipcdev
, type
, val
);
201 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
206 *out
= ipc_read_data_low(ipcdev
, type
);
207 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
208 *++out
= ipc_read_data_high(ipcdev
, type
);
212 mutex_unlock(&ipcdev
->lock
);
215 EXPORT_SYMBOL_GPL(intel_punit_ipc_command
);
217 static irqreturn_t
intel_punit_ioc(int irq
, void *dev_id
)
219 IPC_DEV
*ipcdev
= dev_id
;
221 complete(&ipcdev
->cmd_complete
);
225 static int intel_punit_get_bars(struct platform_device
*pdev
)
227 struct resource
*res
;
231 * The following resources are required
232 * - BIOS_IPC BASE_DATA
233 * - BIOS_IPC BASE_IFACE
235 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
236 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
238 return PTR_ERR(addr
);
239 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
241 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
242 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
244 return PTR_ERR(addr
);
245 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
248 * The following resources are optional
249 * - ISPDRIVER_IPC BASE_DATA
250 * - ISPDRIVER_IPC BASE_IFACE
251 * - GTDRIVER_IPC BASE_DATA
252 * - GTDRIVER_IPC BASE_IFACE
254 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
255 if (res
&& resource_size(res
) > 1) {
256 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
258 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
261 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 3);
262 if (res
&& resource_size(res
) > 1) {
263 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
265 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
268 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 4);
269 if (res
&& resource_size(res
) > 1) {
270 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
272 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
275 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 5);
276 if (res
&& resource_size(res
) > 1) {
277 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
279 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_IFACE
] = addr
;
285 static int intel_punit_ipc_probe(struct platform_device
*pdev
)
289 punit_ipcdev
= devm_kzalloc(&pdev
->dev
,
290 sizeof(*punit_ipcdev
), GFP_KERNEL
);
294 platform_set_drvdata(pdev
, punit_ipcdev
);
296 irq
= platform_get_irq(pdev
, 0);
298 punit_ipcdev
->irq
= 0;
299 dev_warn(&pdev
->dev
, "Invalid IRQ, using polling mode\n");
301 ret
= devm_request_irq(&pdev
->dev
, irq
, intel_punit_ioc
,
302 IRQF_NO_SUSPEND
, "intel_punit_ipc",
305 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", irq
);
308 punit_ipcdev
->irq
= irq
;
311 ret
= intel_punit_get_bars(pdev
);
315 punit_ipcdev
->dev
= &pdev
->dev
;
316 mutex_init(&punit_ipcdev
->lock
);
317 init_completion(&punit_ipcdev
->cmd_complete
);
323 static int intel_punit_ipc_remove(struct platform_device
*pdev
)
328 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
333 static struct platform_driver intel_punit_ipc_driver
= {
334 .probe
= intel_punit_ipc_probe
,
335 .remove
= intel_punit_ipc_remove
,
337 .name
= "intel_punit_ipc",
338 .acpi_match_table
= ACPI_PTR(punit_ipc_acpi_ids
),
342 static int __init
intel_punit_ipc_init(void)
344 return platform_driver_register(&intel_punit_ipc_driver
);
347 static void __exit
intel_punit_ipc_exit(void)
349 platform_driver_unregister(&intel_punit_ipc_driver
);
352 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
353 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
354 MODULE_LICENSE("GPL v2");
356 /* Some modules are dependent on this, so init earlier */
357 fs_initcall(intel_punit_ipc_init
);
358 module_exit(intel_punit_ipc_exit
);