2013-06-21 16:24:54 +09:00
/*
2013-07-31 17:14:10 +09:00
* Synopsys Designware PCIe host controller driver
2013-06-21 16:24:54 +09:00
*
* Copyright ( C ) 2013 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Jingoo Han < jg1 . han @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2013-09-06 15:54:59 +09:00
# include <linux/irq.h>
# include <linux/irqdomain.h>
2013-06-21 16:24:54 +09:00
# include <linux/kernel.h>
# include <linux/module.h>
2013-09-06 15:54:59 +09:00
# include <linux/msi.h>
2013-06-21 16:24:54 +09:00
# include <linux/of_address.h>
2014-03-05 14:25:51 +01:00
# include <linux/of_pci.h>
2013-06-21 16:24:54 +09:00
# include <linux/pci.h>
# include <linux/pci_regs.h>
2014-07-17 14:30:40 +05:30
# include <linux/platform_device.h>
2013-06-21 16:24:54 +09:00
# include <linux/types.h>
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
# include <linux/delay.h>
2013-06-21 16:24:54 +09:00
2013-07-31 17:14:10 +09:00
# include "pcie-designware.h"
2013-06-21 16:24:54 +09:00
/* Synopsis specific PCIE configuration registers */
# define PCIE_PORT_LINK_CONTROL 0x710
# define PORT_LINK_MODE_MASK (0x3f << 16)
2013-07-31 17:14:10 +09:00
# define PORT_LINK_MODE_1_LANES (0x1 << 16)
# define PORT_LINK_MODE_2_LANES (0x3 << 16)
2013-06-21 16:24:54 +09:00
# define PORT_LINK_MODE_4_LANES (0x7 << 16)
2015-05-13 14:44:34 +08:00
# define PORT_LINK_MODE_8_LANES (0xf << 16)
2013-06-21 16:24:54 +09:00
# define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
# define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
2015-08-26 11:17:34 +08:00
# define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
2013-07-31 17:14:10 +09:00
# define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
# define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
# define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
2015-05-13 14:44:34 +08:00
# define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
2013-06-21 16:24:54 +09:00
# define PCIE_MSI_ADDR_LO 0x820
# define PCIE_MSI_ADDR_HI 0x824
# define PCIE_MSI_INTR0_ENABLE 0x828
# define PCIE_MSI_INTR0_MASK 0x82C
# define PCIE_MSI_INTR0_STATUS 0x830
# define PCIE_ATU_VIEWPORT 0x900
# define PCIE_ATU_REGION_INBOUND (0x1 << 31)
# define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
# define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
# define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
# define PCIE_ATU_CR1 0x904
# define PCIE_ATU_TYPE_MEM (0x0 << 0)
# define PCIE_ATU_TYPE_IO (0x2 << 0)
# define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
# define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
# define PCIE_ATU_CR2 0x908
# define PCIE_ATU_ENABLE (0x1 << 31)
# define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
# define PCIE_ATU_LOWER_BASE 0x90C
# define PCIE_ATU_UPPER_BASE 0x910
# define PCIE_ATU_LIMIT 0x914
# define PCIE_ATU_LOWER_TARGET 0x918
# define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
# define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
# define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
# define PCIE_ATU_UPPER_TARGET 0x91C
2016-03-10 14:44:44 -06:00
/* PCIe Port Logic registers */
# define PLR_OFFSET 0x700
# define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
# define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
2015-10-29 19:57:21 -05:00
static struct pci_ops dw_pcie_ops ;
2013-06-21 16:24:54 +09:00
2015-10-08 14:27:48 -05:00
int dw_pcie_cfg_read ( void __iomem * addr , int size , u32 * val )
2013-06-21 16:24:54 +09:00
{
2015-10-08 14:27:53 -05:00
if ( ( uintptr_t ) addr & ( size - 1 ) ) {
* val = 0 ;
return PCIBIOS_BAD_REGISTER_NUMBER ;
}
2015-10-08 14:27:43 -05:00
if ( size = = 4 )
* val = readl ( addr ) ;
2013-06-21 16:24:54 +09:00
else if ( size = = 2 )
2015-10-08 14:27:48 -05:00
* val = readw ( addr ) ;
2015-10-08 14:27:43 -05:00
else if ( size = = 1 )
2015-10-08 14:27:48 -05:00
* val = readb ( addr ) ;
2015-10-08 14:27:43 -05:00
else {
* val = 0 ;
2013-06-21 16:24:54 +09:00
return PCIBIOS_BAD_REGISTER_NUMBER ;
2015-10-08 14:27:43 -05:00
}
2013-06-21 16:24:54 +09:00
return PCIBIOS_SUCCESSFUL ;
}
2015-10-08 14:27:48 -05:00
int dw_pcie_cfg_write ( void __iomem * addr , int size , u32 val )
2013-06-21 16:24:54 +09:00
{
2015-10-08 14:27:53 -05:00
if ( ( uintptr_t ) addr & ( size - 1 ) )
return PCIBIOS_BAD_REGISTER_NUMBER ;
2013-06-21 16:24:54 +09:00
if ( size = = 4 )
writel ( val , addr ) ;
else if ( size = = 2 )
2015-10-08 14:27:48 -05:00
writew ( val , addr ) ;
2013-06-21 16:24:54 +09:00
else if ( size = = 1 )
2015-10-08 14:27:48 -05:00
writeb ( val , addr ) ;
2013-06-21 16:24:54 +09:00
else
return PCIBIOS_BAD_REGISTER_NUMBER ;
return PCIBIOS_SUCCESSFUL ;
}
2013-08-28 20:53:30 +09:00
static inline void dw_pcie_readl_rc ( struct pcie_port * pp , u32 reg , u32 * val )
2013-06-21 16:24:54 +09:00
{
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > readl_rc )
2013-08-28 20:53:30 +09:00
pp - > ops - > readl_rc ( pp , pp - > dbi_base + reg , val ) ;
2013-07-31 17:14:10 +09:00
else
2013-08-28 20:53:30 +09:00
* val = readl ( pp - > dbi_base + reg ) ;
2013-06-21 16:24:54 +09:00
}
2013-08-28 20:53:30 +09:00
static inline void dw_pcie_writel_rc ( struct pcie_port * pp , u32 val , u32 reg )
2013-06-21 16:24:54 +09:00
{
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > writel_rc )
2013-08-28 20:53:30 +09:00
pp - > ops - > writel_rc ( pp , val , pp - > dbi_base + reg ) ;
2013-07-31 17:14:10 +09:00
else
2013-08-28 20:53:30 +09:00
writel ( val , pp - > dbi_base + reg ) ;
2013-06-21 16:24:54 +09:00
}
2013-10-09 09:12:37 -06:00
static int dw_pcie_rd_own_conf ( struct pcie_port * pp , int where , int size ,
u32 * val )
2013-06-21 16:24:54 +09:00
{
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > rd_own_conf )
2016-01-05 15:48:11 -06:00
return pp - > ops - > rd_own_conf ( pp , where , size , val ) ;
2013-07-31 17:14:10 +09:00
2016-01-05 15:48:11 -06:00
return dw_pcie_cfg_read ( pp - > dbi_base + where , size , val ) ;
2013-06-21 16:24:54 +09:00
}
2013-10-09 09:12:37 -06:00
static int dw_pcie_wr_own_conf ( struct pcie_port * pp , int where , int size ,
u32 val )
2013-06-21 16:24:54 +09:00
{
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > wr_own_conf )
2016-01-05 15:48:11 -06:00
return pp - > ops - > wr_own_conf ( pp , where , size , val ) ;
2013-07-31 17:14:10 +09:00
2016-01-05 15:48:11 -06:00
return dw_pcie_cfg_write ( pp - > dbi_base + where , size , val ) ;
2013-06-21 16:24:54 +09:00
}
2015-04-30 16:22:28 +08:00
static void dw_pcie_prog_outbound_atu ( struct pcie_port * pp , int index ,
int type , u64 cpu_addr , u64 pci_addr , u32 size )
{
2015-12-18 14:38:55 +02:00
u32 val ;
2015-04-30 16:22:28 +08:00
dw_pcie_writel_rc ( pp , PCIE_ATU_REGION_OUTBOUND | index ,
PCIE_ATU_VIEWPORT ) ;
dw_pcie_writel_rc ( pp , lower_32_bits ( cpu_addr ) , PCIE_ATU_LOWER_BASE ) ;
dw_pcie_writel_rc ( pp , upper_32_bits ( cpu_addr ) , PCIE_ATU_UPPER_BASE ) ;
dw_pcie_writel_rc ( pp , lower_32_bits ( cpu_addr + size - 1 ) ,
PCIE_ATU_LIMIT ) ;
dw_pcie_writel_rc ( pp , lower_32_bits ( pci_addr ) , PCIE_ATU_LOWER_TARGET ) ;
dw_pcie_writel_rc ( pp , upper_32_bits ( pci_addr ) , PCIE_ATU_UPPER_TARGET ) ;
dw_pcie_writel_rc ( pp , type , PCIE_ATU_CR1 ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_ENABLE , PCIE_ATU_CR2 ) ;
2015-12-18 14:38:55 +02:00
/*
* Make sure ATU enable takes effect before any subsequent config
* and I / O accesses .
*/
dw_pcie_readl_rc ( pp , PCIE_ATU_CR2 , & val ) ;
2015-04-30 16:22:28 +08:00
}
2013-09-06 15:54:59 +09:00
static struct irq_chip dw_msi_irq_chip = {
. name = " PCI-MSI " ,
2014-11-23 12:23:20 +01:00
. irq_enable = pci_msi_unmask_irq ,
. irq_disable = pci_msi_mask_irq ,
. irq_mask = pci_msi_mask_irq ,
. irq_unmask = pci_msi_unmask_irq ,
2013-09-06 15:54:59 +09:00
} ;
/* MSI int handler */
2014-03-28 17:52:58 +01:00
irqreturn_t dw_handle_msi_irq ( struct pcie_port * pp )
2013-09-06 15:54:59 +09:00
{
unsigned long val ;
2013-10-09 21:32:12 +09:00
int i , pos , irq ;
2014-03-28 17:52:58 +01:00
irqreturn_t ret = IRQ_NONE ;
2013-09-06 15:54:59 +09:00
for ( i = 0 ; i < MAX_MSI_CTRLS ; i + + ) {
dw_pcie_rd_own_conf ( pp , PCIE_MSI_INTR0_STATUS + i * 12 , 4 ,
( u32 * ) & val ) ;
if ( val ) {
2014-03-28 17:52:58 +01:00
ret = IRQ_HANDLED ;
2013-09-06 15:54:59 +09:00
pos = 0 ;
while ( ( pos = find_next_bit ( & val , 32 , pos ) ) ! = 32 ) {
2013-10-09 21:32:12 +09:00
irq = irq_find_mapping ( pp - > irq_domain ,
i * 32 + pos ) ;
2013-12-12 19:29:03 +01:00
dw_pcie_wr_own_conf ( pp ,
PCIE_MSI_INTR0_STATUS + i * 12 ,
4 , 1 < < pos ) ;
2013-10-09 21:32:12 +09:00
generic_handle_irq ( irq ) ;
2013-09-06 15:54:59 +09:00
pos + + ;
}
}
}
2014-03-28 17:52:58 +01:00
return ret ;
2013-09-06 15:54:59 +09:00
}
void dw_pcie_msi_init ( struct pcie_port * pp )
{
2015-09-18 13:58:35 -05:00
u64 msi_target ;
2013-09-06 15:54:59 +09:00
pp - > msi_data = __get_free_pages ( GFP_KERNEL , 0 ) ;
2015-09-18 13:58:35 -05:00
msi_target = virt_to_phys ( ( void * ) pp - > msi_data ) ;
2013-09-06 15:54:59 +09:00
/* program the msi_data */
dw_pcie_wr_own_conf ( pp , PCIE_MSI_ADDR_LO , 4 ,
2015-09-18 13:58:35 -05:00
( u32 ) ( msi_target & 0xffffffff ) ) ;
dw_pcie_wr_own_conf ( pp , PCIE_MSI_ADDR_HI , 4 ,
( u32 ) ( msi_target > > 32 & 0xffffffff ) ) ;
2013-09-06 15:54:59 +09:00
}
2014-07-21 12:58:42 -04:00
static void dw_pcie_msi_clear_irq ( struct pcie_port * pp , int irq )
{
unsigned int res , bit , val ;
res = ( irq / 32 ) * 12 ;
bit = irq % 32 ;
dw_pcie_rd_own_conf ( pp , PCIE_MSI_INTR0_ENABLE + res , 4 , & val ) ;
val & = ~ ( 1 < < bit ) ;
dw_pcie_wr_own_conf ( pp , PCIE_MSI_INTR0_ENABLE + res , 4 , val ) ;
}
2013-11-29 14:35:24 +01:00
static void clear_irq_range ( struct pcie_port * pp , unsigned int irq_base ,
2013-12-27 09:30:25 +09:00
unsigned int nvec , unsigned int pos )
2013-11-29 14:35:24 +01:00
{
2014-07-21 12:58:42 -04:00
unsigned int i ;
2013-11-29 14:35:24 +01:00
2013-12-09 15:11:25 -07:00
for ( i = 0 ; i < nvec ; i + + ) {
2013-11-29 14:35:24 +01:00
irq_set_msi_desc_off ( irq_base , i , NULL ) ;
2013-12-27 09:30:25 +09:00
/* Disable corresponding interrupt on MSI controller */
2014-07-21 12:58:42 -04:00
if ( pp - > ops - > msi_clear_irq )
pp - > ops - > msi_clear_irq ( pp , pos + i ) ;
else
dw_pcie_msi_clear_irq ( pp , pos + i ) ;
2013-11-29 14:35:24 +01:00
}
2014-09-30 18:36:27 +02:00
bitmap_release_region ( pp - > msi_irq_in_use , pos , order_base_2 ( nvec ) ) ;
2013-11-29 14:35:24 +01:00
}
2014-07-21 12:58:42 -04:00
static void dw_pcie_msi_set_irq ( struct pcie_port * pp , int irq )
{
unsigned int res , bit , val ;
res = ( irq / 32 ) * 12 ;
bit = irq % 32 ;
dw_pcie_rd_own_conf ( pp , PCIE_MSI_INTR0_ENABLE + res , 4 , & val ) ;
val | = 1 < < bit ;
dw_pcie_wr_own_conf ( pp , PCIE_MSI_INTR0_ENABLE + res , 4 , val ) ;
}
2013-09-06 15:54:59 +09:00
static int assign_irq ( int no_irqs , struct msi_desc * desc , int * pos )
{
2014-09-30 18:36:27 +02:00
int irq , pos0 , i ;
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = ( struct pcie_port * ) msi_desc_to_pci_sysdata ( desc ) ;
2013-09-06 15:54:59 +09:00
2014-09-30 18:36:27 +02:00
pos0 = bitmap_find_free_region ( pp - > msi_irq_in_use , MAX_MSI_IRQS ,
order_base_2 ( no_irqs ) ) ;
if ( pos0 < 0 )
goto no_valid_irq ;
2013-09-06 15:54:59 +09:00
2013-10-09 21:32:12 +09:00
irq = irq_find_mapping ( pp - > irq_domain , pos0 ) ;
if ( ! irq )
2013-09-06 15:54:59 +09:00
goto no_valid_irq ;
2013-11-29 14:35:24 +01:00
/*
* irq_create_mapping ( called from dw_pcie_host_init ) pre - allocates
* descs so there is no need to allocate descs here . We can therefore
* assume that if irq_find_mapping above returns non - zero , then the
* descs are also successfully allocated .
*/
2013-12-09 15:11:25 -07:00
for ( i = 0 ; i < no_irqs ; i + + ) {
2013-11-29 14:35:24 +01:00
if ( irq_set_msi_desc_off ( irq , i , desc ) ! = 0 ) {
clear_irq_range ( pp , irq , i , pos0 ) ;
goto no_valid_irq ;
}
2013-09-06 15:54:59 +09:00
/*Enable corresponding interrupt in MSI interrupt controller */
2014-07-21 12:58:42 -04:00
if ( pp - > ops - > msi_set_irq )
pp - > ops - > msi_set_irq ( pp , pos0 + i ) ;
else
dw_pcie_msi_set_irq ( pp , pos0 + i ) ;
2013-09-06 15:54:59 +09:00
}
* pos = pos0 ;
2015-09-18 13:58:35 -05:00
desc - > nvec_used = no_irqs ;
desc - > msi_attrib . multiple = order_base_2 ( no_irqs ) ;
2013-09-06 15:54:59 +09:00
return irq ;
no_valid_irq :
* pos = pos0 ;
return - ENOSPC ;
}
2015-09-18 13:58:35 -05:00
static void dw_msi_setup_msg ( struct pcie_port * pp , unsigned int irq , u32 pos )
2013-09-06 15:54:59 +09:00
{
struct msi_msg msg ;
2015-09-18 13:58:35 -05:00
u64 msi_target ;
2013-09-06 15:54:59 +09:00
2014-09-23 22:28:58 +08:00
if ( pp - > ops - > get_msi_addr )
2015-09-18 13:58:35 -05:00
msi_target = pp - > ops - > get_msi_addr ( pp ) ;
2014-07-21 12:58:42 -04:00
else
2015-09-18 13:58:35 -05:00
msi_target = virt_to_phys ( ( void * ) pp - > msi_data ) ;
msg . address_lo = ( u32 ) ( msi_target & 0xffffffff ) ;
msg . address_hi = ( u32 ) ( msi_target > > 32 & 0xffffffff ) ;
2014-09-23 22:28:59 +08:00
if ( pp - > ops - > get_msi_data )
msg . data = pp - > ops - > get_msi_data ( pp , pos ) ;
else
msg . data = pos ;
2014-11-09 23:10:34 +08:00
pci_write_msi_msg ( irq , & msg ) ;
2015-09-18 13:58:35 -05:00
}
static int dw_msi_setup_irq ( struct msi_controller * chip , struct pci_dev * pdev ,
struct msi_desc * desc )
{
int irq , pos ;
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = pdev - > bus - > sysdata ;
2015-09-18 13:58:35 -05:00
if ( desc - > msi_attrib . is_msix )
return - EINVAL ;
irq = assign_irq ( 1 , desc , & pos ) ;
if ( irq < 0 )
return irq ;
dw_msi_setup_msg ( pp , irq , pos ) ;
2013-09-06 15:54:59 +09:00
return 0 ;
}
2015-09-18 13:58:35 -05:00
static int dw_msi_setup_irqs ( struct msi_controller * chip , struct pci_dev * pdev ,
int nvec , int type )
{
# ifdef CONFIG_PCI_MSI
int irq , pos ;
struct msi_desc * desc ;
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = pdev - > bus - > sysdata ;
2015-09-18 13:58:35 -05:00
/* MSI-X interrupts are not supported */
if ( type = = PCI_CAP_ID_MSIX )
return - EINVAL ;
WARN_ON ( ! list_is_singular ( & pdev - > dev . msi_list ) ) ;
desc = list_entry ( pdev - > dev . msi_list . next , struct msi_desc , list ) ;
irq = assign_irq ( nvec , desc , & pos ) ;
if ( irq < 0 )
return irq ;
dw_msi_setup_msg ( pp , irq , pos ) ;
return 0 ;
# else
return - EINVAL ;
# endif
}
2014-11-11 17:45:45 -07:00
static void dw_msi_teardown_irq ( struct msi_controller * chip , unsigned int irq )
2013-09-06 15:54:59 +09:00
{
2014-09-30 18:36:26 +02:00
struct irq_data * data = irq_get_irq_data ( irq ) ;
2015-06-01 16:05:41 +08:00
struct msi_desc * msi = irq_data_get_msi_desc ( data ) ;
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = ( struct pcie_port * ) msi_desc_to_pci_sysdata ( msi ) ;
2014-09-30 18:36:26 +02:00
clear_irq_range ( pp , irq , 1 , data - > hwirq ) ;
2013-09-06 15:54:59 +09:00
}
2014-11-11 17:45:45 -07:00
static struct msi_controller dw_pcie_msi_chip = {
2013-09-06 15:54:59 +09:00
. setup_irq = dw_msi_setup_irq ,
2015-09-18 13:58:35 -05:00
. setup_irqs = dw_msi_setup_irqs ,
2013-09-06 15:54:59 +09:00
. teardown_irq = dw_msi_teardown_irq ,
} ;
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
int dw_pcie_wait_for_link ( struct pcie_port * pp )
{
int retries ;
/* check if the link is up or not */
for ( retries = 0 ; retries < LINK_WAIT_MAX_RETRIES ; retries + + ) {
if ( dw_pcie_link_up ( pp ) ) {
dev_info ( pp - > dev , " link up \n " ) ;
return 0 ;
}
usleep_range ( LINK_WAIT_USLEEP_MIN , LINK_WAIT_USLEEP_MAX ) ;
}
dev_err ( pp - > dev , " phy link never came up \n " ) ;
return - ETIMEDOUT ;
}
2013-07-31 17:14:10 +09:00
int dw_pcie_link_up ( struct pcie_port * pp )
{
2016-03-10 14:44:44 -06:00
u32 val ;
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > link_up )
return pp - > ops - > link_up ( pp ) ;
2016-01-05 15:48:11 -06:00
2016-03-10 14:44:44 -06:00
val = readl ( pp - > dbi_base + PCIE_PHY_DEBUG_R1 ) ;
return val & PCIE_PHY_DEBUG_R1_LINK_UP ;
2013-07-31 17:14:10 +09:00
}
2013-09-06 15:54:59 +09:00
static int dw_pcie_msi_map ( struct irq_domain * domain , unsigned int irq ,
irq_hw_number_t hwirq )
{
irq_set_chip_and_handler ( irq , & dw_msi_irq_chip , handle_simple_irq ) ;
irq_set_chip_data ( irq , domain - > host_data ) ;
return 0 ;
}
static const struct irq_domain_ops msi_domain_ops = {
. map = dw_pcie_msi_map ,
} ;
2015-02-19 20:41:48 +03:00
int dw_pcie_host_init ( struct pcie_port * pp )
2013-07-31 17:14:10 +09:00
{
struct device_node * np = pp - > dev - > of_node ;
2014-07-17 14:30:40 +05:30
struct platform_device * pdev = to_platform_device ( pp - > dev ) ;
2015-10-29 19:57:21 -05:00
struct pci_bus * bus , * child ;
2014-07-17 14:30:40 +05:30
struct resource * cfg_res ;
2015-10-29 19:56:58 -05:00
int i , ret ;
2015-10-29 19:57:06 -05:00
LIST_HEAD ( res ) ;
struct resource_entry * win ;
2013-09-06 15:54:59 +09:00
2014-07-17 14:30:40 +05:30
cfg_res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " config " ) ;
if ( cfg_res ) {
2014-09-05 17:48:54 -06:00
pp - > cfg0_size = resource_size ( cfg_res ) / 2 ;
pp - > cfg1_size = resource_size ( cfg_res ) / 2 ;
2014-07-17 14:30:40 +05:30
pp - > cfg0_base = cfg_res - > start ;
2014-09-05 17:48:54 -06:00
pp - > cfg1_base = cfg_res - > start + pp - > cfg0_size ;
2015-07-21 17:54:11 -04:00
} else if ( ! pp - > va_cfg0_base ) {
2014-07-17 14:30:40 +05:30
dev_err ( pp - > dev , " missing *config* reg space \n " ) ;
}
2015-10-29 19:57:06 -05:00
ret = of_pci_get_host_bridge_resources ( np , 0 , 0xff , & res , & pp - > io_base ) ;
if ( ret )
return ret ;
2013-07-31 17:14:10 +09:00
2016-05-28 18:18:54 -05:00
ret = devm_request_pci_bus_resources ( & pdev - > dev , & res ) ;
if ( ret )
goto error ;
2013-07-31 17:14:10 +09:00
/* Get the I/O and memory ranges from DT */
2015-10-29 19:57:06 -05:00
resource_list_for_each_entry ( win , & res ) {
switch ( resource_type ( win - > res ) ) {
case IORESOURCE_IO :
pp - > io = win - > res ;
pp - > io - > name = " I/O " ;
pp - > io_size = resource_size ( pp - > io ) ;
pp - > io_bus_addr = pp - > io - > start - win - > offset ;
2015-10-29 19:57:21 -05:00
ret = pci_remap_iospace ( pp - > io , pp - > io_base ) ;
2016-05-28 18:48:11 -05:00
if ( ret )
2015-10-29 19:57:21 -05:00
dev_warn ( pp - > dev , " error %d: failed to map resource %pR \n " ,
ret , pp - > io ) ;
2015-10-29 19:57:06 -05:00
break ;
case IORESOURCE_MEM :
pp - > mem = win - > res ;
pp - > mem - > name = " MEM " ;
pp - > mem_size = resource_size ( pp - > mem ) ;
pp - > mem_bus_addr = pp - > mem - > start - win - > offset ;
break ;
case 0 :
pp - > cfg = win - > res ;
pp - > cfg0_size = resource_size ( pp - > cfg ) / 2 ;
pp - > cfg1_size = resource_size ( pp - > cfg ) / 2 ;
pp - > cfg0_base = pp - > cfg - > start ;
pp - > cfg1_base = pp - > cfg - > start + pp - > cfg0_size ;
break ;
case IORESOURCE_BUS :
pp - > busn = win - > res ;
break ;
2013-07-31 17:14:10 +09:00
}
2014-07-23 19:52:38 +02:00
}
2013-07-31 17:14:10 +09:00
if ( ! pp - > dbi_base ) {
2015-10-29 19:57:06 -05:00
pp - > dbi_base = devm_ioremap ( pp - > dev , pp - > cfg - > start ,
resource_size ( pp - > cfg ) ) ;
2013-07-31 17:14:10 +09:00
if ( ! pp - > dbi_base ) {
dev_err ( pp - > dev , " error with ioremap \n " ) ;
2016-05-31 11:14:08 -05:00
ret = - ENOMEM ;
goto error ;
2013-07-31 17:14:10 +09:00
}
}
2015-10-29 19:57:06 -05:00
pp - > mem_base = pp - > mem - > start ;
2013-07-31 17:14:10 +09:00
if ( ! pp - > va_cfg0_base ) {
2014-07-23 14:54:51 -04:00
pp - > va_cfg0_base = devm_ioremap ( pp - > dev , pp - > cfg0_base ,
2014-09-05 17:48:54 -06:00
pp - > cfg0_size ) ;
2014-07-23 14:54:51 -04:00
if ( ! pp - > va_cfg0_base ) {
dev_err ( pp - > dev , " error with ioremap in function \n " ) ;
2016-05-31 11:14:08 -05:00
ret = - ENOMEM ;
goto error ;
2014-07-23 14:54:51 -04:00
}
2013-07-31 17:14:10 +09:00
}
2014-07-23 14:54:51 -04:00
2013-07-31 17:14:10 +09:00
if ( ! pp - > va_cfg1_base ) {
2014-07-23 14:54:51 -04:00
pp - > va_cfg1_base = devm_ioremap ( pp - > dev , pp - > cfg1_base ,
2014-09-05 17:48:54 -06:00
pp - > cfg1_size ) ;
2014-07-23 14:54:51 -04:00
if ( ! pp - > va_cfg1_base ) {
dev_err ( pp - > dev , " error with ioremap \n " ) ;
2016-05-31 11:14:08 -05:00
ret = - ENOMEM ;
goto error ;
2014-07-23 14:54:51 -04:00
}
2013-07-31 17:14:10 +09:00
}
2015-09-29 00:03:10 +08:00
ret = of_property_read_u32 ( np , " num-lanes " , & pp - > lanes ) ;
if ( ret )
pp - > lanes = 0 ;
2013-07-31 17:14:10 +09:00
2013-09-06 15:54:59 +09:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
2014-07-23 14:54:51 -04:00
if ( ! pp - > ops - > msi_host_init ) {
pp - > irq_domain = irq_domain_add_linear ( pp - > dev - > of_node ,
MAX_MSI_IRQS , & msi_domain_ops ,
& dw_pcie_msi_chip ) ;
if ( ! pp - > irq_domain ) {
dev_err ( pp - > dev , " irq domain init failed \n " ) ;
2016-05-31 11:14:08 -05:00
ret = - ENXIO ;
goto error ;
2014-07-23 14:54:51 -04:00
}
2013-09-06 15:54:59 +09:00
2014-07-23 14:54:51 -04:00
for ( i = 0 ; i < MAX_MSI_IRQS ; i + + )
irq_create_mapping ( pp - > irq_domain , i ) ;
} else {
ret = pp - > ops - > msi_host_init ( pp , & dw_pcie_msi_chip ) ;
if ( ret < 0 )
2016-05-31 11:14:08 -05:00
goto error ;
2014-07-23 14:54:51 -04:00
}
2013-09-06 15:54:59 +09:00
}
2013-07-31 17:14:10 +09:00
if ( pp - > ops - > host_init )
pp - > ops - > host_init ( pp ) ;
2015-10-29 19:57:21 -05:00
pp - > root_bus_nr = pp - > busn - > start ;
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
bus = pci_scan_root_bus_msi ( pp - > dev , pp - > root_bus_nr ,
& dw_pcie_ops , pp , & res ,
& dw_pcie_msi_chip ) ;
dw_pcie_msi_chip . dev = pp - > dev ;
} else
bus = pci_scan_root_bus ( pp - > dev , pp - > root_bus_nr , & dw_pcie_ops ,
pp , & res ) ;
2016-05-31 11:14:08 -05:00
if ( ! bus ) {
ret = - ENOMEM ;
goto error ;
}
2015-10-29 19:57:21 -05:00
if ( pp - > ops - > scan_bus )
pp - > ops - > scan_bus ( pp ) ;
# ifdef CONFIG_ARM
/* support old dtbs that incorrectly describe IRQs */
pci_fixup_irqs ( pci_common_swizzle , of_irq_parse_and_map_pci ) ;
2014-11-11 15:38:07 -07:00
# endif
2016-01-29 11:29:32 +00:00
pci_bus_size_bridges ( bus ) ;
pci_bus_assign_resources ( bus ) ;
2013-07-31 17:14:10 +09:00
2016-01-29 11:29:32 +00:00
list_for_each_entry ( child , & bus - > children , node )
pcie_bus_configure_settings ( child ) ;
2013-07-31 17:14:10 +09:00
2015-10-29 19:57:21 -05:00
pci_bus_add_devices ( bus ) ;
2013-07-31 17:14:10 +09:00
return 0 ;
2016-05-31 11:14:08 -05:00
error :
pci_free_resource_list ( & res ) ;
return ret ;
2013-07-31 17:14:10 +09:00
}
static int dw_pcie_rd_other_conf ( struct pcie_port * pp , struct pci_bus * bus ,
2013-06-21 16:24:54 +09:00
u32 devfn , int where , int size , u32 * val )
{
2015-04-30 16:22:29 +08:00
int ret , type ;
2015-10-08 14:27:48 -05:00
u32 busdev , cfg_size ;
2015-04-30 16:22:29 +08:00
u64 cpu_addr ;
void __iomem * va_cfg_base ;
2013-06-21 16:24:54 +09:00
2016-01-05 15:56:30 -06:00
if ( pp - > ops - > rd_other_conf )
return pp - > ops - > rd_other_conf ( pp , bus , devfn , where , size , val ) ;
2013-06-21 16:24:54 +09:00
busdev = PCIE_ATU_BUS ( bus - > number ) | PCIE_ATU_DEV ( PCI_SLOT ( devfn ) ) |
PCIE_ATU_FUNC ( PCI_FUNC ( devfn ) ) ;
if ( bus - > parent - > number = = pp - > root_bus_nr ) {
2015-04-30 16:22:29 +08:00
type = PCIE_ATU_TYPE_CFG0 ;
2015-10-29 19:56:58 -05:00
cpu_addr = pp - > cfg0_base ;
2015-04-30 16:22:29 +08:00
cfg_size = pp - > cfg0_size ;
va_cfg_base = pp - > va_cfg0_base ;
2013-06-21 16:24:54 +09:00
} else {
2015-04-30 16:22:29 +08:00
type = PCIE_ATU_TYPE_CFG1 ;
2015-10-29 19:56:58 -05:00
cpu_addr = pp - > cfg1_base ;
2015-04-30 16:22:29 +08:00
cfg_size = pp - > cfg1_size ;
va_cfg_base = pp - > va_cfg1_base ;
2013-06-21 16:24:54 +09:00
}
2015-04-30 16:22:29 +08:00
dw_pcie_prog_outbound_atu ( pp , PCIE_ATU_REGION_INDEX0 ,
type , cpu_addr ,
busdev , cfg_size ) ;
2015-10-08 14:27:48 -05:00
ret = dw_pcie_cfg_read ( va_cfg_base + where , size , val ) ;
2015-04-30 16:22:29 +08:00
dw_pcie_prog_outbound_atu ( pp , PCIE_ATU_REGION_INDEX0 ,
2015-10-29 19:56:58 -05:00
PCIE_ATU_TYPE_IO , pp - > io_base ,
2015-04-30 16:22:29 +08:00
pp - > io_bus_addr , pp - > io_size ) ;
2013-06-21 16:24:54 +09:00
return ret ;
}
2013-07-31 17:14:10 +09:00
static int dw_pcie_wr_other_conf ( struct pcie_port * pp , struct pci_bus * bus ,
2013-06-21 16:24:54 +09:00
u32 devfn , int where , int size , u32 val )
{
2015-04-30 16:22:29 +08:00
int ret , type ;
2015-10-08 14:27:48 -05:00
u32 busdev , cfg_size ;
2015-04-30 16:22:29 +08:00
u64 cpu_addr ;
void __iomem * va_cfg_base ;
2013-06-21 16:24:54 +09:00
2016-01-05 15:56:30 -06:00
if ( pp - > ops - > wr_other_conf )
return pp - > ops - > wr_other_conf ( pp , bus , devfn , where , size , val ) ;
2013-06-21 16:24:54 +09:00
busdev = PCIE_ATU_BUS ( bus - > number ) | PCIE_ATU_DEV ( PCI_SLOT ( devfn ) ) |
PCIE_ATU_FUNC ( PCI_FUNC ( devfn ) ) ;
if ( bus - > parent - > number = = pp - > root_bus_nr ) {
2015-04-30 16:22:29 +08:00
type = PCIE_ATU_TYPE_CFG0 ;
2015-10-29 19:56:58 -05:00
cpu_addr = pp - > cfg0_base ;
2015-04-30 16:22:29 +08:00
cfg_size = pp - > cfg0_size ;
va_cfg_base = pp - > va_cfg0_base ;
2013-06-21 16:24:54 +09:00
} else {
2015-04-30 16:22:29 +08:00
type = PCIE_ATU_TYPE_CFG1 ;
2015-10-29 19:56:58 -05:00
cpu_addr = pp - > cfg1_base ;
2015-04-30 16:22:29 +08:00
cfg_size = pp - > cfg1_size ;
va_cfg_base = pp - > va_cfg1_base ;
2013-06-21 16:24:54 +09:00
}
2015-04-30 16:22:29 +08:00
dw_pcie_prog_outbound_atu ( pp , PCIE_ATU_REGION_INDEX0 ,
type , cpu_addr ,
busdev , cfg_size ) ;
2015-10-08 14:27:48 -05:00
ret = dw_pcie_cfg_write ( va_cfg_base + where , size , val ) ;
2015-04-30 16:22:29 +08:00
dw_pcie_prog_outbound_atu ( pp , PCIE_ATU_REGION_INDEX0 ,
2015-10-29 19:56:58 -05:00
PCIE_ATU_TYPE_IO , pp - > io_base ,
2015-04-30 16:22:29 +08:00
pp - > io_bus_addr , pp - > io_size ) ;
2013-06-21 16:24:54 +09:00
return ret ;
}
2013-07-31 17:14:10 +09:00
static int dw_pcie_valid_config ( struct pcie_port * pp ,
2013-06-21 16:24:54 +09:00
struct pci_bus * bus , int dev )
{
/* If there is no link, then there is no device */
if ( bus - > number ! = pp - > root_bus_nr ) {
2013-07-31 17:14:10 +09:00
if ( ! dw_pcie_link_up ( pp ) )
2013-06-21 16:24:54 +09:00
return 0 ;
}
/* access only one slot on each root port */
if ( bus - > number = = pp - > root_bus_nr & & dev > 0 )
return 0 ;
/*
* do not read more than one device on the bus directly attached
* to RC ' s ( Virtual Bridge ' s ) DS side .
*/
if ( bus - > primary = = pp - > root_bus_nr & & dev > 0 )
return 0 ;
return 1 ;
}
2013-07-31 17:14:10 +09:00
static int dw_pcie_rd_conf ( struct pci_bus * bus , u32 devfn , int where ,
2013-06-21 16:24:54 +09:00
int size , u32 * val )
{
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = bus - > sysdata ;
2013-06-21 16:24:54 +09:00
2013-07-31 17:14:10 +09:00
if ( dw_pcie_valid_config ( pp , bus , PCI_SLOT ( devfn ) ) = = 0 ) {
2013-06-21 16:24:54 +09:00
* val = 0xffffffff ;
return PCIBIOS_DEVICE_NOT_FOUND ;
}
2016-01-05 15:48:11 -06:00
if ( bus - > number = = pp - > root_bus_nr )
return dw_pcie_rd_own_conf ( pp , where , size , val ) ;
2013-06-21 16:24:54 +09:00
2016-01-05 15:48:11 -06:00
return dw_pcie_rd_other_conf ( pp , bus , devfn , where , size , val ) ;
2013-06-21 16:24:54 +09:00
}
2013-07-31 17:14:10 +09:00
static int dw_pcie_wr_conf ( struct pci_bus * bus , u32 devfn ,
2013-06-21 16:24:54 +09:00
int where , int size , u32 val )
{
2015-10-29 19:57:21 -05:00
struct pcie_port * pp = bus - > sysdata ;
2013-06-21 16:24:54 +09:00
2013-07-31 17:14:10 +09:00
if ( dw_pcie_valid_config ( pp , bus , PCI_SLOT ( devfn ) ) = = 0 )
2013-06-21 16:24:54 +09:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2016-01-05 15:48:11 -06:00
if ( bus - > number = = pp - > root_bus_nr )
return dw_pcie_wr_own_conf ( pp , where , size , val ) ;
2013-06-21 16:24:54 +09:00
2016-01-05 15:48:11 -06:00
return dw_pcie_wr_other_conf ( pp , bus , devfn , where , size , val ) ;
2013-06-21 16:24:54 +09:00
}
2013-07-31 17:14:10 +09:00
static struct pci_ops dw_pcie_ops = {
. read = dw_pcie_rd_conf ,
. write = dw_pcie_wr_conf ,
2013-06-21 16:24:54 +09:00
} ;
2013-07-31 17:14:10 +09:00
void dw_pcie_setup_rc ( struct pcie_port * pp )
2013-06-21 16:24:54 +09:00
{
u32 val ;
2014-04-14 14:22:54 -06:00
/* set the number of lanes */
2013-08-28 20:53:30 +09:00
dw_pcie_readl_rc ( pp , PCIE_PORT_LINK_CONTROL , & val ) ;
2013-06-21 16:24:54 +09:00
val & = ~ PORT_LINK_MODE_MASK ;
2013-07-31 17:14:10 +09:00
switch ( pp - > lanes ) {
case 1 :
val | = PORT_LINK_MODE_1_LANES ;
break ;
case 2 :
val | = PORT_LINK_MODE_2_LANES ;
break ;
case 4 :
val | = PORT_LINK_MODE_4_LANES ;
break ;
2015-05-13 14:44:34 +08:00
case 8 :
val | = PORT_LINK_MODE_8_LANES ;
break ;
2015-09-29 00:03:10 +08:00
default :
dev_err ( pp - > dev , " num-lanes %u: invalid value \n " , pp - > lanes ) ;
return ;
2013-07-31 17:14:10 +09:00
}
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , val , PCIE_PORT_LINK_CONTROL ) ;
2013-06-21 16:24:54 +09:00
/* set link width speed control register */
2013-08-28 20:53:30 +09:00
dw_pcie_readl_rc ( pp , PCIE_LINK_WIDTH_SPEED_CONTROL , & val ) ;
2013-06-21 16:24:54 +09:00
val & = ~ PORT_LOGIC_LINK_WIDTH_MASK ;
2013-07-31 17:14:10 +09:00
switch ( pp - > lanes ) {
case 1 :
val | = PORT_LOGIC_LINK_WIDTH_1_LANES ;
break ;
case 2 :
val | = PORT_LOGIC_LINK_WIDTH_2_LANES ;
break ;
case 4 :
val | = PORT_LOGIC_LINK_WIDTH_4_LANES ;
break ;
2015-05-13 14:44:34 +08:00
case 8 :
val | = PORT_LOGIC_LINK_WIDTH_8_LANES ;
break ;
2013-07-31 17:14:10 +09:00
}
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , val , PCIE_LINK_WIDTH_SPEED_CONTROL ) ;
2013-06-21 16:24:54 +09:00
/* setup RC BARs */
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , 0x00000004 , PCI_BASE_ADDRESS_0 ) ;
2014-02-19 17:34:35 +05:30
dw_pcie_writel_rc ( pp , 0x00000000 , PCI_BASE_ADDRESS_1 ) ;
2013-06-21 16:24:54 +09:00
/* setup interrupt pins */
2013-08-28 20:53:30 +09:00
dw_pcie_readl_rc ( pp , PCI_INTERRUPT_LINE , & val ) ;
2013-06-21 16:24:54 +09:00
val & = 0xffff00ff ;
val | = 0x00000100 ;
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , val , PCI_INTERRUPT_LINE ) ;
2013-06-21 16:24:54 +09:00
/* setup bus numbers */
2013-08-28 20:53:30 +09:00
dw_pcie_readl_rc ( pp , PCI_PRIMARY_BUS , & val ) ;
2013-06-21 16:24:54 +09:00
val & = 0xff000000 ;
val | = 0x00010100 ;
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , val , PCI_PRIMARY_BUS ) ;
2013-06-21 16:24:54 +09:00
/* setup command register */
2013-08-28 20:53:30 +09:00
dw_pcie_readl_rc ( pp , PCI_COMMAND , & val ) ;
2013-06-21 16:24:54 +09:00
val & = 0xffff0000 ;
val | = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR ;
2013-08-28 20:53:30 +09:00
dw_pcie_writel_rc ( pp , val , PCI_COMMAND ) ;
2016-03-16 19:40:33 +08:00
/*
* If the platform provides - > rd_other_conf , it means the platform
* uses its own address translation component rather than ATU , so
* we should not program the ATU here .
*/
if ( ! pp - > ops - > rd_other_conf )
dw_pcie_prog_outbound_atu ( pp , PCIE_ATU_REGION_INDEX1 ,
PCIE_ATU_TYPE_MEM , pp - > mem_base ,
pp - > mem_bus_addr , pp - > mem_size ) ;
dw_pcie_wr_own_conf ( pp , PCI_BASE_ADDRESS_0 , 4 , 0 ) ;
/* program correct class for RC */
dw_pcie_wr_own_conf ( pp , PCI_CLASS_DEVICE , 2 , PCI_CLASS_BRIDGE_PCI ) ;
dw_pcie_rd_own_conf ( pp , PCIE_LINK_WIDTH_SPEED_CONTROL , 4 , & val ) ;
val | = PORT_LOGIC_SPEED_CHANGE ;
dw_pcie_wr_own_conf ( pp , PCIE_LINK_WIDTH_SPEED_CONTROL , 4 , val ) ;
2013-06-21 16:24:54 +09:00
}
MODULE_AUTHOR ( " Jingoo Han <jg1.han@samsung.com> " ) ;
2013-07-31 17:14:10 +09:00
MODULE_DESCRIPTION ( " Designware PCIe host controller driver " ) ;
2013-06-21 16:24:54 +09:00
MODULE_LICENSE ( " GPL v2 " ) ;