2013-06-21 11:24:54 +04:00
/*
2013-07-31 12:14:10 +04:00
* Synopsys Designware PCIe host controller driver
2013-06-21 11:24:54 +04: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 10:54:59 +04:00
# include <linux/irq.h>
# include <linux/irqdomain.h>
2013-06-21 11:24:54 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2013-09-06 10:54:59 +04:00
# include <linux/msi.h>
2013-06-21 11:24:54 +04:00
# include <linux/of_address.h>
# include <linux/pci.h>
# include <linux/pci_regs.h>
# include <linux/types.h>
2013-07-31 12:14:10 +04:00
# include "pcie-designware.h"
2013-06-21 11:24:54 +04:00
/* Synopsis specific PCIE configuration registers */
# define PCIE_PORT_LINK_CONTROL 0x710
# define PORT_LINK_MODE_MASK (0x3f << 16)
2013-07-31 12:14:10 +04:00
# define PORT_LINK_MODE_1_LANES (0x1 << 16)
# define PORT_LINK_MODE_2_LANES (0x3 << 16)
2013-06-21 11:24:54 +04:00
# define PORT_LINK_MODE_4_LANES (0x7 << 16)
# define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
# define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
# define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
2013-07-31 12:14:10 +04: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)
2013-06-21 11:24:54 +04: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
2013-07-31 12:14:10 +04:00
static struct hw_pci dw_pci ;
2013-10-09 19:12:37 +04:00
static unsigned long global_io_offset ;
2013-06-21 11:24:54 +04:00
static inline struct pcie_port * sys_to_pcie ( struct pci_sys_data * sys )
{
return sys - > private_data ;
}
2013-12-11 13:38:32 +04:00
int dw_pcie_cfg_read ( void __iomem * addr , int where , int size , u32 * val )
2013-06-21 11:24:54 +04:00
{
* val = readl ( addr ) ;
if ( size = = 1 )
* val = ( * val > > ( 8 * ( where & 3 ) ) ) & 0xff ;
else if ( size = = 2 )
* val = ( * val > > ( 8 * ( where & 3 ) ) ) & 0xffff ;
else if ( size ! = 4 )
return PCIBIOS_BAD_REGISTER_NUMBER ;
return PCIBIOS_SUCCESSFUL ;
}
2013-12-11 13:38:32 +04:00
int dw_pcie_cfg_write ( void __iomem * addr , int where , int size , u32 val )
2013-06-21 11:24:54 +04:00
{
if ( size = = 4 )
writel ( val , addr ) ;
else if ( size = = 2 )
writew ( val , addr + ( where & 2 ) ) ;
else if ( size = = 1 )
writeb ( val , addr + ( where & 3 ) ) ;
else
return PCIBIOS_BAD_REGISTER_NUMBER ;
return PCIBIOS_SUCCESSFUL ;
}
2013-08-28 15:53:30 +04:00
static inline void dw_pcie_readl_rc ( struct pcie_port * pp , u32 reg , u32 * val )
2013-06-21 11:24:54 +04:00
{
2013-07-31 12:14:10 +04:00
if ( pp - > ops - > readl_rc )
2013-08-28 15:53:30 +04:00
pp - > ops - > readl_rc ( pp , pp - > dbi_base + reg , val ) ;
2013-07-31 12:14:10 +04:00
else
2013-08-28 15:53:30 +04:00
* val = readl ( pp - > dbi_base + reg ) ;
2013-06-21 11:24:54 +04:00
}
2013-08-28 15:53:30 +04:00
static inline void dw_pcie_writel_rc ( struct pcie_port * pp , u32 val , u32 reg )
2013-06-21 11:24:54 +04:00
{
2013-07-31 12:14:10 +04:00
if ( pp - > ops - > writel_rc )
2013-08-28 15:53:30 +04:00
pp - > ops - > writel_rc ( pp , val , pp - > dbi_base + reg ) ;
2013-07-31 12:14:10 +04:00
else
2013-08-28 15:53:30 +04:00
writel ( val , pp - > dbi_base + reg ) ;
2013-06-21 11:24:54 +04:00
}
2013-10-09 19:12:37 +04:00
static int dw_pcie_rd_own_conf ( struct pcie_port * pp , int where , int size ,
u32 * val )
2013-06-21 11:24:54 +04:00
{
int ret ;
2013-07-31 12:14:10 +04:00
if ( pp - > ops - > rd_own_conf )
ret = pp - > ops - > rd_own_conf ( pp , where , size , val ) ;
else
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_read ( pp - > dbi_base + ( where & ~ 0x3 ) , where ,
size , val ) ;
2013-07-31 12:14:10 +04:00
2013-06-21 11:24:54 +04:00
return ret ;
}
2013-10-09 19:12:37 +04:00
static int dw_pcie_wr_own_conf ( struct pcie_port * pp , int where , int size ,
u32 val )
2013-06-21 11:24:54 +04:00
{
int ret ;
2013-07-31 12:14:10 +04:00
if ( pp - > ops - > wr_own_conf )
ret = pp - > ops - > wr_own_conf ( pp , where , size , val ) ;
else
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_write ( pp - > dbi_base + ( where & ~ 0x3 ) , where ,
size , val ) ;
2013-07-31 12:14:10 +04:00
2013-06-21 11:24:54 +04:00
return ret ;
}
2013-09-06 10:54:59 +04:00
static struct irq_chip dw_msi_irq_chip = {
. name = " PCI-MSI " ,
. irq_enable = unmask_msi_irq ,
. irq_disable = mask_msi_irq ,
. irq_mask = mask_msi_irq ,
. irq_unmask = unmask_msi_irq ,
} ;
/* MSI int handler */
void dw_handle_msi_irq ( struct pcie_port * pp )
{
unsigned long val ;
2013-10-09 16:32:12 +04:00
int i , pos , irq ;
2013-09-06 10:54:59 +04: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 ) {
pos = 0 ;
while ( ( pos = find_next_bit ( & val , 32 , pos ) ) ! = 32 ) {
2013-10-09 16:32:12 +04:00
irq = irq_find_mapping ( pp - > irq_domain ,
i * 32 + pos ) ;
2013-12-12 22:29:03 +04:00
dw_pcie_wr_own_conf ( pp ,
PCIE_MSI_INTR0_STATUS + i * 12 ,
4 , 1 < < pos ) ;
2013-10-09 16:32:12 +04:00
generic_handle_irq ( irq ) ;
2013-09-06 10:54:59 +04:00
pos + + ;
}
}
}
}
void dw_pcie_msi_init ( struct pcie_port * pp )
{
pp - > msi_data = __get_free_pages ( GFP_KERNEL , 0 ) ;
/* program the msi_data */
dw_pcie_wr_own_conf ( pp , PCIE_MSI_ADDR_LO , 4 ,
virt_to_phys ( ( void * ) pp - > msi_data ) ) ;
dw_pcie_wr_own_conf ( pp , PCIE_MSI_ADDR_HI , 4 , 0 ) ;
}
static int find_valid_pos0 ( struct pcie_port * pp , int msgvec , int pos , int * pos0 )
{
int flag = 1 ;
do {
pos = find_next_zero_bit ( pp - > msi_irq_in_use ,
MAX_MSI_IRQS , pos ) ;
/*if you have reached to the end then get out from here.*/
if ( pos = = MAX_MSI_IRQS )
return - ENOSPC ;
/*
* Check if this position is at correct offset . nvec is always a
2013-11-14 22:28:18 +04:00
* power of two . pos0 must be nvec bit aligned .
2013-09-06 10:54:59 +04:00
*/
if ( pos % msgvec )
pos + = msgvec - ( pos % msgvec ) ;
else
flag = 0 ;
} while ( flag ) ;
* pos0 = pos ;
return 0 ;
}
2013-11-29 17:35:24 +04:00
static void clear_irq_range ( struct pcie_port * pp , unsigned int irq_base ,
2013-12-27 04:30:25 +04:00
unsigned int nvec , unsigned int pos )
2013-11-29 17:35:24 +04:00
{
unsigned int i , res , bit , val ;
2013-12-10 02:11:25 +04:00
for ( i = 0 ; i < nvec ; i + + ) {
2013-11-29 17:35:24 +04:00
irq_set_msi_desc_off ( irq_base , i , NULL ) ;
clear_bit ( pos + i , pp - > msi_irq_in_use ) ;
2013-12-27 04:30:25 +04:00
/* Disable corresponding interrupt on MSI controller */
2013-11-29 17:35:24 +04:00
res = ( ( pos + i ) / 32 ) * 12 ;
bit = ( pos + i ) % 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 10:54:59 +04:00
static int assign_irq ( int no_irqs , struct msi_desc * desc , int * pos )
{
int res , bit , irq , pos0 , pos1 , i ;
u32 val ;
struct pcie_port * pp = sys_to_pcie ( desc - > dev - > bus - > sysdata ) ;
if ( ! pp ) {
BUG ( ) ;
return - EINVAL ;
}
pos0 = find_first_zero_bit ( pp - > msi_irq_in_use ,
MAX_MSI_IRQS ) ;
if ( pos0 % no_irqs ) {
if ( find_valid_pos0 ( pp , no_irqs , pos0 , & pos0 ) )
goto no_valid_irq ;
}
if ( no_irqs > 1 ) {
pos1 = find_next_bit ( pp - > msi_irq_in_use ,
MAX_MSI_IRQS , pos0 ) ;
/* there must be nvec number of consecutive free bits */
while ( ( pos1 - pos0 ) < no_irqs ) {
if ( find_valid_pos0 ( pp , no_irqs , pos1 , & pos0 ) )
goto no_valid_irq ;
pos1 = find_next_bit ( pp - > msi_irq_in_use ,
MAX_MSI_IRQS , pos0 ) ;
}
}
2013-10-09 16:32:12 +04:00
irq = irq_find_mapping ( pp - > irq_domain , pos0 ) ;
if ( ! irq )
2013-09-06 10:54:59 +04:00
goto no_valid_irq ;
2013-11-29 17:35:24 +04: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-10 02:11:25 +04:00
for ( i = 0 ; i < no_irqs ; i + + ) {
2013-11-29 17:35:24 +04: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 10:54:59 +04:00
set_bit ( pos0 + i , pp - > msi_irq_in_use ) ;
/*Enable corresponding interrupt in MSI interrupt controller */
res = ( ( pos0 + i ) / 32 ) * 12 ;
bit = ( pos0 + i ) % 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 ) ;
}
* pos = pos0 ;
return irq ;
no_valid_irq :
* pos = pos0 ;
return - ENOSPC ;
}
static void clear_irq ( unsigned int irq )
{
2013-11-29 17:35:24 +04:00
unsigned int pos , nvec ;
2013-09-06 10:54:59 +04:00
struct msi_desc * msi ;
struct pcie_port * pp ;
2013-10-09 16:32:12 +04:00
struct irq_data * data = irq_get_irq_data ( irq ) ;
2013-09-06 10:54:59 +04:00
/* get the port structure */
2014-02-24 01:40:11 +04:00
msi = irq_data_get_msi ( data ) ;
2013-09-06 10:54:59 +04:00
pp = sys_to_pcie ( msi - > dev - > bus - > sysdata ) ;
if ( ! pp ) {
BUG ( ) ;
return ;
}
2013-11-29 17:35:24 +04:00
/* undo what was done in assign_irq */
2013-10-09 16:32:12 +04:00
pos = data - > hwirq ;
2013-11-29 17:35:24 +04:00
nvec = 1 < < msi - > msi_attrib . multiple ;
2013-09-06 10:54:59 +04:00
2013-11-29 17:35:24 +04:00
clear_irq_range ( pp , irq , nvec , pos ) ;
2013-09-06 10:54:59 +04:00
2013-11-29 17:35:24 +04:00
/* all irqs cleared; reset attributes */
msi - > irq = 0 ;
msi - > msi_attrib . multiple = 0 ;
2013-09-06 10:54:59 +04:00
}
static int dw_msi_setup_irq ( struct msi_chip * chip , struct pci_dev * pdev ,
struct msi_desc * desc )
{
int irq , pos , msgvec ;
u16 msg_ctr ;
struct msi_msg msg ;
struct pcie_port * pp = sys_to_pcie ( pdev - > bus - > sysdata ) ;
if ( ! pp ) {
BUG ( ) ;
return - EINVAL ;
}
pci_read_config_word ( pdev , desc - > msi_attrib . pos + PCI_MSI_FLAGS ,
& msg_ctr ) ;
msgvec = ( msg_ctr & PCI_MSI_FLAGS_QSIZE ) > > 4 ;
if ( msgvec = = 0 )
msgvec = ( msg_ctr & PCI_MSI_FLAGS_QMASK ) > > 1 ;
if ( msgvec > 5 )
msgvec = 0 ;
irq = assign_irq ( ( 1 < < msgvec ) , desc , & pos ) ;
if ( irq < 0 )
return irq ;
2013-11-29 17:35:25 +04:00
/*
* write_msi_msg ( ) will update PCI_MSI_FLAGS so there is
* no need to explicitly call pci_write_config_word ( ) .
*/
2013-09-06 10:54:59 +04:00
desc - > msi_attrib . multiple = msgvec ;
msg . address_lo = virt_to_phys ( ( void * ) pp - > msi_data ) ;
msg . address_hi = 0x0 ;
msg . data = pos ;
write_msi_msg ( irq , & msg ) ;
return 0 ;
}
static void dw_msi_teardown_irq ( struct msi_chip * chip , unsigned int irq )
{
clear_irq ( irq ) ;
}
static struct msi_chip dw_pcie_msi_chip = {
. setup_irq = dw_msi_setup_irq ,
. teardown_irq = dw_msi_teardown_irq ,
} ;
2013-07-31 12:14:10 +04:00
int dw_pcie_link_up ( struct pcie_port * pp )
{
if ( pp - > ops - > link_up )
return pp - > ops - > link_up ( pp ) ;
else
return 0 ;
}
2013-09-06 10:54:59 +04: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 ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
return 0 ;
}
static const struct irq_domain_ops msi_domain_ops = {
. map = dw_pcie_msi_map ,
} ;
2013-07-31 12:14:10 +04:00
int __init dw_pcie_host_init ( struct pcie_port * pp )
{
struct device_node * np = pp - > dev - > of_node ;
struct of_pci_range range ;
struct of_pci_range_parser parser ;
u32 val ;
2013-10-09 16:32:12 +04:00
int i ;
2013-09-06 10:54:59 +04:00
2013-07-31 12:14:10 +04:00
if ( of_pci_range_parser_init ( & parser , np ) ) {
dev_err ( pp - > dev , " missing ranges property \n " ) ;
return - EINVAL ;
}
/* Get the I/O and memory ranges from DT */
for_each_of_pci_range ( & parser , & range ) {
unsigned long restype = range . flags & IORESOURCE_TYPE_BITS ;
if ( restype = = IORESOURCE_IO ) {
of_pci_range_to_resource ( & range , np , & pp - > io ) ;
pp - > io . name = " I/O " ;
pp - > io . start = max_t ( resource_size_t ,
PCIBIOS_MIN_IO ,
range . pci_addr + global_io_offset ) ;
pp - > io . end = min_t ( resource_size_t ,
IO_SPACE_LIMIT ,
range . pci_addr + range . size
+ global_io_offset ) ;
pp - > config . io_size = resource_size ( & pp - > io ) ;
pp - > config . io_bus_addr = range . pci_addr ;
2013-12-11 13:38:33 +04:00
pp - > io_base = range . cpu_addr ;
2013-07-31 12:14:10 +04:00
}
if ( restype = = IORESOURCE_MEM ) {
of_pci_range_to_resource ( & range , np , & pp - > mem ) ;
pp - > mem . name = " MEM " ;
pp - > config . mem_size = resource_size ( & pp - > mem ) ;
pp - > config . mem_bus_addr = range . pci_addr ;
}
if ( restype = = 0 ) {
of_pci_range_to_resource ( & range , np , & pp - > cfg ) ;
pp - > config . cfg0_size = resource_size ( & pp - > cfg ) / 2 ;
pp - > config . cfg1_size = resource_size ( & pp - > cfg ) / 2 ;
}
}
if ( ! pp - > dbi_base ) {
pp - > dbi_base = devm_ioremap ( pp - > dev , pp - > cfg . start ,
resource_size ( & pp - > cfg ) ) ;
if ( ! pp - > dbi_base ) {
dev_err ( pp - > dev , " error with ioremap \n " ) ;
return - ENOMEM ;
}
}
pp - > cfg0_base = pp - > cfg . start ;
pp - > cfg1_base = pp - > cfg . start + pp - > config . cfg0_size ;
pp - > mem_base = pp - > mem . start ;
pp - > va_cfg0_base = devm_ioremap ( pp - > dev , pp - > cfg0_base ,
pp - > config . cfg0_size ) ;
if ( ! pp - > va_cfg0_base ) {
dev_err ( pp - > dev , " error with ioremap in function \n " ) ;
return - ENOMEM ;
}
pp - > va_cfg1_base = devm_ioremap ( pp - > dev , pp - > cfg1_base ,
pp - > config . cfg1_size ) ;
if ( ! pp - > va_cfg1_base ) {
dev_err ( pp - > dev , " error with ioremap \n " ) ;
return - ENOMEM ;
}
if ( of_property_read_u32 ( np , " num-lanes " , & pp - > lanes ) ) {
dev_err ( pp - > dev , " Failed to parse the number of lanes \n " ) ;
return - EINVAL ;
}
2013-09-06 10:54:59 +04:00
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
2013-10-09 16:32:12 +04:00
pp - > irq_domain = irq_domain_add_linear ( pp - > dev - > of_node ,
2013-09-06 10:54:59 +04:00
MAX_MSI_IRQS , & msi_domain_ops ,
& dw_pcie_msi_chip ) ;
2013-10-09 16:32:12 +04:00
if ( ! pp - > irq_domain ) {
2013-09-06 10:54:59 +04:00
dev_err ( pp - > dev , " irq domain init failed \n " ) ;
return - ENXIO ;
}
2013-10-09 16:32:12 +04:00
for ( i = 0 ; i < MAX_MSI_IRQS ; i + + )
irq_create_mapping ( pp - > irq_domain , i ) ;
2013-09-06 10:54:59 +04:00
}
2013-07-31 12:14:10 +04:00
if ( pp - > ops - > host_init )
pp - > ops - > host_init ( pp ) ;
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 ) ;
dw_pci . nr_controllers = 1 ;
dw_pci . private_data = ( void * * ) & pp ;
pci_common_init ( & dw_pci ) ;
pci_assign_unassigned_resources ( ) ;
# ifdef CONFIG_PCI_DOMAINS
dw_pci . domain + + ;
# endif
return 0 ;
}
static void dw_pcie_prog_viewport_cfg0 ( struct pcie_port * pp , u32 busdev )
2013-06-21 11:24:54 +04:00
{
/* Program viewport 0 : OUTBOUND : CFG0 */
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0 ,
PCIE_ATU_VIEWPORT ) ;
dw_pcie_writel_rc ( pp , pp - > cfg0_base , PCIE_ATU_LOWER_BASE ) ;
dw_pcie_writel_rc ( pp , ( pp - > cfg0_base > > 32 ) , PCIE_ATU_UPPER_BASE ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , pp - > cfg0_base + pp - > config . cfg0_size - 1 ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_LIMIT ) ;
dw_pcie_writel_rc ( pp , busdev , PCIE_ATU_LOWER_TARGET ) ;
dw_pcie_writel_rc ( pp , 0 , PCIE_ATU_UPPER_TARGET ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_TYPE_CFG0 , PCIE_ATU_CR1 ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_ENABLE , PCIE_ATU_CR2 ) ;
2013-06-21 11:24:54 +04:00
}
2013-07-31 12:14:10 +04:00
static void dw_pcie_prog_viewport_cfg1 ( struct pcie_port * pp , u32 busdev )
2013-06-21 11:24:54 +04:00
{
/* Program viewport 1 : OUTBOUND : CFG1 */
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1 ,
PCIE_ATU_VIEWPORT ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_TYPE_CFG1 , PCIE_ATU_CR1 ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_ENABLE , PCIE_ATU_CR2 ) ;
dw_pcie_writel_rc ( pp , pp - > cfg1_base , PCIE_ATU_LOWER_BASE ) ;
dw_pcie_writel_rc ( pp , ( pp - > cfg1_base > > 32 ) , PCIE_ATU_UPPER_BASE ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , pp - > cfg1_base + pp - > config . cfg1_size - 1 ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_LIMIT ) ;
dw_pcie_writel_rc ( pp , busdev , PCIE_ATU_LOWER_TARGET ) ;
dw_pcie_writel_rc ( pp , 0 , PCIE_ATU_UPPER_TARGET ) ;
2013-06-21 11:24:54 +04:00
}
2013-07-31 12:14:10 +04:00
static void dw_pcie_prog_viewport_mem_outbound ( struct pcie_port * pp )
2013-06-21 11:24:54 +04:00
{
/* Program viewport 0 : OUTBOUND : MEM */
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0 ,
PCIE_ATU_VIEWPORT ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_TYPE_MEM , PCIE_ATU_CR1 ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_ENABLE , PCIE_ATU_CR2 ) ;
dw_pcie_writel_rc ( pp , pp - > mem_base , PCIE_ATU_LOWER_BASE ) ;
dw_pcie_writel_rc ( pp , ( pp - > mem_base > > 32 ) , PCIE_ATU_UPPER_BASE ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , pp - > mem_base + pp - > config . mem_size - 1 ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_LIMIT ) ;
dw_pcie_writel_rc ( pp , pp - > config . mem_bus_addr , PCIE_ATU_LOWER_TARGET ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , upper_32_bits ( pp - > config . mem_bus_addr ) ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_UPPER_TARGET ) ;
2013-06-21 11:24:54 +04:00
}
2013-07-31 12:14:10 +04:00
static void dw_pcie_prog_viewport_io_outbound ( struct pcie_port * pp )
2013-06-21 11:24:54 +04:00
{
/* Program viewport 1 : OUTBOUND : IO */
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1 ,
PCIE_ATU_VIEWPORT ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_TYPE_IO , PCIE_ATU_CR1 ) ;
dw_pcie_writel_rc ( pp , PCIE_ATU_ENABLE , PCIE_ATU_CR2 ) ;
dw_pcie_writel_rc ( pp , pp - > io_base , PCIE_ATU_LOWER_BASE ) ;
dw_pcie_writel_rc ( pp , ( pp - > io_base > > 32 ) , PCIE_ATU_UPPER_BASE ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , pp - > io_base + pp - > config . io_size - 1 ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_LIMIT ) ;
dw_pcie_writel_rc ( pp , pp - > config . io_bus_addr , PCIE_ATU_LOWER_TARGET ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_writel_rc ( pp , upper_32_bits ( pp - > config . io_bus_addr ) ,
2013-08-28 15:53:30 +04:00
PCIE_ATU_UPPER_TARGET ) ;
2013-06-21 11:24:54 +04:00
}
2013-07-31 12:14:10 +04:00
static int dw_pcie_rd_other_conf ( struct pcie_port * pp , struct pci_bus * bus ,
2013-06-21 11:24:54 +04:00
u32 devfn , int where , int size , u32 * val )
{
int ret = PCIBIOS_SUCCESSFUL ;
u32 address , busdev ;
busdev = PCIE_ATU_BUS ( bus - > number ) | PCIE_ATU_DEV ( PCI_SLOT ( devfn ) ) |
PCIE_ATU_FUNC ( PCI_FUNC ( devfn ) ) ;
address = where & ~ 0x3 ;
if ( bus - > parent - > number = = pp - > root_bus_nr ) {
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_cfg0 ( pp , busdev ) ;
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_read ( pp - > va_cfg0_base + address , where , size ,
val ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_mem_outbound ( pp ) ;
2013-06-21 11:24:54 +04:00
} else {
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_cfg1 ( pp , busdev ) ;
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_read ( pp - > va_cfg1_base + address , where , size ,
val ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_io_outbound ( pp ) ;
2013-06-21 11:24:54 +04:00
}
return ret ;
}
2013-07-31 12:14:10 +04:00
static int dw_pcie_wr_other_conf ( struct pcie_port * pp , struct pci_bus * bus ,
2013-06-21 11:24:54 +04:00
u32 devfn , int where , int size , u32 val )
{
int ret = PCIBIOS_SUCCESSFUL ;
u32 address , busdev ;
busdev = PCIE_ATU_BUS ( bus - > number ) | PCIE_ATU_DEV ( PCI_SLOT ( devfn ) ) |
PCIE_ATU_FUNC ( PCI_FUNC ( devfn ) ) ;
address = where & ~ 0x3 ;
if ( bus - > parent - > number = = pp - > root_bus_nr ) {
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_cfg0 ( pp , busdev ) ;
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_write ( pp - > va_cfg0_base + address , where , size ,
val ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_mem_outbound ( pp ) ;
2013-06-21 11:24:54 +04:00
} else {
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_cfg1 ( pp , busdev ) ;
2013-12-11 13:38:32 +04:00
ret = dw_pcie_cfg_write ( pp - > va_cfg1_base + address , where , size ,
val ) ;
2013-07-31 12:14:10 +04:00
dw_pcie_prog_viewport_io_outbound ( pp ) ;
2013-06-21 11:24:54 +04:00
}
return ret ;
}
2013-07-31 12:14:10 +04:00
static int dw_pcie_valid_config ( struct pcie_port * pp ,
2013-06-21 11:24:54 +04: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 12:14:10 +04:00
if ( ! dw_pcie_link_up ( pp ) )
2013-06-21 11:24:54 +04: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 12:14:10 +04:00
static int dw_pcie_rd_conf ( struct pci_bus * bus , u32 devfn , int where ,
2013-06-21 11:24:54 +04:00
int size , u32 * val )
{
struct pcie_port * pp = sys_to_pcie ( bus - > sysdata ) ;
unsigned long flags ;
int ret ;
if ( ! pp ) {
BUG ( ) ;
return - EINVAL ;
}
2013-07-31 12:14:10 +04:00
if ( dw_pcie_valid_config ( pp , bus , PCI_SLOT ( devfn ) ) = = 0 ) {
2013-06-21 11:24:54 +04:00
* val = 0xffffffff ;
return PCIBIOS_DEVICE_NOT_FOUND ;
}
spin_lock_irqsave ( & pp - > conf_lock , flags ) ;
if ( bus - > number ! = pp - > root_bus_nr )
2013-07-31 12:14:10 +04:00
ret = dw_pcie_rd_other_conf ( pp , bus , devfn ,
2013-06-21 11:24:54 +04:00
where , size , val ) ;
else
2013-07-31 12:14:10 +04:00
ret = dw_pcie_rd_own_conf ( pp , where , size , val ) ;
2013-06-21 11:24:54 +04:00
spin_unlock_irqrestore ( & pp - > conf_lock , flags ) ;
return ret ;
}
2013-07-31 12:14:10 +04:00
static int dw_pcie_wr_conf ( struct pci_bus * bus , u32 devfn ,
2013-06-21 11:24:54 +04:00
int where , int size , u32 val )
{
struct pcie_port * pp = sys_to_pcie ( bus - > sysdata ) ;
unsigned long flags ;
int ret ;
if ( ! pp ) {
BUG ( ) ;
return - EINVAL ;
}
2013-07-31 12:14:10 +04:00
if ( dw_pcie_valid_config ( pp , bus , PCI_SLOT ( devfn ) ) = = 0 )
2013-06-21 11:24:54 +04:00
return PCIBIOS_DEVICE_NOT_FOUND ;
spin_lock_irqsave ( & pp - > conf_lock , flags ) ;
if ( bus - > number ! = pp - > root_bus_nr )
2013-07-31 12:14:10 +04:00
ret = dw_pcie_wr_other_conf ( pp , bus , devfn ,
2013-06-21 11:24:54 +04:00
where , size , val ) ;
else
2013-07-31 12:14:10 +04:00
ret = dw_pcie_wr_own_conf ( pp , where , size , val ) ;
2013-06-21 11:24:54 +04:00
spin_unlock_irqrestore ( & pp - > conf_lock , flags ) ;
return ret ;
}
2013-07-31 12:14:10 +04:00
static struct pci_ops dw_pcie_ops = {
. read = dw_pcie_rd_conf ,
. write = dw_pcie_wr_conf ,
2013-06-21 11:24:54 +04:00
} ;
2013-10-09 19:12:37 +04:00
static int dw_pcie_setup ( int nr , struct pci_sys_data * sys )
2013-07-31 12:14:10 +04:00
{
struct pcie_port * pp ;
pp = sys_to_pcie ( sys ) ;
if ( ! pp )
return 0 ;
if ( global_io_offset < SZ_1M & & pp - > config . io_size > 0 ) {
sys - > io_offset = global_io_offset - pp - > config . io_bus_addr ;
2013-12-11 13:38:33 +04:00
pci_ioremap_io ( global_io_offset , pp - > io_base ) ;
2013-07-31 12:14:10 +04:00
global_io_offset + = SZ_64K ;
pci_add_resource_offset ( & sys - > resources , & pp - > io ,
sys - > io_offset ) ;
}
sys - > mem_offset = pp - > mem . start - pp - > config . mem_bus_addr ;
pci_add_resource_offset ( & sys - > resources , & pp - > mem , sys - > mem_offset ) ;
return 1 ;
}
2013-10-09 19:12:37 +04:00
static struct pci_bus * dw_pcie_scan_bus ( int nr , struct pci_sys_data * sys )
2013-06-21 11:24:54 +04:00
{
struct pci_bus * bus ;
struct pcie_port * pp = sys_to_pcie ( sys ) ;
if ( pp ) {
pp - > root_bus_nr = sys - > busnr ;
2013-07-31 12:14:10 +04:00
bus = pci_scan_root_bus ( NULL , sys - > busnr , & dw_pcie_ops ,
2013-06-21 11:24:54 +04:00
sys , & sys - > resources ) ;
} else {
bus = NULL ;
BUG ( ) ;
}
return bus ;
}
2013-10-09 19:12:37 +04:00
static int dw_pcie_map_irq ( const struct pci_dev * dev , u8 slot , u8 pin )
2013-06-21 11:24:54 +04:00
{
struct pcie_port * pp = sys_to_pcie ( dev - > bus - > sysdata ) ;
return pp - > irq ;
}
2013-09-06 10:54:59 +04:00
static void dw_pcie_add_bus ( struct pci_bus * bus )
{
if ( IS_ENABLED ( CONFIG_PCI_MSI ) ) {
struct pcie_port * pp = sys_to_pcie ( bus - > sysdata ) ;
dw_pcie_msi_chip . dev = pp - > dev ;
bus - > msi = & dw_pcie_msi_chip ;
}
}
2013-07-31 12:14:10 +04:00
static struct hw_pci dw_pci = {
. setup = dw_pcie_setup ,
. scan = dw_pcie_scan_bus ,
. map_irq = dw_pcie_map_irq ,
2013-09-06 10:54:59 +04:00
. add_bus = dw_pcie_add_bus ,
2013-06-21 11:24:54 +04:00
} ;
2013-07-31 12:14:10 +04:00
void dw_pcie_setup_rc ( struct pcie_port * pp )
2013-06-21 11:24:54 +04:00
{
struct pcie_port_info * config = & pp - > config ;
u32 val ;
u32 membase ;
u32 memlimit ;
/* set the number of lines as 4 */
2013-08-28 15:53:30 +04:00
dw_pcie_readl_rc ( pp , PCIE_PORT_LINK_CONTROL , & val ) ;
2013-06-21 11:24:54 +04:00
val & = ~ PORT_LINK_MODE_MASK ;
2013-07-31 12:14:10 +04: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 ;
}
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCIE_PORT_LINK_CONTROL ) ;
2013-06-21 11:24:54 +04:00
/* set link width speed control register */
2013-08-28 15:53:30 +04:00
dw_pcie_readl_rc ( pp , PCIE_LINK_WIDTH_SPEED_CONTROL , & val ) ;
2013-06-21 11:24:54 +04:00
val & = ~ PORT_LOGIC_LINK_WIDTH_MASK ;
2013-07-31 12:14:10 +04: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 ;
}
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCIE_LINK_WIDTH_SPEED_CONTROL ) ;
2013-06-21 11:24:54 +04:00
/* setup RC BARs */
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , 0x00000004 , PCI_BASE_ADDRESS_0 ) ;
2014-02-19 16:04:35 +04:00
dw_pcie_writel_rc ( pp , 0x00000000 , PCI_BASE_ADDRESS_1 ) ;
2013-06-21 11:24:54 +04:00
/* setup interrupt pins */
2013-08-28 15:53:30 +04:00
dw_pcie_readl_rc ( pp , PCI_INTERRUPT_LINE , & val ) ;
2013-06-21 11:24:54 +04:00
val & = 0xffff00ff ;
val | = 0x00000100 ;
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCI_INTERRUPT_LINE ) ;
2013-06-21 11:24:54 +04:00
/* setup bus numbers */
2013-08-28 15:53:30 +04:00
dw_pcie_readl_rc ( pp , PCI_PRIMARY_BUS , & val ) ;
2013-06-21 11:24:54 +04:00
val & = 0xff000000 ;
val | = 0x00010100 ;
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCI_PRIMARY_BUS ) ;
2013-06-21 11:24:54 +04:00
/* setup memory base, memory limit */
membase = ( ( u32 ) pp - > mem_base & 0xfff00000 ) > > 16 ;
memlimit = ( config - > mem_size + ( u32 ) pp - > mem_base ) & 0xfff00000 ;
val = memlimit | membase ;
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCI_MEMORY_BASE ) ;
2013-06-21 11:24:54 +04:00
/* setup command register */
2013-08-28 15:53:30 +04:00
dw_pcie_readl_rc ( pp , PCI_COMMAND , & val ) ;
2013-06-21 11:24:54 +04:00
val & = 0xffff0000 ;
val | = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR ;
2013-08-28 15:53:30 +04:00
dw_pcie_writel_rc ( pp , val , PCI_COMMAND ) ;
2013-06-21 11:24:54 +04:00
}
MODULE_AUTHOR ( " Jingoo Han <jg1.han@samsung.com> " ) ;
2013-07-31 12:14:10 +04:00
MODULE_DESCRIPTION ( " Designware PCIe host controller driver " ) ;
2013-06-21 11:24:54 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;