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>
21 #include <linux/platform_device.h>
22 #include <asm/intel_punit_ipc.h>
24 /* IPC Mailbox registers */
25 #define OFFSET_DATA_LOW 0x0
26 #define OFFSET_DATA_HIGH 0x4
27 /* bit field of interface register */
28 #define CMD_RUN BIT(31)
29 #define CMD_ERRCODE_MASK GENMASK(7, 0)
30 #define CMD_PARA1_SHIFT 8
31 #define CMD_PARA2_SHIFT 16
33 #define CMD_TIMEOUT_SECONDS 1
45 struct completion cmd_complete
;
46 /* base of interface and data registers */
47 void __iomem
*base
[RESERVED_IPC
][BASE_MAX
];
51 static IPC_DEV
*punit_ipcdev
;
53 static inline u32
ipc_read_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
55 return readl(ipcdev
->base
[type
][BASE_IFACE
]);
58 static inline void ipc_write_cmd(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 cmd
)
60 writel(cmd
, ipcdev
->base
[type
][BASE_IFACE
]);
63 static inline u32
ipc_read_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
)
65 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
68 static inline u32
ipc_read_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
)
70 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
73 static inline void ipc_write_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
75 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
78 static inline void ipc_write_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
80 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
83 static const char *ipc_err_string(int error
)
85 if (error
== IPC_PUNIT_ERR_SUCCESS
)
87 else if (error
== IPC_PUNIT_ERR_INVALID_CMD
)
88 return "invalid command";
89 else if (error
== IPC_PUNIT_ERR_INVALID_PARAMETER
)
90 return "invalid parameter";
91 else if (error
== IPC_PUNIT_ERR_CMD_TIMEOUT
)
92 return "command timeout";
93 else if (error
== IPC_PUNIT_ERR_CMD_LOCKED
)
94 return "command locked";
95 else if (error
== IPC_PUNIT_ERR_INVALID_VR_ID
)
96 return "invalid vr id";
97 else if (error
== IPC_PUNIT_ERR_VR_ERR
)
100 return "unknown error";
103 static int intel_punit_ipc_check_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
105 int loops
= CMD_TIMEOUT_SECONDS
* USEC_PER_SEC
;
110 if (!wait_for_completion_timeout(&ipcdev
->cmd_complete
,
111 CMD_TIMEOUT_SECONDS
* HZ
)) {
112 dev_err(ipcdev
->dev
, "IPC timed out\n");
116 while ((ipc_read_status(ipcdev
, type
) & CMD_RUN
) && --loops
)
119 dev_err(ipcdev
->dev
, "IPC timed out\n");
124 status
= ipc_read_status(ipcdev
, type
);
125 errcode
= status
& CMD_ERRCODE_MASK
;
127 dev_err(ipcdev
->dev
, "IPC failed: %s, IPC_STS=0x%x\n",
128 ipc_err_string(errcode
), status
);
136 * intel_punit_ipc_simple_command() - Simple IPC command
137 * @cmd: IPC command code.
138 * @para1: First 8bit parameter, set 0 if not used.
139 * @para2: Second 8bit parameter, set 0 if not used.
141 * Send a IPC command to P-Unit when there is no data transaction
143 * Return: IPC error code or 0 on success.
145 int intel_punit_ipc_simple_command(int cmd
, int para1
, int para2
)
147 IPC_DEV
*ipcdev
= punit_ipcdev
;
152 mutex_lock(&ipcdev
->lock
);
154 reinit_completion(&ipcdev
->cmd_complete
);
155 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
157 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
158 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
159 ipc_write_cmd(ipcdev
, type
, val
);
160 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
162 mutex_unlock(&ipcdev
->lock
);
166 EXPORT_SYMBOL(intel_punit_ipc_simple_command
);
169 * intel_punit_ipc_command() - IPC command with data and pointers
170 * @cmd: IPC command code.
171 * @para1: First 8bit parameter, set 0 if not used.
172 * @para2: Second 8bit parameter, set 0 if not used.
173 * @in: Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
176 * Send a IPC command to P-Unit with data transaction
178 * Return: IPC error code or 0 on success.
180 int intel_punit_ipc_command(u32 cmd
, u32 para1
, u32 para2
, u32
*in
, u32
*out
)
182 IPC_DEV
*ipcdev
= punit_ipcdev
;
187 mutex_lock(&ipcdev
->lock
);
189 reinit_completion(&ipcdev
->cmd_complete
);
190 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
193 ipc_write_data_low(ipcdev
, type
, *in
);
194 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
195 ipc_write_data_high(ipcdev
, type
, *++in
);
198 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
199 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
200 ipc_write_cmd(ipcdev
, type
, val
);
202 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
207 *out
= ipc_read_data_low(ipcdev
, type
);
208 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
209 *++out
= ipc_read_data_high(ipcdev
, type
);
213 mutex_unlock(&ipcdev
->lock
);
216 EXPORT_SYMBOL_GPL(intel_punit_ipc_command
);
218 static irqreturn_t
intel_punit_ioc(int irq
, void *dev_id
)
220 IPC_DEV
*ipcdev
= dev_id
;
222 complete(&ipcdev
->cmd_complete
);
226 static int intel_punit_get_bars(struct platform_device
*pdev
)
228 struct resource
*res
;
232 * The following resources are required
233 * - BIOS_IPC BASE_DATA
234 * - BIOS_IPC BASE_IFACE
236 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
237 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
239 return PTR_ERR(addr
);
240 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
242 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
243 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
245 return PTR_ERR(addr
);
246 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
249 * The following resources are optional
250 * - ISPDRIVER_IPC BASE_DATA
251 * - ISPDRIVER_IPC BASE_IFACE
252 * - GTDRIVER_IPC BASE_DATA
253 * - GTDRIVER_IPC BASE_IFACE
255 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
256 if (res
&& resource_size(res
) > 1) {
257 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
259 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
262 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 3);
263 if (res
&& resource_size(res
) > 1) {
264 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
266 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
269 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 4);
270 if (res
&& resource_size(res
) > 1) {
271 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
273 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
276 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 5);
277 if (res
&& resource_size(res
) > 1) {
278 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
280 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_IFACE
] = addr
;
286 static int intel_punit_ipc_probe(struct platform_device
*pdev
)
290 punit_ipcdev
= devm_kzalloc(&pdev
->dev
,
291 sizeof(*punit_ipcdev
), GFP_KERNEL
);
295 platform_set_drvdata(pdev
, punit_ipcdev
);
297 irq
= platform_get_irq(pdev
, 0);
299 punit_ipcdev
->irq
= 0;
300 dev_warn(&pdev
->dev
, "Invalid IRQ, using polling mode\n");
302 ret
= devm_request_irq(&pdev
->dev
, irq
, intel_punit_ioc
,
303 IRQF_NO_SUSPEND
, "intel_punit_ipc",
306 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", irq
);
309 punit_ipcdev
->irq
= irq
;
312 ret
= intel_punit_get_bars(pdev
);
316 punit_ipcdev
->dev
= &pdev
->dev
;
317 mutex_init(&punit_ipcdev
->lock
);
318 init_completion(&punit_ipcdev
->cmd_complete
);
324 static int intel_punit_ipc_remove(struct platform_device
*pdev
)
329 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
334 static struct platform_driver intel_punit_ipc_driver
= {
335 .probe
= intel_punit_ipc_probe
,
336 .remove
= intel_punit_ipc_remove
,
338 .name
= "intel_punit_ipc",
339 .acpi_match_table
= ACPI_PTR(punit_ipc_acpi_ids
),
343 static int __init
intel_punit_ipc_init(void)
345 return platform_driver_register(&intel_punit_ipc_driver
);
348 static void __exit
intel_punit_ipc_exit(void)
350 platform_driver_unregister(&intel_punit_ipc_driver
);
353 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
354 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
355 MODULE_LICENSE("GPL v2");
357 /* Some modules are dependent on this, so init earlier */
358 fs_initcall(intel_punit_ipc_init
);
359 module_exit(intel_punit_ipc_exit
);