root/drivers/platform/x86/intel_punit_ipc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ipc_read_status
  2. ipc_write_cmd
  3. ipc_read_data_low
  4. ipc_read_data_high
  5. ipc_write_data_low
  6. ipc_write_data_high
  7. ipc_err_string
  8. intel_punit_ipc_check_status
  9. intel_punit_ipc_simple_command
  10. intel_punit_ipc_command
  11. intel_punit_ioc
  12. intel_punit_get_bars
  13. intel_punit_ipc_probe
  14. intel_punit_ipc_remove
  15. intel_punit_ipc_init
  16. intel_punit_ipc_exit

   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         struct resource *res;
 228         void __iomem *addr;
 229 
 230         /*
 231          * The following resources are required
 232          * - BIOS_IPC BASE_DATA
 233          * - BIOS_IPC BASE_IFACE
 234          */
 235         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 236         addr = devm_ioremap_resource(&pdev->dev, res);
 237         if (IS_ERR(addr))
 238                 return PTR_ERR(addr);
 239         punit_ipcdev->base[BIOS_IPC][BASE_DATA] = addr;
 240 
 241         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 242         addr = devm_ioremap_resource(&pdev->dev, res);
 243         if (IS_ERR(addr))
 244                 return PTR_ERR(addr);
 245         punit_ipcdev->base[BIOS_IPC][BASE_IFACE] = addr;
 246 
 247         /*
 248          * The following resources are optional
 249          * - ISPDRIVER_IPC BASE_DATA
 250          * - ISPDRIVER_IPC BASE_IFACE
 251          * - GTDRIVER_IPC BASE_DATA
 252          * - GTDRIVER_IPC BASE_IFACE
 253          */
 254         res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 255         if (res) {
 256                 addr = devm_ioremap_resource(&pdev->dev, res);
 257                 if (!IS_ERR(addr))
 258                         punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
 259         }
 260 
 261         res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
 262         if (res) {
 263                 addr = devm_ioremap_resource(&pdev->dev, res);
 264                 if (!IS_ERR(addr))
 265                         punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
 266         }
 267 
 268         res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
 269         if (res) {
 270                 addr = devm_ioremap_resource(&pdev->dev, res);
 271                 if (!IS_ERR(addr))
 272                         punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
 273         }
 274 
 275         res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
 276         if (res) {
 277                 addr = devm_ioremap_resource(&pdev->dev, res);
 278                 if (!IS_ERR(addr))
 279                         punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
 280         }
 281 
 282         return 0;
 283 }
 284 
 285 static int intel_punit_ipc_probe(struct platform_device *pdev)
 286 {
 287         int irq, ret;
 288 
 289         punit_ipcdev = devm_kzalloc(&pdev->dev,
 290                                     sizeof(*punit_ipcdev), GFP_KERNEL);
 291         if (!punit_ipcdev)
 292                 return -ENOMEM;
 293 
 294         platform_set_drvdata(pdev, punit_ipcdev);
 295 
 296         irq = platform_get_irq_optional(pdev, 0);
 297         if (irq < 0) {
 298                 dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
 299         } else {
 300                 ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
 301                                        IRQF_NO_SUSPEND, "intel_punit_ipc",
 302                                        &punit_ipcdev);
 303                 if (ret) {
 304                         dev_err(&pdev->dev, "Failed to request irq: %d\n", irq);
 305                         return ret;
 306                 }
 307                 punit_ipcdev->irq = irq;
 308         }
 309 
 310         ret = intel_punit_get_bars(pdev);
 311         if (ret)
 312                 goto out;
 313 
 314         punit_ipcdev->dev = &pdev->dev;
 315         mutex_init(&punit_ipcdev->lock);
 316         init_completion(&punit_ipcdev->cmd_complete);
 317 
 318 out:
 319         return ret;
 320 }
 321 
 322 static int intel_punit_ipc_remove(struct platform_device *pdev)
 323 {
 324         return 0;
 325 }
 326 
 327 static const struct acpi_device_id punit_ipc_acpi_ids[] = {
 328         { "INT34D4", 0 },
 329         { }
 330 };
 331 
 332 static struct platform_driver intel_punit_ipc_driver = {
 333         .probe = intel_punit_ipc_probe,
 334         .remove = intel_punit_ipc_remove,
 335         .driver = {
 336                 .name = "intel_punit_ipc",
 337                 .acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids),
 338         },
 339 };
 340 
 341 static int __init intel_punit_ipc_init(void)
 342 {
 343         return platform_driver_register(&intel_punit_ipc_driver);
 344 }
 345 
 346 static void __exit intel_punit_ipc_exit(void)
 347 {
 348         platform_driver_unregister(&intel_punit_ipc_driver);
 349 }
 350 
 351 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
 352 MODULE_DESCRIPTION("Intel P-Unit IPC driver");
 353 MODULE_LICENSE("GPL v2");
 354 
 355 /* Some modules are dependent on this, so init earlier */
 356 fs_initcall(intel_punit_ipc_init);
 357 module_exit(intel_punit_ipc_exit);

/* [<][>][^][v][top][bottom][index][help] */