summaryrefslogtreecommitdiff
path: root/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
blob: 4d3bd32ff9eae58ba82f94a4b9bf2351896feacd (plain)
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
// 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");