1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/bitmap.h> |
3 | #include <linux/workqueue.h> |
4 | |
5 | #include "nitrox_csr.h" |
6 | #include "nitrox_hal.h" |
7 | #include "nitrox_dev.h" |
8 | #include "nitrox_mbx.h" |
9 | |
10 | #define RING_TO_VFNO(_x, _y) ((_x) / (_y)) |
11 | |
12 | /* |
13 | * mbx_msg_type - Mailbox message types |
14 | */ |
15 | enum mbx_msg_type { |
16 | MBX_MSG_TYPE_NOP, |
17 | MBX_MSG_TYPE_REQ, |
18 | MBX_MSG_TYPE_ACK, |
19 | MBX_MSG_TYPE_NACK, |
20 | }; |
21 | |
22 | /* |
23 | * mbx_msg_opcode - Mailbox message opcodes |
24 | */ |
25 | enum mbx_msg_opcode { |
26 | MSG_OP_VF_MODE = 1, |
27 | MSG_OP_VF_UP, |
28 | MSG_OP_VF_DOWN, |
29 | MSG_OP_CHIPID_VFID, |
30 | MSG_OP_MCODE_INFO = 11, |
31 | }; |
32 | |
33 | struct pf2vf_work { |
34 | struct nitrox_vfdev *vfdev; |
35 | struct nitrox_device *ndev; |
36 | struct work_struct pf2vf_resp; |
37 | }; |
38 | |
39 | static inline u64 pf2vf_read_mbox(struct nitrox_device *ndev, int ring) |
40 | { |
41 | u64 reg_addr; |
42 | |
43 | reg_addr = NPS_PKT_MBOX_VF_PF_PFDATAX(ring); |
44 | return nitrox_read_csr(ndev, offset: reg_addr); |
45 | } |
46 | |
47 | static inline void pf2vf_write_mbox(struct nitrox_device *ndev, u64 value, |
48 | int ring) |
49 | { |
50 | u64 reg_addr; |
51 | |
52 | reg_addr = NPS_PKT_MBOX_PF_VF_PFDATAX(ring); |
53 | nitrox_write_csr(ndev, offset: reg_addr, value); |
54 | } |
55 | |
56 | static void pf2vf_send_response(struct nitrox_device *ndev, |
57 | struct nitrox_vfdev *vfdev) |
58 | { |
59 | union mbox_msg msg; |
60 | |
61 | msg.value = vfdev->msg.value; |
62 | |
63 | switch (vfdev->msg.opcode) { |
64 | case MSG_OP_VF_MODE: |
65 | msg.data = ndev->mode; |
66 | break; |
67 | case MSG_OP_VF_UP: |
68 | vfdev->nr_queues = vfdev->msg.data; |
69 | atomic_set(v: &vfdev->state, i: __NDEV_READY); |
70 | break; |
71 | case MSG_OP_CHIPID_VFID: |
72 | msg.id.chipid = ndev->idx; |
73 | msg.id.vfid = vfdev->vfno; |
74 | break; |
75 | case MSG_OP_VF_DOWN: |
76 | vfdev->nr_queues = 0; |
77 | atomic_set(v: &vfdev->state, i: __NDEV_NOT_READY); |
78 | break; |
79 | case MSG_OP_MCODE_INFO: |
80 | msg.data = 0; |
81 | msg.mcode_info.count = 2; |
82 | msg.mcode_info.info = MCODE_TYPE_SE_SSL | (MCODE_TYPE_AE << 5); |
83 | msg.mcode_info.next_se_grp = 1; |
84 | msg.mcode_info.next_ae_grp = 1; |
85 | break; |
86 | default: |
87 | msg.type = MBX_MSG_TYPE_NOP; |
88 | break; |
89 | } |
90 | |
91 | if (msg.type == MBX_MSG_TYPE_NOP) |
92 | return; |
93 | |
94 | /* send ACK to VF */ |
95 | msg.type = MBX_MSG_TYPE_ACK; |
96 | pf2vf_write_mbox(ndev, value: msg.value, ring: vfdev->ring); |
97 | |
98 | vfdev->msg.value = 0; |
99 | atomic64_inc(v: &vfdev->mbx_resp); |
100 | } |
101 | |
102 | static void pf2vf_resp_handler(struct work_struct *work) |
103 | { |
104 | struct pf2vf_work *pf2vf_resp = container_of(work, struct pf2vf_work, |
105 | pf2vf_resp); |
106 | struct nitrox_vfdev *vfdev = pf2vf_resp->vfdev; |
107 | struct nitrox_device *ndev = pf2vf_resp->ndev; |
108 | |
109 | switch (vfdev->msg.type) { |
110 | case MBX_MSG_TYPE_REQ: |
111 | /* process the request from VF */ |
112 | pf2vf_send_response(ndev, vfdev); |
113 | break; |
114 | case MBX_MSG_TYPE_ACK: |
115 | case MBX_MSG_TYPE_NACK: |
116 | break; |
117 | } |
118 | |
119 | kfree(objp: pf2vf_resp); |
120 | } |
121 | |
122 | void nitrox_pf2vf_mbox_handler(struct nitrox_device *ndev) |
123 | { |
124 | DECLARE_BITMAP(csr, BITS_PER_TYPE(u64)); |
125 | struct nitrox_vfdev *vfdev; |
126 | struct pf2vf_work *pfwork; |
127 | u64 value, reg_addr; |
128 | u32 i; |
129 | int vfno; |
130 | |
131 | /* loop for VF(0..63) */ |
132 | reg_addr = NPS_PKT_MBOX_INT_LO; |
133 | value = nitrox_read_csr(ndev, offset: reg_addr); |
134 | bitmap_from_u64(dst: csr, mask: value); |
135 | for_each_set_bit(i, csr, BITS_PER_TYPE(csr)) { |
136 | /* get the vfno from ring */ |
137 | vfno = RING_TO_VFNO(i, ndev->iov.max_vf_queues); |
138 | vfdev = ndev->iov.vfdev + vfno; |
139 | vfdev->ring = i; |
140 | /* fill the vf mailbox data */ |
141 | vfdev->msg.value = pf2vf_read_mbox(ndev, ring: vfdev->ring); |
142 | pfwork = kzalloc(size: sizeof(*pfwork), GFP_ATOMIC); |
143 | if (!pfwork) |
144 | continue; |
145 | |
146 | pfwork->vfdev = vfdev; |
147 | pfwork->ndev = ndev; |
148 | INIT_WORK(&pfwork->pf2vf_resp, pf2vf_resp_handler); |
149 | queue_work(wq: ndev->iov.pf2vf_wq, work: &pfwork->pf2vf_resp); |
150 | /* clear the corresponding vf bit */ |
151 | nitrox_write_csr(ndev, offset: reg_addr, BIT_ULL(i)); |
152 | } |
153 | |
154 | /* loop for VF(64..127) */ |
155 | reg_addr = NPS_PKT_MBOX_INT_HI; |
156 | value = nitrox_read_csr(ndev, offset: reg_addr); |
157 | bitmap_from_u64(dst: csr, mask: value); |
158 | for_each_set_bit(i, csr, BITS_PER_TYPE(csr)) { |
159 | /* get the vfno from ring */ |
160 | vfno = RING_TO_VFNO(i + 64, ndev->iov.max_vf_queues); |
161 | vfdev = ndev->iov.vfdev + vfno; |
162 | vfdev->ring = (i + 64); |
163 | /* fill the vf mailbox data */ |
164 | vfdev->msg.value = pf2vf_read_mbox(ndev, ring: vfdev->ring); |
165 | |
166 | pfwork = kzalloc(size: sizeof(*pfwork), GFP_ATOMIC); |
167 | if (!pfwork) |
168 | continue; |
169 | |
170 | pfwork->vfdev = vfdev; |
171 | pfwork->ndev = ndev; |
172 | INIT_WORK(&pfwork->pf2vf_resp, pf2vf_resp_handler); |
173 | queue_work(wq: ndev->iov.pf2vf_wq, work: &pfwork->pf2vf_resp); |
174 | /* clear the corresponding vf bit */ |
175 | nitrox_write_csr(ndev, offset: reg_addr, BIT_ULL(i)); |
176 | } |
177 | } |
178 | |
179 | int nitrox_mbox_init(struct nitrox_device *ndev) |
180 | { |
181 | struct nitrox_vfdev *vfdev; |
182 | int i; |
183 | |
184 | ndev->iov.vfdev = kcalloc(n: ndev->iov.num_vfs, |
185 | size: sizeof(struct nitrox_vfdev), GFP_KERNEL); |
186 | if (!ndev->iov.vfdev) |
187 | return -ENOMEM; |
188 | |
189 | for (i = 0; i < ndev->iov.num_vfs; i++) { |
190 | vfdev = ndev->iov.vfdev + i; |
191 | vfdev->vfno = i; |
192 | } |
193 | |
194 | /* allocate pf2vf response workqueue */ |
195 | ndev->iov.pf2vf_wq = alloc_workqueue(fmt: "nitrox_pf2vf" , flags: 0, max_active: 0); |
196 | if (!ndev->iov.pf2vf_wq) { |
197 | kfree(objp: ndev->iov.vfdev); |
198 | ndev->iov.vfdev = NULL; |
199 | return -ENOMEM; |
200 | } |
201 | /* enable pf2vf mailbox interrupts */ |
202 | enable_pf2vf_mbox_interrupts(ndev); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | void nitrox_mbox_cleanup(struct nitrox_device *ndev) |
208 | { |
209 | /* disable pf2vf mailbox interrupts */ |
210 | disable_pf2vf_mbox_interrupts(ndev); |
211 | /* destroy workqueue */ |
212 | if (ndev->iov.pf2vf_wq) |
213 | destroy_workqueue(wq: ndev->iov.pf2vf_wq); |
214 | |
215 | kfree(objp: ndev->iov.vfdev); |
216 | ndev->iov.pf2vf_wq = NULL; |
217 | ndev->iov.vfdev = NULL; |
218 | } |
219 | |