1 // SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) 2 /* Copyright(c) 2021 Intel Corporation */ 3 #include <linux/iopoll.h> 4 #include <linux/mutex.h> 5 #include <linux/types.h> 6 #include "adf_accel_devices.h" 7 #include "adf_common_drv.h" 8 #include "adf_gen4_pfvf.h" 9 #include "adf_pfvf_pf_proto.h" 10 #include "adf_pfvf_utils.h" 11 12 #define ADF_4XXX_PF2VM_OFFSET(i) (0x40B010 + ((i) * 0x20)) 13 #define ADF_4XXX_VM2PF_OFFSET(i) (0x40B014 + ((i) * 0x20)) 14 15 /* VF2PF interrupt source registers */ 16 #define ADF_4XXX_VM2PF_SOU 0x41A180 17 #define ADF_4XXX_VM2PF_MSK 0x41A1C0 18 #define ADF_GEN4_VF_MSK 0xFFFF 19 20 #define ADF_PFVF_GEN4_MSGTYPE_SHIFT 2 21 #define ADF_PFVF_GEN4_MSGTYPE_MASK 0x3F 22 #define ADF_PFVF_GEN4_MSGDATA_SHIFT 8 23 #define ADF_PFVF_GEN4_MSGDATA_MASK 0xFFFFFF 24 25 static const struct pfvf_csr_format csr_gen4_fmt = { 26 { ADF_PFVF_GEN4_MSGTYPE_SHIFT, ADF_PFVF_GEN4_MSGTYPE_MASK }, 27 { ADF_PFVF_GEN4_MSGDATA_SHIFT, ADF_PFVF_GEN4_MSGDATA_MASK }, 28 }; 29 30 static u32 adf_gen4_pf_get_pf2vf_offset(u32 i) 31 { 32 return ADF_4XXX_PF2VM_OFFSET(i); 33 } 34 35 static u32 adf_gen4_pf_get_vf2pf_offset(u32 i) 36 { 37 return ADF_4XXX_VM2PF_OFFSET(i); 38 } 39 40 static void adf_gen4_enable_vf2pf_interrupts(void __iomem *pmisc_addr, u32 vf_mask) 41 { 42 u32 val; 43 44 val = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_MSK) & ~vf_mask; 45 ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, val); 46 } 47 48 static void adf_gen4_disable_all_vf2pf_interrupts(void __iomem *pmisc_addr) 49 { 50 ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, ADF_GEN4_VF_MSK); 51 } 52 53 static u32 adf_gen4_disable_pending_vf2pf_interrupts(void __iomem *pmisc_addr) 54 { 55 u32 sources, disabled, pending; 56 57 /* Get the interrupt sources triggered by VFs */ 58 sources = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_SOU); 59 if (!sources) 60 return 0; 61 62 /* Get the already disabled interrupts */ 63 disabled = ADF_CSR_RD(pmisc_addr, ADF_4XXX_VM2PF_MSK); 64 65 pending = sources & ~disabled; 66 if (!pending) 67 return 0; 68 69 /* Due to HW limitations, when disabling the interrupts, we can't 70 * just disable the requested sources, as this would lead to missed 71 * interrupts if VM2PF_SOU changes just before writing to VM2PF_MSK. 72 * To work around it, disable all and re-enable only the sources that 73 * are not in vf_mask and were not already disabled. Re-enabling will 74 * trigger a new interrupt for the sources that have changed in the 75 * meantime, if any. 76 */ 77 ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, ADF_GEN4_VF_MSK); 78 ADF_CSR_WR(pmisc_addr, ADF_4XXX_VM2PF_MSK, disabled | sources); 79 80 /* Return the sources of the (new) interrupt(s) */ 81 return pending; 82 } 83 84 static int adf_gen4_pfvf_send(struct adf_accel_dev *accel_dev, 85 struct pfvf_message msg, u32 pfvf_offset, 86 struct mutex *csr_lock) 87 { 88 void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); 89 u32 csr_val; 90 int ret; 91 92 csr_val = adf_pfvf_csr_msg_of(accel_dev, msg, &csr_gen4_fmt); 93 if (unlikely(!csr_val)) 94 return -EINVAL; 95 96 mutex_lock(csr_lock); 97 98 ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val | ADF_PFVF_INT); 99 100 /* Wait for confirmation from remote that it received the message */ 101 ret = read_poll_timeout(ADF_CSR_RD, csr_val, !(csr_val & ADF_PFVF_INT), 102 ADF_PFVF_MSG_ACK_DELAY_US, 103 ADF_PFVF_MSG_ACK_MAX_DELAY_US, 104 true, pmisc_addr, pfvf_offset); 105 if (ret < 0) 106 dev_dbg(&GET_DEV(accel_dev), "ACK not received from remote\n"); 107 108 mutex_unlock(csr_lock); 109 return ret; 110 } 111 112 static struct pfvf_message adf_gen4_pfvf_recv(struct adf_accel_dev *accel_dev, 113 u32 pfvf_offset, u8 compat_ver) 114 { 115 void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); 116 struct pfvf_message msg = { 0 }; 117 u32 csr_val; 118 119 /* Read message from the CSR */ 120 csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset); 121 if (!(csr_val & ADF_PFVF_INT)) { 122 dev_info(&GET_DEV(accel_dev), 123 "Spurious PFVF interrupt, msg 0x%.8x. Ignored\n", csr_val); 124 return msg; 125 } 126 127 /* We can now acknowledge the message reception by clearing the 128 * interrupt bit 129 */ 130 ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val & ~ADF_PFVF_INT); 131 132 /* Return the pfvf_message format */ 133 return adf_pfvf_message_of(accel_dev, csr_val, &csr_gen4_fmt); 134 } 135 136 void adf_gen4_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops) 137 { 138 pfvf_ops->enable_comms = adf_enable_pf2vf_comms; 139 pfvf_ops->get_pf2vf_offset = adf_gen4_pf_get_pf2vf_offset; 140 pfvf_ops->get_vf2pf_offset = adf_gen4_pf_get_vf2pf_offset; 141 pfvf_ops->enable_vf2pf_interrupts = adf_gen4_enable_vf2pf_interrupts; 142 pfvf_ops->disable_all_vf2pf_interrupts = adf_gen4_disable_all_vf2pf_interrupts; 143 pfvf_ops->disable_pending_vf2pf_interrupts = adf_gen4_disable_pending_vf2pf_interrupts; 144 pfvf_ops->send_msg = adf_gen4_pfvf_send; 145 pfvf_ops->recv_msg = adf_gen4_pfvf_recv; 146 } 147 EXPORT_SYMBOL_GPL(adf_gen4_init_pf_pfvf_ops); 148