162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
362306a36Sopenharmony_ci * Copyright 2020 NXP
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include "sja1105.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/* Since devlink regions have a fixed size and the static config has a variable
862306a36Sopenharmony_ci * size, we need to calculate the maximum possible static config size by
962306a36Sopenharmony_ci * creating a dummy config with all table entries populated to the max, and get
1062306a36Sopenharmony_ci * its packed length. This is done dynamically as opposed to simply hardcoding
1162306a36Sopenharmony_ci * a number, since currently not all static config tables are implemented, so
1262306a36Sopenharmony_ci * we are avoiding a possible code desynchronization.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_cistatic size_t sja1105_static_config_get_max_size(struct sja1105_private *priv)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct sja1105_static_config config;
1762306a36Sopenharmony_ci	enum sja1105_blk_idx blk_idx;
1862306a36Sopenharmony_ci	int rc;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	rc = sja1105_static_config_init(&config,
2162306a36Sopenharmony_ci					priv->info->static_ops,
2262306a36Sopenharmony_ci					priv->info->device_id);
2362306a36Sopenharmony_ci	if (rc)
2462306a36Sopenharmony_ci		return 0;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	for (blk_idx = 0; blk_idx < BLK_IDX_MAX; blk_idx++) {
2762306a36Sopenharmony_ci		struct sja1105_table *table = &config.tables[blk_idx];
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci		table->entry_count = table->ops->max_entry_count;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	return sja1105_static_config_get_length(&config);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int
3662306a36Sopenharmony_cisja1105_region_static_config_snapshot(struct devlink *dl,
3762306a36Sopenharmony_ci				      const struct devlink_region_ops *ops,
3862306a36Sopenharmony_ci				      struct netlink_ext_ack *extack,
3962306a36Sopenharmony_ci				      u8 **data)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
4262306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
4362306a36Sopenharmony_ci	size_t max_len, len;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	len = sja1105_static_config_get_length(&priv->static_config);
4662306a36Sopenharmony_ci	max_len = sja1105_static_config_get_max_size(priv);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	*data = kcalloc(max_len, sizeof(u8), GFP_KERNEL);
4962306a36Sopenharmony_ci	if (!*data)
5062306a36Sopenharmony_ci		return -ENOMEM;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return static_config_buf_prepare_for_upload(priv, *data, len);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct devlink_region_ops sja1105_region_static_config_ops = {
5662306a36Sopenharmony_ci	.name = "static-config",
5762306a36Sopenharmony_ci	.snapshot = sja1105_region_static_config_snapshot,
5862306a36Sopenharmony_ci	.destructor = kfree,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cienum sja1105_region_id {
6262306a36Sopenharmony_ci	SJA1105_REGION_STATIC_CONFIG = 0,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct sja1105_region {
6662306a36Sopenharmony_ci	const struct devlink_region_ops *ops;
6762306a36Sopenharmony_ci	size_t (*get_size)(struct sja1105_private *priv);
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct sja1105_region sja1105_regions[] = {
7162306a36Sopenharmony_ci	[SJA1105_REGION_STATIC_CONFIG] = {
7262306a36Sopenharmony_ci		.ops = &sja1105_region_static_config_ops,
7362306a36Sopenharmony_ci		.get_size = sja1105_static_config_get_max_size,
7462306a36Sopenharmony_ci	},
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int sja1105_setup_devlink_regions(struct dsa_switch *ds)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int i, num_regions = ARRAY_SIZE(sja1105_regions);
8062306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
8162306a36Sopenharmony_ci	const struct devlink_region_ops *ops;
8262306a36Sopenharmony_ci	struct devlink_region *region;
8362306a36Sopenharmony_ci	u64 size;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *),
8662306a36Sopenharmony_ci				GFP_KERNEL);
8762306a36Sopenharmony_ci	if (!priv->regions)
8862306a36Sopenharmony_ci		return -ENOMEM;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 0; i < num_regions; i++) {
9162306a36Sopenharmony_ci		size = sja1105_regions[i].get_size(priv);
9262306a36Sopenharmony_ci		ops = sja1105_regions[i].ops;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		region = dsa_devlink_region_create(ds, ops, 1, size);
9562306a36Sopenharmony_ci		if (IS_ERR(region)) {
9662306a36Sopenharmony_ci			while (--i >= 0)
9762306a36Sopenharmony_ci				dsa_devlink_region_destroy(priv->regions[i]);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci			kfree(priv->regions);
10062306a36Sopenharmony_ci			return PTR_ERR(region);
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		priv->regions[i] = region;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void sja1105_teardown_devlink_regions(struct dsa_switch *ds)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int i, num_regions = ARRAY_SIZE(sja1105_regions);
11262306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	for (i = 0; i < num_regions; i++)
11562306a36Sopenharmony_ci		dsa_devlink_region_destroy(priv->regions[i]);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	kfree(priv->regions);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint sja1105_devlink_info_get(struct dsa_switch *ds,
12162306a36Sopenharmony_ci			     struct devlink_info_req *req,
12262306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return devlink_info_version_fixed_put(req,
12762306a36Sopenharmony_ci					      DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
12862306a36Sopenharmony_ci					      priv->info->name);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciint sja1105_devlink_setup(struct dsa_switch *ds)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	return sja1105_setup_devlink_regions(ds);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid sja1105_devlink_teardown(struct dsa_switch *ds)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	sja1105_teardown_devlink_regions(ds);
13962306a36Sopenharmony_ci}
140