kernel_samsung_a53x/drivers/misc/samsung/scsc/pcie_mif_paean.c
2024-06-15 16:02:09 -03:00

1032 lines
33 KiB
C
Executable file

/****************************************************************************
*
* Copyright (c) 2014 - 2021 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
/* Implements */
#include "pcie_mif.h"
/* Uses */
#include <linux/pfn.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <asm/barrier.h>
#include <scsc/scsc_logring.h>
#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 <linux/delay.h>
#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;
}