2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2016-04-26 10:31:46 +02:00
/*
* PCIe host controller driver for Marvell Armada - 8 K SoCs
*
* Armada - 8 K PCIe Glue Layer Source Code
*
* Copyright ( C ) 2016 Marvell Technology Group Ltd .
*
2016-07-02 19:13:21 -04:00
* Author : Yehuda Yitshak < yehuday @ marvell . com >
* Author : Shadi Ammouri < shadi @ marvell . com >
2016-04-26 10:31:46 +02:00
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2016-07-02 19:13:21 -04:00
# include <linux/init.h>
2016-04-26 10:31:46 +02:00
# include <linux/of.h>
# include <linux/pci.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/resource.h>
# include <linux/of_pci.h>
# include <linux/of_irq.h>
# include "pcie-designware.h"
2019-04-01 15:12:39 +02:00
# define ARMADA8K_PCIE_MAX_LANES PCIE_LNK_X4
2016-04-26 10:31:46 +02:00
struct armada8k_pcie {
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2016-04-26 10:31:46 +02:00
struct clk * clk ;
2018-02-28 17:35:30 +01:00
struct clk * clk_reg ;
2019-04-01 15:12:39 +02:00
struct phy * phy [ ARMADA8K_PCIE_MAX_LANES ] ;
unsigned int phy_count ;
2016-04-26 10:31:46 +02:00
} ;
# define PCIE_VENDOR_REGS_OFFSET 0x8000
2016-10-06 13:29:59 -05:00
# define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0)
2016-04-26 10:31:46 +02:00
# define PCIE_APP_LTSSM_EN BIT(2)
# define PCIE_DEVICE_TYPE_SHIFT 4
# define PCIE_DEVICE_TYPE_MASK 0xF
# define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */
2016-10-06 13:29:59 -05:00
# define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8)
2016-04-26 10:31:46 +02:00
# define PCIE_GLB_STS_RDLH_LINK_UP BIT(1)
# define PCIE_GLB_STS_PHY_LINK_UP BIT(9)
2016-10-06 13:29:59 -05:00
# define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C)
# define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20)
2016-04-26 10:31:46 +02:00
# define PCIE_INT_A_ASSERT_MASK BIT(9)
# define PCIE_INT_B_ASSERT_MASK BIT(10)
# define PCIE_INT_C_ASSERT_MASK BIT(11)
# define PCIE_INT_D_ASSERT_MASK BIT(12)
2016-10-06 13:29:59 -05:00
# define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50)
# define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54)
# define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C)
# define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60)
2016-04-26 10:31:46 +02:00
/*
2019-05-30 08:05:58 -05:00
* AR / AW Cache defaults : Normal memory , Write - Back , Read / Write
2016-04-26 10:31:46 +02:00
* allocate
*/
# define ARCACHE_DEFAULT_VALUE 0x3511
# define AWCACHE_DEFAULT_VALUE 0x5311
# define DOMAIN_OUTER_SHAREABLE 0x2
# define AX_USER_DOMAIN_MASK 0x3
# define AX_USER_DOMAIN_SHIFT 4
2017-02-15 18:48:14 +05:30
# define to_armada8k_pcie(x) dev_get_drvdata((x)->dev)
2016-04-26 10:31:46 +02:00
2019-04-01 15:12:39 +02:00
static void armada8k_pcie_disable_phys ( struct armada8k_pcie * pcie )
{
int i ;
for ( i = 0 ; i < ARMADA8K_PCIE_MAX_LANES ; i + + ) {
phy_power_off ( pcie - > phy [ i ] ) ;
phy_exit ( pcie - > phy [ i ] ) ;
}
}
static int armada8k_pcie_enable_phys ( struct armada8k_pcie * pcie )
{
int ret ;
int i ;
for ( i = 0 ; i < ARMADA8K_PCIE_MAX_LANES ; i + + ) {
ret = phy_init ( pcie - > phy [ i ] ) ;
if ( ret )
return ret ;
ret = phy_set_mode_ext ( pcie - > phy [ i ] , PHY_MODE_PCIE ,
pcie - > phy_count ) ;
if ( ret ) {
phy_exit ( pcie - > phy [ i ] ) ;
return ret ;
}
ret = phy_power_on ( pcie - > phy [ i ] ) ;
if ( ret ) {
phy_exit ( pcie - > phy [ i ] ) ;
return ret ;
}
}
return 0 ;
}
static int armada8k_pcie_setup_phys ( struct armada8k_pcie * pcie )
{
struct dw_pcie * pci = pcie - > pci ;
struct device * dev = pci - > dev ;
struct device_node * node = dev - > of_node ;
int ret = 0 ;
int i ;
for ( i = 0 ; i < ARMADA8K_PCIE_MAX_LANES ; i + + ) {
pcie - > phy [ i ] = devm_of_phy_get_by_index ( dev , node , i ) ;
if ( IS_ERR ( pcie - > phy [ i ] ) ) {
2019-08-29 12:53:17 +02:00
if ( PTR_ERR ( pcie - > phy [ i ] ) ! = - ENODEV )
return PTR_ERR ( pcie - > phy [ i ] ) ;
2019-04-01 15:12:39 +02:00
pcie - > phy [ i ] = NULL ;
continue ;
}
pcie - > phy_count + + ;
}
/* Old bindings miss the PHY handle, so just warn if there is no PHY */
if ( ! pcie - > phy_count )
dev_warn ( dev , " No available PHY \n " ) ;
ret = armada8k_pcie_enable_phys ( pcie ) ;
if ( ret )
dev_err ( dev , " Failed to initialize PHY(s) (%d) \n " , ret ) ;
return ret ;
}
2017-02-15 18:48:14 +05:30
static int armada8k_pcie_link_up ( struct dw_pcie * pci )
2016-04-26 10:31:46 +02:00
{
u32 reg ;
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP ;
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_STATUS_REG ) ;
2016-04-26 10:31:46 +02:00
if ( ( reg & mask ) = = mask )
return 1 ;
2017-02-15 18:48:14 +05:30
dev_dbg ( pci - > dev , " No link detected (Global-Status: 0x%08x). \n " , reg ) ;
2016-04-26 10:31:46 +02:00
return 0 ;
}
2020-11-05 15:11:53 -06:00
static int armada8k_pcie_start_link ( struct dw_pcie * pci )
{
u32 reg ;
/* Start LTSSM */
reg = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_CONTROL_REG ) ;
reg | = PCIE_APP_LTSSM_EN ;
dw_pcie_writel_dbi ( pci , PCIE_GLOBAL_CONTROL_REG , reg ) ;
return 0 ;
}
2022-06-24 17:34:25 +03:00
static int armada8k_pcie_host_init ( struct dw_pcie_rp * pp )
2016-04-26 10:31:46 +02:00
{
u32 reg ;
2020-11-05 15:11:53 -06:00
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2017-02-15 18:48:14 +05:30
if ( ! dw_pcie_link_up ( pci ) ) {
2016-04-26 10:31:46 +02:00
/* Disable LTSSM state machine to enable configuration */
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_CONTROL_REG ) ;
2016-04-26 10:31:46 +02:00
reg & = ~ ( PCIE_APP_LTSSM_EN ) ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_GLOBAL_CONTROL_REG , reg ) ;
2016-04-26 10:31:46 +02:00
}
/* Set the device to root complex mode */
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_CONTROL_REG ) ;
2016-04-26 10:31:46 +02:00
reg & = ~ ( PCIE_DEVICE_TYPE_MASK < < PCIE_DEVICE_TYPE_SHIFT ) ;
reg | = PCIE_DEVICE_TYPE_RC < < PCIE_DEVICE_TYPE_SHIFT ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_GLOBAL_CONTROL_REG , reg ) ;
2016-04-26 10:31:46 +02:00
/* Set the PCIe master AxCache attributes */
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_ARCACHE_TRC_REG , ARCACHE_DEFAULT_VALUE ) ;
dw_pcie_writel_dbi ( pci , PCIE_AWCACHE_TRC_REG , AWCACHE_DEFAULT_VALUE ) ;
2016-04-26 10:31:46 +02:00
/* Set the PCIe master AxDomain attributes */
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_ARUSER_REG ) ;
2016-04-26 10:31:46 +02:00
reg & = ~ ( AX_USER_DOMAIN_MASK < < AX_USER_DOMAIN_SHIFT ) ;
reg | = DOMAIN_OUTER_SHAREABLE < < AX_USER_DOMAIN_SHIFT ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_ARUSER_REG , reg ) ;
2016-04-26 10:31:46 +02:00
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_AWUSER_REG ) ;
2016-04-26 10:31:46 +02:00
reg & = ~ ( AX_USER_DOMAIN_MASK < < AX_USER_DOMAIN_SHIFT ) ;
reg | = DOMAIN_OUTER_SHAREABLE < < AX_USER_DOMAIN_SHIFT ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_AWUSER_REG , reg ) ;
2016-04-26 10:31:46 +02:00
/* Enable INT A-D interrupts */
2017-02-15 18:48:14 +05:30
reg = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_INT_MASK1_REG ) ;
2016-04-26 10:31:46 +02:00
reg | = PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCIE_GLOBAL_INT_MASK1_REG , reg ) ;
2016-04-26 10:31:46 +02:00
2017-07-15 23:39:45 -07:00
return 0 ;
2016-04-26 10:31:46 +02:00
}
static irqreturn_t armada8k_pcie_irq_handler ( int irq , void * arg )
{
2016-10-06 13:30:00 -05:00
struct armada8k_pcie * pcie = arg ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = pcie - > pci ;
2016-04-26 10:31:46 +02:00
u32 val ;
/*
* Interrupts are directly handled by the device driver of the
* PCI device . However , they are also latched into the PCIe
* controller , so we simply discard them .
*/
2017-02-15 18:48:14 +05:30
val = dw_pcie_readl_dbi ( pci , PCIE_GLOBAL_INT_CAUSE1_REG ) ;
dw_pcie_writel_dbi ( pci , PCIE_GLOBAL_INT_CAUSE1_REG , val ) ;
2016-04-26 10:31:46 +02:00
return IRQ_HANDLED ;
}
2017-06-05 16:53:46 +08:00
static const struct dw_pcie_host_ops armada8k_pcie_host_ops = {
2016-04-26 10:31:46 +02:00
. host_init = armada8k_pcie_host_init ,
} ;
2016-10-06 13:30:00 -05:00
static int armada8k_add_pcie_port ( struct armada8k_pcie * pcie ,
2016-04-26 10:31:46 +02:00
struct platform_device * pdev )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = pcie - > pci ;
2022-06-24 17:34:25 +03:00
struct dw_pcie_rp * pp = & pci - > pp ;
2016-04-26 10:31:46 +02:00
struct device * dev = & pdev - > dev ;
int ret ;
pp - > ops = & armada8k_pcie_host_ops ;
pp - > irq = platform_get_irq ( pdev , 0 ) ;
2020-08-02 14:25:53 +00:00
if ( pp - > irq < 0 )
2017-08-31 14:52:03 -03:00
return pp - > irq ;
2016-04-26 10:31:46 +02:00
ret = devm_request_irq ( dev , pp - > irq , armada8k_pcie_irq_handler ,
2016-10-06 13:30:00 -05:00
IRQF_SHARED , " armada8k-pcie " , pcie ) ;
2016-04-26 10:31:46 +02:00
if ( ret ) {
dev_err ( dev , " failed to request irq %d \n " , pp - > irq ) ;
return ret ;
}
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
dev_err ( dev , " failed to initialize host: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2017-02-15 18:48:14 +05:30
static const struct dw_pcie_ops dw_pcie_ops = {
. link_up = armada8k_pcie_link_up ,
2020-11-05 15:11:53 -06:00
. start_link = armada8k_pcie_start_link ,
2017-02-15 18:48:14 +05:30
} ;
2016-04-26 10:31:46 +02:00
static int armada8k_pcie_probe ( struct platform_device * pdev )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2016-04-26 10:31:46 +02:00
struct armada8k_pcie * pcie ;
struct device * dev = & pdev - > dev ;
struct resource * base ;
int ret ;
pcie = devm_kzalloc ( dev , sizeof ( * pcie ) , GFP_KERNEL ) ;
if ( ! 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
pcie - > pci = pci ;
2016-04-26 10:31:46 +02:00
pcie - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( pcie - > clk ) )
return PTR_ERR ( pcie - > clk ) ;
2017-07-22 17:25:19 -03:00
ret = clk_prepare_enable ( pcie - > clk ) ;
if ( ret )
return ret ;
2016-04-26 10:31:46 +02:00
2018-02-28 17:35:30 +01:00
pcie - > clk_reg = devm_clk_get ( dev , " reg " ) ;
if ( pcie - > clk_reg = = ERR_PTR ( - EPROBE_DEFER ) ) {
ret = - EPROBE_DEFER ;
goto fail ;
}
if ( ! IS_ERR ( pcie - > clk_reg ) ) {
ret = clk_prepare_enable ( pcie - > clk_reg ) ;
if ( ret )
goto fail_clkreg ;
}
2016-04-26 10:31:46 +02:00
/* Get the dw-pcie unit configuration/control registers base. */
base = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ctrl " ) ;
2017-04-19 17:49:04 +01:00
pci - > dbi_base = devm_pci_remap_cfg_resource ( dev , base ) ;
2017-02-15 18:48:14 +05:30
if ( IS_ERR ( pci - > dbi_base ) ) {
ret = PTR_ERR ( pci - > dbi_base ) ;
2018-02-28 17:35:30 +01:00
goto fail_clkreg ;
2016-04-26 10:31:46 +02:00
}
2019-04-01 15:12:39 +02:00
ret = armada8k_pcie_setup_phys ( pcie ) ;
if ( ret )
goto fail_clkreg ;
2017-02-15 18:48:11 +05:30
platform_set_drvdata ( pdev , pcie ) ;
2016-10-06 13:30:00 -05:00
ret = armada8k_add_pcie_port ( pcie , pdev ) ;
2016-04-26 10:31:46 +02:00
if ( ret )
2019-04-01 15:12:39 +02:00
goto disable_phy ;
2016-04-26 10:31:46 +02:00
return 0 ;
2019-04-01 15:12:39 +02:00
disable_phy :
armada8k_pcie_disable_phys ( pcie ) ;
2018-02-28 17:35:30 +01:00
fail_clkreg :
clk_disable_unprepare ( pcie - > clk_reg ) ;
2016-04-26 10:31:46 +02:00
fail :
2018-02-28 17:35:29 +01:00
clk_disable_unprepare ( pcie - > clk ) ;
2016-04-26 10:31:46 +02:00
return ret ;
}
static const struct of_device_id armada8k_pcie_of_match [ ] = {
{ . compatible = " marvell,armada8k-pcie " , } ,
{ } ,
} ;
static struct platform_driver armada8k_pcie_driver = {
. probe = armada8k_pcie_probe ,
. driver = {
. name = " armada8k-pcie " ,
2022-07-06 14:27:21 -05:00
. of_match_table = armada8k_pcie_of_match ,
2017-04-20 15:36:25 -05:00
. suppress_bind_attrs = true ,
2016-04-26 10:31:46 +02:00
} ,
} ;
2016-07-02 19:13:21 -04:00
builtin_platform_driver ( armada8k_pcie_driver ) ;