2014-02-11 11:39:26 +05:30
/*
* PCIe host controller driver for ST Microelectronics SPEAr13xx SoCs
*
* SPEAr13xx PCIe Glue Layer Source Code
*
* Copyright ( C ) 2010 - 2014 ST Microelectronics
2015-06-25 15:01:08 -07:00
* Pratyush Anand < pratyush . anand @ gmail . com >
2015-06-25 15:01:11 -07:00
* Mohit Kumar < mohit . kumar . dhaka @ gmail . com >
2014-02-11 11:39:26 +05:30
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2016-08-22 17:59:45 -04:00
# include <linux/init.h>
2014-02-11 11:39:26 +05:30
# include <linux/of.h>
# include <linux/pci.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/resource.h>
# include "pcie-designware.h"
struct spear13xx_pcie {
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2014-02-11 11:39:26 +05:30
void __iomem * app_base ;
struct phy * phy ;
struct clk * clk ;
bool is_gen1 ;
} ;
struct pcie_app_reg {
u32 app_ctrl_0 ; /* cr0 */
u32 app_ctrl_1 ; /* cr1 */
u32 app_status_0 ; /* cr2 */
u32 app_status_1 ; /* cr3 */
u32 msg_status ; /* cr4 */
u32 msg_payload ; /* cr5 */
u32 int_sts ; /* cr6 */
u32 int_clr ; /* cr7 */
u32 int_mask ; /* cr8 */
u32 mst_bmisc ; /* cr9 */
u32 phy_ctrl ; /* cr10 */
u32 phy_status ; /* cr11 */
u32 cxpl_debug_info_0 ; /* cr12 */
u32 cxpl_debug_info_1 ; /* cr13 */
u32 ven_msg_ctrl_0 ; /* cr14 */
u32 ven_msg_ctrl_1 ; /* cr15 */
u32 ven_msg_data_0 ; /* cr16 */
u32 ven_msg_data_1 ; /* cr17 */
u32 ven_msi_0 ; /* cr18 */
u32 ven_msi_1 ; /* cr19 */
u32 mst_rmisc ; /* cr20 */
} ;
/* CR0 ID */
# define APP_LTSSM_ENABLE_ID 3
# define DEVICE_TYPE_RC (4 << 25)
# define MISCTRL_EN_ID 30
# define REG_TRANSLATION_ENABLE 31
/* CR3 ID */
# define XMLH_LINK_UP (1 << 6)
/* CR6 */
# define MSI_CTRL_INT (1 << 26)
# define EXP_CAP_ID_OFFSET 0x70
2017-02-15 18:48:14 +05:30
# define to_spear13xx_pcie(x) dev_get_drvdata((x)->dev)
2014-02-11 11:39:26 +05:30
2016-10-06 13:42:08 -05:00
static int spear13xx_pcie_establish_link ( struct spear13xx_pcie * spear13xx_pcie )
2014-02-11 11:39:26 +05:30
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = spear13xx_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
2014-02-11 11:39:26 +05:30
struct pcie_app_reg * app_reg = spear13xx_pcie - > app_base ;
2016-10-06 13:42:08 -05:00
u32 val ;
2014-02-11 11:39:26 +05:30
u32 exp_cap_off = EXP_CAP_ID_OFFSET ;
2017-02-15 18:48:14 +05:30
if ( dw_pcie_link_up ( pci ) ) {
dev_err ( pci - > dev , " link already up \n " ) ;
2014-02-11 11:39:26 +05:30
return 0 ;
}
dw_pcie_setup_rc ( pp ) ;
/*
* this controller support only 128 bytes read size , however its
* default value in capability register is 512 bytes . So force
* it to 128 here .
*/
2017-02-15 18:48:14 +05:30
dw_pcie_read ( pci - > dbi_base + exp_cap_off + PCI_EXP_DEVCTL , 2 , & val ) ;
2014-02-11 11:39:26 +05:30
val & = ~ PCI_EXP_DEVCTL_READRQ ;
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + exp_cap_off + PCI_EXP_DEVCTL , 2 , val ) ;
2014-02-11 11:39:26 +05:30
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + PCI_VENDOR_ID , 2 , 0x104A ) ;
dw_pcie_write ( pci - > dbi_base + PCI_DEVICE_ID , 2 , 0xCD80 ) ;
2014-02-11 11:39:26 +05:30
/*
* if is_gen1 is set then handle it , so that some buggy card
* also works
*/
if ( spear13xx_pcie - > is_gen1 ) {
2017-02-15 18:48:14 +05:30
dw_pcie_read ( pci - > dbi_base + exp_cap_off + PCI_EXP_LNKCAP ,
2017-02-15 18:48:12 +05:30
4 , & val ) ;
2014-02-11 11:39:26 +05:30
if ( ( val & PCI_EXP_LNKCAP_SLS ) ! = PCI_EXP_LNKCAP_SLS_2_5GB ) {
val & = ~ ( ( u32 ) PCI_EXP_LNKCAP_SLS ) ;
val | = PCI_EXP_LNKCAP_SLS_2_5GB ;
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + exp_cap_off +
2017-02-15 18:48:12 +05:30
PCI_EXP_LNKCAP , 4 , val ) ;
2014-02-11 11:39:26 +05:30
}
2017-02-15 18:48:14 +05:30
dw_pcie_read ( pci - > dbi_base + exp_cap_off + PCI_EXP_LNKCTL2 ,
2017-02-15 18:48:12 +05:30
2 , & val ) ;
2014-02-11 11:39:26 +05:30
if ( ( val & PCI_EXP_LNKCAP_SLS ) ! = PCI_EXP_LNKCAP_SLS_2_5GB ) {
val & = ~ ( ( u32 ) PCI_EXP_LNKCAP_SLS ) ;
val | = PCI_EXP_LNKCAP_SLS_2_5GB ;
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + exp_cap_off +
2017-02-15 18:48:12 +05:30
PCI_EXP_LNKCTL2 , 2 , val ) ;
2014-02-11 11:39:26 +05:30
}
}
/* enable ltssm */
writel ( DEVICE_TYPE_RC | ( 1 < < MISCTRL_EN_ID )
| ( 1 < < APP_LTSSM_ENABLE_ID )
| ( ( u32 ) 1 < < REG_TRANSLATION_ENABLE ) ,
& app_reg - > app_ctrl_0 ) ;
2017-02-15 18:48:14 +05:30
return dw_pcie_wait_for_link ( pci ) ;
2014-02-11 11:39:26 +05:30
}
static irqreturn_t spear13xx_pcie_irq_handler ( int irq , void * arg )
{
2016-10-06 13:42:08 -05:00
struct spear13xx_pcie * spear13xx_pcie = arg ;
2014-02-11 11:39:26 +05:30
struct pcie_app_reg * app_reg = spear13xx_pcie - > app_base ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = spear13xx_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
2014-02-11 11:39:26 +05:30
unsigned int status ;
status = readl ( & app_reg - > int_sts ) ;
if ( status & MSI_CTRL_INT ) {
2015-08-20 01:31:24 -05:00
BUG_ON ( ! IS_ENABLED ( CONFIG_PCI_MSI ) ) ;
2014-02-11 11:39:26 +05:30
dw_handle_msi_irq ( pp ) ;
}
writel ( status , & app_reg - > int_clr ) ;
return IRQ_HANDLED ;
}
2016-10-06 13:42:08 -05:00
static void spear13xx_pcie_enable_interrupts ( struct spear13xx_pcie * spear13xx_pcie )
2014-02-11 11:39:26 +05:30
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = spear13xx_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
2014-02-11 11:39:26 +05:30
struct pcie_app_reg * app_reg = spear13xx_pcie - > app_base ;
/* Enable MSI interrupt */
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
dw_pcie_msi_init ( pp ) ;
writel ( readl ( & app_reg - > int_mask ) |
MSI_CTRL_INT , & app_reg - > int_mask ) ;
}
}
2017-02-15 18:48:14 +05:30
static int spear13xx_pcie_link_up ( struct dw_pcie * pci )
2014-02-11 11:39:26 +05:30
{
2017-02-15 18:48:14 +05:30
struct spear13xx_pcie * spear13xx_pcie = to_spear13xx_pcie ( pci ) ;
2014-02-11 11:39:26 +05:30
struct pcie_app_reg * app_reg = spear13xx_pcie - > app_base ;
if ( readl ( & app_reg - > app_status_1 ) & XMLH_LINK_UP )
return 1 ;
return 0 ;
}
static void spear13xx_pcie_host_init ( struct pcie_port * pp )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct spear13xx_pcie * spear13xx_pcie = to_spear13xx_pcie ( pci ) ;
2016-10-06 13:42:08 -05:00
spear13xx_pcie_establish_link ( spear13xx_pcie ) ;
spear13xx_pcie_enable_interrupts ( spear13xx_pcie ) ;
2014-02-11 11:39:26 +05:30
}
2017-02-15 18:48:14 +05:30
static struct dw_pcie_host_ops spear13xx_pcie_host_ops = {
2014-02-11 11:39:26 +05:30
. host_init = spear13xx_pcie_host_init ,
} ;
2016-10-06 13:42:08 -05:00
static int spear13xx_add_pcie_port ( struct spear13xx_pcie * spear13xx_pcie ,
struct platform_device * pdev )
2014-02-11 11:39:26 +05:30
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = spear13xx_pcie - > pci ;
struct pcie_port * pp = & pci - > pp ;
struct device * dev = & pdev - > dev ;
2014-02-11 11:39:26 +05:30
int ret ;
pp - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ! pp - > irq ) {
dev_err ( dev , " failed to get irq \n " ) ;
return - ENODEV ;
}
ret = devm_request_irq ( dev , pp - > irq , spear13xx_pcie_irq_handler ,
2015-12-10 21:18:20 +02:00
IRQF_SHARED | IRQF_NO_THREAD ,
2016-10-06 13:42:08 -05:00
" spear1340-pcie " , spear13xx_pcie ) ;
2014-02-11 11:39:26 +05:30
if ( ret ) {
dev_err ( dev , " failed to request irq %d \n " , pp - > irq ) ;
return ret ;
}
pp - > root_bus_nr = - 1 ;
pp - > ops = & spear13xx_pcie_host_ops ;
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
dev_err ( dev , " failed to initialize host \n " ) ;
return ret ;
}
return 0 ;
}
2017-02-15 18:48:14 +05:30
static const struct dw_pcie_ops dw_pcie_ops = {
. link_up = spear13xx_pcie_link_up ,
} ;
2015-02-19 20:41:48 +03:00
static int spear13xx_pcie_probe ( struct platform_device * pdev )
2014-02-11 11:39:26 +05:30
{
2016-10-06 13:42:09 -05:00
struct device * dev = & pdev - > dev ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2014-02-11 11:39:26 +05:30
struct spear13xx_pcie * spear13xx_pcie ;
2016-10-06 13:42:09 -05:00
struct device_node * np = dev - > of_node ;
2014-02-11 11:39:26 +05:30
struct resource * dbi_base ;
int ret ;
spear13xx_pcie = devm_kzalloc ( dev , sizeof ( * spear13xx_pcie ) , GFP_KERNEL ) ;
2014-11-12 12:29:02 +09:00
if ( ! spear13xx_pcie )
2014-02-11 11:39:26 +05:30
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
spear13xx_pcie - > pci = pci ;
2014-02-11 11:39:26 +05:30
spear13xx_pcie - > phy = devm_phy_get ( dev , " pcie-phy " ) ;
if ( IS_ERR ( spear13xx_pcie - > phy ) ) {
ret = PTR_ERR ( spear13xx_pcie - > phy ) ;
if ( ret = = - EPROBE_DEFER )
dev_info ( dev , " probe deferred \n " ) ;
else
dev_err ( dev , " couldn't get pcie-phy \n " ) ;
return ret ;
}
phy_init ( spear13xx_pcie - > phy ) ;
spear13xx_pcie - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( spear13xx_pcie - > clk ) ) {
dev_err ( dev , " couldn't get clk for pcie \n " ) ;
return PTR_ERR ( spear13xx_pcie - > clk ) ;
}
ret = clk_prepare_enable ( spear13xx_pcie - > clk ) ;
if ( ret ) {
dev_err ( dev , " couldn't enable clk for pcie \n " ) ;
return ret ;
}
2014-09-03 10:50:49 +05:30
dbi_base = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dbi " ) ;
2017-04-19 17:48:59 +01:00
pci - > dbi_base = devm_pci_remap_cfg_resource ( dev , dbi_base ) ;
2017-02-15 18:48:14 +05:30
if ( IS_ERR ( pci - > dbi_base ) ) {
2014-02-11 11:39:26 +05:30
dev_err ( dev , " couldn't remap dbi base %p \n " , dbi_base ) ;
2017-02-15 18:48:14 +05:30
ret = PTR_ERR ( pci - > dbi_base ) ;
2014-02-11 11:39:26 +05:30
goto fail_clk ;
}
2017-02-15 18:48:14 +05:30
spear13xx_pcie - > app_base = pci - > dbi_base + 0x2000 ;
2014-02-11 11:39:26 +05:30
if ( of_property_read_bool ( np , " st,pcie-is-gen1 " ) )
spear13xx_pcie - > is_gen1 = true ;
2017-02-15 18:48:11 +05:30
platform_set_drvdata ( pdev , spear13xx_pcie ) ;
2016-10-06 13:42:08 -05:00
ret = spear13xx_add_pcie_port ( spear13xx_pcie , pdev ) ;
2014-02-11 11:39:26 +05:30
if ( ret < 0 )
goto fail_clk ;
return 0 ;
fail_clk :
clk_disable_unprepare ( spear13xx_pcie - > clk ) ;
return ret ;
}
static const struct of_device_id spear13xx_pcie_of_match [ ] = {
{ . compatible = " st,spear1340-pcie " , } ,
{ } ,
} ;
2015-02-19 20:41:48 +03:00
static struct platform_driver spear13xx_pcie_driver = {
2014-02-11 11:39:26 +05:30
. probe = spear13xx_pcie_probe ,
. driver = {
. name = " spear-pcie " ,
. of_match_table = of_match_ptr ( spear13xx_pcie_of_match ) ,
} ,
} ;
2016-11-23 22:55:07 +08:00
builtin_platform_driver ( spear13xx_pcie_driver ) ;