298 lines
8.5 KiB
C
298 lines
8.5 KiB
C
|
/**
|
||
|
******************************************************************************
|
||
|
*
|
||
|
* @file rwnx_dini.c - Add support for dini platform
|
||
|
*
|
||
|
* Copyright (C) RivieraWaves 2012-2019
|
||
|
*
|
||
|
******************************************************************************
|
||
|
*/
|
||
|
|
||
|
#include "rwnx_dini.h"
|
||
|
#include "rwnx_defs.h"
|
||
|
#include "rwnx_irqs.h"
|
||
|
#include "reg_access.h"
|
||
|
|
||
|
/* Config FPGA is accessed via bar0 */
|
||
|
#define CFPGA_DMA0_CTRL_REG 0x02C
|
||
|
#define CFPGA_DMA1_CTRL_REG 0x04C
|
||
|
#define CFPGA_DMA2_CTRL_REG 0x06C
|
||
|
#define CFPGA_UINTR_SRC_REG 0x0E8
|
||
|
#define CFPGA_UINTR_MASK_REG 0x0EC
|
||
|
#define CFPGA_BAR4_HIADDR_REG 0x100
|
||
|
#define CFPGA_BAR4_LOADDR_REG 0x104
|
||
|
#define CFPGA_BAR4_LOADDR_MASK_REG 0x110
|
||
|
#define CFPGA_BAR_TOUT 0x120
|
||
|
|
||
|
#define CFPGA_DMA_CTRL_ENABLE 0x00001400
|
||
|
#define CFPGA_DMA_CTRL_DISABLE 0x00001000
|
||
|
#define CFPGA_DMA_CTRL_CLEAR 0x00001800
|
||
|
#define CFPGA_DMA_CTRL_REREAD_TIME_MASK (BIT(10) - 1)
|
||
|
|
||
|
#define CFPGA_BAR4_LOADDR_MASK_MAX 0xFF000000
|
||
|
|
||
|
#define CFPGA_PCIEX_IT 0x00000001
|
||
|
#define CFPGA_ALL_ITS 0x0000000F
|
||
|
|
||
|
/* Programmable BAR4 Window start address */
|
||
|
#define CPU_RAM_WINDOW_HIGH 0x00000000
|
||
|
#define CPU_RAM_WINDOW_LOW 0x00000000
|
||
|
#define AHB_BRIDGE_WINDOW_HIGH 0x00000000
|
||
|
#define AHB_BRIDGE_WINDOW_LOW 0x60000000
|
||
|
|
||
|
struct rwnx_dini {
|
||
|
u8 *pci_bar0_vaddr;
|
||
|
u8 *pci_bar4_vaddr;
|
||
|
};
|
||
|
|
||
|
static const u32 mv_cfg_fpga_dma_ctrl_regs[] = {
|
||
|
CFPGA_DMA0_CTRL_REG,
|
||
|
CFPGA_DMA1_CTRL_REG,
|
||
|
CFPGA_DMA2_CTRL_REG
|
||
|
};
|
||
|
|
||
|
/* This also clears running transactions */
|
||
|
static void dini_dma_on(struct rwnx_dini *rwnx_dini)
|
||
|
{
|
||
|
int i;
|
||
|
u32 reread_time;
|
||
|
volatile void *reg;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
|
||
|
reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
|
||
|
reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
|
||
|
|
||
|
writel(CFPGA_DMA_CTRL_CLEAR | reread_time, reg);
|
||
|
writel(CFPGA_DMA_CTRL_ENABLE | reread_time, reg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* This also clears running transactions */
|
||
|
static void dini_dma_off(struct rwnx_dini *rwnx_dini)
|
||
|
{
|
||
|
int i;
|
||
|
u32 reread_time;
|
||
|
volatile void *reg;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
|
||
|
reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
|
||
|
reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
|
||
|
|
||
|
writel(CFPGA_DMA_CTRL_DISABLE | reread_time, reg);
|
||
|
writel(CFPGA_DMA_CTRL_CLEAR | reread_time, reg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Configure address range for BAR4.
|
||
|
* By default BAR4_LOADDR_MASK value is 0xFF000000, then there is no need to
|
||
|
* change it because the addresses we need to access are covered by this mask
|
||
|
*/
|
||
|
static void dini_set_bar4_win(u32 low, u32 high, struct rwnx_dini *rwnx_dini)
|
||
|
{
|
||
|
writel(low, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_REG);
|
||
|
writel(high, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_HIADDR_REG);
|
||
|
writel(CFPGA_BAR4_LOADDR_MASK_MAX,
|
||
|
rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_MASK_REG);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Enable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
|
||
|
* and request the corresponding IRQ line
|
||
|
*/
|
||
|
int rwnx_cfpga_irq_enable(struct rwnx_hw *rwnx_hw)
|
||
|
{
|
||
|
struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
unsigned int cfpga_uintr_mask;
|
||
|
volatile void *reg;
|
||
|
int ret;
|
||
|
|
||
|
/* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
|
||
|
ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0, "rwnx", rwnx_hw);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
|
||
|
cfpga_uintr_mask = readl(reg);
|
||
|
writel(cfpga_uintr_mask | CFPGA_PCIEX_IT, reg);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Disable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
|
||
|
* and free the corresponding IRQ line
|
||
|
*/
|
||
|
int rwnx_cfpga_irq_disable(struct rwnx_hw *rwnx_hw)
|
||
|
{
|
||
|
struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
unsigned int cfpga_uintr_mask;
|
||
|
volatile void *reg;
|
||
|
|
||
|
reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
|
||
|
cfpga_uintr_mask = readl(reg);
|
||
|
writel(cfpga_uintr_mask & ~CFPGA_PCIEX_IT, reg);
|
||
|
|
||
|
free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int rwnx_dini_platform_enable(struct rwnx_hw *rwnx_hw)
|
||
|
{
|
||
|
struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
|
||
|
#ifdef CONFIG_RWNX_SDM
|
||
|
writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
|
||
|
#endif
|
||
|
|
||
|
dini_dma_on(rwnx_dini);
|
||
|
return rwnx_cfpga_irq_enable(rwnx_hw);
|
||
|
}
|
||
|
|
||
|
static int rwnx_dini_platform_disable(struct rwnx_hw *rwnx_hw)
|
||
|
{
|
||
|
struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
int ret;
|
||
|
|
||
|
ret = rwnx_cfpga_irq_disable(rwnx_hw);
|
||
|
dini_dma_off(rwnx_dini);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void rwnx_dini_platform_deinit(struct rwnx_plat *rwnx_plat)
|
||
|
{
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
|
||
|
pci_disable_device(rwnx_plat->pci_dev);
|
||
|
iounmap(rwnx_dini->pci_bar0_vaddr);
|
||
|
iounmap(rwnx_dini->pci_bar4_vaddr);
|
||
|
pci_release_regions(rwnx_plat->pci_dev);
|
||
|
|
||
|
kfree(rwnx_plat);
|
||
|
}
|
||
|
|
||
|
static u8 *rwnx_dini_get_address(struct rwnx_plat *rwnx_plat, int addr_name,
|
||
|
unsigned int offset)
|
||
|
{
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
|
||
|
if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
|
||
|
return NULL;
|
||
|
|
||
|
if (addr_name == RWNX_ADDR_CPU)
|
||
|
dini_set_bar4_win(CPU_RAM_WINDOW_LOW, CPU_RAM_WINDOW_HIGH, rwnx_dini);
|
||
|
else
|
||
|
dini_set_bar4_win(AHB_BRIDGE_WINDOW_LOW, AHB_BRIDGE_WINDOW_HIGH, rwnx_dini);
|
||
|
|
||
|
return rwnx_dini->pci_bar4_vaddr + offset;
|
||
|
}
|
||
|
|
||
|
static void rwnx_dini_ack_irq(struct rwnx_plat *rwnx_plat)
|
||
|
{
|
||
|
struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
|
||
|
|
||
|
writel(CFPGA_ALL_ITS, rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_SRC_REG);
|
||
|
}
|
||
|
|
||
|
static const u32 rwnx_dini_config_reg[] = {
|
||
|
NXMAC_DEBUG_PORT_SEL_ADDR,
|
||
|
SYSCTRL_DIAG_CONF_ADDR,
|
||
|
RF_V6_DIAGPORT_CONF1_ADDR,
|
||
|
RF_v6_PHYDIAG_CONF1_ADDR,
|
||
|
};
|
||
|
|
||
|
static int rwnx_dini_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
|
||
|
{
|
||
|
if (!list)
|
||
|
return 0;
|
||
|
|
||
|
*list = rwnx_dini_config_reg;
|
||
|
return ARRAY_SIZE(rwnx_dini_config_reg);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* rwnx_dini_platform_init - Initialize the DINI platform
|
||
|
*
|
||
|
* @pci_dev PCI device
|
||
|
* @rwnx_plat Pointer on struct rwnx_stat * to be populated
|
||
|
*
|
||
|
* @return 0 on success, < 0 otherwise
|
||
|
*
|
||
|
* Allocate and initialize a rwnx_plat structure for the dini platform.
|
||
|
*/
|
||
|
int rwnx_dini_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
|
||
|
{
|
||
|
struct rwnx_dini *rwnx_dini;
|
||
|
u16 pci_cmd;
|
||
|
int ret = 0;
|
||
|
|
||
|
*rwnx_plat = kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_dini),
|
||
|
GFP_KERNEL);
|
||
|
if (!*rwnx_plat)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
rwnx_dini = (struct rwnx_dini *)(*rwnx_plat)->priv;
|
||
|
|
||
|
/* Hotplug fixups */
|
||
|
pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
|
||
|
pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
|
||
|
pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
|
||
|
pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES >> 2);
|
||
|
|
||
|
ret = pci_enable_device(pci_dev);
|
||
|
if (ret) {
|
||
|
dev_err(&(pci_dev->dev), "pci_enable_device failed\n");
|
||
|
goto out_enable;
|
||
|
}
|
||
|
|
||
|
pci_set_master(pci_dev);
|
||
|
|
||
|
ret = pci_request_regions(pci_dev, KBUILD_MODNAME);
|
||
|
if (ret) {
|
||
|
dev_err(&(pci_dev->dev), "pci_request_regions failed\n");
|
||
|
goto out_request;
|
||
|
}
|
||
|
|
||
|
rwnx_dini->pci_bar0_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 0);
|
||
|
if (!rwnx_dini->pci_bar0_vaddr) {
|
||
|
dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 0);
|
||
|
ret = -ENOMEM;
|
||
|
goto out_bar0;
|
||
|
}
|
||
|
rwnx_dini->pci_bar4_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 4);
|
||
|
if (!rwnx_dini->pci_bar4_vaddr) {
|
||
|
dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 4);
|
||
|
ret = -ENOMEM;
|
||
|
goto out_bar4;
|
||
|
}
|
||
|
|
||
|
(*rwnx_plat)->enable = rwnx_dini_platform_enable;
|
||
|
(*rwnx_plat)->disable = rwnx_dini_platform_disable;
|
||
|
(*rwnx_plat)->deinit = rwnx_dini_platform_deinit;
|
||
|
(*rwnx_plat)->get_address = rwnx_dini_get_address;
|
||
|
(*rwnx_plat)->ack_irq = rwnx_dini_ack_irq;
|
||
|
(*rwnx_plat)->get_config_reg = rwnx_dini_get_config_reg;
|
||
|
|
||
|
#ifdef CONFIG_RWNX_SDM
|
||
|
writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
out_bar4:
|
||
|
iounmap(rwnx_dini->pci_bar0_vaddr);
|
||
|
out_bar0:
|
||
|
pci_release_regions(pci_dev);
|
||
|
out_request:
|
||
|
pci_disable_device(pci_dev);
|
||
|
out_enable:
|
||
|
kfree(*rwnx_plat);
|
||
|
return ret;
|
||
|
}
|