1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for the Intel P-Unit Mailbox IPC mechanism 4 * 5 * (C) Copyright 2015 Intel Corporation 6 * 7 * The heart of the P-Unit is the Foxton microcontroller and its firmware, 8 * which provide mailbox interface for power management usage. 9 */ 10 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> 16 #include <linux/io.h> 17 #include <linux/mod_devicetable.h> 18 #include <linux/module.h> 19 #include <linux/platform_device.h> 20 21 #include <asm/intel_punit_ipc.h> 22 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 31 32 #define CMD_TIMEOUT_SECONDS 1 33 34 enum { 35 BASE_DATA = 0, 36 BASE_IFACE, 37 BASE_MAX, 38 }; 39 40 typedef struct { 41 struct device *dev; 42 struct mutex lock; 43 int irq; 44 struct completion cmd_complete; 45 /* base of interface and data registers */ 46 void __iomem *base[RESERVED_IPC][BASE_MAX]; 47 IPC_TYPE type; 48 } IPC_DEV; 49 50 static IPC_DEV *punit_ipcdev; 51 52 static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) 53 { 54 return readl(ipcdev->base[type][BASE_IFACE]); 55 } 56 57 static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd) 58 { 59 writel(cmd, ipcdev->base[type][BASE_IFACE]); 60 } 61 62 static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) 63 { 64 return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW); 65 } 66 67 static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) 68 { 69 return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH); 70 } 71 72 static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32 data) 73 { 74 writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW); 75 } 76 77 static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32 data) 78 { 79 writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH); 80 } 81 82 static const char *ipc_err_string(int error) 83 { 84 if (error == IPC_PUNIT_ERR_SUCCESS) 85 return "no error"; 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) 97 return "vr error"; 98 else 99 return "unknown error"; 100 } 101 102 static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type) 103 { 104 int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC; 105 int errcode; 106 int status; 107 108 if (ipcdev->irq) { 109 if (!wait_for_completion_timeout(&ipcdev->cmd_complete, 110 CMD_TIMEOUT_SECONDS * HZ)) { 111 dev_err(ipcdev->dev, "IPC timed out\n"); 112 return -ETIMEDOUT; 113 } 114 } else { 115 while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --loops) 116 udelay(1); 117 if (!loops) { 118 dev_err(ipcdev->dev, "IPC timed out\n"); 119 return -ETIMEDOUT; 120 } 121 } 122 123 status = ipc_read_status(ipcdev, type); 124 errcode = status & CMD_ERRCODE_MASK; 125 if (errcode) { 126 dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n", 127 ipc_err_string(errcode), status); 128 return -EIO; 129 } 130 131 return 0; 132 } 133 134 /** 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. 139 * 140 * Send a IPC command to P-Unit when there is no data transaction 141 * 142 * Return: IPC error code or 0 on success. 143 */ 144 int intel_punit_ipc_simple_command(int cmd, int para1, int para2) 145 { 146 IPC_DEV *ipcdev = punit_ipcdev; 147 IPC_TYPE type; 148 u32 val; 149 int ret; 150 151 mutex_lock(&ipcdev->lock); 152 153 reinit_completion(&ipcdev->cmd_complete); 154 type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET; 155 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); 160 161 mutex_unlock(&ipcdev->lock); 162 163 return ret; 164 } 165 EXPORT_SYMBOL(intel_punit_ipc_simple_command); 166 167 /** 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. 173 * @out: Output data. 174 * 175 * Send a IPC command to P-Unit with data transaction 176 * 177 * Return: IPC error code or 0 on success. 178 */ 179 int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out) 180 { 181 IPC_DEV *ipcdev = punit_ipcdev; 182 IPC_TYPE type; 183 u32 val; 184 int ret; 185 186 mutex_lock(&ipcdev->lock); 187 188 reinit_completion(&ipcdev->cmd_complete); 189 type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET; 190 191 if (in) { 192 ipc_write_data_low(ipcdev, type, *in); 193 if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC) 194 ipc_write_data_high(ipcdev, type, *++in); 195 } 196 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); 200 201 ret = intel_punit_ipc_check_status(ipcdev, type); 202 if (ret) 203 goto out; 204 205 if (out) { 206 *out = ipc_read_data_low(ipcdev, type); 207 if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC) 208 *++out = ipc_read_data_high(ipcdev, type); 209 } 210 211 out: 212 mutex_unlock(&ipcdev->lock); 213 return ret; 214 } 215 EXPORT_SYMBOL_GPL(intel_punit_ipc_command); 216 217 static irqreturn_t intel_punit_ioc(int irq, void *dev_id) 218 { 219 IPC_DEV *ipcdev = dev_id; 220 221 complete(&ipcdev->cmd_complete); 222 return IRQ_HANDLED; 223 } 224 225 static int intel_punit_get_bars(struct platform_device *pdev) 226 { 227 void __iomem *addr; 228 229 /* 230 * The following resources are required 231 * - BIOS_IPC BASE_DATA 232 * - BIOS_IPC BASE_IFACE 233 */ 234 addr = devm_platform_ioremap_resource(pdev, 0); 235 if (IS_ERR(addr)) 236 return PTR_ERR(addr); 237 punit_ipcdev->base[BIOS_IPC][BASE_DATA] = addr; 238 239 addr = devm_platform_ioremap_resource(pdev, 1); 240 if (IS_ERR(addr)) 241 return PTR_ERR(addr); 242 punit_ipcdev->base[BIOS_IPC][BASE_IFACE] = addr; 243 244 /* 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 250 */ 251 addr = devm_platform_ioremap_resource(pdev, 2); 252 if (!IS_ERR(addr)) 253 punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; 254 255 addr = devm_platform_ioremap_resource(pdev, 3); 256 if (!IS_ERR(addr)) 257 punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; 258 259 addr = devm_platform_ioremap_resource(pdev, 4); 260 if (!IS_ERR(addr)) 261 punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; 262 263 addr = devm_platform_ioremap_resource(pdev, 5); 264 if (!IS_ERR(addr)) 265 punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr; 266 267 return 0; 268 } 269 270 static int intel_punit_ipc_probe(struct platform_device *pdev) 271 { 272 int irq, ret; 273 274 punit_ipcdev = devm_kzalloc(&pdev->dev, 275 sizeof(*punit_ipcdev), GFP_KERNEL); 276 if (!punit_ipcdev) 277 return -ENOMEM; 278 279 platform_set_drvdata(pdev, punit_ipcdev); 280 281 irq = platform_get_irq_optional(pdev, 0); 282 if (irq < 0) { 283 dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n"); 284 } else { 285 ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc, 286 IRQF_NO_SUSPEND, "intel_punit_ipc", 287 &punit_ipcdev); 288 if (ret) { 289 dev_err(&pdev->dev, "Failed to request irq: %d\n", irq); 290 return ret; 291 } 292 punit_ipcdev->irq = irq; 293 } 294 295 ret = intel_punit_get_bars(pdev); 296 if (ret) 297 return ret; 298 299 punit_ipcdev->dev = &pdev->dev; 300 mutex_init(&punit_ipcdev->lock); 301 init_completion(&punit_ipcdev->cmd_complete); 302 303 return 0; 304 } 305 306 static int intel_punit_ipc_remove(struct platform_device *pdev) 307 { 308 return 0; 309 } 310 311 static const struct acpi_device_id punit_ipc_acpi_ids[] = { 312 { "INT34D4", 0 }, 313 { } 314 }; 315 MODULE_DEVICE_TABLE(acpi, punit_ipc_acpi_ids); 316 317 static struct platform_driver intel_punit_ipc_driver = { 318 .probe = intel_punit_ipc_probe, 319 .remove = intel_punit_ipc_remove, 320 .driver = { 321 .name = "intel_punit_ipc", 322 .acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), 323 }, 324 }; 325 326 static int __init intel_punit_ipc_init(void) 327 { 328 return platform_driver_register(&intel_punit_ipc_driver); 329 } 330 331 static void __exit intel_punit_ipc_exit(void) 332 { 333 platform_driver_unregister(&intel_punit_ipc_driver); 334 } 335 336 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); 337 MODULE_DESCRIPTION("Intel P-Unit IPC driver"); 338 MODULE_LICENSE("GPL v2"); 339 340 /* Some modules are dependent on this, so init earlier */ 341 fs_initcall(intel_punit_ipc_init); 342 module_exit(intel_punit_ipc_exit); 343