18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
38c2ecf20Sopenharmony_ci * Copyright 2020 NXP Semiconductors
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include "sja1105.h"
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/* Since devlink regions have a fixed size and the static config has a variable
88c2ecf20Sopenharmony_ci * size, we need to calculate the maximum possible static config size by
98c2ecf20Sopenharmony_ci * creating a dummy config with all table entries populated to the max, and get
108c2ecf20Sopenharmony_ci * its packed length. This is done dynamically as opposed to simply hardcoding
118c2ecf20Sopenharmony_ci * a number, since currently not all static config tables are implemented, so
128c2ecf20Sopenharmony_ci * we are avoiding a possible code desynchronization.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_cistatic size_t sja1105_static_config_get_max_size(struct sja1105_private *priv)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct sja1105_static_config config;
178c2ecf20Sopenharmony_ci	enum sja1105_blk_idx blk_idx;
188c2ecf20Sopenharmony_ci	int rc;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	rc = sja1105_static_config_init(&config,
218c2ecf20Sopenharmony_ci					priv->info->static_ops,
228c2ecf20Sopenharmony_ci					priv->info->device_id);
238c2ecf20Sopenharmony_ci	if (rc)
248c2ecf20Sopenharmony_ci		return 0;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	for (blk_idx = 0; blk_idx < BLK_IDX_MAX; blk_idx++) {
278c2ecf20Sopenharmony_ci		struct sja1105_table *table = &config.tables[blk_idx];
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci		table->entry_count = table->ops->max_entry_count;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	return sja1105_static_config_get_length(&config);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int
368c2ecf20Sopenharmony_cisja1105_region_static_config_snapshot(struct devlink *dl,
378c2ecf20Sopenharmony_ci				      const struct devlink_region_ops *ops,
388c2ecf20Sopenharmony_ci				      struct netlink_ext_ack *extack,
398c2ecf20Sopenharmony_ci				      u8 **data)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
428c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
438c2ecf20Sopenharmony_ci	size_t max_len, len;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	len = sja1105_static_config_get_length(&priv->static_config);
468c2ecf20Sopenharmony_ci	max_len = sja1105_static_config_get_max_size(priv);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	*data = kcalloc(max_len, sizeof(u8), GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!*data)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return static_config_buf_prepare_for_upload(priv, *data, len);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct devlink_region_ops sja1105_region_static_config_ops = {
568c2ecf20Sopenharmony_ci	.name = "static-config",
578c2ecf20Sopenharmony_ci	.snapshot = sja1105_region_static_config_snapshot,
588c2ecf20Sopenharmony_ci	.destructor = kfree,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cienum sja1105_region_id {
628c2ecf20Sopenharmony_ci	SJA1105_REGION_STATIC_CONFIG = 0,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct sja1105_region {
668c2ecf20Sopenharmony_ci	const struct devlink_region_ops *ops;
678c2ecf20Sopenharmony_ci	size_t (*get_size)(struct sja1105_private *priv);
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct sja1105_region sja1105_regions[] = {
718c2ecf20Sopenharmony_ci	[SJA1105_REGION_STATIC_CONFIG] = {
728c2ecf20Sopenharmony_ci		.ops = &sja1105_region_static_config_ops,
738c2ecf20Sopenharmony_ci		.get_size = sja1105_static_config_get_max_size,
748c2ecf20Sopenharmony_ci	},
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int sja1105_setup_devlink_regions(struct dsa_switch *ds)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int i, num_regions = ARRAY_SIZE(sja1105_regions);
808c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
818c2ecf20Sopenharmony_ci	const struct devlink_region_ops *ops;
828c2ecf20Sopenharmony_ci	struct devlink_region *region;
838c2ecf20Sopenharmony_ci	u64 size;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *),
868c2ecf20Sopenharmony_ci				GFP_KERNEL);
878c2ecf20Sopenharmony_ci	if (!priv->regions)
888c2ecf20Sopenharmony_ci		return -ENOMEM;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	for (i = 0; i < num_regions; i++) {
918c2ecf20Sopenharmony_ci		size = sja1105_regions[i].get_size(priv);
928c2ecf20Sopenharmony_ci		ops = sja1105_regions[i].ops;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		region = dsa_devlink_region_create(ds, ops, 1, size);
958c2ecf20Sopenharmony_ci		if (IS_ERR(region)) {
968c2ecf20Sopenharmony_ci			while (--i >= 0)
978c2ecf20Sopenharmony_ci				dsa_devlink_region_destroy(priv->regions[i]);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci			kfree(priv->regions);
1008c2ecf20Sopenharmony_ci			return PTR_ERR(region);
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		priv->regions[i] = region;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void sja1105_teardown_devlink_regions(struct dsa_switch *ds)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int i, num_regions = ARRAY_SIZE(sja1105_regions);
1128c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	for (i = 0; i < num_regions; i++)
1158c2ecf20Sopenharmony_ci		dsa_devlink_region_destroy(priv->regions[i]);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	kfree(priv->regions);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv,
1218c2ecf20Sopenharmony_ci						  bool *be_vlan)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	*be_vlan = priv->best_effort_vlan_filtering;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
1298c2ecf20Sopenharmony_ci						  bool be_vlan)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct dsa_switch *ds = priv->ds;
1328c2ecf20Sopenharmony_ci	bool vlan_filtering;
1338c2ecf20Sopenharmony_ci	int port;
1348c2ecf20Sopenharmony_ci	int rc;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	priv->best_effort_vlan_filtering = be_vlan;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	rtnl_lock();
1398c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++) {
1408c2ecf20Sopenharmony_ci		struct switchdev_trans trans;
1418c2ecf20Sopenharmony_ci		struct dsa_port *dp;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		if (!dsa_is_user_port(ds, port))
1448c2ecf20Sopenharmony_ci			continue;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		dp = dsa_to_port(ds, port);
1478c2ecf20Sopenharmony_ci		vlan_filtering = dsa_port_is_vlan_filtering(dp);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		trans.ph_prepare = true;
1508c2ecf20Sopenharmony_ci		rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
1518c2ecf20Sopenharmony_ci		if (rc)
1528c2ecf20Sopenharmony_ci			break;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		trans.ph_prepare = false;
1558c2ecf20Sopenharmony_ci		rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
1568c2ecf20Sopenharmony_ci		if (rc)
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	rtnl_unlock();
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return rc;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cienum sja1105_devlink_param_id {
1658c2ecf20Sopenharmony_ci	SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
1668c2ecf20Sopenharmony_ci	SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciint sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
1708c2ecf20Sopenharmony_ci			      struct devlink_param_gset_ctx *ctx)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
1738c2ecf20Sopenharmony_ci	int err;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	switch (id) {
1768c2ecf20Sopenharmony_ci	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
1778c2ecf20Sopenharmony_ci		err = sja1105_best_effort_vlan_filtering_get(priv,
1788c2ecf20Sopenharmony_ci							     &ctx->val.vbool);
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci	default:
1818c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return err;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciint sja1105_devlink_param_set(struct dsa_switch *ds, u32 id,
1898c2ecf20Sopenharmony_ci			      struct devlink_param_gset_ctx *ctx)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
1928c2ecf20Sopenharmony_ci	int err;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	switch (id) {
1958c2ecf20Sopenharmony_ci	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
1968c2ecf20Sopenharmony_ci		err = sja1105_best_effort_vlan_filtering_set(priv,
1978c2ecf20Sopenharmony_ci							     ctx->val.vbool);
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	default:
2008c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return err;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const struct devlink_param sja1105_devlink_params[] = {
2088c2ecf20Sopenharmony_ci	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
2098c2ecf20Sopenharmony_ci				 "best_effort_vlan_filtering",
2108c2ecf20Sopenharmony_ci				 DEVLINK_PARAM_TYPE_BOOL,
2118c2ecf20Sopenharmony_ci				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int sja1105_setup_devlink_params(struct dsa_switch *ds)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return dsa_devlink_params_register(ds, sja1105_devlink_params,
2178c2ecf20Sopenharmony_ci					   ARRAY_SIZE(sja1105_devlink_params));
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void sja1105_teardown_devlink_params(struct dsa_switch *ds)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	dsa_devlink_params_unregister(ds, sja1105_devlink_params,
2238c2ecf20Sopenharmony_ci				      ARRAY_SIZE(sja1105_devlink_params));
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ciint sja1105_devlink_info_get(struct dsa_switch *ds,
2278c2ecf20Sopenharmony_ci			     struct devlink_info_req *req,
2288c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
2318c2ecf20Sopenharmony_ci	int rc;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	rc = devlink_info_driver_name_put(req, "sja1105");
2348c2ecf20Sopenharmony_ci	if (rc)
2358c2ecf20Sopenharmony_ci		return rc;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	rc = devlink_info_version_fixed_put(req,
2388c2ecf20Sopenharmony_ci					    DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
2398c2ecf20Sopenharmony_ci					    priv->info->name);
2408c2ecf20Sopenharmony_ci	return rc;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ciint sja1105_devlink_setup(struct dsa_switch *ds)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int rc;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	rc = sja1105_setup_devlink_params(ds);
2488c2ecf20Sopenharmony_ci	if (rc)
2498c2ecf20Sopenharmony_ci		return rc;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	rc = sja1105_setup_devlink_regions(ds);
2528c2ecf20Sopenharmony_ci	if (rc < 0) {
2538c2ecf20Sopenharmony_ci		sja1105_teardown_devlink_params(ds);
2548c2ecf20Sopenharmony_ci		return rc;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_civoid sja1105_devlink_teardown(struct dsa_switch *ds)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	sja1105_teardown_devlink_params(ds);
2638c2ecf20Sopenharmony_ci	sja1105_teardown_devlink_regions(ds);
2648c2ecf20Sopenharmony_ci}
265