628 lines
16 KiB
C
628 lines
16 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/configfs.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/of.h>
|
|
#include <linux/pci-epc.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pci-epf.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include "amlogic-pcie-v3.h"
|
|
|
|
#define AML_PCI_MAX_RESOURCES 4
|
|
|
|
static int ep_link_times = WAIT_LINKUP_TIMEOUT - 10;
|
|
module_param(ep_link_times, int, 0644);
|
|
MODULE_PARM_DESC(ep_link_times, "select pcie ep link speed ");
|
|
|
|
struct amlogic_pcie_ep {
|
|
struct amlogic_pcie amlogic;
|
|
struct pci_epc *epc;
|
|
struct resource *mem_res;
|
|
struct gpio_desc *reset_gpio;
|
|
struct delayed_work work;
|
|
u32 max_regions;
|
|
unsigned long ob_region_map;
|
|
phys_addr_t *ob_addr;
|
|
u8 max_functions;
|
|
};
|
|
|
|
static inline u32 amlogic_pcieinter_ep_read(struct amlogic_pcie *pcie, u32 reg)
|
|
{
|
|
return readl(pcie->ecam_base + reg);
|
|
}
|
|
|
|
static inline void amlogic_pcieinter_ep_write(struct amlogic_pcie *pcie,
|
|
u32 val,
|
|
u32 reg)
|
|
{
|
|
writel(val, pcie->ecam_base + EP_BASE_OFFSET + reg);
|
|
}
|
|
|
|
static int amlogic_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
struct pci_epf_header *hdr)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u32 val;
|
|
|
|
/* All functions share the same vendor ID with function 0 */
|
|
if (!func_no)
|
|
val = hdr->vendorid;
|
|
else
|
|
val = amlogic_pcieinter_read(amlogic, PCIE_PCI_IDS0);
|
|
|
|
val |= hdr->deviceid << 16;
|
|
amlogic_pcieinter_write(amlogic, val, PCIE_PCI_IDS0);
|
|
|
|
val = hdr->revid;
|
|
val |= hdr->progif_code << 8;
|
|
val |= hdr->subclass_code << 16;
|
|
val |= hdr->baseclass_code << 24;
|
|
amlogic_pcieinter_write(amlogic, val, PCIE_PCI_IDS1);
|
|
|
|
if (!func_no)
|
|
val = hdr->subsys_vendor_id;
|
|
else
|
|
val = amlogic_pcieinter_read(amlogic, PCIE_PCI_IDS2);
|
|
val |= hdr->subsys_id << 16;
|
|
amlogic_pcieinter_write(amlogic, val, PCIE_PCI_IDS2);
|
|
|
|
if (hdr->interrupt_pin > PCI_INTERRUPT_INTD)
|
|
return -EINVAL;
|
|
val = amlogic_pcieinter_read(amlogic, PCIE_PCI_IRQ);
|
|
val &= (~GENMASK(2, 0));
|
|
val |= hdr->interrupt_pin;
|
|
amlogic_pcieinter_write(amlogic, val, PCIE_PCI_IRQ);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
struct pci_epf_bar *epf_bar)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
dma_addr_t cpu_addr = epf_bar->phys_addr;
|
|
enum pci_barno bar = epf_bar->barno;
|
|
int flags = epf_bar->flags;
|
|
u64 size = 1ULL << fls64(epf_bar->size - 1);
|
|
u64 reg = 0;
|
|
int trsl_param = 0, atr_size = 0;
|
|
bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH);
|
|
bool is_64bits = size > SZ_4G;
|
|
|
|
atr_size = ilog2(size);
|
|
|
|
if (((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)) {
|
|
reg = PCIE_BAR_IO;
|
|
} else {
|
|
if (is_64bits && is_prefetch) {
|
|
reg |= PCIE_BAR_MEM_TYPE_64 |
|
|
PCIE_BAR_MEM_TYPE_PREFETCH |
|
|
PCIE_BAR_MEM_MASK;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
lower_32_bits(reg));
|
|
bar += 1;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
upper_32_bits(reg));
|
|
} else if (is_prefetch) {
|
|
reg |= PCIE_BAR_MEM_TYPE_PREFETCH;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
lower_32_bits(reg));
|
|
bar += 1;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
lower_32_bits(reg));
|
|
|
|
} else if (is_64bits) {
|
|
reg |= PCIE_BAR_MEM_TYPE_64;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
lower_32_bits(reg));
|
|
bar += 1;
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no,
|
|
PCIE_BAR_0 + bar * 4,
|
|
upper_32_bits(reg));
|
|
} else {
|
|
}
|
|
trsl_param = ATR_TRSLID_AXIMEMORY;
|
|
}
|
|
|
|
amlogic_pcie_ep_fn_writel(amlogic, func_no, PCIE_BAR_0 + bar * 4, reg);
|
|
|
|
amlogic_pcie_cfg_addr_map(amlogic, ATR_PCIE_WIN0 +
|
|
ATR_TABLE_SIZE * (bar - 2),
|
|
0, cpu_addr,
|
|
atr_size, trsl_param);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void amlogic_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
struct pci_epf_bar *epf_bar)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
enum pci_barno bar = epf_bar->barno;
|
|
|
|
amlogic_pcieinter_write(amlogic, 0,
|
|
ATR_PCIE_WIN0 + ATR_TABLE_SIZE * (bar - 2) +
|
|
ATR_SRC_ADDR_LOW);
|
|
}
|
|
|
|
static int amlogic_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
phys_addr_t addr, u64 pci_addr,
|
|
size_t size)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u32 r;
|
|
|
|
/*
|
|
* find_first_zero_bit() is an arch-specific function controlled by marco find_first_bit.
|
|
*/
|
|
/* coverity[callee_ptr_arith:SUPPRESS] */
|
|
r = find_first_zero_bit(&ep->ob_region_map,
|
|
sizeof(ep->ob_region_map) * BITS_PER_LONG);
|
|
if (r >= ep->max_regions - 1) {
|
|
dev_err(&epc->dev, "no free outbound region\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
amlogic_pcie_cfg_addr_map(amlogic,
|
|
ATR_AXI4_SLV0 + ATR_TABLE_SIZE * (r + 1),
|
|
addr, pci_addr,
|
|
ilog2(size), ATR_TRSLID_PCIE_MEMORY);
|
|
|
|
set_bit(r, &ep->ob_region_map);
|
|
ep->ob_addr[r] = addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void amlogic_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
phys_addr_t addr)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u32 r;
|
|
|
|
for (r = 0; r < ep->max_regions - 1; r++)
|
|
if (ep->ob_addr[r] == addr)
|
|
break;
|
|
|
|
if (r == ep->max_regions - 1)
|
|
return;
|
|
|
|
amlogic_pcieinter_write(amlogic, 0,
|
|
ATR_AXI4_SLV0 + ATR_TABLE_SIZE * (r + 1) +
|
|
ATR_SRC_ADDR_LOW);
|
|
|
|
ep->ob_addr[r] = 0;
|
|
clear_bit(r, &ep->ob_region_map);
|
|
}
|
|
|
|
static int amlogic_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
u8 mmc)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u16 flags;
|
|
|
|
/* Multiple Message Capable BIT[3:1] is Ro,
|
|
* so this write invalid. Default is 32 vectors requested
|
|
*/
|
|
flags = amlogic_pcie_ep_fn_readw(amlogic, func_no,
|
|
EP_FUNC_MSI_CAP_OFFSET +
|
|
PCI_MSI_FLAGS);
|
|
flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1);
|
|
flags |= PCI_MSI_FLAGS_64BIT;
|
|
flags &= ~PCI_MSI_FLAGS_MASKBIT;
|
|
amlogic_pcie_ep_fn_writew(amlogic, func_no,
|
|
EP_FUNC_MSI_CAP_OFFSET +
|
|
PCI_MSI_FLAGS, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u16 flags, mme;
|
|
|
|
flags = amlogic_pcie_ep_fn_readw(amlogic, func_no,
|
|
EP_FUNC_MSI_CAP_OFFSET +
|
|
PCI_MSI_FLAGS);
|
|
if (!(flags & PCI_MSI_FLAGS_ENABLE))
|
|
return -EINVAL;
|
|
|
|
mme = ((flags & PCI_MSI_FLAGS_QSIZE) >> 4);
|
|
|
|
return mme;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_send_legacy_irq(struct amlogic_pcie_ep *ep, u8 func_no,
|
|
u8 intx)
|
|
{
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u16 cmd;
|
|
u32 val = 0;
|
|
|
|
amlogic_pcieinter_write(amlogic, 0xffffffff, IMASK_HOST);
|
|
|
|
cmd = amlogic_pcie_ep_fn_readw(&ep->amlogic, func_no,
|
|
PCI_COMMAND);
|
|
|
|
if (cmd & PCI_COMMAND_INTX_DISABLE)
|
|
return -EINVAL;
|
|
|
|
val = amlogic_pciectrl_read(&ep->amlogic, PCIE_A_CTRL0);
|
|
val |= INT_INTX_MASK;
|
|
amlogic_pciectrl_write(&ep->amlogic, val, PCIE_A_CTRL0);
|
|
mdelay(1);
|
|
val = amlogic_pcieinter_read(&ep->amlogic, ISTATUS_HOST);
|
|
val |= INT_INTX_MASK;
|
|
amlogic_pcieinter_write(&ep->amlogic, val, ISTATUS_HOST);
|
|
|
|
amlogic_pcieinter_write(amlogic, 0xffffffff, IMASK_HOST);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_send_msi_irq(struct amlogic_pcie_ep *ep, u8 func_no,
|
|
u8 interrupt_num)
|
|
{
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
u32 val = 0;
|
|
|
|
amlogic_pcieinter_write(amlogic, 0xffffffff, IMASK_HOST);
|
|
|
|
val = amlogic_pciectrl_read(&ep->amlogic, PCIE_A_CTRL0);
|
|
val |= INT_MSI;
|
|
amlogic_pciectrl_write(&ep->amlogic, val, PCIE_A_CTRL0);
|
|
val = amlogic_pcieinter_read(&ep->amlogic, ISTATUS_HOST);
|
|
val |= INT_MSI;
|
|
amlogic_pcieinter_write(&ep->amlogic, val, ISTATUS_HOST);
|
|
|
|
amlogic_pcieinter_write(amlogic, 0xffffffff, IMASK_HOST);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|
enum pci_epc_irq_type type,
|
|
u16 interrupt_num)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
|
|
switch (type) {
|
|
case PCI_EPC_IRQ_LEGACY:
|
|
return amlogic_pcie_ep_send_legacy_irq(ep, func_no, 0);
|
|
case PCI_EPC_IRQ_MSI:
|
|
return amlogic_pcie_ep_send_msi_irq(ep, func_no, interrupt_num);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int amlogic_pcie_ep_init_port_for_m31_phy(struct amlogic_pcie *amlogic)
|
|
{
|
|
u32 val;
|
|
|
|
val = readl(amlogic->rst_base + RESETCTRL1_OFFSET);
|
|
val &= ~(1 << amlogic->m31phy_rst_bit);
|
|
writel(val, amlogic->rst_base + RESETCTRL1_OFFSET);
|
|
usleep_range(100, 200);
|
|
|
|
val = readl(amlogic->rst_base + RESETCTRL1_OFFSET);
|
|
val |= (1 << amlogic->m31phy_rst_bit);
|
|
writel(val, amlogic->rst_base + RESETCTRL1_OFFSET);
|
|
usleep_range(100, 200);
|
|
|
|
val = readl(amlogic->phy_base + 0x470);
|
|
val |= (1 << 6);
|
|
writel(val, amlogic->phy_base + 0x470);
|
|
|
|
/*set phy for gen3 device*/
|
|
val = readl(amlogic->phy_base);
|
|
val |= BIT(19);
|
|
writel(val, amlogic->phy_base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_init_port_for_aml_phy(struct amlogic_pcie *amlogic)
|
|
{
|
|
struct device *dev = amlogic->dev;
|
|
//static int cnt = 0;
|
|
int ret = 0, i;
|
|
u32 val;
|
|
|
|
//if (cnt++ == 0) {
|
|
ret = amlogic_pcie_set_reset_for_aml_phy(amlogic);
|
|
if (ret)
|
|
return ret;
|
|
//}
|
|
|
|
/* set phy pll */
|
|
if (of_property_read_bool(dev_of_node(dev), "phy_pll_setting"))
|
|
ret = amlogic_pcie_set_phy_pll(amlogic);
|
|
|
|
/* debug GEN3 form zhang yong*/
|
|
for (i = 0; i < 4; i++) {
|
|
val = readl(amlogic->phy_base + REG_DCHD_EQ_LPBK_REG + i * 4);
|
|
val &= ~(2 << 6);
|
|
val |= (1 << 6);
|
|
writel(val, amlogic->phy_base + REG_DCHD_EQ_LPBK_REG + i * 4);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_init_port(struct amlogic_pcie *amlogic)
|
|
{
|
|
struct device *dev = amlogic->dev;
|
|
u32 regs, ret = 0;
|
|
|
|
switch (amlogic->phy_type) {
|
|
case AMLOGIC_PHY:
|
|
dev_dbg(dev, " pcie init ep port and AMLOGIC_PHY\n");
|
|
ret = amlogic_pcie_ep_init_port_for_aml_phy(amlogic);
|
|
break;
|
|
case M31_COMBPHY:
|
|
dev_dbg(dev, " pcie init ep port and M31_COMBPHY\n");
|
|
break;
|
|
case M31_PHY:
|
|
default:
|
|
dev_dbg(dev, " pcie init ep port and M31_PHY\n");
|
|
ret = amlogic_pcie_ep_init_port_for_m31_phy(amlogic);
|
|
break;
|
|
}
|
|
|
|
regs = amlogic_pciectrl_read(amlogic, PCIE_A_CTRL0);
|
|
if (amlogic->is_rc)
|
|
regs |= PORT_TYPE;
|
|
else
|
|
regs &= ~PORT_TYPE;
|
|
amlogic_pciectrl_write(amlogic, regs, PCIE_A_CTRL0);
|
|
|
|
// Loopback Master
|
|
//regs = amlogic_pciectrl_read(amlogic, PCIE_A_CTRL3);
|
|
//regs |= (1<<2);
|
|
//amlogic_pciectrl_write(amlogic, regs, PCIE_A_CTRL3);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void amlogic_pcie_ep_start_link(struct amlogic_pcie *amlogic)
|
|
{
|
|
struct device *dev = amlogic->dev;
|
|
u32 regs;
|
|
|
|
if (!amlogic_pcie_link_up(amlogic))
|
|
return;
|
|
|
|
usleep_range(100, 200);
|
|
regs = amlogic_pcieinter_read(amlogic, PCIE_BASIC_STATUS);
|
|
|
|
dev_info(dev, "current linK speed is GEN%d,link width is x%d\n",
|
|
((regs >> 8) & 0x3f), (regs & 0xff));
|
|
}
|
|
|
|
static void amlogic_reset_gpio_irq_work(struct work_struct *work)
|
|
{
|
|
struct amlogic_pcie_ep *ep = container_of(work,
|
|
struct amlogic_pcie_ep,
|
|
work.work);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
|
|
amlogic_pcie_ep_init_port(amlogic);
|
|
usleep_range(20, 100);
|
|
amlogic_pcie_ep_start_link(amlogic);
|
|
}
|
|
|
|
static int amlogic_pcie_ep_start(struct pci_epc *epc)
|
|
{
|
|
struct amlogic_pcie_ep *ep = epc_get_drvdata(epc);
|
|
|
|
INIT_DELAYED_WORK(&ep->work, amlogic_reset_gpio_irq_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pci_epc_features amlogic_pcie_epc_features = {
|
|
.linkup_notifier = false,
|
|
.msi_capable = true,
|
|
.msix_capable = false,
|
|
.reserved_bar = 1 << BAR_0 | 1 << BAR_1,
|
|
.bar_fixed_64bit = 1 << BAR_0,
|
|
.bar_fixed_size[2] = 256,
|
|
};
|
|
|
|
static const struct pci_epc_features*
|
|
amlogic_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
|
|
{
|
|
return &amlogic_pcie_epc_features;
|
|
}
|
|
|
|
static const struct pci_epc_ops amlogic_pcie_epc_ops = {
|
|
.write_header = amlogic_pcie_ep_write_header,
|
|
.set_bar = amlogic_pcie_ep_set_bar,
|
|
.clear_bar = amlogic_pcie_ep_clear_bar,
|
|
.map_addr = amlogic_pcie_ep_map_addr,
|
|
.unmap_addr = amlogic_pcie_ep_unmap_addr,
|
|
.set_msi = amlogic_pcie_ep_set_msi,
|
|
.get_msi = amlogic_pcie_ep_get_msi,
|
|
.raise_irq = amlogic_pcie_ep_raise_irq,
|
|
.start = amlogic_pcie_ep_start,
|
|
.get_features = amlogic_pcie_ep_get_features,
|
|
};
|
|
|
|
static int amlogic_pcie_parse_ep_dt(struct amlogic_pcie *amlogic,
|
|
struct amlogic_pcie_ep *ep)
|
|
{
|
|
struct device *dev = amlogic->dev;
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct resource *res;
|
|
int err;
|
|
|
|
err = amlogic_pcie_parse_dt(amlogic);
|
|
if (err)
|
|
return err;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem0");
|
|
if (!res) {
|
|
dev_err(dev, "missing \"mem0\"\n");
|
|
return -EINVAL;
|
|
}
|
|
ep->mem_res = res;
|
|
|
|
ep->max_regions = AML_PCI_MAX_RESOURCES;
|
|
of_property_read_u32(dev->of_node, "max-outbound-regions",
|
|
&ep->max_regions);
|
|
|
|
ep->ob_addr = devm_kcalloc(dev,
|
|
ep->max_regions, sizeof(*ep->ob_addr),
|
|
GFP_KERNEL);
|
|
if (!ep->ob_addr)
|
|
return -ENOMEM;
|
|
|
|
err = of_property_read_u8(dev->of_node, "max-functions",
|
|
&ep->max_functions);
|
|
if (err < 0 || ep->max_functions > 1)
|
|
ep->max_functions = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t amlogic_reset_gpio_handler(int irq, void *data)
|
|
{
|
|
struct amlogic_pcie_ep *ep = data;
|
|
|
|
schedule_delayed_work(&ep->work, 0);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static const struct of_device_id amlogic_pcie_ep_of_match[] = {
|
|
{ .compatible = "amlogic,amlogic-pcie-ep"},
|
|
{ .compatible = "amlogic, amlogic-pcie-ep"},
|
|
{},
|
|
};
|
|
|
|
static int amlogic_pcie_ep_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct amlogic_pcie_ep *ep;
|
|
struct amlogic_pcie *amlogic;
|
|
struct pci_epc *epc;
|
|
int err;
|
|
|
|
ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
|
|
if (!ep)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, ep);
|
|
amlogic = &ep->amlogic;
|
|
amlogic->is_rc = false;
|
|
amlogic->dev = dev;
|
|
|
|
if (ep_link_times)
|
|
amlogic->link_times = ep_link_times;
|
|
|
|
err = amlogic_pcie_parse_ep_dt(amlogic, ep);
|
|
if (err)
|
|
return err;
|
|
|
|
ep->reset_gpio = gpio_to_desc(amlogic->reset_gpio);
|
|
|
|
err = devm_request_threaded_irq(dev, gpiod_to_irq(ep->reset_gpio),
|
|
NULL, amlogic_reset_gpio_handler,
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
|
"amlogic-pcie-v3-ep-reset", ep);
|
|
|
|
epc = devm_pci_epc_create(dev, &amlogic_pcie_epc_ops);
|
|
if (IS_ERR(epc)) {
|
|
dev_err(dev, "failed to create epc device\n");
|
|
return PTR_ERR(epc);
|
|
}
|
|
|
|
ep->epc = epc;
|
|
epc->max_functions = ep->max_functions;
|
|
epc_set_drvdata(epc, ep);
|
|
|
|
err = amlogic_pcie_enable_clocks(amlogic);
|
|
if (err)
|
|
return err;
|
|
|
|
//TODO set phy pll
|
|
//err = amlogic_pcie_ep_init_port(amlogic);
|
|
//if (err)
|
|
// return err;
|
|
|
|
err = pci_epc_mem_init(epc, ep->mem_res->start,
|
|
resource_size(ep->mem_res), PAGE_SIZE);
|
|
if (err < 0) {
|
|
dev_err(dev, "failed to initialize the memory space\n");
|
|
goto err_disable_clocks;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_disable_clocks:
|
|
amlogic_pcie_deinit_phys(amlogic);
|
|
amlogic_pcie_disable_clocks(amlogic);
|
|
return err;
|
|
}
|
|
|
|
static int amlogic_pcie_ep_remove(struct platform_device *pdev)
|
|
{
|
|
struct amlogic_pcie_ep *ep = platform_get_drvdata(pdev);
|
|
struct amlogic_pcie *amlogic = &ep->amlogic;
|
|
|
|
amlogic_pcie_deinit_phys(amlogic);
|
|
amlogic_pcie_disable_clocks(amlogic);
|
|
pci_epc_mem_exit(ep->epc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver amlogic_pcie_ep_driver = {
|
|
.driver = {
|
|
.name = "amlogic-pcie-ep",
|
|
.of_match_table = amlogic_pcie_ep_of_match,
|
|
},
|
|
.probe = amlogic_pcie_ep_probe,
|
|
.remove = amlogic_pcie_ep_remove,
|
|
};
|
|
|
|
int __init aml_pcie_ep_v3_init(void)
|
|
{
|
|
return platform_driver_register(&amlogic_pcie_ep_driver);
|
|
}
|
|
|
|
void __exit aml_pcie_ep_v3_exit(void)
|
|
{
|
|
platform_driver_unregister(&amlogic_pcie_ep_driver);
|
|
}
|
|
|
|
/*
|
|
* module_platform_driver(amlogic_pcie_ep_driver);
|
|
*
|
|
* MODULE_AUTHOR("Amlogic Inc");
|
|
* MODULE_DESCRIPTION("Amlogic AXI PCIe EP driver");
|
|
* MODULE_LICENSE("GPL v2");
|
|
*/
|