324 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC4 *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 0x02823#define L2_CTL_DATA_STALL_FLD 024#define L2_CTL_DATA_STALL_MASK GENMASK(1, L2_CTL_DATA_STALL_FLD)25#define L2_CTL_TAG_STALL_FLD 226#define L2_CTL_TAG_STALL_MASK GENMASK(3, L2_CTL_TAG_STALL_FLD)27#define L2_CTL_WS_STALL_FLD 428#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 033#define L2_CTL_STALL_MAX 334#define L2_CTL_STALL_SET_DELAY_US 135#define L2_CTL_STALL_SET_TOUT_US 100036 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_STALL58};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 NULL256};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_match318 }319};320module_platform_driver(l2_ctl_driver);321 322MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");323MODULE_DESCRIPTION("Baikal-T1 L2-cache driver");324