2013-10-29 20:12:51 +04:00
/*
* pci - rcar - gen2 : internal PCI bus support
*
* Copyright ( C ) 2013 Renesas Solutions Corp .
* Copyright ( C ) 2013 Cogent Embedded , Inc .
*
* 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 .
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
2014-04-07 11:30:20 +02:00
# include <linux/of_pci.h>
2013-10-29 20:12:51 +04:00
# include <linux/pci.h>
# include <linux/platform_device.h>
2013-12-04 20:33:35 +04:00
# include <linux/pm_runtime.h>
2014-02-18 11:11:32 +09:00
# include <linux/sizes.h>
2013-10-29 20:12:51 +04:00
# include <linux/slab.h>
/* AHB-PCI Bridge PCI communication registers */
# define RCAR_AHBPCI_PCICOM_OFFSET 0x800
# define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00)
# define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04)
# define RCAR_PCIAHB_PREFETCH0 0x0
# define RCAR_PCIAHB_PREFETCH4 0x1
# define RCAR_PCIAHB_PREFETCH8 0x2
# define RCAR_PCIAHB_PREFETCH16 0x3
# define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10)
# define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14)
# define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1)
# define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1)
# define RCAR_AHBPCI_WIN1_HOST (1 << 30)
# define RCAR_AHBPCI_WIN1_DEVICE (1 << 31)
# define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20)
# define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24)
2014-02-18 11:11:01 +09:00
# define RCAR_PCI_INT_SIGTABORT (1 << 0)
# define RCAR_PCI_INT_SIGRETABORT (1 << 1)
# define RCAR_PCI_INT_REMABORT (1 << 2)
# define RCAR_PCI_INT_PERR (1 << 3)
# define RCAR_PCI_INT_SIGSERR (1 << 4)
# define RCAR_PCI_INT_RESERR (1 << 5)
# define RCAR_PCI_INT_WIN1ERR (1 << 12)
# define RCAR_PCI_INT_WIN2ERR (1 << 13)
2013-10-29 20:12:51 +04:00
# define RCAR_PCI_INT_A (1 << 16)
# define RCAR_PCI_INT_B (1 << 17)
# define RCAR_PCI_INT_PME (1 << 19)
2014-02-18 11:11:01 +09:00
# define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \
RCAR_PCI_INT_SIGRETABORT | \
RCAR_PCI_INT_SIGRETABORT | \
RCAR_PCI_INT_REMABORT | \
RCAR_PCI_INT_PERR | \
RCAR_PCI_INT_SIGSERR | \
RCAR_PCI_INT_RESERR | \
RCAR_PCI_INT_WIN1ERR | \
RCAR_PCI_INT_WIN2ERR )
2013-10-29 20:12:51 +04:00
# define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30)
# define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0)
# define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1)
# define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2)
# define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7)
# define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17)
# define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \
RCAR_AHB_BUS_MMODE_BYTE_BURST | \
RCAR_AHB_BUS_MMODE_WR_INCR | \
RCAR_AHB_BUS_MMODE_HBUS_REQ | \
RCAR_AHB_BUS_SMODE_READYCTR )
# define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34)
# define RCAR_USBCTR_USBH_RST (1 << 0)
# define RCAR_USBCTR_PCICLK_MASK (1 << 1)
# define RCAR_USBCTR_PLL_RST (1 << 2)
# define RCAR_USBCTR_DIRPD (1 << 8)
# define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9)
# define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10)
# define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10)
# define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10)
# define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10)
# define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10)
# define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40)
# define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0)
# define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1)
# define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12)
# define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48)
struct rcar_pci_priv {
2013-12-04 20:33:35 +04:00
struct device * dev ;
2013-10-29 20:12:51 +04:00
void __iomem * reg ;
struct resource io_res ;
struct resource mem_res ;
struct resource * cfg_res ;
2014-05-20 01:10:20 +04:00
unsigned busnr ;
2013-10-29 20:12:51 +04:00
int irq ;
2014-02-18 11:11:32 +09:00
unsigned long window_size ;
2013-10-29 20:12:51 +04:00
} ;
/* PCI configuration space operations */
static void __iomem * rcar_pci_cfg_base ( struct pci_bus * bus , unsigned int devfn ,
int where )
{
struct pci_sys_data * sys = bus - > sysdata ;
struct rcar_pci_priv * priv = sys - > private_data ;
int slot , val ;
if ( sys - > busnr ! = bus - > number | | PCI_FUNC ( devfn ) )
return NULL ;
/* Only one EHCI/OHCI device built-in */
slot = PCI_SLOT ( devfn ) ;
if ( slot > 2 )
return NULL ;
2014-02-18 11:11:11 +09:00
/* bridge logic only has registers to 0x40 */
if ( slot = = 0x0 & & where > = 0x40 )
return NULL ;
2013-10-29 20:12:51 +04:00
val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG :
RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG ;
iowrite32 ( val , priv - > reg + RCAR_AHBPCI_WIN1_CTR_REG ) ;
return priv - > reg + ( slot > > 1 ) * 0x100 + where ;
}
/* PCI interrupt mapping */
2014-02-18 11:11:21 +09:00
static int rcar_pci_map_irq ( const struct pci_dev * dev , u8 slot , u8 pin )
2013-10-29 20:12:51 +04:00
{
struct pci_sys_data * sys = dev - > bus - > sysdata ;
struct rcar_pci_priv * priv = sys - > private_data ;
2014-04-07 11:30:20 +02:00
int irq ;
irq = of_irq_parse_and_map_pci ( dev , slot , pin ) ;
if ( ! irq )
irq = priv - > irq ;
2013-10-29 20:12:51 +04:00
2014-04-07 11:30:20 +02:00
return irq ;
2013-10-29 20:12:51 +04:00
}
2014-02-18 11:11:01 +09:00
# ifdef CONFIG_PCI_DEBUG
/* if debug enabled, then attach an error handler irq to the bridge */
static irqreturn_t rcar_pci_err_irq ( int irq , void * pw )
{
struct rcar_pci_priv * priv = pw ;
u32 status = ioread32 ( priv - > reg + RCAR_PCI_INT_STATUS_REG ) ;
if ( status & RCAR_PCI_INT_ALLERRORS ) {
dev_err ( priv - > dev , " error irq: status %08x \n " , status ) ;
/* clear the error(s) */
iowrite32 ( status & RCAR_PCI_INT_ALLERRORS ,
priv - > reg + RCAR_PCI_INT_STATUS_REG ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
static void rcar_pci_setup_errirq ( struct rcar_pci_priv * priv )
{
int ret ;
u32 val ;
ret = devm_request_irq ( priv - > dev , priv - > irq , rcar_pci_err_irq ,
IRQF_SHARED , " error irq " , priv ) ;
if ( ret ) {
dev_err ( priv - > dev , " cannot claim IRQ for error handling \n " ) ;
return ;
}
val = ioread32 ( priv - > reg + RCAR_PCI_INT_ENABLE_REG ) ;
val | = RCAR_PCI_INT_ALLERRORS ;
iowrite32 ( val , priv - > reg + RCAR_PCI_INT_ENABLE_REG ) ;
}
# else
static inline void rcar_pci_setup_errirq ( struct rcar_pci_priv * priv ) { }
# endif
2013-10-29 20:12:51 +04:00
/* PCI host controller setup */
2014-02-18 11:11:21 +09:00
static int rcar_pci_setup ( int nr , struct pci_sys_data * sys )
2013-10-29 20:12:51 +04:00
{
struct rcar_pci_priv * priv = sys - > private_data ;
void __iomem * reg = priv - > reg ;
u32 val ;
2013-12-04 20:33:35 +04:00
pm_runtime_enable ( priv - > dev ) ;
pm_runtime_get_sync ( priv - > dev ) ;
2013-10-29 20:12:51 +04:00
val = ioread32 ( reg + RCAR_PCI_UNIT_REV_REG ) ;
2013-12-04 20:33:35 +04:00
dev_info ( priv - > dev , " PCI: bus%u revision %x \n " , sys - > busnr , val ) ;
2013-10-29 20:12:51 +04:00
/* Disable Direct Power Down State and assert reset */
val = ioread32 ( reg + RCAR_USBCTR_REG ) & ~ RCAR_USBCTR_DIRPD ;
val | = RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST ;
iowrite32 ( val , reg + RCAR_USBCTR_REG ) ;
udelay ( 4 ) ;
2014-02-18 11:11:32 +09:00
/* De-assert reset and reset PCIAHB window1 size */
2013-10-29 20:12:51 +04:00
val & = ~ ( RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK |
RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST ) ;
2014-02-18 11:11:32 +09:00
/* Setup PCIAHB window1 size */
switch ( priv - > window_size ) {
case SZ_2G :
val | = RCAR_USBCTR_PCIAHB_WIN1_2G ;
break ;
case SZ_1G :
val | = RCAR_USBCTR_PCIAHB_WIN1_1G ;
break ;
case SZ_512M :
val | = RCAR_USBCTR_PCIAHB_WIN1_512M ;
break ;
default :
pr_warn ( " unknown window size %ld - defaulting to 256M \n " ,
priv - > window_size ) ;
priv - > window_size = SZ_256M ;
/* fall-through */
case SZ_256M :
val | = RCAR_USBCTR_PCIAHB_WIN1_256M ;
break ;
}
iowrite32 ( val , reg + RCAR_USBCTR_REG ) ;
2013-10-29 20:12:51 +04:00
/* Configure AHB master and slave modes */
iowrite32 ( RCAR_AHB_BUS_MODE , reg + RCAR_AHB_BUS_CTR_REG ) ;
/* Configure PCI arbiter */
val = ioread32 ( reg + RCAR_PCI_ARBITER_CTR_REG ) ;
val | = RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
RCAR_PCI_ARBITER_PCIBP_MODE ;
iowrite32 ( val , reg + RCAR_PCI_ARBITER_CTR_REG ) ;
2014-02-18 11:11:32 +09:00
/* PCI-AHB mapping: 0x40000000 base */
2013-10-29 20:12:51 +04:00
iowrite32 ( 0x40000000 | RCAR_PCIAHB_PREFETCH16 ,
reg + RCAR_PCIAHB_WIN1_CTR_REG ) ;
/* AHB-PCI mapping: OHCI/EHCI registers */
val = priv - > mem_res . start | RCAR_AHBPCI_WIN_CTR_MEM ;
iowrite32 ( val , reg + RCAR_AHBPCI_WIN2_CTR_REG ) ;
/* Enable AHB-PCI bridge PCI configuration access */
iowrite32 ( RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG ,
reg + RCAR_AHBPCI_WIN1_CTR_REG ) ;
/* Set PCI-AHB Window1 address */
iowrite32 ( 0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH ,
reg + PCI_BASE_ADDRESS_1 ) ;
/* Set AHB-PCI bridge PCI communication area address */
val = priv - > cfg_res - > start + RCAR_AHBPCI_PCICOM_OFFSET ;
iowrite32 ( val , reg + PCI_BASE_ADDRESS_0 ) ;
val = ioread32 ( reg + PCI_COMMAND ) ;
val | = PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ;
iowrite32 ( val , reg + PCI_COMMAND ) ;
/* Enable PCI interrupts */
iowrite32 ( RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME ,
reg + RCAR_PCI_INT_ENABLE_REG ) ;
2014-02-18 11:11:01 +09:00
if ( priv - > irq > 0 )
rcar_pci_setup_errirq ( priv ) ;
2013-10-29 20:12:51 +04:00
/* Add PCI resources */
pci_add_resource ( & sys - > resources , & priv - > io_res ) ;
pci_add_resource ( & sys - > resources , & priv - > mem_res ) ;
2014-05-20 01:10:20 +04:00
/* Setup bus number based on platform device id / of bus-range */
sys - > busnr = priv - > busnr ;
2013-10-29 20:12:51 +04:00
return 1 ;
}
static struct pci_ops rcar_pci_ops = {
2015-01-09 20:34:47 -06:00
. map_bus = rcar_pci_cfg_base ,
. read = pci_generic_config_read ,
. write = pci_generic_config_write ,
2013-10-29 20:12:51 +04:00
} ;
2014-02-18 11:11:21 +09:00
static int rcar_pci_probe ( struct platform_device * pdev )
2013-10-29 20:12:51 +04:00
{
struct resource * cfg_res , * mem_res ;
struct rcar_pci_priv * priv ;
void __iomem * reg ;
2014-02-18 11:11:21 +09:00
struct hw_pci hw ;
void * hw_private [ 1 ] ;
2013-10-29 20:12:51 +04:00
cfg_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
reg = devm_ioremap_resource ( & pdev - > dev , cfg_res ) ;
2013-11-19 11:40:28 +08:00
if ( IS_ERR ( reg ) )
return PTR_ERR ( reg ) ;
2013-10-29 20:12:51 +04:00
mem_res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! mem_res | | ! mem_res - > start )
return - ENODEV ;
2015-02-16 10:54:08 +09:00
if ( mem_res - > start & 0xFFFF )
return - EINVAL ;
2013-10-29 20:12:51 +04:00
priv = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct rcar_pci_priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > mem_res = * mem_res ;
/*
* The controller does not support / use port I / O ,
* so setup a dummy port I / O region here .
*/
priv - > io_res . start = priv - > mem_res . start ;
priv - > io_res . end = priv - > mem_res . end ;
priv - > io_res . flags = IORESOURCE_IO ;
priv - > cfg_res = cfg_res ;
priv - > irq = platform_get_irq ( pdev , 0 ) ;
priv - > reg = reg ;
2013-12-04 20:33:35 +04:00
priv - > dev = & pdev - > dev ;
2013-10-29 20:12:51 +04:00
2014-02-18 11:10:51 +09:00
if ( priv - > irq < 0 ) {
dev_err ( & pdev - > dev , " no valid irq found \n " ) ;
return priv - > irq ;
}
2014-02-18 11:11:32 +09:00
priv - > window_size = SZ_1G ;
2014-05-20 01:10:20 +04:00
if ( pdev - > dev . of_node ) {
struct resource busnr ;
int ret ;
ret = of_pci_parse_bus_range ( pdev - > dev . of_node , & busnr ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to parse bus-range \n " ) ;
return ret ;
}
priv - > busnr = busnr . start ;
if ( busnr . end ! = busnr . start )
dev_warn ( & pdev - > dev , " only one bus number supported \n " ) ;
} else {
priv - > busnr = pdev - > id ;
}
2014-02-18 11:11:21 +09:00
hw_private [ 0 ] = priv ;
memset ( & hw , 0 , sizeof ( hw ) ) ;
hw . nr_controllers = ARRAY_SIZE ( hw_private ) ;
hw . private_data = hw_private ;
hw . map_irq = rcar_pci_map_irq ;
hw . ops = & rcar_pci_ops ;
hw . setup = rcar_pci_setup ;
pci_common_init_dev ( & pdev - > dev , & hw ) ;
return 0 ;
2013-10-29 20:12:51 +04:00
}
2014-05-20 01:10:20 +04:00
static struct of_device_id rcar_pci_of_match [ ] = {
{ . compatible = " renesas,pci-r8a7790 " , } ,
{ . compatible = " renesas,pci-r8a7791 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rcar_pci_of_match ) ;
2013-10-29 20:12:51 +04:00
static struct platform_driver rcar_pci_driver = {
. driver = {
. name = " pci-rcar-gen2 " ,
2014-02-18 11:11:21 +09:00
. suppress_bind_attrs = true ,
2014-05-20 01:10:20 +04:00
. of_match_table = rcar_pci_of_match ,
2013-10-29 20:12:51 +04:00
} ,
2014-02-18 11:11:21 +09:00
. probe = rcar_pci_probe ,
2013-10-29 20:12:51 +04:00
} ;
2014-02-18 11:11:21 +09:00
module_platform_driver ( rcar_pci_driver ) ;
2013-10-29 20:12:51 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Renesas R-Car Gen2 internal PCI " ) ;
MODULE_AUTHOR ( " Valentine Barshak <valentine.barshak@cogentembedded.com> " ) ;