Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dpll/zl3073x/devlink.c
26282 views
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
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
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
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
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
*/
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
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
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
*/
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
261