1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for the Intel P-Unit Mailbox IPC mechanism
5 * (C) Copyright 2015 Intel Corporation
7 * The heart of the P-Unit is the Foxton microcontroller and its firmware,
8 * which provide mailbox interface for power management usage.
11 #include <linux/acpi.h>
12 #include <linux/bitops.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/interrupt.h>
17 #include <linux/mod_devicetable.h>
18 #include <linux/module.h>
19 #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
)
230 * The following resources are required
231 * - BIOS_IPC BASE_DATA
232 * - BIOS_IPC BASE_IFACE
234 addr
= devm_platform_ioremap_resource(pdev
, 0);
236 return PTR_ERR(addr
);
237 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
239 addr
= devm_platform_ioremap_resource(pdev
, 1);
241 return PTR_ERR(addr
);
242 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
245 * The following resources are optional
246 * - ISPDRIVER_IPC BASE_DATA
247 * - ISPDRIVER_IPC BASE_IFACE
248 * - GTDRIVER_IPC BASE_DATA
249 * - GTDRIVER_IPC BASE_IFACE
251 addr
= devm_platform_ioremap_resource(pdev
, 2);
253 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
255 addr
= devm_platform_ioremap_resource(pdev
, 3);
257 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
259 addr
= devm_platform_ioremap_resource(pdev
, 4);
261 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
263 addr
= devm_platform_ioremap_resource(pdev
, 5);
265 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_IFACE
] = addr
;
270 static int intel_punit_ipc_probe(struct platform_device
*pdev
)
274 punit_ipcdev
= devm_kzalloc(&pdev
->dev
,
275 sizeof(*punit_ipcdev
), GFP_KERNEL
);
279 platform_set_drvdata(pdev
, punit_ipcdev
);
281 irq
= platform_get_irq_optional(pdev
, 0);
283 dev_warn(&pdev
->dev
, "Invalid IRQ, using polling mode\n");
285 ret
= devm_request_irq(&pdev
->dev
, irq
, intel_punit_ioc
,
286 IRQF_NO_SUSPEND
, "intel_punit_ipc",
289 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", irq
);
292 punit_ipcdev
->irq
= irq
;
295 ret
= intel_punit_get_bars(pdev
);
299 punit_ipcdev
->dev
= &pdev
->dev
;
300 mutex_init(&punit_ipcdev
->lock
);
301 init_completion(&punit_ipcdev
->cmd_complete
);
306 static int intel_punit_ipc_remove(struct platform_device
*pdev
)
311 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
316 static struct platform_driver intel_punit_ipc_driver
= {
317 .probe
= intel_punit_ipc_probe
,
318 .remove
= intel_punit_ipc_remove
,
320 .name
= "intel_punit_ipc",
321 .acpi_match_table
= ACPI_PTR(punit_ipc_acpi_ids
),
325 static int __init
intel_punit_ipc_init(void)
327 return platform_driver_register(&intel_punit_ipc_driver
);
330 static void __exit
intel_punit_ipc_exit(void)
332 platform_driver_unregister(&intel_punit_ipc_driver
);
335 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
336 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
337 MODULE_LICENSE("GPL v2");
339 /* Some modules are dependent on this, so init earlier */
340 fs_initcall(intel_punit_ipc_init
);
341 module_exit(intel_punit_ipc_exit
);