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
*
* 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/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>
2015-10-29 20:02:51 -05:00
# include <linux/regmap.h>
# 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
# define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp)
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 {
struct regmap * subctrl ;
u32 port_id ;
struct pcie_port pp ;
2015-11-27 01:17:05 +08:00
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 ;
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
2016-10-06 13:34:24 -05:00
reg_val = dw_pcie_readl_rc ( pp , 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 ;
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
if ( size = = 4 )
2016-10-06 13:34:24 -05:00
dw_pcie_writel_rc ( pp , reg , val ) ;
2015-10-29 20:02:51 -05:00
else if ( size = = 2 ) {
2016-10-06 13:34:24 -05:00
reg_val = dw_pcie_readl_rc ( pp , reg ) ;
2015-10-29 20:02:51 -05:00
* ( u16 __force * ) walker = val ;
2016-10-06 13:34:24 -05:00
dw_pcie_writel_rc ( pp , reg , reg_val ) ;
2015-10-29 20:02:51 -05:00
} else if ( size = = 1 ) {
2016-10-06 13:34:24 -05:00
reg_val = dw_pcie_readl_rc ( pp , reg ) ;
2015-10-29 20:02:51 -05:00
* ( u8 __force * ) walker = val ;
2016-10-06 13:34:24 -05:00
dw_pcie_writel_rc ( pp , 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 )
{
2016-10-06 13:34:24 -05:00
struct pcie_port * pp = & hisi_pcie - > pp ;
2015-11-27 01:17:05 +08:00
u32 val ;
2016-10-06 13:34:24 -05:00
val = dw_pcie_readl_rc ( pp , PCIE_SYS_STATE4 ) ;
2015-11-27 01:17:05 +08:00
return ( ( val & PCIE_LTSSM_STATE_MASK ) = = PCIE_LTSSM_LINKUP_STATE ) ;
}
static int hisi_pcie_link_up ( struct pcie_port * pp )
{
struct hisi_pcie * hisi_pcie = to_hisi_pcie ( pp ) ;
return hisi_pcie - > soc_ops - > hisi_pcie_link_up ( hisi_pcie ) ;
}
2015-10-29 20:02:51 -05:00
static struct pcie_host_ops hisi_pcie_host_ops = {
. rd_own_conf = hisi_pcie_cfg_read ,
. wr_own_conf = hisi_pcie_cfg_write ,
. link_up = hisi_pcie_link_up ,
} ;
2015-11-24 15:38:07 -06:00
static int hisi_add_pcie_port ( struct pcie_port * pp ,
2015-10-29 20:02:51 -05:00
struct platform_device * pdev )
{
2016-10-06 13:34:25 -05:00
struct device * dev = pp - > dev ;
2015-10-29 20:02:51 -05:00
int ret ;
u32 port_id ;
struct hisi_pcie * hisi_pcie = to_hisi_pcie ( pp ) ;
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 ;
}
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 ;
2015-10-29 20:02:51 -05:00
struct hisi_pcie * hisi_pcie ;
struct pcie_port * pp ;
2015-11-27 01:17:05 +08:00
const struct of_device_id * match ;
2015-10-29 20:02:51 -05:00
struct resource * reg ;
2015-11-27 01:17:05 +08:00
struct device_driver * driver ;
2015-10-29 20:02:51 -05:00
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 ;
pp = & hisi_pcie - > pp ;
2016-10-06 13:34:25 -05:00
pp - > dev = dev ;
driver = dev - > driver ;
2015-11-27 01:17:05 +08:00
2016-10-06 13:34:25 -05:00
match = of_match_device ( driver - > of_match_table , dev ) ;
2015-11-27 01:17:05 +08:00
hisi_pcie - > soc_ops = ( struct pcie_soc_ops * ) match - > data ;
2015-10-29 20:02:51 -05:00
hisi_pcie - > subctrl =
syscon_regmap_lookup_by_compatible ( " hisilicon,pcie-sas-subctrl " ) ;
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 " ) ;
2016-10-06 13:34:23 -05:00
pp - > dbi_base = devm_ioremap_resource ( dev , reg ) ;
if ( IS_ERR ( pp - > dbi_base ) ) {
2016-10-06 13:34:25 -05:00
dev_err ( dev , " cannot get rc_dbi base \n " ) ;
2016-10-06 13:34:23 -05:00
return PTR_ERR ( pp - > dbi_base ) ;
2015-10-29 20:02:51 -05:00
}
ret = hisi_add_pcie_port ( pp , pdev ) ;
if ( ret )
return ret ;
2016-10-06 13:34:25 -05:00
dev_warn ( dev , " only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields \n " ) ;
2015-10-29 20:02:51 -05:00
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 ,
} ,
} ;
2016-07-02 19:13:25 -04:00
builtin_platform_driver ( hisi_pcie_driver ) ;