162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Cavium, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/firmware.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/printk.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "cptpf.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DRV_NAME "thunder-cpt" 1762306a36Sopenharmony_ci#define DRV_VERSION "1.0" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic u32 num_vfs = 4; /* Default 4 VF enabled */ 2062306a36Sopenharmony_cimodule_param(num_vfs, uint, 0444); 2162306a36Sopenharmony_ciMODULE_PARM_DESC(num_vfs, "Number of VFs to enable(1-16)"); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Disable cores specified by coremask 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic void cpt_disable_cores(struct cpt_device *cpt, u64 coremask, 2762306a36Sopenharmony_ci u8 type, u8 grp) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci u64 pf_exe_ctl; 3062306a36Sopenharmony_ci u32 timeout = 100; 3162306a36Sopenharmony_ci u64 grpmask = 0; 3262306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (type == AE_TYPES) 3562306a36Sopenharmony_ci coremask = (coremask << cpt->max_se_cores); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Disengage the cores from groups */ 3862306a36Sopenharmony_ci grpmask = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); 3962306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 4062306a36Sopenharmony_ci (grpmask & ~coremask)); 4162306a36Sopenharmony_ci udelay(CSR_DELAY); 4262306a36Sopenharmony_ci grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); 4362306a36Sopenharmony_ci while (grp & coremask) { 4462306a36Sopenharmony_ci dev_err(dev, "Cores still busy %llx", coremask); 4562306a36Sopenharmony_ci grp = cpt_read_csr64(cpt->reg_base, 4662306a36Sopenharmony_ci CPTX_PF_EXEC_BUSY(0)); 4762306a36Sopenharmony_ci if (timeout--) 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci udelay(CSR_DELAY); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Disable the cores */ 5462306a36Sopenharmony_ci pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); 5562306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 5662306a36Sopenharmony_ci (pf_exe_ctl & ~coremask)); 5762306a36Sopenharmony_ci udelay(CSR_DELAY); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Enable cores specified by coremask 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic void cpt_enable_cores(struct cpt_device *cpt, u64 coremask, 6462306a36Sopenharmony_ci u8 type) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci u64 pf_exe_ctl; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (type == AE_TYPES) 6962306a36Sopenharmony_ci coremask = (coremask << cpt->max_se_cores); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); 7262306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 7362306a36Sopenharmony_ci (pf_exe_ctl | coremask)); 7462306a36Sopenharmony_ci udelay(CSR_DELAY); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void cpt_configure_group(struct cpt_device *cpt, u8 grp, 7862306a36Sopenharmony_ci u64 coremask, u8 type) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci u64 pf_gx_en = 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (type == AE_TYPES) 8362306a36Sopenharmony_ci coremask = (coremask << cpt->max_se_cores); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci pf_gx_en = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); 8662306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 8762306a36Sopenharmony_ci (pf_gx_en | coremask)); 8862306a36Sopenharmony_ci udelay(CSR_DELAY); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void cpt_disable_mbox_interrupts(struct cpt_device *cpt) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci /* Clear mbox(0) interupts for all vfs */ 9462306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1CX(0, 0), ~0ull); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void cpt_disable_ecc_interrupts(struct cpt_device *cpt) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci /* Clear ecc(0) interupts for all vfs */ 10062306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_ECC0_ENA_W1C(0), ~0ull); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void cpt_disable_exec_interrupts(struct cpt_device *cpt) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci /* Clear exec interupts for all vfs */ 10662306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_EXEC_ENA_W1C(0), ~0ull); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void cpt_disable_all_interrupts(struct cpt_device *cpt) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci cpt_disable_mbox_interrupts(cpt); 11262306a36Sopenharmony_ci cpt_disable_ecc_interrupts(cpt); 11362306a36Sopenharmony_ci cpt_disable_exec_interrupts(cpt); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void cpt_enable_mbox_interrupts(struct cpt_device *cpt) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci /* Set mbox(0) interupts for all vfs */ 11962306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1SX(0, 0), ~0ull); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int cpt_load_microcode(struct cpt_device *cpt, struct microcode *mcode) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int ret = 0, core = 0, shift = 0; 12562306a36Sopenharmony_ci u32 total_cores = 0; 12662306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!mcode || !mcode->code) { 12962306a36Sopenharmony_ci dev_err(dev, "Either the mcode is null or data is NULL\n"); 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (mcode->code_size == 0) { 13462306a36Sopenharmony_ci dev_err(dev, "microcode size is 0\n"); 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Assumes 0-9 are SE cores for UCODE_BASE registers and 13962306a36Sopenharmony_ci * AE core bases follow 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if (mcode->is_ae) { 14262306a36Sopenharmony_ci core = CPT_MAX_SE_CORES; /* start couting from 10 */ 14362306a36Sopenharmony_ci total_cores = CPT_MAX_TOTAL_CORES; /* upto 15 */ 14462306a36Sopenharmony_ci } else { 14562306a36Sopenharmony_ci core = 0; /* start couting from 0 */ 14662306a36Sopenharmony_ci total_cores = CPT_MAX_SE_CORES; /* upto 9 */ 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Point to microcode for each core of the group */ 15062306a36Sopenharmony_ci for (; core < total_cores ; core++, shift++) { 15162306a36Sopenharmony_ci if (mcode->core_mask & (1 << shift)) { 15262306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, 15362306a36Sopenharmony_ci CPTX_PF_ENGX_UCODE_BASE(0, core), 15462306a36Sopenharmony_ci (u64)mcode->phys_base); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int do_cpt_init(struct cpt_device *cpt, struct microcode *mcode) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int ret = 0; 16362306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Make device not ready */ 16662306a36Sopenharmony_ci cpt->flags &= ~CPT_FLAG_DEVICE_READY; 16762306a36Sopenharmony_ci /* Disable All PF interrupts */ 16862306a36Sopenharmony_ci cpt_disable_all_interrupts(cpt); 16962306a36Sopenharmony_ci /* Calculate mcode group and coremasks */ 17062306a36Sopenharmony_ci if (mcode->is_ae) { 17162306a36Sopenharmony_ci if (mcode->num_cores > cpt->max_ae_cores) { 17262306a36Sopenharmony_ci dev_err(dev, "Requested for more cores than available AE cores\n"); 17362306a36Sopenharmony_ci ret = -EINVAL; 17462306a36Sopenharmony_ci goto cpt_init_fail; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { 17862306a36Sopenharmony_ci dev_err(dev, "Can't load, all eight microcode groups in use"); 17962306a36Sopenharmony_ci return -ENFILE; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mcode->group = cpt->next_group; 18362306a36Sopenharmony_ci /* Convert requested cores to mask */ 18462306a36Sopenharmony_ci mcode->core_mask = GENMASK(mcode->num_cores, 0); 18562306a36Sopenharmony_ci cpt_disable_cores(cpt, mcode->core_mask, AE_TYPES, 18662306a36Sopenharmony_ci mcode->group); 18762306a36Sopenharmony_ci /* Load microcode for AE engines */ 18862306a36Sopenharmony_ci ret = cpt_load_microcode(cpt, mcode); 18962306a36Sopenharmony_ci if (ret) { 19062306a36Sopenharmony_ci dev_err(dev, "Microcode load Failed for %s\n", 19162306a36Sopenharmony_ci mcode->version); 19262306a36Sopenharmony_ci goto cpt_init_fail; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci cpt->next_group++; 19562306a36Sopenharmony_ci /* Configure group mask for the mcode */ 19662306a36Sopenharmony_ci cpt_configure_group(cpt, mcode->group, mcode->core_mask, 19762306a36Sopenharmony_ci AE_TYPES); 19862306a36Sopenharmony_ci /* Enable AE cores for the group mask */ 19962306a36Sopenharmony_ci cpt_enable_cores(cpt, mcode->core_mask, AE_TYPES); 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci if (mcode->num_cores > cpt->max_se_cores) { 20262306a36Sopenharmony_ci dev_err(dev, "Requested for more cores than available SE cores\n"); 20362306a36Sopenharmony_ci ret = -EINVAL; 20462306a36Sopenharmony_ci goto cpt_init_fail; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { 20762306a36Sopenharmony_ci dev_err(dev, "Can't load, all eight microcode groups in use"); 20862306a36Sopenharmony_ci return -ENFILE; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci mcode->group = cpt->next_group; 21262306a36Sopenharmony_ci /* Covert requested cores to mask */ 21362306a36Sopenharmony_ci mcode->core_mask = GENMASK(mcode->num_cores, 0); 21462306a36Sopenharmony_ci cpt_disable_cores(cpt, mcode->core_mask, SE_TYPES, 21562306a36Sopenharmony_ci mcode->group); 21662306a36Sopenharmony_ci /* Load microcode for SE engines */ 21762306a36Sopenharmony_ci ret = cpt_load_microcode(cpt, mcode); 21862306a36Sopenharmony_ci if (ret) { 21962306a36Sopenharmony_ci dev_err(dev, "Microcode load Failed for %s\n", 22062306a36Sopenharmony_ci mcode->version); 22162306a36Sopenharmony_ci goto cpt_init_fail; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci cpt->next_group++; 22462306a36Sopenharmony_ci /* Configure group mask for the mcode */ 22562306a36Sopenharmony_ci cpt_configure_group(cpt, mcode->group, mcode->core_mask, 22662306a36Sopenharmony_ci SE_TYPES); 22762306a36Sopenharmony_ci /* Enable SE cores for the group mask */ 22862306a36Sopenharmony_ci cpt_enable_cores(cpt, mcode->core_mask, SE_TYPES); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Enabled PF mailbox interrupts */ 23262306a36Sopenharmony_ci cpt_enable_mbox_interrupts(cpt); 23362306a36Sopenharmony_ci cpt->flags |= CPT_FLAG_DEVICE_READY; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cicpt_init_fail: 23862306a36Sopenharmony_ci /* Enabled PF mailbox interrupts */ 23962306a36Sopenharmony_ci cpt_enable_mbox_interrupts(cpt); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistruct ucode_header { 24562306a36Sopenharmony_ci u8 version[CPT_UCODE_VERSION_SZ]; 24662306a36Sopenharmony_ci __be32 code_length; 24762306a36Sopenharmony_ci u32 data_length; 24862306a36Sopenharmony_ci u64 sram_address; 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int cpt_ucode_load_fw(struct cpt_device *cpt, const u8 *fw, bool is_ae) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci const struct firmware *fw_entry; 25462306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 25562306a36Sopenharmony_ci struct ucode_header *ucode; 25662306a36Sopenharmony_ci unsigned int code_length; 25762306a36Sopenharmony_ci struct microcode *mcode; 25862306a36Sopenharmony_ci int j, ret = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = request_firmware(&fw_entry, fw, dev); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ucode = (struct ucode_header *)fw_entry->data; 26562306a36Sopenharmony_ci mcode = &cpt->mcode[cpt->next_mc_idx]; 26662306a36Sopenharmony_ci memcpy(mcode->version, (u8 *)fw_entry->data, CPT_UCODE_VERSION_SZ); 26762306a36Sopenharmony_ci code_length = ntohl(ucode->code_length); 26862306a36Sopenharmony_ci if (code_length == 0 || code_length >= INT_MAX / 2) { 26962306a36Sopenharmony_ci ret = -EINVAL; 27062306a36Sopenharmony_ci goto fw_release; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci mcode->code_size = code_length * 2; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mcode->is_ae = is_ae; 27562306a36Sopenharmony_ci mcode->core_mask = 0ULL; 27662306a36Sopenharmony_ci mcode->num_cores = is_ae ? 6 : 10; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Allocate DMAable space */ 27962306a36Sopenharmony_ci mcode->code = dma_alloc_coherent(&cpt->pdev->dev, mcode->code_size, 28062306a36Sopenharmony_ci &mcode->phys_base, GFP_KERNEL); 28162306a36Sopenharmony_ci if (!mcode->code) { 28262306a36Sopenharmony_ci dev_err(dev, "Unable to allocate space for microcode"); 28362306a36Sopenharmony_ci ret = -ENOMEM; 28462306a36Sopenharmony_ci goto fw_release; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci memcpy((void *)mcode->code, (void *)(fw_entry->data + sizeof(*ucode)), 28862306a36Sopenharmony_ci mcode->code_size); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Byte swap 64-bit */ 29162306a36Sopenharmony_ci for (j = 0; j < (mcode->code_size / 8); j++) 29262306a36Sopenharmony_ci ((__be64 *)mcode->code)[j] = cpu_to_be64(((u64 *)mcode->code)[j]); 29362306a36Sopenharmony_ci /* MC needs 16-bit swap */ 29462306a36Sopenharmony_ci for (j = 0; j < (mcode->code_size / 2); j++) 29562306a36Sopenharmony_ci ((__be16 *)mcode->code)[j] = cpu_to_be16(((u16 *)mcode->code)[j]); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci dev_dbg(dev, "mcode->code_size = %u\n", mcode->code_size); 29862306a36Sopenharmony_ci dev_dbg(dev, "mcode->is_ae = %u\n", mcode->is_ae); 29962306a36Sopenharmony_ci dev_dbg(dev, "mcode->num_cores = %u\n", mcode->num_cores); 30062306a36Sopenharmony_ci dev_dbg(dev, "mcode->code = %llx\n", (u64)mcode->code); 30162306a36Sopenharmony_ci dev_dbg(dev, "mcode->phys_base = %llx\n", mcode->phys_base); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = do_cpt_init(cpt, mcode); 30462306a36Sopenharmony_ci if (ret) { 30562306a36Sopenharmony_ci dev_err(dev, "do_cpt_init failed with ret: %d\n", ret); 30662306a36Sopenharmony_ci goto fw_release; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci dev_info(dev, "Microcode Loaded %s\n", mcode->version); 31062306a36Sopenharmony_ci mcode->is_mc_valid = 1; 31162306a36Sopenharmony_ci cpt->next_mc_idx++; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cifw_release: 31462306a36Sopenharmony_ci release_firmware(fw_entry); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int cpt_ucode_load(struct cpt_device *cpt) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int ret = 0; 32262306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-ae.out", true); 32562306a36Sopenharmony_ci if (ret) { 32662306a36Sopenharmony_ci dev_err(dev, "ae:cpt_ucode_load failed with ret: %d\n", ret); 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-se.out", false); 33062306a36Sopenharmony_ci if (ret) { 33162306a36Sopenharmony_ci dev_err(dev, "se:cpt_ucode_load failed with ret: %d\n", ret); 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic irqreturn_t cpt_mbx0_intr_handler(int irq, void *cpt_irq) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct cpt_device *cpt = (struct cpt_device *)cpt_irq; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci cpt_mbox_intr_handler(cpt, 0); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return IRQ_HANDLED; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void cpt_reset(struct cpt_device *cpt) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_RESET(0), 1); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void cpt_find_max_enabled_cores(struct cpt_device *cpt) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci union cptx_pf_constants pf_cnsts = {0}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pf_cnsts.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_CONSTANTS(0)); 35762306a36Sopenharmony_ci cpt->max_se_cores = pf_cnsts.s.se; 35862306a36Sopenharmony_ci cpt->max_ae_cores = pf_cnsts.s.ae; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic u32 cpt_check_bist_status(struct cpt_device *cpt) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci union cptx_pf_bist_status bist_sts = {0}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci bist_sts.u = cpt_read_csr64(cpt->reg_base, 36662306a36Sopenharmony_ci CPTX_PF_BIST_STATUS(0)); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return bist_sts.u; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic u64 cpt_check_exe_bist_status(struct cpt_device *cpt) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci union cptx_pf_exe_bist_status bist_sts = {0}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci bist_sts.u = cpt_read_csr64(cpt->reg_base, 37662306a36Sopenharmony_ci CPTX_PF_EXE_BIST_STATUS(0)); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return bist_sts.u; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void cpt_disable_all_cores(struct cpt_device *cpt) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci u32 grp, timeout = 100; 38462306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Disengage the cores from groups */ 38762306a36Sopenharmony_ci for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { 38862306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 0); 38962306a36Sopenharmony_ci udelay(CSR_DELAY); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); 39362306a36Sopenharmony_ci while (grp) { 39462306a36Sopenharmony_ci dev_err(dev, "Cores still busy"); 39562306a36Sopenharmony_ci grp = cpt_read_csr64(cpt->reg_base, 39662306a36Sopenharmony_ci CPTX_PF_EXEC_BUSY(0)); 39762306a36Sopenharmony_ci if (timeout--) 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci udelay(CSR_DELAY); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci /* Disable the cores */ 40362306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 0); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* 40762306a36Sopenharmony_ci * Ensure all cores are disengaged from all groups by 40862306a36Sopenharmony_ci * calling cpt_disable_all_cores() before calling this 40962306a36Sopenharmony_ci * function. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic void cpt_unload_microcode(struct cpt_device *cpt) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci u32 grp = 0, core; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Free microcode bases and reset group masks */ 41662306a36Sopenharmony_ci for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { 41762306a36Sopenharmony_ci struct microcode *mcode = &cpt->mcode[grp]; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (cpt->mcode[grp].code) 42062306a36Sopenharmony_ci dma_free_coherent(&cpt->pdev->dev, mcode->code_size, 42162306a36Sopenharmony_ci mcode->code, mcode->phys_base); 42262306a36Sopenharmony_ci mcode->code = NULL; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci /* Clear UCODE_BASE registers for all engines */ 42562306a36Sopenharmony_ci for (core = 0; core < CPT_MAX_TOTAL_CORES; core++) 42662306a36Sopenharmony_ci cpt_write_csr64(cpt->reg_base, 42762306a36Sopenharmony_ci CPTX_PF_ENGX_UCODE_BASE(0, core), 0ull); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int cpt_device_init(struct cpt_device *cpt) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci u64 bist; 43362306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Reset the PF when probed first */ 43662306a36Sopenharmony_ci cpt_reset(cpt); 43762306a36Sopenharmony_ci msleep(100); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /*Check BIST status*/ 44062306a36Sopenharmony_ci bist = (u64)cpt_check_bist_status(cpt); 44162306a36Sopenharmony_ci if (bist) { 44262306a36Sopenharmony_ci dev_err(dev, "RAM BIST failed with code 0x%llx", bist); 44362306a36Sopenharmony_ci return -ENODEV; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci bist = cpt_check_exe_bist_status(cpt); 44762306a36Sopenharmony_ci if (bist) { 44862306a36Sopenharmony_ci dev_err(dev, "Engine BIST failed with code 0x%llx", bist); 44962306a36Sopenharmony_ci return -ENODEV; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /*Get CLK frequency*/ 45362306a36Sopenharmony_ci /*Get max enabled cores */ 45462306a36Sopenharmony_ci cpt_find_max_enabled_cores(cpt); 45562306a36Sopenharmony_ci /*Disable all cores*/ 45662306a36Sopenharmony_ci cpt_disable_all_cores(cpt); 45762306a36Sopenharmony_ci /*Reset device parameters*/ 45862306a36Sopenharmony_ci cpt->next_mc_idx = 0; 45962306a36Sopenharmony_ci cpt->next_group = 0; 46062306a36Sopenharmony_ci /* PF is ready */ 46162306a36Sopenharmony_ci cpt->flags |= CPT_FLAG_DEVICE_READY; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int cpt_register_interrupts(struct cpt_device *cpt) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci int ret; 46962306a36Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Enable MSI-X */ 47262306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(cpt->pdev, CPT_PF_MSIX_VECTORS, 47362306a36Sopenharmony_ci CPT_PF_MSIX_VECTORS, PCI_IRQ_MSIX); 47462306a36Sopenharmony_ci if (ret < 0) { 47562306a36Sopenharmony_ci dev_err(&cpt->pdev->dev, "Request for #%d msix vectors failed\n", 47662306a36Sopenharmony_ci CPT_PF_MSIX_VECTORS); 47762306a36Sopenharmony_ci return ret; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Register mailbox interrupt handlers */ 48162306a36Sopenharmony_ci ret = request_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), 48262306a36Sopenharmony_ci cpt_mbx0_intr_handler, 0, "CPT Mbox0", cpt); 48362306a36Sopenharmony_ci if (ret) 48462306a36Sopenharmony_ci goto fail; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Enable mailbox interrupt */ 48762306a36Sopenharmony_ci cpt_enable_mbox_interrupts(cpt); 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cifail: 49162306a36Sopenharmony_ci dev_err(dev, "Request irq failed\n"); 49262306a36Sopenharmony_ci pci_disable_msix(cpt->pdev); 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void cpt_unregister_interrupts(struct cpt_device *cpt) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci free_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), cpt); 49962306a36Sopenharmony_ci pci_disable_msix(cpt->pdev); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int cpt_sriov_init(struct cpt_device *cpt, int num_vfs) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci int pos = 0; 50562306a36Sopenharmony_ci int err; 50662306a36Sopenharmony_ci u16 total_vf_cnt; 50762306a36Sopenharmony_ci struct pci_dev *pdev = cpt->pdev; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); 51062306a36Sopenharmony_ci if (!pos) { 51162306a36Sopenharmony_ci dev_err(&pdev->dev, "SRIOV capability is not found in PCIe config space\n"); 51262306a36Sopenharmony_ci return -ENODEV; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci cpt->num_vf_en = num_vfs; /* User requested VFs */ 51662306a36Sopenharmony_ci pci_read_config_word(pdev, (pos + PCI_SRIOV_TOTAL_VF), &total_vf_cnt); 51762306a36Sopenharmony_ci if (total_vf_cnt < cpt->num_vf_en) 51862306a36Sopenharmony_ci cpt->num_vf_en = total_vf_cnt; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!total_vf_cnt) 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /*Enabled the available VFs */ 52462306a36Sopenharmony_ci err = pci_enable_sriov(pdev, cpt->num_vf_en); 52562306a36Sopenharmony_ci if (err) { 52662306a36Sopenharmony_ci dev_err(&pdev->dev, "SRIOV enable failed, num VF is %d\n", 52762306a36Sopenharmony_ci cpt->num_vf_en); 52862306a36Sopenharmony_ci cpt->num_vf_en = 0; 52962306a36Sopenharmony_ci return err; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* TODO: Optionally enable static VQ priorities feature */ 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci dev_info(&pdev->dev, "SRIOV enabled, number of VF available %d\n", 53562306a36Sopenharmony_ci cpt->num_vf_en); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci cpt->flags |= CPT_FLAG_SRIOV_ENABLED; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int cpt_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 54562306a36Sopenharmony_ci struct cpt_device *cpt; 54662306a36Sopenharmony_ci int err; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (num_vfs > 16 || num_vfs < 4) { 54962306a36Sopenharmony_ci dev_warn(dev, "Invalid vf count %d, Resetting it to 4(default)\n", 55062306a36Sopenharmony_ci num_vfs); 55162306a36Sopenharmony_ci num_vfs = 4; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci cpt = devm_kzalloc(dev, sizeof(*cpt), GFP_KERNEL); 55562306a36Sopenharmony_ci if (!cpt) 55662306a36Sopenharmony_ci return -ENOMEM; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pci_set_drvdata(pdev, cpt); 55962306a36Sopenharmony_ci cpt->pdev = pdev; 56062306a36Sopenharmony_ci err = pci_enable_device(pdev); 56162306a36Sopenharmony_ci if (err) { 56262306a36Sopenharmony_ci dev_err(dev, "Failed to enable PCI device\n"); 56362306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 56862306a36Sopenharmony_ci if (err) { 56962306a36Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 57062306a36Sopenharmony_ci goto cpt_err_disable_device; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); 57462306a36Sopenharmony_ci if (err) { 57562306a36Sopenharmony_ci dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); 57662306a36Sopenharmony_ci goto cpt_err_release_regions; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* MAP PF's configuration registers */ 58062306a36Sopenharmony_ci cpt->reg_base = pcim_iomap(pdev, 0, 0); 58162306a36Sopenharmony_ci if (!cpt->reg_base) { 58262306a36Sopenharmony_ci dev_err(dev, "Cannot map config register space, aborting\n"); 58362306a36Sopenharmony_ci err = -ENOMEM; 58462306a36Sopenharmony_ci goto cpt_err_release_regions; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* CPT device HW initialization */ 58862306a36Sopenharmony_ci cpt_device_init(cpt); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Register interrupts */ 59162306a36Sopenharmony_ci err = cpt_register_interrupts(cpt); 59262306a36Sopenharmony_ci if (err) 59362306a36Sopenharmony_ci goto cpt_err_release_regions; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci err = cpt_ucode_load(cpt); 59662306a36Sopenharmony_ci if (err) 59762306a36Sopenharmony_ci goto cpt_err_unregister_interrupts; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Configure SRIOV */ 60062306a36Sopenharmony_ci err = cpt_sriov_init(cpt, num_vfs); 60162306a36Sopenharmony_ci if (err) 60262306a36Sopenharmony_ci goto cpt_err_unregister_interrupts; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cicpt_err_unregister_interrupts: 60762306a36Sopenharmony_ci cpt_unregister_interrupts(cpt); 60862306a36Sopenharmony_cicpt_err_release_regions: 60962306a36Sopenharmony_ci pci_release_regions(pdev); 61062306a36Sopenharmony_cicpt_err_disable_device: 61162306a36Sopenharmony_ci pci_disable_device(pdev); 61262306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void cpt_remove(struct pci_dev *pdev) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct cpt_device *cpt = pci_get_drvdata(pdev); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Disengage SE and AE cores from all groups*/ 62162306a36Sopenharmony_ci cpt_disable_all_cores(cpt); 62262306a36Sopenharmony_ci /* Unload microcodes */ 62362306a36Sopenharmony_ci cpt_unload_microcode(cpt); 62462306a36Sopenharmony_ci cpt_unregister_interrupts(cpt); 62562306a36Sopenharmony_ci pci_disable_sriov(pdev); 62662306a36Sopenharmony_ci pci_release_regions(pdev); 62762306a36Sopenharmony_ci pci_disable_device(pdev); 62862306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void cpt_shutdown(struct pci_dev *pdev) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct cpt_device *cpt = pci_get_drvdata(pdev); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!cpt) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci dev_info(&pdev->dev, "Shutdown device %x:%x.\n", 63962306a36Sopenharmony_ci (u32)pdev->vendor, (u32)pdev->device); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci cpt_unregister_interrupts(cpt); 64262306a36Sopenharmony_ci pci_release_regions(pdev); 64362306a36Sopenharmony_ci pci_disable_device(pdev); 64462306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* Supported devices */ 64862306a36Sopenharmony_cistatic const struct pci_device_id cpt_id_table[] = { 64962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CPT_81XX_PCI_PF_DEVICE_ID) }, 65062306a36Sopenharmony_ci { 0, } /* end of table */ 65162306a36Sopenharmony_ci}; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic struct pci_driver cpt_pci_driver = { 65462306a36Sopenharmony_ci .name = DRV_NAME, 65562306a36Sopenharmony_ci .id_table = cpt_id_table, 65662306a36Sopenharmony_ci .probe = cpt_probe, 65762306a36Sopenharmony_ci .remove = cpt_remove, 65862306a36Sopenharmony_ci .shutdown = cpt_shutdown, 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cimodule_pci_driver(cpt_pci_driver); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ciMODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); 66462306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder CPT Physical Function Driver"); 66562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 66662306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 66762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cpt_id_table); 668