1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2016 Cavium, Inc. |
4 | */ |
5 | |
6 | #include "cptvf.h" |
7 | |
8 | static void cptvf_send_msg_to_pf(struct cpt_vf *cptvf, struct cpt_mbox *mbx) |
9 | { |
10 | /* Writing mbox(1) causes interrupt */ |
11 | cpt_write_csr64(hw_addr: cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 0), |
12 | val: mbx->msg); |
13 | cpt_write_csr64(hw_addr: cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 1), |
14 | val: mbx->data); |
15 | } |
16 | |
17 | /* Interrupt handler to handle mailbox messages from VFs */ |
18 | void cptvf_handle_mbox_intr(struct cpt_vf *cptvf) |
19 | { |
20 | struct cpt_mbox mbx = {}; |
21 | |
22 | /* |
23 | * MBOX[0] contains msg |
24 | * MBOX[1] contains data |
25 | */ |
26 | mbx.msg = cpt_read_csr64(hw_addr: cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 0)); |
27 | mbx.data = cpt_read_csr64(hw_addr: cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 1)); |
28 | dev_dbg(&cptvf->pdev->dev, "%s: Mailbox msg 0x%llx from PF\n" , |
29 | __func__, mbx.msg); |
30 | switch (mbx.msg) { |
31 | case CPT_MSG_READY: |
32 | { |
33 | cptvf->pf_acked = true; |
34 | cptvf->vfid = mbx.data; |
35 | dev_dbg(&cptvf->pdev->dev, "Received VFID %d\n" , cptvf->vfid); |
36 | break; |
37 | } |
38 | case CPT_MSG_QBIND_GRP: |
39 | cptvf->pf_acked = true; |
40 | cptvf->vftype = mbx.data; |
41 | dev_dbg(&cptvf->pdev->dev, "VF %d type %s group %d\n" , |
42 | cptvf->vfid, ((mbx.data == SE_TYPES) ? "SE" : "AE" ), |
43 | cptvf->vfgrp); |
44 | break; |
45 | case CPT_MBOX_MSG_TYPE_ACK: |
46 | cptvf->pf_acked = true; |
47 | break; |
48 | case CPT_MBOX_MSG_TYPE_NACK: |
49 | cptvf->pf_nacked = true; |
50 | break; |
51 | default: |
52 | dev_err(&cptvf->pdev->dev, "Invalid msg from PF, msg 0x%llx\n" , |
53 | mbx.msg); |
54 | break; |
55 | } |
56 | } |
57 | |
58 | static int cptvf_send_msg_to_pf_timeout(struct cpt_vf *cptvf, |
59 | struct cpt_mbox *mbx) |
60 | { |
61 | int timeout = CPT_MBOX_MSG_TIMEOUT; |
62 | int sleep = 10; |
63 | |
64 | cptvf->pf_acked = false; |
65 | cptvf->pf_nacked = false; |
66 | cptvf_send_msg_to_pf(cptvf, mbx); |
67 | /* Wait for previous message to be acked, timeout 2sec */ |
68 | while (!cptvf->pf_acked) { |
69 | if (cptvf->pf_nacked) |
70 | return -EINVAL; |
71 | msleep(msecs: sleep); |
72 | if (cptvf->pf_acked) |
73 | break; |
74 | timeout -= sleep; |
75 | if (!timeout) { |
76 | dev_err(&cptvf->pdev->dev, "PF didn't ack to mbox msg %llx from VF%u\n" , |
77 | (mbx->msg & 0xFF), cptvf->vfid); |
78 | return -EBUSY; |
79 | } |
80 | } |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | /* |
86 | * Checks if VF is able to comminicate with PF |
87 | * and also gets the CPT number this VF is associated to. |
88 | */ |
89 | int cptvf_check_pf_ready(struct cpt_vf *cptvf) |
90 | { |
91 | struct pci_dev *pdev = cptvf->pdev; |
92 | struct cpt_mbox mbx = {}; |
93 | |
94 | mbx.msg = CPT_MSG_READY; |
95 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
96 | dev_err(&pdev->dev, "PF didn't respond to READY msg\n" ); |
97 | return -EBUSY; |
98 | } |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | /* |
104 | * Communicate VQs size to PF to program CPT(0)_PF_Q(0-15)_CTL of the VF. |
105 | * Must be ACKed. |
106 | */ |
107 | int cptvf_send_vq_size_msg(struct cpt_vf *cptvf) |
108 | { |
109 | struct pci_dev *pdev = cptvf->pdev; |
110 | struct cpt_mbox mbx = {}; |
111 | |
112 | mbx.msg = CPT_MSG_QLEN; |
113 | mbx.data = cptvf->qsize; |
114 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
115 | dev_err(&pdev->dev, "PF didn't respond to vq_size msg\n" ); |
116 | return -EBUSY; |
117 | } |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | /* |
123 | * Communicate VF group required to PF and get the VQ binded to that group |
124 | */ |
125 | int cptvf_send_vf_to_grp_msg(struct cpt_vf *cptvf) |
126 | { |
127 | struct pci_dev *pdev = cptvf->pdev; |
128 | struct cpt_mbox mbx = {}; |
129 | |
130 | mbx.msg = CPT_MSG_QBIND_GRP; |
131 | /* Convey group of the VF */ |
132 | mbx.data = cptvf->vfgrp; |
133 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
134 | dev_err(&pdev->dev, "PF didn't respond to vf_type msg\n" ); |
135 | return -EBUSY; |
136 | } |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | /* |
142 | * Communicate VF group required to PF and get the VQ binded to that group |
143 | */ |
144 | int cptvf_send_vf_priority_msg(struct cpt_vf *cptvf) |
145 | { |
146 | struct pci_dev *pdev = cptvf->pdev; |
147 | struct cpt_mbox mbx = {}; |
148 | |
149 | mbx.msg = CPT_MSG_VQ_PRIORITY; |
150 | /* Convey group of the VF */ |
151 | mbx.data = cptvf->priority; |
152 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
153 | dev_err(&pdev->dev, "PF didn't respond to vf_type msg\n" ); |
154 | return -EBUSY; |
155 | } |
156 | return 0; |
157 | } |
158 | |
159 | /* |
160 | * Communicate to PF that VF is UP and running |
161 | */ |
162 | int cptvf_send_vf_up(struct cpt_vf *cptvf) |
163 | { |
164 | struct pci_dev *pdev = cptvf->pdev; |
165 | struct cpt_mbox mbx = {}; |
166 | |
167 | mbx.msg = CPT_MSG_VF_UP; |
168 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
169 | dev_err(&pdev->dev, "PF didn't respond to UP msg\n" ); |
170 | return -EBUSY; |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | /* |
177 | * Communicate to PF that VF is DOWN and running |
178 | */ |
179 | int cptvf_send_vf_down(struct cpt_vf *cptvf) |
180 | { |
181 | struct pci_dev *pdev = cptvf->pdev; |
182 | struct cpt_mbox mbx = {}; |
183 | |
184 | mbx.msg = CPT_MSG_VF_DOWN; |
185 | if (cptvf_send_msg_to_pf_timeout(cptvf, mbx: &mbx)) { |
186 | dev_err(&pdev->dev, "PF didn't respond to DOWN msg\n" ); |
187 | return -EBUSY; |
188 | } |
189 | |
190 | return 0; |
191 | } |
192 | |