18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Marvell OcteonTX CPT driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Marvell International Ltd.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
88c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "otx_cpt_common.h"
128c2ecf20Sopenharmony_ci#include "otx_cptpf.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define DRV_NAME	"octeontx-cpt"
158c2ecf20Sopenharmony_ci#define DRV_VERSION	"1.0"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic void otx_cpt_disable_mbox_interrupts(struct otx_cpt_device *cpt)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	/* Disable mbox(0) interrupts for all VFs */
208c2ecf20Sopenharmony_ci	writeq(~0ull, cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1CX(0));
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic void otx_cpt_enable_mbox_interrupts(struct otx_cpt_device *cpt)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	/* Enable mbox(0) interrupts for all VFs */
268c2ecf20Sopenharmony_ci	writeq(~0ull, cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1SX(0));
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic irqreturn_t otx_cpt_mbx0_intr_handler(int __always_unused irq,
308c2ecf20Sopenharmony_ci					     void *cpt)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	otx_cpt_mbox_intr_handler(cpt, 0);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void otx_cpt_reset(struct otx_cpt_device *cpt)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	writeq(1, cpt->reg_base + OTX_CPT_PF_RESET);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void otx_cpt_find_max_enabled_cores(struct otx_cpt_device *cpt)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	union otx_cptx_pf_constants pf_cnsts = {0};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	pf_cnsts.u = readq(cpt->reg_base + OTX_CPT_PF_CONSTANTS);
478c2ecf20Sopenharmony_ci	cpt->eng_grps.avail.max_se_cnt = pf_cnsts.s.se;
488c2ecf20Sopenharmony_ci	cpt->eng_grps.avail.max_ae_cnt = pf_cnsts.s.ae;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic u32 otx_cpt_check_bist_status(struct otx_cpt_device *cpt)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	union otx_cptx_pf_bist_status bist_sts = {0};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	bist_sts.u = readq(cpt->reg_base + OTX_CPT_PF_BIST_STATUS);
568c2ecf20Sopenharmony_ci	return bist_sts.u;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic u64 otx_cpt_check_exe_bist_status(struct otx_cpt_device *cpt)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	union otx_cptx_pf_exe_bist_status bist_sts = {0};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	bist_sts.u = readq(cpt->reg_base + OTX_CPT_PF_EXE_BIST_STATUS);
648c2ecf20Sopenharmony_ci	return bist_sts.u;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int otx_cpt_device_init(struct otx_cpt_device *cpt)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct device *dev = &cpt->pdev->dev;
708c2ecf20Sopenharmony_ci	u16 sdevid;
718c2ecf20Sopenharmony_ci	u64 bist;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Reset the PF when probed first */
748c2ecf20Sopenharmony_ci	otx_cpt_reset(cpt);
758c2ecf20Sopenharmony_ci	mdelay(100);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	pci_read_config_word(cpt->pdev, PCI_SUBSYSTEM_ID, &sdevid);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Check BIST status */
808c2ecf20Sopenharmony_ci	bist = (u64)otx_cpt_check_bist_status(cpt);
818c2ecf20Sopenharmony_ci	if (bist) {
828c2ecf20Sopenharmony_ci		dev_err(dev, "RAM BIST failed with code 0x%llx\n", bist);
838c2ecf20Sopenharmony_ci		return -ENODEV;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	bist = otx_cpt_check_exe_bist_status(cpt);
878c2ecf20Sopenharmony_ci	if (bist) {
888c2ecf20Sopenharmony_ci		dev_err(dev, "Engine BIST failed with code 0x%llx\n", bist);
898c2ecf20Sopenharmony_ci		return -ENODEV;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Get max enabled cores */
938c2ecf20Sopenharmony_ci	otx_cpt_find_max_enabled_cores(cpt);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) &&
968c2ecf20Sopenharmony_ci	    (cpt->eng_grps.avail.max_se_cnt == 0)) {
978c2ecf20Sopenharmony_ci		cpt->pf_type = OTX_CPT_AE;
988c2ecf20Sopenharmony_ci	} else if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) &&
998c2ecf20Sopenharmony_ci		   (cpt->eng_grps.avail.max_ae_cnt == 0)) {
1008c2ecf20Sopenharmony_ci		cpt->pf_type = OTX_CPT_SE;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Get max VQs/VFs supported by the device */
1048c2ecf20Sopenharmony_ci	cpt->max_vfs = pci_sriov_get_totalvfs(cpt->pdev);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Disable all cores */
1078c2ecf20Sopenharmony_ci	otx_cpt_disable_all_cores(cpt);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int otx_cpt_register_interrupts(struct otx_cpt_device *cpt)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct device *dev = &cpt->pdev->dev;
1158c2ecf20Sopenharmony_ci	u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT;
1168c2ecf20Sopenharmony_ci	u32 num_vec = OTX_CPT_PF_MSIX_VECTORS;
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Enable MSI-X */
1208c2ecf20Sopenharmony_ci	ret = pci_alloc_irq_vectors(cpt->pdev, num_vec, num_vec, PCI_IRQ_MSIX);
1218c2ecf20Sopenharmony_ci	if (ret < 0) {
1228c2ecf20Sopenharmony_ci		dev_err(&cpt->pdev->dev,
1238c2ecf20Sopenharmony_ci			"Request for #%d msix vectors failed\n",
1248c2ecf20Sopenharmony_ci			num_vec);
1258c2ecf20Sopenharmony_ci		return ret;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* Register mailbox interrupt handlers */
1298c2ecf20Sopenharmony_ci	ret = request_irq(pci_irq_vector(cpt->pdev,
1308c2ecf20Sopenharmony_ci				OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)),
1318c2ecf20Sopenharmony_ci				otx_cpt_mbx0_intr_handler, 0, "CPT Mbox0", cpt);
1328c2ecf20Sopenharmony_ci	if (ret) {
1338c2ecf20Sopenharmony_ci		dev_err(dev, "Request irq failed\n");
1348c2ecf20Sopenharmony_ci		pci_free_irq_vectors(cpt->pdev);
1358c2ecf20Sopenharmony_ci		return ret;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	/* Enable mailbox interrupt */
1388c2ecf20Sopenharmony_ci	otx_cpt_enable_mbox_interrupts(cpt);
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void otx_cpt_unregister_interrupts(struct otx_cpt_device *cpt)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	otx_cpt_disable_mbox_interrupts(cpt);
1478c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(cpt->pdev,
1488c2ecf20Sopenharmony_ci				OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)),
1498c2ecf20Sopenharmony_ci				cpt);
1508c2ecf20Sopenharmony_ci	pci_free_irq_vectors(cpt->pdev);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int otx_cpt_sriov_configure(struct pci_dev *pdev, int numvfs)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct otx_cpt_device *cpt = pci_get_drvdata(pdev);
1578c2ecf20Sopenharmony_ci	int ret = 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (numvfs > cpt->max_vfs)
1608c2ecf20Sopenharmony_ci		numvfs = cpt->max_vfs;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (numvfs > 0) {
1638c2ecf20Sopenharmony_ci		ret = otx_cpt_try_create_default_eng_grps(cpt->pdev,
1648c2ecf20Sopenharmony_ci							  &cpt->eng_grps,
1658c2ecf20Sopenharmony_ci							  cpt->pf_type);
1668c2ecf20Sopenharmony_ci		if (ret)
1678c2ecf20Sopenharmony_ci			return ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		cpt->vfs_enabled = numvfs;
1708c2ecf20Sopenharmony_ci		ret = pci_enable_sriov(pdev, numvfs);
1718c2ecf20Sopenharmony_ci		if (ret) {
1728c2ecf20Sopenharmony_ci			cpt->vfs_enabled = 0;
1738c2ecf20Sopenharmony_ci			return ret;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci		otx_cpt_set_eng_grps_is_rdonly(&cpt->eng_grps, true);
1768c2ecf20Sopenharmony_ci		try_module_get(THIS_MODULE);
1778c2ecf20Sopenharmony_ci		ret = numvfs;
1788c2ecf20Sopenharmony_ci	} else {
1798c2ecf20Sopenharmony_ci		pci_disable_sriov(pdev);
1808c2ecf20Sopenharmony_ci		otx_cpt_set_eng_grps_is_rdonly(&cpt->eng_grps, false);
1818c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
1828c2ecf20Sopenharmony_ci		cpt->vfs_enabled = 0;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	dev_notice(&cpt->pdev->dev, "VFs enabled: %d\n", ret);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return ret;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int otx_cpt_probe(struct pci_dev *pdev,
1908c2ecf20Sopenharmony_ci			 const struct pci_device_id __always_unused *ent)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1938c2ecf20Sopenharmony_ci	struct otx_cpt_device *cpt;
1948c2ecf20Sopenharmony_ci	int err;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	cpt = devm_kzalloc(dev, sizeof(*cpt), GFP_KERNEL);
1978c2ecf20Sopenharmony_ci	if (!cpt)
1988c2ecf20Sopenharmony_ci		return -ENOMEM;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, cpt);
2018c2ecf20Sopenharmony_ci	cpt->pdev = pdev;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
2048c2ecf20Sopenharmony_ci	if (err) {
2058c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable PCI device\n");
2068c2ecf20Sopenharmony_ci		goto err_clear_drvdata;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
2108c2ecf20Sopenharmony_ci	if (err) {
2118c2ecf20Sopenharmony_ci		dev_err(dev, "PCI request regions failed 0x%x\n", err);
2128c2ecf20Sopenharmony_ci		goto err_disable_device;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48));
2168c2ecf20Sopenharmony_ci	if (err) {
2178c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to get usable DMA configuration\n");
2188c2ecf20Sopenharmony_ci		goto err_release_regions;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48));
2228c2ecf20Sopenharmony_ci	if (err) {
2238c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n");
2248c2ecf20Sopenharmony_ci		goto err_release_regions;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* MAP PF's configuration registers */
2288c2ecf20Sopenharmony_ci	cpt->reg_base = pci_iomap(pdev, OTX_CPT_PF_PCI_CFG_BAR, 0);
2298c2ecf20Sopenharmony_ci	if (!cpt->reg_base) {
2308c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot map config register space, aborting\n");
2318c2ecf20Sopenharmony_ci		err = -ENOMEM;
2328c2ecf20Sopenharmony_ci		goto err_release_regions;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* CPT device HW initialization */
2368c2ecf20Sopenharmony_ci	err = otx_cpt_device_init(cpt);
2378c2ecf20Sopenharmony_ci	if (err)
2388c2ecf20Sopenharmony_ci		goto err_unmap_region;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Register interrupts */
2418c2ecf20Sopenharmony_ci	err = otx_cpt_register_interrupts(cpt);
2428c2ecf20Sopenharmony_ci	if (err)
2438c2ecf20Sopenharmony_ci		goto err_unmap_region;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Initialize engine groups */
2468c2ecf20Sopenharmony_ci	err = otx_cpt_init_eng_grps(pdev, &cpt->eng_grps, cpt->pf_type);
2478c2ecf20Sopenharmony_ci	if (err)
2488c2ecf20Sopenharmony_ci		goto err_unregister_interrupts;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cierr_unregister_interrupts:
2538c2ecf20Sopenharmony_ci	otx_cpt_unregister_interrupts(cpt);
2548c2ecf20Sopenharmony_cierr_unmap_region:
2558c2ecf20Sopenharmony_ci	pci_iounmap(pdev, cpt->reg_base);
2568c2ecf20Sopenharmony_cierr_release_regions:
2578c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
2588c2ecf20Sopenharmony_cierr_disable_device:
2598c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
2608c2ecf20Sopenharmony_cierr_clear_drvdata:
2618c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return err;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void otx_cpt_remove(struct pci_dev *pdev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct otx_cpt_device *cpt = pci_get_drvdata(pdev);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (!cpt)
2718c2ecf20Sopenharmony_ci		return;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Disable VFs */
2748c2ecf20Sopenharmony_ci	pci_disable_sriov(pdev);
2758c2ecf20Sopenharmony_ci	/* Cleanup engine groups */
2768c2ecf20Sopenharmony_ci	otx_cpt_cleanup_eng_grps(pdev, &cpt->eng_grps);
2778c2ecf20Sopenharmony_ci	/* Disable CPT PF interrupts */
2788c2ecf20Sopenharmony_ci	otx_cpt_unregister_interrupts(cpt);
2798c2ecf20Sopenharmony_ci	/* Disengage SE and AE cores from all groups */
2808c2ecf20Sopenharmony_ci	otx_cpt_disable_all_cores(cpt);
2818c2ecf20Sopenharmony_ci	pci_iounmap(pdev, cpt->reg_base);
2828c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
2838c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
2848c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/* Supported devices */
2888c2ecf20Sopenharmony_cistatic const struct pci_device_id otx_cpt_id_table[] = {
2898c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX_CPT_PCI_PF_DEVICE_ID) },
2908c2ecf20Sopenharmony_ci	{ 0, }  /* end of table */
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic struct pci_driver otx_cpt_pci_driver = {
2948c2ecf20Sopenharmony_ci	.name = DRV_NAME,
2958c2ecf20Sopenharmony_ci	.id_table = otx_cpt_id_table,
2968c2ecf20Sopenharmony_ci	.probe = otx_cpt_probe,
2978c2ecf20Sopenharmony_ci	.remove = otx_cpt_remove,
2988c2ecf20Sopenharmony_ci	.sriov_configure = otx_cpt_sriov_configure
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cimodule_pci_driver(otx_cpt_pci_driver);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd.");
3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell OcteonTX CPT Physical Function Driver");
3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3068c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
3078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, otx_cpt_id_table);
308