162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* Copyright (c) 2021, Microsoft Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci#include <linux/device.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/mm.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <net/mana/shm_channel.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define PAGE_FRAME_L48_WIDTH_BYTES 6 1262306a36Sopenharmony_ci#define PAGE_FRAME_L48_WIDTH_BITS (PAGE_FRAME_L48_WIDTH_BYTES * 8) 1362306a36Sopenharmony_ci#define PAGE_FRAME_L48_MASK 0x0000FFFFFFFFFFFF 1462306a36Sopenharmony_ci#define PAGE_FRAME_H4_WIDTH_BITS 4 1562306a36Sopenharmony_ci#define VECTOR_MASK 0xFFFF 1662306a36Sopenharmony_ci#define SHMEM_VF_RESET_STATE ((u32)-1) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define SMC_MSG_TYPE_ESTABLISH_HWC 1 1962306a36Sopenharmony_ci#define SMC_MSG_TYPE_ESTABLISH_HWC_VERSION 0 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define SMC_MSG_TYPE_DESTROY_HWC 2 2262306a36Sopenharmony_ci#define SMC_MSG_TYPE_DESTROY_HWC_VERSION 0 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SMC_MSG_DIRECTION_REQUEST 0 2562306a36Sopenharmony_ci#define SMC_MSG_DIRECTION_RESPONSE 1 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Structures labeled with "HW DATA" are exchanged with the hardware. All of 2862306a36Sopenharmony_ci * them are naturally aligned and hence don't need __packed. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Shared memory channel protocol header 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * msg_type: set on request and response; response matches request. 3462306a36Sopenharmony_ci * msg_version: newer PF writes back older response (matching request) 3562306a36Sopenharmony_ci * older PF acts on latest version known and sets that version in result 3662306a36Sopenharmony_ci * (less than request). 3762306a36Sopenharmony_ci * direction: 0 for request, VF->PF; 1 for response, PF->VF. 3862306a36Sopenharmony_ci * status: 0 on request, 3962306a36Sopenharmony_ci * operation result on response (success = 0, failure = 1 or greater). 4062306a36Sopenharmony_ci * reset_vf: If set on either establish or destroy request, indicates perform 4162306a36Sopenharmony_ci * FLR before/after the operation. 4262306a36Sopenharmony_ci * owner_is_pf: 1 indicates PF owned, 0 indicates VF owned. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ciunion smc_proto_hdr { 4562306a36Sopenharmony_ci u32 as_uint32; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci struct { 4862306a36Sopenharmony_ci u8 msg_type : 3; 4962306a36Sopenharmony_ci u8 msg_version : 3; 5062306a36Sopenharmony_ci u8 reserved_1 : 1; 5162306a36Sopenharmony_ci u8 direction : 1; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci u8 status; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci u8 reserved_2; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci u8 reset_vf : 1; 5862306a36Sopenharmony_ci u8 reserved_3 : 6; 5962306a36Sopenharmony_ci u8 owner_is_pf : 1; 6062306a36Sopenharmony_ci }; 6162306a36Sopenharmony_ci}; /* HW DATA */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define SMC_APERTURE_BITS 256 6462306a36Sopenharmony_ci#define SMC_BASIC_UNIT (sizeof(u32)) 6562306a36Sopenharmony_ci#define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8)) 6662306a36Sopenharmony_ci#define SMC_LAST_DWORD (SMC_APERTURE_DWORDS - 1) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int mana_smc_poll_register(void __iomem *base, bool reset) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci void __iomem *ptr = base + SMC_LAST_DWORD * SMC_BASIC_UNIT; 7162306a36Sopenharmony_ci u32 last_dword; 7262306a36Sopenharmony_ci int i; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Poll the hardware for the ownership bit. This should be pretty fast, 7562306a36Sopenharmony_ci * but let's do it in a loop just in case the hardware or the PF 7662306a36Sopenharmony_ci * driver are temporarily busy. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci for (i = 0; i < 20 * 1000; i++) { 7962306a36Sopenharmony_ci last_dword = readl(ptr); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* shmem reads as 0xFFFFFFFF in the reset case */ 8262306a36Sopenharmony_ci if (reset && last_dword == SHMEM_VF_RESET_STATE) 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* If bit_31 is set, the PF currently owns the SMC. */ 8662306a36Sopenharmony_ci if (!(last_dword & BIT(31))) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci usleep_range(1000, 2000); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return -ETIMEDOUT; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int mana_smc_read_response(struct shm_channel *sc, u32 msg_type, 9662306a36Sopenharmony_ci u32 msg_version, bool reset_vf) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci void __iomem *base = sc->base; 9962306a36Sopenharmony_ci union smc_proto_hdr hdr; 10062306a36Sopenharmony_ci int err; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Wait for PF to respond. */ 10362306a36Sopenharmony_ci err = mana_smc_poll_register(base, reset_vf); 10462306a36Sopenharmony_ci if (err) 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci hdr.as_uint32 = readl(base + SMC_LAST_DWORD * SMC_BASIC_UNIT); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (reset_vf && hdr.as_uint32 == SHMEM_VF_RESET_STATE) 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Validate protocol fields from the PF driver */ 11362306a36Sopenharmony_ci if (hdr.msg_type != msg_type || hdr.msg_version > msg_version || 11462306a36Sopenharmony_ci hdr.direction != SMC_MSG_DIRECTION_RESPONSE) { 11562306a36Sopenharmony_ci dev_err(sc->dev, "Wrong SMC response 0x%x, type=%d, ver=%d\n", 11662306a36Sopenharmony_ci hdr.as_uint32, msg_type, msg_version); 11762306a36Sopenharmony_ci return -EPROTO; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Validate the operation result */ 12162306a36Sopenharmony_ci if (hdr.status != 0) { 12262306a36Sopenharmony_ci dev_err(sc->dev, "SMC operation failed: 0x%x\n", hdr.status); 12362306a36Sopenharmony_ci return -EPROTO; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_civoid mana_smc_init(struct shm_channel *sc, struct device *dev, 13062306a36Sopenharmony_ci void __iomem *base) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci sc->dev = dev; 13362306a36Sopenharmony_ci sc->base = base; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint mana_smc_setup_hwc(struct shm_channel *sc, bool reset_vf, u64 eq_addr, 13762306a36Sopenharmony_ci u64 cq_addr, u64 rq_addr, u64 sq_addr, 13862306a36Sopenharmony_ci u32 eq_msix_index) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci union smc_proto_hdr *hdr; 14162306a36Sopenharmony_ci u16 all_addr_h4bits = 0; 14262306a36Sopenharmony_ci u16 frame_addr_seq = 0; 14362306a36Sopenharmony_ci u64 frame_addr = 0; 14462306a36Sopenharmony_ci u8 shm_buf[32]; 14562306a36Sopenharmony_ci u64 *shmem; 14662306a36Sopenharmony_ci u32 *dword; 14762306a36Sopenharmony_ci u8 *ptr; 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci int i; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Ensure VF already has possession of shared memory */ 15262306a36Sopenharmony_ci err = mana_smc_poll_register(sc->base, false); 15362306a36Sopenharmony_ci if (err) { 15462306a36Sopenharmony_ci dev_err(sc->dev, "Timeout when setting up HWC: %d\n", err); 15562306a36Sopenharmony_ci return err; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!PAGE_ALIGNED(eq_addr) || !PAGE_ALIGNED(cq_addr) || 15962306a36Sopenharmony_ci !PAGE_ALIGNED(rq_addr) || !PAGE_ALIGNED(sq_addr)) 16062306a36Sopenharmony_ci return -EINVAL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if ((eq_msix_index & VECTOR_MASK) != eq_msix_index) 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Scheme for packing four addresses and extra info into 256 bits. 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Addresses must be page frame aligned, so only frame address bits 16862306a36Sopenharmony_ci * are transferred. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * 52-bit frame addresses are split into the lower 48 bits and upper 17162306a36Sopenharmony_ci * 4 bits. Lower 48 bits of 4 address are written sequentially from 17262306a36Sopenharmony_ci * the start of the 256-bit shared memory region followed by 16 bits 17362306a36Sopenharmony_ci * containing the upper 4 bits of the 4 addresses in sequence. 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * A 16 bit EQ vector number fills out the next-to-last 32-bit dword. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * The final 32-bit dword is used for protocol control information as 17862306a36Sopenharmony_ci * defined in smc_proto_hdr. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci memset(shm_buf, 0, sizeof(shm_buf)); 18262306a36Sopenharmony_ci ptr = shm_buf; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* EQ addr: low 48 bits of frame address */ 18562306a36Sopenharmony_ci shmem = (u64 *)ptr; 18662306a36Sopenharmony_ci frame_addr = PHYS_PFN(eq_addr); 18762306a36Sopenharmony_ci *shmem = frame_addr & PAGE_FRAME_L48_MASK; 18862306a36Sopenharmony_ci all_addr_h4bits |= (frame_addr >> PAGE_FRAME_L48_WIDTH_BITS) << 18962306a36Sopenharmony_ci (frame_addr_seq++ * PAGE_FRAME_H4_WIDTH_BITS); 19062306a36Sopenharmony_ci ptr += PAGE_FRAME_L48_WIDTH_BYTES; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* CQ addr: low 48 bits of frame address */ 19362306a36Sopenharmony_ci shmem = (u64 *)ptr; 19462306a36Sopenharmony_ci frame_addr = PHYS_PFN(cq_addr); 19562306a36Sopenharmony_ci *shmem = frame_addr & PAGE_FRAME_L48_MASK; 19662306a36Sopenharmony_ci all_addr_h4bits |= (frame_addr >> PAGE_FRAME_L48_WIDTH_BITS) << 19762306a36Sopenharmony_ci (frame_addr_seq++ * PAGE_FRAME_H4_WIDTH_BITS); 19862306a36Sopenharmony_ci ptr += PAGE_FRAME_L48_WIDTH_BYTES; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* RQ addr: low 48 bits of frame address */ 20162306a36Sopenharmony_ci shmem = (u64 *)ptr; 20262306a36Sopenharmony_ci frame_addr = PHYS_PFN(rq_addr); 20362306a36Sopenharmony_ci *shmem = frame_addr & PAGE_FRAME_L48_MASK; 20462306a36Sopenharmony_ci all_addr_h4bits |= (frame_addr >> PAGE_FRAME_L48_WIDTH_BITS) << 20562306a36Sopenharmony_ci (frame_addr_seq++ * PAGE_FRAME_H4_WIDTH_BITS); 20662306a36Sopenharmony_ci ptr += PAGE_FRAME_L48_WIDTH_BYTES; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* SQ addr: low 48 bits of frame address */ 20962306a36Sopenharmony_ci shmem = (u64 *)ptr; 21062306a36Sopenharmony_ci frame_addr = PHYS_PFN(sq_addr); 21162306a36Sopenharmony_ci *shmem = frame_addr & PAGE_FRAME_L48_MASK; 21262306a36Sopenharmony_ci all_addr_h4bits |= (frame_addr >> PAGE_FRAME_L48_WIDTH_BITS) << 21362306a36Sopenharmony_ci (frame_addr_seq++ * PAGE_FRAME_H4_WIDTH_BITS); 21462306a36Sopenharmony_ci ptr += PAGE_FRAME_L48_WIDTH_BYTES; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* High 4 bits of the four frame addresses */ 21762306a36Sopenharmony_ci *((u16 *)ptr) = all_addr_h4bits; 21862306a36Sopenharmony_ci ptr += sizeof(u16); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* EQ MSIX vector number */ 22162306a36Sopenharmony_ci *((u16 *)ptr) = (u16)eq_msix_index; 22262306a36Sopenharmony_ci ptr += sizeof(u16); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 32-bit protocol header in final dword */ 22562306a36Sopenharmony_ci *((u32 *)ptr) = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci hdr = (union smc_proto_hdr *)ptr; 22862306a36Sopenharmony_ci hdr->msg_type = SMC_MSG_TYPE_ESTABLISH_HWC; 22962306a36Sopenharmony_ci hdr->msg_version = SMC_MSG_TYPE_ESTABLISH_HWC_VERSION; 23062306a36Sopenharmony_ci hdr->direction = SMC_MSG_DIRECTION_REQUEST; 23162306a36Sopenharmony_ci hdr->reset_vf = reset_vf; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Write 256-message buffer to shared memory (final 32-bit write 23462306a36Sopenharmony_ci * triggers HW to set possession bit to PF). 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci dword = (u32 *)shm_buf; 23762306a36Sopenharmony_ci for (i = 0; i < SMC_APERTURE_DWORDS; i++) 23862306a36Sopenharmony_ci writel(*dword++, sc->base + i * SMC_BASIC_UNIT); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Read shmem response (polling for VF possession) and validate. 24162306a36Sopenharmony_ci * For setup, waiting for response on shared memory is not strictly 24262306a36Sopenharmony_ci * necessary, since wait occurs later for results to appear in EQE's. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci err = mana_smc_read_response(sc, SMC_MSG_TYPE_ESTABLISH_HWC, 24562306a36Sopenharmony_ci SMC_MSG_TYPE_ESTABLISH_HWC_VERSION, 24662306a36Sopenharmony_ci reset_vf); 24762306a36Sopenharmony_ci if (err) { 24862306a36Sopenharmony_ci dev_err(sc->dev, "Error when setting up HWC: %d\n", err); 24962306a36Sopenharmony_ci return err; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciint mana_smc_teardown_hwc(struct shm_channel *sc, bool reset_vf) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci union smc_proto_hdr hdr = {}; 25862306a36Sopenharmony_ci int err; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Ensure already has possession of shared memory */ 26162306a36Sopenharmony_ci err = mana_smc_poll_register(sc->base, false); 26262306a36Sopenharmony_ci if (err) { 26362306a36Sopenharmony_ci dev_err(sc->dev, "Timeout when tearing down HWC\n"); 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Set up protocol header for HWC destroy message */ 26862306a36Sopenharmony_ci hdr.msg_type = SMC_MSG_TYPE_DESTROY_HWC; 26962306a36Sopenharmony_ci hdr.msg_version = SMC_MSG_TYPE_DESTROY_HWC_VERSION; 27062306a36Sopenharmony_ci hdr.direction = SMC_MSG_DIRECTION_REQUEST; 27162306a36Sopenharmony_ci hdr.reset_vf = reset_vf; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Write message in high 32 bits of 256-bit shared memory, causing HW 27462306a36Sopenharmony_ci * to set possession bit to PF. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci writel(hdr.as_uint32, sc->base + SMC_LAST_DWORD * SMC_BASIC_UNIT); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Read shmem response (polling for VF possession) and validate. 27962306a36Sopenharmony_ci * For teardown, waiting for response is required to ensure hardware 28062306a36Sopenharmony_ci * invalidates MST entries before software frees memory. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci err = mana_smc_read_response(sc, SMC_MSG_TYPE_DESTROY_HWC, 28362306a36Sopenharmony_ci SMC_MSG_TYPE_DESTROY_HWC_VERSION, 28462306a36Sopenharmony_ci reset_vf); 28562306a36Sopenharmony_ci if (err) { 28662306a36Sopenharmony_ci dev_err(sc->dev, "Error when tearing down HWC: %d\n", err); 28762306a36Sopenharmony_ci return err; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 292