/**************************************************************************** * * Copyright (c) 2014 - 2021 Samsung Electronics Co., Ltd. All rights reserved * ****************************************************************************/ /* Implements */ #include "pcie_mif.h" /* Uses */ #include #include #include #include #include #include #include #include #include #include "pcie_mif_module.h" #include "pcie_proc.h" #include "pcie_mbox.h" #include "pmu_paean.h" #include "pmu_patch_paean.h" #include "mif_reg_paean_pcie.h" #include "mifproc.h" #include #define PCIE_MIF_RESET_REQUEST_SOURCE 31 struct pcie_mif *g_pcie; struct scsc_mif_abs *g_if; bool global_enable; static bool fw_compiled_in_kernel; module_param(fw_compiled_in_kernel, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(fw_compiled_in_kernel, "Use FW compiled in kernel"); static void *pcie_mif_map(struct scsc_mif_abs *interface, size_t *allocated); static int pcie_mif_reset(struct scsc_mif_abs *interface, bool reset); static int pcie_mif_set_mbox_pmu(struct scsc_mif_abs *interface, u32 val); /* Module parameters */ static void pcie_mif_unmap(struct scsc_mif_abs *interface, void *mem); static bool enable_pcie_mif_arm_reset = true; module_param(enable_pcie_mif_arm_reset, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(enable_pcie_mif_arm_reset, "Enables ARM cores reset"); #define NUM_TO_HOST_IRQ_PMU 1 #define NUM_TO_HOST_IRQ_WLAN 16 #define NUM_TO_HOST_IRQ_WPAN 10 #define NUM_TO_HOST_IRQ_PMU_START 0 #define NUM_TO_HOST_IRQ_PMU_END ((NUM_TO_HOST_IRQ_PMU_START + NUM_TO_HOST_IRQ_PMU) - 1) #define NUM_TO_HOST_IRQ_WLAN_START (NUM_TO_HOST_IRQ_PMU_END + 1) #define NUM_TO_HOST_IRQ_WLAN_END ((NUM_TO_HOST_IRQ_WLAN_START + NUM_TO_HOST_IRQ_WLAN) - 1) #define NUM_TO_HOST_IRQ_WPAN_START (NUM_TO_HOST_IRQ_WLAN_END + 1) #define NUM_TO_HOST_IRQ_WPAN_END ((NUM_TO_HOST_IRQ_WPAN_START + NUM_TO_HOST_IRQ_WPAN) - 1) #define NUM_TO_HOST_IRQ_TOTAL (NUM_TO_HOST_IRQ_PMU + NUM_TO_HOST_IRQ_WLAN + NUM_TO_HOST_IRQ_WPAN) /* Types */ struct msi_emul { u8 msi_bit; u32 msi_vec; bool is_masked; }; struct pcie_mif { struct scsc_mif_abs interface; struct pci_dev *pdev; int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */ __iomem void *registers_pert; __iomem void *registers_ramrp; __iomem void *registers_msix; struct device *dev; bool in_reset; void *mem; /* DMA memory mapped to PCIe space for MX-AP comms */ struct pcie_mbox mbox; /* mailbox emulation */ size_t mem_allocated; dma_addr_t dma_addr; /* Callback function and dev pointer mif_intr manager handler */ void (*wlan_handler)(int irq, void *data); void (*wpan_handler)(int irq, void *data); void *irq_dev; /* spinlock to serialize driver access */ spinlock_t mif_spinlock; spinlock_t mask_spinlock; /* Reset Request handler and context */ void (*reset_request_handler)(int irq_num_ignored, void *data); void *reset_request_handler_data; enum wlbt_boot_state { WLBT_BOOT_IN_RESET = 0, WLBT_BOOT_WAIT_CFG_REQ, WLBT_BOOT_ACK_CFG_REQ, WLBT_BOOT_CFG_DONE, WLBT_BOOT_CFG_ERROR } boot_state; int *ka_patch_fw; size_t ka_patch_len; /* Callback function and dev pointer mif_intr manager handler */ void (*pmu_handler)(int irq, void *data); void *irq_dev_pmu; uintptr_t remap_addr_wlan; uintptr_t remap_addr_wpan; struct msi_emul pmu_msi[NUM_TO_HOST_IRQ_PMU]; struct msi_emul wlan_msi[NUM_TO_HOST_IRQ_WLAN]; struct msi_emul wpan_msi[NUM_TO_HOST_IRQ_WPAN]; u32 rcv_irq_wlan; u32 rcv_irq_wpan; }; /* Private Macros */ /** Upcast from interface member to pcie_mif */ #define pcie_mif_from_mif_abs(MIF_ABS_PTR) container_of(MIF_ABS_PTR, struct pcie_mif, interface) static inline void pcie_mif_reg_write(struct pcie_mif *pcie, u32 offset, u32 value) { writel(value, pcie->registers_ramrp + offset); } static inline u32 pcie_mif_reg_read(struct pcie_mif *pcie, u32 offset) { return readl(pcie->registers_ramrp + offset); } static void pcie_mif_irq_default_handler(int irq, void *data) { /* Avoid unused parameter error */ (void)irq; (void)data; } static void pcie_mif_irq_reset_request_default_handler(int irq, void *data) { /* Avoid unused parameter error */ (void)irq; (void)data; /* int handler not registered */ SCSC_TAG_INFO_DEV(PCIE_MIF, NULL, "INT reset_request handler not registered\n"); } irqreturn_t pcie_mif_pmu_isr(int irq, void *data) { struct pcie_mif *pcie = (struct pcie_mif *)data; SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "INT received, boot_state = %u\n", pcie->boot_state); /* pcie->boot_state = WLBT_BOOT_CFG_DONE; */ if (pcie->pmu_handler != pcie_mif_irq_default_handler) pcie->pmu_handler(irq, pcie->irq_dev_pmu); else SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "MIF PMU Interrupt Handler not registered\n"); return IRQ_HANDLED; } irqreturn_t pcie_mif_isr_wlan(int irq, void *data) { struct pcie_mif *pcie = (struct pcie_mif *)data; u8 i; unsigned long flags; spin_lock_irqsave(&pcie->mif_spinlock, flags); /* Set the rcv_irq before calling the handler! */ pcie->rcv_irq_wlan = 0; /* get MSI bit correspin to line */ for (i = 0; i < NUM_TO_HOST_IRQ_WLAN; i++) { if (pcie->wlan_msi[i].msi_vec == irq) pcie->rcv_irq_wlan = pcie->wlan_msi[i].msi_bit; } SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "IN WLAN ISR!!!!!!!!!!!!!!!!! line %d msi_bit %u cpu %d", irq, pcie->rcv_irq_wlan, smp_processor_id()); #if 0 /* not yet */ /* We need to disable this IRQ line based on MBOX emulation */ disable_irq_nosync(irq); #endif if (pcie->wlan_handler != pcie_mif_irq_default_handler) { pcie->wlan_handler(irq, pcie->irq_dev); } else SCSC_TAG_ERR_DEV(PLAT_MIF, pcie->dev, "MIF Interrupt Handler not registered.\n"); spin_unlock_irqrestore(&pcie->mif_spinlock, flags); return IRQ_HANDLED; } irqreturn_t pcie_mif_isr_wpan(int irq, void *data) { struct pcie_mif *pcie = (struct pcie_mif *)data; u8 i; unsigned long flags; spin_lock_irqsave(&pcie->mif_spinlock, flags); /* Set the rcv_irq before calling the handler! */ pcie->rcv_irq_wlan = 0; /* get MSI bit correspin to line */ for (i = 0; i < NUM_TO_HOST_IRQ_WPAN; i++) { if (pcie->wpan_msi[i].msi_vec == irq) pcie->rcv_irq_wpan = pcie->wpan_msi[i].msi_bit; } SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "IN WPAN ISR!!!!!!!!!!!!!!!!! line %d msi_bit %u", irq, pcie->rcv_irq_wpan); #if 0 /* not yet */ /* We need to disable this IRQ line based on MBOX emulation */ disable_irq_nosync(irq); #endif if (pcie->wpan_handler != pcie_mif_irq_default_handler) pcie->wpan_handler(irq, pcie->irq_dev); else SCSC_TAG_ERR_DEV(PLAT_MIF, pcie->dev, "MIF Interrupt Handler not registered.\n"); spin_unlock_irqrestore(&pcie->mif_spinlock, flags); return IRQ_HANDLED; } static void pcie_mif_destroy(struct scsc_mif_abs *interface) { } static char *pcie_mif_get_uid(struct scsc_mif_abs *interface) { /* Avoid unused parameter error */ (void)interface; /* TODO */ /* return "0" for the time being */ return "0"; } static void pcie_set_atu(struct pcie_mif *pcie) { pcie_atu_config_t atu; /* Send ATU mappings */ atu.start_addr_0 = pcie->dma_addr; atu.end_addr_0 = pcie->dma_addr + PCIE_MIF_PREALLOC_MEM; SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Copy ATU offset 0x%x start 0x%lx end 0x%lx\n", RAMRP_HOSTIF_PMU_BUF_PTR, atu.start_addr_0, atu.end_addr_0); memcpy_toio(pcie->registers_ramrp + RAMRP_HOSTIF_PMU_BUF_PTR, &atu, sizeof(pcie_atu_config_t)); msleep(1); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Write PMU_HOSTIF_MBOX_REQ_PCIE_ATU_CONFIG at offset %x\n", RAMRP_HOSTIF_PMU_MBOX_FROM_HOST_PTR); writel(PMU_HOSTIF_MBOX_REQ_PCIE_ATU_CONFIG, pcie->registers_ramrp + RAMRP_HOSTIF_PMU_MBOX_FROM_HOST_PTR); msleep(1); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "TRIGGER IRQ 0x2: PMU_INTERRUPT_FROM_HOST_INTGR\n"); writel(0x2, pcie->registers_pert + PMU_INTERRUPT_FROM_HOST_INTGR); msleep(1); } static void pcie_set_remappers(struct pcie_mif *pcie) { u32 cmd_dw; /* Set remmapers */ cmd_dw = readl(pcie->registers_pert + PMU_WLAN_UCPU0_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WLAN_UCPU0_RMP_BOOT_ADDR 0x%x\n", cmd_dw); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "write PMU_WLAN_UCPU0_RMP_BOOT_ADDR 0x%x\n", (pcie->remap_addr_wlan >> 12)); writel((pcie->remap_addr_wlan >> 12), pcie->registers_pert + PMU_WLAN_UCPU0_RMP_BOOT_ADDR); cmd_dw = readl(pcie->registers_pert + PMU_WLAN_UCPU0_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WLAN_UCPU0_RMP_BOOT_ADDR 0x%x\n", cmd_dw); cmd_dw = readl(pcie->registers_pert + PMU_WLAN_UCPU1_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WLAN_UCPU1_RMP_BOOT_ADDR 0x%x\n", cmd_dw); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "write PMU_WLAN_UCPU1_RMP_BOOT_ADDR 0x%x\n", (pcie->remap_addr_wlan >> 12)); writel((pcie->remap_addr_wlan >> 12), pcie->registers_pert + PMU_WLAN_UCPU1_RMP_BOOT_ADDR); cmd_dw = readl(pcie->registers_pert + PMU_WLAN_UCPU1_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WLAN_UCPU1_RMP_BOOT_ADDR 0x%x\n", cmd_dw); cmd_dw = readl(pcie->registers_pert + PMU_WPAN_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WPAN_RMP_BOOT_ADDR 0x%x\n", cmd_dw); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "write PMU_WPAN_RMP_BOOT_ADDR 0x%x\n", (pcie->remap_addr_wpan >> 12)); writel((pcie->remap_addr_wpan >> 12), pcie->registers_pert + PMU_WPAN_RMP_BOOT_ADDR); cmd_dw = readl(pcie->registers_pert + PMU_WPAN_RMP_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "read PMU_WPAN_RMP_BOOT_ADDR 0x%x\n", cmd_dw); } static void pcie_load_pmu(struct pcie_mif *pcie) { uint32_t *ka_patch_addr; size_t ka_patch_len; u32 cmd_dw; if (pcie->ka_patch_fw && !fw_compiled_in_kernel) { SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "ka_patch present in FW image\n"); ka_patch_addr = pcie->ka_patch_fw; ka_patch_len = pcie->ka_patch_len; } else { SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "ka_patch not present in FW image. ARRAY_SIZE %d Use default\n", ARRAY_SIZE(ka_patch)); ka_patch_addr = &ka_patch[0]; ka_patch_len = ARRAY_SIZE(ka_patch) * sizeof(uint32_t); } SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "KA patch download start\n"); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "ka_patch_len: 0x%x\n", ka_patch_len); memcpy_toio(pcie->registers_ramrp + PMU_BOOT_RAM_START_ADDR, ka_patch_addr, ka_patch_len); msleep(100); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "KA patch done\n"); wmb(); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "End GFG\n"); writel(SECOND_STAGE_BOOT_ADDR, pcie->registers_pert + PMU_SECOND_STAGE_BOOT_ADDR); msleep(100); cmd_dw = readl(pcie->registers_pert + PMU_SECOND_STAGE_BOOT_ADDR); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "PMU_SECOND_STAGE_BOOT_ADDR 0x%x\n", cmd_dw); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "TRIGGER IRQ 0x1: PMU_INTERRUPT_FROM_HOST_INTGR\n"); writel(0x1, pcie->registers_pert + PMU_INTERRUPT_FROM_HOST_INTGR); msleep(100); pcie_set_atu(pcie); pcie_set_remappers(pcie); pcie->boot_state = WLBT_BOOT_CFG_DONE; } static bool already_done; static int pcie_mif_reset(struct scsc_mif_abs *interface, bool reset) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); if (enable_pcie_mif_arm_reset || !reset) { /* Sanity check */ if (!reset && pcie->in_reset == true) { if (already_done == false) { pcie_load_pmu(pcie); already_done = true; } else { pcie_set_atu(pcie); pcie_set_remappers(pcie); } pcie->in_reset = false; } else if (pcie->in_reset == false) { pcie->in_reset = true; } } else SCSC_TAG_INFO(PCIE_MIF, "Not resetting ARM Cores enable_pcie_mif_arm_reset: %d\n", enable_pcie_mif_arm_reset); return 0; } static void *pcie_mif_map(struct scsc_mif_abs *interface, size_t *allocated) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); size_t map_len = PCIE_MIF_ALLOC_MEM; if (pcie->mem_allocated) { SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Do not allocate again\n"); *allocated = map_len; return pcie->mem; } if (allocated) *allocated = 0; pcie->mem_allocated = 0; /* should return PAGE_ALIGN Memory */ SCSC_TAG_ERR(PCIE_MIF, "Allocate %d DMA memory\n", PCIE_MIF_PREALLOC_MEM); pcie->mem = dma_alloc_coherent(pcie->dev, PCIE_MIF_PREALLOC_MEM, &pcie->dma_addr, GFP_KERNEL); if (pcie->mem == NULL) { SCSC_TAG_ERR(PCIE_MIF, "Error allocating %d DMA memory\n", PCIE_MIF_PREALLOC_MEM); return 0; } SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Allocated dma coherent vmem: %x phy addr %x\n", pcie->mem, (void *)pcie->dma_addr); /* Return the max allocatable memory on this abs. implementation */ if (allocated) *allocated = map_len; pcie->mem_allocated = map_len; return pcie->mem; } /* HERE: Not sure why mem is passed in - its stored in pcie - as it should be */ static void pcie_mif_unmap(struct scsc_mif_abs *interface, void *mem) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Do not unmap.. but memset to 0\n"); memset(pcie->mem, 0, PCIE_MIF_PREALLOC_MEM); return; /* Avoid unused parameter error */ (void)mem; if (pcie->mem_allocated) { pcie->mem_allocated = 0; dma_free_coherent(pcie->dev, PCIE_MIF_PREALLOC_MEM, pcie->mem, pcie->dma_addr); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Freed dma coherent mem: %x addr %x\n", pcie->mem, (void *)pcie->dma_addr); } } static u32 pcie_mif_irq_bit_mask_status_get(struct scsc_mif_abs *interface, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); u32 val = 0; SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Mask status get not implemented\n"); return val; } static u32 pcie_mif_irq_get(struct scsc_mif_abs *interface, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); u32 val = 0; u32 bit; if (target == SCSC_MIF_ABS_TARGET_WLAN) { bit = pcie->rcv_irq_wlan; } else { bit = pcie->rcv_irq_wpan; } val = 1 << bit; /* Function has to return the interrupts that are enabled *AND* not masked */ SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "Bit %u get val 0x%x\n", bit, val); return val; } static void pcie_mif_irq_bit_clear(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "Bit Clear %u subsystem %d\n", bit_num, target); #if 0 /* not yet */ if (target == SCSC_MIF_ABS_TARGET_WLAN) { if (pcie->wlan_msi[bit_num].is_masked == false) { enable_irq(pcie->wlan_msi[bit_num].msi_vec); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Enable IRQ %d\n", pcie->wlan_msi[bit_num].msi_vec); } } else if (pcie->wpan_msi[bit_num].is_masked == false) { enable_irq(pcie->wpan_msi[bit_num].msi_vec); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Enable IRQ %d\n", pcie->wpan_msi[bit_num].msi_vec); } #endif return; } static void pcie_mif_irq_bit_mask(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); unsigned long flags; unsigned int logic_bit_num; /* bit_num is MSI, so we need to modify to logical */ spin_lock_irqsave(&pcie->mask_spinlock, flags); if (target == SCSC_MIF_ABS_TARGET_WLAN) { logic_bit_num = bit_num - pcie->wlan_msi[0].msi_bit; if (logic_bit_num >= NUM_TO_HOST_IRQ_WLAN) goto end; if (pcie->wlan_msi[logic_bit_num].is_masked) { SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Mask %u subsystem %d line %d. Already masked ignore request\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); goto end; } pcie->wlan_msi[logic_bit_num].is_masked = true; disable_irq_nosync(pcie->wlan_msi[logic_bit_num].msi_vec); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Mask %u subsystem %d line %d\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); } else { logic_bit_num = bit_num - pcie->wpan_msi[0].msi_bit; if (logic_bit_num >= NUM_TO_HOST_IRQ_WPAN) goto end; if (pcie->wpan_msi[logic_bit_num].is_masked) { SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Mask %u subsystem %d line %d. Already masked ignore request\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); goto end; } pcie->wpan_msi[logic_bit_num].is_masked = true; disable_irq_nosync(pcie->wpan_msi[logic_bit_num].msi_vec); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Mask %u subsystem %d line %d\n", bit_num, target, pcie->wpan_msi[logic_bit_num].msi_vec); } end: spin_unlock_irqrestore(&pcie->mask_spinlock, flags); return; } static void pcie_mif_irq_bit_unmask(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); unsigned long flags; unsigned int logic_bit_num; /* bit_num is MSI, so we need to modify to logical */ spin_lock_irqsave(&pcie->mask_spinlock, flags); if (target == SCSC_MIF_ABS_TARGET_WLAN) { logic_bit_num = bit_num - pcie->wlan_msi[0].msi_bit; if (logic_bit_num >= NUM_TO_HOST_IRQ_WLAN) goto end; if (!pcie->wlan_msi[logic_bit_num].is_masked) { SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Mask %u subsystem %d line %d. Already enabled ignore request\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); goto end; } pcie->wlan_msi[logic_bit_num].is_masked = false; enable_irq(pcie->wlan_msi[logic_bit_num].msi_vec); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Unmask %u subsystem %d line %d\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); } else { logic_bit_num = bit_num - pcie->wpan_msi[0].msi_bit; if (logic_bit_num >= NUM_TO_HOST_IRQ_WPAN) goto end; if (!pcie->wpan_msi[logic_bit_num].is_masked) { SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Unmask %u subsystem %d line %d. Already enabled ignore request\n", bit_num, target, pcie->wlan_msi[logic_bit_num].msi_vec); goto end; } pcie->wpan_msi[logic_bit_num].is_masked = false; enable_irq(pcie->wpan_msi[logic_bit_num].msi_vec); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "MSI Bit Unmask %u subsystem %d line %d\n", bit_num, target, pcie->wpan_msi[logic_bit_num].msi_vec); } end: spin_unlock_irqrestore(&pcie->mask_spinlock, flags); return; } static void pcie_mif_remap_set(struct scsc_mif_abs *interface, uintptr_t remap_addr, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Setting remapper address %u target %s\n", remap_addr, (target == SCSC_MIF_ABS_TARGET_WPAN) ? "WPAN" : "WLAN"); if (target == SCSC_MIF_ABS_TARGET_WLAN) pcie->remap_addr_wlan = remap_addr + DRAM_BASE_ADDR; else if (target == SCSC_MIF_ABS_TARGET_WPAN) pcie->remap_addr_wpan = remap_addr + DRAM_BASE_ADDR; else SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Incorrect target %d\n", target); } static void pcie_mif_irq_bit_set(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); u32 target_off = WLAN_INTERRUPT_FROM_HOST_PENDING_BIT_ARRAY_SET; SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "Setting INT from host: bit %d on target %d\n", bit_num, target); if (bit_num >= NUM_TO_HOST_IRQ_TOTAL) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Incorrect INT number: %d\n", bit_num); return; } if (target == SCSC_MIF_ABS_TARGET_WLAN) target_off = WLAN_INTERRUPT_FROM_HOST_PENDING_BIT_ARRAY_SET; else target_off = WPAN_INTERRUPT_FROM_HOST_INTGR; writel(1 << bit_num, pcie->registers_pert + target_off); } static void pcie_mif_irq_reg_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); pcie->wlan_handler = handler; pcie->irq_dev = dev; } static void pcie_mif_irq_unreg_handler(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); pcie->wlan_handler = pcie_mif_irq_default_handler; pcie->irq_dev = NULL; } static void pcie_mif_get_msi_range(struct scsc_mif_abs *interface, u8 *start, u8 *end, enum scsc_mif_abs_target target) { if (target == SCSC_MIF_ABS_TARGET_WLAN) { *start = NUM_TO_HOST_IRQ_WLAN_START; *end = NUM_TO_HOST_IRQ_WLAN_END; } else { *start = NUM_TO_HOST_IRQ_WPAN_START; *end = NUM_TO_HOST_IRQ_WPAN_END; } } static void pcie_mif_irq_reg_handler_wpan(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); pcie->wpan_handler = handler; pcie->irq_dev = dev; } static void pcie_mif_irq_unreg_handler_wpan(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); pcie->wpan_handler = pcie_mif_irq_default_handler; pcie->irq_dev = NULL; } static void pcie_mif_irq_reg_reset_request_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); pcie->reset_request_handler = handler; pcie->reset_request_handler_data = dev; } static void pcie_mif_irq_unreg_reset_request_handler(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "UnRegistering mif reset_request int handler %pS\n", interface); pcie->reset_request_handler = pcie_mif_irq_reset_request_default_handler; pcie->reset_request_handler = NULL; } static u32 *pcie_mif_get_mbox_ptr(struct scsc_mif_abs *interface, u32 mbox_index, enum scsc_mif_abs_target target) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); u32 *addr; SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "mbox_index 0x%x\n", mbox_index); addr = pcie->registers_ramrp + MAILBOX_WLBT_WL_REG(ISSR(mbox_index)); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "bar1 pcie->registers_ramrp %x offset %x addr %x\n", pcie->registers_ramrp, MAILBOX_WLBT_WL_REG(ISSR(mbox_index)), addr); return addr; } static int pcie_mif_get_mbox_pmu(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); u32 val = 0; val = readl(pcie->registers_ramrp + RAMRP_HOSTIF_PMU_MBOX_TO_HOST_PTR); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "Read WB2AP_MAILBOX: %u\n", val); return val; } static int pcie_mif_set_mbox_pmu(struct scsc_mif_abs *interface, u32 val) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Write CMD %d at offset 0x%x\n", val, RAMRP_HOSTIF_PMU_MBOX_FROM_HOST_PTR); writel(val, pcie->registers_ramrp + RAMRP_HOSTIF_PMU_MBOX_FROM_HOST_PTR); msleep(1); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "TRIGGER IRQ 0x2: PMU_INTERRUPT_FROM_HOST_INTGR\n"); writel(0x2, pcie->registers_pert + PMU_INTERRUPT_FROM_HOST_INTGR); msleep(1); return 0; } static int pcie_load_pmu_fw(struct scsc_mif_abs *interface, u32 *ka_patch, size_t ka_patch_len) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); if (pcie->ka_patch_fw) { SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "ka_patch already allocated\n"); devm_kfree(pcie->dev, pcie->ka_patch_fw); } pcie->ka_patch_fw = devm_kzalloc(pcie->dev, ka_patch_len, GFP_KERNEL); if (!pcie->ka_patch_fw) { SCSC_TAG_WARNING_DEV(PCIE_MIF, pcie->dev, "Unable to allocate 0x%x\n", ka_patch_len); return -ENOMEM; } memcpy(pcie->ka_patch_fw, ka_patch, ka_patch_len); pcie->ka_patch_len = ka_patch_len; SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "load_pmu_fw sz %u done\n", ka_patch_len); return 0; } static void pcie_mif_irq_reg_pmu_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Registering mif pmu int handler %pS in %p %p\n", handler, pcie, interface); pcie->pmu_handler = handler; pcie->irq_dev_pmu = dev; } static int pcie_mif_get_mifram_ref(struct scsc_mif_abs *interface, void *ptr, scsc_mifram_ref *ref) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); if (ptr > (pcie->mem + PCIE_MIF_PREALLOC_MEM)) { SCSC_TAG_ERR(PCIE_MIF, "ooops limits reached\n"); return -ENOMEM; } /* Ref is byte offset wrt start of shared memory */ *ref = (scsc_mifram_ref)((uintptr_t)ptr - (uintptr_t)pcie->mem); return 0; } static void *pcie_mif_get_mifram_ptr(struct scsc_mif_abs *interface, scsc_mifram_ref ref) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_DEBUG_DEV(PCIE_MIF, pcie->dev, "\n"); /* Check limits */ if (ref >= 0 && ref < PCIE_MIF_ALLOC_MEM) return (void *)((uintptr_t)pcie->mem + (uintptr_t)ref); else return NULL; } static uintptr_t pcie_mif_get_mif_pfn(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); return virt_to_phys(pcie->mem) >> PAGE_SHIFT; } static struct device *pcie_mif_get_mif_device(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); return pcie->dev; } static void pcie_mif_irq_clear(void) { } static void pcie_mif_dump_register(struct scsc_mif_abs *interface) { } static void __iomem *pcie_mif_map_bar(struct pci_dev *pdev, u8 index) { void __iomem *vaddr; dma_addr_t busaddr; size_t len; int ret; ret = pcim_iomap_regions(pdev, 1 << index, DRV_NAME); if (ret) return IOMEM_ERR_PTR(ret); busaddr = pci_resource_start(pdev, index); len = pci_resource_len(pdev, index); vaddr = pcim_iomap_table(pdev)[index]; if (!vaddr) return IOMEM_ERR_PTR(-ENOMEM); SCSC_TAG_ERR_DEV(PCIE_MIF, &pdev->dev, "BAR%u vaddr=0x%p busaddr=%pad len=%u\n", index, vaddr, &busaddr, (int)len); return vaddr; } static int pcie_mif_enable_msi(struct pcie_mif *pcie) { struct msi_desc *msi_desc; int num_vectors; int ret; u32 acc = 0, i; num_vectors = pci_alloc_irq_vectors(pcie->pdev, 1, NUM_TO_HOST_IRQ_TOTAL, PCI_IRQ_MSI); if (num_vectors != NUM_TO_HOST_IRQ_TOTAL) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "failed to get %d MSI vectors, only %d available\n", NUM_TO_HOST_IRQ_TOTAL, num_vectors); if (num_vectors >= 0) return -EINVAL; else return num_vectors; } SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "vectors %d\n", num_vectors); for (i = 0; i < NUM_TO_HOST_IRQ_PMU; i++, acc++) { pcie->pmu_msi[i].msi_vec = pci_irq_vector(pcie->pdev, 0); SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Setting MSI %d interrupt vector line %d for PMU\n", acc, pcie->pmu_msi[i].msi_vec); ret = request_irq(pcie->pmu_msi[i].msi_vec, pcie_mif_pmu_isr, 0, DRV_NAME, pcie); if (ret) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Failed to register MSI handler. Aborting.\n"); return -EINVAL; } } for (i = 0; i < NUM_TO_HOST_IRQ_WLAN; i++, acc++) { pcie->wlan_msi[i].msi_vec = pci_irq_vector(pcie->pdev, acc); pcie->wlan_msi[i].msi_bit = acc; pcie->wlan_msi[i].is_masked = false; SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Setting MSI %d interrupt vector line %d for WLAN\n", acc, pcie->wlan_msi[i].msi_vec); ret = request_irq(pcie->wlan_msi[i].msi_vec, pcie_mif_isr_wlan, 0, DRV_NAME, pcie); if (ret) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Failed to register MSI handler. Aborting.\n"); return -EINVAL; } } for (i = 0; i < NUM_TO_HOST_IRQ_WPAN; i++, acc++) { pcie->wpan_msi[i].msi_vec = pci_irq_vector(pcie->pdev, acc); pcie->wpan_msi[i].msi_bit = acc; pcie->wpan_msi[i].is_masked = false; SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Setting MSI %d interrupt vector line %d for WPAN\n", acc, pcie->wpan_msi[i].msi_vec); ret = request_irq(pcie->wpan_msi[i].msi_vec, pcie_mif_isr_wpan, 0, DRV_NAME, pcie); if (ret) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Failed to register MSI handler. Aborting.\n"); return -EINVAL; } } msi_desc = irq_get_msi_desc(pcie->pdev->irq); if (!msi_desc) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "msi_desc is NULL\n"); ret = -EINVAL; goto free_msi_vector; } SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "msi base data is %d\n", msi_desc->msg.data); return 0; free_msi_vector: pci_free_irq_vectors(pcie->pdev); return ret; } struct scsc_mif_abs *pcie_mif_create(struct pci_dev *pdev, const struct pci_device_id *id) { int rc = 0; struct scsc_mif_abs *pcie_if; struct pcie_mif *pcie = (struct pcie_mif *)devm_kzalloc(&pdev->dev, sizeof(struct pcie_mif), GFP_KERNEL); u16 cmd; /* Avoid unused parameter error */ (void)id; SCSC_TAG_ERR(PCIE_MIF, "Paean. Hello World !!!\n"); if (!pcie) { SCSC_TAG_ERR(PCIE_MIF, "PCIE is null!\n"); return NULL; } pcie_if = &pcie->interface; g_if = pcie_if; /* initialise interface structure */ pcie_if->destroy = pcie_mif_destroy; pcie_if->get_uid = pcie_mif_get_uid; pcie_if->reset = pcie_mif_reset; pcie_if->map = pcie_mif_map; pcie_if->unmap = pcie_mif_unmap; pcie_if->irq_bit_set = pcie_mif_irq_bit_set; pcie_if->irq_get = pcie_mif_irq_get; pcie_if->irq_bit_mask_status_get = pcie_mif_irq_bit_mask_status_get; pcie_if->irq_bit_clear = pcie_mif_irq_bit_clear; pcie_if->irq_bit_mask = pcie_mif_irq_bit_mask; pcie_if->irq_bit_unmask = pcie_mif_irq_bit_unmask; pcie_if->irq_reg_handler = pcie_mif_irq_reg_handler; pcie_if->irq_unreg_handler = pcie_mif_irq_unreg_handler; pcie_if->get_msi_range = pcie_mif_get_msi_range; pcie_if->remap_set = pcie_mif_remap_set; pcie_if->irq_reg_handler_wpan = pcie_mif_irq_reg_handler_wpan; pcie_if->irq_unreg_handler_wpan = pcie_mif_irq_unreg_handler_wpan; pcie_if->irq_reg_reset_request_handler = pcie_mif_irq_reg_reset_request_handler; pcie_if->irq_unreg_reset_request_handler = pcie_mif_irq_unreg_reset_request_handler; pcie_if->get_mbox_ptr = pcie_mif_get_mbox_ptr; pcie_if->get_mifram_ptr = pcie_mif_get_mifram_ptr; pcie_if->get_mifram_ref = pcie_mif_get_mifram_ref; pcie_if->get_mifram_pfn = pcie_mif_get_mif_pfn; pcie_if->get_mif_device = pcie_mif_get_mif_device; pcie_if->irq_clear = pcie_mif_irq_clear; pcie_if->mif_dump_registers = pcie_mif_dump_register; pcie_if->mif_read_register = NULL; /* Suspend/resume not supported in PCIe MIF */ pcie_if->suspend_reg_handler = NULL; pcie_if->suspend_unreg_handler = NULL; pcie_if->get_mbox_pmu = pcie_mif_get_mbox_pmu; pcie_if->set_mbox_pmu = pcie_mif_set_mbox_pmu; pcie_if->load_pmu_fw = pcie_load_pmu_fw; pcie_if->irq_reg_pmu_handler = pcie_mif_irq_reg_pmu_handler; pcie->pmu_handler = pcie_mif_irq_default_handler; pcie->irq_dev_pmu = NULL; /* Update state */ pcie->pdev = pdev; pcie->dev = &pdev->dev; pcie->wlan_handler = pcie_mif_irq_default_handler; pcie->wpan_handler = pcie_mif_irq_default_handler; pcie->irq_dev = NULL; /* Just do whats is necessary to meet the pci probe * -BAR0 stuff * -Interrupt (will be able to handle interrupts?) */ /* My stuff */ pci_set_drvdata(pdev, pcie); rc = pcim_enable_device(pdev); if (rc) { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Error enabling device.\n"); return NULL; } /* This function returns the flags associated with this resource.*/ /* esource flags are used to define some features of the individual resource. * For PCI resources associated with PCI I/O regions, the information is extracted from the base address registers */ /* IORESOURCE_MEM = If the associated I/O region exists, one and only one of these flags is set */ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { SCSC_TAG_ERR(PCIE_MIF, "Incorrect BAR configuration\n"); return NULL; } pci_set_master(pdev); /* Access iomap allocation table */ /* return __iomem * const * */ pcie->registers_pert = pcie_mif_map_bar(pdev, 0); SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "BAR0/1 registers %x\n", pcie->registers_pert); pcie->registers_ramrp = pcie_mif_map_bar(pdev, 2); SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "BAR2/3 registers %x\n", pcie->registers_ramrp); pcie->registers_msix = pcie_mif_map_bar(pdev, 4); SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "BAR4/5 registers %x\n", pcie->registers_msix); pcie_mif_enable_msi(pcie); SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Setting DMA mask to 32\n"); if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "Error DMA mask 32bits.\n"); pcie->dma_using_dac = 0; } else { SCSC_TAG_ERR_DEV(PCIE_MIF, pcie->dev, "Failed to set DMA mask. Aborting.\n"); return NULL; } pcie->in_reset = true; /* Initialize spinlock */ spin_lock_init(&pcie->mif_spinlock); spin_lock_init(&pcie->mask_spinlock); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "PCI read configuration\n"); pci_read_config_word(pdev, PCI_COMMAND, &cmd); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "PCIE PCI_COMMAND 0x%x\n", cmd); pci_read_config_word(pdev, PCI_VENDOR_ID, &cmd); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "PCIE PCI_VENDOR_ID 0x%x\n", cmd); pci_read_config_word(pdev, PCI_DEVICE_ID, &cmd); SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "PCIE PCI_DEVICE_ID 0x%x\n", cmd); pcie->mem_allocated = 0; pcie_create_proc_dir(pcie); return pcie_if; } void pcie_mif_destroy_pcie(struct pci_dev *pdev, struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); SCSC_TAG_ERR(PCIE_MIF, "Paean. Bye World !!!\n"); free_irq(pci_irq_vector(pdev, 0), pcie); free_irq(pci_irq_vector(pdev, 1), pcie); pci_free_irq_vectors(pdev); pcie_remove_proc_dir(); /* Unmap/free allocated memory */ pcie_mif_unmap(interface, NULL); /* Create debug proc entry */ pcie_remove_proc_dir(); pcie_mif_reset(interface, true); pci_disable_device(pdev); } struct pci_dev *pcie_mif_get_pci_dev(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); BUG_ON(!interface || !pcie); return pcie->pdev; } struct device *pcie_mif_get_dev(struct scsc_mif_abs *interface) { struct pcie_mif *pcie = pcie_mif_from_mif_abs(interface); BUG_ON(!interface || !pcie); return pcie->dev; } /* Functions for proc entry */ void *pcie_mif_get_mem(struct pcie_mif *pcie) { SCSC_TAG_INFO_DEV(PCIE_MIF, pcie->dev, "READ %x\n", pcie->mem); return pcie->mem; }