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/mod_devicetable.h>
16 #include <linux/acpi.h>
17 #include <linux/delay.h>
18 #include <linux/bitops.h>
19 #include <linux/device.h>
20 #include <linux/interrupt.h>
22 #include <linux/platform_device.h>
23 #include <asm/intel_punit_ipc.h>
25 /* IPC Mailbox registers */
26 #define OFFSET_DATA_LOW 0x0
27 #define OFFSET_DATA_HIGH 0x4
28 /* bit field of interface register */
29 #define CMD_RUN BIT(31)
30 #define CMD_ERRCODE_MASK GENMASK(7, 0)
31 #define CMD_PARA1_SHIFT 8
32 #define CMD_PARA2_SHIFT 16
34 #define CMD_TIMEOUT_SECONDS 1
46 struct completion cmd_complete
;
47 /* base of interface and data registers */
48 void __iomem
*base
[RESERVED_IPC
][BASE_MAX
];
52 static IPC_DEV
*punit_ipcdev
;
54 static inline u32
ipc_read_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
56 return readl(ipcdev
->base
[type
][BASE_IFACE
]);
59 static inline void ipc_write_cmd(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 cmd
)
61 writel(cmd
, ipcdev
->base
[type
][BASE_IFACE
]);
64 static inline u32
ipc_read_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
)
66 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
69 static inline u32
ipc_read_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
)
71 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
74 static inline void ipc_write_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
76 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
79 static inline void ipc_write_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
81 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
84 static const char *ipc_err_string(int error
)
86 if (error
== IPC_PUNIT_ERR_SUCCESS
)
88 else if (error
== IPC_PUNIT_ERR_INVALID_CMD
)
89 return "invalid command";
90 else if (error
== IPC_PUNIT_ERR_INVALID_PARAMETER
)
91 return "invalid parameter";
92 else if (error
== IPC_PUNIT_ERR_CMD_TIMEOUT
)
93 return "command timeout";
94 else if (error
== IPC_PUNIT_ERR_CMD_LOCKED
)
95 return "command locked";
96 else if (error
== IPC_PUNIT_ERR_INVALID_VR_ID
)
97 return "invalid vr id";
98 else if (error
== IPC_PUNIT_ERR_VR_ERR
)
101 return "unknown error";
104 static int intel_punit_ipc_check_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
106 int loops
= CMD_TIMEOUT_SECONDS
* USEC_PER_SEC
;
111 if (!wait_for_completion_timeout(&ipcdev
->cmd_complete
,
112 CMD_TIMEOUT_SECONDS
* HZ
)) {
113 dev_err(ipcdev
->dev
, "IPC timed out\n");
117 while ((ipc_read_status(ipcdev
, type
) & CMD_RUN
) && --loops
)
120 dev_err(ipcdev
->dev
, "IPC timed out\n");
125 status
= ipc_read_status(ipcdev
, type
);
126 errcode
= status
& CMD_ERRCODE_MASK
;
128 dev_err(ipcdev
->dev
, "IPC failed: %s, IPC_STS=0x%x\n",
129 ipc_err_string(errcode
), status
);
137 * intel_punit_ipc_simple_command() - Simple IPC command
138 * @cmd: IPC command code.
139 * @para1: First 8bit parameter, set 0 if not used.
140 * @para2: Second 8bit parameter, set 0 if not used.
142 * Send a IPC command to P-Unit when there is no data transaction
144 * Return: IPC error code or 0 on success.
146 int intel_punit_ipc_simple_command(int cmd
, int para1
, int para2
)
148 IPC_DEV
*ipcdev
= punit_ipcdev
;
153 mutex_lock(&ipcdev
->lock
);
155 reinit_completion(&ipcdev
->cmd_complete
);
156 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
158 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
159 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
160 ipc_write_cmd(ipcdev
, type
, val
);
161 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
163 mutex_unlock(&ipcdev
->lock
);
167 EXPORT_SYMBOL(intel_punit_ipc_simple_command
);
170 * intel_punit_ipc_command() - IPC command with data and pointers
171 * @cmd: IPC command code.
172 * @para1: First 8bit parameter, set 0 if not used.
173 * @para2: Second 8bit parameter, set 0 if not used.
174 * @in: Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
177 * Send a IPC command to P-Unit with data transaction
179 * Return: IPC error code or 0 on success.
181 int intel_punit_ipc_command(u32 cmd
, u32 para1
, u32 para2
, u32
*in
, u32
*out
)
183 IPC_DEV
*ipcdev
= punit_ipcdev
;
188 mutex_lock(&ipcdev
->lock
);
190 reinit_completion(&ipcdev
->cmd_complete
);
191 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
194 ipc_write_data_low(ipcdev
, type
, *in
);
195 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
196 ipc_write_data_high(ipcdev
, type
, *++in
);
199 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
200 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
201 ipc_write_cmd(ipcdev
, type
, val
);
203 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
208 *out
= ipc_read_data_low(ipcdev
, type
);
209 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
210 *++out
= ipc_read_data_high(ipcdev
, type
);
214 mutex_unlock(&ipcdev
->lock
);
217 EXPORT_SYMBOL_GPL(intel_punit_ipc_command
);
219 static irqreturn_t
intel_punit_ioc(int irq
, void *dev_id
)
221 IPC_DEV
*ipcdev
= dev_id
;
223 complete(&ipcdev
->cmd_complete
);
227 static int intel_punit_get_bars(struct platform_device
*pdev
)
229 struct resource
*res
;
233 * The following resources are required
234 * - BIOS_IPC BASE_DATA
235 * - BIOS_IPC BASE_IFACE
237 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
238 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
240 return PTR_ERR(addr
);
241 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
243 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
244 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
246 return PTR_ERR(addr
);
247 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
250 * The following resources are optional
251 * - ISPDRIVER_IPC BASE_DATA
252 * - ISPDRIVER_IPC BASE_IFACE
253 * - GTDRIVER_IPC BASE_DATA
254 * - GTDRIVER_IPC BASE_IFACE
256 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
257 if (res
&& resource_size(res
) > 1) {
258 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
260 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
263 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 3);
264 if (res
&& resource_size(res
) > 1) {
265 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
267 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
270 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 4);
271 if (res
&& resource_size(res
) > 1) {
272 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
274 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
277 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 5);
278 if (res
&& resource_size(res
) > 1) {
279 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
281 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_IFACE
] = addr
;
287 static int intel_punit_ipc_probe(struct platform_device
*pdev
)
291 punit_ipcdev
= devm_kzalloc(&pdev
->dev
,
292 sizeof(*punit_ipcdev
), GFP_KERNEL
);
296 platform_set_drvdata(pdev
, punit_ipcdev
);
298 irq
= platform_get_irq(pdev
, 0);
300 punit_ipcdev
->irq
= 0;
301 dev_warn(&pdev
->dev
, "Invalid IRQ, using polling mode\n");
303 ret
= devm_request_irq(&pdev
->dev
, irq
, intel_punit_ioc
,
304 IRQF_NO_SUSPEND
, "intel_punit_ipc",
307 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", irq
);
310 punit_ipcdev
->irq
= irq
;
313 ret
= intel_punit_get_bars(pdev
);
317 punit_ipcdev
->dev
= &pdev
->dev
;
318 mutex_init(&punit_ipcdev
->lock
);
319 init_completion(&punit_ipcdev
->cmd_complete
);
325 static int intel_punit_ipc_remove(struct platform_device
*pdev
)
330 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
335 static struct platform_driver intel_punit_ipc_driver
= {
336 .probe
= intel_punit_ipc_probe
,
337 .remove
= intel_punit_ipc_remove
,
339 .name
= "intel_punit_ipc",
340 .acpi_match_table
= ACPI_PTR(punit_ipc_acpi_ids
),
344 static int __init
intel_punit_ipc_init(void)
346 return platform_driver_register(&intel_punit_ipc_driver
);
349 static void __exit
intel_punit_ipc_exit(void)
351 platform_driver_unregister(&intel_punit_ipc_driver
);
354 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
355 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
356 MODULE_LICENSE("GPL v2");
358 /* Some modules are dependent on this, so init earlier */
359 fs_initcall(intel_punit_ipc_init
);
360 module_exit(intel_punit_ipc_exit
);