18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
28c2ecf20Sopenharmony_ci/* Google virtual Ethernet (gve) driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015-2019 Google, Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/pci.h>
98c2ecf20Sopenharmony_ci#include "gve.h"
108c2ecf20Sopenharmony_ci#include "gve_adminq.h"
118c2ecf20Sopenharmony_ci#include "gve_register.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define GVE_MAX_ADMINQ_RELEASE_CHECK	500
148c2ecf20Sopenharmony_ci#define GVE_ADMINQ_SLEEP_LEN		20
158c2ecf20Sopenharmony_ci#define GVE_MAX_ADMINQ_EVENT_COUNTER_CHECK	100
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciint gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	priv->adminq = dma_alloc_coherent(dev, PAGE_SIZE,
208c2ecf20Sopenharmony_ci					  &priv->adminq_bus_addr, GFP_KERNEL);
218c2ecf20Sopenharmony_ci	if (unlikely(!priv->adminq))
228c2ecf20Sopenharmony_ci		return -ENOMEM;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	priv->adminq_mask = (PAGE_SIZE / sizeof(union gve_adminq_command)) - 1;
258c2ecf20Sopenharmony_ci	priv->adminq_prod_cnt = 0;
268c2ecf20Sopenharmony_ci	priv->adminq_cmd_fail = 0;
278c2ecf20Sopenharmony_ci	priv->adminq_timeouts = 0;
288c2ecf20Sopenharmony_ci	priv->adminq_describe_device_cnt = 0;
298c2ecf20Sopenharmony_ci	priv->adminq_cfg_device_resources_cnt = 0;
308c2ecf20Sopenharmony_ci	priv->adminq_register_page_list_cnt = 0;
318c2ecf20Sopenharmony_ci	priv->adminq_unregister_page_list_cnt = 0;
328c2ecf20Sopenharmony_ci	priv->adminq_create_tx_queue_cnt = 0;
338c2ecf20Sopenharmony_ci	priv->adminq_create_rx_queue_cnt = 0;
348c2ecf20Sopenharmony_ci	priv->adminq_destroy_tx_queue_cnt = 0;
358c2ecf20Sopenharmony_ci	priv->adminq_destroy_rx_queue_cnt = 0;
368c2ecf20Sopenharmony_ci	priv->adminq_dcfg_device_resources_cnt = 0;
378c2ecf20Sopenharmony_ci	priv->adminq_set_driver_parameter_cnt = 0;
388c2ecf20Sopenharmony_ci	priv->adminq_report_stats_cnt = 0;
398c2ecf20Sopenharmony_ci	priv->adminq_report_link_speed_cnt = 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Setup Admin queue with the device */
428c2ecf20Sopenharmony_ci	iowrite32be(priv->adminq_bus_addr / PAGE_SIZE,
438c2ecf20Sopenharmony_ci		    &priv->reg_bar0->adminq_pfn);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	gve_set_admin_queue_ok(priv);
468c2ecf20Sopenharmony_ci	return 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_civoid gve_adminq_release(struct gve_priv *priv)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int i = 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* Tell the device the adminq is leaving */
548c2ecf20Sopenharmony_ci	iowrite32be(0x0, &priv->reg_bar0->adminq_pfn);
558c2ecf20Sopenharmony_ci	while (ioread32be(&priv->reg_bar0->adminq_pfn)) {
568c2ecf20Sopenharmony_ci		/* If this is reached the device is unrecoverable and still
578c2ecf20Sopenharmony_ci		 * holding memory. Continue looping to avoid memory corruption,
588c2ecf20Sopenharmony_ci		 * but WARN so it is visible what is going on.
598c2ecf20Sopenharmony_ci		 */
608c2ecf20Sopenharmony_ci		if (i == GVE_MAX_ADMINQ_RELEASE_CHECK)
618c2ecf20Sopenharmony_ci			WARN(1, "Unrecoverable platform error!");
628c2ecf20Sopenharmony_ci		i++;
638c2ecf20Sopenharmony_ci		msleep(GVE_ADMINQ_SLEEP_LEN);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	gve_clear_device_rings_ok(priv);
668c2ecf20Sopenharmony_ci	gve_clear_device_resources_ok(priv);
678c2ecf20Sopenharmony_ci	gve_clear_admin_queue_ok(priv);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid gve_adminq_free(struct device *dev, struct gve_priv *priv)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	if (!gve_get_admin_queue_ok(priv))
738c2ecf20Sopenharmony_ci		return;
748c2ecf20Sopenharmony_ci	gve_adminq_release(priv);
758c2ecf20Sopenharmony_ci	dma_free_coherent(dev, PAGE_SIZE, priv->adminq, priv->adminq_bus_addr);
768c2ecf20Sopenharmony_ci	gve_clear_admin_queue_ok(priv);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void gve_adminq_kick_cmd(struct gve_priv *priv, u32 prod_cnt)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	iowrite32be(prod_cnt, &priv->reg_bar0->adminq_doorbell);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic bool gve_adminq_wait_for_cmd(struct gve_priv *priv, u32 prod_cnt)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int i;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	for (i = 0; i < GVE_MAX_ADMINQ_EVENT_COUNTER_CHECK; i++) {
898c2ecf20Sopenharmony_ci		if (ioread32be(&priv->reg_bar0->adminq_event_counter)
908c2ecf20Sopenharmony_ci		    == prod_cnt)
918c2ecf20Sopenharmony_ci			return true;
928c2ecf20Sopenharmony_ci		msleep(GVE_ADMINQ_SLEEP_LEN);
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return false;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int gve_adminq_parse_err(struct gve_priv *priv, u32 status)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	if (status != GVE_ADMINQ_COMMAND_PASSED &&
1018c2ecf20Sopenharmony_ci	    status != GVE_ADMINQ_COMMAND_UNSET) {
1028c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "AQ command failed with status %d\n", status);
1038c2ecf20Sopenharmony_ci		priv->adminq_cmd_fail++;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	switch (status) {
1068c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_PASSED:
1078c2ecf20Sopenharmony_ci		return 0;
1088c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_UNSET:
1098c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "parse_aq_err: err and status both unset, this should not be possible.\n");
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_ABORTED:
1128c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_CANCELLED:
1138c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_DATALOSS:
1148c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_FAILED_PRECONDITION:
1158c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_UNAVAILABLE:
1168c2ecf20Sopenharmony_ci		return -EAGAIN;
1178c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_ALREADY_EXISTS:
1188c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_INTERNAL_ERROR:
1198c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_INVALID_ARGUMENT:
1208c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_NOT_FOUND:
1218c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_OUT_OF_RANGE:
1228c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_UNKNOWN_ERROR:
1238c2ecf20Sopenharmony_ci		return -EINVAL;
1248c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_DEADLINE_EXCEEDED:
1258c2ecf20Sopenharmony_ci		return -ETIME;
1268c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_PERMISSION_DENIED:
1278c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_UNAUTHENTICATED:
1288c2ecf20Sopenharmony_ci		return -EACCES;
1298c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_RESOURCE_EXHAUSTED:
1308c2ecf20Sopenharmony_ci		return -ENOMEM;
1318c2ecf20Sopenharmony_ci	case GVE_ADMINQ_COMMAND_ERROR_UNIMPLEMENTED:
1328c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1338c2ecf20Sopenharmony_ci	default:
1348c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "parse_aq_err: unknown status code %d\n", status);
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* Flushes all AQ commands currently queued and waits for them to complete.
1408c2ecf20Sopenharmony_ci * If there are failures, it will return the first error.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic int gve_adminq_kick_and_wait(struct gve_priv *priv)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int tail, head;
1458c2ecf20Sopenharmony_ci	int i;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
1488c2ecf20Sopenharmony_ci	head = priv->adminq_prod_cnt;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	gve_adminq_kick_cmd(priv, head);
1518c2ecf20Sopenharmony_ci	if (!gve_adminq_wait_for_cmd(priv, head)) {
1528c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "AQ commands timed out, need to reset AQ\n");
1538c2ecf20Sopenharmony_ci		priv->adminq_timeouts++;
1548c2ecf20Sopenharmony_ci		return -ENOTRECOVERABLE;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for (i = tail; i < head; i++) {
1588c2ecf20Sopenharmony_ci		union gve_adminq_command *cmd;
1598c2ecf20Sopenharmony_ci		u32 status, err;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		cmd = &priv->adminq[i & priv->adminq_mask];
1628c2ecf20Sopenharmony_ci		status = be32_to_cpu(READ_ONCE(cmd->status));
1638c2ecf20Sopenharmony_ci		err = gve_adminq_parse_err(priv, status);
1648c2ecf20Sopenharmony_ci		if (err)
1658c2ecf20Sopenharmony_ci			// Return the first error if we failed.
1668c2ecf20Sopenharmony_ci			return err;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/* This function is not threadsafe - the caller is responsible for any
1738c2ecf20Sopenharmony_ci * necessary locks.
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic int gve_adminq_issue_cmd(struct gve_priv *priv,
1768c2ecf20Sopenharmony_ci				union gve_adminq_command *cmd_orig)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	union gve_adminq_command *cmd;
1798c2ecf20Sopenharmony_ci	u32 opcode;
1808c2ecf20Sopenharmony_ci	u32 tail;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	// Check if next command will overflow the buffer.
1858c2ecf20Sopenharmony_ci	if (((priv->adminq_prod_cnt + 1) & priv->adminq_mask) ==
1868c2ecf20Sopenharmony_ci	    (tail & priv->adminq_mask)) {
1878c2ecf20Sopenharmony_ci		int err;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		// Flush existing commands to make room.
1908c2ecf20Sopenharmony_ci		err = gve_adminq_kick_and_wait(priv);
1918c2ecf20Sopenharmony_ci		if (err)
1928c2ecf20Sopenharmony_ci			return err;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		// Retry.
1958c2ecf20Sopenharmony_ci		tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
1968c2ecf20Sopenharmony_ci		if (((priv->adminq_prod_cnt + 1) & priv->adminq_mask) ==
1978c2ecf20Sopenharmony_ci		    (tail & priv->adminq_mask)) {
1988c2ecf20Sopenharmony_ci			// This should never happen. We just flushed the
1998c2ecf20Sopenharmony_ci			// command queue so there should be enough space.
2008c2ecf20Sopenharmony_ci			return -ENOMEM;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	cmd = &priv->adminq[priv->adminq_prod_cnt & priv->adminq_mask];
2058c2ecf20Sopenharmony_ci	priv->adminq_prod_cnt++;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	memcpy(cmd, cmd_orig, sizeof(*cmd_orig));
2088c2ecf20Sopenharmony_ci	opcode = be32_to_cpu(READ_ONCE(cmd->opcode));
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	switch (opcode) {
2118c2ecf20Sopenharmony_ci	case GVE_ADMINQ_DESCRIBE_DEVICE:
2128c2ecf20Sopenharmony_ci		priv->adminq_describe_device_cnt++;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	case GVE_ADMINQ_CONFIGURE_DEVICE_RESOURCES:
2158c2ecf20Sopenharmony_ci		priv->adminq_cfg_device_resources_cnt++;
2168c2ecf20Sopenharmony_ci		break;
2178c2ecf20Sopenharmony_ci	case GVE_ADMINQ_REGISTER_PAGE_LIST:
2188c2ecf20Sopenharmony_ci		priv->adminq_register_page_list_cnt++;
2198c2ecf20Sopenharmony_ci		break;
2208c2ecf20Sopenharmony_ci	case GVE_ADMINQ_UNREGISTER_PAGE_LIST:
2218c2ecf20Sopenharmony_ci		priv->adminq_unregister_page_list_cnt++;
2228c2ecf20Sopenharmony_ci		break;
2238c2ecf20Sopenharmony_ci	case GVE_ADMINQ_CREATE_TX_QUEUE:
2248c2ecf20Sopenharmony_ci		priv->adminq_create_tx_queue_cnt++;
2258c2ecf20Sopenharmony_ci		break;
2268c2ecf20Sopenharmony_ci	case GVE_ADMINQ_CREATE_RX_QUEUE:
2278c2ecf20Sopenharmony_ci		priv->adminq_create_rx_queue_cnt++;
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	case GVE_ADMINQ_DESTROY_TX_QUEUE:
2308c2ecf20Sopenharmony_ci		priv->adminq_destroy_tx_queue_cnt++;
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci	case GVE_ADMINQ_DESTROY_RX_QUEUE:
2338c2ecf20Sopenharmony_ci		priv->adminq_destroy_rx_queue_cnt++;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	case GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES:
2368c2ecf20Sopenharmony_ci		priv->adminq_dcfg_device_resources_cnt++;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case GVE_ADMINQ_SET_DRIVER_PARAMETER:
2398c2ecf20Sopenharmony_ci		priv->adminq_set_driver_parameter_cnt++;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case GVE_ADMINQ_REPORT_STATS:
2428c2ecf20Sopenharmony_ci		priv->adminq_report_stats_cnt++;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	case GVE_ADMINQ_REPORT_LINK_SPEED:
2458c2ecf20Sopenharmony_ci		priv->adminq_report_link_speed_cnt++;
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	default:
2488c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return 0;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* This function is not threadsafe - the caller is responsible for any
2558c2ecf20Sopenharmony_ci * necessary locks.
2568c2ecf20Sopenharmony_ci * The caller is also responsible for making sure there are no commands
2578c2ecf20Sopenharmony_ci * waiting to be executed.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_cistatic int gve_adminq_execute_cmd(struct gve_priv *priv, union gve_adminq_command *cmd_orig)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	u32 tail, head;
2628c2ecf20Sopenharmony_ci	int err;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
2658c2ecf20Sopenharmony_ci	head = priv->adminq_prod_cnt;
2668c2ecf20Sopenharmony_ci	if (tail != head)
2678c2ecf20Sopenharmony_ci		// This is not a valid path
2688c2ecf20Sopenharmony_ci		return -EINVAL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	err = gve_adminq_issue_cmd(priv, cmd_orig);
2718c2ecf20Sopenharmony_ci	if (err)
2728c2ecf20Sopenharmony_ci		return err;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return gve_adminq_kick_and_wait(priv);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/* The device specifies that the management vector can either be the first irq
2788c2ecf20Sopenharmony_ci * or the last irq. ntfy_blk_msix_base_idx indicates the first irq assigned to
2798c2ecf20Sopenharmony_ci * the ntfy blks. It if is 0 then the management vector is last, if it is 1 then
2808c2ecf20Sopenharmony_ci * the management vector is first.
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * gve arranges the msix vectors so that the management vector is last.
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_ci#define GVE_NTFY_BLK_BASE_MSIX_IDX	0
2858c2ecf20Sopenharmony_ciint gve_adminq_configure_device_resources(struct gve_priv *priv,
2868c2ecf20Sopenharmony_ci					  dma_addr_t counter_array_bus_addr,
2878c2ecf20Sopenharmony_ci					  u32 num_counters,
2888c2ecf20Sopenharmony_ci					  dma_addr_t db_array_bus_addr,
2898c2ecf20Sopenharmony_ci					  u32 num_ntfy_blks)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
2948c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_CONFIGURE_DEVICE_RESOURCES);
2958c2ecf20Sopenharmony_ci	cmd.configure_device_resources =
2968c2ecf20Sopenharmony_ci		(struct gve_adminq_configure_device_resources) {
2978c2ecf20Sopenharmony_ci		.counter_array = cpu_to_be64(counter_array_bus_addr),
2988c2ecf20Sopenharmony_ci		.num_counters = cpu_to_be32(num_counters),
2998c2ecf20Sopenharmony_ci		.irq_db_addr = cpu_to_be64(db_array_bus_addr),
3008c2ecf20Sopenharmony_ci		.num_irq_dbs = cpu_to_be32(num_ntfy_blks),
3018c2ecf20Sopenharmony_ci		.irq_db_stride = cpu_to_be32(sizeof(priv->ntfy_blocks[0])),
3028c2ecf20Sopenharmony_ci		.ntfy_blk_msix_base_idx =
3038c2ecf20Sopenharmony_ci					cpu_to_be32(GVE_NTFY_BLK_BASE_MSIX_IDX),
3048c2ecf20Sopenharmony_ci	};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return gve_adminq_execute_cmd(priv, &cmd);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ciint gve_adminq_deconfigure_device_resources(struct gve_priv *priv)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
3148c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return gve_adminq_execute_cmd(priv, &cmd);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int gve_adminq_create_tx_queue(struct gve_priv *priv, u32 queue_index)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct gve_tx_ring *tx = &priv->tx[queue_index];
3228c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
3238c2ecf20Sopenharmony_ci	int err;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
3268c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_CREATE_TX_QUEUE);
3278c2ecf20Sopenharmony_ci	cmd.create_tx_queue = (struct gve_adminq_create_tx_queue) {
3288c2ecf20Sopenharmony_ci		.queue_id = cpu_to_be32(queue_index),
3298c2ecf20Sopenharmony_ci		.reserved = 0,
3308c2ecf20Sopenharmony_ci		.queue_resources_addr =
3318c2ecf20Sopenharmony_ci			cpu_to_be64(tx->q_resources_bus),
3328c2ecf20Sopenharmony_ci		.tx_ring_addr = cpu_to_be64(tx->bus),
3338c2ecf20Sopenharmony_ci		.queue_page_list_id = cpu_to_be32(tx->tx_fifo.qpl->id),
3348c2ecf20Sopenharmony_ci		.ntfy_id = cpu_to_be32(tx->ntfy_id),
3358c2ecf20Sopenharmony_ci	};
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	err = gve_adminq_issue_cmd(priv, &cmd);
3388c2ecf20Sopenharmony_ci	if (err)
3398c2ecf20Sopenharmony_ci		return err;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciint gve_adminq_create_tx_queues(struct gve_priv *priv, u32 num_queues)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	int err;
3478c2ecf20Sopenharmony_ci	int i;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	for (i = 0; i < num_queues; i++) {
3508c2ecf20Sopenharmony_ci		err = gve_adminq_create_tx_queue(priv, i);
3518c2ecf20Sopenharmony_ci		if (err)
3528c2ecf20Sopenharmony_ci			return err;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return gve_adminq_kick_and_wait(priv);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int gve_adminq_create_rx_queue(struct gve_priv *priv, u32 queue_index)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct gve_rx_ring *rx = &priv->rx[queue_index];
3618c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
3628c2ecf20Sopenharmony_ci	int err;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
3658c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_CREATE_RX_QUEUE);
3668c2ecf20Sopenharmony_ci	cmd.create_rx_queue = (struct gve_adminq_create_rx_queue) {
3678c2ecf20Sopenharmony_ci		.queue_id = cpu_to_be32(queue_index),
3688c2ecf20Sopenharmony_ci		.index = cpu_to_be32(queue_index),
3698c2ecf20Sopenharmony_ci		.reserved = 0,
3708c2ecf20Sopenharmony_ci		.ntfy_id = cpu_to_be32(rx->ntfy_id),
3718c2ecf20Sopenharmony_ci		.queue_resources_addr = cpu_to_be64(rx->q_resources_bus),
3728c2ecf20Sopenharmony_ci		.rx_desc_ring_addr = cpu_to_be64(rx->desc.bus),
3738c2ecf20Sopenharmony_ci		.rx_data_ring_addr = cpu_to_be64(rx->data.data_bus),
3748c2ecf20Sopenharmony_ci		.queue_page_list_id = cpu_to_be32(rx->data.qpl->id),
3758c2ecf20Sopenharmony_ci	};
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	err = gve_adminq_issue_cmd(priv, &cmd);
3788c2ecf20Sopenharmony_ci	if (err)
3798c2ecf20Sopenharmony_ci		return err;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ciint gve_adminq_create_rx_queues(struct gve_priv *priv, u32 num_queues)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	int err;
3878c2ecf20Sopenharmony_ci	int i;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	for (i = 0; i < num_queues; i++) {
3908c2ecf20Sopenharmony_ci		err = gve_adminq_create_rx_queue(priv, i);
3918c2ecf20Sopenharmony_ci		if (err)
3928c2ecf20Sopenharmony_ci			return err;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return gve_adminq_kick_and_wait(priv);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int gve_adminq_destroy_tx_queue(struct gve_priv *priv, u32 queue_index)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
4018c2ecf20Sopenharmony_ci	int err;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4048c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_DESTROY_TX_QUEUE);
4058c2ecf20Sopenharmony_ci	cmd.destroy_tx_queue = (struct gve_adminq_destroy_tx_queue) {
4068c2ecf20Sopenharmony_ci		.queue_id = cpu_to_be32(queue_index),
4078c2ecf20Sopenharmony_ci	};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	err = gve_adminq_issue_cmd(priv, &cmd);
4108c2ecf20Sopenharmony_ci	if (err)
4118c2ecf20Sopenharmony_ci		return err;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ciint gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 num_queues)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	int err;
4198c2ecf20Sopenharmony_ci	int i;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	for (i = 0; i < num_queues; i++) {
4228c2ecf20Sopenharmony_ci		err = gve_adminq_destroy_tx_queue(priv, i);
4238c2ecf20Sopenharmony_ci		if (err)
4248c2ecf20Sopenharmony_ci			return err;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return gve_adminq_kick_and_wait(priv);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int gve_adminq_destroy_rx_queue(struct gve_priv *priv, u32 queue_index)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
4338c2ecf20Sopenharmony_ci	int err;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4368c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_DESTROY_RX_QUEUE);
4378c2ecf20Sopenharmony_ci	cmd.destroy_rx_queue = (struct gve_adminq_destroy_rx_queue) {
4388c2ecf20Sopenharmony_ci		.queue_id = cpu_to_be32(queue_index),
4398c2ecf20Sopenharmony_ci	};
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	err = gve_adminq_issue_cmd(priv, &cmd);
4428c2ecf20Sopenharmony_ci	if (err)
4438c2ecf20Sopenharmony_ci		return err;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return 0;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ciint gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 num_queues)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	int err;
4518c2ecf20Sopenharmony_ci	int i;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	for (i = 0; i < num_queues; i++) {
4548c2ecf20Sopenharmony_ci		err = gve_adminq_destroy_rx_queue(priv, i);
4558c2ecf20Sopenharmony_ci		if (err)
4568c2ecf20Sopenharmony_ci			return err;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return gve_adminq_kick_and_wait(priv);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ciint gve_adminq_describe_device(struct gve_priv *priv)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct gve_device_descriptor *descriptor;
4658c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
4668c2ecf20Sopenharmony_ci	dma_addr_t descriptor_bus;
4678c2ecf20Sopenharmony_ci	int err = 0;
4688c2ecf20Sopenharmony_ci	u8 *mac;
4698c2ecf20Sopenharmony_ci	u16 mtu;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4728c2ecf20Sopenharmony_ci	descriptor = dma_alloc_coherent(&priv->pdev->dev, PAGE_SIZE,
4738c2ecf20Sopenharmony_ci					&descriptor_bus, GFP_KERNEL);
4748c2ecf20Sopenharmony_ci	if (!descriptor)
4758c2ecf20Sopenharmony_ci		return -ENOMEM;
4768c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_DESCRIBE_DEVICE);
4778c2ecf20Sopenharmony_ci	cmd.describe_device.device_descriptor_addr =
4788c2ecf20Sopenharmony_ci						cpu_to_be64(descriptor_bus);
4798c2ecf20Sopenharmony_ci	cmd.describe_device.device_descriptor_version =
4808c2ecf20Sopenharmony_ci			cpu_to_be32(GVE_ADMINQ_DEVICE_DESCRIPTOR_VERSION);
4818c2ecf20Sopenharmony_ci	cmd.describe_device.available_length = cpu_to_be32(PAGE_SIZE);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	err = gve_adminq_execute_cmd(priv, &cmd);
4848c2ecf20Sopenharmony_ci	if (err)
4858c2ecf20Sopenharmony_ci		goto free_device_descriptor;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
4888c2ecf20Sopenharmony_ci	if (priv->tx_desc_cnt * sizeof(priv->tx->desc[0]) < PAGE_SIZE) {
4898c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "Tx desc count %d too low\n", priv->tx_desc_cnt);
4908c2ecf20Sopenharmony_ci		err = -EINVAL;
4918c2ecf20Sopenharmony_ci		goto free_device_descriptor;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci	priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
4948c2ecf20Sopenharmony_ci	if (priv->rx_desc_cnt * sizeof(priv->rx->desc.desc_ring[0])
4958c2ecf20Sopenharmony_ci	    < PAGE_SIZE ||
4968c2ecf20Sopenharmony_ci	    priv->rx_desc_cnt * sizeof(priv->rx->data.data_ring[0])
4978c2ecf20Sopenharmony_ci	    < PAGE_SIZE) {
4988c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "Rx desc count %d too low\n", priv->rx_desc_cnt);
4998c2ecf20Sopenharmony_ci		err = -EINVAL;
5008c2ecf20Sopenharmony_ci		goto free_device_descriptor;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	priv->max_registered_pages =
5038c2ecf20Sopenharmony_ci				be64_to_cpu(descriptor->max_registered_pages);
5048c2ecf20Sopenharmony_ci	mtu = be16_to_cpu(descriptor->mtu);
5058c2ecf20Sopenharmony_ci	if (mtu < ETH_MIN_MTU) {
5068c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "MTU %d below minimum MTU\n", mtu);
5078c2ecf20Sopenharmony_ci		err = -EINVAL;
5088c2ecf20Sopenharmony_ci		goto free_device_descriptor;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci	priv->dev->max_mtu = mtu;
5118c2ecf20Sopenharmony_ci	priv->num_event_counters = be16_to_cpu(descriptor->counters);
5128c2ecf20Sopenharmony_ci	ether_addr_copy(priv->dev->dev_addr, descriptor->mac);
5138c2ecf20Sopenharmony_ci	mac = descriptor->mac;
5148c2ecf20Sopenharmony_ci	dev_info(&priv->pdev->dev, "MAC addr: %pM\n", mac);
5158c2ecf20Sopenharmony_ci	priv->tx_pages_per_qpl = be16_to_cpu(descriptor->tx_pages_per_qpl);
5168c2ecf20Sopenharmony_ci	priv->rx_pages_per_qpl = be16_to_cpu(descriptor->rx_pages_per_qpl);
5178c2ecf20Sopenharmony_ci	if (priv->rx_pages_per_qpl < priv->rx_desc_cnt) {
5188c2ecf20Sopenharmony_ci		dev_err(&priv->pdev->dev, "rx_pages_per_qpl cannot be smaller than rx_desc_cnt, setting rx_desc_cnt down to %d.\n",
5198c2ecf20Sopenharmony_ci			priv->rx_pages_per_qpl);
5208c2ecf20Sopenharmony_ci		priv->rx_desc_cnt = priv->rx_pages_per_qpl;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	priv->default_num_queues = be16_to_cpu(descriptor->default_num_queues);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cifree_device_descriptor:
5258c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev, sizeof(*descriptor), descriptor,
5268c2ecf20Sopenharmony_ci			  descriptor_bus);
5278c2ecf20Sopenharmony_ci	return err;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ciint gve_adminq_register_page_list(struct gve_priv *priv,
5318c2ecf20Sopenharmony_ci				  struct gve_queue_page_list *qpl)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct device *hdev = &priv->pdev->dev;
5348c2ecf20Sopenharmony_ci	u32 num_entries = qpl->num_entries;
5358c2ecf20Sopenharmony_ci	u32 size = num_entries * sizeof(qpl->page_buses[0]);
5368c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
5378c2ecf20Sopenharmony_ci	dma_addr_t page_list_bus;
5388c2ecf20Sopenharmony_ci	__be64 *page_list;
5398c2ecf20Sopenharmony_ci	int err;
5408c2ecf20Sopenharmony_ci	int i;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
5438c2ecf20Sopenharmony_ci	page_list = dma_alloc_coherent(hdev, size, &page_list_bus, GFP_KERNEL);
5448c2ecf20Sopenharmony_ci	if (!page_list)
5458c2ecf20Sopenharmony_ci		return -ENOMEM;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	for (i = 0; i < num_entries; i++)
5488c2ecf20Sopenharmony_ci		page_list[i] = cpu_to_be64(qpl->page_buses[i]);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_REGISTER_PAGE_LIST);
5518c2ecf20Sopenharmony_ci	cmd.reg_page_list = (struct gve_adminq_register_page_list) {
5528c2ecf20Sopenharmony_ci		.page_list_id = cpu_to_be32(qpl->id),
5538c2ecf20Sopenharmony_ci		.num_pages = cpu_to_be32(num_entries),
5548c2ecf20Sopenharmony_ci		.page_address_list_addr = cpu_to_be64(page_list_bus),
5558c2ecf20Sopenharmony_ci	};
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	err = gve_adminq_execute_cmd(priv, &cmd);
5588c2ecf20Sopenharmony_ci	dma_free_coherent(hdev, size, page_list, page_list_bus);
5598c2ecf20Sopenharmony_ci	return err;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ciint gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
5678c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_UNREGISTER_PAGE_LIST);
5688c2ecf20Sopenharmony_ci	cmd.unreg_page_list = (struct gve_adminq_unregister_page_list) {
5698c2ecf20Sopenharmony_ci		.page_list_id = cpu_to_be32(page_list_id),
5708c2ecf20Sopenharmony_ci	};
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	return gve_adminq_execute_cmd(priv, &cmd);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ciint gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
5808c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_SET_DRIVER_PARAMETER);
5818c2ecf20Sopenharmony_ci	cmd.set_driver_param = (struct gve_adminq_set_driver_parameter) {
5828c2ecf20Sopenharmony_ci		.parameter_type = cpu_to_be32(GVE_SET_PARAM_MTU),
5838c2ecf20Sopenharmony_ci		.parameter_value = cpu_to_be64(mtu),
5848c2ecf20Sopenharmony_ci	};
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return gve_adminq_execute_cmd(priv, &cmd);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ciint gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
5908c2ecf20Sopenharmony_ci			    dma_addr_t stats_report_addr, u64 interval)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	union gve_adminq_command cmd;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
5958c2ecf20Sopenharmony_ci	cmd.opcode = cpu_to_be32(GVE_ADMINQ_REPORT_STATS);
5968c2ecf20Sopenharmony_ci	cmd.report_stats = (struct gve_adminq_report_stats) {
5978c2ecf20Sopenharmony_ci		.stats_report_len = cpu_to_be64(stats_report_len),
5988c2ecf20Sopenharmony_ci		.stats_report_addr = cpu_to_be64(stats_report_addr),
5998c2ecf20Sopenharmony_ci		.interval = cpu_to_be64(interval),
6008c2ecf20Sopenharmony_ci	};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return gve_adminq_execute_cmd(priv, &cmd);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ciint gve_adminq_report_link_speed(struct gve_priv *priv)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	union gve_adminq_command gvnic_cmd;
6088c2ecf20Sopenharmony_ci	dma_addr_t link_speed_region_bus;
6098c2ecf20Sopenharmony_ci	__be64 *link_speed_region;
6108c2ecf20Sopenharmony_ci	int err;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	link_speed_region =
6138c2ecf20Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev, sizeof(*link_speed_region),
6148c2ecf20Sopenharmony_ci				   &link_speed_region_bus, GFP_KERNEL);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (!link_speed_region)
6178c2ecf20Sopenharmony_ci		return -ENOMEM;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	memset(&gvnic_cmd, 0, sizeof(gvnic_cmd));
6208c2ecf20Sopenharmony_ci	gvnic_cmd.opcode = cpu_to_be32(GVE_ADMINQ_REPORT_LINK_SPEED);
6218c2ecf20Sopenharmony_ci	gvnic_cmd.report_link_speed.link_speed_address =
6228c2ecf20Sopenharmony_ci		cpu_to_be64(link_speed_region_bus);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	err = gve_adminq_execute_cmd(priv, &gvnic_cmd);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	priv->link_speed = be64_to_cpu(*link_speed_region);
6278c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev, sizeof(*link_speed_region), link_speed_region,
6288c2ecf20Sopenharmony_ci			  link_speed_region_bus);
6298c2ecf20Sopenharmony_ci	return err;
6308c2ecf20Sopenharmony_ci}
631