1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/device/devres.h>
4 #include <linux/netlink.h>
5 #include <linux/sprintf.h>
6 #include <linux/types.h>
7 #include <net/devlink.h>
8
9 #include "core.h"
10 #include "devlink.h"
11 #include "dpll.h"
12 #include "regs.h"
13
14 /**
15 * zl3073x_devlink_info_get - Devlink device info callback
16 * @devlink: devlink structure pointer
17 * @req: devlink request pointer to store information
18 * @extack: netlink extack pointer to report errors
19 *
20 * Return: 0 on success, <0 on error
21 */
22 static int
zl3073x_devlink_info_get(struct devlink * devlink,struct devlink_info_req * req,struct netlink_ext_ack * extack)23 zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
24 struct netlink_ext_ack *extack)
25 {
26 struct zl3073x_dev *zldev = devlink_priv(devlink);
27 u16 id, revision, fw_ver;
28 char buf[16];
29 u32 cfg_ver;
30 int rc;
31
32 rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id);
33 if (rc)
34 return rc;
35
36 snprintf(buf, sizeof(buf), "%X", id);
37 rc = devlink_info_version_fixed_put(req,
38 DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
39 buf);
40 if (rc)
41 return rc;
42
43 rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
44 if (rc)
45 return rc;
46
47 snprintf(buf, sizeof(buf), "%X", revision);
48 rc = devlink_info_version_fixed_put(req,
49 DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
50 buf);
51 if (rc)
52 return rc;
53
54 rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver);
55 if (rc)
56 return rc;
57
58 snprintf(buf, sizeof(buf), "%u", fw_ver);
59 rc = devlink_info_version_running_put(req,
60 DEVLINK_INFO_VERSION_GENERIC_FW,
61 buf);
62 if (rc)
63 return rc;
64
65 rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver);
66 if (rc)
67 return rc;
68
69 /* No custom config version */
70 if (cfg_ver == U32_MAX)
71 return 0;
72
73 snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu",
74 FIELD_GET(GENMASK(31, 24), cfg_ver),
75 FIELD_GET(GENMASK(23, 16), cfg_ver),
76 FIELD_GET(GENMASK(15, 8), cfg_ver),
77 FIELD_GET(GENMASK(7, 0), cfg_ver));
78
79 return devlink_info_version_running_put(req, "custom_cfg", buf);
80 }
81
82 static int
zl3073x_devlink_reload_down(struct devlink * devlink,bool netns_change,enum devlink_reload_action action,enum devlink_reload_limit limit,struct netlink_ext_ack * extack)83 zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change,
84 enum devlink_reload_action action,
85 enum devlink_reload_limit limit,
86 struct netlink_ext_ack *extack)
87 {
88 struct zl3073x_dev *zldev = devlink_priv(devlink);
89 struct zl3073x_dpll *zldpll;
90
91 if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
92 return -EOPNOTSUPP;
93
94 /* Unregister all DPLLs */
95 list_for_each_entry(zldpll, &zldev->dplls, list)
96 zl3073x_dpll_unregister(zldpll);
97
98 return 0;
99 }
100
101 static int
zl3073x_devlink_reload_up(struct devlink * devlink,enum devlink_reload_action action,enum devlink_reload_limit limit,u32 * actions_performed,struct netlink_ext_ack * extack)102 zl3073x_devlink_reload_up(struct devlink *devlink,
103 enum devlink_reload_action action,
104 enum devlink_reload_limit limit,
105 u32 *actions_performed,
106 struct netlink_ext_ack *extack)
107 {
108 struct zl3073x_dev *zldev = devlink_priv(devlink);
109 union devlink_param_value val;
110 struct zl3073x_dpll *zldpll;
111 int rc;
112
113 if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
114 return -EOPNOTSUPP;
115
116 rc = devl_param_driverinit_value_get(devlink,
117 DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
118 &val);
119 if (rc)
120 return rc;
121
122 if (zldev->clock_id != val.vu64) {
123 dev_dbg(zldev->dev,
124 "'clock_id' changed to %016llx\n", val.vu64);
125 zldev->clock_id = val.vu64;
126 }
127
128 /* Re-register all DPLLs */
129 list_for_each_entry(zldpll, &zldev->dplls, list) {
130 rc = zl3073x_dpll_register(zldpll);
131 if (rc)
132 dev_warn(zldev->dev,
133 "Failed to re-register DPLL%u\n", zldpll->id);
134 }
135
136 *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
137
138 return 0;
139 }
140
141 static const struct devlink_ops zl3073x_devlink_ops = {
142 .info_get = zl3073x_devlink_info_get,
143 .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
144 .reload_down = zl3073x_devlink_reload_down,
145 .reload_up = zl3073x_devlink_reload_up,
146 };
147
148 static void
zl3073x_devlink_free(void * ptr)149 zl3073x_devlink_free(void *ptr)
150 {
151 devlink_free(ptr);
152 }
153
154 /**
155 * zl3073x_devm_alloc - allocates zl3073x device structure
156 * @dev: pointer to device structure
157 *
158 * Allocates zl3073x device structure as device resource.
159 *
160 * Return: pointer to zl3073x device on success, error pointer on error
161 */
zl3073x_devm_alloc(struct device * dev)162 struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
163 {
164 struct zl3073x_dev *zldev;
165 struct devlink *devlink;
166 int rc;
167
168 devlink = devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev);
169 if (!devlink)
170 return ERR_PTR(-ENOMEM);
171
172 /* Add devres action to free devlink device */
173 rc = devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink);
174 if (rc)
175 return ERR_PTR(rc);
176
177 zldev = devlink_priv(devlink);
178 zldev->dev = dev;
179 dev_set_drvdata(zldev->dev, zldev);
180
181 return zldev;
182 }
183 EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
184
185 static int
zl3073x_devlink_param_clock_id_validate(struct devlink * devlink,u32 id,union devlink_param_value val,struct netlink_ext_ack * extack)186 zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id,
187 union devlink_param_value val,
188 struct netlink_ext_ack *extack)
189 {
190 if (!val.vu64) {
191 NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero");
192 return -EINVAL;
193 }
194
195 return 0;
196 }
197
198 static const struct devlink_param zl3073x_devlink_params[] = {
199 DEVLINK_PARAM_GENERIC(CLOCK_ID, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
200 NULL, NULL,
201 zl3073x_devlink_param_clock_id_validate),
202 };
203
204 static void
zl3073x_devlink_unregister(void * ptr)205 zl3073x_devlink_unregister(void *ptr)
206 {
207 struct devlink *devlink = priv_to_devlink(ptr);
208
209 devl_lock(devlink);
210
211 /* Unregister devlink params */
212 devl_params_unregister(devlink, zl3073x_devlink_params,
213 ARRAY_SIZE(zl3073x_devlink_params));
214
215 /* Unregister devlink instance */
216 devl_unregister(devlink);
217
218 devl_unlock(devlink);
219 }
220
221 /**
222 * zl3073x_devlink_register - register devlink instance and params
223 * @zldev: zl3073x device to register the devlink for
224 *
225 * Register the devlink instance and parameters associated with the device.
226 *
227 * Return: 0 on success, <0 on error
228 */
zl3073x_devlink_register(struct zl3073x_dev * zldev)229 int zl3073x_devlink_register(struct zl3073x_dev *zldev)
230 {
231 struct devlink *devlink = priv_to_devlink(zldev);
232 union devlink_param_value value;
233 int rc;
234
235 devl_lock(devlink);
236
237 /* Register devlink params */
238 rc = devl_params_register(devlink, zl3073x_devlink_params,
239 ARRAY_SIZE(zl3073x_devlink_params));
240 if (rc) {
241 devl_unlock(devlink);
242
243 return rc;
244 }
245
246 value.vu64 = zldev->clock_id;
247 devl_param_driverinit_value_set(devlink,
248 DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
249 value);
250
251 /* Register devlink instance */
252 devl_register(devlink);
253
254 devl_unlock(devlink);
255
256 /* Add devres action to unregister devlink device */
257 return devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister,
258 zldev);
259 }
260