162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright 2008-2013 Solarflare Communications Inc.
462306a36Sopenharmony_ci * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#ifndef CDX_MCDI_H
862306a36Sopenharmony_ci#define CDX_MCDI_H
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/mutex.h>
1162306a36Sopenharmony_ci#include <linux/kref.h>
1262306a36Sopenharmony_ci#include <linux/rpmsg.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "bitfield.h"
1562306a36Sopenharmony_ci#include "mc_cdx_pcol.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#ifdef DEBUG
1862306a36Sopenharmony_ci#define CDX_WARN_ON_ONCE_PARANOID(x) WARN_ON_ONCE(x)
1962306a36Sopenharmony_ci#define CDX_WARN_ON_PARANOID(x) WARN_ON(x)
2062306a36Sopenharmony_ci#else
2162306a36Sopenharmony_ci#define CDX_WARN_ON_ONCE_PARANOID(x) do {} while (0)
2262306a36Sopenharmony_ci#define CDX_WARN_ON_PARANOID(x) do {} while (0)
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/**
2662306a36Sopenharmony_ci * enum cdx_mcdi_mode - MCDI transaction mode
2762306a36Sopenharmony_ci * @MCDI_MODE_EVENTS: wait for an mcdi response callback.
2862306a36Sopenharmony_ci * @MCDI_MODE_FAIL: we think MCDI is dead, so fail-fast all calls
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cienum cdx_mcdi_mode {
3162306a36Sopenharmony_ci	MCDI_MODE_EVENTS,
3262306a36Sopenharmony_ci	MCDI_MODE_FAIL,
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define MCDI_RPC_TIMEOUT	(10 * HZ)
3662306a36Sopenharmony_ci#define MCDI_RPC_LONG_TIMEOU	(60 * HZ)
3762306a36Sopenharmony_ci#define MCDI_RPC_POST_RST_TIME	(10 * HZ)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MCDI_BUF_LEN (8 + MCDI_CTL_SDU_LEN_MAX)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * enum cdx_mcdi_cmd_state - State for an individual MCDI command
4362306a36Sopenharmony_ci * @MCDI_STATE_QUEUED: Command not started and is waiting to run.
4462306a36Sopenharmony_ci * @MCDI_STATE_RETRY: Command was submitted and MC rejected with no resources,
4562306a36Sopenharmony_ci *	as MC have too many outstanding commands. Command will be retried once
4662306a36Sopenharmony_ci *	another command returns.
4762306a36Sopenharmony_ci * @MCDI_STATE_RUNNING: Command was accepted and is running.
4862306a36Sopenharmony_ci * @MCDI_STATE_RUNNING_CANCELLED: Command is running but the issuer cancelled
4962306a36Sopenharmony_ci *	the command.
5062306a36Sopenharmony_ci * @MCDI_STATE_FINISHED: Processing of this command has completed.
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cienum cdx_mcdi_cmd_state {
5462306a36Sopenharmony_ci	MCDI_STATE_QUEUED,
5562306a36Sopenharmony_ci	MCDI_STATE_RETRY,
5662306a36Sopenharmony_ci	MCDI_STATE_RUNNING,
5762306a36Sopenharmony_ci	MCDI_STATE_RUNNING_CANCELLED,
5862306a36Sopenharmony_ci	MCDI_STATE_FINISHED,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/**
6262306a36Sopenharmony_ci * struct cdx_mcdi - CDX MCDI Firmware interface, to interact
6362306a36Sopenharmony_ci *	with CDX controller.
6462306a36Sopenharmony_ci * @mcdi: MCDI interface
6562306a36Sopenharmony_ci * @mcdi_ops: MCDI operations
6662306a36Sopenharmony_ci * @r5_rproc : R5 Remoteproc device handle
6762306a36Sopenharmony_ci * @rpdev: RPMsg device
6862306a36Sopenharmony_ci * @ept: RPMsg endpoint
6962306a36Sopenharmony_ci * @work: Post probe work
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistruct cdx_mcdi {
7262306a36Sopenharmony_ci	/* MCDI interface */
7362306a36Sopenharmony_ci	struct cdx_mcdi_data *mcdi;
7462306a36Sopenharmony_ci	const struct cdx_mcdi_ops *mcdi_ops;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	struct rproc *r5_rproc;
7762306a36Sopenharmony_ci	struct rpmsg_device *rpdev;
7862306a36Sopenharmony_ci	struct rpmsg_endpoint *ept;
7962306a36Sopenharmony_ci	struct work_struct work;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct cdx_mcdi_ops {
8362306a36Sopenharmony_ci	void (*mcdi_request)(struct cdx_mcdi *cdx,
8462306a36Sopenharmony_ci			     const struct cdx_dword *hdr, size_t hdr_len,
8562306a36Sopenharmony_ci			     const struct cdx_dword *sdu, size_t sdu_len);
8662306a36Sopenharmony_ci	unsigned int (*mcdi_rpc_timeout)(struct cdx_mcdi *cdx, unsigned int cmd);
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_citypedef void cdx_mcdi_async_completer(struct cdx_mcdi *cdx,
9062306a36Sopenharmony_ci				      unsigned long cookie, int rc,
9162306a36Sopenharmony_ci				      struct cdx_dword *outbuf,
9262306a36Sopenharmony_ci				      size_t outlen_actual);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * struct cdx_mcdi_cmd - An outstanding MCDI command
9662306a36Sopenharmony_ci * @ref: Reference count. There will be one reference if the command is
9762306a36Sopenharmony_ci *	in the mcdi_iface cmd_list, another if it's on a cleanup list,
9862306a36Sopenharmony_ci *	and a third if it's queued in the work queue.
9962306a36Sopenharmony_ci * @list: The data for this entry in mcdi->cmd_list
10062306a36Sopenharmony_ci * @cleanup_list: The data for this entry in a cleanup list
10162306a36Sopenharmony_ci * @work: The work item for this command, queued in mcdi->workqueue
10262306a36Sopenharmony_ci * @mcdi: The mcdi_iface for this command
10362306a36Sopenharmony_ci * @state: The state of this command
10462306a36Sopenharmony_ci * @inlen: inbuf length
10562306a36Sopenharmony_ci * @inbuf: Input buffer
10662306a36Sopenharmony_ci * @quiet: Whether to silence errors
10762306a36Sopenharmony_ci * @reboot_seen: Whether a reboot has been seen during this command,
10862306a36Sopenharmony_ci *	to prevent duplicates
10962306a36Sopenharmony_ci * @seq: Sequence number
11062306a36Sopenharmony_ci * @started: Jiffies this command was started at
11162306a36Sopenharmony_ci * @cookie: Context for completion function
11262306a36Sopenharmony_ci * @completer: Completion function
11362306a36Sopenharmony_ci * @handle: Command handle
11462306a36Sopenharmony_ci * @cmd: Command number
11562306a36Sopenharmony_ci * @rc: Return code
11662306a36Sopenharmony_ci * @outlen: Length of output buffer
11762306a36Sopenharmony_ci * @outbuf: Output buffer
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistruct cdx_mcdi_cmd {
12062306a36Sopenharmony_ci	struct kref ref;
12162306a36Sopenharmony_ci	struct list_head list;
12262306a36Sopenharmony_ci	struct list_head cleanup_list;
12362306a36Sopenharmony_ci	struct work_struct work;
12462306a36Sopenharmony_ci	struct cdx_mcdi_iface *mcdi;
12562306a36Sopenharmony_ci	enum cdx_mcdi_cmd_state state;
12662306a36Sopenharmony_ci	size_t inlen;
12762306a36Sopenharmony_ci	const struct cdx_dword *inbuf;
12862306a36Sopenharmony_ci	bool quiet;
12962306a36Sopenharmony_ci	bool reboot_seen;
13062306a36Sopenharmony_ci	u8 seq;
13162306a36Sopenharmony_ci	unsigned long started;
13262306a36Sopenharmony_ci	unsigned long cookie;
13362306a36Sopenharmony_ci	cdx_mcdi_async_completer *completer;
13462306a36Sopenharmony_ci	unsigned int handle;
13562306a36Sopenharmony_ci	unsigned int cmd;
13662306a36Sopenharmony_ci	int rc;
13762306a36Sopenharmony_ci	size_t outlen;
13862306a36Sopenharmony_ci	struct cdx_dword *outbuf;
13962306a36Sopenharmony_ci	/* followed by inbuf data if necessary */
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * struct cdx_mcdi_iface - MCDI protocol context
14462306a36Sopenharmony_ci * @cdx: The associated NIC
14562306a36Sopenharmony_ci * @iface_lock: Serialise access to this structure
14662306a36Sopenharmony_ci * @outstanding_cleanups: Count of cleanups
14762306a36Sopenharmony_ci * @cmd_list: List of outstanding and running commands
14862306a36Sopenharmony_ci * @workqueue: Workqueue used for delayed processing
14962306a36Sopenharmony_ci * @cmd_complete_wq: Waitqueue for command completion
15062306a36Sopenharmony_ci * @db_held_by: Command the MC doorbell is in use by
15162306a36Sopenharmony_ci * @seq_held_by: Command each sequence number is in use by
15262306a36Sopenharmony_ci * @prev_handle: The last used command handle
15362306a36Sopenharmony_ci * @mode: Poll for mcdi completion, or wait for an mcdi_event
15462306a36Sopenharmony_ci * @prev_seq: The last used sequence number
15562306a36Sopenharmony_ci * @new_epoch: Indicates start of day or start of MC reboot recovery
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistruct cdx_mcdi_iface {
15862306a36Sopenharmony_ci	struct cdx_mcdi *cdx;
15962306a36Sopenharmony_ci	/* Serialise access */
16062306a36Sopenharmony_ci	struct mutex iface_lock;
16162306a36Sopenharmony_ci	unsigned int outstanding_cleanups;
16262306a36Sopenharmony_ci	struct list_head cmd_list;
16362306a36Sopenharmony_ci	struct workqueue_struct *workqueue;
16462306a36Sopenharmony_ci	wait_queue_head_t cmd_complete_wq;
16562306a36Sopenharmony_ci	struct cdx_mcdi_cmd *db_held_by;
16662306a36Sopenharmony_ci	struct cdx_mcdi_cmd *seq_held_by[16];
16762306a36Sopenharmony_ci	unsigned int prev_handle;
16862306a36Sopenharmony_ci	enum cdx_mcdi_mode mode;
16962306a36Sopenharmony_ci	u8 prev_seq;
17062306a36Sopenharmony_ci	bool new_epoch;
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/**
17462306a36Sopenharmony_ci * struct cdx_mcdi_data - extra state for NICs that implement MCDI
17562306a36Sopenharmony_ci * @iface: Interface/protocol state
17662306a36Sopenharmony_ci * @fn_flags: Flags for this function, as returned by %MC_CMD_DRV_ATTACH.
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_cistruct cdx_mcdi_data {
17962306a36Sopenharmony_ci	struct cdx_mcdi_iface iface;
18062306a36Sopenharmony_ci	u32 fn_flags;
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline struct cdx_mcdi_iface *cdx_mcdi_if(struct cdx_mcdi *cdx)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return cdx->mcdi ? &cdx->mcdi->iface : NULL;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciint cdx_mcdi_init(struct cdx_mcdi *cdx);
18962306a36Sopenharmony_civoid cdx_mcdi_finish(struct cdx_mcdi *cdx);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len);
19262306a36Sopenharmony_ciint cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
19362306a36Sopenharmony_ci		 const struct cdx_dword *inbuf, size_t inlen,
19462306a36Sopenharmony_ci		 struct cdx_dword *outbuf, size_t outlen, size_t *outlen_actual);
19562306a36Sopenharmony_ciint cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
19662306a36Sopenharmony_ci		       const struct cdx_dword *inbuf, size_t inlen,
19762306a36Sopenharmony_ci		       cdx_mcdi_async_completer *complete,
19862306a36Sopenharmony_ci		       unsigned long cookie);
19962306a36Sopenharmony_ciint cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
20062306a36Sopenharmony_ci				 unsigned int timeout_jiffies);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * We expect that 16- and 32-bit fields in MCDI requests and responses
20462306a36Sopenharmony_ci * are appropriately aligned, but 64-bit fields are only
20562306a36Sopenharmony_ci * 32-bit-aligned.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_ci#define MCDI_DECLARE_BUF(_name, _len) struct cdx_dword _name[DIV_ROUND_UP(_len, 4)] = {{0}}
20862306a36Sopenharmony_ci#define _MCDI_PTR(_buf, _offset)					\
20962306a36Sopenharmony_ci	((u8 *)(_buf) + (_offset))
21062306a36Sopenharmony_ci#define MCDI_PTR(_buf, _field)						\
21162306a36Sopenharmony_ci	_MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
21262306a36Sopenharmony_ci#define _MCDI_CHECK_ALIGN(_ofst, _align)				\
21362306a36Sopenharmony_ci	((void)BUILD_BUG_ON_ZERO((_ofst) & ((_align) - 1)),		\
21462306a36Sopenharmony_ci	 (_ofst))
21562306a36Sopenharmony_ci#define _MCDI_DWORD(_buf, _field)					\
21662306a36Sopenharmony_ci	((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2))
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci#define MCDI_BYTE(_buf, _field)						\
21962306a36Sopenharmony_ci	((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1),	\
22062306a36Sopenharmony_ci	 *MCDI_PTR(_buf, _field))
22162306a36Sopenharmony_ci#define MCDI_WORD(_buf, _field)						\
22262306a36Sopenharmony_ci	((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2),	\
22362306a36Sopenharmony_ci	 le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field)))
22462306a36Sopenharmony_ci#define MCDI_SET_DWORD(_buf, _field, _value)				\
22562306a36Sopenharmony_ci	CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), CDX_DWORD, _value)
22662306a36Sopenharmony_ci#define MCDI_DWORD(_buf, _field)					\
22762306a36Sopenharmony_ci	CDX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), CDX_DWORD)
22862306a36Sopenharmony_ci#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1)		\
22962306a36Sopenharmony_ci	CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field),		\
23062306a36Sopenharmony_ci			     MC_CMD_ ## _name1, _value1)
23162306a36Sopenharmony_ci#define MCDI_SET_QWORD(_buf, _field, _value)				\
23262306a36Sopenharmony_ci	do {								\
23362306a36Sopenharmony_ci		CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0],	\
23462306a36Sopenharmony_ci				     CDX_DWORD, (u32)(_value));	\
23562306a36Sopenharmony_ci		CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1],	\
23662306a36Sopenharmony_ci				     CDX_DWORD, (u64)(_value) >> 32);	\
23762306a36Sopenharmony_ci	} while (0)
23862306a36Sopenharmony_ci#define MCDI_QWORD(_buf, _field)					\
23962306a36Sopenharmony_ci	(CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], CDX_DWORD) |	\
24062306a36Sopenharmony_ci	(u64)CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], CDX_DWORD) << 32)
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#endif /* CDX_MCDI_H */
243