linux/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * processor thermal device mailbox driver for Workload type hints
   4 * Copyright (c) 2020, Intel Corporation.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/module.h>
   9#include <linux/pci.h>
  10#include "processor_thermal_device.h"
  11
  12#define MBOX_CMD_WORKLOAD_TYPE_READ     0x0E
  13#define MBOX_CMD_WORKLOAD_TYPE_WRITE    0x0F
  14
  15#define MBOX_OFFSET_DATA                0x5810
  16#define MBOX_OFFSET_INTERFACE           0x5818
  17
  18#define MBOX_BUSY_BIT                   31
  19#define MBOX_RETRY_COUNT                100
  20
  21#define MBOX_DATA_BIT_VALID             31
  22#define MBOX_DATA_BIT_AC_DC             30
  23
  24static DEFINE_MUTEX(mbox_lock);
  25
  26static int send_mbox_cmd(struct pci_dev *pdev, u16 cmd_id, u32 cmd_data, u32 *cmd_resp)
  27{
  28        struct proc_thermal_device *proc_priv;
  29        u32 retries, data;
  30        int ret;
  31
  32        mutex_lock(&mbox_lock);
  33        proc_priv = pci_get_drvdata(pdev);
  34
  35        /* Poll for rb bit == 0 */
  36        retries = MBOX_RETRY_COUNT;
  37        do {
  38                data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
  39                if (data & BIT_ULL(MBOX_BUSY_BIT)) {
  40                        ret = -EBUSY;
  41                        continue;
  42                }
  43                ret = 0;
  44                break;
  45        } while (--retries);
  46
  47        if (ret)
  48                goto unlock_mbox;
  49
  50        if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_WRITE)
  51                writel(cmd_data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_DATA)));
  52
  53        /* Write command register */
  54        data = BIT_ULL(MBOX_BUSY_BIT) | cmd_id;
  55        writel(data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_INTERFACE)));
  56
  57        /* Poll for rb bit == 0 */
  58        retries = MBOX_RETRY_COUNT;
  59        do {
  60                data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
  61                if (data & BIT_ULL(MBOX_BUSY_BIT)) {
  62                        ret = -EBUSY;
  63                        continue;
  64                }
  65
  66                if (data) {
  67                        ret = -ENXIO;
  68                        goto unlock_mbox;
  69                }
  70
  71                if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_READ) {
  72                        data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_DATA));
  73                        *cmd_resp = data & 0xff;
  74                }
  75
  76                ret = 0;
  77                break;
  78        } while (--retries);
  79
  80unlock_mbox:
  81        mutex_unlock(&mbox_lock);
  82        return ret;
  83}
  84
  85int processor_thermal_send_mbox_cmd(struct pci_dev *pdev, u16 cmd_id, u32 cmd_data, u32 *cmd_resp)
  86{
  87        return send_mbox_cmd(pdev, cmd_id, cmd_data, cmd_resp);
  88}
  89EXPORT_SYMBOL_GPL(processor_thermal_send_mbox_cmd);
  90
  91/* List of workload types */
  92static const char * const workload_types[] = {
  93        "none",
  94        "idle",
  95        "semi_active",
  96        "bursty",
  97        "sustained",
  98        "battery_life",
  99        NULL
 100};
 101
 102
 103static ssize_t workload_available_types_show(struct device *dev,
 104                                               struct device_attribute *attr,
 105                                               char *buf)
 106{
 107        int i = 0;
 108        int ret = 0;
 109
 110        while (workload_types[i] != NULL)
 111                ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
 112
 113        ret += sprintf(&buf[ret], "\n");
 114
 115        return ret;
 116}
 117
 118static DEVICE_ATTR_RO(workload_available_types);
 119
 120static ssize_t workload_type_store(struct device *dev,
 121                                    struct device_attribute *attr,
 122                                    const char *buf, size_t count)
 123{
 124        struct pci_dev *pdev = to_pci_dev(dev);
 125        char str_preference[15];
 126        u32 data = 0;
 127        ssize_t ret;
 128
 129        ret = sscanf(buf, "%14s", str_preference);
 130        if (ret != 1)
 131                return -EINVAL;
 132
 133        ret = match_string(workload_types, -1, str_preference);
 134        if (ret < 0)
 135                return ret;
 136
 137        ret &= 0xff;
 138
 139        if (ret)
 140                data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
 141
 142        data |= ret;
 143
 144        ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data, NULL);
 145        if (ret)
 146                return false;
 147
 148        return count;
 149}
 150
 151static ssize_t workload_type_show(struct device *dev,
 152                                   struct device_attribute *attr,
 153                                   char *buf)
 154{
 155        struct pci_dev *pdev = to_pci_dev(dev);
 156        u32 cmd_resp;
 157        int ret;
 158
 159        ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
 160        if (ret)
 161                return false;
 162
 163        cmd_resp &= 0xff;
 164
 165        if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
 166                return -EINVAL;
 167
 168        return sprintf(buf, "%s\n", workload_types[cmd_resp]);
 169}
 170
 171static DEVICE_ATTR_RW(workload_type);
 172
 173static struct attribute *workload_req_attrs[] = {
 174        &dev_attr_workload_available_types.attr,
 175        &dev_attr_workload_type.attr,
 176        NULL
 177};
 178
 179static const struct attribute_group workload_req_attribute_group = {
 180        .attrs = workload_req_attrs,
 181        .name = "workload_request"
 182};
 183
 184
 185
 186static bool workload_req_created;
 187
 188int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
 189{
 190        u32 cmd_resp;
 191        int ret;
 192
 193        /* Check if there is a mailbox support, if fails return success */
 194        ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
 195        if (ret)
 196                return 0;
 197
 198        ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
 199        if (ret)
 200                return ret;
 201
 202        workload_req_created = true;
 203
 204        return 0;
 205}
 206EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
 207
 208void proc_thermal_mbox_remove(struct pci_dev *pdev)
 209{
 210        if (workload_req_created)
 211                sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
 212
 213        workload_req_created = false;
 214
 215}
 216EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
 217
 218MODULE_LICENSE("GPL v2");
 219