2018-01-26 12:50:27 -06:00
// SPDX-License-Identifier: GPL-2.0
2014-07-22 15:23:45 -06:00
/*
* pcie - dra7xx - PCIe controller driver for TI DRA7xx SoCs
*
* Copyright ( C ) 2013 - 2014 Texas Instruments Incorporated - http : //www.ti.com
*
* Authors : Kishon Vijay Abraham I < kishon @ ti . com >
*/
2017-03-27 15:15:08 +05:30
# include <linux/delay.h>
2017-10-09 14:33:37 +05:30
# include <linux/device.h>
2014-07-22 15:23:45 -06:00
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/irqdomain.h>
# include <linux/kernel.h>
2016-08-24 16:57:47 -04:00
# include <linux/init.h>
2017-03-27 15:15:08 +05:30
# include <linux/of_device.h>
2015-07-28 19:09:09 +05:30
# include <linux/of_gpio.h>
2017-01-11 17:36:53 +05:30
# include <linux/of_pci.h>
2014-07-22 15:23:45 -06:00
# include <linux/pci.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/resource.h>
# include <linux/types.h>
2017-03-27 15:15:11 +05:30
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2014-07-22 15:23:45 -06:00
# include "pcie-designware.h"
/* PCIe controller wrapper DRA7XX configuration registers */
# define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024
# define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028
# define ERR_SYS BIT(0)
# define ERR_FATAL BIT(1)
# define ERR_NONFATAL BIT(2)
# define ERR_COR BIT(3)
# define ERR_AXI BIT(4)
# define ERR_ECRC BIT(5)
# define PME_TURN_OFF BIT(8)
# define PME_TO_ACK BIT(9)
# define PM_PME BIT(10)
# define LINK_REQ_RST BIT(11)
# define LINK_UP_EVT BIT(12)
# define CFG_BME_EVT BIT(13)
# define CFG_MSE_EVT BIT(14)
# define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \
ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \
LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT )
# define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034
# define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038
# define INTA BIT(0)
# define INTB BIT(1)
# define INTC BIT(2)
# define INTD BIT(3)
# define MSI BIT(4)
# define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
2017-03-27 15:15:08 +05:30
# define PCIECTRL_TI_CONF_DEVICE_TYPE 0x0100
# define DEVICE_TYPE_EP 0x0
# define DEVICE_TYPE_LEG_EP 0x1
# define DEVICE_TYPE_RC 0x4
2014-07-22 15:23:45 -06:00
# define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104
# define LTSSM_EN 0x1
# define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C
# define LINK_UP BIT(16)
2015-10-29 19:56:51 -05:00
# define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF
2014-07-22 15:23:45 -06:00
2017-01-11 17:36:53 +05:30
# define EXP_CAP_ID_OFFSET 0x70
2017-03-27 15:15:08 +05:30
# define PCIECTRL_TI_CONF_INTX_ASSERT 0x0124
# define PCIECTRL_TI_CONF_INTX_DEASSERT 0x0128
# define PCIECTRL_TI_CONF_MSI_XMT 0x012c
# define MSI_REQ_GRANT BIT(0)
# define MSI_VECTOR_SHIFT 7
2014-07-22 15:23:45 -06:00
struct dra7xx_pcie {
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
2016-10-06 13:33:06 -05:00
void __iomem * base ; /* DT ti_conf */
int phy_count ; /* DT phy-names count */
struct phy * * phy ;
2017-01-11 17:36:53 +05:30
int link_gen ;
2017-01-11 17:36:54 +05:30
struct irq_domain * irq_domain ;
2017-03-27 15:15:08 +05:30
enum dw_pcie_device_mode mode ;
} ;
struct dra7xx_pcie_of_data {
enum dw_pcie_device_mode mode ;
2014-07-22 15:23:45 -06:00
} ;
2017-02-15 18:48:14 +05:30
# define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev)
2014-07-22 15:23:45 -06:00
static inline u32 dra7xx_pcie_readl ( struct dra7xx_pcie * pcie , u32 offset )
{
return readl ( pcie - > base + offset ) ;
}
static inline void dra7xx_pcie_writel ( struct dra7xx_pcie * pcie , u32 offset ,
u32 value )
{
writel ( value , pcie - > base + offset ) ;
}
PCI: dwc: Make cpu_addr_fixup take struct dw_pcie as argument
The current cpu addr fixup mask for ARTPEC-6, GENMASK(27, 0), is wrong.
The correct cpu addr fixup mask for ARTPEC-6 is GENMASK(28, 0).
However, having a hardcoded cpu addr fixup mask in each driver is
arguably wrong.
A device tree property called something like "cpu-addr-fixup-mask"
would have been a better solution.
Introducing such a property is not needed though, since we already have
pp->cfg0_base and ep->phys_base, which is derived from already existing
device tree properties.
It is also worth noting that for ARTPEC-7, hardcoding the cpu addr fixup
mask is not possible, since it uses a High Address Bits Look Up Table,
which means that it can, at runtime, map the PCIe window to an arbitrary
address in the 32-bit address space.
By using pp->cfg0_base and ep->phys_base, we avoid hardcoding a mask
in each driver. This should work for ARTPEC-6, DRA7xx, and ARTPEC-7.
I have not changed the code in DRA7xx though, since their existing
code works, but if they want, they could use the same logic as
artpec6_pcie_cpu_addr_fixup, and thus remove their hardcoded mask.
The reason why the fixup mask is needed is explained in commit f4c55c5a3f7f
("PCI: designware: Program ATU with untranslated address").
Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
2017-12-20 00:29:36 +01:00
static u64 dra7xx_pcie_cpu_addr_fixup ( struct dw_pcie * pci , u64 pci_addr )
2017-03-13 19:13:23 +05:30
{
return pci_addr & DRA7XX_CPU_TO_BUS_ADDR ;
}
2017-02-15 18:48:14 +05:30
static int dra7xx_pcie_link_up ( struct dw_pcie * pci )
2014-07-22 15:23:45 -06:00
{
2017-02-15 18:48:14 +05:30
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
2014-07-22 15:23:45 -06:00
u32 reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_PHY_CS ) ;
return ! ! ( reg & LINK_UP ) ;
}
2017-03-27 15:15:08 +05:30
static void dra7xx_pcie_stop_link ( struct dw_pcie * pci )
2014-07-22 15:23:45 -06:00
{
2017-03-27 15:15:08 +05:30
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
u32 reg ;
reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD ) ;
reg & = ~ LTSSM_EN ;
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD , reg ) ;
}
static int dra7xx_pcie_establish_link ( struct dw_pcie * pci )
{
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
2017-02-15 18:48:14 +05:30
struct device * dev = pci - > dev ;
2015-06-02 16:47:17 -05:00
u32 reg ;
2017-01-11 17:36:53 +05:30
u32 exp_cap_off = EXP_CAP_ID_OFFSET ;
2014-07-22 15:23:45 -06:00
2017-02-15 18:48:14 +05:30
if ( dw_pcie_link_up ( pci ) ) {
2016-10-06 13:33:06 -05:00
dev_err ( dev , " link is already up \n " ) ;
2014-07-22 15:23:45 -06:00
return 0 ;
}
2017-01-11 17:36:53 +05:30
if ( dra7xx - > link_gen = = 1 ) {
2017-02-15 18:48:14 +05:30
dw_pcie_read ( pci - > dbi_base + exp_cap_off + PCI_EXP_LNKCAP ,
2017-02-15 18:48:12 +05:30
4 , & reg ) ;
2017-01-11 17:36:53 +05:30
if ( ( reg & PCI_EXP_LNKCAP_SLS ) ! = PCI_EXP_LNKCAP_SLS_2_5GB ) {
reg & = ~ ( ( u32 ) PCI_EXP_LNKCAP_SLS ) ;
reg | = PCI_EXP_LNKCAP_SLS_2_5GB ;
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + exp_cap_off +
2017-02-15 18:48:12 +05:30
PCI_EXP_LNKCAP , 4 , reg ) ;
2017-01-11 17:36:53 +05:30
}
2017-02-15 18:48:14 +05:30
dw_pcie_read ( pci - > dbi_base + exp_cap_off + PCI_EXP_LNKCTL2 ,
2017-02-15 18:48:12 +05:30
2 , & reg ) ;
2017-01-11 17:36:53 +05:30
if ( ( reg & PCI_EXP_LNKCAP_SLS ) ! = PCI_EXP_LNKCAP_SLS_2_5GB ) {
reg & = ~ ( ( u32 ) PCI_EXP_LNKCAP_SLS ) ;
reg | = PCI_EXP_LNKCAP_SLS_2_5GB ;
2017-02-15 18:48:14 +05:30
dw_pcie_write ( pci - > dbi_base + exp_cap_off +
2017-02-15 18:48:12 +05:30
PCI_EXP_LNKCTL2 , 2 , reg ) ;
2017-01-11 17:36:53 +05:30
}
}
2014-07-22 15:23:45 -06:00
reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD ) ;
reg | = LTSSM_EN ;
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD , reg ) ;
2017-03-27 15:15:08 +05:30
return 0 ;
2014-07-22 15:23:45 -06:00
}
2017-03-27 15:15:07 +05:30
static void dra7xx_pcie_enable_msi_interrupts ( struct dra7xx_pcie * dra7xx )
2014-07-22 15:23:45 -06:00
{
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI ,
2017-06-19 15:21:36 +05:30
LEG_EP_INTERRUPTS | MSI ) ;
2017-03-27 15:15:07 +05:30
dra7xx_pcie_writel ( dra7xx ,
PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI ,
2017-01-11 17:36:54 +05:30
MSI | LEG_EP_INTERRUPTS ) ;
2014-07-22 15:23:45 -06:00
}
2017-03-27 15:15:07 +05:30
static void dra7xx_pcie_enable_wrapper_interrupts ( struct dra7xx_pcie * dra7xx )
{
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN ,
2017-06-19 15:21:36 +05:30
INTERRUPTS ) ;
2017-03-27 15:15:07 +05:30
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN ,
INTERRUPTS ) ;
}
static void dra7xx_pcie_enable_interrupts ( struct dra7xx_pcie * dra7xx )
{
dra7xx_pcie_enable_wrapper_interrupts ( dra7xx ) ;
dra7xx_pcie_enable_msi_interrupts ( dra7xx ) ;
}
2017-07-15 23:39:45 -07:00
static int dra7xx_pcie_host_init ( struct pcie_port * pp )
2014-07-22 15:23:45 -06:00
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
2016-10-06 13:33:05 -05:00
2016-03-16 19:40:33 +08:00
dw_pcie_setup_rc ( pp ) ;
2017-03-27 15:15:08 +05:30
dra7xx_pcie_establish_link ( pci ) ;
dw_pcie_wait_for_link ( pci ) ;
2017-01-11 17:36:54 +05:30
dw_pcie_msi_init ( pp ) ;
2016-10-06 13:33:05 -05:00
dra7xx_pcie_enable_interrupts ( dra7xx ) ;
2017-07-15 23:39:45 -07:00
return 0 ;
2014-07-22 15:23:45 -06:00
}
2017-06-05 16:53:46 +08:00
static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = {
2014-07-22 15:23:45 -06:00
. host_init = dra7xx_pcie_host_init ,
} ;
static int dra7xx_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 = dra7xx_pcie_intx_map ,
2017-12-29 17:11:30 +05:30
. xlate = pci_irqd_intx_xlate ,
2014-07-22 15:23:45 -06:00
} ;
static int dra7xx_pcie_init_irq_domain ( struct pcie_port * pp )
{
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = to_dw_pcie_from_pp ( pp ) ;
struct device * dev = pci - > dev ;
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
2014-07-22 15:23:45 -06:00
struct device_node * node = dev - > of_node ;
struct device_node * pcie_intc_node = of_get_next_child ( node , NULL ) ;
if ( ! pcie_intc_node ) {
dev_err ( dev , " No PCIe Intc node found \n " ) ;
2016-07-14 23:18:27 +02:00
return - ENODEV ;
2014-07-22 15:23:45 -06:00
}
2017-08-15 16:28:27 -05:00
dra7xx - > irq_domain = irq_domain_add_linear ( pcie_intc_node , PCI_NUM_INTX ,
2017-01-11 17:36:54 +05:30
& intx_domain_ops , pp ) ;
if ( ! dra7xx - > irq_domain ) {
2014-07-22 15:23:45 -06:00
dev_err ( dev , " Failed to get a INTx IRQ domain \n " ) ;
2016-07-14 23:18:27 +02:00
return - ENODEV ;
2014-07-22 15:23:45 -06:00
}
return 0 ;
}
static irqreturn_t dra7xx_pcie_msi_irq_handler ( int irq , void * arg )
{
2016-10-06 13:33:05 -05:00
struct dra7xx_pcie * dra7xx = arg ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = dra7xx - > pci ;
struct pcie_port * pp = & pci - > pp ;
2017-12-29 17:11:31 +05:30
unsigned long reg ;
u32 virq , bit ;
2014-07-22 15:23:45 -06:00
reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI ) ;
switch ( reg ) {
case MSI :
dw_handle_msi_irq ( pp ) ;
break ;
case INTA :
case INTB :
case INTC :
case INTD :
2017-12-29 17:11:31 +05:30
for_each_set_bit ( bit , & reg , PCI_NUM_INTX ) {
virq = irq_find_mapping ( dra7xx - > irq_domain , bit ) ;
if ( virq )
generic_handle_irq ( virq ) ;
}
2014-07-22 15:23:45 -06:00
break ;
}
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI , reg ) ;
return IRQ_HANDLED ;
}
static irqreturn_t dra7xx_pcie_irq_handler ( int irq , void * arg )
{
struct dra7xx_pcie * dra7xx = arg ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = dra7xx - > pci ;
struct device * dev = pci - > dev ;
2017-03-27 15:15:08 +05:30
struct dw_pcie_ep * ep = & pci - > ep ;
2014-07-22 15:23:45 -06:00
u32 reg ;
reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN ) ;
if ( reg & ERR_SYS )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " System Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & ERR_FATAL )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " Fatal Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & ERR_NONFATAL )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " Non Fatal Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & ERR_COR )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " Correctable Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & ERR_AXI )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " AXI tag lookup fatal Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & ERR_ECRC )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " ECRC Error \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & PME_TURN_OFF )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev ,
2014-07-22 15:23:45 -06:00
" Power Management Event Turn-Off message received \n " ) ;
if ( reg & PME_TO_ACK )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev ,
2014-07-22 15:23:45 -06:00
" Power Management Turn-Off Ack message received \n " ) ;
if ( reg & PM_PME )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " PM Power Management Event message received \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & LINK_REQ_RST )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " Link Request Reset \n " ) ;
2014-07-22 15:23:45 -06:00
2017-03-27 15:15:08 +05:30
if ( reg & LINK_UP_EVT ) {
if ( dra7xx - > mode = = DW_PCIE_EP_TYPE )
dw_pcie_ep_linkup ( ep ) ;
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " Link-up state change \n " ) ;
2017-03-27 15:15:08 +05:30
}
2014-07-22 15:23:45 -06:00
if ( reg & CFG_BME_EVT )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " CFG 'Bus Master Enable' change \n " ) ;
2014-07-22 15:23:45 -06:00
if ( reg & CFG_MSE_EVT )
2016-10-06 13:33:06 -05:00
dev_dbg ( dev , " CFG 'Memory Space Enable' change \n " ) ;
2014-07-22 15:23:45 -06:00
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN , reg ) ;
return IRQ_HANDLED ;
}
2017-03-27 15:15:08 +05:30
static void dra7xx_pcie_ep_init ( struct dw_pcie_ep * ep )
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
2017-08-18 20:28:03 +05:30
enum pci_barno bar ;
for ( bar = BAR_0 ; bar < = BAR_5 ; bar + + )
dw_pcie_ep_reset_bar ( pci , bar ) ;
2017-03-27 15:15:08 +05:30
dra7xx_pcie_enable_wrapper_interrupts ( dra7xx ) ;
}
static void dra7xx_pcie_raise_legacy_irq ( struct dra7xx_pcie * dra7xx )
{
dra7xx_pcie_writel ( dra7xx , PCIECTRL_TI_CONF_INTX_ASSERT , 0x1 ) ;
mdelay ( 1 ) ;
dra7xx_pcie_writel ( dra7xx , PCIECTRL_TI_CONF_INTX_DEASSERT , 0x1 ) ;
}
static void dra7xx_pcie_raise_msi_irq ( struct dra7xx_pcie * dra7xx ,
u8 interrupt_num )
{
u32 reg ;
reg = ( interrupt_num - 1 ) < < MSI_VECTOR_SHIFT ;
reg | = MSI_REQ_GRANT ;
dra7xx_pcie_writel ( dra7xx , PCIECTRL_TI_CONF_MSI_XMT , reg ) ;
}
2018-02-01 11:36:07 -06:00
static int dra7xx_pcie_raise_irq ( struct dw_pcie_ep * ep , u8 func_no ,
2017-03-27 15:15:08 +05:30
enum pci_epc_irq_type type , u8 interrupt_num )
{
struct dw_pcie * pci = to_dw_pcie_from_ep ( ep ) ;
struct dra7xx_pcie * dra7xx = to_dra7xx_pcie ( pci ) ;
switch ( type ) {
case PCI_EPC_IRQ_LEGACY :
dra7xx_pcie_raise_legacy_irq ( dra7xx ) ;
break ;
case PCI_EPC_IRQ_MSI :
dra7xx_pcie_raise_msi_irq ( dra7xx , interrupt_num ) ;
break ;
default :
dev_err ( pci - > dev , " UNKNOWN IRQ type \n " ) ;
}
return 0 ;
}
static struct dw_pcie_ep_ops pcie_ep_ops = {
. ep_init = dra7xx_pcie_ep_init ,
. raise_irq = dra7xx_pcie_raise_irq ,
} ;
static int __init dra7xx_add_pcie_ep ( struct dra7xx_pcie * dra7xx ,
struct platform_device * pdev )
{
int ret ;
struct dw_pcie_ep * ep ;
struct resource * res ;
struct device * dev = & pdev - > dev ;
struct dw_pcie * pci = dra7xx - > pci ;
ep = & pci - > ep ;
ep - > ops = & pcie_ep_ops ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ep_dbics " ) ;
pci - > dbi_base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! pci - > dbi_base )
return - ENOMEM ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ep_dbics2 " ) ;
pci - > dbi_base2 = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! pci - > dbi_base2 )
return - ENOMEM ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " addr_space " ) ;
if ( ! res )
return - EINVAL ;
ep - > phys_base = res - > start ;
ep - > addr_size = resource_size ( res ) ;
ret = dw_pcie_ep_init ( ep ) ;
if ( ret ) {
dev_err ( dev , " failed to initialize endpoint \n " ) ;
return ret ;
}
return 0 ;
}
2014-11-06 14:37:39 +09:00
static int __init dra7xx_add_pcie_port ( struct dra7xx_pcie * dra7xx ,
struct platform_device * pdev )
2014-07-22 15:23:45 -06:00
{
int ret ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = dra7xx - > pci ;
struct pcie_port * pp = & pci - > pp ;
struct device * dev = pci - > dev ;
2014-07-22 15:23:45 -06:00
struct resource * res ;
pp - > irq = platform_get_irq ( pdev , 1 ) ;
if ( pp - > irq < 0 ) {
dev_err ( dev , " missing IRQ resource \n " ) ;
2017-08-31 14:52:02 -03:00
return pp - > irq ;
2014-07-22 15:23:45 -06:00
}
2016-10-06 13:33:06 -05:00
ret = devm_request_irq ( dev , pp - > irq , dra7xx_pcie_msi_irq_handler ,
2015-12-10 21:18:20 +02:00
IRQF_SHARED | IRQF_NO_THREAD ,
2016-10-06 13:33:05 -05:00
" dra7-pcie-msi " , dra7xx ) ;
2014-07-22 15:23:45 -06:00
if ( ret ) {
2016-10-06 13:33:06 -05:00
dev_err ( dev , " failed to request irq \n " ) ;
2014-07-22 15:23:45 -06:00
return ret ;
}
2017-01-11 17:36:54 +05:30
ret = dra7xx_pcie_init_irq_domain ( pp ) ;
if ( ret < 0 )
return ret ;
2014-07-22 15:23:45 -06:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " rc_dbics " ) ;
2017-02-15 18:48:14 +05:30
pci - > dbi_base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! pci - > dbi_base )
2014-07-22 15:23:45 -06:00
return - ENOMEM ;
2017-12-20 00:29:29 +01:00
pp - > ops = & dra7xx_pcie_host_ops ;
2014-07-22 15:23:45 -06:00
ret = dw_pcie_host_init ( pp ) ;
if ( ret ) {
2016-10-06 13:33:06 -05:00
dev_err ( dev , " failed to initialize host \n " ) ;
2014-07-22 15:23:45 -06: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:23 +05:30
. cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup ,
2017-03-27 15:15:08 +05:30
. start_link = dra7xx_pcie_establish_link ,
. stop_link = dra7xx_pcie_stop_link ,
2017-02-15 18:48:14 +05:30
. link_up = dra7xx_pcie_link_up ,
} ;
2017-01-11 17:36:55 +05:30
static void dra7xx_pcie_disable_phy ( struct dra7xx_pcie * dra7xx )
{
int phy_count = dra7xx - > phy_count ;
while ( phy_count - - ) {
phy_power_off ( dra7xx - > phy [ phy_count ] ) ;
phy_exit ( dra7xx - > phy [ phy_count ] ) ;
}
}
static int dra7xx_pcie_enable_phy ( struct dra7xx_pcie * dra7xx )
{
int phy_count = dra7xx - > phy_count ;
int ret ;
int i ;
for ( i = 0 ; i < phy_count ; i + + ) {
ret = phy_init ( dra7xx - > phy [ i ] ) ;
if ( ret < 0 )
goto err_phy ;
ret = phy_power_on ( dra7xx - > phy [ i ] ) ;
if ( ret < 0 ) {
phy_exit ( dra7xx - > phy [ i ] ) ;
goto err_phy ;
}
}
return 0 ;
err_phy :
while ( - - i > = 0 ) {
phy_power_off ( dra7xx - > phy [ i ] ) ;
phy_exit ( dra7xx - > phy [ i ] ) ;
}
return ret ;
}
2017-03-27 15:15:08 +05:30
static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = {
. mode = DW_PCIE_RC_TYPE ,
} ;
static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
. mode = DW_PCIE_EP_TYPE ,
} ;
static const struct of_device_id of_dra7xx_pcie_match [ ] = {
{
. compatible = " ti,dra7-pcie " ,
. data = & dra7xx_pcie_rc_of_data ,
} ,
{
. compatible = " ti,dra7-pcie-ep " ,
. data = & dra7xx_pcie_ep_of_data ,
} ,
{ } ,
} ;
2017-03-27 15:15:11 +05:30
/*
* dra7xx_pcie_ep_unaligned_memaccess : workaround for AM572x / AM571x Errata i870
* @ dra7xx : the dra7xx device where the workaround should be applied
*
* Access to the PCIe slave port that are not 32 - bit aligned will result
* in incorrect mapping to TLP Address and Byte enable fields . Therefore ,
* byte and half - word accesses are not possible to byte offset 0x1 , 0x2 , or
* 0x3 .
*
* To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
*/
static int dra7xx_pcie_ep_unaligned_memaccess ( struct device * dev )
{
int ret ;
struct device_node * np = dev - > of_node ;
struct of_phandle_args args ;
struct regmap * regmap ;
regmap = syscon_regmap_lookup_by_phandle ( np ,
" ti,syscon-unaligned-access " ) ;
if ( IS_ERR ( regmap ) ) {
dev_dbg ( dev , " can't get ti,syscon-unaligned-access \n " ) ;
return - EINVAL ;
}
ret = of_parse_phandle_with_fixed_args ( np , " ti,syscon-unaligned-access " ,
2 , 0 , & args ) ;
if ( ret ) {
dev_err ( dev , " failed to parse ti,syscon-unaligned-access \n " ) ;
return ret ;
}
ret = regmap_update_bits ( regmap , args . args [ 0 ] , args . args [ 1 ] ,
args . args [ 1 ] ) ;
if ( ret )
dev_err ( dev , " failed to enable unaligned access \n " ) ;
of_node_put ( args . np ) ;
return ret ;
}
2014-07-22 15:23:45 -06:00
static int __init dra7xx_pcie_probe ( struct platform_device * pdev )
{
u32 reg ;
int ret ;
int irq ;
int i ;
int phy_count ;
struct phy * * phy ;
2017-10-09 14:33:37 +05:30
struct device_link * * link ;
2014-07-22 15:23:45 -06:00
void __iomem * base ;
struct resource * res ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci ;
struct dra7xx_pcie * dra7xx ;
2014-07-22 15:23:45 -06:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
char name [ 10 ] ;
2017-01-11 17:36:52 +05:30
struct gpio_desc * reset ;
2017-03-27 15:15:08 +05:30
const struct of_device_id * match ;
const struct dra7xx_pcie_of_data * data ;
enum dw_pcie_device_mode mode ;
match = of_match_device ( of_match_ptr ( of_dra7xx_pcie_match ) , dev ) ;
if ( ! match )
return - EINVAL ;
data = ( struct dra7xx_pcie_of_data * ) match - > data ;
mode = ( enum dw_pcie_device_mode ) data - > mode ;
2014-07-22 15:23:45 -06:00
dra7xx = devm_kzalloc ( dev , sizeof ( * dra7xx ) , GFP_KERNEL ) ;
if ( ! dra7xx )
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 ;
2014-07-22 15:23:45 -06:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
2017-08-09 11:16:03 -05:00
dev_err ( dev , " missing IRQ resource: %d \n " , irq ) ;
return irq ;
2014-07-22 15:23:45 -06:00
}
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ti_conf " ) ;
base = devm_ioremap_nocache ( dev , res - > start , resource_size ( res ) ) ;
if ( ! base )
return - ENOMEM ;
phy_count = of_property_count_strings ( np , " phy-names " ) ;
if ( phy_count < 0 ) {
dev_err ( dev , " unable to find the strings \n " ) ;
return phy_count ;
}
phy = devm_kzalloc ( dev , sizeof ( * phy ) * phy_count , GFP_KERNEL ) ;
if ( ! phy )
return - ENOMEM ;
2017-10-09 14:33:37 +05:30
link = devm_kzalloc ( dev , sizeof ( * link ) * phy_count , GFP_KERNEL ) ;
if ( ! link )
return - ENOMEM ;
2014-07-22 15:23:45 -06:00
for ( i = 0 ; i < phy_count ; i + + ) {
snprintf ( name , sizeof ( name ) , " pcie-phy%d " , i ) ;
phy [ i ] = devm_phy_get ( dev , name ) ;
if ( IS_ERR ( phy [ i ] ) )
return PTR_ERR ( phy [ i ] ) ;
2017-10-09 14:33:37 +05:30
link [ i ] = device_link_add ( dev , & phy [ i ] - > dev , DL_FLAG_STATELESS ) ;
if ( ! link [ i ] ) {
ret = - EINVAL ;
goto err_link ;
}
2014-07-22 15:23:45 -06:00
}
dra7xx - > base = base ;
dra7xx - > phy = phy ;
2017-02-15 18:48:14 +05:30
dra7xx - > pci = pci ;
2014-07-22 15:23:45 -06:00
dra7xx - > phy_count = phy_count ;
2017-01-11 17:36:55 +05:30
ret = dra7xx_pcie_enable_phy ( dra7xx ) ;
if ( ret ) {
dev_err ( dev , " failed to enable phy \n " ) ;
return ret ;
}
2017-02-15 18:48:11 +05:30
platform_set_drvdata ( pdev , dra7xx ) ;
2014-07-22 15:23:45 -06:00
pm_runtime_enable ( dev ) ;
ret = pm_runtime_get_sync ( dev ) ;
2015-08-20 01:30:36 -05:00
if ( ret < 0 ) {
2014-07-22 15:23:45 -06:00
dev_err ( dev , " pm_runtime_get_sync failed \n " ) ;
2015-07-31 17:55:10 +05:30
goto err_get_sync ;
2014-07-22 15:23:45 -06:00
}
2017-01-11 17:36:52 +05:30
reset = devm_gpiod_get_optional ( dev , NULL , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( reset ) ) {
ret = PTR_ERR ( reset ) ;
dev_err ( & pdev - > dev , " gpio request failed, ret %d \n " , ret ) ;
2015-07-28 19:09:09 +05:30
goto err_gpio ;
2014-07-22 15:23:45 -06:00
}
reg = dra7xx_pcie_readl ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD ) ;
reg & = ~ LTSSM_EN ;
dra7xx_pcie_writel ( dra7xx , PCIECTRL_DRA7XX_CONF_DEVICE_CMD , reg ) ;
2017-01-11 17:36:53 +05:30
dra7xx - > link_gen = of_pci_get_max_link_speed ( np ) ;
if ( dra7xx - > link_gen < 0 | | dra7xx - > link_gen > 2 )
dra7xx - > link_gen = 2 ;
2017-03-27 15:15:08 +05:30
switch ( mode ) {
case DW_PCIE_RC_TYPE :
2017-12-20 00:29:30 +01:00
if ( ! IS_ENABLED ( CONFIG_PCI_DRA7XX_HOST ) ) {
ret = - ENODEV ;
goto err_gpio ;
}
2017-03-27 15:15:08 +05:30
dra7xx_pcie_writel ( dra7xx , PCIECTRL_TI_CONF_DEVICE_TYPE ,
DEVICE_TYPE_RC ) ;
ret = dra7xx_add_pcie_port ( dra7xx , pdev ) ;
if ( ret < 0 )
goto err_gpio ;
break ;
case DW_PCIE_EP_TYPE :
2017-12-20 00:29:30 +01:00
if ( ! IS_ENABLED ( CONFIG_PCI_DRA7XX_EP ) ) {
ret = - ENODEV ;
goto err_gpio ;
}
2017-03-27 15:15:08 +05:30
dra7xx_pcie_writel ( dra7xx , PCIECTRL_TI_CONF_DEVICE_TYPE ,
DEVICE_TYPE_EP ) ;
2017-03-27 15:15:11 +05:30
ret = dra7xx_pcie_ep_unaligned_memaccess ( dev ) ;
if ( ret )
goto err_gpio ;
2017-03-27 15:15:08 +05:30
ret = dra7xx_add_pcie_ep ( dra7xx , pdev ) ;
if ( ret < 0 )
goto err_gpio ;
break ;
default :
dev_err ( dev , " INVALID device type %d \n " , mode ) ;
}
dra7xx - > mode = mode ;
2014-07-22 15:23:45 -06:00
2017-03-13 19:13:28 +05:30
ret = devm_request_irq ( dev , irq , dra7xx_pcie_irq_handler ,
IRQF_SHARED , " dra7xx-pcie-main " , dra7xx ) ;
if ( ret ) {
dev_err ( dev , " failed to request irq \n " ) ;
goto err_gpio ;
}
2014-07-22 15:23:45 -06:00
return 0 ;
2015-07-28 19:09:09 +05:30
err_gpio :
2014-07-22 15:23:45 -06:00
pm_runtime_put ( dev ) ;
2015-07-31 17:55:10 +05:30
err_get_sync :
2014-07-22 15:23:45 -06:00
pm_runtime_disable ( dev ) ;
2017-01-11 17:36:55 +05:30
dra7xx_pcie_disable_phy ( dra7xx ) ;
2014-07-22 15:23:45 -06:00
2017-10-09 14:33:37 +05:30
err_link :
while ( - - i > = 0 )
device_link_del ( link [ i ] ) ;
2014-07-22 15:23:45 -06:00
return ret ;
}
2015-07-31 17:55:11 +05:30
# ifdef CONFIG_PM_SLEEP
2015-07-31 17:55:12 +05:30
static int dra7xx_pcie_suspend ( struct device * dev )
{
struct dra7xx_pcie * dra7xx = dev_get_drvdata ( dev ) ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = dra7xx - > pci ;
2015-07-31 17:55:12 +05:30
u32 val ;
2017-03-27 15:15:08 +05:30
if ( dra7xx - > mode ! = DW_PCIE_RC_TYPE )
return 0 ;
2015-07-31 17:55:12 +05:30
/* clear MSE */
2017-02-15 18:48:14 +05:30
val = dw_pcie_readl_dbi ( pci , PCI_COMMAND ) ;
2015-07-31 17:55:12 +05:30
val & = ~ PCI_COMMAND_MEMORY ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCI_COMMAND , val ) ;
2015-07-31 17:55:12 +05:30
return 0 ;
}
static int dra7xx_pcie_resume ( struct device * dev )
{
struct dra7xx_pcie * dra7xx = dev_get_drvdata ( dev ) ;
2017-02-15 18:48:14 +05:30
struct dw_pcie * pci = dra7xx - > pci ;
2015-07-31 17:55:12 +05:30
u32 val ;
2017-03-27 15:15:08 +05:30
if ( dra7xx - > mode ! = DW_PCIE_RC_TYPE )
return 0 ;
2015-07-31 17:55:12 +05:30
/* set MSE */
2017-02-15 18:48:14 +05:30
val = dw_pcie_readl_dbi ( pci , PCI_COMMAND ) ;
2015-07-31 17:55:12 +05:30
val | = PCI_COMMAND_MEMORY ;
2017-02-15 18:48:14 +05:30
dw_pcie_writel_dbi ( pci , PCI_COMMAND , val ) ;
2015-07-31 17:55:12 +05:30
return 0 ;
}
2015-07-31 17:55:11 +05:30
static int dra7xx_pcie_suspend_noirq ( struct device * dev )
{
struct dra7xx_pcie * dra7xx = dev_get_drvdata ( dev ) ;
2017-01-11 17:36:55 +05:30
dra7xx_pcie_disable_phy ( dra7xx ) ;
2015-07-31 17:55:11 +05:30
return 0 ;
}
static int dra7xx_pcie_resume_noirq ( struct device * dev )
{
struct dra7xx_pcie * dra7xx = dev_get_drvdata ( dev ) ;
int ret ;
2017-01-11 17:36:55 +05:30
ret = dra7xx_pcie_enable_phy ( dra7xx ) ;
if ( ret ) {
dev_err ( dev , " failed to enable phy \n " ) ;
return ret ;
2015-07-31 17:55:11 +05:30
}
return 0 ;
}
# endif
2017-12-01 10:36:52 +05:30
static void dra7xx_pcie_shutdown ( struct platform_device * pdev )
2017-09-20 10:54:15 +05:30
{
struct device * dev = & pdev - > dev ;
struct dra7xx_pcie * dra7xx = dev_get_drvdata ( dev ) ;
int ret ;
dra7xx_pcie_stop_link ( dra7xx - > pci ) ;
ret = pm_runtime_put_sync ( dev ) ;
if ( ret < 0 )
dev_dbg ( dev , " pm_runtime_put_sync failed \n " ) ;
pm_runtime_disable ( dev ) ;
dra7xx_pcie_disable_phy ( dra7xx ) ;
}
2015-07-31 17:55:11 +05:30
static const struct dev_pm_ops dra7xx_pcie_pm_ops = {
2015-07-31 17:55:12 +05:30
SET_SYSTEM_SLEEP_PM_OPS ( dra7xx_pcie_suspend , dra7xx_pcie_resume )
2015-07-31 17:55:11 +05:30
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS ( dra7xx_pcie_suspend_noirq ,
dra7xx_pcie_resume_noirq )
} ;
2014-07-22 15:23:45 -06:00
static struct platform_driver dra7xx_pcie_driver = {
. driver = {
. name = " dra7-pcie " ,
. of_match_table = of_dra7xx_pcie_match ,
2016-08-24 16:57:47 -04:00
. suppress_bind_attrs = true ,
2015-07-31 17:55:11 +05:30
. pm = & dra7xx_pcie_pm_ops ,
2014-07-22 15:23:45 -06:00
} ,
2017-09-20 10:54:15 +05:30
. shutdown = dra7xx_pcie_shutdown ,
2014-07-22 15:23:45 -06:00
} ;
2016-08-24 16:57:47 -04:00
builtin_platform_driver_probe ( dra7xx_pcie_driver , dra7xx_pcie_probe ) ;