2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2016-03-10 14:44:52 -06:00
/*
* PCIe RC driver for Synopsys DesignWare Core
*
* Copyright ( C ) 2015 - 2016 Synopsys , Inc . ( www . synopsys . com )
*
2016-11-15 16:10:47 +00:00
* Authors : Joao Pinto < Joao . Pinto @ synopsys . com >
2016-03-10 14:44:52 -06:00
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2016-07-02 19:13:23 -04:00
# include <linux/init.h>
2018-05-15 15:41:42 +01:00
# include <linux/of_device.h>
2016-03-10 14:44:52 -06:00
# include <linux/pci.h>
# include <linux/platform_device.h>
# include <linux/resource.h>
# include <linux/types.h>
2018-05-15 15:41:42 +01:00
# include <linux/regmap.h>
2016-03-10 14:44:52 -06:00
# include "pcie-designware.h"
struct dw_plat_pcie {
2018-05-15 15:41:42 +01:00
struct dw_pcie * pci ;
struct regmap * regmap ;
enum dw_pcie_device_mode mode ;
2016-03-10 14:44:52 -06:00
} ;
2018-05-15 15:41:42 +01:00
struct dw_plat_pcie_of_data {
enum dw_pcie_device_mode mode ;
} ;
static const struct of_device_id dw_plat_pcie_of_match [ ] ;
2017-07-15 23:39:45 -07:00
static int dw_plat_pcie_host_init ( struct pcie_port * pp )
2016-03-10 14:44:52 -06:00
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2016-03-10 14:44:52 -06:00
dw_pcie_setup_rc ( pp ) ;
2017-02-15 18:48:14 +05:30
dw_pcie_wait_for_link ( pci ) ;
2016-03-10 14:44:52 -06:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) )
dw_pcie_msi_init ( pp ) ;
2017-07-15 23:39:45 -07:00
return 0 ;
2016-03-10 14:44:52 -06:00
}
2018-05-14 16:06:33 +01:00
static void dw_plat_set_num_vectors ( struct pcie_port * pp )
{
pp - > num_vectors = MAX_MSI_IRQS ;
}
2017-06-05 16:53:46 +08:00
static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = {
2016-03-10 14:44:52 -06:00
. host_init = dw_plat_pcie_host_init ,
2018-05-14 16:06:33 +01:00
. set_num_vectors = dw_plat_set_num_vectors ,
2016-03-10 14:44:52 -06:00
} ;
2018-05-15 15:41:42 +01:00
static int dw_plat_pcie_establish_link ( struct dw_pcie * pci )
{
return 0 ;
}
static const struct dw_pcie_ops dw_pcie_ops = {
. start_link = dw_plat_pcie_establish_link ,
} ;
static void dw_plat_pcie_ep_init ( struct dw_pcie_ep * ep )
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
enum pci_barno bar ;
for ( bar = BAR_0 ; bar < = BAR_5 ; bar + + )
dw_pcie_ep_reset_bar ( pci , bar ) ;
}
static int dw_plat_pcie_ep_raise_irq ( struct dw_pcie_ep * ep , u8 func_no ,
enum pci_epc_irq_type type ,
2018-07-19 10:32:13 +02:00
u16 interrupt_num )
2018-05-15 15:41:42 +01:00
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
switch ( type ) {
case PCI_EPC_IRQ_LEGACY :
2018-07-19 10:32:16 +02:00
return dw_pcie_ep_raise_legacy_irq ( ep , func_no ) ;
2018-05-15 15:41:42 +01:00
case PCI_EPC_IRQ_MSI :
return dw_pcie_ep_raise_msi_irq ( ep , func_no , interrupt_num ) ;
2018-07-19 10:32:14 +02:00
case PCI_EPC_IRQ_MSIX :
return dw_pcie_ep_raise_msix_irq ( ep , func_no , interrupt_num ) ;
2018-05-15 15:41:42 +01:00
default :
dev_err ( pci - > dev , " UNKNOWN IRQ type \n " ) ;
}
return 0 ;
}
2019-01-14 16:45:01 +05:30
static const struct pci_epc_features dw_plat_pcie_epc_features = {
. linkup_notifier = false ,
. msi_capable = true ,
. msix_capable = true ,
} ;
static const struct pci_epc_features *
dw_plat_pcie_get_features ( struct dw_pcie_ep * ep )
{
return & dw_plat_pcie_epc_features ;
}
2019-03-25 15:09:40 +05:30
static const struct dw_pcie_ep_ops pcie_ep_ops = {
2018-05-15 15:41:42 +01:00
. ep_init = dw_plat_pcie_ep_init ,
. raise_irq = dw_plat_pcie_ep_raise_irq ,
2019-01-14 16:45:01 +05:30
. get_features = dw_plat_pcie_get_features ,
2018-05-15 15:41:42 +01:00
} ;
static int dw_plat_add_pcie_port ( struct dw_plat_pcie * dw_plat_pcie ,
2016-03-10 14:44:52 -06:00
struct platform_device * pdev )
{
2018-05-15 15:41:42 +01:00
struct dw_pcie * pci = dw_plat_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
2017-02-15 18:48:14 +05:30
struct device * dev = & pdev - > dev ;
2016-03-10 14:44:52 -06:00
int ret ;
pp - > irq = platform_get_irq ( pdev , 1 ) ;
if ( pp - > irq < 0 )
return pp - > irq ;
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
pp - > msi_irq = platform_get_irq ( pdev , 0 ) ;
if ( pp - > msi_irq < 0 )
return pp - > msi_irq ;
}
pp - > ops = & dw_plat_pcie_host_ops ;
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
2018-05-15 15:41:42 +01:00
dev_err ( dev , " Failed to initialize host \n " ) ;
2016-03-10 14:44:52 -06:00
return ret ;
}
return 0 ;
}
2018-05-15 15:41:42 +01:00
static int dw_plat_add_pcie_ep ( struct dw_plat_pcie * dw_plat_pcie ,
struct platform_device * pdev )
{
int ret ;
struct dw_pcie_ep * ep ;
struct resource * res ;
struct device * dev = & pdev - > dev ;
struct dw_pcie * pci = dw_plat_pcie - > pci ;
ep = & pci - > ep ;
ep - > ops = & pcie_ep_ops ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dbi2 " ) ;
pci - > dbi_base2 = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( pci - > dbi_base2 ) )
return PTR_ERR ( pci - > dbi_base2 ) ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " addr_space " ) ;
if ( ! res )
return - EINVAL ;
ep - > phys_base = res - > start ;
ep - > addr_size = resource_size ( res ) ;
ret = dw_pcie_ep_init ( ep ) ;
if ( ret ) {
dev_err ( dev , " Failed to initialize endpoint \n " ) ;
return ret ;
}
return 0 ;
}
2017-04-03 17:35:12 -05:00
2016-03-10 14:44:52 -06:00
static int dw_plat_pcie_probe ( struct platform_device * pdev )
{
2016-10-06 13:32:15 -05:00
struct device * dev = & pdev - > dev ;
2016-03-10 14:44:52 -06:00
struct dw_plat_pcie * dw_plat_pcie ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2016-03-10 14:44:52 -06:00
struct resource * res ; /* Resource from DT */
int ret ;
2018-05-15 15:41:42 +01:00
const struct of_device_id * match ;
const struct dw_plat_pcie_of_data * data ;
enum dw_pcie_device_mode mode ;
match = of_match_device ( dw_plat_pcie_of_match , dev ) ;
if ( ! match )
return - EINVAL ;
data = ( struct dw_plat_pcie_of_data * ) match - > data ;
mode = ( enum dw_pcie_device_mode ) data - > mode ;
2016-03-10 14:44:52 -06:00
2016-10-06 13:32:15 -05:00
dw_plat_pcie = devm_kzalloc ( dev , sizeof ( * dw_plat_pcie ) , GFP_KERNEL ) ;
2016-03-10 14:44:52 -06:00
if ( ! dw_plat_pcie )
return - ENOMEM ;
2017-02-15 18:48:14 +05:30
pci = devm_kzalloc ( dev , sizeof ( * pci ) , GFP_KERNEL ) ;
if ( ! pci )
return - ENOMEM ;
pci - > dev = dev ;
2017-04-03 17:35:12 -05:00
pci - > ops = & dw_pcie_ops ;
2016-03-10 14:44:52 -06:00
2017-02-25 02:08:12 -08:00
dw_plat_pcie - > pci = pci ;
2018-05-15 15:41:42 +01:00
dw_plat_pcie - > mode = mode ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dbi " ) ;
if ( ! res )
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2017-02-25 02:08:12 -08:00
2017-02-15 18:48:14 +05:30
pci - > dbi_base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( pci - > dbi_base ) )
return PTR_ERR ( pci - > dbi_base ) ;
2016-03-10 14:44:52 -06:00
2017-02-15 18:48:11 +05:30
platform_set_drvdata ( pdev , dw_plat_pcie ) ;
2018-05-15 15:41:42 +01:00
switch ( dw_plat_pcie - > mode ) {
case DW_PCIE_RC_TYPE :
if ( ! IS_ENABLED ( CONFIG_PCIE_DW_PLAT_HOST ) )
return - ENODEV ;
ret = dw_plat_add_pcie_port ( dw_plat_pcie , pdev ) ;
if ( ret < 0 )
return ret ;
break ;
case DW_PCIE_EP_TYPE :
if ( ! IS_ENABLED ( CONFIG_PCIE_DW_PLAT_EP ) )
return - ENODEV ;
ret = dw_plat_add_pcie_ep ( dw_plat_pcie , pdev ) ;
if ( ret < 0 )
return ret ;
break ;
default :
dev_err ( dev , " INVALID device type %d \n " , dw_plat_pcie - > mode ) ;
}
2016-03-10 14:44:52 -06:00
return 0 ;
}
2018-05-15 15:41:42 +01:00
static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = {
. mode = DW_PCIE_RC_TYPE ,
} ;
static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = {
. mode = DW_PCIE_EP_TYPE ,
} ;
2016-03-10 14:44:52 -06:00
static const struct of_device_id dw_plat_pcie_of_match [ ] = {
2018-05-15 15:41:42 +01:00
{
. compatible = " snps,dw-pcie " ,
. data = & dw_plat_pcie_rc_of_data ,
} ,
{
. compatible = " snps,dw-pcie-ep " ,
. data = & dw_plat_pcie_ep_of_data ,
} ,
2016-03-10 14:44:52 -06:00
{ } ,
} ;
static struct platform_driver dw_plat_pcie_driver = {
. driver = {
. name = " dw-pcie " ,
. of_match_table = dw_plat_pcie_of_match ,
2017-04-20 15:36:25 -05:00
. suppress_bind_attrs = true ,
2016-03-10 14:44:52 -06:00
} ,
. probe = dw_plat_pcie_probe ,
} ;
2016-07-02 19:13:23 -04:00
builtin_platform_driver ( dw_plat_pcie_driver ) ;