Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright(c) 2023 Advanced Micro Devices, Inc */
  3
  4#include <linux/errno.h>
  5#include <linux/pci.h>
  6#include <linux/utsname.h>
  7
  8#include "core.h"
  9
 10int pdsc_err_to_errno(enum pds_core_status_code code)
 11{
 12	switch (code) {
 13	case PDS_RC_SUCCESS:
 14		return 0;
 15	case PDS_RC_EVERSION:
 16	case PDS_RC_EQTYPE:
 17	case PDS_RC_EQID:
 18	case PDS_RC_EINVAL:
 19	case PDS_RC_ENOSUPP:
 20		return -EINVAL;
 21	case PDS_RC_EPERM:
 22		return -EPERM;
 23	case PDS_RC_ENOENT:
 24		return -ENOENT;
 25	case PDS_RC_EAGAIN:
 26		return -EAGAIN;
 27	case PDS_RC_ENOMEM:
 28		return -ENOMEM;
 29	case PDS_RC_EFAULT:
 30		return -EFAULT;
 31	case PDS_RC_EBUSY:
 32		return -EBUSY;
 33	case PDS_RC_EEXIST:
 34		return -EEXIST;
 35	case PDS_RC_EVFID:
 36		return -ENODEV;
 37	case PDS_RC_ECLIENT:
 38		return -ECHILD;
 39	case PDS_RC_ENOSPC:
 40		return -ENOSPC;
 41	case PDS_RC_ERANGE:
 42		return -ERANGE;
 43	case PDS_RC_BAD_ADDR:
 44		return -EFAULT;
 45	case PDS_RC_BAD_PCI:
 46		return -ENXIO;
 47	case PDS_RC_EOPCODE:
 48	case PDS_RC_EINTR:
 49	case PDS_RC_DEV_CMD:
 50	case PDS_RC_ERROR:
 51	case PDS_RC_ERDMA:
 52	case PDS_RC_EIO:
 53	default:
 54		return -EIO;
 55	}
 56}
 57
 58bool pdsc_is_fw_running(struct pdsc *pdsc)
 59{
 60	if (!pdsc->info_regs)
 61		return false;
 62
 63	pdsc->fw_status = ioread8(&pdsc->info_regs->fw_status);
 64	pdsc->last_fw_time = jiffies;
 65	pdsc->last_hb = ioread32(&pdsc->info_regs->fw_heartbeat);
 66
 67	/* Firmware is useful only if the running bit is set and
 68	 * fw_status != 0xff (bad PCI read)
 69	 */
 70	return (pdsc->fw_status != PDS_RC_BAD_PCI) &&
 71		(pdsc->fw_status & PDS_CORE_FW_STS_F_RUNNING);
 72}
 73
 74bool pdsc_is_fw_good(struct pdsc *pdsc)
 75{
 76	bool fw_running = pdsc_is_fw_running(pdsc);
 77	u8 gen;
 78
 79	/* Make sure to update the cached fw_status by calling
 80	 * pdsc_is_fw_running() before getting the generation
 81	 */
 82	gen = pdsc->fw_status & PDS_CORE_FW_STS_F_GENERATION;
 83
 84	return fw_running && gen == pdsc->fw_generation;
 85}
 86
 87static u8 pdsc_devcmd_status(struct pdsc *pdsc)
 88{
 89	return ioread8(&pdsc->cmd_regs->comp.status);
 90}
 91
 92static bool pdsc_devcmd_done(struct pdsc *pdsc)
 93{
 94	return ioread32(&pdsc->cmd_regs->done) & PDS_CORE_DEV_CMD_DONE;
 95}
 96
 97static void pdsc_devcmd_dbell(struct pdsc *pdsc)
 98{
 99	iowrite32(0, &pdsc->cmd_regs->done);
100	iowrite32(1, &pdsc->cmd_regs->doorbell);
101}
102
103static void pdsc_devcmd_clean(struct pdsc *pdsc)
104{
105	iowrite32(0, &pdsc->cmd_regs->doorbell);
106	memset_io(&pdsc->cmd_regs->cmd, 0, sizeof(pdsc->cmd_regs->cmd));
107}
108
109static const char *pdsc_devcmd_str(int opcode)
110{
111	switch (opcode) {
112	case PDS_CORE_CMD_NOP:
113		return "PDS_CORE_CMD_NOP";
114	case PDS_CORE_CMD_IDENTIFY:
115		return "PDS_CORE_CMD_IDENTIFY";
116	case PDS_CORE_CMD_RESET:
117		return "PDS_CORE_CMD_RESET";
118	case PDS_CORE_CMD_INIT:
119		return "PDS_CORE_CMD_INIT";
120	case PDS_CORE_CMD_FW_DOWNLOAD:
121		return "PDS_CORE_CMD_FW_DOWNLOAD";
122	case PDS_CORE_CMD_FW_CONTROL:
123		return "PDS_CORE_CMD_FW_CONTROL";
124	default:
125		return "PDS_CORE_CMD_UNKNOWN";
126	}
127}
128
129static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
130{
131	struct device *dev = pdsc->dev;
132	unsigned long start_time;
133	unsigned long max_wait;
134	unsigned long duration;
135	int timeout = 0;
136	bool running;
137	int done = 0;
138	int err = 0;
139	int status;
140
141	start_time = jiffies;
142	max_wait = start_time + (max_seconds * HZ);
143
144	while (!done && !timeout) {
145		running = pdsc_is_fw_running(pdsc);
146		if (!running)
147			break;
148
149		done = pdsc_devcmd_done(pdsc);
150		if (done)
151			break;
152
153		timeout = time_after(jiffies, max_wait);
154		if (timeout)
155			break;
156
157		usleep_range(100, 200);
158	}
159	duration = jiffies - start_time;
160
161	if (done && duration > HZ)
162		dev_dbg(dev, "DEVCMD %d %s after %ld secs\n",
163			opcode, pdsc_devcmd_str(opcode), duration / HZ);
164
165	if ((!done || timeout) && running) {
166		dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n",
167			opcode, pdsc_devcmd_str(opcode), done, timeout,
168			max_seconds);
169		err = -ETIMEDOUT;
170		pdsc_devcmd_clean(pdsc);
171	}
172
173	status = pdsc_devcmd_status(pdsc);
174	err = pdsc_err_to_errno(status);
175	if (err && err != -EAGAIN)
176		dev_err(dev, "DEVCMD %d %s failed, status=%d err %d %pe\n",
177			opcode, pdsc_devcmd_str(opcode), status, err,
178			ERR_PTR(err));
179
180	return err;
181}
182
183int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
184		       union pds_core_dev_comp *comp, int max_seconds)
185{
186	int err;
187
188	if (!pdsc->cmd_regs)
189		return -ENXIO;
190
191	memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd));
192	pdsc_devcmd_dbell(pdsc);
193	err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds);
194
195	if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq)
196		queue_work(pdsc->wq, &pdsc->health_work);
197	else
198		memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
199
200	return err;
201}
202
203int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
204		union pds_core_dev_comp *comp, int max_seconds)
205{
206	int err;
207
208	mutex_lock(&pdsc->devcmd_lock);
209	err = pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds);
210	mutex_unlock(&pdsc->devcmd_lock);
211
212	return err;
213}
214
215int pdsc_devcmd_init(struct pdsc *pdsc)
216{
217	union pds_core_dev_comp comp = {};
218	union pds_core_dev_cmd cmd = {
219		.opcode = PDS_CORE_CMD_INIT,
220	};
221
222	return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
223}
224
225int pdsc_devcmd_reset(struct pdsc *pdsc)
226{
227	union pds_core_dev_comp comp = {};
228	union pds_core_dev_cmd cmd = {
229		.reset.opcode = PDS_CORE_CMD_RESET,
230	};
231
232	return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
233}
234
235static int pdsc_devcmd_identify_locked(struct pdsc *pdsc)
236{
237	union pds_core_dev_comp comp = {};
238	union pds_core_dev_cmd cmd = {
239		.identify.opcode = PDS_CORE_CMD_IDENTIFY,
240		.identify.ver = PDS_CORE_IDENTITY_VERSION_1,
241	};
242
243	return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
244}
245
246static void pdsc_init_devinfo(struct pdsc *pdsc)
247{
248	pdsc->dev_info.asic_type = ioread8(&pdsc->info_regs->asic_type);
249	pdsc->dev_info.asic_rev = ioread8(&pdsc->info_regs->asic_rev);
250	pdsc->fw_generation = PDS_CORE_FW_STS_F_GENERATION &
251			      ioread8(&pdsc->info_regs->fw_status);
252
253	memcpy_fromio(pdsc->dev_info.fw_version,
254		      pdsc->info_regs->fw_version,
255		      PDS_CORE_DEVINFO_FWVERS_BUFLEN);
256	pdsc->dev_info.fw_version[PDS_CORE_DEVINFO_FWVERS_BUFLEN] = 0;
257
258	memcpy_fromio(pdsc->dev_info.serial_num,
259		      pdsc->info_regs->serial_num,
260		      PDS_CORE_DEVINFO_SERIAL_BUFLEN);
261	pdsc->dev_info.serial_num[PDS_CORE_DEVINFO_SERIAL_BUFLEN] = 0;
262
263	dev_dbg(pdsc->dev, "fw_version %s\n", pdsc->dev_info.fw_version);
264}
265
266static int pdsc_identify(struct pdsc *pdsc)
267{
268	struct pds_core_drv_identity drv = {};
269	size_t sz;
270	int err;
271	int n;
272
273	drv.drv_type = cpu_to_le32(PDS_DRIVER_LINUX);
274	/* Catching the return quiets a Wformat-truncation complaint */
275	n = snprintf(drv.driver_ver_str, sizeof(drv.driver_ver_str),
276		     "%s %s", PDS_CORE_DRV_NAME, utsname()->release);
277	if (n > sizeof(drv.driver_ver_str))
278		dev_dbg(pdsc->dev, "release name truncated, don't care\n");
279
280	/* Next let's get some info about the device
281	 * We use the devcmd_lock at this level in order to
282	 * get safe access to the cmd_regs->data before anyone
283	 * else can mess it up
284	 */
285	mutex_lock(&pdsc->devcmd_lock);
286
287	sz = min_t(size_t, sizeof(drv), sizeof(pdsc->cmd_regs->data));
288	memcpy_toio(&pdsc->cmd_regs->data, &drv, sz);
289
290	err = pdsc_devcmd_identify_locked(pdsc);
291	if (!err) {
292		sz = min_t(size_t, sizeof(pdsc->dev_ident),
293			   sizeof(pdsc->cmd_regs->data));
294		memcpy_fromio(&pdsc->dev_ident, &pdsc->cmd_regs->data, sz);
295	}
296	mutex_unlock(&pdsc->devcmd_lock);
297
298	if (err) {
299		dev_err(pdsc->dev, "Cannot identify device: %pe\n",
300			ERR_PTR(err));
301		return err;
302	}
303
304	if (isprint(pdsc->dev_info.fw_version[0]) &&
305	    isascii(pdsc->dev_info.fw_version[0]))
306		dev_info(pdsc->dev, "FW: %.*s\n",
307			 (int)(sizeof(pdsc->dev_info.fw_version) - 1),
308			 pdsc->dev_info.fw_version);
309	else
310		dev_info(pdsc->dev, "FW: (invalid string) 0x%02x 0x%02x 0x%02x 0x%02x ...\n",
311			 (u8)pdsc->dev_info.fw_version[0],
312			 (u8)pdsc->dev_info.fw_version[1],
313			 (u8)pdsc->dev_info.fw_version[2],
314			 (u8)pdsc->dev_info.fw_version[3]);
315
316	return 0;
317}
318
319int pdsc_dev_init(struct pdsc *pdsc)
320{
321	unsigned int nintrs;
322	int err;
323
324	/* Initial init and reset of device */
325	pdsc_init_devinfo(pdsc);
326	pdsc->devcmd_timeout = PDS_CORE_DEVCMD_TIMEOUT;
327
328	err = pdsc_devcmd_reset(pdsc);
329	if (err)
330		return err;
331
332	err = pdsc_identify(pdsc);
333	if (err)
334		return err;
335
336	pdsc_debugfs_add_ident(pdsc);
337
338	/* Now we can reserve interrupts */
339	nintrs = le32_to_cpu(pdsc->dev_ident.nintrs);
340	nintrs = min_t(unsigned int, num_online_cpus(), nintrs);
341
342	/* Get intr_info struct array for tracking */
343	pdsc->intr_info = kcalloc(nintrs, sizeof(*pdsc->intr_info), GFP_KERNEL);
344	if (!pdsc->intr_info) {
345		err = -ENOMEM;
346		goto err_out;
347	}
348
349	err = pci_alloc_irq_vectors(pdsc->pdev, nintrs, nintrs, PCI_IRQ_MSIX);
350	if (err != nintrs) {
351		dev_err(pdsc->dev, "Can't get %d intrs from OS: %pe\n",
352			nintrs, ERR_PTR(err));
353		err = -ENOSPC;
354		goto err_out;
355	}
356	pdsc->nintrs = nintrs;
357
358	return 0;
359
360err_out:
361	kfree(pdsc->intr_info);
362	pdsc->intr_info = NULL;
363
364	return err;
365}