2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
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
*/
# 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 ;
} ;
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)
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
2020-11-05 15:11:53 -06:00
static int spear13xx_pcie_start_link ( struct dw_pcie * pci )
2014-02-11 11:39:26 +05:30
{
2020-11-05 15:11:53 -06:00
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 ;
/* 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 ) ;
2020-11-05 15:11:53 -06:00
return 0 ;
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
{
struct pcie_app_reg * app_reg = spear13xx_pcie - > app_base ;
/* Enable MSI interrupt */
2020-11-05 15:11:54 -06:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) )
2014-02-11 11:39:26 +05:30
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 ;
}
2017-07-15 23:39:45 -07:00
static int spear13xx_pcie_host_init ( struct pcie_port * pp )
2014-02-11 11:39:26 +05:30
{
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 ) ;
2020-11-05 15:11:53 -06:00
u32 exp_cap_off = dw_pcie_find_capability ( pci , PCI_CAP_ID_EXP ) ;
u32 val ;
2016-10-06 13:42:08 -05:00
2020-11-05 15:11:46 -06:00
spear13xx_pcie - > app_base = pci - > dbi_base + 0x2000 ;
2020-11-05 15:11:53 -06:00
/*
* this controller support only 128 bytes read size , however its
* default value in capability register is 512 bytes . So force
* it to 128 here .
*/
val = dw_pcie_readw_dbi ( pci , exp_cap_off + PCI_EXP_DEVCTL ) ;
val & = ~ PCI_EXP_DEVCTL_READRQ ;
dw_pcie_writew_dbi ( pci , exp_cap_off + PCI_EXP_DEVCTL , val ) ;
dw_pcie_writew_dbi ( pci , PCI_VENDOR_ID , 0x104A ) ;
dw_pcie_writew_dbi ( pci , PCI_DEVICE_ID , 0xCD80 ) ;
2016-10-06 13:42:08 -05:00
spear13xx_pcie_enable_interrupts ( spear13xx_pcie ) ;
2017-07-15 23:39:45 -07:00
return 0 ;
2014-02-11 11:39:26 +05:30
}
2017-06-05 16:53:46 +08:00
static const 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 ) ;
2020-08-02 14:25:53 +00:00
if ( pp - > irq < 0 )
2017-08-31 14:52:05 -03:00
return pp - > irq ;
2020-08-02 14:25:53 +00:00
2014-02-11 11:39:26 +05:30
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 - > ops = & spear13xx_pcie_host_ops ;
2020-11-05 15:11:51 -06:00
pp - > msi_irq = - ENODEV ;
2014-02-11 11:39:26 +05:30
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 ,
2020-11-05 15:11:53 -06:00
. start_link = spear13xx_pcie_start_link ,
2017-02-15 18:48:14 +05:30
} ;
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
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 ;
}
if ( of_property_read_bool ( np , " st,pcie-is-gen1 " ) )
2020-08-20 21:54:14 -06:00
pci - > link_gen = 1 ;
2014-02-11 11:39:26 +05:30
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 ) ,
2017-04-20 15:36:25 -05:00
. suppress_bind_attrs = true ,
2014-02-11 11:39:26 +05:30
} ,
} ;
2016-11-23 22:55:07 +08:00
builtin_platform_driver ( spear13xx_pcie_driver ) ;