2014-11-05 11:45:11 +03:00
/*
* PCIe host controller driver for Freescale Layerscape SoCs
*
* Copyright ( C ) 2014 Freescale Semiconductor .
*
2015-10-16 10:19:19 +03:00
* Author : Minghuan Lian < Minghuan . Lian @ freescale . com >
2014-11-05 11:45:11 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/interrupt.h>
2016-07-03 02:13:27 +03:00
# include <linux/init.h>
2014-11-05 11:45:11 +03:00
# include <linux/of_pci.h>
# include <linux/of_platform.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/pci.h>
# include <linux/platform_device.h>
# include <linux/resource.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include "pcie-designware.h"
/* PEX1/2 Misc Ports Status Register */
# define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
# define LTSSM_STATE_SHIFT 20
# define LTSSM_STATE_MASK 0x3f
# define LTSSM_PCIE_L0 0x11 /* L0 state */
2015-10-16 10:19:19 +03:00
/* PEX Internal Configuration Registers */
# define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
# define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */
2017-08-28 13:52:58 +03:00
# define PCIE_IATU_NUM 6
2015-10-16 10:19:17 +03:00
struct ls_pcie_drvdata {
2015-10-16 10:19:19 +03:00
u32 lut_offset ;
u32 ltssm_shift ;
2016-10-25 15:36:56 +03:00
u32 lut_dbg ;
2017-06-05 11:53:46 +03:00
const struct dw_pcie_host_ops * ops ;
2017-02-15 16:18:14 +03:00
const struct dw_pcie_ops * dw_pcie_ops ;
2015-10-16 10:19:17 +03:00
} ;
2014-11-05 11:45:11 +03:00
struct ls_pcie {
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci ;
2015-10-16 10:19:19 +03:00
void __iomem * lut ;
2014-11-05 11:45:11 +03:00
struct regmap * scfg ;
2015-10-16 10:19:17 +03:00
const struct ls_pcie_drvdata * drvdata ;
2014-11-05 11:45:11 +03:00
int index ;
} ;
2017-02-15 16:18:14 +03:00
# define to_ls_pcie(x) dev_get_drvdata((x)->dev)
2014-11-05 11:45:11 +03:00
2015-10-16 10:19:16 +03:00
static bool ls_pcie_is_bridge ( struct ls_pcie * pcie )
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = pcie - > pci ;
2015-10-16 10:19:16 +03:00
u32 header_type ;
2017-02-15 16:18:14 +03:00
header_type = ioread8 ( pci - > dbi_base + PCI_HEADER_TYPE ) ;
2015-10-16 10:19:16 +03:00
header_type & = 0x7f ;
return header_type = = PCI_HEADER_TYPE_BRIDGE ;
}
2015-10-16 10:19:19 +03:00
/* Clear multi-function bit */
static void ls_pcie_clear_multifunction ( struct ls_pcie * pcie )
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = pcie - > pci ;
iowrite8 ( PCI_HEADER_TYPE_BRIDGE , pci - > dbi_base + PCI_HEADER_TYPE ) ;
2015-10-16 10:19:19 +03:00
}
/* Fix class value */
static void ls_pcie_fix_class ( struct ls_pcie * pcie )
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = pcie - > pci ;
iowrite16 ( PCI_CLASS_BRIDGE_PCI , pci - > dbi_base + PCI_CLASS_DEVICE ) ;
2015-10-16 10:19:19 +03:00
}
2016-03-01 02:24:15 +03:00
/* Drop MSG TLP except for Vendor MSG */
static void ls_pcie_drop_msg_tlp ( struct ls_pcie * pcie )
{
u32 val ;
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = pcie - > pci ;
2016-03-01 02:24:15 +03:00
2017-02-15 16:18:14 +03:00
val = ioread32 ( pci - > dbi_base + PCIE_STRFMR1 ) ;
2016-03-01 02:24:15 +03:00
val & = 0xDFFFFFFF ;
2017-02-15 16:18:14 +03:00
iowrite32 ( val , pci - > dbi_base + PCIE_STRFMR1 ) ;
2016-03-01 02:24:15 +03:00
}
2017-08-28 13:52:58 +03:00
static void ls_pcie_disable_outbound_atus ( struct ls_pcie * pcie )
{
int i ;
for ( i = 0 ; i < PCIE_IATU_NUM ; i + + )
dw_pcie_disable_atu ( pcie - > pci , DW_PCIE_REGION_OUTBOUND , i ) ;
}
2017-02-15 16:18:14 +03:00
static int ls1021_pcie_link_up ( struct dw_pcie * pci )
2014-11-05 11:45:11 +03:00
{
u32 state ;
2017-02-15 16:18:14 +03:00
struct ls_pcie * pcie = to_ls_pcie ( pci ) ;
2014-11-05 11:45:11 +03:00
2015-10-16 10:19:17 +03:00
if ( ! pcie - > scfg )
return 0 ;
2014-11-05 11:45:11 +03:00
regmap_read ( pcie - > scfg , SCFG_PEXMSCPORTSR ( pcie - > index ) , & state ) ;
state = ( state > > LTSSM_STATE_SHIFT ) & LTSSM_STATE_MASK ;
if ( state < LTSSM_PCIE_L0 )
return 0 ;
return 1 ;
}
2017-08-28 13:52:56 +03:00
static int ls_pcie_link_up ( struct dw_pcie * pci )
{
struct ls_pcie * pcie = to_ls_pcie ( pci ) ;
u32 state ;
state = ( ioread32 ( pcie - > lut + pcie - > drvdata - > lut_dbg ) > >
pcie - > drvdata - > ltssm_shift ) &
LTSSM_STATE_MASK ;
if ( state < LTSSM_PCIE_L0 )
return 0 ;
return 1 ;
}
static int ls_pcie_host_init ( struct pcie_port * pp )
{
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct ls_pcie * pcie = to_ls_pcie ( pci ) ;
2017-08-28 13:52:58 +03:00
/*
* Disable outbound windows configured by the bootloader to avoid
* one transaction hitting multiple outbound windows .
* dw_pcie_setup_rc ( ) will reconfigure the outbound windows .
*/
ls_pcie_disable_outbound_atus ( pcie ) ;
2017-08-28 13:52:56 +03:00
iowrite32 ( 1 , pci - > dbi_base + PCIE_DBI_RO_WR_EN ) ;
ls_pcie_fix_class ( pcie ) ;
ls_pcie_clear_multifunction ( pcie ) ;
iowrite32 ( 0 , pci - > dbi_base + PCIE_DBI_RO_WR_EN ) ;
ls_pcie_drop_msg_tlp ( pcie ) ;
dw_pcie_setup_rc ( pp ) ;
return 0 ;
}
2017-07-16 09:39:45 +03:00
static int ls1021_pcie_host_init ( struct pcie_port * pp )
2015-06-03 00:24:25 +03:00
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct ls_pcie * pcie = to_ls_pcie ( pci ) ;
struct device * dev = pci - > dev ;
2016-03-01 02:24:15 +03:00
u32 index [ 2 ] ;
2017-07-16 09:39:45 +03:00
int ret ;
2015-10-16 10:19:17 +03:00
2016-10-06 21:38:05 +03:00
pcie - > scfg = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
2015-10-16 10:19:17 +03:00
" fsl,pcie-scfg " ) ;
if ( IS_ERR ( pcie - > scfg ) ) {
2017-07-16 09:39:45 +03:00
ret = PTR_ERR ( pcie - > scfg ) ;
2016-10-06 21:38:05 +03:00
dev_err ( dev , " No syscfg phandle specified \n " ) ;
2015-10-16 10:19:17 +03:00
pcie - > scfg = NULL ;
2017-07-16 09:39:45 +03:00
return ret ;
2015-10-16 10:19:17 +03:00
}
2016-10-06 21:38:05 +03:00
if ( of_property_read_u32_array ( dev - > of_node ,
2015-10-16 10:19:17 +03:00
" fsl,pcie-scfg " , index , 2 ) ) {
pcie - > scfg = NULL ;
2017-07-16 09:39:45 +03:00
return - EINVAL ;
2015-10-16 10:19:17 +03:00
}
pcie - > index = index [ 1 ] ;
2015-06-03 00:24:25 +03:00
2017-08-28 13:52:57 +03:00
return ls_pcie_host_init ( pp ) ;
2014-11-05 11:45:11 +03:00
}
2015-10-16 10:19:20 +03:00
static int ls_pcie_msi_host_init ( struct pcie_port * pp ,
struct msi_controller * chip )
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct device * dev = pci - > dev ;
2016-10-06 21:38:05 +03:00
struct device_node * np = dev - > of_node ;
2015-10-16 10:19:20 +03:00
struct device_node * msi_node ;
/*
* The MSI domain is set by the generic of_msi_configure ( ) . This
* . msi_host_init ( ) function keeps us from doing the default MSI
* domain setup in dw_pcie_host_init ( ) and also enforces the
* requirement that " msi-parent " exists .
*/
msi_node = of_parse_phandle ( np , " msi-parent " , 0 ) ;
if ( ! msi_node ) {
2016-10-06 21:38:05 +03:00
dev_err ( dev , " failed to find msi-parent \n " ) ;
2015-10-16 10:19:20 +03:00
return - EINVAL ;
}
return 0 ;
}
2017-06-05 11:53:46 +03:00
static const struct dw_pcie_host_ops ls1021_pcie_host_ops = {
2015-10-16 10:19:17 +03:00
. host_init = ls1021_pcie_host_init ,
2015-10-16 10:19:20 +03:00
. msi_host_init = ls_pcie_msi_host_init ,
2015-10-16 10:19:17 +03:00
} ;
2017-06-05 11:53:46 +03:00
static const struct dw_pcie_host_ops ls_pcie_host_ops = {
2015-10-16 10:19:19 +03:00
. host_init = ls_pcie_host_init ,
2015-10-16 10:19:20 +03:00
. msi_host_init = ls_pcie_msi_host_init ,
2015-10-16 10:19:19 +03:00
} ;
2017-02-15 16:18:14 +03:00
static const struct dw_pcie_ops dw_ls1021_pcie_ops = {
. link_up = ls1021_pcie_link_up ,
} ;
static const struct dw_pcie_ops dw_ls_pcie_ops = {
. link_up = ls_pcie_link_up ,
} ;
2015-10-16 10:19:17 +03:00
static struct ls_pcie_drvdata ls1021_drvdata = {
. ops = & ls1021_pcie_host_ops ,
2017-02-15 16:18:14 +03:00
. dw_pcie_ops = & dw_ls1021_pcie_ops ,
2014-11-05 11:45:11 +03:00
} ;
2015-10-16 10:19:19 +03:00
static struct ls_pcie_drvdata ls1043_drvdata = {
. lut_offset = 0x10000 ,
. ltssm_shift = 24 ,
2016-10-25 15:36:56 +03:00
. lut_dbg = 0x7fc ,
. ops = & ls_pcie_host_ops ,
2017-02-15 16:18:14 +03:00
. dw_pcie_ops = & dw_ls_pcie_ops ,
2016-10-25 15:36:56 +03:00
} ;
static struct ls_pcie_drvdata ls1046_drvdata = {
. lut_offset = 0x80000 ,
. ltssm_shift = 24 ,
. lut_dbg = 0x407fc ,
2015-10-16 10:19:19 +03:00
. ops = & ls_pcie_host_ops ,
2017-02-15 16:18:14 +03:00
. dw_pcie_ops = & dw_ls_pcie_ops ,
2015-10-16 10:19:19 +03:00
} ;
static struct ls_pcie_drvdata ls2080_drvdata = {
. lut_offset = 0x80000 ,
. ltssm_shift = 0 ,
2016-10-25 15:36:56 +03:00
. lut_dbg = 0x7fc ,
2015-10-16 10:19:19 +03:00
. ops = & ls_pcie_host_ops ,
2017-02-15 16:18:14 +03:00
. dw_pcie_ops = & dw_ls_pcie_ops ,
2015-10-16 10:19:19 +03:00
} ;
2015-10-16 10:19:17 +03:00
static const struct of_device_id ls_pcie_of_match [ ] = {
{ . compatible = " fsl,ls1021a-pcie " , . data = & ls1021_drvdata } ,
2015-10-16 10:19:19 +03:00
{ . compatible = " fsl,ls1043a-pcie " , . data = & ls1043_drvdata } ,
2016-10-25 15:36:56 +03:00
{ . compatible = " fsl,ls1046a-pcie " , . data = & ls1046_drvdata } ,
2015-10-16 10:19:19 +03:00
{ . compatible = " fsl,ls2080a-pcie " , . data = & ls2080_drvdata } ,
2016-01-27 20:32:05 +03:00
{ . compatible = " fsl,ls2085a-pcie " , . data = & ls2080_drvdata } ,
2015-10-16 10:19:17 +03:00
{ } ,
} ;
2016-10-06 21:38:06 +03:00
static int __init ls_add_pcie_port ( struct ls_pcie * pcie )
2014-11-05 11:45:11 +03:00
{
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci = pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
struct device * dev = pci - > dev ;
2014-11-05 11:45:11 +03:00
int ret ;
2017-02-15 16:18:14 +03:00
pp - > ops = pcie - > drvdata - > ops ;
2014-11-05 11:45:11 +03:00
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
2016-10-06 21:38:05 +03:00
dev_err ( dev , " failed to initialize host \n " ) ;
2014-11-05 11:45:11 +03:00
return ret ;
}
return 0 ;
}
static int __init ls_pcie_probe ( struct platform_device * pdev )
{
2016-10-06 21:38:05 +03:00
struct device * dev = & pdev - > dev ;
2017-02-15 16:18:14 +03:00
struct dw_pcie * pci ;
2014-11-05 11:45:11 +03:00
struct ls_pcie * pcie ;
struct resource * dbi_base ;
int ret ;
2016-10-06 21:38:05 +03:00
pcie = devm_kzalloc ( dev , sizeof ( * pcie ) , GFP_KERNEL ) ;
2014-11-05 11:45:11 +03:00
if ( ! pcie )
return - ENOMEM ;
2017-02-15 16:18:14 +03:00
pci = devm_kzalloc ( dev , sizeof ( * pci ) , GFP_KERNEL ) ;
if ( ! pci )
return - ENOMEM ;
2017-02-01 01:36:11 +03:00
pcie - > drvdata = of_device_get_match_data ( dev ) ;
2017-02-15 16:18:14 +03:00
pci - > dev = dev ;
pci - > ops = pcie - > drvdata - > dw_pcie_ops ;
2016-10-06 21:38:06 +03:00
2017-02-25 13:08:12 +03:00
pcie - > pci = pci ;
2014-11-05 11:45:11 +03:00
dbi_base = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " regs " ) ;
2017-04-19 19:49:08 +03:00
pci - > dbi_base = devm_pci_remap_cfg_resource ( dev , dbi_base ) ;
2017-02-15 16:18:14 +03:00
if ( IS_ERR ( pci - > dbi_base ) )
return PTR_ERR ( pci - > dbi_base ) ;
2014-11-05 11:45:11 +03:00
2017-02-15 16:18:14 +03:00
pcie - > lut = pci - > dbi_base + pcie - > drvdata - > lut_offset ;
2014-11-05 11:45:11 +03:00
2015-10-16 10:19:16 +03:00
if ( ! ls_pcie_is_bridge ( pcie ) )
return - ENODEV ;
2017-02-15 16:18:11 +03:00
platform_set_drvdata ( pdev , pcie ) ;
2016-10-06 21:38:06 +03:00
ret = ls_add_pcie_port ( pcie ) ;
2014-11-05 11:45:11 +03:00
if ( ret < 0 )
return ret ;
return 0 ;
}
static struct platform_driver ls_pcie_driver = {
. driver = {
. name = " layerscape-pcie " ,
. of_match_table = ls_pcie_of_match ,
2017-04-20 23:36:25 +03:00
. suppress_bind_attrs = true ,
2014-11-05 11:45:11 +03:00
} ,
} ;
2016-07-03 02:13:27 +03:00
builtin_platform_driver_probe ( ls_pcie_driver , ls_pcie_probe ) ;