Skip to content

Commit 83ca8b3

Browse files
fancerarndb
authored andcommitted
memory: Add Baikal-T1 L2-cache Control Block driver
Baikal-T1 SoC provides a way to tune the MIPS P5600 CM2 L2-cache performance up. It can be done by changing the L2-RAM Data/Tag/WS latencies in a dedicated register exposed by the system controller. The driver added by this commit provides a dts properties-based and sysfs-based interface for it. The device DT node is supposed to be a child of Baikal-T1 System Controller node. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Serge Semin <[email protected]> Cc: Alexey Malahov <[email protected]> Cc: Paul Burton <[email protected]> Cc: Olof Johansson <[email protected]> Cc: Rob Herring <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Signed-off-by: Arnd Bergmann <[email protected]>
1 parent 8f93662 commit 83ca8b3

File tree

3 files changed

+334
-0
lines changed

3 files changed

+334
-0
lines changed

drivers/memory/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ config ATMEL_EBI
4646
tree is used. This bus supports NANDs, external ethernet controller,
4747
SRAMs, ATA devices, etc.
4848

49+
config BT1_L2_CTL
50+
bool "Baikal-T1 CM2 L2-RAM Cache Control Block"
51+
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
52+
select MFD_SYSCON
53+
help
54+
Baikal-T1 CPU is based on the MIPS P5600 Warrior IP-core. The CPU
55+
resides Coherency Manager v2 with embedded 1MB L2-cache. It's
56+
possible to tune the L2 cache performance up by setting the data,
57+
tags and way-select latencies of RAM access. This driver provides a
58+
dt properties-based and sysfs interface for it.
59+
4960
config TI_AEMIF
5061
tristate "Texas Instruments AEMIF driver"
5162
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF

drivers/memory/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
1111
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
1212
obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
1313
obj-$(CONFIG_ARCH_BRCMSTB) += brcmstb_dpfe.o
14+
obj-$(CONFIG_BT1_L2_CTL) += bt1-l2-ctl.o
1415
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
1516
obj-$(CONFIG_TI_EMIF) += emif.o
1617
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o

drivers/memory/bt1-l2-ctl.c

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
4+
*
5+
* Authors:
6+
* Serge Semin <[email protected]>
7+
*
8+
* Baikal-T1 CM2 L2-cache Control Block driver.
9+
*/
10+
11+
#include <linux/kernel.h>
12+
#include <linux/module.h>
13+
#include <linux/bitfield.h>
14+
#include <linux/types.h>
15+
#include <linux/device.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/regmap.h>
18+
#include <linux/mfd/syscon.h>
19+
#include <linux/sysfs.h>
20+
#include <linux/of.h>
21+
22+
#define L2_CTL_REG 0x028
23+
#define L2_CTL_DATA_STALL_FLD 0
24+
#define L2_CTL_DATA_STALL_MASK GENMASK(1, L2_CTL_DATA_STALL_FLD)
25+
#define L2_CTL_TAG_STALL_FLD 2
26+
#define L2_CTL_TAG_STALL_MASK GENMASK(3, L2_CTL_TAG_STALL_FLD)
27+
#define L2_CTL_WS_STALL_FLD 4
28+
#define L2_CTL_WS_STALL_MASK GENMASK(5, L2_CTL_WS_STALL_FLD)
29+
#define L2_CTL_SET_CLKRATIO BIT(13)
30+
#define L2_CTL_CLKRATIO_LOCK BIT(31)
31+
32+
#define L2_CTL_STALL_MIN 0
33+
#define L2_CTL_STALL_MAX 3
34+
#define L2_CTL_STALL_SET_DELAY_US 1
35+
#define L2_CTL_STALL_SET_TOUT_US 1000
36+
37+
/*
38+
* struct l2_ctl - Baikal-T1 L2 Control block private data.
39+
* @dev: Pointer to the device structure.
40+
* @sys_regs: Baikal-T1 System Controller registers map.
41+
*/
42+
struct l2_ctl {
43+
struct device *dev;
44+
45+
struct regmap *sys_regs;
46+
};
47+
48+
/*
49+
* enum l2_ctl_stall - Baikal-T1 L2-cache-RAM stall identifier.
50+
* @L2_WSSTALL: Way-select latency.
51+
* @L2_TAGSTALL: Tag latency.
52+
* @L2_DATASTALL: Data latency.
53+
*/
54+
enum l2_ctl_stall {
55+
L2_WS_STALL,
56+
L2_TAG_STALL,
57+
L2_DATA_STALL
58+
};
59+
60+
/*
61+
* struct l2_ctl_device_attribute - Baikal-T1 L2-cache device attribute.
62+
* @dev_attr: Actual sysfs device attribute.
63+
* @id: L2-cache stall field identifier.
64+
*/
65+
struct l2_ctl_device_attribute {
66+
struct device_attribute dev_attr;
67+
enum l2_ctl_stall id;
68+
};
69+
#define to_l2_ctl_dev_attr(_dev_attr) \
70+
container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr)
71+
72+
#define L2_CTL_ATTR_RW(_name, _prefix, _id) \
73+
struct l2_ctl_device_attribute l2_ctl_attr_##_name = \
74+
{ __ATTR(_name, 0644, _prefix##_show, _prefix##_store), _id }
75+
76+
static int l2_ctl_get_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 *val)
77+
{
78+
u32 data = 0;
79+
int ret;
80+
81+
ret = regmap_read(l2->sys_regs, L2_CTL_REG, &data);
82+
if (ret)
83+
return ret;
84+
85+
switch (id) {
86+
case L2_WS_STALL:
87+
*val = FIELD_GET(L2_CTL_WS_STALL_MASK, data);
88+
break;
89+
case L2_TAG_STALL:
90+
*val = FIELD_GET(L2_CTL_TAG_STALL_MASK, data);
91+
break;
92+
case L2_DATA_STALL:
93+
*val = FIELD_GET(L2_CTL_DATA_STALL_MASK, data);
94+
break;
95+
default:
96+
return -EINVAL;
97+
}
98+
99+
return 0;
100+
}
101+
102+
static int l2_ctl_set_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 val)
103+
{
104+
u32 mask = 0, data = 0;
105+
int ret;
106+
107+
val = clamp_val(val, L2_CTL_STALL_MIN, L2_CTL_STALL_MAX);
108+
109+
switch (id) {
110+
case L2_WS_STALL:
111+
data = FIELD_PREP(L2_CTL_WS_STALL_MASK, val);
112+
mask = L2_CTL_WS_STALL_MASK;
113+
break;
114+
case L2_TAG_STALL:
115+
data = FIELD_PREP(L2_CTL_TAG_STALL_MASK, val);
116+
mask = L2_CTL_TAG_STALL_MASK;
117+
break;
118+
case L2_DATA_STALL:
119+
data = FIELD_PREP(L2_CTL_DATA_STALL_MASK, val);
120+
mask = L2_CTL_DATA_STALL_MASK;
121+
break;
122+
default:
123+
return -EINVAL;
124+
}
125+
126+
data |= L2_CTL_SET_CLKRATIO;
127+
mask |= L2_CTL_SET_CLKRATIO;
128+
129+
ret = regmap_update_bits(l2->sys_regs, L2_CTL_REG, mask, data);
130+
if (ret)
131+
return ret;
132+
133+
return regmap_read_poll_timeout(l2->sys_regs, L2_CTL_REG, data,
134+
data & L2_CTL_CLKRATIO_LOCK,
135+
L2_CTL_STALL_SET_DELAY_US,
136+
L2_CTL_STALL_SET_TOUT_US);
137+
}
138+
139+
static void l2_ctl_clear_data(void *data)
140+
{
141+
struct l2_ctl *l2 = data;
142+
struct platform_device *pdev = to_platform_device(l2->dev);
143+
144+
platform_set_drvdata(pdev, NULL);
145+
}
146+
147+
static struct l2_ctl *l2_ctl_create_data(struct platform_device *pdev)
148+
{
149+
struct device *dev = &pdev->dev;
150+
struct l2_ctl *l2;
151+
int ret;
152+
153+
l2 = devm_kzalloc(dev, sizeof(*l2), GFP_KERNEL);
154+
if (!l2)
155+
return ERR_PTR(-ENOMEM);
156+
157+
ret = devm_add_action(dev, l2_ctl_clear_data, l2);
158+
if (ret) {
159+
dev_err(dev, "Can't add L2 CTL data clear action\n");
160+
return ERR_PTR(ret);
161+
}
162+
163+
l2->dev = dev;
164+
platform_set_drvdata(pdev, l2);
165+
166+
return l2;
167+
}
168+
169+
static int l2_ctl_find_sys_regs(struct l2_ctl *l2)
170+
{
171+
l2->sys_regs = syscon_node_to_regmap(l2->dev->of_node->parent);
172+
if (IS_ERR(l2->sys_regs)) {
173+
dev_err(l2->dev, "Couldn't get L2 CTL register map\n");
174+
return PTR_ERR(l2->sys_regs);
175+
}
176+
177+
return 0;
178+
}
179+
180+
static int l2_ctl_of_parse_property(struct l2_ctl *l2, enum l2_ctl_stall id,
181+
const char *propname)
182+
{
183+
int ret = 0;
184+
u32 data;
185+
186+
if (!of_property_read_u32(l2->dev->of_node, propname, &data)) {
187+
ret = l2_ctl_set_latency(l2, id, data);
188+
if (ret)
189+
dev_err(l2->dev, "Invalid value of '%s'\n", propname);
190+
}
191+
192+
return ret;
193+
}
194+
195+
static int l2_ctl_of_parse(struct l2_ctl *l2)
196+
{
197+
int ret;
198+
199+
ret = l2_ctl_of_parse_property(l2, L2_WS_STALL, "baikal,l2-ws-latency");
200+
if (ret)
201+
return ret;
202+
203+
ret = l2_ctl_of_parse_property(l2, L2_TAG_STALL, "baikal,l2-tag-latency");
204+
if (ret)
205+
return ret;
206+
207+
return l2_ctl_of_parse_property(l2, L2_DATA_STALL,
208+
"baikal,l2-data-latency");
209+
}
210+
211+
static ssize_t l2_ctl_latency_show(struct device *dev,
212+
struct device_attribute *attr,
213+
char *buf)
214+
{
215+
struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
216+
struct l2_ctl *l2 = dev_get_drvdata(dev);
217+
u32 data;
218+
int ret;
219+
220+
ret = l2_ctl_get_latency(l2, devattr->id, &data);
221+
if (ret)
222+
return ret;
223+
224+
return scnprintf(buf, PAGE_SIZE, "%u\n", data);
225+
}
226+
227+
static ssize_t l2_ctl_latency_store(struct device *dev,
228+
struct device_attribute *attr,
229+
const char *buf, size_t count)
230+
{
231+
struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr);
232+
struct l2_ctl *l2 = dev_get_drvdata(dev);
233+
u32 data;
234+
int ret;
235+
236+
if (kstrtouint(buf, 0, &data) < 0)
237+
return -EINVAL;
238+
239+
ret = l2_ctl_set_latency(l2, devattr->id, data);
240+
if (ret)
241+
return ret;
242+
243+
return count;
244+
}
245+
static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL);
246+
static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL);
247+
static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL);
248+
249+
static struct attribute *l2_ctl_sysfs_attrs[] = {
250+
&l2_ctl_attr_l2_ws_latency.dev_attr.attr,
251+
&l2_ctl_attr_l2_tag_latency.dev_attr.attr,
252+
&l2_ctl_attr_l2_data_latency.dev_attr.attr,
253+
NULL
254+
};
255+
ATTRIBUTE_GROUPS(l2_ctl_sysfs);
256+
257+
static void l2_ctl_remove_sysfs(void *data)
258+
{
259+
struct l2_ctl *l2 = data;
260+
261+
device_remove_groups(l2->dev, l2_ctl_sysfs_groups);
262+
}
263+
264+
static int l2_ctl_init_sysfs(struct l2_ctl *l2)
265+
{
266+
int ret;
267+
268+
ret = device_add_groups(l2->dev, l2_ctl_sysfs_groups);
269+
if (ret) {
270+
dev_err(l2->dev, "Failed to create L2 CTL sysfs nodes\n");
271+
return ret;
272+
}
273+
274+
ret = devm_add_action_or_reset(l2->dev, l2_ctl_remove_sysfs, l2);
275+
if (ret)
276+
dev_err(l2->dev, "Can't add L2 CTL sysfs remove action\n");
277+
278+
return ret;
279+
}
280+
281+
static int l2_ctl_probe(struct platform_device *pdev)
282+
{
283+
struct l2_ctl *l2;
284+
int ret;
285+
286+
l2 = l2_ctl_create_data(pdev);
287+
if (IS_ERR(l2))
288+
return PTR_ERR(l2);
289+
290+
ret = l2_ctl_find_sys_regs(l2);
291+
if (ret)
292+
return ret;
293+
294+
ret = l2_ctl_of_parse(l2);
295+
if (ret)
296+
return ret;
297+
298+
ret = l2_ctl_init_sysfs(l2);
299+
if (ret)
300+
return ret;
301+
302+
return 0;
303+
}
304+
305+
static const struct of_device_id l2_ctl_of_match[] = {
306+
{ .compatible = "baikal,bt1-l2-ctl" },
307+
{ }
308+
};
309+
MODULE_DEVICE_TABLE(of, l2_ctl_of_match);
310+
311+
static struct platform_driver l2_ctl_driver = {
312+
.probe = l2_ctl_probe,
313+
.driver = {
314+
.name = "bt1-l2-ctl",
315+
.of_match_table = l2_ctl_of_match
316+
}
317+
};
318+
module_platform_driver(l2_ctl_driver);
319+
320+
MODULE_AUTHOR("Serge Semin <[email protected]>");
321+
MODULE_DESCRIPTION("Baikal-T1 L2-cache driver");
322+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)