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
;
230 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
231 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
233 return PTR_ERR(addr
);
234 punit_ipcdev
->base
[BIOS_IPC
][BASE_DATA
] = addr
;
236 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
237 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
239 return PTR_ERR(addr
);
240 punit_ipcdev
->base
[BIOS_IPC
][BASE_IFACE
] = addr
;
242 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
243 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
245 return PTR_ERR(addr
);
246 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_DATA
] = addr
;
248 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 3);
249 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
251 return PTR_ERR(addr
);
252 punit_ipcdev
->base
[ISPDRIVER_IPC
][BASE_IFACE
] = addr
;
254 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 4);
255 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
257 return PTR_ERR(addr
);
258 punit_ipcdev
->base
[GTDRIVER_IPC
][BASE_DATA
] = addr
;
260 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 5);
261 addr
= devm_ioremap_resource(&pdev
->dev
, res
);
263 return PTR_ERR(addr
);
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(pdev
, 0);
282 punit_ipcdev
->irq
= 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
);
307 static int intel_punit_ipc_remove(struct platform_device
*pdev
)
312 static const struct acpi_device_id punit_ipc_acpi_ids
[] = {
317 static struct platform_driver intel_punit_ipc_driver
= {
318 .probe
= intel_punit_ipc_probe
,
319 .remove
= intel_punit_ipc_remove
,
321 .name
= "intel_punit_ipc",
322 .acpi_match_table
= ACPI_PTR(punit_ipc_acpi_ids
),
326 static int __init
intel_punit_ipc_init(void)
328 return platform_driver_register(&intel_punit_ipc_driver
);
331 static void __exit
intel_punit_ipc_exit(void)
333 platform_driver_unregister(&intel_punit_ipc_driver
);
336 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
337 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
338 MODULE_LICENSE("GPL v2");
340 /* Some modules are dependent on this, so init earlier */
341 fs_initcall(intel_punit_ipc_init
);
342 module_exit(intel_punit_ipc_exit
);