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