brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 78bd71b Raw
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