Commit | Line | Data |
---|---|---|
4569cce4 SN |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2023 Advanced Micro Devices, Inc */ | |
3 | ||
4 | #include <linux/pci.h> | |
5 | ||
6 | #include "core.h" | |
7 | #include <linux/pds/pds_auxbus.h> | |
8 | ||
10659034 SN |
9 | /** |
10 | * pds_client_register - Link the client to the firmware | |
06d220f1 | 11 | * @pf: ptr to the PF driver's private data struct |
10659034 SN |
12 | * @devname: name that includes service into, e.g. pds_core.vDPA |
13 | * | |
30ff01ee | 14 | * Return: positive client ID (ci) on success, or |
10659034 SN |
15 | * negative for error |
16 | */ | |
b021d05e | 17 | int pds_client_register(struct pdsc *pf, char *devname) |
10659034 SN |
18 | { |
19 | union pds_core_adminq_comp comp = {}; | |
20 | union pds_core_adminq_cmd cmd = {}; | |
10659034 SN |
21 | int err; |
22 | u16 ci; | |
23 | ||
10659034 SN |
24 | cmd.client_reg.opcode = PDS_AQ_CMD_CLIENT_REG; |
25 | strscpy(cmd.client_reg.devname, devname, | |
26 | sizeof(cmd.client_reg.devname)); | |
27 | ||
28 | err = pdsc_adminq_post(pf, &cmd, &comp, false); | |
29 | if (err) { | |
30 | dev_info(pf->dev, "register dev_name %s with DSC failed, status %d: %pe\n", | |
31 | devname, comp.status, ERR_PTR(err)); | |
32 | return err; | |
33 | } | |
34 | ||
35 | ci = le16_to_cpu(comp.client_reg.client_id); | |
36 | if (!ci) { | |
37 | dev_err(pf->dev, "%s: device returned null client_id\n", | |
38 | __func__); | |
39 | return -EIO; | |
40 | } | |
41 | ||
42 | dev_dbg(pf->dev, "%s: device returned client_id %d for %s\n", | |
43 | __func__, ci, devname); | |
44 | ||
45 | return ci; | |
46 | } | |
47 | EXPORT_SYMBOL_GPL(pds_client_register); | |
48 | ||
49 | /** | |
50 | * pds_client_unregister - Unlink the client from the firmware | |
06d220f1 | 51 | * @pf: ptr to the PF driver's private data struct |
10659034 SN |
52 | * @client_id: id returned from pds_client_register() |
53 | * | |
54 | * Return: 0 on success, or | |
55 | * negative for error | |
56 | */ | |
b021d05e | 57 | int pds_client_unregister(struct pdsc *pf, u16 client_id) |
10659034 SN |
58 | { |
59 | union pds_core_adminq_comp comp = {}; | |
60 | union pds_core_adminq_cmd cmd = {}; | |
10659034 SN |
61 | int err; |
62 | ||
10659034 SN |
63 | cmd.client_unreg.opcode = PDS_AQ_CMD_CLIENT_UNREG; |
64 | cmd.client_unreg.client_id = cpu_to_le16(client_id); | |
65 | ||
66 | err = pdsc_adminq_post(pf, &cmd, &comp, false); | |
67 | if (err) | |
68 | dev_info(pf->dev, "unregister client_id %d failed, status %d: %pe\n", | |
69 | client_id, comp.status, ERR_PTR(err)); | |
70 | ||
71 | return err; | |
72 | } | |
73 | EXPORT_SYMBOL_GPL(pds_client_unregister); | |
74 | ||
75 | /** | |
76 | * pds_client_adminq_cmd - Process an adminq request for the client | |
77 | * @padev: ptr to the client device | |
78 | * @req: ptr to buffer with request | |
79 | * @req_len: length of actual struct used for request | |
80 | * @resp: ptr to buffer where answer is to be copied | |
81 | * @flags: optional flags from pds_core_adminq_flags | |
82 | * | |
83 | * Return: 0 on success, or | |
84 | * negative for error | |
85 | * | |
86 | * Client sends pointers to request and response buffers | |
87 | * Core copies request data into pds_core_client_request_cmd | |
88 | * Core sets other fields as needed | |
89 | * Core posts to AdminQ | |
90 | * Core copies completion data into response buffer | |
91 | */ | |
92 | int pds_client_adminq_cmd(struct pds_auxiliary_dev *padev, | |
93 | union pds_core_adminq_cmd *req, | |
94 | size_t req_len, | |
95 | union pds_core_adminq_comp *resp, | |
96 | u64 flags) | |
97 | { | |
98 | union pds_core_adminq_cmd cmd = {}; | |
99 | struct pci_dev *pf_pdev; | |
100 | struct pdsc *pf; | |
101 | size_t cp_len; | |
102 | int err; | |
103 | ||
104 | pf_pdev = pci_physfn(padev->vf_pdev); | |
105 | pf = pci_get_drvdata(pf_pdev); | |
106 | ||
107 | dev_dbg(pf->dev, "%s: %s opcode %d\n", | |
108 | __func__, dev_name(&padev->aux_dev.dev), req->opcode); | |
109 | ||
110 | if (pf->state) | |
111 | return -ENXIO; | |
112 | ||
113 | /* Wrap the client's request */ | |
114 | cmd.client_request.opcode = PDS_AQ_CMD_CLIENT_CMD; | |
115 | cmd.client_request.client_id = cpu_to_le16(padev->client_id); | |
116 | cp_len = min_t(size_t, req_len, sizeof(cmd.client_request.client_cmd)); | |
117 | memcpy(cmd.client_request.client_cmd, req, cp_len); | |
118 | ||
119 | err = pdsc_adminq_post(pf, &cmd, resp, | |
120 | !!(flags & PDS_AQ_FLAG_FASTPOLL)); | |
121 | if (err && err != -EAGAIN) | |
122 | dev_info(pf->dev, "client admin cmd failed: %pe\n", | |
123 | ERR_PTR(err)); | |
124 | ||
125 | return err; | |
126 | } | |
127 | EXPORT_SYMBOL_GPL(pds_client_adminq_cmd); | |
128 | ||
4569cce4 SN |
129 | static void pdsc_auxbus_dev_release(struct device *dev) |
130 | { | |
131 | struct pds_auxiliary_dev *padev = | |
132 | container_of(dev, struct pds_auxiliary_dev, aux_dev.dev); | |
133 | ||
134 | kfree(padev); | |
135 | } | |
136 | ||
137 | static struct pds_auxiliary_dev *pdsc_auxbus_dev_register(struct pdsc *cf, | |
138 | struct pdsc *pf, | |
10659034 | 139 | u16 client_id, |
4569cce4 SN |
140 | char *name) |
141 | { | |
142 | struct auxiliary_device *aux_dev; | |
143 | struct pds_auxiliary_dev *padev; | |
144 | int err; | |
145 | ||
146 | padev = kzalloc(sizeof(*padev), GFP_KERNEL); | |
147 | if (!padev) | |
148 | return ERR_PTR(-ENOMEM); | |
149 | ||
150 | padev->vf_pdev = cf->pdev; | |
10659034 | 151 | padev->client_id = client_id; |
4569cce4 SN |
152 | |
153 | aux_dev = &padev->aux_dev; | |
154 | aux_dev->name = name; | |
155 | aux_dev->id = cf->uid; | |
156 | aux_dev->dev.parent = cf->dev; | |
157 | aux_dev->dev.release = pdsc_auxbus_dev_release; | |
158 | ||
159 | err = auxiliary_device_init(aux_dev); | |
160 | if (err < 0) { | |
161 | dev_warn(cf->dev, "auxiliary_device_init of %s failed: %pe\n", | |
162 | name, ERR_PTR(err)); | |
163 | goto err_out; | |
164 | } | |
165 | ||
166 | err = auxiliary_device_add(aux_dev); | |
167 | if (err) { | |
168 | dev_warn(cf->dev, "auxiliary_device_add of %s failed: %pe\n", | |
169 | name, ERR_PTR(err)); | |
170 | goto err_out_uninit; | |
171 | } | |
172 | ||
173 | return padev; | |
174 | ||
175 | err_out_uninit: | |
176 | auxiliary_device_uninit(aux_dev); | |
177 | err_out: | |
178 | kfree(padev); | |
179 | return ERR_PTR(err); | |
180 | } | |
181 | ||
182 | int pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf) | |
183 | { | |
184 | struct pds_auxiliary_dev *padev; | |
185 | int err = 0; | |
186 | ||
187 | mutex_lock(&pf->config_lock); | |
188 | ||
189 | padev = pf->vfs[cf->vf_id].padev; | |
190 | if (padev) { | |
b021d05e | 191 | pds_client_unregister(pf, padev->client_id); |
4569cce4 SN |
192 | auxiliary_device_delete(&padev->aux_dev); |
193 | auxiliary_device_uninit(&padev->aux_dev); | |
10659034 | 194 | padev->client_id = 0; |
4569cce4 SN |
195 | } |
196 | pf->vfs[cf->vf_id].padev = NULL; | |
197 | ||
198 | mutex_unlock(&pf->config_lock); | |
199 | return err; | |
200 | } | |
201 | ||
202 | int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf) | |
203 | { | |
204 | struct pds_auxiliary_dev *padev; | |
205 | enum pds_core_vif_types vt; | |
10659034 | 206 | char devname[PDS_DEVNAME_LEN]; |
4569cce4 | 207 | u16 vt_support; |
10659034 | 208 | int client_id; |
4569cce4 SN |
209 | int err = 0; |
210 | ||
211 | mutex_lock(&pf->config_lock); | |
212 | ||
213 | /* We only support vDPA so far, so it is the only one to | |
214 | * be verified that it is available in the Core device and | |
215 | * enabled in the devlink param. In the future this might | |
216 | * become a loop for several VIF types. | |
217 | */ | |
218 | ||
219 | /* Verify that the type is supported and enabled. It is not | |
220 | * an error if there is no auxbus device support for this | |
221 | * VF, it just means something else needs to happen with it. | |
222 | */ | |
223 | vt = PDS_DEV_TYPE_VDPA; | |
224 | vt_support = !!le16_to_cpu(pf->dev_ident.vif_types[vt]); | |
225 | if (!(vt_support && | |
226 | pf->viftype_status[vt].supported && | |
227 | pf->viftype_status[vt].enabled)) | |
228 | goto out_unlock; | |
229 | ||
10659034 SN |
230 | /* Need to register with FW and get the client_id before |
231 | * creating the aux device so that the aux client can run | |
232 | * adminq commands as part its probe | |
233 | */ | |
234 | snprintf(devname, sizeof(devname), "%s.%s.%d", | |
235 | PDS_CORE_DRV_NAME, pf->viftype_status[vt].name, cf->uid); | |
b021d05e | 236 | client_id = pds_client_register(pf, devname); |
10659034 SN |
237 | if (client_id < 0) { |
238 | err = client_id; | |
239 | goto out_unlock; | |
240 | } | |
241 | ||
242 | padev = pdsc_auxbus_dev_register(cf, pf, client_id, | |
4569cce4 SN |
243 | pf->viftype_status[vt].name); |
244 | if (IS_ERR(padev)) { | |
b021d05e | 245 | pds_client_unregister(pf, client_id); |
4569cce4 SN |
246 | err = PTR_ERR(padev); |
247 | goto out_unlock; | |
248 | } | |
249 | pf->vfs[cf->vf_id].padev = padev; | |
250 | ||
251 | out_unlock: | |
252 | mutex_unlock(&pf->config_lock); | |
253 | return err; | |
254 | } |