|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> |
| 3 | + * Copyright 2020 NXP Semiconductors |
| 4 | + */ |
| 5 | +#include "sja1105.h" |
| 6 | + |
| 7 | +/* Since devlink regions have a fixed size and the static config has a variable |
| 8 | + * size, we need to calculate the maximum possible static config size by |
| 9 | + * creating a dummy config with all table entries populated to the max, and get |
| 10 | + * its packed length. This is done dynamically as opposed to simply hardcoding |
| 11 | + * a number, since currently not all static config tables are implemented, so |
| 12 | + * we are avoiding a possible code desynchronization. |
| 13 | + */ |
| 14 | +static size_t sja1105_static_config_get_max_size(struct sja1105_private *priv) |
| 15 | +{ |
| 16 | + struct sja1105_static_config config; |
| 17 | + enum sja1105_blk_idx blk_idx; |
| 18 | + int rc; |
| 19 | + |
| 20 | + rc = sja1105_static_config_init(&config, |
| 21 | + priv->info->static_ops, |
| 22 | + priv->info->device_id); |
| 23 | + if (rc) |
| 24 | + return 0; |
| 25 | + |
| 26 | + for (blk_idx = 0; blk_idx < BLK_IDX_MAX; blk_idx++) { |
| 27 | + struct sja1105_table *table = &config.tables[blk_idx]; |
| 28 | + |
| 29 | + table->entry_count = table->ops->max_entry_count; |
| 30 | + } |
| 31 | + |
| 32 | + return sja1105_static_config_get_length(&config); |
| 33 | +} |
| 34 | + |
| 35 | +static int |
| 36 | +sja1105_region_static_config_snapshot(struct devlink *dl, |
| 37 | + const struct devlink_region_ops *ops, |
| 38 | + struct netlink_ext_ack *extack, |
| 39 | + u8 **data) |
| 40 | +{ |
| 41 | + struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
| 42 | + struct sja1105_private *priv = ds->priv; |
| 43 | + size_t max_len, len; |
| 44 | + |
| 45 | + len = sja1105_static_config_get_length(&priv->static_config); |
| 46 | + max_len = sja1105_static_config_get_max_size(priv); |
| 47 | + |
| 48 | + *data = kcalloc(max_len, sizeof(u8), GFP_KERNEL); |
| 49 | + if (!*data) |
| 50 | + return -ENOMEM; |
| 51 | + |
| 52 | + return static_config_buf_prepare_for_upload(priv, *data, len); |
| 53 | +} |
| 54 | + |
| 55 | +static struct devlink_region_ops sja1105_region_static_config_ops = { |
| 56 | + .name = "static-config", |
| 57 | + .snapshot = sja1105_region_static_config_snapshot, |
| 58 | + .destructor = kfree, |
| 59 | +}; |
| 60 | + |
| 61 | +enum sja1105_region_id { |
| 62 | + SJA1105_REGION_STATIC_CONFIG = 0, |
| 63 | +}; |
| 64 | + |
| 65 | +struct sja1105_region { |
| 66 | + const struct devlink_region_ops *ops; |
| 67 | + size_t (*get_size)(struct sja1105_private *priv); |
| 68 | +}; |
| 69 | + |
| 70 | +static struct sja1105_region sja1105_regions[] = { |
| 71 | + [SJA1105_REGION_STATIC_CONFIG] = { |
| 72 | + .ops = &sja1105_region_static_config_ops, |
| 73 | + .get_size = sja1105_static_config_get_max_size, |
| 74 | + }, |
| 75 | +}; |
| 76 | + |
| 77 | +static int sja1105_setup_devlink_regions(struct dsa_switch *ds) |
| 78 | +{ |
| 79 | + int i, num_regions = ARRAY_SIZE(sja1105_regions); |
| 80 | + struct sja1105_private *priv = ds->priv; |
| 81 | + const struct devlink_region_ops *ops; |
| 82 | + struct devlink_region *region; |
| 83 | + u64 size; |
| 84 | + |
| 85 | + priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *), |
| 86 | + GFP_KERNEL); |
| 87 | + if (!priv->regions) |
| 88 | + return -ENOMEM; |
| 89 | + |
| 90 | + for (i = 0; i < num_regions; i++) { |
| 91 | + size = sja1105_regions[i].get_size(priv); |
| 92 | + ops = sja1105_regions[i].ops; |
| 93 | + |
| 94 | + region = dsa_devlink_region_create(ds, ops, 1, size); |
| 95 | + if (IS_ERR(region)) { |
| 96 | + while (i-- >= 0) |
| 97 | + dsa_devlink_region_destroy(priv->regions[i]); |
| 98 | + return PTR_ERR(region); |
| 99 | + } |
| 100 | + |
| 101 | + priv->regions[i] = region; |
| 102 | + } |
| 103 | + |
| 104 | + return 0; |
| 105 | +} |
| 106 | + |
| 107 | +static void sja1105_teardown_devlink_regions(struct dsa_switch *ds) |
| 108 | +{ |
| 109 | + int i, num_regions = ARRAY_SIZE(sja1105_regions); |
| 110 | + struct sja1105_private *priv = ds->priv; |
| 111 | + |
| 112 | + for (i = 0; i < num_regions; i++) |
| 113 | + dsa_devlink_region_destroy(priv->regions[i]); |
| 114 | + |
| 115 | + kfree(priv->regions); |
| 116 | +} |
| 117 | + |
| 118 | +static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv, |
| 119 | + bool *be_vlan) |
| 120 | +{ |
| 121 | + *be_vlan = priv->best_effort_vlan_filtering; |
| 122 | + |
| 123 | + return 0; |
| 124 | +} |
| 125 | + |
| 126 | +static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv, |
| 127 | + bool be_vlan) |
| 128 | +{ |
| 129 | + struct dsa_switch *ds = priv->ds; |
| 130 | + bool vlan_filtering; |
| 131 | + int port; |
| 132 | + int rc; |
| 133 | + |
| 134 | + priv->best_effort_vlan_filtering = be_vlan; |
| 135 | + |
| 136 | + rtnl_lock(); |
| 137 | + for (port = 0; port < ds->num_ports; port++) { |
| 138 | + struct dsa_port *dp; |
| 139 | + |
| 140 | + if (!dsa_is_user_port(ds, port)) |
| 141 | + continue; |
| 142 | + |
| 143 | + dp = dsa_to_port(ds, port); |
| 144 | + vlan_filtering = dsa_port_is_vlan_filtering(dp); |
| 145 | + |
| 146 | + rc = sja1105_vlan_filtering(ds, port, vlan_filtering); |
| 147 | + if (rc) |
| 148 | + break; |
| 149 | + } |
| 150 | + rtnl_unlock(); |
| 151 | + |
| 152 | + return rc; |
| 153 | +} |
| 154 | + |
| 155 | +enum sja1105_devlink_param_id { |
| 156 | + SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, |
| 157 | + SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, |
| 158 | +}; |
| 159 | + |
| 160 | +int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id, |
| 161 | + struct devlink_param_gset_ctx *ctx) |
| 162 | +{ |
| 163 | + struct sja1105_private *priv = ds->priv; |
| 164 | + int err; |
| 165 | + |
| 166 | + switch (id) { |
| 167 | + case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: |
| 168 | + err = sja1105_best_effort_vlan_filtering_get(priv, |
| 169 | + &ctx->val.vbool); |
| 170 | + break; |
| 171 | + default: |
| 172 | + err = -EOPNOTSUPP; |
| 173 | + break; |
| 174 | + } |
| 175 | + |
| 176 | + return err; |
| 177 | +} |
| 178 | + |
| 179 | +int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id, |
| 180 | + struct devlink_param_gset_ctx *ctx) |
| 181 | +{ |
| 182 | + struct sja1105_private *priv = ds->priv; |
| 183 | + int err; |
| 184 | + |
| 185 | + switch (id) { |
| 186 | + case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: |
| 187 | + err = sja1105_best_effort_vlan_filtering_set(priv, |
| 188 | + ctx->val.vbool); |
| 189 | + break; |
| 190 | + default: |
| 191 | + err = -EOPNOTSUPP; |
| 192 | + break; |
| 193 | + } |
| 194 | + |
| 195 | + return err; |
| 196 | +} |
| 197 | + |
| 198 | +static const struct devlink_param sja1105_devlink_params[] = { |
| 199 | + DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, |
| 200 | + "best_effort_vlan_filtering", |
| 201 | + DEVLINK_PARAM_TYPE_BOOL, |
| 202 | + BIT(DEVLINK_PARAM_CMODE_RUNTIME)), |
| 203 | +}; |
| 204 | + |
| 205 | +static int sja1105_setup_devlink_params(struct dsa_switch *ds) |
| 206 | +{ |
| 207 | + return dsa_devlink_params_register(ds, sja1105_devlink_params, |
| 208 | + ARRAY_SIZE(sja1105_devlink_params)); |
| 209 | +} |
| 210 | + |
| 211 | +static void sja1105_teardown_devlink_params(struct dsa_switch *ds) |
| 212 | +{ |
| 213 | + dsa_devlink_params_unregister(ds, sja1105_devlink_params, |
| 214 | + ARRAY_SIZE(sja1105_devlink_params)); |
| 215 | +} |
| 216 | + |
| 217 | +int sja1105_devlink_info_get(struct dsa_switch *ds, |
| 218 | + struct devlink_info_req *req, |
| 219 | + struct netlink_ext_ack *extack) |
| 220 | +{ |
| 221 | + struct sja1105_private *priv = ds->priv; |
| 222 | + int rc; |
| 223 | + |
| 224 | + rc = devlink_info_driver_name_put(req, "sja1105"); |
| 225 | + if (rc) |
| 226 | + return rc; |
| 227 | + |
| 228 | + rc = devlink_info_version_fixed_put(req, |
| 229 | + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, |
| 230 | + priv->info->name); |
| 231 | + return rc; |
| 232 | +} |
| 233 | + |
| 234 | +int sja1105_devlink_setup(struct dsa_switch *ds) |
| 235 | +{ |
| 236 | + int rc; |
| 237 | + |
| 238 | + rc = sja1105_setup_devlink_params(ds); |
| 239 | + if (rc) |
| 240 | + return rc; |
| 241 | + |
| 242 | + rc = sja1105_setup_devlink_regions(ds); |
| 243 | + if (rc < 0) { |
| 244 | + sja1105_teardown_devlink_params(ds); |
| 245 | + return rc; |
| 246 | + } |
| 247 | + |
| 248 | + return 0; |
| 249 | +} |
| 250 | + |
| 251 | +void sja1105_devlink_teardown(struct dsa_switch *ds) |
| 252 | +{ |
| 253 | + sja1105_teardown_devlink_params(ds); |
| 254 | + sja1105_teardown_devlink_regions(ds); |
| 255 | +} |
0 commit comments