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