125763b3cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29e2c7d99SGeorge Cherian /* 39e2c7d99SGeorge Cherian * Copyright (C) 2016 Cavium, Inc. 49e2c7d99SGeorge Cherian */ 59e2c7d99SGeorge Cherian 69e2c7d99SGeorge Cherian #include <linux/device.h> 79e2c7d99SGeorge Cherian #include <linux/firmware.h> 89e2c7d99SGeorge Cherian #include <linux/interrupt.h> 99e2c7d99SGeorge Cherian #include <linux/module.h> 109e2c7d99SGeorge Cherian #include <linux/moduleparam.h> 119e2c7d99SGeorge Cherian #include <linux/pci.h> 129e2c7d99SGeorge Cherian #include <linux/printk.h> 139e2c7d99SGeorge Cherian 149e2c7d99SGeorge Cherian #include "cptpf.h" 159e2c7d99SGeorge Cherian 169e2c7d99SGeorge Cherian #define DRV_NAME "thunder-cpt" 179e2c7d99SGeorge Cherian #define DRV_VERSION "1.0" 189e2c7d99SGeorge Cherian 199e2c7d99SGeorge Cherian static u32 num_vfs = 4; /* Default 4 VF enabled */ 209e2c7d99SGeorge Cherian module_param(num_vfs, uint, 0444); 219e2c7d99SGeorge Cherian MODULE_PARM_DESC(num_vfs, "Number of VFs to enable(1-16)"); 229e2c7d99SGeorge Cherian 239e2c7d99SGeorge Cherian /* 249e2c7d99SGeorge Cherian * Disable cores specified by coremask 259e2c7d99SGeorge Cherian */ 269e2c7d99SGeorge Cherian static void cpt_disable_cores(struct cpt_device *cpt, u64 coremask, 279e2c7d99SGeorge Cherian u8 type, u8 grp) 289e2c7d99SGeorge Cherian { 299e2c7d99SGeorge Cherian u64 pf_exe_ctl; 309e2c7d99SGeorge Cherian u32 timeout = 100; 319e2c7d99SGeorge Cherian u64 grpmask = 0; 329e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 339e2c7d99SGeorge Cherian 349e2c7d99SGeorge Cherian if (type == AE_TYPES) 359e2c7d99SGeorge Cherian coremask = (coremask << cpt->max_se_cores); 369e2c7d99SGeorge Cherian 379e2c7d99SGeorge Cherian /* Disengage the cores from groups */ 389e2c7d99SGeorge Cherian grpmask = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); 399e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 409e2c7d99SGeorge Cherian (grpmask & ~coremask)); 419e2c7d99SGeorge Cherian udelay(CSR_DELAY); 429e2c7d99SGeorge Cherian grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); 439e2c7d99SGeorge Cherian while (grp & coremask) { 449e2c7d99SGeorge Cherian dev_err(dev, "Cores still busy %llx", coremask); 459e2c7d99SGeorge Cherian grp = cpt_read_csr64(cpt->reg_base, 469e2c7d99SGeorge Cherian CPTX_PF_EXEC_BUSY(0)); 479e2c7d99SGeorge Cherian if (timeout--) 489e2c7d99SGeorge Cherian break; 499e2c7d99SGeorge Cherian 509e2c7d99SGeorge Cherian udelay(CSR_DELAY); 519e2c7d99SGeorge Cherian } 529e2c7d99SGeorge Cherian 539e2c7d99SGeorge Cherian /* Disable the cores */ 549e2c7d99SGeorge Cherian pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); 559e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 569e2c7d99SGeorge Cherian (pf_exe_ctl & ~coremask)); 579e2c7d99SGeorge Cherian udelay(CSR_DELAY); 589e2c7d99SGeorge Cherian } 599e2c7d99SGeorge Cherian 609e2c7d99SGeorge Cherian /* 619e2c7d99SGeorge Cherian * Enable cores specified by coremask 629e2c7d99SGeorge Cherian */ 639e2c7d99SGeorge Cherian static void cpt_enable_cores(struct cpt_device *cpt, u64 coremask, 649e2c7d99SGeorge Cherian u8 type) 659e2c7d99SGeorge Cherian { 669e2c7d99SGeorge Cherian u64 pf_exe_ctl; 679e2c7d99SGeorge Cherian 689e2c7d99SGeorge Cherian if (type == AE_TYPES) 699e2c7d99SGeorge Cherian coremask = (coremask << cpt->max_se_cores); 709e2c7d99SGeorge Cherian 719e2c7d99SGeorge Cherian pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); 729e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 739e2c7d99SGeorge Cherian (pf_exe_ctl | coremask)); 749e2c7d99SGeorge Cherian udelay(CSR_DELAY); 759e2c7d99SGeorge Cherian } 769e2c7d99SGeorge Cherian 779e2c7d99SGeorge Cherian static void cpt_configure_group(struct cpt_device *cpt, u8 grp, 789e2c7d99SGeorge Cherian u64 coremask, u8 type) 799e2c7d99SGeorge Cherian { 809e2c7d99SGeorge Cherian u64 pf_gx_en = 0; 819e2c7d99SGeorge Cherian 829e2c7d99SGeorge Cherian if (type == AE_TYPES) 839e2c7d99SGeorge Cherian coremask = (coremask << cpt->max_se_cores); 849e2c7d99SGeorge Cherian 859e2c7d99SGeorge Cherian pf_gx_en = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); 869e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 879e2c7d99SGeorge Cherian (pf_gx_en | coremask)); 889e2c7d99SGeorge Cherian udelay(CSR_DELAY); 899e2c7d99SGeorge Cherian } 909e2c7d99SGeorge Cherian 919e2c7d99SGeorge Cherian static void cpt_disable_mbox_interrupts(struct cpt_device *cpt) 929e2c7d99SGeorge Cherian { 939e2c7d99SGeorge Cherian /* Clear mbox(0) interupts for all vfs */ 949e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1CX(0, 0), ~0ull); 959e2c7d99SGeorge Cherian } 969e2c7d99SGeorge Cherian 979e2c7d99SGeorge Cherian static void cpt_disable_ecc_interrupts(struct cpt_device *cpt) 989e2c7d99SGeorge Cherian { 999e2c7d99SGeorge Cherian /* Clear ecc(0) interupts for all vfs */ 1009e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_ECC0_ENA_W1C(0), ~0ull); 1019e2c7d99SGeorge Cherian } 1029e2c7d99SGeorge Cherian 1039e2c7d99SGeorge Cherian static void cpt_disable_exec_interrupts(struct cpt_device *cpt) 1049e2c7d99SGeorge Cherian { 1059e2c7d99SGeorge Cherian /* Clear exec interupts for all vfs */ 1069e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_EXEC_ENA_W1C(0), ~0ull); 1079e2c7d99SGeorge Cherian } 1089e2c7d99SGeorge Cherian 1099e2c7d99SGeorge Cherian static void cpt_disable_all_interrupts(struct cpt_device *cpt) 1109e2c7d99SGeorge Cherian { 1119e2c7d99SGeorge Cherian cpt_disable_mbox_interrupts(cpt); 1129e2c7d99SGeorge Cherian cpt_disable_ecc_interrupts(cpt); 1139e2c7d99SGeorge Cherian cpt_disable_exec_interrupts(cpt); 1149e2c7d99SGeorge Cherian } 1159e2c7d99SGeorge Cherian 1169e2c7d99SGeorge Cherian static void cpt_enable_mbox_interrupts(struct cpt_device *cpt) 1179e2c7d99SGeorge Cherian { 1189e2c7d99SGeorge Cherian /* Set mbox(0) interupts for all vfs */ 1199e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1SX(0, 0), ~0ull); 1209e2c7d99SGeorge Cherian } 1219e2c7d99SGeorge Cherian 1229e2c7d99SGeorge Cherian static int cpt_load_microcode(struct cpt_device *cpt, struct microcode *mcode) 1239e2c7d99SGeorge Cherian { 1249e2c7d99SGeorge Cherian int ret = 0, core = 0, shift = 0; 1259e2c7d99SGeorge Cherian u32 total_cores = 0; 1269e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 1279e2c7d99SGeorge Cherian 1289e2c7d99SGeorge Cherian if (!mcode || !mcode->code) { 1299e2c7d99SGeorge Cherian dev_err(dev, "Either the mcode is null or data is NULL\n"); 1309e2c7d99SGeorge Cherian return -EINVAL; 1319e2c7d99SGeorge Cherian } 1329e2c7d99SGeorge Cherian 1339e2c7d99SGeorge Cherian if (mcode->code_size == 0) { 1349e2c7d99SGeorge Cherian dev_err(dev, "microcode size is 0\n"); 1359e2c7d99SGeorge Cherian return -EINVAL; 1369e2c7d99SGeorge Cherian } 1379e2c7d99SGeorge Cherian 1389e2c7d99SGeorge Cherian /* Assumes 0-9 are SE cores for UCODE_BASE registers and 1399e2c7d99SGeorge Cherian * AE core bases follow 1409e2c7d99SGeorge Cherian */ 1419e2c7d99SGeorge Cherian if (mcode->is_ae) { 1429e2c7d99SGeorge Cherian core = CPT_MAX_SE_CORES; /* start couting from 10 */ 1439e2c7d99SGeorge Cherian total_cores = CPT_MAX_TOTAL_CORES; /* upto 15 */ 1449e2c7d99SGeorge Cherian } else { 1459e2c7d99SGeorge Cherian core = 0; /* start couting from 0 */ 1469e2c7d99SGeorge Cherian total_cores = CPT_MAX_SE_CORES; /* upto 9 */ 1479e2c7d99SGeorge Cherian } 1489e2c7d99SGeorge Cherian 1499e2c7d99SGeorge Cherian /* Point to microcode for each core of the group */ 1509e2c7d99SGeorge Cherian for (; core < total_cores ; core++, shift++) { 1519e2c7d99SGeorge Cherian if (mcode->core_mask & (1 << shift)) { 1529e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, 1539e2c7d99SGeorge Cherian CPTX_PF_ENGX_UCODE_BASE(0, core), 1549e2c7d99SGeorge Cherian (u64)mcode->phys_base); 1559e2c7d99SGeorge Cherian } 1569e2c7d99SGeorge Cherian } 1579e2c7d99SGeorge Cherian return ret; 1589e2c7d99SGeorge Cherian } 1599e2c7d99SGeorge Cherian 1609e2c7d99SGeorge Cherian static int do_cpt_init(struct cpt_device *cpt, struct microcode *mcode) 1619e2c7d99SGeorge Cherian { 1629e2c7d99SGeorge Cherian int ret = 0; 1639e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 1649e2c7d99SGeorge Cherian 1659e2c7d99SGeorge Cherian /* Make device not ready */ 1669e2c7d99SGeorge Cherian cpt->flags &= ~CPT_FLAG_DEVICE_READY; 1679e2c7d99SGeorge Cherian /* Disable All PF interrupts */ 1689e2c7d99SGeorge Cherian cpt_disable_all_interrupts(cpt); 1699e2c7d99SGeorge Cherian /* Calculate mcode group and coremasks */ 1709e2c7d99SGeorge Cherian if (mcode->is_ae) { 1719e2c7d99SGeorge Cherian if (mcode->num_cores > cpt->max_ae_cores) { 1729e2c7d99SGeorge Cherian dev_err(dev, "Requested for more cores than available AE cores\n"); 1739e2c7d99SGeorge Cherian ret = -EINVAL; 1749e2c7d99SGeorge Cherian goto cpt_init_fail; 1759e2c7d99SGeorge Cherian } 1769e2c7d99SGeorge Cherian 1779e2c7d99SGeorge Cherian if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { 1789e2c7d99SGeorge Cherian dev_err(dev, "Can't load, all eight microcode groups in use"); 1799e2c7d99SGeorge Cherian return -ENFILE; 1809e2c7d99SGeorge Cherian } 1819e2c7d99SGeorge Cherian 1829e2c7d99SGeorge Cherian mcode->group = cpt->next_group; 1839e2c7d99SGeorge Cherian /* Convert requested cores to mask */ 1849e2c7d99SGeorge Cherian mcode->core_mask = GENMASK(mcode->num_cores, 0); 1859e2c7d99SGeorge Cherian cpt_disable_cores(cpt, mcode->core_mask, AE_TYPES, 1869e2c7d99SGeorge Cherian mcode->group); 1879e2c7d99SGeorge Cherian /* Load microcode for AE engines */ 1889e2c7d99SGeorge Cherian ret = cpt_load_microcode(cpt, mcode); 1899e2c7d99SGeorge Cherian if (ret) { 1909e2c7d99SGeorge Cherian dev_err(dev, "Microcode load Failed for %s\n", 1919e2c7d99SGeorge Cherian mcode->version); 1929e2c7d99SGeorge Cherian goto cpt_init_fail; 1939e2c7d99SGeorge Cherian } 1949e2c7d99SGeorge Cherian cpt->next_group++; 1959e2c7d99SGeorge Cherian /* Configure group mask for the mcode */ 1969e2c7d99SGeorge Cherian cpt_configure_group(cpt, mcode->group, mcode->core_mask, 1979e2c7d99SGeorge Cherian AE_TYPES); 1989e2c7d99SGeorge Cherian /* Enable AE cores for the group mask */ 1999e2c7d99SGeorge Cherian cpt_enable_cores(cpt, mcode->core_mask, AE_TYPES); 2009e2c7d99SGeorge Cherian } else { 2019e2c7d99SGeorge Cherian if (mcode->num_cores > cpt->max_se_cores) { 2029e2c7d99SGeorge Cherian dev_err(dev, "Requested for more cores than available SE cores\n"); 2039e2c7d99SGeorge Cherian ret = -EINVAL; 2049e2c7d99SGeorge Cherian goto cpt_init_fail; 2059e2c7d99SGeorge Cherian } 2069e2c7d99SGeorge Cherian if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { 2079e2c7d99SGeorge Cherian dev_err(dev, "Can't load, all eight microcode groups in use"); 2089e2c7d99SGeorge Cherian return -ENFILE; 2099e2c7d99SGeorge Cherian } 2109e2c7d99SGeorge Cherian 2119e2c7d99SGeorge Cherian mcode->group = cpt->next_group; 2129e2c7d99SGeorge Cherian /* Covert requested cores to mask */ 2139e2c7d99SGeorge Cherian mcode->core_mask = GENMASK(mcode->num_cores, 0); 2149e2c7d99SGeorge Cherian cpt_disable_cores(cpt, mcode->core_mask, SE_TYPES, 2159e2c7d99SGeorge Cherian mcode->group); 2169e2c7d99SGeorge Cherian /* Load microcode for SE engines */ 2179e2c7d99SGeorge Cherian ret = cpt_load_microcode(cpt, mcode); 2189e2c7d99SGeorge Cherian if (ret) { 2199e2c7d99SGeorge Cherian dev_err(dev, "Microcode load Failed for %s\n", 2209e2c7d99SGeorge Cherian mcode->version); 2219e2c7d99SGeorge Cherian goto cpt_init_fail; 2229e2c7d99SGeorge Cherian } 2239e2c7d99SGeorge Cherian cpt->next_group++; 2249e2c7d99SGeorge Cherian /* Configure group mask for the mcode */ 2259e2c7d99SGeorge Cherian cpt_configure_group(cpt, mcode->group, mcode->core_mask, 2269e2c7d99SGeorge Cherian SE_TYPES); 2279e2c7d99SGeorge Cherian /* Enable SE cores for the group mask */ 2289e2c7d99SGeorge Cherian cpt_enable_cores(cpt, mcode->core_mask, SE_TYPES); 2299e2c7d99SGeorge Cherian } 2309e2c7d99SGeorge Cherian 2319e2c7d99SGeorge Cherian /* Enabled PF mailbox interrupts */ 2329e2c7d99SGeorge Cherian cpt_enable_mbox_interrupts(cpt); 2339e2c7d99SGeorge Cherian cpt->flags |= CPT_FLAG_DEVICE_READY; 2349e2c7d99SGeorge Cherian 2359e2c7d99SGeorge Cherian return ret; 2369e2c7d99SGeorge Cherian 2379e2c7d99SGeorge Cherian cpt_init_fail: 2389e2c7d99SGeorge Cherian /* Enabled PF mailbox interrupts */ 2399e2c7d99SGeorge Cherian cpt_enable_mbox_interrupts(cpt); 2409e2c7d99SGeorge Cherian 2419e2c7d99SGeorge Cherian return ret; 2429e2c7d99SGeorge Cherian } 2439e2c7d99SGeorge Cherian 2449e2c7d99SGeorge Cherian struct ucode_header { 2459e2c7d99SGeorge Cherian u8 version[CPT_UCODE_VERSION_SZ]; 246f2d4576aSHerbert Xu __be32 code_length; 2479e2c7d99SGeorge Cherian u32 data_length; 2489e2c7d99SGeorge Cherian u64 sram_address; 2499e2c7d99SGeorge Cherian }; 2509e2c7d99SGeorge Cherian 2519e2c7d99SGeorge Cherian static int cpt_ucode_load_fw(struct cpt_device *cpt, const u8 *fw, bool is_ae) 2529e2c7d99SGeorge Cherian { 2539e2c7d99SGeorge Cherian const struct firmware *fw_entry; 2549e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 2559e2c7d99SGeorge Cherian struct ucode_header *ucode; 256*2526d6bfSDan Carpenter unsigned int code_length; 2579e2c7d99SGeorge Cherian struct microcode *mcode; 2589e2c7d99SGeorge Cherian int j, ret = 0; 2599e2c7d99SGeorge Cherian 2609e2c7d99SGeorge Cherian ret = request_firmware(&fw_entry, fw, dev); 2619e2c7d99SGeorge Cherian if (ret) 2629e2c7d99SGeorge Cherian return ret; 2639e2c7d99SGeorge Cherian 2649e2c7d99SGeorge Cherian ucode = (struct ucode_header *)fw_entry->data; 2659e2c7d99SGeorge Cherian mcode = &cpt->mcode[cpt->next_mc_idx]; 2669e2c7d99SGeorge Cherian memcpy(mcode->version, (u8 *)fw_entry->data, CPT_UCODE_VERSION_SZ); 267*2526d6bfSDan Carpenter code_length = ntohl(ucode->code_length); 268*2526d6bfSDan Carpenter if (code_length == 0 || code_length >= INT_MAX / 2) { 269baf5b752SCorentin LABBE ret = -EINVAL; 270baf5b752SCorentin LABBE goto fw_release; 271baf5b752SCorentin LABBE } 272*2526d6bfSDan Carpenter mcode->code_size = code_length * 2; 2739e2c7d99SGeorge Cherian 2749e2c7d99SGeorge Cherian mcode->is_ae = is_ae; 2759e2c7d99SGeorge Cherian mcode->core_mask = 0ULL; 2769e2c7d99SGeorge Cherian mcode->num_cores = is_ae ? 6 : 10; 2779e2c7d99SGeorge Cherian 2789e2c7d99SGeorge Cherian /* Allocate DMAable space */ 279750afb08SLuis Chamberlain mcode->code = dma_alloc_coherent(&cpt->pdev->dev, mcode->code_size, 2809e2c7d99SGeorge Cherian &mcode->phys_base, GFP_KERNEL); 2819e2c7d99SGeorge Cherian if (!mcode->code) { 2829e2c7d99SGeorge Cherian dev_err(dev, "Unable to allocate space for microcode"); 283baf5b752SCorentin LABBE ret = -ENOMEM; 284baf5b752SCorentin LABBE goto fw_release; 2859e2c7d99SGeorge Cherian } 2869e2c7d99SGeorge Cherian 2879e2c7d99SGeorge Cherian memcpy((void *)mcode->code, (void *)(fw_entry->data + sizeof(*ucode)), 2889e2c7d99SGeorge Cherian mcode->code_size); 2899e2c7d99SGeorge Cherian 2909e2c7d99SGeorge Cherian /* Byte swap 64-bit */ 2919e2c7d99SGeorge Cherian for (j = 0; j < (mcode->code_size / 8); j++) 292f2d4576aSHerbert Xu ((__be64 *)mcode->code)[j] = cpu_to_be64(((u64 *)mcode->code)[j]); 2939e2c7d99SGeorge Cherian /* MC needs 16-bit swap */ 2949e2c7d99SGeorge Cherian for (j = 0; j < (mcode->code_size / 2); j++) 295f2d4576aSHerbert Xu ((__be16 *)mcode->code)[j] = cpu_to_be16(((u16 *)mcode->code)[j]); 2969e2c7d99SGeorge Cherian 2979e2c7d99SGeorge Cherian dev_dbg(dev, "mcode->code_size = %u\n", mcode->code_size); 2989e2c7d99SGeorge Cherian dev_dbg(dev, "mcode->is_ae = %u\n", mcode->is_ae); 2999e2c7d99SGeorge Cherian dev_dbg(dev, "mcode->num_cores = %u\n", mcode->num_cores); 3009e2c7d99SGeorge Cherian dev_dbg(dev, "mcode->code = %llx\n", (u64)mcode->code); 3019e2c7d99SGeorge Cherian dev_dbg(dev, "mcode->phys_base = %llx\n", mcode->phys_base); 3029e2c7d99SGeorge Cherian 3039e2c7d99SGeorge Cherian ret = do_cpt_init(cpt, mcode); 3049e2c7d99SGeorge Cherian if (ret) { 3059e2c7d99SGeorge Cherian dev_err(dev, "do_cpt_init failed with ret: %d\n", ret); 306baf5b752SCorentin LABBE goto fw_release; 3079e2c7d99SGeorge Cherian } 3089e2c7d99SGeorge Cherian 3099e2c7d99SGeorge Cherian dev_info(dev, "Microcode Loaded %s\n", mcode->version); 3109e2c7d99SGeorge Cherian mcode->is_mc_valid = 1; 3119e2c7d99SGeorge Cherian cpt->next_mc_idx++; 312baf5b752SCorentin LABBE 313baf5b752SCorentin LABBE fw_release: 3149e2c7d99SGeorge Cherian release_firmware(fw_entry); 3159e2c7d99SGeorge Cherian 3169e2c7d99SGeorge Cherian return ret; 3179e2c7d99SGeorge Cherian } 3189e2c7d99SGeorge Cherian 3199e2c7d99SGeorge Cherian static int cpt_ucode_load(struct cpt_device *cpt) 3209e2c7d99SGeorge Cherian { 3219e2c7d99SGeorge Cherian int ret = 0; 3229e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 3239e2c7d99SGeorge Cherian 3249e2c7d99SGeorge Cherian ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-ae.out", true); 3259e2c7d99SGeorge Cherian if (ret) { 3269e2c7d99SGeorge Cherian dev_err(dev, "ae:cpt_ucode_load failed with ret: %d\n", ret); 3279e2c7d99SGeorge Cherian return ret; 3289e2c7d99SGeorge Cherian } 3299e2c7d99SGeorge Cherian ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-se.out", false); 3309e2c7d99SGeorge Cherian if (ret) { 3319e2c7d99SGeorge Cherian dev_err(dev, "se:cpt_ucode_load failed with ret: %d\n", ret); 3329e2c7d99SGeorge Cherian return ret; 3339e2c7d99SGeorge Cherian } 3349e2c7d99SGeorge Cherian 3359e2c7d99SGeorge Cherian return ret; 3369e2c7d99SGeorge Cherian } 3379e2c7d99SGeorge Cherian 3389e2c7d99SGeorge Cherian static irqreturn_t cpt_mbx0_intr_handler(int irq, void *cpt_irq) 3399e2c7d99SGeorge Cherian { 3409e2c7d99SGeorge Cherian struct cpt_device *cpt = (struct cpt_device *)cpt_irq; 3419e2c7d99SGeorge Cherian 3429e2c7d99SGeorge Cherian cpt_mbox_intr_handler(cpt, 0); 3439e2c7d99SGeorge Cherian 3449e2c7d99SGeorge Cherian return IRQ_HANDLED; 3459e2c7d99SGeorge Cherian } 3469e2c7d99SGeorge Cherian 3479e2c7d99SGeorge Cherian static void cpt_reset(struct cpt_device *cpt) 3489e2c7d99SGeorge Cherian { 3499e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_RESET(0), 1); 3509e2c7d99SGeorge Cherian } 3519e2c7d99SGeorge Cherian 3529e2c7d99SGeorge Cherian static void cpt_find_max_enabled_cores(struct cpt_device *cpt) 3539e2c7d99SGeorge Cherian { 3549e2c7d99SGeorge Cherian union cptx_pf_constants pf_cnsts = {0}; 3559e2c7d99SGeorge Cherian 3569e2c7d99SGeorge Cherian pf_cnsts.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_CONSTANTS(0)); 3579e2c7d99SGeorge Cherian cpt->max_se_cores = pf_cnsts.s.se; 3589e2c7d99SGeorge Cherian cpt->max_ae_cores = pf_cnsts.s.ae; 3599e2c7d99SGeorge Cherian } 3609e2c7d99SGeorge Cherian 3619e2c7d99SGeorge Cherian static u32 cpt_check_bist_status(struct cpt_device *cpt) 3629e2c7d99SGeorge Cherian { 3639e2c7d99SGeorge Cherian union cptx_pf_bist_status bist_sts = {0}; 3649e2c7d99SGeorge Cherian 3659e2c7d99SGeorge Cherian bist_sts.u = cpt_read_csr64(cpt->reg_base, 3669e2c7d99SGeorge Cherian CPTX_PF_BIST_STATUS(0)); 3679e2c7d99SGeorge Cherian 3689e2c7d99SGeorge Cherian return bist_sts.u; 3699e2c7d99SGeorge Cherian } 3709e2c7d99SGeorge Cherian 3719e2c7d99SGeorge Cherian static u64 cpt_check_exe_bist_status(struct cpt_device *cpt) 3729e2c7d99SGeorge Cherian { 3739e2c7d99SGeorge Cherian union cptx_pf_exe_bist_status bist_sts = {0}; 3749e2c7d99SGeorge Cherian 3759e2c7d99SGeorge Cherian bist_sts.u = cpt_read_csr64(cpt->reg_base, 3769e2c7d99SGeorge Cherian CPTX_PF_EXE_BIST_STATUS(0)); 3779e2c7d99SGeorge Cherian 3789e2c7d99SGeorge Cherian return bist_sts.u; 3799e2c7d99SGeorge Cherian } 3809e2c7d99SGeorge Cherian 3819e2c7d99SGeorge Cherian static void cpt_disable_all_cores(struct cpt_device *cpt) 3829e2c7d99SGeorge Cherian { 3839e2c7d99SGeorge Cherian u32 grp, timeout = 100; 3849e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 3859e2c7d99SGeorge Cherian 3869e2c7d99SGeorge Cherian /* Disengage the cores from groups */ 3879e2c7d99SGeorge Cherian for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { 3889e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 0); 3899e2c7d99SGeorge Cherian udelay(CSR_DELAY); 3909e2c7d99SGeorge Cherian } 3919e2c7d99SGeorge Cherian 3929e2c7d99SGeorge Cherian grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); 3939e2c7d99SGeorge Cherian while (grp) { 3949e2c7d99SGeorge Cherian dev_err(dev, "Cores still busy"); 3959e2c7d99SGeorge Cherian grp = cpt_read_csr64(cpt->reg_base, 3969e2c7d99SGeorge Cherian CPTX_PF_EXEC_BUSY(0)); 3979e2c7d99SGeorge Cherian if (timeout--) 3989e2c7d99SGeorge Cherian break; 3999e2c7d99SGeorge Cherian 4009e2c7d99SGeorge Cherian udelay(CSR_DELAY); 4019e2c7d99SGeorge Cherian } 4029e2c7d99SGeorge Cherian /* Disable the cores */ 4039e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 0); 4049e2c7d99SGeorge Cherian } 4059e2c7d99SGeorge Cherian 406c4d7d318SLee Jones /* 4079e2c7d99SGeorge Cherian * Ensure all cores are disengaged from all groups by 4089e2c7d99SGeorge Cherian * calling cpt_disable_all_cores() before calling this 4099e2c7d99SGeorge Cherian * function. 4109e2c7d99SGeorge Cherian */ 4119e2c7d99SGeorge Cherian static void cpt_unload_microcode(struct cpt_device *cpt) 4129e2c7d99SGeorge Cherian { 4139e2c7d99SGeorge Cherian u32 grp = 0, core; 4149e2c7d99SGeorge Cherian 4159e2c7d99SGeorge Cherian /* Free microcode bases and reset group masks */ 4169e2c7d99SGeorge Cherian for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { 4179e2c7d99SGeorge Cherian struct microcode *mcode = &cpt->mcode[grp]; 4189e2c7d99SGeorge Cherian 4199e2c7d99SGeorge Cherian if (cpt->mcode[grp].code) 4209e2c7d99SGeorge Cherian dma_free_coherent(&cpt->pdev->dev, mcode->code_size, 4219e2c7d99SGeorge Cherian mcode->code, mcode->phys_base); 4229e2c7d99SGeorge Cherian mcode->code = NULL; 4239e2c7d99SGeorge Cherian } 4249e2c7d99SGeorge Cherian /* Clear UCODE_BASE registers for all engines */ 4259e2c7d99SGeorge Cherian for (core = 0; core < CPT_MAX_TOTAL_CORES; core++) 4269e2c7d99SGeorge Cherian cpt_write_csr64(cpt->reg_base, 4279e2c7d99SGeorge Cherian CPTX_PF_ENGX_UCODE_BASE(0, core), 0ull); 4289e2c7d99SGeorge Cherian } 4299e2c7d99SGeorge Cherian 4309e2c7d99SGeorge Cherian static int cpt_device_init(struct cpt_device *cpt) 4319e2c7d99SGeorge Cherian { 4329e2c7d99SGeorge Cherian u64 bist; 4339e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 4349e2c7d99SGeorge Cherian 4359e2c7d99SGeorge Cherian /* Reset the PF when probed first */ 4369e2c7d99SGeorge Cherian cpt_reset(cpt); 437e9acf052SJia-Ju Bai msleep(100); 4389e2c7d99SGeorge Cherian 4399e2c7d99SGeorge Cherian /*Check BIST status*/ 4409e2c7d99SGeorge Cherian bist = (u64)cpt_check_bist_status(cpt); 4419e2c7d99SGeorge Cherian if (bist) { 4429e2c7d99SGeorge Cherian dev_err(dev, "RAM BIST failed with code 0x%llx", bist); 4439e2c7d99SGeorge Cherian return -ENODEV; 4449e2c7d99SGeorge Cherian } 4459e2c7d99SGeorge Cherian 4469e2c7d99SGeorge Cherian bist = cpt_check_exe_bist_status(cpt); 4479e2c7d99SGeorge Cherian if (bist) { 4489e2c7d99SGeorge Cherian dev_err(dev, "Engine BIST failed with code 0x%llx", bist); 4499e2c7d99SGeorge Cherian return -ENODEV; 4509e2c7d99SGeorge Cherian } 4519e2c7d99SGeorge Cherian 4529e2c7d99SGeorge Cherian /*Get CLK frequency*/ 4539e2c7d99SGeorge Cherian /*Get max enabled cores */ 4549e2c7d99SGeorge Cherian cpt_find_max_enabled_cores(cpt); 4559e2c7d99SGeorge Cherian /*Disable all cores*/ 4569e2c7d99SGeorge Cherian cpt_disable_all_cores(cpt); 4579e2c7d99SGeorge Cherian /*Reset device parameters*/ 4589e2c7d99SGeorge Cherian cpt->next_mc_idx = 0; 4599e2c7d99SGeorge Cherian cpt->next_group = 0; 4609e2c7d99SGeorge Cherian /* PF is ready */ 4619e2c7d99SGeorge Cherian cpt->flags |= CPT_FLAG_DEVICE_READY; 4629e2c7d99SGeorge Cherian 4639e2c7d99SGeorge Cherian return 0; 4649e2c7d99SGeorge Cherian } 4659e2c7d99SGeorge Cherian 4669e2c7d99SGeorge Cherian static int cpt_register_interrupts(struct cpt_device *cpt) 4679e2c7d99SGeorge Cherian { 4689e2c7d99SGeorge Cherian int ret; 4699e2c7d99SGeorge Cherian struct device *dev = &cpt->pdev->dev; 4709e2c7d99SGeorge Cherian 4719e2c7d99SGeorge Cherian /* Enable MSI-X */ 472613844e8SChristoph Hellwig ret = pci_alloc_irq_vectors(cpt->pdev, CPT_PF_MSIX_VECTORS, 473613844e8SChristoph Hellwig CPT_PF_MSIX_VECTORS, PCI_IRQ_MSIX); 474613844e8SChristoph Hellwig if (ret < 0) { 475613844e8SChristoph Hellwig dev_err(&cpt->pdev->dev, "Request for #%d msix vectors failed\n", 476613844e8SChristoph Hellwig CPT_PF_MSIX_VECTORS); 4779e2c7d99SGeorge Cherian return ret; 478613844e8SChristoph Hellwig } 4799e2c7d99SGeorge Cherian 4809e2c7d99SGeorge Cherian /* Register mailbox interrupt handlers */ 481613844e8SChristoph Hellwig ret = request_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), 4829e2c7d99SGeorge Cherian cpt_mbx0_intr_handler, 0, "CPT Mbox0", cpt); 4839e2c7d99SGeorge Cherian if (ret) 4849e2c7d99SGeorge Cherian goto fail; 4859e2c7d99SGeorge Cherian 4869e2c7d99SGeorge Cherian /* Enable mailbox interrupt */ 4879e2c7d99SGeorge Cherian cpt_enable_mbox_interrupts(cpt); 4889e2c7d99SGeorge Cherian return 0; 4899e2c7d99SGeorge Cherian 4909e2c7d99SGeorge Cherian fail: 4919e2c7d99SGeorge Cherian dev_err(dev, "Request irq failed\n"); 492613844e8SChristoph Hellwig pci_disable_msix(cpt->pdev); 4939e2c7d99SGeorge Cherian return ret; 4949e2c7d99SGeorge Cherian } 4959e2c7d99SGeorge Cherian 4969e2c7d99SGeorge Cherian static void cpt_unregister_interrupts(struct cpt_device *cpt) 4979e2c7d99SGeorge Cherian { 498613844e8SChristoph Hellwig free_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), cpt); 499613844e8SChristoph Hellwig pci_disable_msix(cpt->pdev); 5009e2c7d99SGeorge Cherian } 5019e2c7d99SGeorge Cherian 5029e2c7d99SGeorge Cherian static int cpt_sriov_init(struct cpt_device *cpt, int num_vfs) 5039e2c7d99SGeorge Cherian { 5049e2c7d99SGeorge Cherian int pos = 0; 5059e2c7d99SGeorge Cherian int err; 5069e2c7d99SGeorge Cherian u16 total_vf_cnt; 5079e2c7d99SGeorge Cherian struct pci_dev *pdev = cpt->pdev; 5089e2c7d99SGeorge Cherian 5099e2c7d99SGeorge Cherian pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); 5109e2c7d99SGeorge Cherian if (!pos) { 5119e2c7d99SGeorge Cherian dev_err(&pdev->dev, "SRIOV capability is not found in PCIe config space\n"); 5129e2c7d99SGeorge Cherian return -ENODEV; 5139e2c7d99SGeorge Cherian } 5149e2c7d99SGeorge Cherian 5159e2c7d99SGeorge Cherian cpt->num_vf_en = num_vfs; /* User requested VFs */ 5169e2c7d99SGeorge Cherian pci_read_config_word(pdev, (pos + PCI_SRIOV_TOTAL_VF), &total_vf_cnt); 5179e2c7d99SGeorge Cherian if (total_vf_cnt < cpt->num_vf_en) 5189e2c7d99SGeorge Cherian cpt->num_vf_en = total_vf_cnt; 5199e2c7d99SGeorge Cherian 5209e2c7d99SGeorge Cherian if (!total_vf_cnt) 5219e2c7d99SGeorge Cherian return 0; 5229e2c7d99SGeorge Cherian 5239e2c7d99SGeorge Cherian /*Enabled the available VFs */ 5249e2c7d99SGeorge Cherian err = pci_enable_sriov(pdev, cpt->num_vf_en); 5259e2c7d99SGeorge Cherian if (err) { 5269e2c7d99SGeorge Cherian dev_err(&pdev->dev, "SRIOV enable failed, num VF is %d\n", 5279e2c7d99SGeorge Cherian cpt->num_vf_en); 5289e2c7d99SGeorge Cherian cpt->num_vf_en = 0; 5299e2c7d99SGeorge Cherian return err; 5309e2c7d99SGeorge Cherian } 5319e2c7d99SGeorge Cherian 5329e2c7d99SGeorge Cherian /* TODO: Optionally enable static VQ priorities feature */ 5339e2c7d99SGeorge Cherian 5349e2c7d99SGeorge Cherian dev_info(&pdev->dev, "SRIOV enabled, number of VF available %d\n", 5359e2c7d99SGeorge Cherian cpt->num_vf_en); 5369e2c7d99SGeorge Cherian 5379e2c7d99SGeorge Cherian cpt->flags |= CPT_FLAG_SRIOV_ENABLED; 5389e2c7d99SGeorge Cherian 5399e2c7d99SGeorge Cherian return 0; 5409e2c7d99SGeorge Cherian } 5419e2c7d99SGeorge Cherian 5429e2c7d99SGeorge Cherian static int cpt_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 5439e2c7d99SGeorge Cherian { 5449e2c7d99SGeorge Cherian struct device *dev = &pdev->dev; 5459e2c7d99SGeorge Cherian struct cpt_device *cpt; 5469e2c7d99SGeorge Cherian int err; 5479e2c7d99SGeorge Cherian 5489e2c7d99SGeorge Cherian if (num_vfs > 16 || num_vfs < 4) { 5499e2c7d99SGeorge Cherian dev_warn(dev, "Invalid vf count %d, Resetting it to 4(default)\n", 5509e2c7d99SGeorge Cherian num_vfs); 5519e2c7d99SGeorge Cherian num_vfs = 4; 5529e2c7d99SGeorge Cherian } 5539e2c7d99SGeorge Cherian 5549e2c7d99SGeorge Cherian cpt = devm_kzalloc(dev, sizeof(*cpt), GFP_KERNEL); 5559e2c7d99SGeorge Cherian if (!cpt) 5569e2c7d99SGeorge Cherian return -ENOMEM; 5579e2c7d99SGeorge Cherian 5589e2c7d99SGeorge Cherian pci_set_drvdata(pdev, cpt); 5599e2c7d99SGeorge Cherian cpt->pdev = pdev; 5609e2c7d99SGeorge Cherian err = pci_enable_device(pdev); 5619e2c7d99SGeorge Cherian if (err) { 5629e2c7d99SGeorge Cherian dev_err(dev, "Failed to enable PCI device\n"); 5639e2c7d99SGeorge Cherian pci_set_drvdata(pdev, NULL); 5649e2c7d99SGeorge Cherian return err; 5659e2c7d99SGeorge Cherian } 5669e2c7d99SGeorge Cherian 5679e2c7d99SGeorge Cherian err = pci_request_regions(pdev, DRV_NAME); 5689e2c7d99SGeorge Cherian if (err) { 5699e2c7d99SGeorge Cherian dev_err(dev, "PCI request regions failed 0x%x\n", err); 5709e2c7d99SGeorge Cherian goto cpt_err_disable_device; 5719e2c7d99SGeorge Cherian } 5729e2c7d99SGeorge Cherian 573aeb4d8c0SChristophe JAILLET err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); 5749e2c7d99SGeorge Cherian if (err) { 575aeb4d8c0SChristophe JAILLET dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); 5769e2c7d99SGeorge Cherian goto cpt_err_release_regions; 5779e2c7d99SGeorge Cherian } 5789e2c7d99SGeorge Cherian 5799e2c7d99SGeorge Cherian /* MAP PF's configuration registers */ 5809e2c7d99SGeorge Cherian cpt->reg_base = pcim_iomap(pdev, 0, 0); 5819e2c7d99SGeorge Cherian if (!cpt->reg_base) { 5829e2c7d99SGeorge Cherian dev_err(dev, "Cannot map config register space, aborting\n"); 5839e2c7d99SGeorge Cherian err = -ENOMEM; 5849e2c7d99SGeorge Cherian goto cpt_err_release_regions; 5859e2c7d99SGeorge Cherian } 5869e2c7d99SGeorge Cherian 5879e2c7d99SGeorge Cherian /* CPT device HW initialization */ 5889e2c7d99SGeorge Cherian cpt_device_init(cpt); 5899e2c7d99SGeorge Cherian 5909e2c7d99SGeorge Cherian /* Register interrupts */ 5919e2c7d99SGeorge Cherian err = cpt_register_interrupts(cpt); 5929e2c7d99SGeorge Cherian if (err) 5939e2c7d99SGeorge Cherian goto cpt_err_release_regions; 5949e2c7d99SGeorge Cherian 5959e2c7d99SGeorge Cherian err = cpt_ucode_load(cpt); 5969e2c7d99SGeorge Cherian if (err) 5979e2c7d99SGeorge Cherian goto cpt_err_unregister_interrupts; 5989e2c7d99SGeorge Cherian 5999e2c7d99SGeorge Cherian /* Configure SRIOV */ 6009e2c7d99SGeorge Cherian err = cpt_sriov_init(cpt, num_vfs); 6019e2c7d99SGeorge Cherian if (err) 6029e2c7d99SGeorge Cherian goto cpt_err_unregister_interrupts; 6039e2c7d99SGeorge Cherian 6049e2c7d99SGeorge Cherian return 0; 6059e2c7d99SGeorge Cherian 6069e2c7d99SGeorge Cherian cpt_err_unregister_interrupts: 6079e2c7d99SGeorge Cherian cpt_unregister_interrupts(cpt); 6089e2c7d99SGeorge Cherian cpt_err_release_regions: 6099e2c7d99SGeorge Cherian pci_release_regions(pdev); 6109e2c7d99SGeorge Cherian cpt_err_disable_device: 6119e2c7d99SGeorge Cherian pci_disable_device(pdev); 6129e2c7d99SGeorge Cherian pci_set_drvdata(pdev, NULL); 6139e2c7d99SGeorge Cherian return err; 6149e2c7d99SGeorge Cherian } 6159e2c7d99SGeorge Cherian 6169e2c7d99SGeorge Cherian static void cpt_remove(struct pci_dev *pdev) 6179e2c7d99SGeorge Cherian { 6189e2c7d99SGeorge Cherian struct cpt_device *cpt = pci_get_drvdata(pdev); 6199e2c7d99SGeorge Cherian 6209e2c7d99SGeorge Cherian /* Disengage SE and AE cores from all groups*/ 6219e2c7d99SGeorge Cherian cpt_disable_all_cores(cpt); 6229e2c7d99SGeorge Cherian /* Unload microcodes */ 6239e2c7d99SGeorge Cherian cpt_unload_microcode(cpt); 6249e2c7d99SGeorge Cherian cpt_unregister_interrupts(cpt); 6259e2c7d99SGeorge Cherian pci_disable_sriov(pdev); 6269e2c7d99SGeorge Cherian pci_release_regions(pdev); 6279e2c7d99SGeorge Cherian pci_disable_device(pdev); 6289e2c7d99SGeorge Cherian pci_set_drvdata(pdev, NULL); 6299e2c7d99SGeorge Cherian } 6309e2c7d99SGeorge Cherian 6319e2c7d99SGeorge Cherian static void cpt_shutdown(struct pci_dev *pdev) 6329e2c7d99SGeorge Cherian { 6339e2c7d99SGeorge Cherian struct cpt_device *cpt = pci_get_drvdata(pdev); 6349e2c7d99SGeorge Cherian 6359e2c7d99SGeorge Cherian if (!cpt) 6369e2c7d99SGeorge Cherian return; 6379e2c7d99SGeorge Cherian 6389e2c7d99SGeorge Cherian dev_info(&pdev->dev, "Shutdown device %x:%x.\n", 6399e2c7d99SGeorge Cherian (u32)pdev->vendor, (u32)pdev->device); 6409e2c7d99SGeorge Cherian 6419e2c7d99SGeorge Cherian cpt_unregister_interrupts(cpt); 6429e2c7d99SGeorge Cherian pci_release_regions(pdev); 6439e2c7d99SGeorge Cherian pci_disable_device(pdev); 6449e2c7d99SGeorge Cherian pci_set_drvdata(pdev, NULL); 6459e2c7d99SGeorge Cherian } 6469e2c7d99SGeorge Cherian 6479e2c7d99SGeorge Cherian /* Supported devices */ 6489e2c7d99SGeorge Cherian static const struct pci_device_id cpt_id_table[] = { 6499e2c7d99SGeorge Cherian { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CPT_81XX_PCI_PF_DEVICE_ID) }, 6509e2c7d99SGeorge Cherian { 0, } /* end of table */ 6519e2c7d99SGeorge Cherian }; 6529e2c7d99SGeorge Cherian 6539e2c7d99SGeorge Cherian static struct pci_driver cpt_pci_driver = { 6549e2c7d99SGeorge Cherian .name = DRV_NAME, 6559e2c7d99SGeorge Cherian .id_table = cpt_id_table, 6569e2c7d99SGeorge Cherian .probe = cpt_probe, 6579e2c7d99SGeorge Cherian .remove = cpt_remove, 6589e2c7d99SGeorge Cherian .shutdown = cpt_shutdown, 6599e2c7d99SGeorge Cherian }; 6609e2c7d99SGeorge Cherian 6619e2c7d99SGeorge Cherian module_pci_driver(cpt_pci_driver); 6629e2c7d99SGeorge Cherian 6639e2c7d99SGeorge Cherian MODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); 6649e2c7d99SGeorge Cherian MODULE_DESCRIPTION("Cavium Thunder CPT Physical Function Driver"); 6659e2c7d99SGeorge Cherian MODULE_LICENSE("GPL v2"); 6669e2c7d99SGeorge Cherian MODULE_VERSION(DRV_VERSION); 6679e2c7d99SGeorge Cherian MODULE_DEVICE_TABLE(pci, cpt_id_table); 668