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/bitops.h>
12 #include <linux/delay.h>
13 #include <linux/device.h>
14 #include <linux/interrupt.h>
16 #include <linux/mod_devicetable.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
20 #include <asm/intel_punit_ipc.h>
22 /* IPC Mailbox registers */
23 #define OFFSET_DATA_LOW 0x0
24 #define OFFSET_DATA_HIGH 0x4
25 /* bit field of interface register */
26 #define CMD_RUN BIT(31)
27 #define CMD_ERRCODE_MASK GENMASK(7, 0)
28 #define CMD_PARA1_SHIFT 8
29 #define CMD_PARA2_SHIFT 16
31 #define CMD_TIMEOUT_SECONDS 1
43 struct completion cmd_complete
;
44 /* base of interface and data registers */
45 void __iomem
*base
[RESERVED_IPC
][BASE_MAX
];
49 static IPC_DEV
*punit_ipcdev
;
51 static inline u32
ipc_read_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
53 return readl(ipcdev
->base
[type
][BASE_IFACE
]);
56 static inline void ipc_write_cmd(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 cmd
)
58 writel(cmd
, ipcdev
->base
[type
][BASE_IFACE
]);
61 static inline u32
ipc_read_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
)
63 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
66 static inline u32
ipc_read_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
)
68 return readl(ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
71 static inline void ipc_write_data_low(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
73 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_LOW
);
76 static inline void ipc_write_data_high(IPC_DEV
*ipcdev
, IPC_TYPE type
, u32 data
)
78 writel(data
, ipcdev
->base
[type
][BASE_DATA
] + OFFSET_DATA_HIGH
);
81 static const char *ipc_err_string(int error
)
83 if (error
== IPC_PUNIT_ERR_SUCCESS
)
85 else if (error
== IPC_PUNIT_ERR_INVALID_CMD
)
86 return "invalid command";
87 else if (error
== IPC_PUNIT_ERR_INVALID_PARAMETER
)
88 return "invalid parameter";
89 else if (error
== IPC_PUNIT_ERR_CMD_TIMEOUT
)
90 return "command timeout";
91 else if (error
== IPC_PUNIT_ERR_CMD_LOCKED
)
92 return "command locked";
93 else if (error
== IPC_PUNIT_ERR_INVALID_VR_ID
)
94 return "invalid vr id";
95 else if (error
== IPC_PUNIT_ERR_VR_ERR
)
98 return "unknown error";
101 static int intel_punit_ipc_check_status(IPC_DEV
*ipcdev
, IPC_TYPE type
)
103 int loops
= CMD_TIMEOUT_SECONDS
* USEC_PER_SEC
;
108 if (!wait_for_completion_timeout(&ipcdev
->cmd_complete
,
109 CMD_TIMEOUT_SECONDS
* HZ
)) {
110 dev_err(ipcdev
->dev
, "IPC timed out\n");
114 while ((ipc_read_status(ipcdev
, type
) & CMD_RUN
) && --loops
)
117 dev_err(ipcdev
->dev
, "IPC timed out\n");
122 status
= ipc_read_status(ipcdev
, type
);
123 errcode
= status
& CMD_ERRCODE_MASK
;
125 dev_err(ipcdev
->dev
, "IPC failed: %s, IPC_STS=0x%x\n",
126 ipc_err_string(errcode
), status
);
134 * intel_punit_ipc_simple_command() - Simple IPC command
135 * @cmd: IPC command code.
136 * @para1: First 8bit parameter, set 0 if not used.
137 * @para2: Second 8bit parameter, set 0 if not used.
139 * Send a IPC command to P-Unit when there is no data transaction
141 * Return: IPC error code or 0 on success.
143 int intel_punit_ipc_simple_command(int cmd
, int para1
, int para2
)
145 IPC_DEV
*ipcdev
= punit_ipcdev
;
150 mutex_lock(&ipcdev
->lock
);
152 reinit_completion(&ipcdev
->cmd_complete
);
153 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
155 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
156 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
157 ipc_write_cmd(ipcdev
, type
, val
);
158 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
160 mutex_unlock(&ipcdev
->lock
);
164 EXPORT_SYMBOL(intel_punit_ipc_simple_command
);
167 * intel_punit_ipc_command() - IPC command with data and pointers
168 * @cmd: IPC command code.
169 * @para1: First 8bit parameter, set 0 if not used.
170 * @para2: Second 8bit parameter, set 0 if not used.
171 * @in: Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
174 * Send a IPC command to P-Unit with data transaction
176 * Return: IPC error code or 0 on success.
178 int intel_punit_ipc_command(u32 cmd
, u32 para1
, u32 para2
, u32
*in
, u32
*out
)
180 IPC_DEV
*ipcdev
= punit_ipcdev
;
185 mutex_lock(&ipcdev
->lock
);
187 reinit_completion(&ipcdev
->cmd_complete
);
188 type
= (cmd
& IPC_PUNIT_CMD_TYPE_MASK
) >> IPC_TYPE_OFFSET
;
191 ipc_write_data_low(ipcdev
, type
, *in
);
192 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
193 ipc_write_data_high(ipcdev
, type
, *++in
);
196 val
= cmd
& ~IPC_PUNIT_CMD_TYPE_MASK
;
197 val
|= CMD_RUN
| para2
<< CMD_PARA2_SHIFT
| para1
<< CMD_PARA1_SHIFT
;
198 ipc_write_cmd(ipcdev
, type
, val
);
200 ret
= intel_punit_ipc_check_status(ipcdev
, type
);
205 *out
= ipc_read_data_low(ipcdev
, type
);
206 if (type
== GTDRIVER_IPC
|| type
== ISPDRIVER_IPC
)
207 *++out
= ipc_read_data_high(ipcdev
, type
);
211 mutex_unlock(&ipcdev
->lock
);
214 EXPORT_SYMBOL_GPL(intel_punit_ipc_command
);
216 static irqreturn_t
intel_punit_ioc(int irq
, void *dev_id
)
218 IPC_DEV
*ipcdev
= dev_id
;
220 complete(&ipcdev
->cmd_complete
);
224 static int intel_punit_get_bars(struct platform_device
*pdev
)
229 * The following resources are required
230 * - BIOS_IPC BASE_DATA
231 * - BIOS_IPC BASE_IFACE
233 addr
= devm_platform_ioremap_resource(pdev
, 0);
235 return PTR_ERR(addr
);
236 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
238 addr
= devm_platform_ioremap_resource(pdev
, 1);
240 return PTR_ERR(addr
);
241 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
244 * The following resources are optional
245 * - ISPDRIVER_IPC BASE_DATA
246 * - ISPDRIVER_IPC BASE_IFACE
247 * - GTDRIVER_IPC BASE_DATA
248 * - GTDRIVER_IPC BASE_IFACE
250 addr
= devm_platform_ioremap_resource(pdev
, 2);
252 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
254 addr
= devm_platform_ioremap_resource(pdev
, 3);
256 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
258 addr
= devm_platform_ioremap_resource(pdev
, 4);
260 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
262 addr
= devm_platform_ioremap_resource(pdev
, 5);
264 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_IFACE
] = addr
;
269 static int intel_punit_ipc_probe(struct platform_device
*pdev
)
273 punit_ipcdev
= devm_kzalloc(&pdev
->dev
,
274 sizeof(*punit_ipcdev
), GFP_KERNEL
);
278 platform_set_drvdata(pdev
, punit_ipcdev
);
280 irq
= platform_get_irq_optional(pdev
, 0);
282 dev_warn(&pdev
->dev
, "Invalid IRQ, using polling mode\n");
284 ret
= devm_request_irq(&pdev
->dev
, irq
, intel_punit_ioc
,
285 IRQF_NO_SUSPEND
, "intel_punit_ipc",
288 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", irq
);
291 punit_ipcdev
->irq
= irq
;
294 ret
= intel_punit_get_bars(pdev
);
298 punit_ipcdev
->dev
= &pdev
->dev
;
299 mutex_init(&punit_ipcdev
->lock
);
300 init_completion(&punit_ipcdev
->cmd_complete
);
305 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
309 MODULE_DEVICE_TABLE(acpi
, punit_ipc_acpi_ids
);
311 static struct platform_driver intel_punit_ipc_driver
= {
312 .probe
= intel_punit_ipc_probe
,
314 .name
= "intel_punit_ipc",
315 .acpi_match_table
= punit_ipc_acpi_ids
,
319 static int __init
intel_punit_ipc_init(void)
321 return platform_driver_register(&intel_punit_ipc_driver
);
324 static void __exit
intel_punit_ipc_exit(void)
326 platform_driver_unregister(&intel_punit_ipc_driver
);
329 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
330 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
331 MODULE_LICENSE("GPL v2");
333 /* Some modules are dependent on this, so init earlier */
334 fs_initcall(intel_punit_ipc_init
);
335 module_exit(intel_punit_ipc_exit
);