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