2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2013-07-31 17:14:10 +09:00
/*
* PCIe host controller driver for Samsung EXYNOS SoCs
*
* Copyright ( C ) 2013 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Jingoo Han < jg1 . han @ samsung . com >
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2016-08-22 17:59:47 -04:00
# include <linux/init.h>
2017-02-01 10:13:06 +05:30
# include <linux/of_device.h>
2013-07-31 17:14:10 +09:00
# include <linux/of_gpio.h>
# include <linux/pci.h>
# include <linux/platform_device.h>
2017-02-13 17:26:13 +09:00
# include <linux/phy/phy.h>
2013-07-31 17:14:10 +09:00
# include <linux/resource.h>
# include <linux/signal.h>
# include <linux/types.h>
# include "pcie-designware.h"
2017-02-15 18:48:14 +05:30
# define to_exynos_pcie(x) dev_get_drvdata((x)->dev)
2013-07-31 17:14:10 +09:00
/* PCIe ELBI registers */
# define PCIE_IRQ_PULSE 0x000
2017-01-16 15:31:37 +09:00
# define IRQ_INTA_ASSERT BIT(0)
# define IRQ_INTB_ASSERT BIT(2)
# define IRQ_INTC_ASSERT BIT(4)
# define IRQ_INTD_ASSERT BIT(6)
2013-07-31 17:14:10 +09:00
# define PCIE_IRQ_LEVEL 0x004
# define PCIE_IRQ_SPECIAL 0x008
# define PCIE_IRQ_EN_PULSE 0x00c
# define PCIE_IRQ_EN_LEVEL 0x010
2017-01-16 15:31:37 +09:00
# define IRQ_MSI_ENABLE BIT(2)
2013-07-31 17:14:10 +09:00
# define PCIE_IRQ_EN_SPECIAL 0x014
# define PCIE_PWR_RESET 0x018
# define PCIE_CORE_RESET 0x01c
2017-01-16 15:31:37 +09:00
# define PCIE_CORE_RESET_ENABLE BIT(0)
2013-07-31 17:14:10 +09:00
# define PCIE_STICKY_RESET 0x020
# define PCIE_NONSTICKY_RESET 0x024
# define PCIE_APP_INIT_RESET 0x028
# define PCIE_APP_LTSSM_ENABLE 0x02c
# define PCIE_ELBI_RDLH_LINKUP 0x064
# define PCIE_ELBI_LTSSM_ENABLE 0x1
# define PCIE_ELBI_SLV_AWMISC 0x11c
# define PCIE_ELBI_SLV_ARMISC 0x120
2017-01-16 15:31:37 +09:00
# define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
2013-07-31 17:14:10 +09:00
2017-02-01 10:13:06 +05:30
struct exynos_pcie_mem_res {
void __iomem * elbi_base ; /* DT 0th resource: PCIe CTRL */
} ;
struct exynos_pcie_clk_res {
struct clk * clk ;
struct clk * bus_clk ;
} ;
struct exynos_pcie {
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci ;
2017-02-01 10:13:06 +05:30
struct exynos_pcie_mem_res * mem_res ;
struct exynos_pcie_clk_res * clk_res ;
const struct exynos_pcie_ops * ops ;
int reset_gpio ;
2017-02-13 17:26:13 +09:00
struct phy * phy ;
2017-02-01 10:13:06 +05:30
} ;
struct exynos_pcie_ops {
int ( * get_mem_resources ) ( struct platform_device * pdev ,
struct exynos_pcie * ep ) ;
int ( * get_clk_resources ) ( struct exynos_pcie * ep ) ;
int ( * init_clk_resources ) ( struct exynos_pcie * ep ) ;
void ( * deinit_clk_resources ) ( struct exynos_pcie * ep ) ;
} ;
static int exynos5440_pcie_get_mem_resources ( struct platform_device * pdev ,
struct exynos_pcie * ep )
2013-08-29 21:35:56 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
struct device * dev = pci - > dev ;
2017-02-01 10:13:06 +05:30
struct resource * res ;
ep - > mem_res = devm_kzalloc ( dev , sizeof ( * ep - > mem_res ) , GFP_KERNEL ) ;
if ( ! ep - > mem_res )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
ep - > mem_res - > elbi_base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( ep - > mem_res - > elbi_base ) )
return PTR_ERR ( ep - > mem_res - > elbi_base ) ;
return 0 ;
2013-08-29 21:35:56 +09:00
}
2017-02-01 10:13:06 +05:30
static int exynos5440_pcie_get_clk_resources ( struct exynos_pcie * ep )
2013-08-29 21:35:56 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
struct device * dev = pci - > dev ;
2017-02-01 10:13:06 +05:30
ep - > clk_res = devm_kzalloc ( dev , sizeof ( * ep - > clk_res ) , GFP_KERNEL ) ;
if ( ! ep - > clk_res )
return - ENOMEM ;
ep - > clk_res - > clk = devm_clk_get ( dev , " pcie " ) ;
if ( IS_ERR ( ep - > clk_res - > clk ) ) {
dev_err ( dev , " Failed to get pcie rc clock \n " ) ;
return PTR_ERR ( ep - > clk_res - > clk ) ;
}
ep - > clk_res - > bus_clk = devm_clk_get ( dev , " pcie_bus " ) ;
if ( IS_ERR ( ep - > clk_res - > bus_clk ) ) {
dev_err ( dev , " Failed to get pcie bus clock \n " ) ;
return PTR_ERR ( ep - > clk_res - > bus_clk ) ;
}
return 0 ;
2013-08-29 21:35:56 +09:00
}
2017-02-01 10:13:06 +05:30
static int exynos5440_pcie_init_clk_resources ( struct exynos_pcie * ep )
2013-08-29 21:35:56 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
struct device * dev = pci - > dev ;
2017-02-01 10:13:06 +05:30
int ret ;
ret = clk_prepare_enable ( ep - > clk_res - > clk ) ;
if ( ret ) {
dev_err ( dev , " cannot enable pcie rc clock " ) ;
return ret ;
}
ret = clk_prepare_enable ( ep - > clk_res - > bus_clk ) ;
if ( ret ) {
dev_err ( dev , " cannot enable pcie bus clock " ) ;
goto err_bus_clk ;
}
return 0 ;
err_bus_clk :
clk_disable_unprepare ( ep - > clk_res - > clk ) ;
return ret ;
2013-08-29 21:35:56 +09:00
}
2017-02-01 10:13:06 +05:30
static void exynos5440_pcie_deinit_clk_resources ( struct exynos_pcie * ep )
2013-08-29 21:35:56 +09:00
{
2017-02-01 10:13:06 +05:30
clk_disable_unprepare ( ep - > clk_res - > bus_clk ) ;
clk_disable_unprepare ( ep - > clk_res - > clk ) ;
2013-08-29 21:35:56 +09:00
}
2017-02-01 10:13:06 +05:30
static const struct exynos_pcie_ops exynos5440_pcie_ops = {
. get_mem_resources = exynos5440_pcie_get_mem_resources ,
. get_clk_resources = exynos5440_pcie_get_clk_resources ,
. init_clk_resources = exynos5440_pcie_init_clk_resources ,
. deinit_clk_resources = exynos5440_pcie_deinit_clk_resources ,
} ;
2017-01-16 15:31:35 +09:00
static void exynos_pcie_writel ( void __iomem * base , u32 val , u32 reg )
2013-08-29 21:35:56 +09:00
{
2017-01-16 15:31:35 +09:00
writel ( val , base + reg ) ;
2013-08-29 21:35:56 +09:00
}
2017-01-16 15:31:35 +09:00
static u32 exynos_pcie_readl ( void __iomem * base , u32 reg )
2013-08-29 21:35:56 +09:00
{
2017-01-16 15:31:35 +09:00
return readl ( base + reg ) ;
2013-08-29 21:35:56 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_sideband_dbi_w_mode ( struct exynos_pcie * ep , bool on )
2013-07-31 17:14:10 +09:00
{
u32 val ;
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_ELBI_SLV_AWMISC ) ;
2017-01-16 15:31:38 +09:00
if ( on )
2013-07-31 17:14:10 +09:00
val | = PCIE_ELBI_SLV_DBI_ENABLE ;
2017-01-16 15:31:38 +09:00
else
2013-07-31 17:14:10 +09:00
val & = ~ PCIE_ELBI_SLV_DBI_ENABLE ;
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_ELBI_SLV_AWMISC ) ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_sideband_dbi_r_mode ( struct exynos_pcie * ep , bool on )
2013-07-31 17:14:10 +09:00
{
u32 val ;
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_ELBI_SLV_ARMISC ) ;
2017-01-16 15:31:38 +09:00
if ( on )
2013-07-31 17:14:10 +09:00
val | = PCIE_ELBI_SLV_DBI_ENABLE ;
2017-01-16 15:31:38 +09:00
else
2013-07-31 17:14:10 +09:00
val & = ~ PCIE_ELBI_SLV_DBI_ENABLE ;
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_ELBI_SLV_ARMISC ) ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_assert_core_reset ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
u32 val ;
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_CORE_RESET ) ;
2013-07-31 17:14:10 +09:00
val & = ~ PCIE_CORE_RESET_ENABLE ;
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_CORE_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 0 , PCIE_PWR_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 0 , PCIE_STICKY_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 0 , PCIE_NONSTICKY_RESET ) ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_deassert_core_reset ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
u32 val ;
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_CORE_RESET ) ;
2013-07-31 17:14:10 +09:00
val | = PCIE_CORE_RESET_ENABLE ;
2013-08-29 21:35:56 +09:00
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_CORE_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 1 , PCIE_STICKY_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 1 , PCIE_NONSTICKY_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 1 , PCIE_APP_INIT_RESET ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 0 , PCIE_APP_INIT_RESET ) ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_assert_reset ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
2017-02-15 18:48:14 +05:30
struct device * dev = pci - > dev ;
2013-07-31 17:14:10 +09:00
2017-01-16 15:31:34 +09:00
if ( ep - > reset_gpio > = 0 )
devm_gpio_request_one ( dev , ep - > reset_gpio ,
2013-07-31 17:14:10 +09:00
GPIOF_OUT_INIT_HIGH , " RESET " ) ;
}
2017-01-16 15:31:34 +09:00
static int exynos_pcie_establish_link ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
2017-02-15 18:48:14 +05:30
struct pcie_port * pp = & pci - > pp ;
struct device * dev = pci - > dev ;
2013-07-31 17:14:10 +09:00
2017-02-15 18:48:14 +05:30
if ( dw_pcie_link_up ( pci ) ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " Link already up \n " ) ;
2013-07-31 17:14:10 +09:00
return 0 ;
}
2017-01-16 15:31:34 +09:00
exynos_pcie_assert_core_reset ( ep ) ;
2017-02-13 17:26:13 +09:00
2017-12-27 18:43:27 +09:00
phy_reset ( ep - > phy ) ;
2013-07-31 17:14:10 +09:00
2017-12-27 18:43:27 +09:00
exynos_pcie_writel ( ep - > mem_res - > elbi_base , 1 ,
PCIE_PWR_RESET ) ;
phy_power_on ( ep - > phy ) ;
phy_init ( ep - > phy ) ;
2013-07-31 17:14:10 +09:00
2017-01-16 15:31:34 +09:00
exynos_pcie_deassert_core_reset ( ep ) ;
2013-07-31 17:14:10 +09:00
dw_pcie_setup_rc ( pp ) ;
2017-01-16 15:31:34 +09:00
exynos_pcie_assert_reset ( ep ) ;
2013-07-31 17:14:10 +09:00
/* assert LTSSM enable */
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , PCIE_ELBI_LTSSM_ENABLE ,
2013-08-29 21:35:56 +09:00
PCIE_APP_LTSSM_ENABLE ) ;
2013-07-31 17:14:10 +09:00
/* check if the link is up or not */
2017-02-15 18:48:14 +05:30
if ( ! dw_pcie_wait_for_link ( pci ) )
PCI: designware: Add generic dw_pcie_wait_for_link()
Several DesignWare-based drivers (dra7xx, exynos, imx6, keystone, qcom, and
spear13xx) had similar loops waiting for the link to come up.
Add a generic dw_pcie_wait_for_link() for use by all these drivers so the
waiting is done consistently, e.g., always using usleep_range() rather than
mdelay() and using similar timeouts and retry counts.
Note that this changes the Keystone link training/wait for link strategy,
so we initiate link training, then wait longer for the link to come up
before re-initiating link training.
[bhelgaas: changelog, split into its own patch, update pci-keystone.c, pcie-qcom.c]
Signed-off-by: Joao Pinto <jpinto@synopsys.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Pratyush Anand <pratyush.anand@gmail.com>
2016-03-10 14:44:35 -06:00
return 0 ;
2013-07-31 17:14:10 +09:00
2017-12-27 18:43:27 +09:00
phy_power_off ( ep - > phy ) ;
PCI: designware: Add generic dw_pcie_wait_for_link()
Several DesignWare-based drivers (dra7xx, exynos, imx6, keystone, qcom, and
spear13xx) had similar loops waiting for the link to come up.
Add a generic dw_pcie_wait_for_link() for use by all these drivers so the
waiting is done consistently, e.g., always using usleep_range() rather than
mdelay() and using similar timeouts and retry counts.
Note that this changes the Keystone link training/wait for link strategy,
so we initiate link training, then wait longer for the link to come up
before re-initiating link training.
[bhelgaas: changelog, split into its own patch, update pci-keystone.c, pcie-qcom.c]
Signed-off-by: Joao Pinto <jpinto@synopsys.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Pratyush Anand <pratyush.anand@gmail.com>
2016-03-10 14:44:35 -06:00
return - ETIMEDOUT ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_clear_irq_pulse ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
u32 val ;
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_IRQ_PULSE ) ;
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_IRQ_PULSE ) ;
2013-07-31 17:14:10 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_enable_irq_pulse ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
u32 val ;
/* enable INTX interrupt */
val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
2015-03-25 14:13:12 +09:00
IRQ_INTC_ASSERT | IRQ_INTD_ASSERT ;
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_IRQ_EN_PULSE ) ;
2013-07-31 17:14:10 +09:00
}
static irqreturn_t exynos_pcie_irq_handler ( int irq , void * arg )
{
2017-01-16 15:31:34 +09:00
struct exynos_pcie * ep = arg ;
2013-07-31 17:14:10 +09:00
2017-01-16 15:31:34 +09:00
exynos_pcie_clear_irq_pulse ( ep ) ;
2013-07-31 17:14:10 +09:00
return IRQ_HANDLED ;
}
2013-09-06 15:54:59 +09:00
static irqreturn_t exynos_pcie_msi_irq_handler ( int irq , void * arg )
{
2017-01-16 15:31:34 +09:00
struct exynos_pcie * ep = arg ;
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
2017-02-15 18:48:14 +05:30
struct pcie_port * pp = & pci - > pp ;
2013-09-06 15:54:59 +09:00
2014-03-28 17:52:58 +01:00
return dw_handle_msi_irq ( pp ) ;
2013-09-06 15:54:59 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_msi_init ( struct exynos_pcie * ep )
2013-09-06 15:54:59 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
2017-02-15 18:48:14 +05:30
struct pcie_port * pp = & pci - > pp ;
2013-09-06 15:54:59 +09:00
u32 val ;
dw_pcie_msi_init ( pp ) ;
/* enable MSI interrupt */
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_IRQ_EN_LEVEL ) ;
2013-09-06 15:54:59 +09:00
val | = IRQ_MSI_ENABLE ;
2017-02-01 10:13:06 +05:30
exynos_pcie_writel ( ep - > mem_res - > elbi_base , val , PCIE_IRQ_EN_LEVEL ) ;
2013-09-06 15:54:59 +09:00
}
2017-01-16 15:31:34 +09:00
static void exynos_pcie_enable_interrupts ( struct exynos_pcie * ep )
2013-07-31 17:14:10 +09:00
{
2017-01-16 15:31:34 +09:00
exynos_pcie_enable_irq_pulse ( ep ) ;
2013-09-06 15:54:59 +09:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) )
2017-01-16 15:31:34 +09:00
exynos_pcie_msi_init ( ep ) ;
2013-07-31 17:14:10 +09:00
}
2017-03-13 19:13:26 +05:30
static u32 exynos_pcie_read_dbi ( struct dw_pcie * pci , void __iomem * base ,
u32 reg , size_t size )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2016-08-17 14:17:58 -05:00
u32 val ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_r_mode ( ep , true ) ;
2017-03-13 19:13:26 +05:30
dw_pcie_read ( base + reg , size , & val ) ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_r_mode ( ep , false ) ;
2016-08-17 14:17:58 -05:00
return val ;
2013-07-31 17:14:10 +09:00
}
2017-03-13 19:13:26 +05:30
static void exynos_pcie_write_dbi ( struct dw_pcie * pci , void __iomem * base ,
u32 reg , size_t size , u32 val )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2016-10-06 13:33:39 -05:00
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_w_mode ( ep , true ) ;
2017-03-13 19:13:26 +05:30
dw_pcie_write ( base + reg , size , val ) ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_w_mode ( ep , false ) ;
2013-07-31 17:14:10 +09:00
}
static int exynos_pcie_rd_own_conf ( struct pcie_port * pp , int where , int size ,
u32 * val )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2013-07-31 17:14:10 +09:00
int ret ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_r_mode ( ep , true ) ;
2017-02-15 18:48:14 +05:30
ret = dw_pcie_read ( pci - > dbi_base + where , size , val ) ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_r_mode ( ep , false ) ;
2013-07-31 17:14:10 +09:00
return ret ;
}
static int exynos_pcie_wr_own_conf ( struct pcie_port * pp , int where , int size ,
u32 val )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2013-07-31 17:14:10 +09:00
int ret ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_w_mode ( ep , true ) ;
2017-02-15 18:48:14 +05:30
ret = dw_pcie_write ( pci - > dbi_base + where , size , val ) ;
2017-01-16 15:31:34 +09:00
exynos_pcie_sideband_dbi_w_mode ( ep , false ) ;
2013-07-31 17:14:10 +09:00
return ret ;
}
2017-02-15 18:48:14 +05:30
static int exynos_pcie_link_up ( struct dw_pcie * pci )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2016-10-06 13:33:39 -05:00
u32 val ;
2013-07-31 17:14:10 +09:00
2017-02-01 10:13:06 +05:30
val = exynos_pcie_readl ( ep - > mem_res - > elbi_base , PCIE_ELBI_RDLH_LINKUP ) ;
2013-07-31 17:14:10 +09:00
if ( val = = PCIE_ELBI_LTSSM_ENABLE )
return 1 ;
return 0 ;
}
2017-07-15 23:39:45 -07:00
static int exynos_pcie_host_init ( struct pcie_port * pp )
2013-07-31 17:14:10 +09:00
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
2017-02-21 15:13:30 -06:00
struct exynos_pcie * ep = to_exynos_pcie ( pci ) ;
2016-10-06 13:33:39 -05:00
2017-01-16 15:31:34 +09:00
exynos_pcie_establish_link ( ep ) ;
exynos_pcie_enable_interrupts ( ep ) ;
2017-07-15 23:39:45 -07:00
return 0 ;
2013-07-31 17:14:10 +09:00
}
2017-06-05 16:53:46 +08:00
static const struct dw_pcie_host_ops exynos_pcie_host_ops = {
2013-07-31 17:14:10 +09:00
. rd_own_conf = exynos_pcie_rd_own_conf ,
. wr_own_conf = exynos_pcie_wr_own_conf ,
. host_init = exynos_pcie_host_init ,
} ;
2017-01-16 15:31:34 +09:00
static int __init exynos_add_pcie_port ( struct exynos_pcie * ep ,
2014-10-22 13:58:49 +09:00
struct platform_device * pdev )
2013-07-31 17:14:10 +09:00
{
2017-02-21 15:13:30 -06:00
struct dw_pcie * pci = ep - > pci ;
2017-02-15 18:48:14 +05:30
struct pcie_port * pp = & pci - > pp ;
struct device * dev = & pdev - > dev ;
2013-07-31 17:14:10 +09:00
int ret ;
pp - > irq = platform_get_irq ( pdev , 1 ) ;
2017-08-31 14:52:01 -03:00
if ( pp - > irq < 0 ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " failed to get irq \n " ) ;
2017-08-31 14:52:01 -03:00
return pp - > irq ;
2013-07-31 17:14:10 +09:00
}
2016-10-06 13:33:41 -05:00
ret = devm_request_irq ( dev , pp - > irq , exynos_pcie_irq_handler ,
2017-01-16 15:31:34 +09:00
IRQF_SHARED , " exynos-pcie " , ep ) ;
2013-07-31 17:14:10 +09:00
if ( ret ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " failed to request irq \n " ) ;
2013-07-31 17:14:10 +09:00
return ret ;
}
2013-09-06 15:54:59 +09:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
pp - > msi_irq = platform_get_irq ( pdev , 0 ) ;
2017-08-31 14:52:01 -03:00
if ( pp - > msi_irq < 0 ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " failed to get msi irq \n " ) ;
2017-08-31 14:52:01 -03:00
return pp - > msi_irq ;
2013-09-06 15:54:59 +09:00
}
2016-10-06 13:33:41 -05:00
ret = devm_request_irq ( dev , pp - > msi_irq ,
2013-09-06 15:54:59 +09:00
exynos_pcie_msi_irq_handler ,
2015-12-10 21:18:20 +02:00
IRQF_SHARED | IRQF_NO_THREAD ,
2017-01-16 15:31:34 +09:00
" exynos-pcie " , ep ) ;
2013-09-06 15:54:59 +09:00
if ( ret ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " failed to request msi irq \n " ) ;
2013-09-06 15:54:59 +09:00
return ret ;
}
}
2013-07-31 17:14:10 +09:00
pp - > root_bus_nr = - 1 ;
pp - > ops = & exynos_pcie_host_ops ;
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
2016-10-06 13:33:41 -05:00
dev_err ( dev , " failed to initialize host \n " ) ;
2013-07-31 17:14:10 +09:00
return ret ;
}
return 0 ;
}
2017-02-15 18:48:14 +05:30
static const struct dw_pcie_ops dw_pcie_ops = {
2017-03-13 19:13:26 +05:30
. read_dbi = exynos_pcie_read_dbi ,
. write_dbi = exynos_pcie_write_dbi ,
2017-02-15 18:48:14 +05:30
. link_up = exynos_pcie_link_up ,
} ;
2013-07-31 17:14:10 +09:00
static int __init exynos_pcie_probe ( struct platform_device * pdev )
{
2016-10-06 13:33:41 -05:00
struct device * dev = & pdev - > dev ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2017-01-16 15:31:34 +09:00
struct exynos_pcie * ep ;
2016-10-06 13:33:41 -05:00
struct device_node * np = dev - > of_node ;
2013-07-31 17:14:10 +09:00
int ret ;
2017-01-16 15:31:34 +09:00
ep = devm_kzalloc ( dev , sizeof ( * ep ) , GFP_KERNEL ) ;
if ( ! ep )
2013-07-31 17:14:10 +09:00
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 ;
2013-07-31 17:14:10 +09:00
2017-02-25 02:08:12 -08:00
ep - > pci = pci ;
2017-02-01 10:13:06 +05:30
ep - > ops = ( const struct exynos_pcie_ops * )
of_device_get_match_data ( dev ) ;
2013-07-31 17:14:10 +09:00
2017-02-01 10:13:06 +05:30
ep - > reset_gpio = of_get_named_gpio ( np , " reset-gpio " , 0 ) ;
2013-07-31 17:14:10 +09:00
2017-02-13 17:26:13 +09:00
ep - > phy = devm_of_phy_get ( dev , np , NULL ) ;
if ( IS_ERR ( ep - > phy ) ) {
if ( PTR_ERR ( ep - > phy ) = = - EPROBE_DEFER )
return PTR_ERR ( ep - > phy ) ;
2017-12-27 18:43:27 +09:00
ep - > phy = NULL ;
}
2017-02-13 17:26:13 +09:00
2017-02-01 10:13:06 +05:30
if ( ep - > ops & & ep - > ops - > get_mem_resources ) {
ret = ep - > ops - > get_mem_resources ( pdev , ep ) ;
if ( ret )
return ret ;
2013-09-29 10:29:11 +08:00
}
2013-07-31 17:14:10 +09:00
2018-01-22 11:28:54 +09:00
if ( ep - > ops & & ep - > ops - > get_clk_resources & &
ep - > ops - > init_clk_resources ) {
2017-02-01 10:13:06 +05:30
ret = ep - > ops - > get_clk_resources ( ep ) ;
if ( ret )
return ret ;
ret = ep - > ops - > init_clk_resources ( ep ) ;
if ( ret )
return ret ;
2013-09-29 10:29:11 +08:00
}
2013-07-31 17:14:10 +09:00
2017-02-21 15:13:30 -06:00
platform_set_drvdata ( pdev , ep ) ;
2017-02-15 18:48:11 +05:30
2017-01-16 15:31:34 +09:00
ret = exynos_add_pcie_port ( ep , pdev ) ;
2013-07-31 17:14:10 +09:00
if ( ret < 0 )
2017-02-01 10:13:06 +05:30
goto fail_probe ;
2013-07-31 17:14:10 +09:00
return 0 ;
2017-02-01 10:13:06 +05:30
fail_probe :
2017-12-27 18:43:27 +09:00
phy_exit ( ep - > phy ) ;
2017-02-13 17:26:13 +09:00
2017-02-01 10:13:06 +05:30
if ( ep - > ops & & ep - > ops - > deinit_clk_resources )
ep - > ops - > deinit_clk_resources ( ep ) ;
2013-07-31 17:14:10 +09:00
return ret ;
}
static int __exit exynos_pcie_remove ( struct platform_device * pdev )
{
2017-01-16 15:31:34 +09:00
struct exynos_pcie * ep = platform_get_drvdata ( pdev ) ;
2013-07-31 17:14:10 +09:00
2017-02-01 10:13:06 +05:30
if ( ep - > ops & & ep - > ops - > deinit_clk_resources )
ep - > ops - > deinit_clk_resources ( ep ) ;
2013-07-31 17:14:10 +09:00
return 0 ;
}
static const struct of_device_id exynos_pcie_of_match [ ] = {
2017-02-01 10:13:06 +05:30
{
. compatible = " samsung,exynos5440-pcie " ,
. data = & exynos5440_pcie_ops
} ,
2013-07-31 17:14:10 +09:00
{ } ,
} ;
static struct platform_driver exynos_pcie_driver = {
. remove = __exit_p ( exynos_pcie_remove ) ,
. driver = {
. name = " exynos-pcie " ,
2013-10-21 14:36:43 +05:30
. of_match_table = exynos_pcie_of_match ,
2013-07-31 17:14:10 +09:00
} ,
} ;
/* Exynos PCIe driver does not allow module unload */
2014-10-22 13:58:49 +09:00
static int __init exynos_pcie_init ( void )
2013-07-31 17:14:10 +09:00
{
return platform_driver_probe ( & exynos_pcie_driver , exynos_pcie_probe ) ;
}
2014-10-22 13:58:49 +09:00
subsys_initcall ( exynos_pcie_init ) ;