1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* processor thermal device mailbox driver for Workload type hints
* Copyright (c) 2020, Intel Corporation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include "processor_thermal_device.h"
#define MBOX_OFFSET_DATA 0x5810
#define MBOX_OFFSET_INTERFACE 0x5818
#define MBOX_BUSY_BIT 31
#define MBOX_RETRY_COUNT 100
static DEFINE_MUTEX(mbox_lock);
static int wait_for_mbox_ready(struct proc_thermal_device *proc_priv)
{
u32 retries, data;
int ret;
/* Poll for rb bit == 0 */
retries = MBOX_RETRY_COUNT;
do {
data = readl(proc_priv->mmio_base + MBOX_OFFSET_INTERFACE);
if (data & BIT_ULL(MBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
ret = 0;
break;
} while (--retries);
return ret;
}
static int send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
{
struct proc_thermal_device *proc_priv;
u32 reg_data;
int ret;
proc_priv = pci_get_drvdata(pdev);
ret = wait_for_mbox_ready(proc_priv);
if (ret)
return ret;
writel(data, (proc_priv->mmio_base + MBOX_OFFSET_DATA));
/* Write command register */
reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
return wait_for_mbox_ready(proc_priv);
}
static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
{
struct proc_thermal_device *proc_priv;
u32 reg_data;
int ret;
proc_priv = pci_get_drvdata(pdev);
ret = wait_for_mbox_ready(proc_priv);
if (ret)
return ret;
/* Write command register */
reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
ret = wait_for_mbox_ready(proc_priv);
if (ret)
return ret;
if (id == MBOX_CMD_WORKLOAD_TYPE_READ)
*resp = readl(proc_priv->mmio_base + MBOX_OFFSET_DATA);
else
*resp = readq(proc_priv->mmio_base + MBOX_OFFSET_DATA);
return 0;
}
int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
{
int ret;
mutex_lock(&mbox_lock);
ret = send_mbox_read_cmd(pdev, id, resp);
mutex_unlock(&mbox_lock);
return ret;
}
EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_read_cmd, "INT340X_THERMAL");
int processor_thermal_send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
{
int ret;
mutex_lock(&mbox_lock);
ret = send_mbox_write_cmd(pdev, id, data);
mutex_unlock(&mbox_lock);
return ret;
}
EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_write_cmd, "INT340X_THERMAL");
#define MBOX_CAMARILLO_RD_INTR_CONFIG 0x1E
#define MBOX_CAMARILLO_WR_INTR_CONFIG 0x1F
#define WLT_TW_MASK GENMASK_ULL(30, 24)
#define SOC_PREDICTION_TW_SHIFT 24
int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable,
int enable_bit, int time_window)
{
u64 data;
int ret;
if (!pdev)
return -ENODEV;
mutex_lock(&mbox_lock);
/* Do read modify write for MBOX_CAMARILLO_RD_INTR_CONFIG */
ret = send_mbox_read_cmd(pdev, MBOX_CAMARILLO_RD_INTR_CONFIG, &data);
if (ret) {
dev_err(&pdev->dev, "MBOX_CAMARILLO_RD_INTR_CONFIG failed\n");
goto unlock;
}
if (time_window >= 0) {
data &= ~WLT_TW_MASK;
/* Program notification delay */
data |= ((u64)time_window << SOC_PREDICTION_TW_SHIFT) & WLT_TW_MASK;
}
if (enable)
data |= BIT(enable_bit);
else
data &= ~BIT(enable_bit);
ret = send_mbox_write_cmd(pdev, MBOX_CAMARILLO_WR_INTR_CONFIG, data);
if (ret)
dev_err(&pdev->dev, "MBOX_CAMARILLO_WR_INTR_CONFIG failed\n");
unlock:
mutex_unlock(&mbox_lock);
return ret;
}
EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, "INT340X_THERMAL");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Processor Thermal Mail Box Interface");
|