1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2023 Intel Corporation. */ |
3 | |
4 | #include <net/devlink.h> |
5 | #include "i40e.h" |
6 | #include "i40e_devlink.h" |
7 | |
8 | static void i40e_info_get_dsn(struct i40e_pf *pf, char *buf, size_t len) |
9 | { |
10 | u8 dsn[8]; |
11 | |
12 | put_unaligned_be64(val: pci_get_dsn(dev: pf->pdev), p: dsn); |
13 | |
14 | snprintf(buf, size: len, fmt: "%8phD" , dsn); |
15 | } |
16 | |
17 | static void i40e_info_fw_mgmt(struct i40e_hw *hw, char *buf, size_t len) |
18 | { |
19 | struct i40e_adminq_info *aq = &hw->aq; |
20 | |
21 | snprintf(buf, size: len, fmt: "%u.%u" , aq->fw_maj_ver, aq->fw_min_ver); |
22 | } |
23 | |
24 | static void i40e_info_fw_mgmt_build(struct i40e_hw *hw, char *buf, size_t len) |
25 | { |
26 | struct i40e_adminq_info *aq = &hw->aq; |
27 | |
28 | snprintf(buf, size: len, fmt: "%05d" , aq->fw_build); |
29 | } |
30 | |
31 | static void i40e_info_fw_api(struct i40e_hw *hw, char *buf, size_t len) |
32 | { |
33 | struct i40e_adminq_info *aq = &hw->aq; |
34 | |
35 | snprintf(buf, size: len, fmt: "%u.%u" , aq->api_maj_ver, aq->api_min_ver); |
36 | } |
37 | |
38 | static void i40e_info_pba(struct i40e_hw *hw, char *buf, size_t len) |
39 | { |
40 | buf[0] = '\0'; |
41 | if (hw->pba_id) |
42 | strscpy(buf, hw->pba_id, len); |
43 | } |
44 | |
45 | enum i40e_devlink_version_type { |
46 | I40E_DL_VERSION_FIXED, |
47 | I40E_DL_VERSION_RUNNING, |
48 | }; |
49 | |
50 | static int i40e_devlink_info_put(struct devlink_info_req *req, |
51 | enum i40e_devlink_version_type type, |
52 | const char *key, const char *value) |
53 | { |
54 | if (!strlen(value)) |
55 | return 0; |
56 | |
57 | switch (type) { |
58 | case I40E_DL_VERSION_FIXED: |
59 | return devlink_info_version_fixed_put(req, version_name: key, version_value: value); |
60 | case I40E_DL_VERSION_RUNNING: |
61 | return devlink_info_version_running_put(req, version_name: key, version_value: value); |
62 | } |
63 | return 0; |
64 | } |
65 | |
66 | static int i40e_devlink_info_get(struct devlink *dl, |
67 | struct devlink_info_req *req, |
68 | struct netlink_ext_ack *extack) |
69 | { |
70 | struct i40e_pf *pf = devlink_priv(devlink: dl); |
71 | struct i40e_hw *hw = &pf->hw; |
72 | char buf[32]; |
73 | int err; |
74 | |
75 | i40e_info_get_dsn(pf, buf, len: sizeof(buf)); |
76 | err = devlink_info_serial_number_put(req, sn: buf); |
77 | if (err) |
78 | return err; |
79 | |
80 | i40e_info_fw_mgmt(hw, buf, len: sizeof(buf)); |
81 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
82 | DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, value: buf); |
83 | if (err) |
84 | return err; |
85 | |
86 | i40e_info_fw_mgmt_build(hw, buf, len: sizeof(buf)); |
87 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
88 | key: "fw.mgmt.build" , value: buf); |
89 | if (err) |
90 | return err; |
91 | |
92 | i40e_info_fw_api(hw, buf, len: sizeof(buf)); |
93 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
94 | DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API, |
95 | value: buf); |
96 | if (err) |
97 | return err; |
98 | |
99 | i40e_info_nvm_ver(hw, buf, len: sizeof(buf)); |
100 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
101 | key: "fw.psid.api" , value: buf); |
102 | if (err) |
103 | return err; |
104 | |
105 | i40e_info_eetrack(hw, buf, len: sizeof(buf)); |
106 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
107 | DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, |
108 | value: buf); |
109 | if (err) |
110 | return err; |
111 | |
112 | i40e_info_civd_ver(hw, buf, len: sizeof(buf)); |
113 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_RUNNING, |
114 | DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, value: buf); |
115 | if (err) |
116 | return err; |
117 | |
118 | i40e_info_pba(hw, buf, len: sizeof(buf)); |
119 | err = i40e_devlink_info_put(req, type: I40E_DL_VERSION_FIXED, |
120 | DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, value: buf); |
121 | |
122 | return err; |
123 | } |
124 | |
125 | static const struct devlink_ops i40e_devlink_ops = { |
126 | .info_get = i40e_devlink_info_get, |
127 | }; |
128 | |
129 | /** |
130 | * i40e_alloc_pf - Allocate devlink and return i40e_pf structure pointer |
131 | * @dev: the device to allocate for |
132 | * |
133 | * Allocate a devlink instance for this device and return the private |
134 | * area as the i40e_pf structure. |
135 | **/ |
136 | struct i40e_pf *i40e_alloc_pf(struct device *dev) |
137 | { |
138 | struct devlink *devlink; |
139 | |
140 | devlink = devlink_alloc(ops: &i40e_devlink_ops, priv_size: sizeof(struct i40e_pf), dev); |
141 | if (!devlink) |
142 | return NULL; |
143 | |
144 | return devlink_priv(devlink); |
145 | } |
146 | |
147 | /** |
148 | * i40e_free_pf - Free i40e_pf structure and associated devlink |
149 | * @pf: the PF structure |
150 | * |
151 | * Free i40e_pf structure and devlink allocated by devlink_alloc. |
152 | **/ |
153 | void i40e_free_pf(struct i40e_pf *pf) |
154 | { |
155 | struct devlink *devlink = priv_to_devlink(priv: pf); |
156 | |
157 | devlink_free(devlink); |
158 | } |
159 | |
160 | /** |
161 | * i40e_devlink_register - Register devlink interface for this PF |
162 | * @pf: the PF to register the devlink for. |
163 | * |
164 | * Register the devlink instance associated with this physical function. |
165 | **/ |
166 | void i40e_devlink_register(struct i40e_pf *pf) |
167 | { |
168 | devlink_register(devlink: priv_to_devlink(priv: pf)); |
169 | } |
170 | |
171 | /** |
172 | * i40e_devlink_unregister - Unregister devlink resources for this PF. |
173 | * @pf: the PF structure to cleanup |
174 | * |
175 | * Releases resources used by devlink and cleans up associated memory. |
176 | **/ |
177 | void i40e_devlink_unregister(struct i40e_pf *pf) |
178 | { |
179 | devlink_unregister(devlink: priv_to_devlink(priv: pf)); |
180 | } |
181 | |
182 | /** |
183 | * i40e_devlink_set_switch_id - Set unique switch id based on pci dsn |
184 | * @pf: the PF to create a devlink port for |
185 | * @ppid: struct with switch id information |
186 | */ |
187 | static void i40e_devlink_set_switch_id(struct i40e_pf *pf, |
188 | struct netdev_phys_item_id *ppid) |
189 | { |
190 | u64 id = pci_get_dsn(dev: pf->pdev); |
191 | |
192 | ppid->id_len = sizeof(id); |
193 | put_unaligned_be64(val: id, p: &ppid->id); |
194 | } |
195 | |
196 | /** |
197 | * i40e_devlink_create_port - Create a devlink port for this PF |
198 | * @pf: the PF to create a port for |
199 | * |
200 | * Create and register a devlink_port for this PF. Note that although each |
201 | * physical function is connected to a separate devlink instance, the port |
202 | * will still be numbered according to the physical function id. |
203 | * |
204 | * Return: zero on success or an error code on failure. |
205 | **/ |
206 | int i40e_devlink_create_port(struct i40e_pf *pf) |
207 | { |
208 | struct devlink *devlink = priv_to_devlink(priv: pf); |
209 | struct devlink_port_attrs attrs = {}; |
210 | struct device *dev = &pf->pdev->dev; |
211 | int err; |
212 | |
213 | attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; |
214 | attrs.phys.port_number = pf->hw.pf_id; |
215 | i40e_devlink_set_switch_id(pf, ppid: &attrs.switch_id); |
216 | devlink_port_attrs_set(devlink_port: &pf->devlink_port, devlink_port_attrs: &attrs); |
217 | err = devlink_port_register(devlink, devlink_port: &pf->devlink_port, port_index: pf->hw.pf_id); |
218 | if (err) { |
219 | dev_err(dev, "devlink_port_register failed: %d\n" , err); |
220 | return err; |
221 | } |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | /** |
227 | * i40e_devlink_destroy_port - Destroy the devlink_port for this PF |
228 | * @pf: the PF to cleanup |
229 | * |
230 | * Unregisters the devlink_port structure associated with this PF. |
231 | **/ |
232 | void i40e_devlink_destroy_port(struct i40e_pf *pf) |
233 | { |
234 | devlink_port_unregister(devlink_port: &pf->devlink_port); |
235 | } |
236 | |