2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2017-05-21 11:42:24 +08:00
/*
* MediaTek PCIe host controller driver .
*
* Copyright ( c ) 2017 MediaTek Inc .
* Author : Ryder Lee < ryder . lee @ mediatek . com >
2017-08-10 14:34:59 +08:00
* Honghui Zhang < honghui . zhang @ mediatek . com >
2017-05-21 11:42:24 +08:00
*/
# include <linux/clk.h>
# include <linux/delay.h>
2017-08-10 14:34:54 +08:00
# include <linux/iopoll.h>
2017-08-10 14:34:59 +08:00
# include <linux/irq.h>
2018-05-04 13:47:33 +08:00
# include <linux/irqchip/chained_irq.h>
2017-08-10 14:34:59 +08:00
# include <linux/irqdomain.h>
2017-05-21 11:42:24 +08:00
# include <linux/kernel.h>
2018-05-04 13:47:33 +08:00
# include <linux/msi.h>
2018-10-15 16:08:59 +08:00
# include <linux/module.h>
2017-05-21 11:42:24 +08:00
# include <linux/of_address.h>
# include <linux/of_pci.h>
# include <linux/of_platform.h>
# include <linux/pci.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/reset.h>
2018-05-11 12:15:30 -05:00
# include "../pci.h"
2017-05-21 11:42:24 +08:00
/* PCIe shared registers */
# define PCIE_SYS_CFG 0x00
# define PCIE_INT_ENABLE 0x0c
# define PCIE_CFG_ADDR 0x20
# define PCIE_CFG_DATA 0x24
/* PCIe per port registers */
# define PCIE_BAR0_SETUP 0x10
# define PCIE_CLASS 0x34
# define PCIE_LINK_STATUS 0x50
# define PCIE_PORT_INT_EN(x) BIT(20 + (x))
# define PCIE_PORT_PERST(x) BIT(1 + (x))
# define PCIE_PORT_LINKUP BIT(0)
# define PCIE_BAR_MAP_MAX GENMASK(31, 16)
# define PCIE_BAR_ENABLE BIT(0)
# define PCIE_REVISION_ID BIT(0)
# define PCIE_CLASS_CODE (0x60400 << 8)
# define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \
( ( ( ( regn ) > > 8 ) & GENMASK ( 3 , 0 ) ) < < 24 ) )
# define PCIE_CONF_FUN(fun) (((fun) << 8) & GENMASK(10, 8))
# define PCIE_CONF_DEV(dev) (((dev) << 11) & GENMASK(15, 11))
# define PCIE_CONF_BUS(bus) (((bus) << 16) & GENMASK(23, 16))
# define PCIE_CONF_ADDR(regn, fun, dev, bus) \
( PCIE_CONF_REG ( regn ) | PCIE_CONF_FUN ( fun ) | \
PCIE_CONF_DEV ( dev ) | PCIE_CONF_BUS ( bus ) )
/* MediaTek specific configuration registers */
# define PCIE_FTS_NUM 0x70c
# define PCIE_FTS_NUM_MASK GENMASK(15, 8)
# define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
# define PCIE_FC_CREDIT 0x73c
# define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
# define PCIE_FC_CREDIT_VAL(x) ((x) << 16)
2017-08-10 14:34:59 +08:00
/* PCIe V2 share registers */
# define PCIE_SYS_CFG_V2 0x0
# define PCIE_CSR_LTSSM_EN(x) BIT(0 + (x) * 8)
# define PCIE_CSR_ASPM_L1_EN(x) BIT(1 + (x) * 8)
/* PCIe V2 per-port registers */
2017-08-14 21:04:28 +08:00
# define PCIE_MSI_VECTOR 0x0c0
2018-05-04 13:47:32 +08:00
# define PCIE_CONF_VEND_ID 0x100
2019-06-28 15:34:25 +08:00
# define PCIE_CONF_DEVICE_ID 0x102
2018-05-04 13:47:32 +08:00
# define PCIE_CONF_CLASS_ID 0x106
2017-08-10 14:34:59 +08:00
# define PCIE_INT_MASK 0x420
# define INTX_MASK GENMASK(19, 16)
# define INTX_SHIFT 16
# define PCIE_INT_STATUS 0x424
2017-08-14 21:04:28 +08:00
# define MSI_STATUS BIT(23)
# define PCIE_IMSI_STATUS 0x42c
# define PCIE_IMSI_ADDR 0x430
# define MSI_MASK BIT(23)
# define MTK_MSI_IRQS_NUM 32
2017-08-10 14:34:59 +08:00
# define PCIE_AHB_TRANS_BASE0_L 0x438
# define PCIE_AHB_TRANS_BASE0_H 0x43c
# define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
# define PCIE_AXI_WINDOW0 0x448
# define WIN_ENABLE BIT(7)
2019-02-01 13:36:07 +08:00
/*
* Define PCIe to AHB window size as 2 ^ 33 to support max 8 GB address space
* translate , support least 4 GB DRAM size access from EP DMA ( physical DRAM
* start from 0x40000000 ) .
*/
# define PCIE2AHB_SIZE 0x21
2017-08-10 14:34:59 +08:00
/* PCIe V2 configuration transaction header */
# define PCIE_CFG_HEADER0 0x460
# define PCIE_CFG_HEADER1 0x464
# define PCIE_CFG_HEADER2 0x468
# define PCIE_CFG_WDATA 0x470
# define PCIE_APP_TLP_REQ 0x488
# define PCIE_CFG_RDATA 0x48c
# define APP_CFG_REQ BIT(0)
# define APP_CPL_STATUS GENMASK(7, 5)
# define CFG_WRRD_TYPE_0 4
# define CFG_WR_FMT 2
# define CFG_RD_FMT 0
# define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0))
# define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24))
# define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29))
# define CFG_DW2_REGN(regn) ((regn) & GENMASK(11, 2))
# define CFG_DW2_FUN(fun) (((fun) << 16) & GENMASK(18, 16))
# define CFG_DW2_DEV(dev) (((dev) << 19) & GENMASK(23, 19))
# define CFG_DW2_BUS(bus) (((bus) << 24) & GENMASK(31, 24))
# define CFG_HEADER_DW0(type, fmt) \
( CFG_DW0_LENGTH ( 1 ) | CFG_DW0_TYPE ( type ) | CFG_DW0_FMT ( fmt ) )
# define CFG_HEADER_DW1(where, size) \
( GENMASK ( ( ( size ) - 1 ) , 0 ) < < ( ( where ) & 0x3 ) )
# define CFG_HEADER_DW2(regn, fun, dev, bus) \
( CFG_DW2_REGN ( regn ) | CFG_DW2_FUN ( fun ) | \
CFG_DW2_DEV ( dev ) | CFG_DW2_BUS ( bus ) )
# define PCIE_RST_CTRL 0x510
# define PCIE_PHY_RSTB BIT(0)
# define PCIE_PIPE_SRSTB BIT(1)
# define PCIE_MAC_SRSTB BIT(2)
# define PCIE_CRSTB BIT(3)
# define PCIE_PERSTB BIT(8)
# define PCIE_LINKDOWN_RST_EN GENMASK(15, 13)
# define PCIE_LINK_STATUS_V2 0x804
# define PCIE_PORT_LINKUP_V2 BIT(10)
2017-08-10 14:34:56 +08:00
struct mtk_pcie_port ;
/**
* struct mtk_pcie_soc - differentiate between host generations
2018-05-04 13:47:32 +08:00
* @ need_fix_class_id : whether this host ' s class ID needed to be fixed or not
2019-06-28 15:34:25 +08:00
* @ need_fix_device_id : whether this host ' s device ID needed to be fixed or not
* @ device_id : device ID which this host need to be fixed
2017-08-10 14:34:56 +08:00
* @ ops : pointer to configuration access functions
* @ startup : pointer to controller setting functions
2017-08-10 14:34:59 +08:00
* @ setup_irq : pointer to initialize IRQ functions
2017-08-10 14:34:56 +08:00
*/
struct mtk_pcie_soc {
2018-05-04 13:47:32 +08:00
bool need_fix_class_id ;
2019-06-28 15:34:25 +08:00
bool need_fix_device_id ;
unsigned int device_id ;
2017-08-10 14:34:56 +08:00
struct pci_ops * ops ;
int ( * startup ) ( struct mtk_pcie_port * port ) ;
2017-08-10 14:34:59 +08:00
int ( * setup_irq ) ( struct mtk_pcie_port * port , struct device_node * node ) ;
2017-08-10 14:34:56 +08:00
} ;
2017-05-21 11:42:24 +08:00
/**
* struct mtk_pcie_port - PCIe port information
* @ base : IO mapped register base
* @ list : port list
* @ pcie : pointer to PCIe host info
* @ reset : pointer to port reset control
2017-08-10 14:34:59 +08:00
* @ sys_ck : pointer to transaction / data link layer clock
* @ ahb_ck : pointer to AHB slave interface operating clock for CSR access
* and RC initiated MMIO access
* @ axi_ck : pointer to application layer MMIO channel operating clock
* @ aux_ck : pointer to pe2_mac_bridge and pe2_mac_core operating clock
* when pcie_mac_ck / pcie_pipe_ck is turned off
* @ obff_ck : pointer to OBFF functional block operating clock
* @ pipe_ck : pointer to LTSSM and PHY / MAC layer operating clock
* @ phy : pointer to PHY control block
2017-08-10 14:34:55 +08:00
* @ slot : port slot
2018-10-15 16:08:59 +08:00
* @ irq : GIC irq
2017-08-10 14:34:59 +08:00
* @ irq_domain : legacy INTx IRQ domain
2018-05-04 13:47:33 +08:00
* @ inner_domain : inner IRQ domain
2017-08-14 21:04:28 +08:00
* @ msi_domain : MSI IRQ domain
2018-05-04 13:47:33 +08:00
* @ lock : protect the msi_irq_in_use bitmap
2017-08-14 21:04:28 +08:00
* @ msi_irq_in_use : bit map for assigned MSI IRQ
2017-05-21 11:42:24 +08:00
*/
struct mtk_pcie_port {
void __iomem * base ;
struct list_head list ;
struct mtk_pcie * pcie ;
struct reset_control * reset ;
struct clk * sys_ck ;
2017-08-10 14:34:59 +08:00
struct clk * ahb_ck ;
struct clk * axi_ck ;
struct clk * aux_ck ;
struct clk * obff_ck ;
struct clk * pipe_ck ;
2017-05-21 11:42:24 +08:00
struct phy * phy ;
2017-08-10 14:34:55 +08:00
u32 slot ;
2018-10-15 16:08:59 +08:00
int irq ;
2017-08-10 14:34:59 +08:00
struct irq_domain * irq_domain ;
2018-05-04 13:47:33 +08:00
struct irq_domain * inner_domain ;
2017-08-14 21:04:28 +08:00
struct irq_domain * msi_domain ;
2018-05-04 13:47:33 +08:00
struct mutex lock ;
2017-08-14 21:04:28 +08:00
DECLARE_BITMAP ( msi_irq_in_use , MTK_MSI_IRQS_NUM ) ;
2017-05-21 11:42:24 +08:00
} ;
/**
* struct mtk_pcie - PCIe host information
* @ dev : pointer to PCIe device
* @ base : IO mapped register base
* @ free_ck : free - run reference clock
* @ mem : non - prefetchable memory resource
* @ ports : pointer to PCIe port information
2017-08-10 14:34:56 +08:00
* @ soc : pointer to SoC - dependent operations
2017-05-21 11:42:24 +08:00
*/
struct mtk_pcie {
struct device * dev ;
void __iomem * base ;
struct clk * free_ck ;
struct list_head ports ;
2017-08-10 14:34:56 +08:00
const struct mtk_pcie_soc * soc ;
2017-05-21 11:42:24 +08:00
} ;
static void mtk_pcie_subsys_powerdown ( struct mtk_pcie * pcie )
{
struct device * dev = pcie - > dev ;
clk_disable_unprepare ( pcie - > free_ck ) ;
2018-10-15 16:08:54 +08:00
pm_runtime_put_sync ( dev ) ;
pm_runtime_disable ( dev ) ;
2017-05-21 11:42:24 +08:00
}
static void mtk_pcie_port_free ( struct mtk_pcie_port * port )
{
struct mtk_pcie * pcie = port - > pcie ;
struct device * dev = pcie - > dev ;
devm_iounmap ( dev , port - > base ) ;
list_del ( & port - > list ) ;
devm_kfree ( dev , port ) ;
}
static void mtk_pcie_put_resources ( struct mtk_pcie * pcie )
{
struct mtk_pcie_port * port , * tmp ;
list_for_each_entry_safe ( port , tmp , & pcie - > ports , list ) {
phy_power_off ( port - > phy ) ;
2017-08-10 14:34:59 +08:00
phy_exit ( port - > phy ) ;
clk_disable_unprepare ( port - > pipe_ck ) ;
clk_disable_unprepare ( port - > obff_ck ) ;
clk_disable_unprepare ( port - > axi_ck ) ;
clk_disable_unprepare ( port - > aux_ck ) ;
clk_disable_unprepare ( port - > ahb_ck ) ;
2017-05-21 11:42:24 +08:00
clk_disable_unprepare ( port - > sys_ck ) ;
mtk_pcie_port_free ( port ) ;
}
mtk_pcie_subsys_powerdown ( pcie ) ;
}
2017-08-10 14:34:59 +08:00
static int mtk_pcie_check_cfg_cpld ( struct mtk_pcie_port * port )
{
u32 val ;
int err ;
err = readl_poll_timeout_atomic ( port - > base + PCIE_APP_TLP_REQ , val ,
! ( val & APP_CFG_REQ ) , 10 ,
100 * USEC_PER_MSEC ) ;
if ( err )
return PCIBIOS_SET_FAILED ;
if ( readl ( port - > base + PCIE_APP_TLP_REQ ) & APP_CPL_STATUS )
return PCIBIOS_SET_FAILED ;
return PCIBIOS_SUCCESSFUL ;
}
static int mtk_pcie_hw_rd_cfg ( struct mtk_pcie_port * port , u32 bus , u32 devfn ,
int where , int size , u32 * val )
{
u32 tmp ;
/* Write PCIe configuration transaction header for Cfgrd */
writel ( CFG_HEADER_DW0 ( CFG_WRRD_TYPE_0 , CFG_RD_FMT ) ,
port - > base + PCIE_CFG_HEADER0 ) ;
writel ( CFG_HEADER_DW1 ( where , size ) , port - > base + PCIE_CFG_HEADER1 ) ;
writel ( CFG_HEADER_DW2 ( where , PCI_FUNC ( devfn ) , PCI_SLOT ( devfn ) , bus ) ,
port - > base + PCIE_CFG_HEADER2 ) ;
/* Trigger h/w to transmit Cfgrd TLP */
tmp = readl ( port - > base + PCIE_APP_TLP_REQ ) ;
tmp | = APP_CFG_REQ ;
writel ( tmp , port - > base + PCIE_APP_TLP_REQ ) ;
/* Check completion status */
if ( mtk_pcie_check_cfg_cpld ( port ) )
return PCIBIOS_SET_FAILED ;
/* Read cpld payload of Cfgrd */
* val = readl ( port - > base + PCIE_CFG_RDATA ) ;
if ( size = = 1 )
* val = ( * val > > ( 8 * ( where & 3 ) ) ) & 0xff ;
else if ( size = = 2 )
* val = ( * val > > ( 8 * ( where & 3 ) ) ) & 0xffff ;
return PCIBIOS_SUCCESSFUL ;
}
static int mtk_pcie_hw_wr_cfg ( struct mtk_pcie_port * port , u32 bus , u32 devfn ,
int where , int size , u32 val )
{
/* Write PCIe configuration transaction header for Cfgwr */
writel ( CFG_HEADER_DW0 ( CFG_WRRD_TYPE_0 , CFG_WR_FMT ) ,
port - > base + PCIE_CFG_HEADER0 ) ;
writel ( CFG_HEADER_DW1 ( where , size ) , port - > base + PCIE_CFG_HEADER1 ) ;
writel ( CFG_HEADER_DW2 ( where , PCI_FUNC ( devfn ) , PCI_SLOT ( devfn ) , bus ) ,
port - > base + PCIE_CFG_HEADER2 ) ;
/* Write Cfgwr data */
val = val < < 8 * ( where & 3 ) ;
writel ( val , port - > base + PCIE_CFG_WDATA ) ;
/* Trigger h/w to transmit Cfgwr TLP */
val = readl ( port - > base + PCIE_APP_TLP_REQ ) ;
val | = APP_CFG_REQ ;
writel ( val , port - > base + PCIE_APP_TLP_REQ ) ;
/* Check completion status */
return mtk_pcie_check_cfg_cpld ( port ) ;
}
static struct mtk_pcie_port * mtk_pcie_find_port ( struct pci_bus * bus ,
unsigned int devfn )
{
struct mtk_pcie * pcie = bus - > sysdata ;
struct mtk_pcie_port * port ;
2018-10-15 16:08:52 +08:00
struct pci_dev * dev = NULL ;
/*
* Walk the bus hierarchy to get the devfn value
* of the port in the root bus .
*/
while ( bus & & bus - > number ) {
dev = bus - > self ;
bus = dev - > bus ;
devfn = dev - > devfn ;
}
2017-08-10 14:34:59 +08:00
list_for_each_entry ( port , & pcie - > ports , list )
if ( port - > slot = = PCI_SLOT ( devfn ) )
return port ;
return NULL ;
}
static int mtk_pcie_config_read ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 * val )
{
struct mtk_pcie_port * port ;
u32 bn = bus - > number ;
int ret ;
port = mtk_pcie_find_port ( bus , devfn ) ;
if ( ! port ) {
* val = ~ 0 ;
return PCIBIOS_DEVICE_NOT_FOUND ;
}
ret = mtk_pcie_hw_rd_cfg ( port , bn , devfn , where , size , val ) ;
if ( ret )
* val = ~ 0 ;
return ret ;
}
static int mtk_pcie_config_write ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 val )
{
struct mtk_pcie_port * port ;
u32 bn = bus - > number ;
port = mtk_pcie_find_port ( bus , devfn ) ;
if ( ! port )
return PCIBIOS_DEVICE_NOT_FOUND ;
return mtk_pcie_hw_wr_cfg ( port , bn , devfn , where , size , val ) ;
}
static struct pci_ops mtk_pcie_ops_v2 = {
. read = mtk_pcie_config_read ,
. write = mtk_pcie_config_write ,
} ;
2018-05-04 13:47:33 +08:00
static void mtk_compose_msi_msg ( struct irq_data * data , struct msi_msg * msg )
2017-08-14 21:04:28 +08:00
{
2018-05-04 13:47:33 +08:00
struct mtk_pcie_port * port = irq_data_get_irq_chip_data ( data ) ;
phys_addr_t addr ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
/* MT2712/MT7622 only support 32-bit MSI addresses */
addr = virt_to_phys ( port - > base + PCIE_MSI_VECTOR ) ;
msg - > address_hi = 0 ;
msg - > address_lo = lower_32_bits ( addr ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
msg - > data = data - > hwirq ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
dev_dbg ( port - > pcie - > dev , " msi#%d address_hi %#x address_lo %#x \n " ,
( int ) data - > hwirq , msg - > address_hi , msg - > address_lo ) ;
2017-08-14 21:04:28 +08:00
}
2018-05-04 13:47:33 +08:00
static int mtk_msi_set_affinity ( struct irq_data * irq_data ,
const struct cpumask * mask , bool force )
2017-08-14 21:04:28 +08:00
{
2018-05-04 13:47:33 +08:00
return - EINVAL ;
2017-08-14 21:04:28 +08:00
}
2018-05-04 13:47:33 +08:00
static void mtk_msi_ack_irq ( struct irq_data * data )
2017-08-14 21:04:28 +08:00
{
2018-05-04 13:47:33 +08:00
struct mtk_pcie_port * port = irq_data_get_irq_chip_data ( data ) ;
u32 hwirq = data - > hwirq ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
writel ( 1 < < hwirq , port - > base + PCIE_IMSI_STATUS ) ;
}
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
static struct irq_chip mtk_msi_bottom_irq_chip = {
. name = " MTK MSI " ,
. irq_compose_msi_msg = mtk_compose_msi_msg ,
. irq_set_affinity = mtk_msi_set_affinity ,
. irq_ack = mtk_msi_ack_irq ,
} ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
static int mtk_pcie_irq_domain_alloc ( struct irq_domain * domain , unsigned int virq ,
unsigned int nr_irqs , void * args )
{
struct mtk_pcie_port * port = domain - > host_data ;
unsigned long bit ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
WARN_ON ( nr_irqs ! = 1 ) ;
mutex_lock ( & port - > lock ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
bit = find_first_zero_bit ( port - > msi_irq_in_use , MTK_MSI_IRQS_NUM ) ;
if ( bit > = MTK_MSI_IRQS_NUM ) {
mutex_unlock ( & port - > lock ) ;
return - ENOSPC ;
}
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
__set_bit ( bit , port - > msi_irq_in_use ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
mutex_unlock ( & port - > lock ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
irq_domain_set_info ( domain , virq , bit , & mtk_msi_bottom_irq_chip ,
domain - > host_data , handle_edge_irq ,
NULL , NULL ) ;
2017-08-14 21:04:28 +08:00
return 0 ;
}
2018-05-04 13:47:33 +08:00
static void mtk_pcie_irq_domain_free ( struct irq_domain * domain ,
unsigned int virq , unsigned int nr_irqs )
2017-08-14 21:04:28 +08:00
{
2018-05-04 13:47:33 +08:00
struct irq_data * d = irq_domain_get_irq_data ( domain , virq ) ;
struct mtk_pcie_port * port = irq_data_get_irq_chip_data ( d ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
mutex_lock ( & port - > lock ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
if ( ! test_bit ( d - > hwirq , port - > msi_irq_in_use ) )
dev_err ( port - > pcie - > dev , " trying to free unused MSI#%lu \n " ,
d - > hwirq ) ;
else
__clear_bit ( d - > hwirq , port - > msi_irq_in_use ) ;
mutex_unlock ( & port - > lock ) ;
2017-08-14 21:04:28 +08:00
2018-05-04 13:47:33 +08:00
irq_domain_free_irqs_parent ( domain , virq , nr_irqs ) ;
2017-08-14 21:04:28 +08:00
}
2018-05-04 13:47:33 +08:00
static const struct irq_domain_ops msi_domain_ops = {
. alloc = mtk_pcie_irq_domain_alloc ,
. free = mtk_pcie_irq_domain_free ,
2017-08-14 21:04:28 +08:00
} ;
static struct irq_chip mtk_msi_irq_chip = {
2018-05-04 13:47:33 +08:00
. name = " MTK PCIe MSI " ,
. irq_ack = irq_chip_ack_parent ,
. irq_mask = pci_msi_mask_irq ,
. irq_unmask = pci_msi_unmask_irq ,
2017-08-14 21:04:28 +08:00
} ;
2018-05-04 13:47:33 +08:00
static struct msi_domain_info mtk_msi_domain_info = {
. flags = ( MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_PCI_MSIX ) ,
. chip = & mtk_msi_irq_chip ,
} ;
static int mtk_pcie_allocate_msi_domains ( struct mtk_pcie_port * port )
2017-08-14 21:04:28 +08:00
{
2018-05-04 13:47:33 +08:00
struct fwnode_handle * fwnode = of_node_to_fwnode ( port - > pcie - > dev - > of_node ) ;
mutex_init ( & port - > lock ) ;
port - > inner_domain = irq_domain_create_linear ( fwnode , MTK_MSI_IRQS_NUM ,
& msi_domain_ops , port ) ;
if ( ! port - > inner_domain ) {
dev_err ( port - > pcie - > dev , " failed to create IRQ domain \n " ) ;
return - ENOMEM ;
}
port - > msi_domain = pci_msi_create_irq_domain ( fwnode , & mtk_msi_domain_info ,
port - > inner_domain ) ;
if ( ! port - > msi_domain ) {
dev_err ( port - > pcie - > dev , " failed to create MSI domain \n " ) ;
irq_domain_remove ( port - > inner_domain ) ;
return - ENOMEM ;
}
2017-08-14 21:04:28 +08:00
return 0 ;
}
static void mtk_pcie_enable_msi ( struct mtk_pcie_port * port )
{
u32 val ;
phys_addr_t msg_addr ;
msg_addr = virt_to_phys ( port - > base + PCIE_MSI_VECTOR ) ;
val = lower_32_bits ( msg_addr ) ;
writel ( val , port - > base + PCIE_IMSI_ADDR ) ;
val = readl ( port - > base + PCIE_INT_MASK ) ;
val & = ~ MSI_MASK ;
writel ( val , port - > base + PCIE_INT_MASK ) ;
}
2018-10-15 16:08:59 +08:00
static void mtk_pcie_irq_teardown ( struct mtk_pcie * pcie )
{
struct mtk_pcie_port * port , * tmp ;
list_for_each_entry_safe ( port , tmp , & pcie - > ports , list ) {
irq_set_chained_handler_and_data ( port - > irq , NULL , NULL ) ;
if ( port - > irq_domain )
irq_domain_remove ( port - > irq_domain ) ;
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
if ( port - > msi_domain )
irq_domain_remove ( port - > msi_domain ) ;
if ( port - > inner_domain )
irq_domain_remove ( port - > inner_domain ) ;
}
irq_dispose_mapping ( port - > irq ) ;
}
}
2017-08-10 14:34:59 +08:00
static int mtk_pcie_intx_map ( struct irq_domain * domain , unsigned int irq ,
irq_hw_number_t hwirq )
{
irq_set_chip_and_handler ( irq , & dummy_irq_chip , handle_simple_irq ) ;
irq_set_chip_data ( irq , domain - > host_data ) ;
return 0 ;
}
static const struct irq_domain_ops intx_domain_ops = {
. map = mtk_pcie_intx_map ,
} ;
static int mtk_pcie_init_irq_domain ( struct mtk_pcie_port * port ,
struct device_node * node )
{
struct device * dev = port - > pcie - > dev ;
struct device_node * pcie_intc_node ;
2018-05-04 13:47:33 +08:00
int ret ;
2017-08-10 14:34:59 +08:00
/* Setup INTx */
pcie_intc_node = of_get_next_child ( node , NULL ) ;
if ( ! pcie_intc_node ) {
dev_err ( dev , " no PCIe Intc node found \n " ) ;
return - ENODEV ;
}
2017-08-30 09:19:14 +08:00
port - > irq_domain = irq_domain_add_linear ( pcie_intc_node , PCI_NUM_INTX ,
2017-08-10 14:34:59 +08:00
& intx_domain_ops , port ) ;
2019-02-27 12:40:42 +08:00
of_node_put ( pcie_intc_node ) ;
2017-08-10 14:34:59 +08:00
if ( ! port - > irq_domain ) {
dev_err ( dev , " failed to get INTx IRQ domain \n " ) ;
return - ENODEV ;
}
2017-08-14 21:04:28 +08:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
2018-05-04 13:47:33 +08:00
ret = mtk_pcie_allocate_msi_domains ( port ) ;
if ( ret )
return ret ;
2017-08-14 21:04:28 +08:00
}
2017-08-10 14:34:59 +08:00
return 0 ;
}
2018-05-04 13:47:33 +08:00
static void mtk_pcie_intr_handler ( struct irq_desc * desc )
2017-08-10 14:34:59 +08:00
{
2018-05-04 13:47:33 +08:00
struct mtk_pcie_port * port = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * irqchip = irq_desc_get_chip ( desc ) ;
2017-08-10 14:34:59 +08:00
unsigned long status ;
u32 virq ;
u32 bit = INTX_SHIFT ;
2018-05-04 13:47:33 +08:00
chained_irq_enter ( irqchip , desc ) ;
status = readl ( port - > base + PCIE_INT_STATUS ) ;
if ( status & INTX_MASK ) {
2017-08-30 09:19:14 +08:00
for_each_set_bit_from ( bit , & status , PCI_NUM_INTX + INTX_SHIFT ) {
2017-08-10 14:34:59 +08:00
/* Clear the INTx */
writel ( 1 < < bit , port - > base + PCIE_INT_STATUS ) ;
virq = irq_find_mapping ( port - > irq_domain ,
bit - INTX_SHIFT ) ;
generic_handle_irq ( virq ) ;
}
}
2017-08-14 21:04:28 +08:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
2018-05-04 13:47:33 +08:00
if ( status & MSI_STATUS ) {
2017-08-14 21:04:28 +08:00
unsigned long imsi_status ;
while ( ( imsi_status = readl ( port - > base + PCIE_IMSI_STATUS ) ) ) {
for_each_set_bit ( bit , & imsi_status , MTK_MSI_IRQS_NUM ) {
2018-05-04 13:47:33 +08:00
virq = irq_find_mapping ( port - > inner_domain , bit ) ;
2017-08-14 21:04:28 +08:00
generic_handle_irq ( virq ) ;
}
}
/* Clear MSI interrupt status */
writel ( MSI_STATUS , port - > base + PCIE_INT_STATUS ) ;
}
}
2018-05-04 13:47:33 +08:00
chained_irq_exit ( irqchip , desc ) ;
2017-08-10 14:34:59 +08:00
}
static int mtk_pcie_setup_irq ( struct mtk_pcie_port * port ,
struct device_node * node )
{
struct mtk_pcie * pcie = port - > pcie ;
struct device * dev = pcie - > dev ;
struct platform_device * pdev = to_platform_device ( dev ) ;
2018-10-15 16:08:59 +08:00
int err ;
2017-08-10 14:34:59 +08:00
err = mtk_pcie_init_irq_domain ( port , node ) ;
if ( err ) {
2017-08-14 21:04:28 +08:00
dev_err ( dev , " failed to init PCIe IRQ domain \n " ) ;
2017-08-10 14:34:59 +08:00
return err ;
}
2018-10-15 16:08:59 +08:00
port - > irq = platform_get_irq ( pdev , port - > slot ) ;
2020-03-12 00:49:02 +05:30
if ( port - > irq < 0 )
return port - > irq ;
2018-10-15 16:08:59 +08:00
irq_set_chained_handler_and_data ( port - > irq ,
mtk_pcie_intr_handler , port ) ;
2018-05-04 13:47:33 +08:00
2017-08-10 14:34:59 +08:00
return 0 ;
}
2018-10-15 16:08:56 +08:00
static int mtk_pcie_startup_port_v2 ( struct mtk_pcie_port * port )
{
struct mtk_pcie * pcie = port - > pcie ;
2019-10-28 11:32:39 -05:00
struct pci_host_bridge * host = pci_host_bridge_from_priv ( pcie ) ;
struct resource * mem = NULL ;
struct resource_entry * entry ;
2018-10-15 16:08:56 +08:00
const struct mtk_pcie_soc * soc = port - > pcie - > soc ;
u32 val ;
int err ;
2019-10-28 11:32:39 -05:00
entry = resource_list_first_type ( & host - > windows , IORESOURCE_MEM ) ;
if ( entry )
mem = entry - > res ;
if ( ! mem )
return - EINVAL ;
2018-10-15 16:08:56 +08:00
/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
if ( pcie - > base ) {
val = readl ( pcie - > base + PCIE_SYS_CFG_V2 ) ;
val | = PCIE_CSR_LTSSM_EN ( port - > slot ) |
PCIE_CSR_ASPM_L1_EN ( port - > slot ) ;
writel ( val , pcie - > base + PCIE_SYS_CFG_V2 ) ;
}
/* Assert all reset signals */
writel ( 0 , port - > base + PCIE_RST_CTRL ) ;
/*
* Enable PCIe link down reset , if link status changed from link up to
* link down , this will reset MAC control registers and configuration
* space .
*/
writel ( PCIE_LINKDOWN_RST_EN , port - > base + PCIE_RST_CTRL ) ;
/* De-assert PHY, PE, PIPE, MAC and configuration reset */
val = readl ( port - > base + PCIE_RST_CTRL ) ;
val | = PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
PCIE_MAC_SRSTB | PCIE_CRSTB ;
writel ( val , port - > base + PCIE_RST_CTRL ) ;
/* Set up vendor ID and class code */
if ( soc - > need_fix_class_id ) {
val = PCI_VENDOR_ID_MEDIATEK ;
writew ( val , port - > base + PCIE_CONF_VEND_ID ) ;
val = PCI_CLASS_BRIDGE_PCI ;
writew ( val , port - > base + PCIE_CONF_CLASS_ID ) ;
}
2019-06-28 15:34:25 +08:00
if ( soc - > need_fix_device_id )
writew ( soc - > device_id , port - > base + PCIE_CONF_DEVICE_ID ) ;
2018-10-15 16:08:56 +08:00
/* 100ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout ( port - > base + PCIE_LINK_STATUS_V2 , val ,
! ! ( val & PCIE_PORT_LINKUP_V2 ) , 20 ,
100 * USEC_PER_MSEC ) ;
if ( err )
return - ETIMEDOUT ;
/* Set INTx mask */
val = readl ( port - > base + PCIE_INT_MASK ) ;
val & = ~ INTX_MASK ;
writel ( val , port - > base + PCIE_INT_MASK ) ;
if ( IS_ENABLED ( CONFIG_PCI_MSI ) )
mtk_pcie_enable_msi ( port ) ;
/* Set AHB to PCIe translation windows */
2019-02-01 13:36:06 +08:00
val = lower_32_bits ( mem - > start ) |
AHB2PCIE_SIZE ( fls ( resource_size ( mem ) ) ) ;
2018-10-15 16:08:56 +08:00
writel ( val , port - > base + PCIE_AHB_TRANS_BASE0_L ) ;
val = upper_32_bits ( mem - > start ) ;
writel ( val , port - > base + PCIE_AHB_TRANS_BASE0_H ) ;
/* Set PCIe to AXI translation memory space.*/
2019-02-01 13:36:07 +08:00
val = PCIE2AHB_SIZE | WIN_ENABLE ;
2018-10-15 16:08:56 +08:00
writel ( val , port - > base + PCIE_AXI_WINDOW0 ) ;
return 0 ;
}
2017-05-21 11:42:24 +08:00
static void __iomem * mtk_pcie_map_bus ( struct pci_bus * bus ,
unsigned int devfn , int where )
{
2017-08-14 21:04:27 +08:00
struct mtk_pcie * pcie = bus - > sysdata ;
2017-05-21 11:42:24 +08:00
writel ( PCIE_CONF_ADDR ( where , PCI_FUNC ( devfn ) , PCI_SLOT ( devfn ) ,
bus - > number ) , pcie - > base + PCIE_CFG_ADDR ) ;
return pcie - > base + PCIE_CFG_DATA + ( where & 3 ) ;
}
static struct pci_ops mtk_pcie_ops = {
. map_bus = mtk_pcie_map_bus ,
. read = pci_generic_config_read ,
. write = pci_generic_config_write ,
} ;
2017-08-10 14:34:54 +08:00
static int mtk_pcie_startup_port ( struct mtk_pcie_port * port )
2017-05-21 11:42:24 +08:00
{
struct mtk_pcie * pcie = port - > pcie ;
2017-08-10 14:34:55 +08:00
u32 func = PCI_FUNC ( port - > slot < < 3 ) ;
u32 slot = PCI_SLOT ( port - > slot < < 3 ) ;
2017-05-21 11:42:24 +08:00
u32 val ;
2017-08-10 14:34:54 +08:00
int err ;
/* assert port PERST_N */
val = readl ( pcie - > base + PCIE_SYS_CFG ) ;
2017-08-10 14:34:55 +08:00
val | = PCIE_PORT_PERST ( port - > slot ) ;
2017-08-10 14:34:54 +08:00
writel ( val , pcie - > base + PCIE_SYS_CFG ) ;
/* de-assert port PERST_N */
val = readl ( pcie - > base + PCIE_SYS_CFG ) ;
2017-08-10 14:34:55 +08:00
val & = ~ PCIE_PORT_PERST ( port - > slot ) ;
2017-08-10 14:34:54 +08:00
writel ( val , pcie - > base + PCIE_SYS_CFG ) ;
/* 100ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout ( port - > base + PCIE_LINK_STATUS , val ,
! ! ( val & PCIE_PORT_LINKUP ) , 20 ,
100 * USEC_PER_MSEC ) ;
if ( err )
return - ETIMEDOUT ;
2017-05-21 11:42:24 +08:00
/* enable interrupt */
val = readl ( pcie - > base + PCIE_INT_ENABLE ) ;
2017-08-10 14:34:55 +08:00
val | = PCIE_PORT_INT_EN ( port - > slot ) ;
2017-05-21 11:42:24 +08:00
writel ( val , pcie - > base + PCIE_INT_ENABLE ) ;
/* map to all DDR region. We need to set it before cfg operation. */
writel ( PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE ,
port - > base + PCIE_BAR0_SETUP ) ;
/* configure class code and revision ID */
writel ( PCIE_CLASS_CODE | PCIE_REVISION_ID , port - > base + PCIE_CLASS ) ;
/* configure FC credit */
writel ( PCIE_CONF_ADDR ( PCIE_FC_CREDIT , func , slot , 0 ) ,
pcie - > base + PCIE_CFG_ADDR ) ;
val = readl ( pcie - > base + PCIE_CFG_DATA ) ;
val & = ~ PCIE_FC_CREDIT_MASK ;
val | = PCIE_FC_CREDIT_VAL ( 0x806c ) ;
writel ( PCIE_CONF_ADDR ( PCIE_FC_CREDIT , func , slot , 0 ) ,
pcie - > base + PCIE_CFG_ADDR ) ;
writel ( val , pcie - > base + PCIE_CFG_DATA ) ;
/* configure RC FTS number to 250 when it leaves L0s */
writel ( PCIE_CONF_ADDR ( PCIE_FTS_NUM , func , slot , 0 ) ,
pcie - > base + PCIE_CFG_ADDR ) ;
val = readl ( pcie - > base + PCIE_CFG_DATA ) ;
val & = ~ PCIE_FTS_NUM_MASK ;
val | = PCIE_FTS_NUM_L0 ( 0x50 ) ;
writel ( PCIE_CONF_ADDR ( PCIE_FTS_NUM , func , slot , 0 ) ,
pcie - > base + PCIE_CFG_ADDR ) ;
writel ( val , pcie - > base + PCIE_CFG_DATA ) ;
2017-08-10 14:34:54 +08:00
return 0 ;
2017-05-21 11:42:24 +08:00
}
2017-08-10 14:34:55 +08:00
static void mtk_pcie_enable_port ( struct mtk_pcie_port * port )
2017-05-21 11:42:24 +08:00
{
2017-08-10 14:34:56 +08:00
struct mtk_pcie * pcie = port - > pcie ;
struct device * dev = pcie - > dev ;
2017-05-21 11:42:24 +08:00
int err ;
err = clk_prepare_enable ( port - > sys_ck ) ;
if ( err ) {
2017-08-10 14:34:59 +08:00
dev_err ( dev , " failed to enable sys_ck%d clock \n " , port - > slot ) ;
2017-05-21 11:42:24 +08:00
goto err_sys_clk ;
}
2017-08-10 14:34:59 +08:00
err = clk_prepare_enable ( port - > ahb_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable ahb_ck%d \n " , port - > slot ) ;
goto err_ahb_clk ;
}
err = clk_prepare_enable ( port - > aux_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable aux_ck%d \n " , port - > slot ) ;
goto err_aux_clk ;
}
err = clk_prepare_enable ( port - > axi_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable axi_ck%d \n " , port - > slot ) ;
goto err_axi_clk ;
}
err = clk_prepare_enable ( port - > obff_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable obff_ck%d \n " , port - > slot ) ;
goto err_obff_clk ;
}
err = clk_prepare_enable ( port - > pipe_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable pipe_ck%d \n " , port - > slot ) ;
goto err_pipe_clk ;
}
2017-05-21 11:42:24 +08:00
reset_control_assert ( port - > reset ) ;
reset_control_deassert ( port - > reset ) ;
2017-08-10 14:34:59 +08:00
err = phy_init ( port - > phy ) ;
if ( err ) {
dev_err ( dev , " failed to initialize port%d phy \n " , port - > slot ) ;
goto err_phy_init ;
}
2017-05-21 11:42:24 +08:00
err = phy_power_on ( port - > phy ) ;
if ( err ) {
2017-08-10 14:34:55 +08:00
dev_err ( dev , " failed to power on port%d phy \n " , port - > slot ) ;
2017-05-21 11:42:24 +08:00
goto err_phy_on ;
}
2017-08-10 14:34:56 +08:00
if ( ! pcie - > soc - > startup ( port ) )
2017-05-21 11:42:24 +08:00
return ;
2017-08-10 14:34:55 +08:00
dev_info ( dev , " Port%d link down \n " , port - > slot ) ;
2017-05-21 11:42:24 +08:00
phy_power_off ( port - > phy ) ;
err_phy_on :
2017-08-10 14:34:59 +08:00
phy_exit ( port - > phy ) ;
err_phy_init :
clk_disable_unprepare ( port - > pipe_ck ) ;
err_pipe_clk :
clk_disable_unprepare ( port - > obff_ck ) ;
err_obff_clk :
clk_disable_unprepare ( port - > axi_ck ) ;
err_axi_clk :
clk_disable_unprepare ( port - > aux_ck ) ;
err_aux_clk :
clk_disable_unprepare ( port - > ahb_ck ) ;
err_ahb_clk :
2017-05-21 11:42:24 +08:00
clk_disable_unprepare ( port - > sys_ck ) ;
err_sys_clk :
mtk_pcie_port_free ( port ) ;
}
2017-08-10 14:34:55 +08:00
static int mtk_pcie_parse_port ( struct mtk_pcie * pcie ,
struct device_node * node ,
int slot )
2017-05-21 11:42:24 +08:00
{
struct mtk_pcie_port * port ;
struct device * dev = pcie - > dev ;
struct platform_device * pdev = to_platform_device ( dev ) ;
char name [ 10 ] ;
int err ;
port = devm_kzalloc ( dev , sizeof ( * port ) , GFP_KERNEL ) ;
if ( ! port )
return - ENOMEM ;
2017-08-10 14:34:57 +08:00
snprintf ( name , sizeof ( name ) , " port%d " , slot ) ;
2020-06-03 01:16:01 +08:00
port - > base = devm_platform_ioremap_resource_byname ( pdev , name ) ;
2017-05-21 11:42:24 +08:00
if ( IS_ERR ( port - > base ) ) {
2017-08-10 14:34:55 +08:00
dev_err ( dev , " failed to map port%d base \n " , slot ) ;
2017-05-21 11:42:24 +08:00
return PTR_ERR ( port - > base ) ;
}
2017-08-10 14:34:55 +08:00
snprintf ( name , sizeof ( name ) , " sys_ck%d " , slot ) ;
2017-05-21 11:42:24 +08:00
port - > sys_ck = devm_clk_get ( dev , name ) ;
if ( IS_ERR ( port - > sys_ck ) ) {
2017-08-10 14:34:59 +08:00
dev_err ( dev , " failed to get sys_ck%d clock \n " , slot ) ;
2017-05-21 11:42:24 +08:00
return PTR_ERR ( port - > sys_ck ) ;
}
2017-08-10 14:34:59 +08:00
/* sys_ck might be divided into the following parts in some chips */
snprintf ( name , sizeof ( name ) , " ahb_ck%d " , slot ) ;
2019-04-10 14:54:16 +08:00
port - > ahb_ck = devm_clk_get_optional ( dev , name ) ;
if ( IS_ERR ( port - > ahb_ck ) )
return PTR_ERR ( port - > ahb_ck ) ;
2017-08-10 14:34:59 +08:00
snprintf ( name , sizeof ( name ) , " axi_ck%d " , slot ) ;
2019-04-10 14:54:16 +08:00
port - > axi_ck = devm_clk_get_optional ( dev , name ) ;
if ( IS_ERR ( port - > axi_ck ) )
return PTR_ERR ( port - > axi_ck ) ;
2017-08-10 14:34:59 +08:00
snprintf ( name , sizeof ( name ) , " aux_ck%d " , slot ) ;
2019-04-10 14:54:16 +08:00
port - > aux_ck = devm_clk_get_optional ( dev , name ) ;
if ( IS_ERR ( port - > aux_ck ) )
return PTR_ERR ( port - > aux_ck ) ;
2017-08-10 14:34:59 +08:00
snprintf ( name , sizeof ( name ) , " obff_ck%d " , slot ) ;
2019-04-10 14:54:16 +08:00
port - > obff_ck = devm_clk_get_optional ( dev , name ) ;
if ( IS_ERR ( port - > obff_ck ) )
return PTR_ERR ( port - > obff_ck ) ;
2017-08-10 14:34:59 +08:00
snprintf ( name , sizeof ( name ) , " pipe_ck%d " , slot ) ;
2019-04-10 14:54:16 +08:00
port - > pipe_ck = devm_clk_get_optional ( dev , name ) ;
if ( IS_ERR ( port - > pipe_ck ) )
return PTR_ERR ( port - > pipe_ck ) ;
2017-08-10 14:34:59 +08:00
2017-08-10 14:34:55 +08:00
snprintf ( name , sizeof ( name ) , " pcie-rst%d " , slot ) ;
2017-07-19 17:26:00 +02:00
port - > reset = devm_reset_control_get_optional_exclusive ( dev , name ) ;
2017-05-21 11:42:24 +08:00
if ( PTR_ERR ( port - > reset ) = = - EPROBE_DEFER )
return PTR_ERR ( port - > reset ) ;
/* some platforms may use default PHY setting */
2017-08-10 14:34:55 +08:00
snprintf ( name , sizeof ( name ) , " pcie-phy%d " , slot ) ;
2017-05-21 11:42:24 +08:00
port - > phy = devm_phy_optional_get ( dev , name ) ;
if ( IS_ERR ( port - > phy ) )
return PTR_ERR ( port - > phy ) ;
2017-08-10 14:34:55 +08:00
port - > slot = slot ;
2017-05-21 11:42:24 +08:00
port - > pcie = pcie ;
2017-08-10 14:34:59 +08:00
if ( pcie - > soc - > setup_irq ) {
err = pcie - > soc - > setup_irq ( port , node ) ;
if ( err )
return err ;
}
2017-05-21 11:42:24 +08:00
INIT_LIST_HEAD ( & port - > list ) ;
list_add_tail ( & port - > list , & pcie - > ports ) ;
return 0 ;
}
static int mtk_pcie_subsys_powerup ( struct mtk_pcie * pcie )
{
struct device * dev = pcie - > dev ;
struct platform_device * pdev = to_platform_device ( dev ) ;
struct resource * regs ;
int err ;
2017-08-10 14:34:57 +08:00
/* get shared registers, which are optional */
regs = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " subsys " ) ;
if ( regs ) {
pcie - > base = devm_ioremap_resource ( dev , regs ) ;
if ( IS_ERR ( pcie - > base ) ) {
dev_err ( dev , " failed to map shared register \n " ) ;
return PTR_ERR ( pcie - > base ) ;
}
2017-05-21 11:42:24 +08:00
}
pcie - > free_ck = devm_clk_get ( dev , " free_ck " ) ;
if ( IS_ERR ( pcie - > free_ck ) ) {
if ( PTR_ERR ( pcie - > free_ck ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
pcie - > free_ck = NULL ;
}
2018-10-15 16:08:54 +08:00
pm_runtime_enable ( dev ) ;
pm_runtime_get_sync ( dev ) ;
2017-05-21 11:42:24 +08:00
/* enable top level clock */
err = clk_prepare_enable ( pcie - > free_ck ) ;
if ( err ) {
dev_err ( dev , " failed to enable free_ck \n " ) ;
goto err_free_ck ;
}
return 0 ;
err_free_ck :
2018-10-15 16:08:54 +08:00
pm_runtime_put_sync ( dev ) ;
pm_runtime_disable ( dev ) ;
2017-05-21 11:42:24 +08:00
return err ;
}
static int mtk_pcie_setup ( struct mtk_pcie * pcie )
{
struct device * dev = pcie - > dev ;
struct device_node * node = dev - > of_node , * child ;
struct mtk_pcie_port * port , * tmp ;
int err ;
for_each_available_child_of_node ( node , child ) {
2017-08-10 14:34:55 +08:00
int slot ;
2017-05-21 11:42:24 +08:00
err = of_pci_get_devfn ( child ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to parse devfn: %d \n " , err ) ;
return err ;
}
2017-08-10 14:34:55 +08:00
slot = PCI_SLOT ( err ) ;
2017-05-21 11:42:24 +08:00
2017-08-10 14:34:55 +08:00
err = mtk_pcie_parse_port ( pcie , child , slot ) ;
2017-05-21 11:42:24 +08:00
if ( err )
return err ;
}
err = mtk_pcie_subsys_powerup ( pcie ) ;
if ( err )
return err ;
/* enable each port, and then check link status */
list_for_each_entry_safe ( port , tmp , & pcie - > ports , list )
2017-08-10 14:34:55 +08:00
mtk_pcie_enable_port ( port ) ;
2017-05-21 11:42:24 +08:00
/* power down PCIe subsys if slots are all empty (link down) */
if ( list_empty ( & pcie - > ports ) )
mtk_pcie_subsys_powerdown ( pcie ) ;
return 0 ;
}
static int mtk_pcie_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mtk_pcie * pcie ;
struct pci_host_bridge * host ;
int err ;
host = devm_pci_alloc_host_bridge ( dev , sizeof ( * pcie ) ) ;
if ( ! host )
return - ENOMEM ;
pcie = pci_host_bridge_priv ( host ) ;
pcie - > dev = dev ;
2017-08-10 14:34:56 +08:00
pcie - > soc = of_device_get_match_data ( dev ) ;
2017-05-21 11:42:24 +08:00
platform_set_drvdata ( pdev , pcie ) ;
INIT_LIST_HEAD ( & pcie - > ports ) ;
err = mtk_pcie_setup ( pcie ) ;
if ( err )
return err ;
2018-10-15 16:08:55 +08:00
host - > ops = pcie - > soc - > ops ;
host - > sysdata = pcie ;
err = pci_host_probe ( host ) ;
2017-05-21 11:42:24 +08:00
if ( err )
goto put_resources ;
return 0 ;
put_resources :
if ( ! list_empty ( & pcie - > ports ) )
mtk_pcie_put_resources ( pcie ) ;
return err ;
}
2018-10-15 16:08:59 +08:00
static void mtk_pcie_free_resources ( struct mtk_pcie * pcie )
{
struct pci_host_bridge * host = pci_host_bridge_from_priv ( pcie ) ;
struct list_head * windows = & host - > windows ;
pci_free_resource_list ( windows ) ;
}
static int mtk_pcie_remove ( struct platform_device * pdev )
{
struct mtk_pcie * pcie = platform_get_drvdata ( pdev ) ;
struct pci_host_bridge * host = pci_host_bridge_from_priv ( pcie ) ;
pci_stop_root_bus ( host - > bus ) ;
pci_remove_root_bus ( host - > bus ) ;
mtk_pcie_free_resources ( pcie ) ;
mtk_pcie_irq_teardown ( pcie ) ;
mtk_pcie_put_resources ( pcie ) ;
return 0 ;
}
2018-10-15 16:08:58 +08:00
static int __maybe_unused mtk_pcie_suspend_noirq ( struct device * dev )
{
struct mtk_pcie * pcie = dev_get_drvdata ( dev ) ;
struct mtk_pcie_port * port ;
if ( list_empty ( & pcie - > ports ) )
return 0 ;
list_for_each_entry ( port , & pcie - > ports , list ) {
clk_disable_unprepare ( port - > pipe_ck ) ;
clk_disable_unprepare ( port - > obff_ck ) ;
clk_disable_unprepare ( port - > axi_ck ) ;
clk_disable_unprepare ( port - > aux_ck ) ;
clk_disable_unprepare ( port - > ahb_ck ) ;
clk_disable_unprepare ( port - > sys_ck ) ;
phy_power_off ( port - > phy ) ;
phy_exit ( port - > phy ) ;
}
clk_disable_unprepare ( pcie - > free_ck ) ;
return 0 ;
}
static int __maybe_unused mtk_pcie_resume_noirq ( struct device * dev )
{
struct mtk_pcie * pcie = dev_get_drvdata ( dev ) ;
struct mtk_pcie_port * port , * tmp ;
if ( list_empty ( & pcie - > ports ) )
return 0 ;
clk_prepare_enable ( pcie - > free_ck ) ;
list_for_each_entry_safe ( port , tmp , & pcie - > ports , list )
mtk_pcie_enable_port ( port ) ;
/* In case of EP was removed while system suspend. */
if ( list_empty ( & pcie - > ports ) )
clk_disable_unprepare ( pcie - > free_ck ) ;
return 0 ;
}
static const struct dev_pm_ops mtk_pcie_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS ( mtk_pcie_suspend_noirq ,
mtk_pcie_resume_noirq )
} ;
2017-08-10 14:34:56 +08:00
static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
. ops = & mtk_pcie_ops ,
. startup = mtk_pcie_startup_port ,
} ;
2018-05-04 13:47:32 +08:00
static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
. ops = & mtk_pcie_ops_v2 ,
. startup = mtk_pcie_startup_port_v2 ,
. setup_irq = mtk_pcie_setup_irq ,
} ;
static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = {
. need_fix_class_id = true ,
2017-08-10 14:34:59 +08:00
. ops = & mtk_pcie_ops_v2 ,
. startup = mtk_pcie_startup_port_v2 ,
. setup_irq = mtk_pcie_setup_irq ,
} ;
2019-06-28 15:34:25 +08:00
static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
. need_fix_class_id = true ,
. need_fix_device_id = true ,
. device_id = PCI_DEVICE_ID_MEDIATEK_7629 ,
. ops = & mtk_pcie_ops_v2 ,
. startup = mtk_pcie_startup_port_v2 ,
. setup_irq = mtk_pcie_setup_irq ,
} ;
2017-05-21 11:42:24 +08:00
static const struct of_device_id mtk_pcie_ids [ ] = {
2017-08-10 14:34:56 +08:00
{ . compatible = " mediatek,mt2701-pcie " , . data = & mtk_pcie_soc_v1 } ,
{ . compatible = " mediatek,mt7623-pcie " , . data = & mtk_pcie_soc_v1 } ,
2018-05-04 13:47:32 +08:00
{ . compatible = " mediatek,mt2712-pcie " , . data = & mtk_pcie_soc_mt2712 } ,
{ . compatible = " mediatek,mt7622-pcie " , . data = & mtk_pcie_soc_mt7622 } ,
2019-06-28 15:34:25 +08:00
{ . compatible = " mediatek,mt7629-pcie " , . data = & mtk_pcie_soc_mt7629 } ,
2017-05-21 11:42:24 +08:00
{ } ,
} ;
static struct platform_driver mtk_pcie_driver = {
. probe = mtk_pcie_probe ,
2018-10-15 16:08:59 +08:00
. remove = mtk_pcie_remove ,
2017-05-21 11:42:24 +08:00
. driver = {
. name = " mtk-pcie " ,
. of_match_table = mtk_pcie_ids ,
. suppress_bind_attrs = true ,
2018-10-15 16:08:58 +08:00
. pm = & mtk_pcie_pm_ops ,
2017-05-21 11:42:24 +08:00
} ,
} ;
2018-10-15 16:08:59 +08:00
module_platform_driver ( mtk_pcie_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;