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