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"
2015-11-27 01:17:05 +08:00
# define PCIE_LTSSM_LINKUP_STATE 0x11
# define PCIE_LTSSM_STATE_MASK 0x3F
# define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
# define PCIE_SYS_STATE4 0x31c
# define PCIE_HIP06_CTRL_OFF 0x1000
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 {
int ( * hisi_pcie_link_up ) ( struct hisi_pcie * pcie ) ;
} ;
2015-10-29 20:02:51 -05:00
struct hisi_pcie {
struct regmap * subctrl ;
void __iomem * reg_base ;
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
} ;
static inline void hisi_pcie_apb_writel ( struct hisi_pcie * pcie ,
u32 val , u32 reg )
{
writel ( val , pcie - > reg_base + reg ) ;
}
static inline u32 hisi_pcie_apb_readl ( struct hisi_pcie * pcie , u32 reg )
{
return readl ( pcie - > reg_base + reg ) ;
}
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 ;
struct hisi_pcie * pcie = to_hisi_pcie ( pp ) ;
void * walker = & reg_val ;
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
reg_val = hisi_pcie_apb_readl ( pcie , reg ) ;
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 ;
struct hisi_pcie * pcie = to_hisi_pcie ( pp ) ;
void * walker = & reg_val ;
walker + = ( where & 0x3 ) ;
reg = where & ~ 0x3 ;
if ( size = = 4 )
hisi_pcie_apb_writel ( pcie , val , reg ) ;
else if ( size = = 2 ) {
reg_val = hisi_pcie_apb_readl ( pcie , reg ) ;
* ( u16 __force * ) walker = val ;
hisi_pcie_apb_writel ( pcie , reg_val , reg ) ;
} else if ( size = = 1 ) {
reg_val = hisi_pcie_apb_readl ( pcie , reg ) ;
* ( u8 __force * ) walker = val ;
hisi_pcie_apb_writel ( pcie , reg_val , reg ) ;
} 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 )
{
u32 val ;
val = hisi_pcie_apb_readl ( hisi_pcie , PCIE_HIP06_CTRL_OFF +
PCIE_SYS_STATE4 ) ;
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:25 -05:00
hisi_pcie - > reg_base = devm_ioremap_resource ( dev , reg ) ;
2015-10-29 20:02:51 -05:00
if ( IS_ERR ( hisi_pcie - > reg_base ) ) {
2016-10-06 13:34:25 -05:00
dev_err ( dev , " cannot get rc_dbi base \n " ) ;
2015-10-29 20:02:51 -05:00
return PTR_ERR ( hisi_pcie - > reg_base ) ;
}
hisi_pcie - > pp . dbi_base = hisi_pcie - > reg_base ;
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 ) ;