2019-02-21 06:16:19 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe controller EP driver for Freescale Layerscape SoCs
*
* Copyright ( C ) 2018 NXP Semiconductor .
*
* Author : Xiaowei Bao < xiaowei . bao @ nxp . com >
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/of_pci.h>
# include <linux/of_platform.h>
# include <linux/of_address.h>
# include <linux/pci.h>
# include <linux/platform_device.h>
# include <linux/resource.h>
# include "pcie-designware.h"
2020-09-18 11:00:21 +03:00
# define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
struct ls_pcie_ep_drvdata {
u32 func_offset ;
const struct dw_pcie_ep_ops * ops ;
const struct dw_pcie_ops * dw_pcie_ops ;
2019-02-21 06:16:19 +03:00
} ;
2020-09-18 11:00:21 +03:00
struct ls_pcie_ep {
struct dw_pcie * pci ;
struct pci_epc_features * ls_epc ;
const struct ls_pcie_ep_drvdata * drvdata ;
} ;
2019-02-21 06:16:19 +03:00
static int ls_pcie_establish_link ( struct dw_pcie * pci )
{
return 0 ;
}
2020-09-18 11:00:21 +03:00
static const struct dw_pcie_ops dw_ls_pcie_ep_ops = {
2019-02-21 06:16:19 +03:00
. start_link = ls_pcie_establish_link ,
} ;
static const struct pci_epc_features *
ls_pcie_ep_get_features ( struct dw_pcie_ep * ep )
{
2020-09-18 11:00:19 +03:00
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
struct ls_pcie_ep * pcie = to_ls_pcie_ep ( pci ) ;
return pcie - > ls_epc ;
2019-02-21 06:16:19 +03:00
}
static void ls_pcie_ep_init ( struct dw_pcie_ep * ep )
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
2020-09-18 11:00:19 +03:00
struct ls_pcie_ep * pcie = to_ls_pcie_ep ( pci ) ;
struct dw_pcie_ep_func * ep_func ;
2019-02-21 06:16:19 +03:00
enum pci_barno bar ;
2020-09-18 11:00:19 +03:00
ep_func = dw_pcie_ep_get_func_from_ep ( ep , 0 ) ;
if ( ! ep_func )
return ;
2019-09-28 02:43:08 +03:00
for ( bar = 0 ; bar < PCI_STD_NUM_BARS ; bar + + )
2019-02-21 06:16:19 +03:00
dw_pcie_ep_reset_bar ( pci , bar ) ;
2020-09-18 11:00:19 +03:00
pcie - > ls_epc - > msi_capable = ep_func - > msi_cap ? true : false ;
pcie - > ls_epc - > msix_capable = ep_func - > msix_cap ? true : false ;
2019-02-21 06:16:19 +03:00
}
static int ls_pcie_ep_raise_irq ( struct dw_pcie_ep * ep , u8 func_no ,
2020-09-18 11:00:18 +03:00
enum pci_epc_irq_type type , u16 interrupt_num )
2019-02-21 06:16:19 +03:00
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
switch ( type ) {
case PCI_EPC_IRQ_LEGACY :
return dw_pcie_ep_raise_legacy_irq ( ep , func_no ) ;
case PCI_EPC_IRQ_MSI :
return dw_pcie_ep_raise_msi_irq ( ep , func_no , interrupt_num ) ;
case PCI_EPC_IRQ_MSIX :
2020-09-18 11:00:20 +03:00
return dw_pcie_ep_raise_msix_irq_doorbell ( ep , func_no ,
interrupt_num ) ;
2019-02-21 06:16:19 +03:00
default :
dev_err ( pci - > dev , " UNKNOWN IRQ type \n " ) ;
return - EINVAL ;
}
}
2020-09-18 11:00:21 +03:00
static unsigned int ls_pcie_ep_func_conf_select ( struct dw_pcie_ep * ep ,
u8 func_no )
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
struct ls_pcie_ep * pcie = to_ls_pcie_ep ( pci ) ;
WARN_ON ( func_no & & ! pcie - > drvdata - > func_offset ) ;
return pcie - > drvdata - > func_offset * func_no ;
}
static const struct dw_pcie_ep_ops ls_pcie_ep_ops = {
2019-02-21 06:16:19 +03:00
. ep_init = ls_pcie_ep_init ,
. raise_irq = ls_pcie_ep_raise_irq ,
. get_features = ls_pcie_ep_get_features ,
2020-09-18 11:00:21 +03:00
. func_conf_select = ls_pcie_ep_func_conf_select ,
} ;
static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = {
. ops = & ls_pcie_ep_ops ,
. dw_pcie_ops = & dw_ls_pcie_ep_ops ,
} ;
static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = {
. func_offset = 0x20000 ,
. ops = & ls_pcie_ep_ops ,
. dw_pcie_ops = & dw_ls_pcie_ep_ops ,
} ;
2020-10-26 08:14:48 +03:00
static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = {
. func_offset = 0x8000 ,
. ops = & ls_pcie_ep_ops ,
. dw_pcie_ops = & dw_ls_pcie_ep_ops ,
} ;
2020-09-18 11:00:21 +03:00
static const struct of_device_id ls_pcie_ep_of_match [ ] = {
{ . compatible = " fsl,ls1046a-pcie-ep " , . data = & ls1_ep_drvdata } ,
{ . compatible = " fsl,ls1088a-pcie-ep " , . data = & ls2_ep_drvdata } ,
{ . compatible = " fsl,ls2088a-pcie-ep " , . data = & ls2_ep_drvdata } ,
2020-10-26 08:14:48 +03:00
{ . compatible = " fsl,lx2160ar2-pcie-ep " , . data = & lx2_ep_drvdata } ,
2020-09-18 11:00:21 +03:00
{ } ,
2019-02-21 06:16:19 +03:00
} ;
static int __init ls_pcie_ep_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct dw_pcie * pci ;
struct ls_pcie_ep * pcie ;
2020-09-18 11:00:19 +03:00
struct pci_epc_features * ls_epc ;
2019-02-21 06:16:19 +03:00
struct resource * dbi_base ;
pcie = devm_kzalloc ( dev , sizeof ( * pcie ) , GFP_KERNEL ) ;
if ( ! pcie )
return - ENOMEM ;
pci = devm_kzalloc ( dev , sizeof ( * pci ) , GFP_KERNEL ) ;
if ( ! pci )
return - ENOMEM ;
2020-09-18 11:00:19 +03:00
ls_epc = devm_kzalloc ( dev , sizeof ( * ls_epc ) , GFP_KERNEL ) ;
if ( ! ls_epc )
return - ENOMEM ;
2020-09-18 11:00:21 +03:00
pcie - > drvdata = of_device_get_match_data ( dev ) ;
2019-02-21 06:16:19 +03:00
pci - > dev = dev ;
2020-09-18 11:00:21 +03:00
pci - > ops = pcie - > drvdata - > dw_pcie_ops ;
2019-02-21 06:16:19 +03:00
2021-03-11 06:37:45 +03:00
ls_epc - > bar_fixed_64bit = ( 1 < < BAR_2 ) | ( 1 < < BAR_4 ) ;
2020-09-18 11:00:19 +03:00
2020-09-18 11:00:21 +03:00
pcie - > pci = pci ;
2020-09-18 11:00:19 +03:00
pcie - > ls_epc = ls_epc ;
2020-09-18 11:00:21 +03:00
dbi_base = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " regs " ) ;
pci - > dbi_base = devm_pci_remap_cfg_resource ( dev , dbi_base ) ;
if ( IS_ERR ( pci - > dbi_base ) )
return PTR_ERR ( pci - > dbi_base ) ;
2020-11-06 00:11:46 +03:00
pci - > ep . ops = & ls_pcie_ep_ops ;
2020-09-18 11:00:21 +03:00
2019-02-21 06:16:19 +03:00
platform_set_drvdata ( pdev , pcie ) ;
2020-11-06 00:11:46 +03:00
return dw_pcie_ep_init ( & pci - > ep ) ;
2019-02-21 06:16:19 +03:00
}
static struct platform_driver ls_pcie_ep_driver = {
. driver = {
. name = " layerscape-pcie-ep " ,
. of_match_table = ls_pcie_ep_of_match ,
. suppress_bind_attrs = true ,
} ,
} ;
builtin_platform_driver_probe ( ls_pcie_ep_driver , ls_pcie_ep_probe ) ;