1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> |
3 | * Copyright 2020 NXP |
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: &config, |
21 | static_ops: priv->info->static_ops, |
22 | device_id: 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: &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(config: &priv->static_config); |
46 | max_len = sja1105_static_config_get_max_size(priv); |
47 | |
48 | *data = kcalloc(n: max_len, size: sizeof(u8), GFP_KERNEL); |
49 | if (!*data) |
50 | return -ENOMEM; |
51 | |
52 | return static_config_buf_prepare_for_upload(priv, config_buf: *data, buf_len: 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(n: num_regions, size: 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, region_max_snapshots: 1, region_size: size); |
95 | if (IS_ERR(ptr: region)) { |
96 | while (--i >= 0) |
97 | dsa_devlink_region_destroy(region: priv->regions[i]); |
98 | |
99 | kfree(objp: priv->regions); |
100 | return PTR_ERR(ptr: region); |
101 | } |
102 | |
103 | priv->regions[i] = region; |
104 | } |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static void sja1105_teardown_devlink_regions(struct dsa_switch *ds) |
110 | { |
111 | int i, num_regions = ARRAY_SIZE(sja1105_regions); |
112 | struct sja1105_private *priv = ds->priv; |
113 | |
114 | for (i = 0; i < num_regions; i++) |
115 | dsa_devlink_region_destroy(region: priv->regions[i]); |
116 | |
117 | kfree(objp: priv->regions); |
118 | } |
119 | |
120 | int sja1105_devlink_info_get(struct dsa_switch *ds, |
121 | struct devlink_info_req *req, |
122 | struct netlink_ext_ack *extack) |
123 | { |
124 | struct sja1105_private *priv = ds->priv; |
125 | |
126 | return devlink_info_version_fixed_put(req, |
127 | DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, |
128 | version_value: priv->info->name); |
129 | } |
130 | |
131 | int sja1105_devlink_setup(struct dsa_switch *ds) |
132 | { |
133 | return sja1105_setup_devlink_regions(ds); |
134 | } |
135 | |
136 | void sja1105_devlink_teardown(struct dsa_switch *ds) |
137 | { |
138 | sja1105_teardown_devlink_regions(ds); |
139 | } |
140 | |