1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Marvell OcteonTX CPT driver |
3 | * |
4 | * Copyright (C) 2019 Marvell International Ltd. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. |
9 | */ |
10 | |
11 | #include "otx_cpt_common.h" |
12 | #include "otx_cptpf.h" |
13 | |
14 | #define DRV_NAME "octeontx-cpt" |
15 | #define DRV_VERSION "1.0" |
16 | |
17 | static void otx_cpt_disable_mbox_interrupts(struct otx_cpt_device *cpt) |
18 | { |
19 | /* Disable mbox(0) interrupts for all VFs */ |
20 | writeq(val: ~0ull, addr: cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1CX(0)); |
21 | } |
22 | |
23 | static void otx_cpt_enable_mbox_interrupts(struct otx_cpt_device *cpt) |
24 | { |
25 | /* Enable mbox(0) interrupts for all VFs */ |
26 | writeq(val: ~0ull, addr: cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1SX(0)); |
27 | } |
28 | |
29 | static irqreturn_t otx_cpt_mbx0_intr_handler(int __always_unused irq, |
30 | void *cpt) |
31 | { |
32 | otx_cpt_mbox_intr_handler(cpt, mbx: 0); |
33 | |
34 | return IRQ_HANDLED; |
35 | } |
36 | |
37 | static void otx_cpt_reset(struct otx_cpt_device *cpt) |
38 | { |
39 | writeq(val: 1, addr: cpt->reg_base + OTX_CPT_PF_RESET); |
40 | } |
41 | |
42 | static void otx_cpt_find_max_enabled_cores(struct otx_cpt_device *cpt) |
43 | { |
44 | union otx_cptx_pf_constants pf_cnsts = {0}; |
45 | |
46 | pf_cnsts.u = readq(addr: cpt->reg_base + OTX_CPT_PF_CONSTANTS); |
47 | cpt->eng_grps.avail.max_se_cnt = pf_cnsts.s.se; |
48 | cpt->eng_grps.avail.max_ae_cnt = pf_cnsts.s.ae; |
49 | } |
50 | |
51 | static u32 otx_cpt_check_bist_status(struct otx_cpt_device *cpt) |
52 | { |
53 | union otx_cptx_pf_bist_status bist_sts = {0}; |
54 | |
55 | bist_sts.u = readq(addr: cpt->reg_base + OTX_CPT_PF_BIST_STATUS); |
56 | return bist_sts.u; |
57 | } |
58 | |
59 | static u64 otx_cpt_check_exe_bist_status(struct otx_cpt_device *cpt) |
60 | { |
61 | union otx_cptx_pf_exe_bist_status bist_sts = {0}; |
62 | |
63 | bist_sts.u = readq(addr: cpt->reg_base + OTX_CPT_PF_EXE_BIST_STATUS); |
64 | return bist_sts.u; |
65 | } |
66 | |
67 | static int otx_cpt_device_init(struct otx_cpt_device *cpt) |
68 | { |
69 | struct device *dev = &cpt->pdev->dev; |
70 | u16 sdevid; |
71 | u64 bist; |
72 | |
73 | /* Reset the PF when probed first */ |
74 | otx_cpt_reset(cpt); |
75 | mdelay(100); |
76 | |
77 | pci_read_config_word(dev: cpt->pdev, PCI_SUBSYSTEM_ID, val: &sdevid); |
78 | |
79 | /* Check BIST status */ |
80 | bist = (u64)otx_cpt_check_bist_status(cpt); |
81 | if (bist) { |
82 | dev_err(dev, "RAM BIST failed with code 0x%llx\n" , bist); |
83 | return -ENODEV; |
84 | } |
85 | |
86 | bist = otx_cpt_check_exe_bist_status(cpt); |
87 | if (bist) { |
88 | dev_err(dev, "Engine BIST failed with code 0x%llx\n" , bist); |
89 | return -ENODEV; |
90 | } |
91 | |
92 | /* Get max enabled cores */ |
93 | otx_cpt_find_max_enabled_cores(cpt); |
94 | |
95 | if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) && |
96 | (cpt->eng_grps.avail.max_se_cnt == 0)) { |
97 | cpt->pf_type = OTX_CPT_AE; |
98 | } else if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) && |
99 | (cpt->eng_grps.avail.max_ae_cnt == 0)) { |
100 | cpt->pf_type = OTX_CPT_SE; |
101 | } |
102 | |
103 | /* Get max VQs/VFs supported by the device */ |
104 | cpt->max_vfs = pci_sriov_get_totalvfs(dev: cpt->pdev); |
105 | |
106 | /* Disable all cores */ |
107 | otx_cpt_disable_all_cores(cpt); |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int otx_cpt_register_interrupts(struct otx_cpt_device *cpt) |
113 | { |
114 | struct device *dev = &cpt->pdev->dev; |
115 | u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT; |
116 | u32 num_vec = OTX_CPT_PF_MSIX_VECTORS; |
117 | int ret; |
118 | |
119 | /* Enable MSI-X */ |
120 | ret = pci_alloc_irq_vectors(dev: cpt->pdev, min_vecs: num_vec, max_vecs: num_vec, PCI_IRQ_MSIX); |
121 | if (ret < 0) { |
122 | dev_err(&cpt->pdev->dev, |
123 | "Request for #%d msix vectors failed\n" , |
124 | num_vec); |
125 | return ret; |
126 | } |
127 | |
128 | /* Register mailbox interrupt handlers */ |
129 | ret = request_irq(irq: pci_irq_vector(dev: cpt->pdev, |
130 | OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)), |
131 | handler: otx_cpt_mbx0_intr_handler, flags: 0, name: "CPT Mbox0" , dev: cpt); |
132 | if (ret) { |
133 | dev_err(dev, "Request irq failed\n" ); |
134 | pci_free_irq_vectors(dev: cpt->pdev); |
135 | return ret; |
136 | } |
137 | /* Enable mailbox interrupt */ |
138 | otx_cpt_enable_mbox_interrupts(cpt); |
139 | return 0; |
140 | } |
141 | |
142 | static void otx_cpt_unregister_interrupts(struct otx_cpt_device *cpt) |
143 | { |
144 | u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT; |
145 | |
146 | otx_cpt_disable_mbox_interrupts(cpt); |
147 | free_irq(pci_irq_vector(dev: cpt->pdev, |
148 | OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)), |
149 | cpt); |
150 | pci_free_irq_vectors(dev: cpt->pdev); |
151 | } |
152 | |
153 | |
154 | static int otx_cpt_sriov_configure(struct pci_dev *pdev, int numvfs) |
155 | { |
156 | struct otx_cpt_device *cpt = pci_get_drvdata(pdev); |
157 | int ret = 0; |
158 | |
159 | if (numvfs > cpt->max_vfs) |
160 | numvfs = cpt->max_vfs; |
161 | |
162 | if (numvfs > 0) { |
163 | ret = otx_cpt_try_create_default_eng_grps(pdev: cpt->pdev, |
164 | eng_grps: &cpt->eng_grps, |
165 | pf_type: cpt->pf_type); |
166 | if (ret) |
167 | return ret; |
168 | |
169 | cpt->vfs_enabled = numvfs; |
170 | ret = pci_enable_sriov(dev: pdev, nr_virtfn: numvfs); |
171 | if (ret) { |
172 | cpt->vfs_enabled = 0; |
173 | return ret; |
174 | } |
175 | otx_cpt_set_eng_grps_is_rdonly(eng_grps: &cpt->eng_grps, is_rdonly: true); |
176 | try_module_get(THIS_MODULE); |
177 | ret = numvfs; |
178 | } else { |
179 | pci_disable_sriov(dev: pdev); |
180 | otx_cpt_set_eng_grps_is_rdonly(eng_grps: &cpt->eng_grps, is_rdonly: false); |
181 | module_put(THIS_MODULE); |
182 | cpt->vfs_enabled = 0; |
183 | } |
184 | dev_notice(&cpt->pdev->dev, "VFs enabled: %d\n" , ret); |
185 | |
186 | return ret; |
187 | } |
188 | |
189 | static int otx_cpt_probe(struct pci_dev *pdev, |
190 | const struct pci_device_id __always_unused *ent) |
191 | { |
192 | struct device *dev = &pdev->dev; |
193 | struct otx_cpt_device *cpt; |
194 | int err; |
195 | |
196 | cpt = devm_kzalloc(dev, size: sizeof(*cpt), GFP_KERNEL); |
197 | if (!cpt) |
198 | return -ENOMEM; |
199 | |
200 | pci_set_drvdata(pdev, data: cpt); |
201 | cpt->pdev = pdev; |
202 | |
203 | err = pci_enable_device(dev: pdev); |
204 | if (err) { |
205 | dev_err(dev, "Failed to enable PCI device\n" ); |
206 | goto err_clear_drvdata; |
207 | } |
208 | |
209 | err = pci_request_regions(pdev, DRV_NAME); |
210 | if (err) { |
211 | dev_err(dev, "PCI request regions failed 0x%x\n" , err); |
212 | goto err_disable_device; |
213 | } |
214 | |
215 | err = dma_set_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(48)); |
216 | if (err) { |
217 | dev_err(dev, "Unable to get usable 48-bit DMA configuration\n" ); |
218 | goto err_release_regions; |
219 | } |
220 | |
221 | /* MAP PF's configuration registers */ |
222 | cpt->reg_base = pci_iomap(dev: pdev, OTX_CPT_PF_PCI_CFG_BAR, max: 0); |
223 | if (!cpt->reg_base) { |
224 | dev_err(dev, "Cannot map config register space, aborting\n" ); |
225 | err = -ENOMEM; |
226 | goto err_release_regions; |
227 | } |
228 | |
229 | /* CPT device HW initialization */ |
230 | err = otx_cpt_device_init(cpt); |
231 | if (err) |
232 | goto err_unmap_region; |
233 | |
234 | /* Register interrupts */ |
235 | err = otx_cpt_register_interrupts(cpt); |
236 | if (err) |
237 | goto err_unmap_region; |
238 | |
239 | /* Initialize engine groups */ |
240 | err = otx_cpt_init_eng_grps(pdev, eng_grps: &cpt->eng_grps, pf_type: cpt->pf_type); |
241 | if (err) |
242 | goto err_unregister_interrupts; |
243 | |
244 | return 0; |
245 | |
246 | err_unregister_interrupts: |
247 | otx_cpt_unregister_interrupts(cpt); |
248 | err_unmap_region: |
249 | pci_iounmap(dev: pdev, cpt->reg_base); |
250 | err_release_regions: |
251 | pci_release_regions(pdev); |
252 | err_disable_device: |
253 | pci_disable_device(dev: pdev); |
254 | err_clear_drvdata: |
255 | pci_set_drvdata(pdev, NULL); |
256 | |
257 | return err; |
258 | } |
259 | |
260 | static void otx_cpt_remove(struct pci_dev *pdev) |
261 | { |
262 | struct otx_cpt_device *cpt = pci_get_drvdata(pdev); |
263 | |
264 | if (!cpt) |
265 | return; |
266 | |
267 | /* Disable VFs */ |
268 | pci_disable_sriov(dev: pdev); |
269 | /* Cleanup engine groups */ |
270 | otx_cpt_cleanup_eng_grps(pdev, eng_grps: &cpt->eng_grps); |
271 | /* Disable CPT PF interrupts */ |
272 | otx_cpt_unregister_interrupts(cpt); |
273 | /* Disengage SE and AE cores from all groups */ |
274 | otx_cpt_disable_all_cores(cpt); |
275 | pci_iounmap(dev: pdev, cpt->reg_base); |
276 | pci_release_regions(pdev); |
277 | pci_disable_device(dev: pdev); |
278 | pci_set_drvdata(pdev, NULL); |
279 | } |
280 | |
281 | /* Supported devices */ |
282 | static const struct pci_device_id otx_cpt_id_table[] = { |
283 | { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX_CPT_PCI_PF_DEVICE_ID) }, |
284 | { 0, } /* end of table */ |
285 | }; |
286 | |
287 | static struct pci_driver otx_cpt_pci_driver = { |
288 | .name = DRV_NAME, |
289 | .id_table = otx_cpt_id_table, |
290 | .probe = otx_cpt_probe, |
291 | .remove = otx_cpt_remove, |
292 | .sriov_configure = otx_cpt_sriov_configure |
293 | }; |
294 | |
295 | module_pci_driver(otx_cpt_pci_driver); |
296 | |
297 | MODULE_AUTHOR("Marvell International Ltd." ); |
298 | MODULE_DESCRIPTION("Marvell OcteonTX CPT Physical Function Driver" ); |
299 | MODULE_LICENSE("GPL v2" ); |
300 | MODULE_VERSION(DRV_VERSION); |
301 | MODULE_DEVICE_TABLE(pci, otx_cpt_id_table); |
302 | |