File Index
Files and directories
Sign in
FILE

devlink.c

File preview with syntax highlighting.

linux-6.19.14/drivers/dpll/zl3073x/devlink.c C
  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 "flash.h"
 13#include "fw.h"
 14#include "regs.h"
 15
 16/**
 17 * zl3073x_devlink_info_get - Devlink device info callback
 18 * @devlink: devlink structure pointer
 19 * @req: devlink request pointer to store information
 20 * @extack: netlink extack pointer to report errors
 21 *
 22 * Return: 0 on success, <0 on error
 23 */
 24static int
 25zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 26			 struct netlink_ext_ack *extack)
 27{
 28	struct zl3073x_dev *zldev = devlink_priv(devlink);
 29	u16 id, revision, fw_ver;
 30	char buf[16];
 31	u32 cfg_ver;
 32	int rc;
 33
 34	rc = zl3073x_read_u16(zldev, ZL_REG_ID, &id);
 35	if (rc)
 36		return rc;
 37
 38	snprintf(buf, sizeof(buf), "%X", id);
 39	rc = devlink_info_version_fixed_put(req,
 40					    DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
 41					    buf);
 42	if (rc)
 43		return rc;
 44
 45	rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
 46	if (rc)
 47		return rc;
 48
 49	snprintf(buf, sizeof(buf), "%X", revision);
 50	rc = devlink_info_version_fixed_put(req,
 51					    DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
 52					    buf);
 53	if (rc)
 54		return rc;
 55
 56	rc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver);
 57	if (rc)
 58		return rc;
 59
 60	snprintf(buf, sizeof(buf), "%u", fw_ver);
 61	rc = devlink_info_version_running_put(req,
 62					      DEVLINK_INFO_VERSION_GENERIC_FW,
 63					      buf);
 64	if (rc)
 65		return rc;
 66
 67	rc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver);
 68	if (rc)
 69		return rc;
 70
 71	/* No custom config version */
 72	if (cfg_ver == U32_MAX)
 73		return 0;
 74
 75	snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu",
 76		 FIELD_GET(GENMASK(31, 24), cfg_ver),
 77		 FIELD_GET(GENMASK(23, 16), cfg_ver),
 78		 FIELD_GET(GENMASK(15, 8), cfg_ver),
 79		 FIELD_GET(GENMASK(7, 0), cfg_ver));
 80
 81	return devlink_info_version_running_put(req, "custom_cfg", buf);
 82}
 83
 84static int
 85zl3073x_devlink_reload_down(struct devlink *devlink, bool netns_change,
 86			    enum devlink_reload_action action,
 87			    enum devlink_reload_limit limit,
 88			    struct netlink_ext_ack *extack)
 89{
 90	struct zl3073x_dev *zldev = devlink_priv(devlink);
 91
 92	if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
 93		return -EOPNOTSUPP;
 94
 95	/* Stop normal operation */
 96	zl3073x_dev_stop(zldev);
 97
 98	return 0;
 99}
100
101static int
102zl3073x_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	int rc;
111
112	if (action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
113		return -EOPNOTSUPP;
114
115	rc = devl_param_driverinit_value_get(devlink,
116					     DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
117					     &val);
118	if (rc)
119		return rc;
120
121	if (zldev->clock_id != val.vu64) {
122		dev_dbg(zldev->dev,
123			"'clock_id' changed to %016llx\n", val.vu64);
124		zldev->clock_id = val.vu64;
125	}
126
127	/* Restart normal operation */
128	rc = zl3073x_dev_start(zldev, false);
129	if (rc)
130		dev_warn(zldev->dev, "Failed to re-start normal operation\n");
131
132	*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
133
134	return 0;
135}
136
137void zl3073x_devlink_flash_notify(struct zl3073x_dev *zldev, const char *msg,
138				  const char *component, u32 done, u32 total)
139{
140	struct devlink *devlink = priv_to_devlink(zldev);
141
142	devlink_flash_update_status_notify(devlink, msg, component, done,
143					   total);
144}
145
146/**
147 * zl3073x_devlink_flash_prepare - Prepare and enter flash mode
148 * @zldev: zl3073x device pointer
149 * @zlfw: pointer to loaded firmware
150 * @extack: netlink extack pointer to report errors
151 *
152 * The function stops normal operation and switches the device to flash mode.
153 * If an error occurs the normal operation is resumed.
154 *
155 * Return: 0 on success, <0 on error
156 */
157static int
158zl3073x_devlink_flash_prepare(struct zl3073x_dev *zldev,
159			      struct zl3073x_fw *zlfw,
160			      struct netlink_ext_ack *extack)
161{
162	struct zl3073x_fw_component *util;
163	int rc;
164
165	util = zlfw->component[ZL_FW_COMPONENT_UTIL];
166	if (!util) {
167		zl3073x_devlink_flash_notify(zldev,
168					     "Utility is missing in firmware",
169					     NULL, 0, 0);
170		return -ENOEXEC;
171	}
172
173	/* Stop normal operation prior entering flash mode */
174	zl3073x_dev_stop(zldev);
175
176	rc = zl3073x_flash_mode_enter(zldev, util->data, util->size, extack);
177	if (rc) {
178		zl3073x_devlink_flash_notify(zldev,
179					     "Failed to enter flash mode",
180					     NULL, 0, 0);
181
182		/* Resume normal operation */
183		zl3073x_dev_start(zldev, true);
184
185		return rc;
186	}
187
188	return 0;
189}
190
191/**
192 * zl3073x_devlink_flash_finish - Leave flash mode and resume normal operation
193 * @zldev: zl3073x device pointer
194 * @extack: netlink extack pointer to report errors
195 *
196 * The function switches the device back to standard mode and resumes normal
197 * operation.
198 *
199 * Return: 0 on success, <0 on error
200 */
201static int
202zl3073x_devlink_flash_finish(struct zl3073x_dev *zldev,
203			     struct netlink_ext_ack *extack)
204{
205	int rc;
206
207	/* Reset device CPU to normal mode */
208	zl3073x_flash_mode_leave(zldev, extack);
209
210	/* Resume normal operation */
211	rc = zl3073x_dev_start(zldev, true);
212	if (rc)
213		zl3073x_devlink_flash_notify(zldev,
214					     "Failed to start normal operation",
215					     NULL, 0, 0);
216
217	return rc;
218}
219
220/**
221 * zl3073x_devlink_flash_update - Devlink flash update callback
222 * @devlink: devlink structure pointer
223 * @params: flashing parameters pointer
224 * @extack: netlink extack pointer to report errors
225 *
226 * Return: 0 on success, <0 on error
227 */
228static int
229zl3073x_devlink_flash_update(struct devlink *devlink,
230			     struct devlink_flash_update_params *params,
231			     struct netlink_ext_ack *extack)
232{
233	struct zl3073x_dev *zldev = devlink_priv(devlink);
234	struct zl3073x_fw *zlfw;
235	int rc = 0;
236
237	zlfw = zl3073x_fw_load(zldev, params->fw->data, params->fw->size,
238			       extack);
239	if (IS_ERR(zlfw)) {
240		zl3073x_devlink_flash_notify(zldev, "Failed to load firmware",
241					     NULL, 0, 0);
242		rc = PTR_ERR(zlfw);
243		goto finish;
244	}
245
246	/* Stop normal operation and enter flash mode */
247	rc = zl3073x_devlink_flash_prepare(zldev, zlfw, extack);
248	if (rc)
249		goto finish;
250
251	rc = zl3073x_fw_flash(zldev, zlfw, extack);
252	if (rc) {
253		zl3073x_devlink_flash_finish(zldev, extack);
254		goto finish;
255	}
256
257	/* Resume normal mode */
258	rc = zl3073x_devlink_flash_finish(zldev, extack);
259
260finish:
261	if (!IS_ERR(zlfw))
262		zl3073x_fw_free(zlfw);
263
264	zl3073x_devlink_flash_notify(zldev,
265				     rc ? "Flashing failed" : "Flashing done",
266				     NULL, 0, 0);
267
268	return rc;
269}
270
271static const struct devlink_ops zl3073x_devlink_ops = {
272	.info_get = zl3073x_devlink_info_get,
273	.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
274	.reload_down = zl3073x_devlink_reload_down,
275	.reload_up = zl3073x_devlink_reload_up,
276	.flash_update = zl3073x_devlink_flash_update,
277};
278
279static void
280zl3073x_devlink_free(void *ptr)
281{
282	devlink_free(ptr);
283}
284
285/**
286 * zl3073x_devm_alloc - allocates zl3073x device structure
287 * @dev: pointer to device structure
288 *
289 * Allocates zl3073x device structure as device resource.
290 *
291 * Return: pointer to zl3073x device on success, error pointer on error
292 */
293struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
294{
295	struct zl3073x_dev *zldev;
296	struct devlink *devlink;
297	int rc;
298
299	devlink = devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev);
300	if (!devlink)
301		return ERR_PTR(-ENOMEM);
302
303	/* Add devres action to free devlink device */
304	rc = devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink);
305	if (rc)
306		return ERR_PTR(rc);
307
308	zldev = devlink_priv(devlink);
309	zldev->dev = dev;
310	dev_set_drvdata(zldev->dev, zldev);
311
312	return zldev;
313}
314EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
315
316static int
317zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id,
318					union devlink_param_value val,
319					struct netlink_ext_ack *extack)
320{
321	if (!val.vu64) {
322		NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero");
323		return -EINVAL;
324	}
325
326	return 0;
327}
328
329static const struct devlink_param zl3073x_devlink_params[] = {
330	DEVLINK_PARAM_GENERIC(CLOCK_ID, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
331			      NULL, NULL,
332			      zl3073x_devlink_param_clock_id_validate),
333};
334
335static void
336zl3073x_devlink_unregister(void *ptr)
337{
338	struct devlink *devlink = priv_to_devlink(ptr);
339
340	devl_lock(devlink);
341
342	/* Unregister devlink params */
343	devl_params_unregister(devlink, zl3073x_devlink_params,
344			       ARRAY_SIZE(zl3073x_devlink_params));
345
346	/* Unregister devlink instance */
347	devl_unregister(devlink);
348
349	devl_unlock(devlink);
350}
351
352/**
353 * zl3073x_devlink_register - register devlink instance and params
354 * @zldev: zl3073x device to register the devlink for
355 *
356 * Register the devlink instance and parameters associated with the device.
357 *
358 * Return: 0 on success, <0 on error
359 */
360int zl3073x_devlink_register(struct zl3073x_dev *zldev)
361{
362	struct devlink *devlink = priv_to_devlink(zldev);
363	union devlink_param_value value;
364	int rc;
365
366	devl_lock(devlink);
367
368	/* Register devlink params */
369	rc = devl_params_register(devlink, zl3073x_devlink_params,
370				  ARRAY_SIZE(zl3073x_devlink_params));
371	if (rc) {
372		devl_unlock(devlink);
373
374		return rc;
375	}
376
377	value.vu64 = zldev->clock_id;
378	devl_param_driverinit_value_set(devlink,
379					DEVLINK_PARAM_GENERIC_ID_CLOCK_ID,
380					value);
381
382	/* Register devlink instance */
383	devl_register(devlink);
384
385	devl_unlock(devlink);
386
387	/* Add devres action to unregister devlink device */
388	return devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister,
389					zldev);
390}
~/ Read-only file preview.