2019-05-29 17:12:40 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-07-15 08:50:57 +04:00
/*
*
* Copyright ( C ) 2013 Freescale Semiconductor , Inc .
*/
# define pr_fmt(fmt) "fsl-pamu: %s: " fmt, __func__
2015-01-28 17:34:33 +03:00
# include "fsl_pamu.h"
2015-09-20 07:29:53 +03:00
# include <linux/fsl/guts.h>
2013-07-15 08:50:57 +04:00
# include <linux/interrupt.h>
# include <linux/genalloc.h>
2015-01-28 17:34:33 +03:00
# include <asm/mpc85xx.h>
2013-07-15 08:50:57 +04:00
/* define indexes for each operation mapping scenario */
# define OMI_QMAN 0x00
# define OMI_FMAN 0x01
# define OMI_QMAN_PRIV 0x02
# define OMI_CAAM 0x03
# define make64(high, low) (((u64)(high) << 32) | (low))
struct pamu_isr_data {
2015-01-28 17:34:33 +03:00
void __iomem * pamu_reg_base ; /* Base address of PAMU regs */
2013-07-15 08:50:57 +04:00
unsigned int count ; /* The number of PAMUs */
} ;
static struct paace * ppaact ;
static struct paace * spaact ;
2017-08-09 17:15:43 +03:00
static bool probed ; /* Has PAMU been probed? */
2013-07-15 08:50:57 +04:00
/*
* Table for matching compatible strings , for device tree
* guts node , for QorIQ SOCs .
* " fsl,qoriq-device-config-2.0 " corresponds to T4 & B4
* SOCs . For the older SOCs " fsl,qoriq-device-config-1.0 "
* string would be used .
2015-01-28 17:34:33 +03:00
*/
2015-03-25 08:28:48 +03:00
static const struct of_device_id guts_device_ids [ ] = {
2013-07-15 08:50:57 +04:00
{ . compatible = " fsl,qoriq-device-config-1.0 " , } ,
{ . compatible = " fsl,qoriq-device-config-2.0 " , } ,
{ }
} ;
/*
* Table for matching compatible strings , for device tree
* L3 cache controller node .
* " fsl,t4240-l3-cache-controller " corresponds to T4 ,
* " fsl,b4860-l3-cache-controller " corresponds to B4 &
* " fsl,p4080-l3-cache-controller " corresponds to other ,
* SOCs .
2015-01-28 17:34:33 +03:00
*/
2013-07-15 08:50:57 +04:00
static const struct of_device_id l3_device_ids [ ] = {
{ . compatible = " fsl,t4240-l3-cache-controller " , } ,
{ . compatible = " fsl,b4860-l3-cache-controller " , } ,
{ . compatible = " fsl,p4080-l3-cache-controller " , } ,
{ }
} ;
/* maximum subwindows permitted per liodn */
static u32 max_subwindow_count ;
/**
* pamu_get_ppaace ( ) - Return the primary PACCE
* @ liodn : liodn PAACT index for desired PAACE
*
* Returns the ppace pointer upon success else return
* null .
*/
static struct paace * pamu_get_ppaace ( int liodn )
{
if ( ! ppaact | | liodn > = PAACE_NUMBER_ENTRIES ) {
pr_debug ( " PPAACT doesn't exist \n " ) ;
return NULL ;
}
return & ppaact [ liodn ] ;
}
/**
* pamu_enable_liodn ( ) - Set valid bit of PACCE
* @ liodn : liodn PAACT index for desired PAACE
*
* Returns 0 upon success else error code < 0 returned
*/
int pamu_enable_liodn ( int liodn )
{
struct paace * ppaace ;
ppaace = pamu_get_ppaace ( liodn ) ;
if ( ! ppaace ) {
pr_debug ( " Invalid primary paace entry \n " ) ;
return - ENOENT ;
}
if ( ! get_bf ( ppaace - > addr_bitfields , PPAACE_AF_WSE ) ) {
pr_debug ( " liodn %d not configured \n " , liodn ) ;
return - EINVAL ;
}
/* Ensure that all other stores to the ppaace complete first */
mb ( ) ;
set_bf ( ppaace - > addr_bitfields , PAACE_AF_V , PAACE_V_VALID ) ;
mb ( ) ;
return 0 ;
}
/**
* pamu_disable_liodn ( ) - Clears valid bit of PACCE
* @ liodn : liodn PAACT index for desired PAACE
*
* Returns 0 upon success else error code < 0 returned
*/
int pamu_disable_liodn ( int liodn )
{
struct paace * ppaace ;
ppaace = pamu_get_ppaace ( liodn ) ;
if ( ! ppaace ) {
pr_debug ( " Invalid primary paace entry \n " ) ;
return - ENOENT ;
}
set_bf ( ppaace - > addr_bitfields , PAACE_AF_V , PAACE_V_INVALID ) ;
mb ( ) ;
return 0 ;
}
/* Derive the window size encoding for a particular PAACE entry */
static unsigned int map_addrspace_size_to_wse ( phys_addr_t addrspace_size )
{
/* Bug if not a power of 2 */
2015-01-28 17:34:33 +03:00
BUG_ON ( addrspace_size & ( addrspace_size - 1 ) ) ;
2013-07-15 08:50:57 +04:00
/* window size is 2^(WSE+1) bytes */
2014-06-24 17:57:15 +04:00
return fls64 ( addrspace_size ) - 2 ;
2013-07-15 08:50:57 +04:00
}
/*
* Set the PAACE type as primary and set the coherency required domain
* attribute
*/
static void pamu_init_ppaace ( struct paace * ppaace )
{
set_bf ( ppaace - > addr_bitfields , PAACE_AF_PT , PAACE_PT_PRIMARY ) ;
set_bf ( ppaace - > domain_attr . to_host . coherency_required , PAACE_DA_HOST_CR ,
PAACE_M_COHERENCE_REQ ) ;
}
/*
* Function used for updating stash destination for the coressponding
* LIODN .
*/
2021-04-01 18:52:41 +03:00
int pamu_update_paace_stash ( int liodn , u32 value )
2013-07-15 08:50:57 +04:00
{
struct paace * paace ;
paace = pamu_get_ppaace ( liodn ) ;
if ( ! paace ) {
pr_debug ( " Invalid liodn entry \n " ) ;
return - ENOENT ;
}
set_bf ( paace - > impl_attr , PAACE_IA_CID , value ) ;
mb ( ) ;
return 0 ;
}
/**
* pamu_config_paace ( ) - Sets up PPAACE entry for specified liodn
*
* @ liodn : Logical IO device number
* @ omi : Operation mapping index - - if ~ omi = = 0 then omi not defined
* @ stashid : cache stash id for associated cpu - - if ~ stashid = = 0 then
* stashid not defined
* @ prot : window permissions
*
* Returns 0 upon success else error code < 0 returned
*/
2021-04-01 18:52:49 +03:00
int pamu_config_ppaace ( int liodn , u32 omi , u32 stashid , int prot )
2013-07-15 08:50:57 +04:00
{
struct paace * ppaace ;
ppaace = pamu_get_ppaace ( liodn ) ;
2015-01-28 17:34:33 +03:00
if ( ! ppaace )
2013-07-15 08:50:57 +04:00
return - ENOENT ;
/* window size is 2^(WSE+1) bytes */
set_bf ( ppaace - > addr_bitfields , PPAACE_AF_WSE ,
2021-04-01 18:52:49 +03:00
map_addrspace_size_to_wse ( 1ULL < < 36 ) ) ;
2013-07-15 08:50:57 +04:00
pamu_init_ppaace ( ppaace ) ;
2021-04-01 18:52:49 +03:00
ppaace - > wbah = 0 ;
set_bf ( ppaace - > addr_bitfields , PPAACE_AF_WBAL , 0 ) ;
2013-07-15 08:50:57 +04:00
/* set up operation mapping if it's configured */
if ( omi < OME_NUMBER_ENTRIES ) {
set_bf ( ppaace - > impl_attr , PAACE_IA_OTM , PAACE_OTM_INDEXED ) ;
ppaace - > op_encode . index_ot . omi = omi ;
} else if ( ~ omi ! = 0 ) {
pr_debug ( " bad operation mapping index: %d \n " , omi ) ;
return - EINVAL ;
}
/* configure stash id */
if ( ~ stashid ! = 0 )
set_bf ( ppaace - > impl_attr , PAACE_IA_CID , stashid ) ;
2021-04-01 18:52:41 +03:00
set_bf ( ppaace - > impl_attr , PAACE_IA_ATM , PAACE_ATM_WINDOW_XLATE ) ;
2021-04-01 18:52:48 +03:00
ppaace - > twbah = 0 ;
set_bf ( ppaace - > win_bitfields , PAACE_WIN_TWBAL , 0 ) ;
2021-04-01 18:52:41 +03:00
set_bf ( ppaace - > addr_bitfields , PAACE_AF_AP , prot ) ;
set_bf ( ppaace - > impl_attr , PAACE_IA_WCE , 0 ) ;
set_bf ( ppaace - > addr_bitfields , PPAACE_AF_MW , 0 ) ;
2013-07-15 08:50:57 +04:00
mb ( ) ;
return 0 ;
}
/**
2015-01-28 17:34:33 +03:00
* get_ome_index ( ) - Returns the index in the operation mapping table
* for device .
* @ * omi_index : pointer for storing the index value
*
*/
2013-07-15 08:50:57 +04:00
void get_ome_index ( u32 * omi_index , struct device * dev )
{
if ( of_device_is_compatible ( dev - > of_node , " fsl,qman-portal " ) )
* omi_index = OMI_QMAN ;
if ( of_device_is_compatible ( dev - > of_node , " fsl,qman " ) )
* omi_index = OMI_QMAN_PRIV ;
}
/**
* get_stash_id - Returns stash destination id corresponding to a
* cache type and vcpu .
* @ stash_dest_hint : L1 , L2 or L3
* @ vcpu : vpcu target for a particular cache type .
*
* Returs stash on success or ~ ( u32 ) 0 on failure .
*
*/
u32 get_stash_id ( u32 stash_dest_hint , u32 vcpu )
{
const u32 * prop ;
struct device_node * node ;
u32 cache_level ;
int len , found = 0 ;
int i ;
/* Fastpath, exit early if L3/CPC cache is target for stashing */
if ( stash_dest_hint = = PAMU_ATTR_CACHE_L3 ) {
node = of_find_matching_node ( NULL , l3_device_ids ) ;
if ( node ) {
2015-01-28 17:34:33 +03:00
prop = of_get_property ( node , " cache-stash-id " , NULL ) ;
2013-07-15 08:50:57 +04:00
if ( ! prop ) {
2017-07-19 00:43:09 +03:00
pr_debug ( " missing cache-stash-id at %pOF \n " ,
node ) ;
2013-07-15 08:50:57 +04:00
of_node_put ( node ) ;
return ~ ( u32 ) 0 ;
}
of_node_put ( node ) ;
return be32_to_cpup ( prop ) ;
}
return ~ ( u32 ) 0 ;
}
2018-08-27 17:46:27 +03:00
for_each_of_cpu_node ( node ) {
2013-07-15 08:50:57 +04:00
prop = of_get_property ( node , " reg " , & len ) ;
for ( i = 0 ; i < len / sizeof ( u32 ) ; i + + ) {
if ( be32_to_cpup ( & prop [ i ] ) = = vcpu ) {
found = 1 ;
goto found_cpu_node ;
}
}
}
found_cpu_node :
/* find the hwnode that represents the cache */
for ( cache_level = PAMU_ATTR_CACHE_L1 ; ( cache_level < PAMU_ATTR_CACHE_L3 ) & & found ; cache_level + + ) {
if ( stash_dest_hint = = cache_level ) {
2015-01-28 17:34:33 +03:00
prop = of_get_property ( node , " cache-stash-id " , NULL ) ;
2013-07-15 08:50:57 +04:00
if ( ! prop ) {
2017-07-19 00:43:09 +03:00
pr_debug ( " missing cache-stash-id at %pOF \n " ,
node ) ;
2013-07-15 08:50:57 +04:00
of_node_put ( node ) ;
return ~ ( u32 ) 0 ;
}
of_node_put ( node ) ;
return be32_to_cpup ( prop ) ;
}
2015-01-28 17:34:33 +03:00
prop = of_get_property ( node , " next-level-cache " , NULL ) ;
2013-07-15 08:50:57 +04:00
if ( ! prop ) {
2017-07-19 00:43:09 +03:00
pr_debug ( " can't find next-level-cache at %pOF \n " , node ) ;
2013-07-15 08:50:57 +04:00
of_node_put ( node ) ;
return ~ ( u32 ) 0 ; /* can't traverse any further */
}
of_node_put ( node ) ;
/* advance to next node in cache hierarchy */
node = of_find_node_by_phandle ( * prop ) ;
if ( ! node ) {
2014-05-17 21:16:44 +04:00
pr_debug ( " Invalid node for cache hierarchy \n " ) ;
2013-07-15 08:50:57 +04:00
return ~ ( u32 ) 0 ;
}
}
pr_debug ( " stash dest not found for %d on vcpu %d \n " ,
2015-01-28 17:34:33 +03:00
stash_dest_hint , vcpu ) ;
2013-07-15 08:50:57 +04:00
return ~ ( u32 ) 0 ;
}
/* Identify if the PAACT table entry belongs to QMAN, BMAN or QMAN Portal */
# define QMAN_PAACE 1
# define QMAN_PORTAL_PAACE 2
# define BMAN_PAACE 3
/**
* Setup operation mapping and stash destinations for QMAN and QMAN portal .
* Memory accesses to QMAN and BMAN private memory need not be coherent , so
* clear the PAACE entry coherency attribute for them .
*/
2015-03-25 08:28:48 +03:00
static void setup_qbman_paace ( struct paace * ppaace , int paace_type )
2013-07-15 08:50:57 +04:00
{
switch ( paace_type ) {
case QMAN_PAACE :
set_bf ( ppaace - > impl_attr , PAACE_IA_OTM , PAACE_OTM_INDEXED ) ;
ppaace - > op_encode . index_ot . omi = OMI_QMAN_PRIV ;
/* setup QMAN Private data stashing for the L3 cache */
set_bf ( ppaace - > impl_attr , PAACE_IA_CID , get_stash_id ( PAMU_ATTR_CACHE_L3 , 0 ) ) ;
set_bf ( ppaace - > domain_attr . to_host . coherency_required , PAACE_DA_HOST_CR ,
0 ) ;
break ;
case QMAN_PORTAL_PAACE :
set_bf ( ppaace - > impl_attr , PAACE_IA_OTM , PAACE_OTM_INDEXED ) ;
ppaace - > op_encode . index_ot . omi = OMI_QMAN ;
2015-01-28 17:34:33 +03:00
/* Set DQRR and Frame stashing for the L3 cache */
2013-07-15 08:50:57 +04:00
set_bf ( ppaace - > impl_attr , PAACE_IA_CID , get_stash_id ( PAMU_ATTR_CACHE_L3 , 0 ) ) ;
break ;
case BMAN_PAACE :
set_bf ( ppaace - > domain_attr . to_host . coherency_required , PAACE_DA_HOST_CR ,
0 ) ;
break ;
}
}
/**
* Setup the operation mapping table for various devices . This is a static
* table where each table index corresponds to a particular device . PAMU uses
* this table to translate device transaction to appropriate corenet
* transaction .
*/
2015-03-25 08:28:48 +03:00
static void setup_omt ( struct ome * omt )
2013-07-15 08:50:57 +04:00
{
struct ome * ome ;
/* Configure OMI_QMAN */
ome = & omt [ OMI_QMAN ] ;
ome - > moe [ IOE_READ_IDX ] = EOE_VALID | EOE_READ ;
ome - > moe [ IOE_EREAD0_IDX ] = EOE_VALID | EOE_RSA ;
ome - > moe [ IOE_WRITE_IDX ] = EOE_VALID | EOE_WRITE ;
ome - > moe [ IOE_EWRITE0_IDX ] = EOE_VALID | EOE_WWSAO ;
ome - > moe [ IOE_DIRECT0_IDX ] = EOE_VALID | EOE_LDEC ;
ome - > moe [ IOE_DIRECT1_IDX ] = EOE_VALID | EOE_LDECPE ;
/* Configure OMI_FMAN */
ome = & omt [ OMI_FMAN ] ;
ome - > moe [ IOE_READ_IDX ] = EOE_VALID | EOE_READI ;
ome - > moe [ IOE_WRITE_IDX ] = EOE_VALID | EOE_WRITE ;
/* Configure OMI_QMAN private */
ome = & omt [ OMI_QMAN_PRIV ] ;
ome - > moe [ IOE_READ_IDX ] = EOE_VALID | EOE_READ ;
ome - > moe [ IOE_WRITE_IDX ] = EOE_VALID | EOE_WRITE ;
ome - > moe [ IOE_EREAD0_IDX ] = EOE_VALID | EOE_RSA ;
ome - > moe [ IOE_EWRITE0_IDX ] = EOE_VALID | EOE_WWSA ;
/* Configure OMI_CAAM */
ome = & omt [ OMI_CAAM ] ;
ome - > moe [ IOE_READ_IDX ] = EOE_VALID | EOE_READI ;
ome - > moe [ IOE_WRITE_IDX ] = EOE_VALID | EOE_WRITE ;
}
/*
* Get the maximum number of PAACT table entries
* and subwindows supported by PAMU
*/
2015-03-25 08:28:48 +03:00
static void get_pamu_cap_values ( unsigned long pamu_reg_base )
2013-07-15 08:50:57 +04:00
{
u32 pc_val ;
pc_val = in_be32 ( ( u32 * ) ( pamu_reg_base + PAMU_PC3 ) ) ;
/* Maximum number of subwindows per liodn */
max_subwindow_count = 1 < < ( 1 + PAMU_PC3_MWCE ( pc_val ) ) ;
}
/* Setup PAMU registers pointing to PAACT, SPAACT and OMT */
2015-03-25 08:28:48 +03:00
static int setup_one_pamu ( unsigned long pamu_reg_base , unsigned long pamu_reg_size ,
phys_addr_t ppaact_phys , phys_addr_t spaact_phys ,
phys_addr_t omt_phys )
2013-07-15 08:50:57 +04:00
{
u32 * pc ;
struct pamu_mmap_regs * pamu_regs ;
pc = ( u32 * ) ( pamu_reg_base + PAMU_PC ) ;
pamu_regs = ( struct pamu_mmap_regs * )
( pamu_reg_base + PAMU_MMAP_REGS_BASE ) ;
/* set up pointers to corenet control blocks */
out_be32 ( & pamu_regs - > ppbah , upper_32_bits ( ppaact_phys ) ) ;
out_be32 ( & pamu_regs - > ppbal , lower_32_bits ( ppaact_phys ) ) ;
ppaact_phys = ppaact_phys + PAACT_SIZE ;
out_be32 ( & pamu_regs - > pplah , upper_32_bits ( ppaact_phys ) ) ;
out_be32 ( & pamu_regs - > pplal , lower_32_bits ( ppaact_phys ) ) ;
out_be32 ( & pamu_regs - > spbah , upper_32_bits ( spaact_phys ) ) ;
out_be32 ( & pamu_regs - > spbal , lower_32_bits ( spaact_phys ) ) ;
spaact_phys = spaact_phys + SPAACT_SIZE ;
out_be32 ( & pamu_regs - > splah , upper_32_bits ( spaact_phys ) ) ;
out_be32 ( & pamu_regs - > splal , lower_32_bits ( spaact_phys ) ) ;
out_be32 ( & pamu_regs - > obah , upper_32_bits ( omt_phys ) ) ;
out_be32 ( & pamu_regs - > obal , lower_32_bits ( omt_phys ) ) ;
omt_phys = omt_phys + OMT_SIZE ;
out_be32 ( & pamu_regs - > olah , upper_32_bits ( omt_phys ) ) ;
out_be32 ( & pamu_regs - > olal , lower_32_bits ( omt_phys ) ) ;
/*
* set PAMU enable bit ,
* allow ppaact & omt to be cached
* & enable PAMU access violation interrupts .
*/
out_be32 ( ( u32 * ) ( pamu_reg_base + PAMU_PICS ) ,
2015-01-28 17:34:33 +03:00
PAMU_ACCESS_VIOLATION_ENABLE ) ;
2013-07-15 08:50:57 +04:00
out_be32 ( pc , PAMU_PC_PE | PAMU_PC_OCE | PAMU_PC_SPCC | PAMU_PC_PPCC ) ;
return 0 ;
}
/* Enable all device LIODNS */
2015-03-25 08:28:48 +03:00
static void setup_liodns ( void )
2013-07-15 08:50:57 +04:00
{
int i , len ;
struct paace * ppaace ;
struct device_node * node = NULL ;
const u32 * prop ;
for_each_node_with_property ( node , " fsl,liodn " ) {
prop = of_get_property ( node , " fsl,liodn " , & len ) ;
for ( i = 0 ; i < len / sizeof ( u32 ) ; i + + ) {
int liodn ;
liodn = be32_to_cpup ( & prop [ i ] ) ;
if ( liodn > = PAACE_NUMBER_ENTRIES ) {
pr_debug ( " Invalid LIODN value %d \n " , liodn ) ;
continue ;
}
ppaace = pamu_get_ppaace ( liodn ) ;
pamu_init_ppaace ( ppaace ) ;
/* window size is 2^(WSE+1) bytes */
set_bf ( ppaace - > addr_bitfields , PPAACE_AF_WSE , 35 ) ;
ppaace - > wbah = 0 ;
set_bf ( ppaace - > addr_bitfields , PPAACE_AF_WBAL , 0 ) ;
set_bf ( ppaace - > impl_attr , PAACE_IA_ATM ,
2015-01-28 17:34:33 +03:00
PAACE_ATM_NO_XLATE ) ;
2013-07-15 08:50:57 +04:00
set_bf ( ppaace - > addr_bitfields , PAACE_AF_AP ,
2015-01-28 17:34:33 +03:00
PAACE_AP_PERMS_ALL ) ;
2013-07-15 08:50:57 +04:00
if ( of_device_is_compatible ( node , " fsl,qman-portal " ) )
setup_qbman_paace ( ppaace , QMAN_PORTAL_PAACE ) ;
if ( of_device_is_compatible ( node , " fsl,qman " ) )
setup_qbman_paace ( ppaace , QMAN_PAACE ) ;
if ( of_device_is_compatible ( node , " fsl,bman " ) )
setup_qbman_paace ( ppaace , BMAN_PAACE ) ;
mb ( ) ;
pamu_enable_liodn ( liodn ) ;
}
}
}
2015-01-28 17:34:33 +03:00
static irqreturn_t pamu_av_isr ( int irq , void * arg )
2013-07-15 08:50:57 +04:00
{
struct pamu_isr_data * data = arg ;
phys_addr_t phys ;
unsigned int i , j , ret ;
2013-08-14 13:44:30 +04:00
pr_emerg ( " access violation interrupt \n " ) ;
2013-07-15 08:50:57 +04:00
for ( i = 0 ; i < data - > count ; i + + ) {
void __iomem * p = data - > pamu_reg_base + i * PAMU_OFFSET ;
u32 pics = in_be32 ( p + PAMU_PICS ) ;
if ( pics & PAMU_ACCESS_VIOLATION_STAT ) {
u32 avs1 = in_be32 ( p + PAMU_AVS1 ) ;
struct paace * paace ;
pr_emerg ( " POES1=%08x \n " , in_be32 ( p + PAMU_POES1 ) ) ;
pr_emerg ( " POES2=%08x \n " , in_be32 ( p + PAMU_POES2 ) ) ;
pr_emerg ( " AVS1=%08x \n " , avs1 ) ;
pr_emerg ( " AVS2=%08x \n " , in_be32 ( p + PAMU_AVS2 ) ) ;
2015-01-28 17:34:33 +03:00
pr_emerg ( " AVA=%016llx \n " ,
make64 ( in_be32 ( p + PAMU_AVAH ) ,
in_be32 ( p + PAMU_AVAL ) ) ) ;
2013-07-15 08:50:57 +04:00
pr_emerg ( " UDAD=%08x \n " , in_be32 ( p + PAMU_UDAD ) ) ;
2015-01-28 17:34:33 +03:00
pr_emerg ( " POEA=%016llx \n " ,
make64 ( in_be32 ( p + PAMU_POEAH ) ,
in_be32 ( p + PAMU_POEAL ) ) ) ;
2013-07-15 08:50:57 +04:00
phys = make64 ( in_be32 ( p + PAMU_POEAH ) ,
2015-01-28 17:34:33 +03:00
in_be32 ( p + PAMU_POEAL ) ) ;
2013-07-15 08:50:57 +04:00
/* Assume that POEA points to a PAACE */
if ( phys ) {
u32 * paace = phys_to_virt ( phys ) ;
/* Only the first four words are relevant */
for ( j = 0 ; j < 4 ; j + + )
2015-01-28 17:34:33 +03:00
pr_emerg ( " PAACE[%u]=%08x \n " ,
j , in_be32 ( paace + j ) ) ;
2013-07-15 08:50:57 +04:00
}
/* clear access violation condition */
2015-01-28 17:34:33 +03:00
out_be32 ( p + PAMU_AVS1 , avs1 & PAMU_AV_MASK ) ;
2013-07-15 08:50:57 +04:00
paace = pamu_get_ppaace ( avs1 > > PAMU_AVS1_LIODN_SHIFT ) ;
BUG_ON ( ! paace ) ;
/* check if we got a violation for a disabled LIODN */
if ( ! get_bf ( paace - > addr_bitfields , PAACE_AF_V ) ) {
/*
* As per hardware erratum A - 00363 8 , access
* violation can be reported for a disabled
* LIODN . If we hit that condition , disable
* access violation reporting .
*/
pics & = ~ PAMU_ACCESS_VIOLATION_ENABLE ;
} else {
/* Disable the LIODN */
ret = pamu_disable_liodn ( avs1 > > PAMU_AVS1_LIODN_SHIFT ) ;
BUG_ON ( ret ) ;
2015-01-28 17:34:33 +03:00
pr_emerg ( " Disabling liodn %x \n " ,
avs1 > > PAMU_AVS1_LIODN_SHIFT ) ;
2013-07-15 08:50:57 +04:00
}
out_be32 ( ( p + PAMU_PICS ) , pics ) ;
}
}
return IRQ_HANDLED ;
}
# define LAWAR_EN 0x80000000
# define LAWAR_TARGET_MASK 0x0FF00000
# define LAWAR_TARGET_SHIFT 20
# define LAWAR_SIZE_MASK 0x0000003F
# define LAWAR_CSDID_MASK 0x000FF000
# define LAWAR_CSDID_SHIFT 12
# define LAW_SIZE_4K 0xb
struct ccsr_law {
u32 lawbarh ; /* LAWn base address high */
u32 lawbarl ; /* LAWn base address low */
u32 lawar ; /* LAWn attributes */
u32 reserved ;
} ;
/*
* Create a coherence subdomain for a given memory block .
*/
2015-03-25 08:28:48 +03:00
static int create_csd ( phys_addr_t phys , size_t size , u32 csd_port_id )
2013-07-15 08:50:57 +04:00
{
struct device_node * np ;
const __be32 * iprop ;
void __iomem * lac = NULL ; /* Local Access Control registers */
struct ccsr_law __iomem * law ;
void __iomem * ccm = NULL ;
u32 __iomem * csdids ;
unsigned int i , num_laws , num_csds ;
u32 law_target = 0 ;
u32 csd_id = 0 ;
int ret = 0 ;
np = of_find_compatible_node ( NULL , NULL , " fsl,corenet-law " ) ;
if ( ! np )
return - ENODEV ;
iprop = of_get_property ( np , " fsl,num-laws " , NULL ) ;
if ( ! iprop ) {
ret = - ENODEV ;
goto error ;
}
num_laws = be32_to_cpup ( iprop ) ;
if ( ! num_laws ) {
ret = - ENODEV ;
goto error ;
}
lac = of_iomap ( np , 0 ) ;
if ( ! lac ) {
ret = - ENODEV ;
goto error ;
}
/* LAW registers are at offset 0xC00 */
law = lac + 0xC00 ;
of_node_put ( np ) ;
np = of_find_compatible_node ( NULL , NULL , " fsl,corenet-cf " ) ;
if ( ! np ) {
ret = - ENODEV ;
goto error ;
}
iprop = of_get_property ( np , " fsl,ccf-num-csdids " , NULL ) ;
if ( ! iprop ) {
ret = - ENODEV ;
goto error ;
}
num_csds = be32_to_cpup ( iprop ) ;
if ( ! num_csds ) {
ret = - ENODEV ;
goto error ;
}
ccm = of_iomap ( np , 0 ) ;
if ( ! ccm ) {
ret = - ENOMEM ;
goto error ;
}
/* The undocumented CSDID registers are at offset 0x600 */
csdids = ccm + 0x600 ;
of_node_put ( np ) ;
np = NULL ;
/* Find an unused coherence subdomain ID */
for ( csd_id = 0 ; csd_id < num_csds ; csd_id + + ) {
if ( ! csdids [ csd_id ] )
break ;
}
/* Store the Port ID in the (undocumented) proper CIDMRxx register */
csdids [ csd_id ] = csd_port_id ;
/* Find the DDR LAW that maps to our buffer. */
for ( i = 0 ; i < num_laws ; i + + ) {
if ( law [ i ] . lawar & LAWAR_EN ) {
phys_addr_t law_start , law_end ;
law_start = make64 ( law [ i ] . lawbarh , law [ i ] . lawbarl ) ;
law_end = law_start +
( 2ULL < < ( law [ i ] . lawar & LAWAR_SIZE_MASK ) ) ;
if ( law_start < = phys & & phys < law_end ) {
law_target = law [ i ] . lawar & LAWAR_TARGET_MASK ;
break ;
}
}
}
if ( i = = 0 | | i = = num_laws ) {
2015-01-28 17:34:33 +03:00
/* This should never happen */
2013-07-15 08:50:57 +04:00
ret = - ENOENT ;
goto error ;
}
/* Find a free LAW entry */
while ( law [ - - i ] . lawar & LAWAR_EN ) {
if ( i = = 0 ) {
/* No higher priority LAW slots available */
ret = - ENOENT ;
goto error ;
}
}
law [ i ] . lawbarh = upper_32_bits ( phys ) ;
law [ i ] . lawbarl = lower_32_bits ( phys ) ;
wmb ( ) ;
law [ i ] . lawar = LAWAR_EN | law_target | ( csd_id < < LAWAR_CSDID_SHIFT ) |
( LAW_SIZE_4K + get_order ( size ) ) ;
wmb ( ) ;
error :
if ( ccm )
iounmap ( ccm ) ;
if ( lac )
iounmap ( lac ) ;
if ( np )
of_node_put ( np ) ;
return ret ;
}
/*
* Table of SVRs and the corresponding PORT_ID values . Port ID corresponds to a
* bit map of snoopers for a given range of memory mapped by a LAW .
*
* All future CoreNet - enabled SOCs will have this erratum ( A - 004510 ) fixed , so this
* table should never need to be updated . SVRs are guaranteed to be unique , so
* there is no worry that a future SOC will inadvertently have one of these
* values .
*/
static const struct {
u32 svr ;
u32 port_id ;
2015-03-25 08:28:48 +03:00
} port_id_map [ ] = {
2015-01-28 17:34:33 +03:00
{ ( SVR_P2040 < < 8 ) | 0x10 , 0xFF000000 } , /* P2040 1.0 */
{ ( SVR_P2040 < < 8 ) | 0x11 , 0xFF000000 } , /* P2040 1.1 */
{ ( SVR_P2041 < < 8 ) | 0x10 , 0xFF000000 } , /* P2041 1.0 */
{ ( SVR_P2041 < < 8 ) | 0x11 , 0xFF000000 } , /* P2041 1.1 */
{ ( SVR_P3041 < < 8 ) | 0x10 , 0xFF000000 } , /* P3041 1.0 */
{ ( SVR_P3041 < < 8 ) | 0x11 , 0xFF000000 } , /* P3041 1.1 */
{ ( SVR_P4040 < < 8 ) | 0x20 , 0xFFF80000 } , /* P4040 2.0 */
{ ( SVR_P4080 < < 8 ) | 0x20 , 0xFFF80000 } , /* P4080 2.0 */
{ ( SVR_P5010 < < 8 ) | 0x10 , 0xFC000000 } , /* P5010 1.0 */
{ ( SVR_P5010 < < 8 ) | 0x20 , 0xFC000000 } , /* P5010 2.0 */
{ ( SVR_P5020 < < 8 ) | 0x10 , 0xFC000000 } , /* P5020 1.0 */
{ ( SVR_P5021 < < 8 ) | 0x10 , 0xFF800000 } , /* P5021 1.0 */
{ ( SVR_P5040 < < 8 ) | 0x10 , 0xFF800000 } , /* P5040 1.0 */
2013-07-15 08:50:57 +04:00
} ;
# define SVR_SECURITY 0x80000 /* The Security (E) bit */
2015-03-25 08:28:48 +03:00
static int fsl_pamu_probe ( struct platform_device * pdev )
2013-07-15 08:50:57 +04:00
{
2015-01-28 17:34:33 +03:00
struct device * dev = & pdev - > dev ;
2013-07-15 08:50:57 +04:00
void __iomem * pamu_regs = NULL ;
struct ccsr_guts __iomem * guts_regs = NULL ;
u32 pamubypenr , pamu_counter ;
unsigned long pamu_reg_off ;
unsigned long pamu_reg_base ;
struct pamu_isr_data * data = NULL ;
struct device_node * guts_node ;
u64 size ;
struct page * p ;
int ret = 0 ;
int irq ;
phys_addr_t ppaact_phys ;
phys_addr_t spaact_phys ;
2015-03-25 08:28:48 +03:00
struct ome * omt ;
2013-07-15 08:50:57 +04:00
phys_addr_t omt_phys ;
size_t mem_size = 0 ;
unsigned int order = 0 ;
u32 csd_port_id = 0 ;
unsigned i ;
/*
* enumerate all PAMUs and allocate and setup PAMU tables
* for each of them ,
* NOTE : All PAMUs share the same LIODN tables .
*/
2017-08-09 17:15:43 +03:00
if ( WARN_ON ( probed ) )
return - EBUSY ;
2015-01-28 17:34:33 +03:00
pamu_regs = of_iomap ( dev - > of_node , 0 ) ;
2013-07-15 08:50:57 +04:00
if ( ! pamu_regs ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " ioremap of PAMU node failed \n " ) ;
2013-07-15 08:50:57 +04:00
return - ENOMEM ;
}
2015-01-28 17:34:33 +03:00
of_get_address ( dev - > of_node , 0 , & size , NULL ) ;
2013-07-15 08:50:57 +04:00
2015-01-28 17:34:33 +03:00
irq = irq_of_parse_and_map ( dev - > of_node , 0 ) ;
2013-07-15 08:50:57 +04:00
if ( irq = = NO_IRQ ) {
2015-01-28 17:34:33 +03:00
dev_warn ( dev , " no interrupts listed in PAMU node \n " ) ;
2013-07-15 08:50:57 +04:00
goto error ;
}
2015-01-28 17:34:33 +03:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2013-07-15 08:50:57 +04:00
if ( ! data ) {
ret = - ENOMEM ;
goto error ;
}
data - > pamu_reg_base = pamu_regs ;
data - > count = size / PAMU_OFFSET ;
/* The ISR needs access to the regs, so we won't iounmap them */
ret = request_irq ( irq , pamu_av_isr , 0 , " pamu " , data ) ;
if ( ret < 0 ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " error %i installing ISR for irq %i \n " , ret , irq ) ;
2013-07-15 08:50:57 +04:00
goto error ;
}
guts_node = of_find_matching_node ( NULL , guts_device_ids ) ;
if ( ! guts_node ) {
2017-07-19 00:43:09 +03:00
dev_err ( dev , " could not find GUTS node %pOF \n " , dev - > of_node ) ;
2013-07-15 08:50:57 +04:00
ret = - ENODEV ;
goto error ;
}
guts_regs = of_iomap ( guts_node , 0 ) ;
of_node_put ( guts_node ) ;
if ( ! guts_regs ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " ioremap of GUTS node failed \n " ) ;
2013-07-15 08:50:57 +04:00
ret = - ENODEV ;
goto error ;
}
/* read in the PAMU capability registers */
get_pamu_cap_values ( ( unsigned long ) pamu_regs ) ;
/*
* To simplify the allocation of a coherency domain , we allocate the
* PAACT and the OMT in the same memory buffer . Unfortunately , this
* wastes more memory compared to allocating the buffers separately .
*/
/* Determine how much memory we need */
mem_size = ( PAGE_SIZE < < get_order ( PAACT_SIZE ) ) +
( PAGE_SIZE < < get_order ( SPAACT_SIZE ) ) +
( PAGE_SIZE < < get_order ( OMT_SIZE ) ) ;
order = get_order ( mem_size ) ;
p = alloc_pages ( GFP_KERNEL | __GFP_ZERO , order ) ;
if ( ! p ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " unable to allocate PAACT/SPAACT/OMT block \n " ) ;
2013-07-15 08:50:57 +04:00
ret = - ENOMEM ;
goto error ;
}
ppaact = page_address ( p ) ;
ppaact_phys = page_to_phys ( p ) ;
/* Make sure the memory is naturally aligned */
if ( ppaact_phys & ( ( PAGE_SIZE < < order ) - 1 ) ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " PAACT/OMT block is unaligned \n " ) ;
2013-07-15 08:50:57 +04:00
ret = - ENOMEM ;
goto error ;
}
spaact = ( void * ) ppaact + ( PAGE_SIZE < < get_order ( PAACT_SIZE ) ) ;
omt = ( void * ) spaact + ( PAGE_SIZE < < get_order ( SPAACT_SIZE ) ) ;
2015-01-28 17:34:33 +03:00
dev_dbg ( dev , " ppaact virt=%p phys=%pa \n " , ppaact , & ppaact_phys ) ;
2013-07-15 08:50:57 +04:00
/* Check to see if we need to implement the work-around on this SOC */
/* Determine the Port ID for our coherence subdomain */
for ( i = 0 ; i < ARRAY_SIZE ( port_id_map ) ; i + + ) {
if ( port_id_map [ i ] . svr = = ( mfspr ( SPRN_SVR ) & ~ SVR_SECURITY ) ) {
csd_port_id = port_id_map [ i ] . port_id ;
2015-01-28 17:34:33 +03:00
dev_dbg ( dev , " found matching SVR %08x \n " ,
2013-07-15 08:50:57 +04:00
port_id_map [ i ] . svr ) ;
break ;
}
}
if ( csd_port_id ) {
2015-01-28 17:34:33 +03:00
dev_dbg ( dev , " creating coherency subdomain at address %pa, size %zu, port id 0x%08x " ,
& ppaact_phys , mem_size , csd_port_id ) ;
2013-07-15 08:50:57 +04:00
ret = create_csd ( ppaact_phys , mem_size , csd_port_id ) ;
if ( ret ) {
2015-01-28 17:34:33 +03:00
dev_err ( dev , " could not create coherence subdomain \n " ) ;
2013-07-15 08:50:57 +04:00
return ret ;
}
}
spaact_phys = virt_to_phys ( spaact ) ;
omt_phys = virt_to_phys ( omt ) ;
pamubypenr = in_be32 ( & guts_regs - > pamubypenr ) ;
for ( pamu_reg_off = 0 , pamu_counter = 0x80000000 ; pamu_reg_off < size ;
pamu_reg_off + = PAMU_OFFSET , pamu_counter > > = 1 ) {
2015-01-28 17:34:33 +03:00
pamu_reg_base = ( unsigned long ) pamu_regs + pamu_reg_off ;
2013-07-15 08:50:57 +04:00
setup_one_pamu ( pamu_reg_base , pamu_reg_off , ppaact_phys ,
2015-01-28 17:34:33 +03:00
spaact_phys , omt_phys ) ;
2013-07-15 08:50:57 +04:00
/* Disable PAMU bypass for this PAMU */
pamubypenr & = ~ pamu_counter ;
}
setup_omt ( omt ) ;
/* Enable all relevant PAMU(s) */
out_be32 ( & guts_regs - > pamubypenr , pamubypenr ) ;
iounmap ( guts_regs ) ;
2015-01-28 17:34:33 +03:00
/* Enable DMA for the LIODNs in the device tree */
2013-07-15 08:50:57 +04:00
setup_liodns ( ) ;
2017-08-09 17:15:43 +03:00
probed = true ;
2013-07-15 08:50:57 +04:00
return 0 ;
error :
if ( irq ! = NO_IRQ )
free_irq ( irq , data ) ;
2020-09-11 16:53:25 +03:00
kfree_sensitive ( data ) ;
2013-07-15 08:50:57 +04:00
if ( pamu_regs )
iounmap ( pamu_regs ) ;
if ( guts_regs )
iounmap ( guts_regs ) ;
if ( ppaact )
free_pages ( ( unsigned long ) ppaact , order ) ;
ppaact = NULL ;
return ret ;
}
2015-03-25 08:28:48 +03:00
static struct platform_driver fsl_of_pamu_driver = {
2013-07-15 08:50:57 +04:00
. driver = {
. name = " fsl-of-pamu " ,
} ,
. probe = fsl_pamu_probe ,
} ;
static __init int fsl_pamu_init ( void )
{
struct platform_device * pdev = NULL ;
struct device_node * np ;
int ret ;
/*
* The normal OF process calls the probe function at some
* indeterminate later time , after most drivers have loaded . This is
* too late for us , because PAMU clients ( like the Qman driver )
* depend on PAMU being initialized early .
*
* So instead , we " manually " call our probe function by creating the
* platform devices ourselves .
*/
/*
* We assume that there is only one PAMU node in the device tree . A
* single PAMU node represents all of the PAMU devices in the SOC
* already . Everything else already makes that assumption , and the
* binding for the PAMU nodes doesn ' t allow for any parent - child
* relationships anyway . In other words , support for more than one
* PAMU node would require significant changes to a lot of code .
*/
np = of_find_compatible_node ( NULL , NULL , " fsl,pamu " ) ;
if ( ! np ) {
2013-08-14 13:44:30 +04:00
pr_err ( " could not find a PAMU node \n " ) ;
2013-07-15 08:50:57 +04:00
return - ENODEV ;
}
ret = platform_driver_register ( & fsl_of_pamu_driver ) ;
if ( ret ) {
2013-08-14 13:44:30 +04:00
pr_err ( " could not register driver (err=%i) \n " , ret ) ;
2013-07-15 08:50:57 +04:00
goto error_driver_register ;
}
pdev = platform_device_alloc ( " fsl-of-pamu " , 0 ) ;
if ( ! pdev ) {
2017-07-19 00:43:09 +03:00
pr_err ( " could not allocate device %pOF \n " , np ) ;
2013-07-15 08:50:57 +04:00
ret = - ENOMEM ;
goto error_device_alloc ;
}
pdev - > dev . of_node = of_node_get ( np ) ;
ret = pamu_domain_init ( ) ;
if ( ret )
goto error_device_add ;
ret = platform_device_add ( pdev ) ;
if ( ret ) {
2017-07-19 00:43:09 +03:00
pr_err ( " could not add device %pOF (err=%i) \n " , np , ret ) ;
2013-07-15 08:50:57 +04:00
goto error_device_add ;
}
return 0 ;
error_device_add :
of_node_put ( pdev - > dev . of_node ) ;
pdev - > dev . of_node = NULL ;
platform_device_put ( pdev ) ;
error_device_alloc :
platform_driver_unregister ( & fsl_of_pamu_driver ) ;
error_driver_register :
of_node_put ( np ) ;
return ret ;
}
arch_initcall ( fsl_pamu_init ) ;