2011-11-17 14:25:09 +00:00
/*
2012-03-14 10:36:14 +01:00
* Atheros AR724X PCI host controller driver
2011-11-17 14:25:09 +00:00
*
* Copyright ( C ) 2011 René Bolldorf < xsecute @ googlemail . com >
2012-03-14 10:36:14 +01:00
* Copyright ( C ) 2009 - 2011 Gabor Juhos < juhosg @ openwrt . org >
2011-11-17 14:25:09 +00:00
*
* 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 .
*/
2012-03-14 10:36:07 +01:00
# include <linux/irq.h>
2011-11-17 14:25:09 +00:00
# include <linux/pci.h>
2013-02-02 11:40:42 +00:00
# include <linux/module.h>
# include <linux/platform_device.h>
2012-03-14 10:36:05 +01:00
# include <asm/mach-ath79/ath79.h>
2012-03-14 10:36:07 +01:00
# include <asm/mach-ath79/ar71xx_regs.h>
2011-11-17 14:25:09 +00:00
2012-08-23 15:35:26 +02:00
# define AR724X_PCI_REG_RESET 0x18
2012-03-14 10:36:07 +01:00
# define AR724X_PCI_REG_INT_STATUS 0x4c
# define AR724X_PCI_REG_INT_MASK 0x50
2012-08-23 15:35:26 +02:00
# define AR724X_PCI_RESET_LINK_UP BIT(0)
2012-03-14 10:36:07 +01:00
# define AR724X_PCI_INT_DEV0 BIT(14)
# define AR724X_PCI_IRQ_COUNT 1
2012-03-14 10:36:05 +01:00
# define AR7240_BAR0_WAR_VALUE 0xffff
2013-02-03 14:52:47 +00:00
# define AR724X_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \
PCI_COMMAND_MASTER | \
PCI_COMMAND_INVALIDATE | \
PCI_COMMAND_PARITY | \
PCI_COMMAND_SERR | \
PCI_COMMAND_FAST_BACK )
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller {
void __iomem * devcfg_base ;
void __iomem * ctrl_base ;
2013-02-03 14:52:47 +00:00
void __iomem * crp_base ;
2011-11-17 14:25:09 +00:00
2013-02-03 09:58:38 +00:00
int irq ;
2013-02-03 10:00:16 +00:00
int irq_base ;
2013-02-03 09:58:38 +00:00
bool link_up ;
bool bar0_is_cached ;
u32 bar0_value ;
struct pci_controller pci_controller ;
2013-02-03 09:59:45 +00:00
struct resource io_res ;
struct resource mem_res ;
2013-02-03 09:58:38 +00:00
} ;
2012-08-23 15:35:26 +02:00
2013-02-03 09:58:38 +00:00
static inline bool ar724x_pci_check_link ( struct ar724x_pci_controller * apc )
2012-08-23 15:35:26 +02:00
{
u32 reset ;
2013-02-03 09:58:38 +00:00
reset = __raw_readl ( apc - > ctrl_base + AR724X_PCI_REG_RESET ) ;
2012-08-23 15:35:26 +02:00
return reset & AR724X_PCI_RESET_LINK_UP ;
}
2012-03-14 10:36:05 +01:00
2013-02-03 09:58:38 +00:00
static inline struct ar724x_pci_controller *
pci_bus_to_ar724x_controller ( struct pci_bus * bus )
{
struct pci_controller * hose ;
hose = ( struct pci_controller * ) bus - > sysdata ;
return container_of ( hose , struct ar724x_pci_controller , pci_controller ) ;
}
2013-02-03 14:52:47 +00:00
static int ar724x_pci_local_write ( struct ar724x_pci_controller * apc ,
int where , int size , u32 value )
{
void __iomem * base ;
u32 data ;
int s ;
WARN_ON ( where & ( size - 1 ) ) ;
if ( ! apc - > link_up )
return PCIBIOS_DEVICE_NOT_FOUND ;
base = apc - > crp_base ;
data = __raw_readl ( base + ( where & ~ 3 ) ) ;
switch ( size ) {
case 1 :
s = ( ( where & 3 ) * 8 ) ;
data & = ~ ( 0xff < < s ) ;
data | = ( ( value & 0xff ) < < s ) ;
break ;
case 2 :
s = ( ( where & 2 ) * 8 ) ;
data & = ~ ( 0xffff < < s ) ;
data | = ( ( value & 0xffff ) < < s ) ;
break ;
case 4 :
data = value ;
break ;
default :
return PCIBIOS_BAD_REGISTER_NUMBER ;
}
__raw_writel ( data , base + ( where & ~ 3 ) ) ;
/* flush write */
__raw_readl ( base + ( where & ~ 3 ) ) ;
return PCIBIOS_SUCCESSFUL ;
}
2012-03-14 10:29:26 +01:00
static int ar724x_pci_read ( struct pci_bus * bus , unsigned int devfn , int where ,
2011-11-17 14:25:09 +00:00
int size , uint32_t * value )
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2012-03-14 10:29:27 +01:00
void __iomem * base ;
2012-03-14 10:36:04 +01:00
u32 data ;
2011-11-17 14:25:09 +00:00
2013-02-03 09:58:38 +00:00
apc = pci_bus_to_ar724x_controller ( bus ) ;
if ( ! apc - > link_up )
2012-08-23 15:35:26 +02:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2011-11-17 14:25:09 +00:00
if ( devfn )
return PCIBIOS_DEVICE_NOT_FOUND ;
2013-02-03 09:58:38 +00:00
base = apc - > devcfg_base ;
2012-03-14 10:36:04 +01:00
data = __raw_readl ( base + ( where & ~ 3 ) ) ;
2011-11-17 14:25:09 +00:00
switch ( size ) {
case 1 :
2012-03-14 10:36:04 +01:00
if ( where & 1 )
data > > = 8 ;
if ( where & 2 )
data > > = 16 ;
data & = 0xff ;
2011-11-17 14:25:09 +00:00
break ;
case 2 :
2012-03-14 10:36:04 +01:00
if ( where & 2 )
data > > = 16 ;
data & = 0xffff ;
2011-11-17 14:25:09 +00:00
break ;
case 4 :
break ;
default :
return PCIBIOS_BAD_REGISTER_NUMBER ;
}
2012-03-14 10:36:05 +01:00
if ( where = = PCI_BASE_ADDRESS_0 & & size = = 4 & &
2013-02-03 09:58:38 +00:00
apc - > bar0_is_cached ) {
2012-03-14 10:36:05 +01:00
/* use the cached value */
2013-02-03 09:58:38 +00:00
* value = apc - > bar0_value ;
2012-03-14 10:36:05 +01:00
} else {
* value = data ;
}
2011-11-17 14:25:09 +00:00
return PCIBIOS_SUCCESSFUL ;
}
2012-03-14 10:29:26 +01:00
static int ar724x_pci_write ( struct pci_bus * bus , unsigned int devfn , int where ,
2011-11-17 14:25:09 +00:00
int size , uint32_t value )
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2012-03-14 10:29:27 +01:00
void __iomem * base ;
2012-03-14 10:36:04 +01:00
u32 data ;
int s ;
2011-11-17 14:25:09 +00:00
2013-02-03 09:58:38 +00:00
apc = pci_bus_to_ar724x_controller ( bus ) ;
if ( ! apc - > link_up )
2012-08-23 15:35:26 +02:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2011-11-17 14:25:09 +00:00
if ( devfn )
return PCIBIOS_DEVICE_NOT_FOUND ;
2012-03-14 10:36:05 +01:00
if ( soc_is_ar7240 ( ) & & where = = PCI_BASE_ADDRESS_0 & & size = = 4 ) {
if ( value ! = 0xffffffff ) {
/*
* WAR for a hw issue . If the BAR0 register of the
* device is set to the proper base address , the
* memory space of the device is not accessible .
*
* Cache the intended value so it can be read back ,
* and write a SoC specific constant value to the
* BAR0 register in order to make the device memory
* accessible .
*/
2013-02-03 09:58:38 +00:00
apc - > bar0_is_cached = true ;
apc - > bar0_value = value ;
2012-03-14 10:36:05 +01:00
value = AR7240_BAR0_WAR_VALUE ;
} else {
2013-02-03 09:58:38 +00:00
apc - > bar0_is_cached = false ;
2012-03-14 10:36:05 +01:00
}
}
2013-02-03 09:58:38 +00:00
base = apc - > devcfg_base ;
2012-03-14 10:36:04 +01:00
data = __raw_readl ( base + ( where & ~ 3 ) ) ;
2011-11-17 14:25:09 +00:00
switch ( size ) {
case 1 :
2012-03-14 10:36:04 +01:00
s = ( ( where & 3 ) * 8 ) ;
data & = ~ ( 0xff < < s ) ;
data | = ( ( value & 0xff ) < < s ) ;
2011-11-17 14:25:09 +00:00
break ;
case 2 :
2012-03-14 10:36:04 +01:00
s = ( ( where & 2 ) * 8 ) ;
data & = ~ ( 0xffff < < s ) ;
data | = ( ( value & 0xffff ) < < s ) ;
2011-11-17 14:25:09 +00:00
break ;
case 4 :
2012-03-14 10:36:04 +01:00
data = value ;
2011-11-17 14:25:09 +00:00
break ;
default :
return PCIBIOS_BAD_REGISTER_NUMBER ;
}
2012-03-14 10:36:04 +01:00
__raw_writel ( data , base + ( where & ~ 3 ) ) ;
/* flush write */
__raw_readl ( base + ( where & ~ 3 ) ) ;
2011-11-17 14:25:09 +00:00
return PCIBIOS_SUCCESSFUL ;
}
2012-03-14 10:29:26 +01:00
static struct pci_ops ar724x_pci_ops = {
. read = ar724x_pci_read ,
. write = ar724x_pci_write ,
2011-11-17 14:25:09 +00:00
} ;
2015-09-14 10:42:37 +02:00
static void ar724x_pci_irq_handler ( struct irq_desc * desc )
2012-03-14 10:36:07 +01:00
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2012-03-14 10:36:07 +01:00
void __iomem * base ;
u32 pending ;
2015-05-20 17:59:51 +08:00
apc = irq_desc_get_handler_data ( desc ) ;
2013-02-03 09:58:38 +00:00
base = apc - > ctrl_base ;
2012-03-14 10:36:07 +01:00
pending = __raw_readl ( base + AR724X_PCI_REG_INT_STATUS ) &
__raw_readl ( base + AR724X_PCI_REG_INT_MASK ) ;
if ( pending & AR724X_PCI_INT_DEV0 )
2013-02-03 10:00:16 +00:00
generic_handle_irq ( apc - > irq_base + 0 ) ;
2012-03-14 10:36:07 +01:00
else
spurious_interrupt ( ) ;
}
static void ar724x_pci_irq_unmask ( struct irq_data * d )
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2012-03-14 10:36:07 +01:00
void __iomem * base ;
2013-02-03 10:00:16 +00:00
int offset ;
2012-03-14 10:36:07 +01:00
u32 t ;
2013-02-03 09:58:38 +00:00
apc = irq_data_get_irq_chip_data ( d ) ;
base = apc - > ctrl_base ;
2013-02-03 10:00:16 +00:00
offset = apc - > irq_base - d - > irq ;
2012-03-14 10:36:07 +01:00
2013-02-03 10:00:16 +00:00
switch ( offset ) {
case 0 :
2012-03-14 10:36:07 +01:00
t = __raw_readl ( base + AR724X_PCI_REG_INT_MASK ) ;
__raw_writel ( t | AR724X_PCI_INT_DEV0 ,
base + AR724X_PCI_REG_INT_MASK ) ;
/* flush write */
__raw_readl ( base + AR724X_PCI_REG_INT_MASK ) ;
}
}
static void ar724x_pci_irq_mask ( struct irq_data * d )
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2012-03-14 10:36:07 +01:00
void __iomem * base ;
2013-02-03 10:00:16 +00:00
int offset ;
2012-03-14 10:36:07 +01:00
u32 t ;
2013-02-03 09:58:38 +00:00
apc = irq_data_get_irq_chip_data ( d ) ;
base = apc - > ctrl_base ;
2013-02-03 10:00:16 +00:00
offset = apc - > irq_base - d - > irq ;
2012-03-14 10:36:07 +01:00
2013-02-03 10:00:16 +00:00
switch ( offset ) {
case 0 :
2012-03-14 10:36:07 +01:00
t = __raw_readl ( base + AR724X_PCI_REG_INT_MASK ) ;
__raw_writel ( t & ~ AR724X_PCI_INT_DEV0 ,
base + AR724X_PCI_REG_INT_MASK ) ;
/* flush write */
__raw_readl ( base + AR724X_PCI_REG_INT_MASK ) ;
t = __raw_readl ( base + AR724X_PCI_REG_INT_STATUS ) ;
__raw_writel ( t | AR724X_PCI_INT_DEV0 ,
base + AR724X_PCI_REG_INT_STATUS ) ;
/* flush write */
__raw_readl ( base + AR724X_PCI_REG_INT_STATUS ) ;
}
}
static struct irq_chip ar724x_pci_irq_chip = {
. name = " AR724X PCI " ,
. irq_mask = ar724x_pci_irq_mask ,
. irq_unmask = ar724x_pci_irq_unmask ,
. irq_mask_ack = ar724x_pci_irq_mask ,
} ;
2013-02-03 10:00:16 +00:00
static void ar724x_pci_irq_init ( struct ar724x_pci_controller * apc ,
int id )
2012-03-14 10:36:07 +01:00
{
void __iomem * base ;
int i ;
2013-02-03 09:58:38 +00:00
base = apc - > ctrl_base ;
2012-03-14 10:36:07 +01:00
__raw_writel ( 0 , base + AR724X_PCI_REG_INT_MASK ) ;
__raw_writel ( 0 , base + AR724X_PCI_REG_INT_STATUS ) ;
2013-02-03 10:00:16 +00:00
apc - > irq_base = ATH79_PCI_IRQ_BASE + ( id * AR724X_PCI_IRQ_COUNT ) ;
2012-03-14 10:36:07 +01:00
2013-02-03 10:00:16 +00:00
for ( i = apc - > irq_base ;
i < apc - > irq_base + AR724X_PCI_IRQ_COUNT ; i + + ) {
2012-03-14 10:36:07 +01:00
irq_set_chip_and_handler ( i , & ar724x_pci_irq_chip ,
handle_level_irq ) ;
2013-02-03 09:58:38 +00:00
irq_set_chip_data ( i , apc ) ;
}
2012-03-14 10:36:07 +01:00
2015-07-13 20:45:56 +00:00
irq_set_chained_handler_and_data ( apc - > irq , ar724x_pci_irq_handler ,
apc ) ;
2012-03-14 10:36:07 +01:00
}
2013-02-02 11:40:42 +00:00
static int ar724x_pci_probe ( struct platform_device * pdev )
{
2013-02-03 09:58:38 +00:00
struct ar724x_pci_controller * apc ;
2013-02-02 11:40:42 +00:00
struct resource * res ;
2013-02-03 10:00:16 +00:00
int id ;
id = pdev - > id ;
if ( id = = - 1 )
id = 0 ;
2013-02-03 09:58:38 +00:00
apc = devm_kzalloc ( & pdev - > dev , sizeof ( struct ar724x_pci_controller ) ,
GFP_KERNEL ) ;
if ( ! apc )
return - ENOMEM ;
2013-02-02 11:40:42 +00:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " ctrl_base " ) ;
2013-03-12 10:30:05 +00:00
apc - > ctrl_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( apc - > ctrl_base ) )
return PTR_ERR ( apc - > ctrl_base ) ;
2013-02-02 11:40:42 +00:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " cfg_base " ) ;
2013-03-12 10:30:05 +00:00
apc - > devcfg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( apc - > devcfg_base ) )
return PTR_ERR ( apc - > devcfg_base ) ;
2013-02-02 11:40:42 +00:00
2013-02-03 14:52:47 +00:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " crp_base " ) ;
2013-03-12 10:30:05 +00:00
apc - > crp_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( apc - > crp_base ) )
return PTR_ERR ( apc - > crp_base ) ;
2013-02-03 14:52:47 +00:00
2013-02-03 09:58:38 +00:00
apc - > irq = platform_get_irq ( pdev , 0 ) ;
if ( apc - > irq < 0 )
2013-02-02 11:40:42 +00:00
return - EINVAL ;
2013-02-03 09:59:45 +00:00
res = platform_get_resource_byname ( pdev , IORESOURCE_IO , " io_base " ) ;
if ( ! res )
return - EINVAL ;
apc - > io_res . parent = res ;
apc - > io_res . name = " PCI IO space " ;
apc - > io_res . start = res - > start ;
apc - > io_res . end = res - > end ;
apc - > io_res . flags = IORESOURCE_IO ;
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " mem_base " ) ;
if ( ! res )
return - EINVAL ;
apc - > mem_res . parent = res ;
apc - > mem_res . name = " PCI memory space " ;
apc - > mem_res . start = res - > start ;
apc - > mem_res . end = res - > end ;
apc - > mem_res . flags = IORESOURCE_MEM ;
2013-02-03 09:58:38 +00:00
apc - > pci_controller . pci_ops = & ar724x_pci_ops ;
2013-02-03 09:59:45 +00:00
apc - > pci_controller . io_resource = & apc - > io_res ;
apc - > pci_controller . mem_resource = & apc - > mem_res ;
2013-02-03 09:58:38 +00:00
apc - > link_up = ar724x_pci_check_link ( apc ) ;
if ( ! apc - > link_up )
2013-02-02 11:40:42 +00:00
dev_warn ( & pdev - > dev , " PCIe link is down \n " ) ;
2013-02-03 10:00:16 +00:00
ar724x_pci_irq_init ( apc , id ) ;
2013-02-02 11:40:42 +00:00
2013-02-03 14:52:47 +00:00
ar724x_pci_local_write ( apc , PCI_COMMAND , 4 , AR724X_PCI_CMD_INIT ) ;
2013-02-03 09:58:38 +00:00
register_pci_controller ( & apc - > pci_controller ) ;
2013-02-02 11:40:42 +00:00
return 0 ;
}
static struct platform_driver ar724x_pci_driver = {
. probe = ar724x_pci_probe ,
. driver = {
. name = " ar724x-pci " ,
} ,
} ;
static int __init ar724x_pci_init ( void )
{
return platform_driver_register ( & ar724x_pci_driver ) ;
}
postcore_initcall ( ar724x_pci_init ) ;