2021-06-25 14:55:11 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Rockchip SoCs .
*
* Copyright ( C ) 2021 Rockchip Electronics Co . , Ltd .
* http : //www.rock-chips.com
*
* Author : Simon Xue < xxm @ rock - chips . com >
*/
# include <linux/clk.h>
# include <linux/gpio/consumer.h>
2022-04-29 08:38:29 -04:00
# include <linux/irqchip/chained_irq.h>
# include <linux/irqdomain.h>
2021-06-25 14:55:11 +08:00
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of_device.h>
2022-04-29 08:38:29 -04:00
# include <linux/of_irq.h>
2021-06-25 14:55:11 +08:00
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include "pcie-designware.h"
/*
* The upper 16 bits of PCIE_CLIENT_CONFIG are a write
* mask for the lower 16 bits .
*/
# define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
# define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val)
2022-04-29 08:38:29 -04:00
# define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val)
2021-06-25 14:55:11 +08:00
# define to_rockchip_pcie(x) dev_get_drvdata((x)->dev)
# define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40)
# define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc)
# define PCIE_SMLH_LINKUP BIT(16)
# define PCIE_RDLH_LINKUP BIT(17)
# define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP)
# define PCIE_L0S_ENTRY 0x11
# define PCIE_CLIENT_GENERAL_CONTROL 0x0
2022-04-29 08:38:29 -04:00
# define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8
# define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c
2021-06-25 14:55:11 +08:00
# define PCIE_CLIENT_GENERAL_DEBUG 0x104
2022-04-29 08:38:29 -04:00
# define PCIE_CLIENT_HOT_RESET_CTRL 0x180
2021-06-25 14:55:11 +08:00
# define PCIE_CLIENT_LTSSM_STATUS 0x300
2022-04-29 08:38:29 -04:00
# define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
2021-06-25 14:55:11 +08:00
# define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0)
struct rockchip_pcie {
struct dw_pcie pci ;
void __iomem * apb_base ;
struct phy * phy ;
struct clk_bulk_data * clks ;
unsigned int clk_cnt ;
struct reset_control * rst ;
struct gpio_desc * rst_gpio ;
struct regulator * vpcie3v3 ;
2022-04-29 08:38:29 -04:00
struct irq_domain * irq_domain ;
2021-06-25 14:55:11 +08:00
} ;
static int rockchip_pcie_readl_apb ( struct rockchip_pcie * rockchip ,
u32 reg )
{
return readl_relaxed ( rockchip - > apb_base + reg ) ;
}
static void rockchip_pcie_writel_apb ( struct rockchip_pcie * rockchip ,
u32 val , u32 reg )
{
writel_relaxed ( val , rockchip - > apb_base + reg ) ;
}
2022-04-29 08:38:29 -04:00
static void rockchip_pcie_legacy_int_handler ( struct irq_desc * desc )
{
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
struct rockchip_pcie * rockchip = irq_desc_get_handler_data ( desc ) ;
unsigned long reg , hwirq ;
chained_irq_enter ( chip , desc ) ;
reg = rockchip_pcie_readl_apb ( rockchip , PCIE_CLIENT_INTR_STATUS_LEGACY ) ;
for_each_set_bit ( hwirq , & reg , 4 )
generic_handle_domain_irq ( rockchip - > irq_domain , hwirq ) ;
chained_irq_exit ( chip , desc ) ;
}
static void rockchip_intx_mask ( struct irq_data * data )
{
rockchip_pcie_writel_apb ( irq_data_get_irq_chip_data ( data ) ,
HIWORD_UPDATE_BIT ( BIT ( data - > hwirq ) ) ,
PCIE_CLIENT_INTR_MASK_LEGACY ) ;
} ;
static void rockchip_intx_unmask ( struct irq_data * data )
{
rockchip_pcie_writel_apb ( irq_data_get_irq_chip_data ( data ) ,
HIWORD_DISABLE_BIT ( BIT ( data - > hwirq ) ) ,
PCIE_CLIENT_INTR_MASK_LEGACY ) ;
} ;
static struct irq_chip rockchip_intx_irq_chip = {
. name = " INTx " ,
. irq_mask = rockchip_intx_mask ,
. irq_unmask = rockchip_intx_unmask ,
. flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND ,
} ;
static int rockchip_pcie_intx_map ( struct irq_domain * domain , unsigned int irq ,
irq_hw_number_t hwirq )
{
irq_set_chip_and_handler ( irq , & rockchip_intx_irq_chip , handle_level_irq ) ;
irq_set_chip_data ( irq , domain - > host_data ) ;
return 0 ;
}
static const struct irq_domain_ops intx_domain_ops = {
. map = rockchip_pcie_intx_map ,
} ;
static int rockchip_pcie_init_irq_domain ( struct rockchip_pcie * rockchip )
{
struct device * dev = rockchip - > pci . dev ;
struct device_node * intc ;
intc = of_get_child_by_name ( dev - > of_node , " legacy-interrupt-controller " ) ;
if ( ! intc ) {
dev_err ( dev , " missing child interrupt-controller node \n " ) ;
return - EINVAL ;
}
rockchip - > irq_domain = irq_domain_add_linear ( intc , PCI_NUM_INTX ,
& intx_domain_ops , rockchip ) ;
of_node_put ( intc ) ;
if ( ! rockchip - > irq_domain ) {
dev_err ( dev , " failed to get a INTx IRQ domain \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2021-06-25 14:55:11 +08:00
static void rockchip_pcie_enable_ltssm ( struct rockchip_pcie * rockchip )
{
rockchip_pcie_writel_apb ( rockchip , PCIE_CLIENT_ENABLE_LTSSM ,
PCIE_CLIENT_GENERAL_CONTROL ) ;
}
static int rockchip_pcie_link_up ( struct dw_pcie * pci )
{
struct rockchip_pcie * rockchip = to_rockchip_pcie ( pci ) ;
u32 val = rockchip_pcie_readl_apb ( rockchip , PCIE_CLIENT_LTSSM_STATUS ) ;
if ( ( val & PCIE_LINKUP ) = = PCIE_LINKUP & &
( val & PCIE_LTSSM_STATUS_MASK ) = = PCIE_L0S_ENTRY )
return 1 ;
return 0 ;
}
static int rockchip_pcie_start_link ( struct dw_pcie * pci )
{
struct rockchip_pcie * rockchip = to_rockchip_pcie ( pci ) ;
/* Reset device */
gpiod_set_value_cansleep ( rockchip - > rst_gpio , 0 ) ;
rockchip_pcie_enable_ltssm ( rockchip ) ;
/*
* PCIe requires the refclk to be stable for 100 µ s prior to releasing
* PERST . See table 2 - 4 in section 2.6 .2 AC Specifications of the PCI
* Express Card Electromechanical Specification , 1.1 . However , we don ' t
* know if the refclk is coming from RC ' s PHY or external OSC . If it ' s
* from RC , so enabling LTSSM is the just right place to release # PERST .
* We need more extra time as before , rather than setting just
* 100u s as we don ' t know how long should the device need to reset .
*/
msleep ( 100 ) ;
gpiod_set_value_cansleep ( rockchip - > rst_gpio , 1 ) ;
return 0 ;
}
2022-06-24 17:34:25 +03:00
static int rockchip_pcie_host_init ( struct dw_pcie_rp * pp )
2021-06-25 14:55:11 +08:00
{
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct rockchip_pcie * rockchip = to_rockchip_pcie ( pci ) ;
2022-04-29 08:38:29 -04:00
struct device * dev = rockchip - > pci . dev ;
2021-06-25 14:55:11 +08:00
u32 val = HIWORD_UPDATE_BIT ( PCIE_LTSSM_ENABLE_ENHANCE ) ;
2022-04-29 08:38:29 -04:00
int irq , ret ;
irq = of_irq_get_byname ( dev - > of_node , " legacy " ) ;
if ( irq < 0 )
return irq ;
ret = rockchip_pcie_init_irq_domain ( rockchip ) ;
if ( ret < 0 )
dev_err ( dev , " failed to init irq domain \n " ) ;
irq_set_chained_handler_and_data ( irq , rockchip_pcie_legacy_int_handler ,
rockchip ) ;
2021-06-25 14:55:11 +08:00
/* LTSSM enable control mode */
rockchip_pcie_writel_apb ( rockchip , val , PCIE_CLIENT_HOT_RESET_CTRL ) ;
rockchip_pcie_writel_apb ( rockchip , PCIE_CLIENT_RC_MODE ,
PCIE_CLIENT_GENERAL_CONTROL ) ;
return 0 ;
}
static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
. host_init = rockchip_pcie_host_init ,
} ;
static int rockchip_pcie_clk_init ( struct rockchip_pcie * rockchip )
{
struct device * dev = rockchip - > pci . dev ;
int ret ;
ret = devm_clk_bulk_get_all ( dev , & rockchip - > clks ) ;
if ( ret < 0 )
return ret ;
rockchip - > clk_cnt = ret ;
return clk_bulk_prepare_enable ( rockchip - > clk_cnt , rockchip - > clks ) ;
}
static int rockchip_pcie_resource_get ( struct platform_device * pdev ,
struct rockchip_pcie * rockchip )
{
rockchip - > apb_base = devm_platform_ioremap_resource_byname ( pdev , " apb " ) ;
if ( IS_ERR ( rockchip - > apb_base ) )
return PTR_ERR ( rockchip - > apb_base ) ;
rockchip - > rst_gpio = devm_gpiod_get_optional ( & pdev - > dev , " reset " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( rockchip - > rst_gpio ) )
return PTR_ERR ( rockchip - > rst_gpio ) ;
2022-04-29 08:38:28 -04:00
rockchip - > rst = devm_reset_control_array_get_exclusive ( & pdev - > dev ) ;
if ( IS_ERR ( rockchip - > rst ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( rockchip - > rst ) ,
" failed to get reset lines \n " ) ;
2021-06-25 14:55:11 +08:00
return 0 ;
}
static int rockchip_pcie_phy_init ( struct rockchip_pcie * rockchip )
{
struct device * dev = rockchip - > pci . dev ;
int ret ;
rockchip - > phy = devm_phy_get ( dev , " pcie-phy " ) ;
if ( IS_ERR ( rockchip - > phy ) )
return dev_err_probe ( dev , PTR_ERR ( rockchip - > phy ) ,
" missing PHY \n " ) ;
ret = phy_init ( rockchip - > phy ) ;
if ( ret < 0 )
return ret ;
ret = phy_power_on ( rockchip - > phy ) ;
if ( ret )
phy_exit ( rockchip - > phy ) ;
return ret ;
}
static void rockchip_pcie_phy_deinit ( struct rockchip_pcie * rockchip )
{
phy_exit ( rockchip - > phy ) ;
phy_power_off ( rockchip - > phy ) ;
}
static const struct dw_pcie_ops dw_pcie_ops = {
. link_up = rockchip_pcie_link_up ,
. start_link = rockchip_pcie_start_link ,
} ;
static int rockchip_pcie_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct rockchip_pcie * rockchip ;
2022-06-24 17:34:25 +03:00
struct dw_pcie_rp * pp ;
2021-06-25 14:55:11 +08:00
int ret ;
rockchip = devm_kzalloc ( dev , sizeof ( * rockchip ) , GFP_KERNEL ) ;
if ( ! rockchip )
return - ENOMEM ;
platform_set_drvdata ( pdev , rockchip ) ;
rockchip - > pci . dev = dev ;
rockchip - > pci . ops = & dw_pcie_ops ;
pp = & rockchip - > pci . pp ;
pp - > ops = & rockchip_pcie_host_ops ;
ret = rockchip_pcie_resource_get ( pdev , rockchip ) ;
if ( ret )
return ret ;
2022-04-29 08:38:28 -04:00
ret = reset_control_assert ( rockchip - > rst ) ;
if ( ret )
return ret ;
2021-06-25 14:55:11 +08:00
/* DON'T MOVE ME: must be enable before PHY init */
rockchip - > vpcie3v3 = devm_regulator_get_optional ( dev , " vpcie3v3 " ) ;
if ( IS_ERR ( rockchip - > vpcie3v3 ) ) {
if ( PTR_ERR ( rockchip - > vpcie3v3 ) ! = - ENODEV )
return dev_err_probe ( dev , PTR_ERR ( rockchip - > vpcie3v3 ) ,
" failed to get vpcie3v3 regulator \n " ) ;
rockchip - > vpcie3v3 = NULL ;
} else {
ret = regulator_enable ( rockchip - > vpcie3v3 ) ;
if ( ret ) {
dev_err ( dev , " failed to enable vpcie3v3 regulator \n " ) ;
return ret ;
}
}
ret = rockchip_pcie_phy_init ( rockchip ) ;
if ( ret )
goto disable_regulator ;
2022-04-29 08:38:28 -04:00
ret = reset_control_deassert ( rockchip - > rst ) ;
2021-06-25 14:55:11 +08:00
if ( ret )
goto deinit_phy ;
ret = rockchip_pcie_clk_init ( rockchip ) ;
if ( ret )
goto deinit_phy ;
ret = dw_pcie_host_init ( pp ) ;
if ( ! ret )
return 0 ;
clk_bulk_disable_unprepare ( rockchip - > clk_cnt , rockchip - > clks ) ;
deinit_phy :
rockchip_pcie_phy_deinit ( rockchip ) ;
disable_regulator :
if ( rockchip - > vpcie3v3 )
regulator_disable ( rockchip - > vpcie3v3 ) ;
return ret ;
}
static const struct of_device_id rockchip_pcie_of_match [ ] = {
{ . compatible = " rockchip,rk3568-pcie " , } ,
{ } ,
} ;
static struct platform_driver rockchip_pcie_driver = {
. driver = {
. name = " rockchip-dw-pcie " ,
. of_match_table = rockchip_pcie_of_match ,
. suppress_bind_attrs = true ,
} ,
. probe = rockchip_pcie_probe ,
} ;
builtin_platform_driver ( rockchip_pcie_driver ) ;