2018-05-09 09:12:59 +08:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Rockchip AXI PCIe endpoint controller driver
*
* Copyright ( c ) 2018 Rockchip , Inc .
*
* Author : Shawn Lin < shawn . lin @ rock - chips . com >
* Simon Xue < xxm @ rock - chips . com >
*/
# include <linux/configfs.h>
# include <linux/delay.h>
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/pci-epc.h>
# include <linux/platform_device.h>
# include <linux/pci-epf.h>
# include <linux/sizes.h>
# include "pcie-rockchip.h"
/**
* struct rockchip_pcie_ep - private data for PCIe endpoint controller driver
* @ rockchip : Rockchip PCIe controller
* @ max_regions : maximum number of regions supported by hardware
* @ ob_region_map : bitmask of mapped outbound regions
* @ ob_addr : base addresses in the AXI bus where the outbound regions start
* @ irq_phys_addr : base address on the AXI bus where the MSI / legacy IRQ
* dedicated outbound regions is mapped .
* @ irq_cpu_addr : base address in the CPU space where a write access triggers
* the sending of a memory write ( MSI ) / normal message ( legacy
* IRQ ) TLP through the PCIe bus .
* @ irq_pci_addr : used to save the current mapping of the MSI / legacy IRQ
* dedicated outbound region .
* @ irq_pci_fn : the latest PCI function that has updated the mapping of
* the MSI / legacy IRQ dedicated outbound region .
* @ irq_pending : bitmask of asserted legacy IRQs .
*/
struct rockchip_pcie_ep {
struct rockchip_pcie rockchip ;
struct pci_epc * epc ;
u32 max_regions ;
unsigned long ob_region_map ;
phys_addr_t * ob_addr ;
phys_addr_t irq_phys_addr ;
void __iomem * irq_cpu_addr ;
u64 irq_pci_addr ;
u8 irq_pci_fn ;
u8 irq_pending ;
} ;
static void rockchip_pcie_clear_ep_ob_atu ( struct rockchip_pcie * rockchip ,
u32 region )
{
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0 ( region ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1 ( region ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC0 ( region ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC1 ( region ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0 ( region ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1 ( region ) ) ;
}
static void rockchip_pcie_prog_ep_ob_atu ( struct rockchip_pcie * rockchip , u8 fn ,
u32 r , u32 type , u64 cpu_addr ,
u64 pci_addr , size_t size )
{
u64 sz = 1ULL < < fls64 ( size - 1 ) ;
int num_pass_bits = ilog2 ( sz ) ;
u32 addr0 , addr1 , desc0 , desc1 ;
bool is_nor_msg = ( type = = AXI_WRAPPER_NOR_MSG ) ;
/* The minimal region size is 1MB */
if ( num_pass_bits < 8 )
num_pass_bits = 8 ;
cpu_addr - = rockchip - > mem_res - > start ;
addr0 = ( ( is_nor_msg ? 0x10 : ( num_pass_bits - 1 ) ) &
PCIE_CORE_OB_REGION_ADDR0_NUM_BITS ) |
( lower_32_bits ( cpu_addr ) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR ) ;
addr1 = upper_32_bits ( is_nor_msg ? cpu_addr : pci_addr ) ;
desc0 = ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN ( fn ) | type ;
desc1 = 0 ;
if ( is_nor_msg ) {
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0 ( r ) ) ;
rockchip_pcie_write ( rockchip , 0 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1 ( r ) ) ;
rockchip_pcie_write ( rockchip , desc0 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC0 ( r ) ) ;
rockchip_pcie_write ( rockchip , desc1 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC1 ( r ) ) ;
} else {
/* PCI bus address region */
rockchip_pcie_write ( rockchip , addr0 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0 ( r ) ) ;
rockchip_pcie_write ( rockchip , addr1 ,
ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1 ( r ) ) ;
rockchip_pcie_write ( rockchip , desc0 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC0 ( r ) ) ;
rockchip_pcie_write ( rockchip , desc1 ,
ROCKCHIP_PCIE_AT_OB_REGION_DESC1 ( r ) ) ;
addr0 =
( ( num_pass_bits - 1 ) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS ) |
( lower_32_bits ( cpu_addr ) &
PCIE_CORE_OB_REGION_ADDR0_LO_ADDR ) ;
addr1 = upper_32_bits ( cpu_addr ) ;
}
/* CPU bus address region */
rockchip_pcie_write ( rockchip , addr0 ,
ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0 ( r ) ) ;
rockchip_pcie_write ( rockchip , addr1 ,
ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1 ( r ) ) ;
}
static int rockchip_pcie_ep_write_header ( struct pci_epc * epc , u8 fn ,
struct pci_epf_header * hdr )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
/* All functions share the same vendor ID with function 0 */
if ( fn = = 0 ) {
u32 vid_regs = ( hdr - > vendorid & GENMASK ( 15 , 0 ) ) |
( hdr - > subsys_vendor_id & GENMASK ( 31 , 16 ) ) < < 16 ;
rockchip_pcie_write ( rockchip , vid_regs ,
PCIE_CORE_CONFIG_VENDOR ) ;
}
rockchip_pcie_write ( rockchip , hdr - > deviceid < < 16 ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) + PCI_VENDOR_ID ) ;
rockchip_pcie_write ( rockchip ,
hdr - > revid |
hdr - > progif_code < < 8 |
hdr - > subclass_code < < 16 |
hdr - > baseclass_code < < 24 ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) + PCI_REVISION_ID ) ;
rockchip_pcie_write ( rockchip , hdr - > cache_line_size ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
PCI_CACHE_LINE_SIZE ) ;
rockchip_pcie_write ( rockchip , hdr - > subsys_id < < 16 ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
PCI_SUBSYSTEM_VENDOR_ID ) ;
rockchip_pcie_write ( rockchip , hdr - > interrupt_pin < < 8 ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
PCI_INTERRUPT_LINE ) ;
return 0 ;
}
static int rockchip_pcie_ep_set_bar ( struct pci_epc * epc , u8 fn ,
struct pci_epf_bar * epf_bar )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
dma_addr_t bar_phys = epf_bar - > phys_addr ;
enum pci_barno bar = epf_bar - > barno ;
int flags = epf_bar - > flags ;
u32 addr0 , addr1 , reg , cfg , b , aperture , ctrl ;
u64 sz ;
/* BAR size is 2^(aperture + 7) */
sz = max_t ( size_t , epf_bar - > size , MIN_EP_APERTURE ) ;
/*
* roundup_pow_of_two ( ) returns an unsigned long , which is not suited
* for 64 bit values .
*/
sz = 1ULL < < fls64 ( sz - 1 ) ;
aperture = ilog2 ( sz ) - 7 ; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */
if ( ( flags & PCI_BASE_ADDRESS_SPACE ) = = PCI_BASE_ADDRESS_SPACE_IO ) {
ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS ;
} else {
bool is_prefetch = ! ! ( flags & PCI_BASE_ADDRESS_MEM_PREFETCH ) ;
bool is_64bits = sz > SZ_2G ;
if ( is_64bits & & ( bar & 1 ) )
return - EINVAL ;
if ( is_64bits & & is_prefetch )
ctrl =
ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS ;
else if ( is_prefetch )
ctrl =
ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS ;
else if ( is_64bits )
ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_64BITS ;
else
ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_32BITS ;
}
if ( bar < BAR_4 ) {
reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0 ( fn ) ;
b = bar ;
} else {
reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1 ( fn ) ;
b = bar - BAR_4 ;
}
addr0 = lower_32_bits ( bar_phys ) ;
addr1 = upper_32_bits ( bar_phys ) ;
cfg = rockchip_pcie_read ( rockchip , reg ) ;
cfg & = ~ ( ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK ( b ) |
ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK ( b ) ) ;
cfg | = ( ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE ( b , aperture ) |
ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL ( b , ctrl ) ) ;
rockchip_pcie_write ( rockchip , cfg , reg ) ;
rockchip_pcie_write ( rockchip , addr0 ,
ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0 ( fn , bar ) ) ;
rockchip_pcie_write ( rockchip , addr1 ,
ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1 ( fn , bar ) ) ;
return 0 ;
}
static void rockchip_pcie_ep_clear_bar ( struct pci_epc * epc , u8 fn ,
struct pci_epf_bar * epf_bar )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u32 reg , cfg , b , ctrl ;
enum pci_barno bar = epf_bar - > barno ;
if ( bar < BAR_4 ) {
reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0 ( fn ) ;
b = bar ;
} else {
reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1 ( fn ) ;
b = bar - BAR_4 ;
}
ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_DISABLED ;
cfg = rockchip_pcie_read ( rockchip , reg ) ;
cfg & = ~ ( ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK ( b ) |
ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK ( b ) ) ;
cfg | = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL ( b , ctrl ) ;
rockchip_pcie_write ( rockchip , cfg , reg ) ;
rockchip_pcie_write ( rockchip , 0x0 ,
ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0 ( fn , bar ) ) ;
rockchip_pcie_write ( rockchip , 0x0 ,
ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1 ( fn , bar ) ) ;
}
static int rockchip_pcie_ep_map_addr ( struct pci_epc * epc , u8 fn ,
phys_addr_t addr , u64 pci_addr ,
size_t size )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * pcie = & ep - > rockchip ;
u32 r ;
r = find_first_zero_bit ( & ep - > ob_region_map ,
sizeof ( ep - > ob_region_map ) * BITS_PER_LONG ) ;
/*
* Region 0 is reserved for configuration space and shouldn ' t
* be used elsewhere per TRM , so leave it out .
*/
if ( r > = ep - > max_regions - 1 ) {
dev_err ( & epc - > dev , " no free outbound region \n " ) ;
return - EINVAL ;
}
rockchip_pcie_prog_ep_ob_atu ( pcie , fn , r , AXI_WRAPPER_MEM_WRITE , addr ,
pci_addr , size ) ;
set_bit ( r , & ep - > ob_region_map ) ;
ep - > ob_addr [ r ] = addr ;
return 0 ;
}
static void rockchip_pcie_ep_unmap_addr ( struct pci_epc * epc , u8 fn ,
phys_addr_t addr )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u32 r ;
for ( r = 0 ; r < ep - > max_regions - 1 ; r + + )
if ( ep - > ob_addr [ r ] = = addr )
break ;
/*
* Region 0 is reserved for configuration space and shouldn ' t
* be used elsewhere per TRM , so leave it out .
*/
if ( r = = ep - > max_regions - 1 )
return ;
rockchip_pcie_clear_ep_ob_atu ( rockchip , r ) ;
ep - > ob_addr [ r ] = 0 ;
clear_bit ( r , & ep - > ob_region_map ) ;
}
static int rockchip_pcie_ep_set_msi ( struct pci_epc * epc , u8 fn ,
u8 multi_msg_cap )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u16 flags ;
flags = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG ) ;
flags & = ~ ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK ;
flags | =
( ( multi_msg_cap < < 1 ) < < ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET ) |
PCI_MSI_FLAGS_64BIT ;
flags & = ~ ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP ;
rockchip_pcie_write ( rockchip , flags ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG ) ;
return 0 ;
}
static int rockchip_pcie_ep_get_msi ( struct pci_epc * epc , u8 fn )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u16 flags ;
flags = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG ) ;
if ( ! ( flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME ) )
return - EINVAL ;
return ( ( flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK ) > >
ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET ) ;
}
static void rockchip_pcie_ep_assert_intx ( struct rockchip_pcie_ep * ep , u8 fn ,
u8 intx , bool is_asserted )
{
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u32 r = ep - > max_regions - 1 ;
u32 offset ;
2019-03-30 15:09:10 +00:00
u32 status ;
2018-05-09 09:12:59 +08:00
u8 msg_code ;
if ( unlikely ( ep - > irq_pci_addr ! = ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR | |
ep - > irq_pci_fn ! = fn ) ) {
rockchip_pcie_prog_ep_ob_atu ( rockchip , fn , r ,
AXI_WRAPPER_NOR_MSG ,
ep - > irq_phys_addr , 0 , 0 ) ;
ep - > irq_pci_addr = ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR ;
ep - > irq_pci_fn = fn ;
}
intx & = 3 ;
if ( is_asserted ) {
ep - > irq_pending | = BIT ( intx ) ;
msg_code = ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA + intx ;
} else {
ep - > irq_pending & = ~ BIT ( intx ) ;
msg_code = ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA + intx ;
}
status = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_CMD_STATUS ) ;
status & = ROCKCHIP_PCIE_EP_CMD_STATUS_IS ;
if ( ( status ! = 0 ) ^ ( ep - > irq_pending ! = 0 ) ) {
status ^ = ROCKCHIP_PCIE_EP_CMD_STATUS_IS ;
rockchip_pcie_write ( rockchip , status ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_CMD_STATUS ) ;
}
offset =
ROCKCHIP_PCIE_MSG_ROUTING ( ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX ) |
ROCKCHIP_PCIE_MSG_CODE ( msg_code ) | ROCKCHIP_PCIE_MSG_NO_DATA ;
writel ( 0 , ep - > irq_cpu_addr + offset ) ;
}
static int rockchip_pcie_ep_send_legacy_irq ( struct rockchip_pcie_ep * ep , u8 fn ,
u8 intx )
{
u16 cmd ;
cmd = rockchip_pcie_read ( & ep - > rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_CMD_STATUS ) ;
if ( cmd & PCI_COMMAND_INTX_DISABLE )
return - EINVAL ;
/*
* Should add some delay between toggling INTx per TRM vaguely saying
* it depends on some cycles of the AHB bus clock to function it . So
* add sufficient 1 ms here .
*/
rockchip_pcie_ep_assert_intx ( ep , fn , intx , true ) ;
mdelay ( 1 ) ;
rockchip_pcie_ep_assert_intx ( ep , fn , intx , false ) ;
return 0 ;
}
static int rockchip_pcie_ep_send_msi_irq ( struct rockchip_pcie_ep * ep , u8 fn ,
u8 interrupt_num )
{
struct rockchip_pcie * rockchip = & ep - > rockchip ;
u16 flags , mme , data , data_mask ;
u8 msi_count ;
u64 pci_addr , pci_addr_mask = 0xff ;
/* Check MSI enable bit */
flags = rockchip_pcie_read ( & ep - > rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG ) ;
if ( ! ( flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME ) )
return - EINVAL ;
/* Get MSI numbers from MME */
mme = ( ( flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK ) > >
ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET ) ;
msi_count = 1 < < mme ;
if ( ! interrupt_num | | interrupt_num > msi_count )
return - EINVAL ;
/* Set MSI private data */
data_mask = msi_count - 1 ;
data = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
PCI_MSI_DATA_64 ) ;
data = ( data & ~ data_mask ) | ( ( interrupt_num - 1 ) & data_mask ) ;
/* Get MSI PCI address */
pci_addr = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
PCI_MSI_ADDRESS_HI ) ;
pci_addr < < = 32 ;
pci_addr | = rockchip_pcie_read ( rockchip ,
ROCKCHIP_PCIE_EP_FUNC_BASE ( fn ) +
ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
PCI_MSI_ADDRESS_LO ) ;
pci_addr & = GENMASK_ULL ( 63 , 2 ) ;
/* Set the outbound region if needed. */
if ( unlikely ( ep - > irq_pci_addr ! = ( pci_addr & ~ pci_addr_mask ) | |
ep - > irq_pci_fn ! = fn ) ) {
rockchip_pcie_prog_ep_ob_atu ( rockchip , fn , ep - > max_regions - 1 ,
AXI_WRAPPER_MEM_WRITE ,
ep - > irq_phys_addr ,
pci_addr & ~ pci_addr_mask ,
pci_addr_mask + 1 ) ;
ep - > irq_pci_addr = ( pci_addr & ~ pci_addr_mask ) ;
ep - > irq_pci_fn = fn ;
}
writew ( data , ep - > irq_cpu_addr + ( pci_addr & pci_addr_mask ) ) ;
return 0 ;
}
static int rockchip_pcie_ep_raise_irq ( struct pci_epc * epc , u8 fn ,
enum pci_epc_irq_type type ,
2018-07-19 10:32:13 +02:00
u16 interrupt_num )
2018-05-09 09:12:59 +08:00
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
switch ( type ) {
case PCI_EPC_IRQ_LEGACY :
return rockchip_pcie_ep_send_legacy_irq ( ep , fn , 0 ) ;
case PCI_EPC_IRQ_MSI :
return rockchip_pcie_ep_send_msi_irq ( ep , fn , interrupt_num ) ;
default :
return - EINVAL ;
}
}
static int rockchip_pcie_ep_start ( struct pci_epc * epc )
{
struct rockchip_pcie_ep * ep = epc_get_drvdata ( epc ) ;
struct rockchip_pcie * rockchip = & ep - > rockchip ;
struct pci_epf * epf ;
u32 cfg ;
cfg = BIT ( 0 ) ;
list_for_each_entry ( epf , & epc - > pci_epf , list )
cfg | = BIT ( epf - > func_no ) ;
rockchip_pcie_write ( rockchip , cfg , PCIE_CORE_PHY_FUNC_CFG ) ;
return 0 ;
}
2019-01-14 16:45:03 +05:30
static const struct pci_epc_features rockchip_pcie_epc_features = {
. linkup_notifier = false ,
. msi_capable = true ,
. msix_capable = false ,
} ;
static const struct pci_epc_features *
rockchip_pcie_ep_get_features ( struct pci_epc * epc , u8 func_no )
{
return & rockchip_pcie_epc_features ;
}
2018-05-09 09:12:59 +08:00
static const struct pci_epc_ops rockchip_pcie_epc_ops = {
. write_header = rockchip_pcie_ep_write_header ,
. set_bar = rockchip_pcie_ep_set_bar ,
. clear_bar = rockchip_pcie_ep_clear_bar ,
. map_addr = rockchip_pcie_ep_map_addr ,
. unmap_addr = rockchip_pcie_ep_unmap_addr ,
. set_msi = rockchip_pcie_ep_set_msi ,
. get_msi = rockchip_pcie_ep_get_msi ,
. raise_irq = rockchip_pcie_ep_raise_irq ,
. start = rockchip_pcie_ep_start ,
2019-01-14 16:45:03 +05:30
. get_features = rockchip_pcie_ep_get_features ,
2018-05-09 09:12:59 +08:00
} ;
static int rockchip_pcie_parse_ep_dt ( struct rockchip_pcie * rockchip ,
struct rockchip_pcie_ep * ep )
{
struct device * dev = rockchip - > dev ;
int err ;
err = rockchip_pcie_parse_dt ( rockchip ) ;
if ( err )
return err ;
err = rockchip_pcie_get_phys ( rockchip ) ;
if ( err )
return err ;
err = of_property_read_u32 ( dev - > of_node ,
" rockchip,max-outbound-regions " ,
& ep - > max_regions ) ;
if ( err < 0 | | ep - > max_regions > MAX_REGION_LIMIT )
ep - > max_regions = MAX_REGION_LIMIT ;
err = of_property_read_u8 ( dev - > of_node , " max-functions " ,
& ep - > epc - > max_functions ) ;
if ( err < 0 )
ep - > epc - > max_functions = 1 ;
return 0 ;
}
static const struct of_device_id rockchip_pcie_ep_of_match [ ] = {
{ . compatible = " rockchip,rk3399-pcie-ep " } ,
{ } ,
} ;
static int rockchip_pcie_ep_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct rockchip_pcie_ep * ep ;
struct rockchip_pcie * rockchip ;
struct pci_epc * epc ;
size_t max_regions ;
int err ;
ep = devm_kzalloc ( dev , sizeof ( * ep ) , GFP_KERNEL ) ;
if ( ! ep )
return - ENOMEM ;
rockchip = & ep - > rockchip ;
rockchip - > is_rc = false ;
rockchip - > dev = dev ;
epc = devm_pci_epc_create ( dev , & rockchip_pcie_epc_ops ) ;
if ( IS_ERR ( epc ) ) {
dev_err ( dev , " failed to create epc device \n " ) ;
return PTR_ERR ( epc ) ;
}
ep - > epc = epc ;
epc_set_drvdata ( epc , ep ) ;
err = rockchip_pcie_parse_ep_dt ( rockchip , ep ) ;
if ( err )
return err ;
err = rockchip_pcie_enable_clocks ( rockchip ) ;
if ( err )
return err ;
err = rockchip_pcie_init_port ( rockchip ) ;
if ( err )
goto err_disable_clocks ;
/* Establish the link automatically */
rockchip_pcie_write ( rockchip , PCIE_CLIENT_LINK_TRAIN_ENABLE ,
PCIE_CLIENT_CONFIG ) ;
max_regions = ep - > max_regions ;
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 14:07:58 -07:00
ep - > ob_addr = devm_kcalloc ( dev , max_regions , sizeof ( * ep - > ob_addr ) ,
2018-05-09 09:12:59 +08:00
GFP_KERNEL ) ;
if ( ! ep - > ob_addr ) {
err = - ENOMEM ;
goto err_uninit_port ;
}
/* Only enable function 0 by default */
rockchip_pcie_write ( rockchip , BIT ( 0 ) , PCIE_CORE_PHY_FUNC_CFG ) ;
err = pci_epc_mem_init ( epc , rockchip - > mem_res - > start ,
2020-05-07 13:33:15 +01:00
resource_size ( rockchip - > mem_res ) , PAGE_SIZE ) ;
2018-05-09 09:12:59 +08:00
if ( err < 0 ) {
dev_err ( dev , " failed to initialize the memory space \n " ) ;
goto err_uninit_port ;
}
ep - > irq_cpu_addr = pci_epc_mem_alloc_addr ( epc , & ep - > irq_phys_addr ,
SZ_128K ) ;
if ( ! ep - > irq_cpu_addr ) {
dev_err ( dev , " failed to reserve memory space for MSI \n " ) ;
err = - ENOMEM ;
goto err_epc_mem_exit ;
}
ep - > irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR ;
return 0 ;
err_epc_mem_exit :
pci_epc_mem_exit ( epc ) ;
err_uninit_port :
rockchip_pcie_deinit_phys ( rockchip ) ;
err_disable_clocks :
rockchip_pcie_disable_clocks ( rockchip ) ;
return err ;
}
static struct platform_driver rockchip_pcie_ep_driver = {
. driver = {
. name = " rockchip-pcie-ep " ,
. of_match_table = rockchip_pcie_ep_of_match ,
} ,
. probe = rockchip_pcie_ep_probe ,
} ;
builtin_platform_driver ( rockchip_pcie_ep_driver ) ;