xref: /linux/drivers/dpll/zl3073x/devlink.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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