/*
* drivers/misc/spear13xx_pcie_gadget.c
*
* Copyright (C) 2010 ST Microelectronics
* Pratyush Anand<pratyush.anand@st.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pci_regs.h>
#include <linux/configfs.h>
#include <mach/pcie.h>
#include <mach/misc_regs.h>
#define IN0_MEM_SIZE (200 * 1024 * 1024 - 1)
/* In current implementation address translation is done using IN0 only.
* So IN1 start address and IN0 end address has been kept same
*/
#define IN1_MEM_SIZE (0 * 1024 * 1024 - 1)
#define IN_IO_SIZE (20 * 1024 * 1024 - 1)
#define IN_CFG0_SIZE (12 * 1024 * 1024 - 1)
#define IN_CFG1_SIZE (12 * 1024 * 1024 - 1)
#define IN_MSG_SIZE (12 * 1024 * 1024 - 1)
/* Keep default BAR size as 4K*/
/* AORAM would be mapped by default*/
#define INBOUND_ADDR_MASK (SPEAR13XX_SYSRAM1_SIZE - 1)
#define INT_TYPE_NO_INT 0
#define INT_TYPE_INTX 1
#define INT_TYPE_MSI 2
struct spear_pcie_gadget_config {
void __iomem *base;
void __iomem *va_app_base;
void __iomem *va_dbi_base;
char int_type[10];
ulong requested_msi;
ulong configured_msi;
ulong bar0_size;
ulong bar0_rw_offset;
void __iomem *va_bar0_address;
};
struct pcie_gadget_target {
struct configfs_subsystem subsys;
struct spear_pcie_gadget_config config;
};
struct pcie_gadget_target_attr {
struct configfs_attribute attr;
ssize_t (*show)(struct spear_pcie_gadget_config *config,
char *buf);
ssize_t (*store)(struct spear_pcie_gadget_config *config,
const char *buf,
size_t count);
};
static void enable_dbi_access(struct pcie_app_reg __iomem *app_reg)
{
/* Enable DBI access */
writel(readl(&app_reg->slv_armisc) | (1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_armisc);
writel(readl(&app_reg->slv_awmisc) | (1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_awmisc);
}
static void disable_dbi_access(struct pcie_app_reg __iomem *app_reg)
{
/* disable DBI access */
writel(readl(&app_reg->slv_armisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_armisc);
writel(readl(&app_reg->slv_awmisc) & ~(1 << AXI_OP_DBI_ACCESS_ID),
&app_reg->slv_awmisc);
}
static void spear_dbi_read_reg(struct spear_pcie_gadget_config *config,
int where, int size, u32 *val)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong va_address;
/* Enable DBI access */
enable_dbi_access(app_reg);
va_address = (ulong)config->va_dbi_base + (where & ~0x3);
*val = readl(va_address);
if (size == 1)
*val = (*val >> (8 * (where & 3))) & 0xff;
else if (size == 2)
*val = (*val >> (8 * (where & 3))) & 0xffff;
/* Disable DBI access */
disable_dbi_access(app_reg);
}
static void spear_dbi_write_reg(struct spear_pcie_gadget_config *config,
int where, int size, u32 val)
{
struct pcie_app_reg __iomem *app_reg = config->va_app_base;
ulong va_address;
/* Enable DBI access */
enable_dbi_access(app_reg);
va_address = (ulong)config->va_dbi_base + (where & ~0x3);
if (size == 4)
writel(val, va_address);
else if (size == 2)
writew(val, va_address + (where & 2));
else if (size == 1)
writeb(val, va_address + (where & 3));
/* Disable DBI access */
disable_dbi_access(app_reg);
}
#define PCI_FIND_CAP_TTL 48
static int pci_find_own_next_cap_ttl(struct spear_pcie_gadget_config *config,
u32 pos, int cap, int *ttl)
{
u32 id;
while ((*ttl)--) {
spear_dbi_read_reg(config, pos, 1, &pos);
if (pos < 0x40)
break;
pos &= ~3;
spear_dbi_read_reg(config, pos + PCI_CAP_LIST_ID, 1, &id);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos += PCI_CAP_LIST_NEXT;
}
return 0;
}
static int pci_find_own_next_cap(struct spear_pcie_gadget_config *config,
u32 pos, int cap)
{
int ttl = PCI_FIND_CAP_TTL;
return pci_find_own_next_cap_ttl(config, pos, cap, &ttl);
}
static int pci_find_own_cap_start(struct