aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/marvell/octeontx2-ccu/ccu.c
blob: 60bfa1438791e7f4ef2c1c0e2ddd6c77592c75e3 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// SPDX-License-Identifier: GPL-2.0
/* Marvell OcteonTx2 CCU controller driver
 *
 * Copyright (C) 2021 Marvell.
 */

#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>

#define CCU_BASE	0x87E050000000
#define CCS_BASE	0x87E087100000

#define CCS_MPARX_MASK(pid)	(0x1000 | ((pid) & 0xff) << 3)

#define CCUX_TADX_MPARX_ACNT(ccu, tad, pid)	\
	(0x401000		|		\
	((ccu) & 0x3) << 24	|		\
	((tad) & 0x1) << 21	|		\
	((pid) & 0xff) << 4)

#define CCUX_TADX_MPARX_HCNT(ccu, tad, pid)	\
	(0x401008		|		\
	(((ccu) & 0x3) << 24)	|		\
	(((tad) & 0x1) << 21)	|		\
	(((pid) & 0xff) << 4))

#define MPARID_MAX	256

/* Global variables */
void __iomem *ccu_base;
void __iomem *ccs_base;
static u8 mparid;
static u64 waymask;
static u32 cpid_mask;
struct dentry *ccu_dent;
struct cpumask cpid_cpumask;
static u8 mparid_configured[MPARID_MAX];

#define COUNTER_BUF_SIZE	65536
#define	CONFIG_BUF_SIZE		4096
char *counter_buf;
char *config_buf;

/* Low level accessor functions */
static inline void apsys_cpidel2_read_remote(void *data)
{
	u64 val;

	asm volatile ("mrs %0, s3_4_c11_c6_4" : "=r" (val) : );
	*(u64 *)data = val;
}

static inline void apsys_cpidel2_write_remote(void *data)
{
	u64 val;

	val = *(u64 *)data;
	asm volatile ("msr s3_4_c11_c6_4, %0" : : "r" (val));
}

static inline u64 ccsreg_read(u64 offset)
{
	return readq(ccs_base + offset);
}

static inline void ccsreg_write(u64 offset, u64 val)
{
	writeq(val, ccs_base + offset);
}

static inline u64 ccureg_read(u64 offset)
{
	return readq(ccu_base + offset);
}

static inline void ccureg_write(u64 offset, u64 val)
{
	writeq(val, ccu_base + offset);
}

/* Mask LLC ways for a partition id */
static inline void ccsreg_mparmask_set(int mparid, u64 waymask)
{
	/* Bits [13:0] mask LTG ways, bits [33:14] mask DTG ways */
	ccsreg_write(CCS_MPARX_MASK(mparid), waymask);
}

static ssize_t otx2_ccu_config_write(struct file *file, const char *buf,
				     size_t count, loff_t *position)
{
	int cpu;

	pr_info("ccu: configuring mparid:%d waymask:0x%llx cpumask:0x%x\n",
		mparid, waymask, cpid_mask);

	/* Configure the LLC ways */
	ccsreg_mparmask_set(mparid, waymask);

	/* Create a bitmap */
	cpumask_clear(&cpid_cpumask);
	for_each_set_bit(cpu, (unsigned long *)&cpid_mask, num_present_cpus())
		cpumask_set_cpu(cpu, &cpid_cpumask);

	/* Configure mparid for all cpus in the bitmap */
	for_each_cpu(cpu, &cpid_cpumask) {
		smp_call_function_single(cpu, apsys_cpidel2_write_remote,
					 &mparid, true);
	}

	/* Some book keeping */
	mparid_configured[mparid] = 1;

	return count;
}

static ssize_t otx2_ccu_config_read(struct file *file, char __user *buf,
				    size_t count, loff_t *position)
{
	u64 val, waymask;
	u32 cpu, sz = 0;
	u8 mparid;

	memset(config_buf, 0, CONFIG_BUF_SIZE);

	/* Read the mparid configured for each cpu and then read
	 * the associated waymask for that mparid.
	 */
	for_each_cpu(cpu, cpu_present_mask) {
		smp_call_function_single(cpu, apsys_cpidel2_read_remote,
					 &val, true);
		mparid = (u8)val;
		waymask = ccsreg_read(CCS_MPARX_MASK(mparid));
		sz += snprintf(config_buf + sz, CONFIG_BUF_SIZE - sz,
			       "core:%d mparid:%d waymask:0x%llx\n",
			       cpu, mparid, waymask);
	}

	/* Copy to the user buffer */
	return simple_read_from_buffer(buf, count, position, config_buf, sz);
}

static const struct file_operations otx2_ccu_config_fops = {
	.read = otx2_ccu_config_read,
	.write = otx2_ccu_config_write,
};

static ssize_t otx2_ccu_counter_read(struct file *file, char __user *buf,
				 size_t count, loff_t *position)
{
	int ccu, tad, pid;
	u64 acnt, hcnt;
	u32 sz = 0;

	memset(counter_buf, 0, COUNTER_BUF_SIZE);

	/* Read the Allocate and Hit counter values only for MPARIDs
	 * that were configured.
	 */
	for (pid = 0; pid < MPARID_MAX; pid++) {
		if (!mparid_configured[pid])
			continue;

		for (ccu = 0; ccu < 4; ccu++) {
			for (tad = 0; tad < 2; tad++) {
				acnt = ccureg_read(CCUX_TADX_MPARX_ACNT(ccu, tad, pid));
				hcnt = ccureg_read(CCUX_TADX_MPARX_HCNT(ccu, tad, pid));
				sz += snprintf(counter_buf + sz, COUNTER_BUF_SIZE - sz,
					      "CCU:%d TAD:%d MPARID:%d ALLOC:0x%llx HIT:0x%llx\n",
					      ccu, tad, pid, acnt, hcnt);
			}
		}
	}

	/* Copy to the user buffer */
	return simple_read_from_buffer(buf, count, position, counter_buf, sz);
}

static const struct file_operations otx2_ccu_counter_fops = {
	.read = otx2_ccu_counter_read,
};

static int __init otx2_ccu_init(void)
{
	u32 cpuid = read_cpuid_id();

	cpuid &= (MIDR_IMPLEMENTOR_MASK | (0xff0 << MIDR_PARTNUM_SHIFT));

	/* Valid only for OcteonTX2 Family */
	if (((ARM_CPU_IMP_CAVIUM << MIDR_IMPLEMENTOR_SHIFT) |
	    (0xB0 << MIDR_PARTNUM_SHIFT)) != cpuid)
		return -ENODEV;

	/* CCU Base address */
	ccu_base = ioremap(CCU_BASE, 0x4000000);
	if (IS_ERR(ccu_base)) {
		pr_err("%s: CCU ioremap failed\n", __func__);
		return PTR_ERR(ccu_base);
	}

	/* CCS Base address */
	ccs_base = ioremap(CCS_BASE, 0x1000);
	if (IS_ERR(ccs_base)) {
		pr_err("%s: CCS ioremap failed\n", __func__);
		return PTR_ERR(ccs_base);
	}

	/* Add debufs hooks */
	ccu_dent = debugfs_create_dir("ccu", NULL);

	debugfs_create_u8("mparid", 0644, ccu_dent, &mparid);

	debugfs_create_u64("waymask", 0644, ccu_dent, &waymask);

	debugfs_create_u32("cpumask", 0644, ccu_dent, &cpid_mask);

	debugfs_create_file("config", 0644, ccu_dent, NULL, &otx2_ccu_config_fops);

	debugfs_create_file("counter", 0644, ccu_dent, NULL, &otx2_ccu_counter_fops);

	counter_buf = kzalloc(COUNTER_BUF_SIZE, GFP_KERNEL);
	if (IS_ERR(counter_buf)) {
		pr_err("Failed to allocate memory for counter buffer\n");
		return PTR_ERR(counter_buf);
	}

	config_buf = kzalloc(CONFIG_BUF_SIZE, GFP_KERNEL);
	if (IS_ERR(config_buf)) {
		pr_err("Failed to allocate memory for config buffer\n");
		kfree(counter_buf);
		return PTR_ERR(config_buf);
	}

	/* Zero MPARID is the default configuration for all CPUs at bootup */
	mparid_configured[0] = 1;
	return 0;
}

static void __exit otx2_ccu_exit(void)
{
	kfree(config_buf);
	kfree(counter_buf);
	debugfs_remove_recursive(ccu_dent);
}

module_init(otx2_ccu_init);
module_exit(otx2_ccu_exit);

MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell OcteonTX2 CCU controller Driver");
MODULE_LICENSE("GPL v2");