2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2015-10-29 20:02:51 -05:00
/*
2015-11-27 01:17:05 +08:00
* PCIe host controller driver for HiSilicon SoCs
2015-10-29 20:02:51 -05:00
*
* Copyright ( C ) 2015 HiSilicon Co . , Ltd . http : //www.hisilicon.com
*
2015-11-27 01:17:05 +08:00
* Authors : Zhou Wang < wangzhou1 @ hisilicon . com >
* Dacai Zhu < zhudacai @ hisilicon . com >
* Gabriele Paoloni < gabriele . paoloni @ huawei . com >
2015-10-29 20:02:51 -05:00
*/
# include <linux/interrupt.h>
2016-07-02 19:13:25 -04:00
# include <linux/init.h>
2015-10-29 20:02:51 -05:00
# include <linux/mfd/syscon.h>
# include <linux/of_address.h>
# include <linux/of_pci.h>
# include <linux/platform_device.h>
2015-11-27 01:17:05 +08:00
# include <linux/of_device.h>
2016-12-01 00:45:35 -06:00
# include <linux/pci.h>
# include <linux/pci-acpi.h>
# include <linux/pci-ecam.h>
2015-10-29 20:02:51 -05:00
# include <linux/regmap.h>
2018-05-31 09:12:37 +08:00
# include "../../pci.h"
2016-12-01 00:45:35 -06:00
2017-02-06 14:25:04 +08:00
# if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
2016-12-01 00:45:35 -06:00
2017-02-07 08:41:09 -06:00
static int hisi_pcie_rd_conf ( struct pci_bus * bus , u32 devfn , int where ,
int size , u32 * val )
2016-12-01 00:45:35 -06:00
{
struct pci_config_window * cfg = bus - > sysdata ;
int dev = PCI_SLOT ( devfn ) ;
if ( bus - > number = = cfg - > busr . start ) {
/* access only one slot on each root port */
if ( dev > 0 )
return PCIBIOS_DEVICE_NOT_FOUND ;
else
return pci_generic_config_read32 ( bus , devfn , where ,
size , val ) ;
}
return pci_generic_config_read ( bus , devfn , where , size , val ) ;
}
2017-02-07 08:41:09 -06:00
static int hisi_pcie_wr_conf ( struct pci_bus * bus , u32 devfn ,
int where , int size , u32 val )
2016-12-01 00:45:35 -06:00
{
struct pci_config_window * cfg = bus - > sysdata ;
int dev = PCI_SLOT ( devfn ) ;
if ( bus - > number = = cfg - > busr . start ) {
/* access only one slot on each root port */
if ( dev > 0 )
return PCIBIOS_DEVICE_NOT_FOUND ;
else
return pci_generic_config_write32 ( bus , devfn , where ,
size , val ) ;
}
return pci_generic_config_write ( bus , devfn , where , size , val ) ;
}
static void __iomem * hisi_pcie_map_bus ( struct pci_bus * bus , unsigned int devfn ,
int where )
{
struct pci_config_window * cfg = bus - > sysdata ;
void __iomem * reg_base = cfg - > priv ;
if ( bus - > number = = cfg - > busr . start )
return reg_base + where ;
else
return pci_ecam_map_bus ( bus , devfn , where ) ;
}
2017-02-06 14:25:04 +08:00
# if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
2016-12-01 00:45:35 -06:00
static int hisi_pcie_init ( struct pci_config_window * cfg )
{
struct device * dev = cfg - > parent ;
struct acpi_device * adev = to_acpi_device ( dev ) ;
struct acpi_pci_root * root = acpi_driver_data ( adev ) ;
struct resource * res ;
void __iomem * reg_base ;
int ret ;
/*
* Retrieve RC base and size from a HISI0081 device with _UID
* matching our segment .
*/
res = devm_kzalloc ( dev , sizeof ( * res ) , GFP_KERNEL ) ;
if ( ! res )
return - ENOMEM ;
ret = acpi_get_rc_resources ( dev , " HISI0081 " , root - > segment , res ) ;
if ( ret ) {
dev_err ( dev , " can't get rc base address \n " ) ;
return - ENOMEM ;
}
2017-04-19 17:49:07 +01:00
reg_base = devm_pci_remap_cfgspace ( dev , res - > start , resource_size ( res ) ) ;
2016-12-01 00:45:35 -06:00
if ( ! reg_base )
return - ENOMEM ;
cfg - > priv = reg_base ;
return 0 ;
}
struct pci_ecam_ops hisi_pcie_ops = {
. bus_shift = 20 ,
. init = hisi_pcie_init ,
. pci_ops = {
. map_bus = hisi_pcie_map_bus ,
2017-02-07 08:41:09 -06:00
. read = hisi_pcie_rd_conf ,
. write = hisi_pcie_wr_conf ,
2016-12-01 00:45:35 -06:00
}
} ;
# endif
# ifdef CONFIG_PCI_HISI
2015-10-29 20:02:51 -05:00
# include "pcie-designware.h"
2016-10-06 13:34:24 -05:00
# define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
# define PCIE_HIP06_CTRL_OFF 0x1000
# define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c)
# define PCIE_LTSSM_LINKUP_STATE 0x11
# define PCIE_LTSSM_STATE_MASK 0x3F
2015-10-29 20:02:51 -05:00
2017-02-15 18:48:14 +05:30
# define to_hisi_pcie(x) dev_get_drvdata((x)->dev)
2015-10-29 20:02:51 -05:00
2015-11-27 01:17:05 +08:00
struct hisi_pcie ;
struct pcie_soc_ops {
2016-10-11 21:40:32 -05:00
int ( * hisi_pcie_link_up ) ( struct hisi_pcie * hisi_pcie ) ;
2015-11-27 01:17:05 +08:00
} ;
2015-10-29 20:02:51 -05:00
struct hisi_pcie {
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2015-10-29 20:02:51 -05:00
struct regmap * subctrl ;
u32 port_id ;
2017-01-31 14:00:48 -06:00
const struct pcie_soc_ops * soc_ops ;
2015-10-29 20:02:51 -05:00
} ;
2015-11-27 01:17:05 +08:00
/* HipXX PCIe host only supports 32-bit config access */
2015-10-29 20:02:51 -05:00
static int hisi_pcie_cfg_read ( struct pcie_port * pp , int where , int size ,
u32 * val )
{
u32 reg ;
u32 reg_val ;
void * walker = & reg_val ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2015-10-29 20:02:51 -05:00
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
2017-02-15 18:48:14 +05:30
reg_val = dw_pcie_readl_dbi ( pci , reg ) ;
2015-10-29 20:02:51 -05:00
if ( size = = 1 )
* val = * ( u8 __force * ) walker ;
else if ( size = = 2 )
* val = * ( u16 __force * ) walker ;
2015-12-04 16:32:25 -06:00
else if ( size = = 4 )
* val = reg_val ;
else
2015-10-29 20:02:51 -05:00
return PCIBIOS_BAD_REGISTER_NUMBER ;
return PCIBIOS_SUCCESSFUL ;
}
2015-11-27 01:17:05 +08:00
/* HipXX PCIe host only supports 32-bit config access */
2015-10-29 20:02:51 -05:00
static int hisi_pcie_cfg_write ( struct pcie_port * pp , int where , int size ,
u32 val )
{
u32 reg_val ;
u32 reg ;
void * walker = & reg_val ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2015-10-29 20:02:51 -05:00
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
if ( size = = 4 )
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , reg , val ) ;
2015-10-29 20:02:51 -05:00
else if ( size = = 2 ) {
2017-02-15 18:48:14 +05:30
reg_val = dw_pcie_readl_dbi ( pci , reg ) ;
2015-10-29 20:02:51 -05:00
* ( u16 __force * ) walker = val ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , reg , reg_val ) ;
2015-10-29 20:02:51 -05:00
} else if ( size = = 1 ) {
2017-02-15 18:48:14 +05:30
reg_val = dw_pcie_readl_dbi ( pci , reg ) ;
2015-10-29 20:02:51 -05:00
* ( u8 __force * ) walker = val ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , reg , reg_val ) ;
2015-10-29 20:02:51 -05:00
} else
return PCIBIOS_BAD_REGISTER_NUMBER ;
return PCIBIOS_SUCCESSFUL ;
}
2015-11-27 01:17:05 +08:00
static int hisi_pcie_link_up_hip05 ( struct hisi_pcie * hisi_pcie )
2015-10-29 20:02:51 -05:00
{
u32 val ;
regmap_read ( hisi_pcie - > subctrl , PCIE_SUBCTRL_SYS_STATE4_REG +
0x100 * hisi_pcie - > port_id , & val ) ;
return ( ( val & PCIE_LTSSM_STATE_MASK ) = = PCIE_LTSSM_LINKUP_STATE ) ;
}
2015-11-27 01:17:05 +08:00
static int hisi_pcie_link_up_hip06 ( struct hisi_pcie * hisi_pcie )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = hisi_pcie - > pci ;
2015-11-27 01:17:05 +08:00
u32 val ;
2017-02-15 18:48:14 +05:30
val = dw_pcie_readl_dbi ( pci , PCIE_SYS_STATE4 ) ;
2015-11-27 01:17:05 +08:00
return ( ( val & PCIE_LTSSM_STATE_MASK ) = = PCIE_LTSSM_LINKUP_STATE ) ;
}
2017-02-15 18:48:14 +05:30
static int hisi_pcie_link_up ( struct dw_pcie * pci )
2015-11-27 01:17:05 +08:00
{
2017-02-15 18:48:14 +05:30
struct hisi_pcie * hisi_pcie = to_hisi_pcie ( pci ) ;
2015-11-27 01:17:05 +08:00
return hisi_pcie - > soc_ops - > hisi_pcie_link_up ( hisi_pcie ) ;
}
2017-08-09 13:18:48 +05:30
static const struct dw_pcie_host_ops hisi_pcie_host_ops = {
2015-10-29 20:02:51 -05:00
. rd_own_conf = hisi_pcie_cfg_read ,
. wr_own_conf = hisi_pcie_cfg_write ,
} ;
2016-10-06 13:34:23 -05:00
static int hisi_add_pcie_port ( struct hisi_pcie * hisi_pcie ,
struct platform_device * pdev )
2015-10-29 20:02:51 -05:00
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = hisi_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
struct device * dev = & pdev - > dev ;
2015-10-29 20:02:51 -05:00
int ret ;
u32 port_id ;
2016-10-06 13:34:25 -05:00
if ( of_property_read_u32 ( dev - > of_node , " port-id " , & port_id ) ) {
dev_err ( dev , " failed to read port-id \n " ) ;
2015-10-29 20:02:51 -05:00
return - EINVAL ;
}
if ( port_id > 3 ) {
2016-10-06 13:34:25 -05:00
dev_err ( dev , " Invalid port-id: %d \n " , port_id ) ;
2015-10-29 20:02:51 -05:00
return - EINVAL ;
}
hisi_pcie - > port_id = port_id ;
pp - > ops = & hisi_pcie_host_ops ;
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
2016-10-06 13:34:25 -05:00
dev_err ( dev , " failed to initialize host \n " ) ;
2015-10-29 20:02:51 -05:00
return ret ;
}
return 0 ;
}
2017-02-15 18:48:14 +05:30
static const struct dw_pcie_ops dw_pcie_ops = {
. link_up = hisi_pcie_link_up ,
} ;
2015-11-24 15:38:07 -06:00
static int hisi_pcie_probe ( struct platform_device * pdev )
2015-10-29 20:02:51 -05:00
{
2016-10-06 13:34:25 -05:00
struct device * dev = & pdev - > dev ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2015-10-29 20:02:51 -05:00
struct hisi_pcie * hisi_pcie ;
struct resource * reg ;
int ret ;
2016-10-06 13:34:25 -05:00
hisi_pcie = devm_kzalloc ( dev , sizeof ( * hisi_pcie ) , GFP_KERNEL ) ;
2015-10-29 20:02:51 -05:00
if ( ! hisi_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 ;
pci - > ops = & dw_pcie_ops ;
2017-02-25 02:08:12 -08:00
hisi_pcie - > pci = pci ;
2017-01-31 14:00:48 -06:00
hisi_pcie - > soc_ops = of_device_get_match_data ( dev ) ;
2015-10-29 20:02:51 -05:00
hisi_pcie - > subctrl =
2017-01-31 14:00:48 -06:00
syscon_regmap_lookup_by_compatible ( " hisilicon,pcie-sas-subctrl " ) ;
2015-10-29 20:02:51 -05:00
if ( IS_ERR ( hisi_pcie - > subctrl ) ) {
2016-10-06 13:34:25 -05:00
dev_err ( dev , " cannot get subctrl base \n " ) ;
2015-10-29 20:02:51 -05:00
return PTR_ERR ( hisi_pcie - > subctrl ) ;
}
reg = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " rc_dbi " ) ;
2017-04-19 17:49:07 +01:00
pci - > dbi_base = devm_pci_remap_cfg_resource ( dev , reg ) ;
2017-02-15 18:48:14 +05:30
if ( IS_ERR ( pci - > dbi_base ) )
return PTR_ERR ( pci - > dbi_base ) ;
2017-02-15 18:48:11 +05:30
platform_set_drvdata ( pdev , hisi_pcie ) ;
2016-10-06 13:34:23 -05:00
ret = hisi_add_pcie_port ( hisi_pcie , pdev ) ;
2015-10-29 20:02:51 -05:00
if ( ret )
return ret ;
return 0 ;
}
2015-11-27 01:17:05 +08:00
static struct pcie_soc_ops hip05_ops = {
& hisi_pcie_link_up_hip05
} ;
static struct pcie_soc_ops hip06_ops = {
& hisi_pcie_link_up_hip06
} ;
2015-10-29 20:02:51 -05:00
static const struct of_device_id hisi_pcie_of_match [ ] = {
2015-11-27 01:17:05 +08:00
{
. compatible = " hisilicon,hip05-pcie " ,
. data = ( void * ) & hip05_ops ,
} ,
{
. compatible = " hisilicon,hip06-pcie " ,
. data = ( void * ) & hip06_ops ,
} ,
2015-10-29 20:02:51 -05:00
{ } ,
} ;
static struct platform_driver hisi_pcie_driver = {
. probe = hisi_pcie_probe ,
. driver = {
. name = " hisi-pcie " ,
. of_match_table = hisi_pcie_of_match ,
2017-04-20 15:36:25 -05:00
. suppress_bind_attrs = true ,
2015-10-29 20:02:51 -05:00
} ,
} ;
2016-07-02 19:13:25 -04:00
builtin_platform_driver ( hisi_pcie_driver ) ;
2016-12-01 00:45:35 -06:00
2017-02-06 14:25:04 +08:00
static int hisi_pcie_almost_ecam_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct pci_ecam_ops * ops ;
ops = ( struct pci_ecam_ops * ) of_device_get_match_data ( dev ) ;
return pci_host_common_probe ( pdev , ops ) ;
}
static int hisi_pcie_platform_init ( struct pci_config_window * cfg )
{
struct device * dev = cfg - > parent ;
struct platform_device * pdev = to_platform_device ( dev ) ;
struct resource * res ;
void __iomem * reg_base ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! res ) {
dev_err ( dev , " missing \" reg[1] \" property \n " ) ;
return - EINVAL ;
}
2017-04-19 17:49:07 +01:00
reg_base = devm_pci_remap_cfgspace ( dev , res - > start , resource_size ( res ) ) ;
2017-02-06 14:25:04 +08:00
if ( ! reg_base )
return - ENOMEM ;
cfg - > priv = reg_base ;
return 0 ;
}
struct pci_ecam_ops hisi_pcie_platform_ops = {
. bus_shift = 20 ,
. init = hisi_pcie_platform_init ,
. pci_ops = {
. map_bus = hisi_pcie_map_bus ,
2017-02-07 08:41:09 -06:00
. read = hisi_pcie_rd_conf ,
. write = hisi_pcie_wr_conf ,
2017-02-06 14:25:04 +08:00
}
} ;
static const struct of_device_id hisi_pcie_almost_ecam_of_match [ ] = {
{
2017-03-23 21:18:17 +08:00
. compatible = " hisilicon,hip06-pcie-ecam " ,
2017-02-06 14:25:04 +08:00
. data = ( void * ) & hisi_pcie_platform_ops ,
} ,
2017-03-23 21:18:17 +08:00
{
. compatible = " hisilicon,hip07-pcie-ecam " ,
. data = ( void * ) & hisi_pcie_platform_ops ,
} ,
2017-02-06 14:25:04 +08:00
{ } ,
} ;
static struct platform_driver hisi_pcie_almost_ecam_driver = {
. probe = hisi_pcie_almost_ecam_probe ,
. driver = {
. name = " hisi-pcie-almost-ecam " ,
. of_match_table = hisi_pcie_almost_ecam_of_match ,
2017-04-20 15:36:25 -05:00
. suppress_bind_attrs = true ,
2017-02-06 14:25:04 +08:00
} ,
} ;
builtin_platform_driver ( hisi_pcie_almost_ecam_driver ) ;
# endif
2016-12-01 00:45:35 -06:00
# endif