2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-02-26 20:29:05 +04:00
# include <linux/types.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/export.h>
# include <linux/io.h>
# include <linux/mcb.h>
# include "mcb-internal.h"
struct mcb_parse_priv {
phys_addr_t mapbase ;
void __iomem * base ;
} ;
# define for_each_chameleon_cell(dtype, p) \
for ( ( dtype ) = get_next_dtype ( ( p ) ) ; \
( dtype ) ! = CHAMELEON_DTYPE_END ; \
( dtype ) = get_next_dtype ( ( p ) ) )
static inline uint32_t get_next_dtype ( void __iomem * p )
{
uint32_t dtype ;
dtype = readl ( p ) ;
return dtype > > 28 ;
}
static int chameleon_parse_bdd ( struct mcb_bus * bus ,
2016-08-26 10:34:58 +03:00
struct chameleon_bar * cb ,
2014-02-26 20:29:05 +04:00
void __iomem * base )
{
return 0 ;
}
static int chameleon_parse_gdd ( struct mcb_bus * bus ,
2016-08-26 10:34:58 +03:00
struct chameleon_bar * cb ,
void __iomem * base , int bar_count )
2014-02-26 20:29:05 +04:00
{
struct chameleon_gdd __iomem * gdd =
( struct chameleon_gdd __iomem * ) base ;
struct mcb_device * mdev ;
2016-08-26 10:34:58 +03:00
u32 dev_mapbase ;
2014-02-26 20:29:05 +04:00
u32 offset ;
u32 size ;
int ret ;
__le32 reg1 ;
__le32 reg2 ;
mdev = mcb_alloc_dev ( bus ) ;
if ( ! mdev )
return - ENOMEM ;
reg1 = readl ( & gdd - > reg1 ) ;
reg2 = readl ( & gdd - > reg2 ) ;
offset = readl ( & gdd - > offset ) ;
size = readl ( & gdd - > size ) ;
mdev - > id = GDD_DEV ( reg1 ) ;
mdev - > rev = GDD_REV ( reg1 ) ;
mdev - > var = GDD_VAR ( reg1 ) ;
2016-05-03 13:42:00 +03:00
mdev - > bar = GDD_BAR ( reg2 ) ;
2014-02-26 20:29:05 +04:00
mdev - > group = GDD_GRP ( reg2 ) ;
mdev - > inst = GDD_INS ( reg2 ) ;
2016-08-26 10:34:58 +03:00
/*
* If the BAR is missing , dev_mapbase is zero , or if the
* device is IO mapped we just print a warning and go on with the
* next device , instead of completely stop the gdd parser
*/
if ( mdev - > bar > bar_count - 1 ) {
pr_info ( " No BAR for 16z%03d \n " , mdev - > id ) ;
ret = 0 ;
goto err ;
}
dev_mapbase = cb [ mdev - > bar ] . addr ;
if ( ! dev_mapbase ) {
pr_info ( " BAR not assigned for 16z%03d \n " , mdev - > id ) ;
ret = 0 ;
goto err ;
}
if ( dev_mapbase & 0x01 ) {
pr_info ( " IO mapped Device (16z%03d) not yet supported \n " ,
mdev - > id ) ;
ret = 0 ;
goto err ;
}
2014-02-26 20:29:05 +04:00
pr_debug ( " Found a 16z%03d \n " , mdev - > id ) ;
mdev - > irq . start = GDD_IRQ ( reg1 ) ;
mdev - > irq . end = GDD_IRQ ( reg1 ) ;
mdev - > irq . flags = IORESOURCE_IRQ ;
2016-08-26 10:34:58 +03:00
mdev - > mem . start = dev_mapbase + offset ;
2014-02-26 20:29:05 +04:00
mdev - > mem . end = mdev - > mem . start + size - 1 ;
mdev - > mem . flags = IORESOURCE_MEM ;
mdev - > is_added = false ;
ret = mcb_device_register ( bus , mdev ) ;
if ( ret < 0 )
goto err ;
return 0 ;
err :
2022-12-02 12:38:50 +03:00
put_device ( & mdev - > dev ) ;
2014-02-26 20:29:05 +04:00
return ret ;
}
2016-08-26 10:34:58 +03:00
static void chameleon_parse_bar ( void __iomem * base ,
struct chameleon_bar * cb , int bar_count )
{
char __iomem * p = base ;
int i ;
/* skip reg1 */
p + = sizeof ( __le32 ) ;
for ( i = 0 ; i < bar_count ; i + + ) {
cb [ i ] . addr = readl ( p ) ;
cb [ i ] . size = readl ( p + 4 ) ;
p + = sizeof ( struct chameleon_bar ) ;
}
}
2023-04-11 11:33:27 +03:00
static int chameleon_get_bar ( void __iomem * * base , phys_addr_t mapbase ,
2016-08-26 10:34:58 +03:00
struct chameleon_bar * * cb )
{
struct chameleon_bar * c ;
int bar_count ;
__le32 reg ;
u32 dtype ;
/*
* For those devices which are not connected
* to the PCI Bus ( e . g . LPC ) there is a bar
* descriptor located directly after the
* chameleon header . This header is comparable
* to a PCI header .
*/
dtype = get_next_dtype ( * base ) ;
if ( dtype = = CHAMELEON_DTYPE_BAR ) {
reg = readl ( * base ) ;
bar_count = BAR_CNT ( reg ) ;
2016-11-15 12:36:51 +03:00
if ( bar_count < = 0 | | bar_count > CHAMELEON_BAR_MAX )
2016-08-26 10:34:58 +03:00
return - ENODEV ;
c = kcalloc ( bar_count , sizeof ( struct chameleon_bar ) ,
GFP_KERNEL ) ;
if ( ! c )
return - ENOMEM ;
chameleon_parse_bar ( * base , c , bar_count ) ;
* base + = BAR_DESC_SIZE ( bar_count ) ;
} else {
c = kzalloc ( sizeof ( struct chameleon_bar ) , GFP_KERNEL ) ;
if ( ! c )
return - ENOMEM ;
bar_count = 1 ;
c - > addr = mapbase ;
}
* cb = c ;
return bar_count ;
}
2014-02-26 20:29:05 +04:00
int chameleon_parse_cells ( struct mcb_bus * bus , phys_addr_t mapbase ,
void __iomem * base )
{
struct chameleon_fpga_header * header ;
2016-08-26 10:34:58 +03:00
struct chameleon_bar * cb ;
2023-04-11 11:33:27 +03:00
void __iomem * p = base ;
2014-02-26 20:29:05 +04:00
int num_cells = 0 ;
2016-08-26 10:34:58 +03:00
uint32_t dtype ;
int bar_count ;
2017-08-29 15:47:25 +03:00
int ret ;
2014-02-26 20:29:05 +04:00
u32 hsize ;
2023-04-11 11:33:27 +03:00
u32 table_size ;
2014-02-26 20:29:05 +04:00
hsize = sizeof ( struct chameleon_fpga_header ) ;
header = kzalloc ( hsize , GFP_KERNEL ) ;
if ( ! header )
return - ENOMEM ;
/* Extract header information */
memcpy_fromio ( header , p , hsize ) ;
/* We only support chameleon v2 at the moment */
header - > magic = le16_to_cpu ( header - > magic ) ;
if ( header - > magic ! = CHAMELEONV2_MAGIC ) {
pr_err ( " Unsupported chameleon version 0x%x \n " ,
header - > magic ) ;
2016-08-26 10:34:58 +03:00
ret = - ENODEV ;
goto free_header ;
2014-02-26 20:29:05 +04:00
}
p + = hsize ;
2016-05-03 10:46:23 +03:00
bus - > revision = header - > revision ;
bus - > model = header - > model ;
bus - > minor = header - > minor ;
snprintf ( bus - > name , CHAMELEON_FILENAME_LEN + 1 , " %s " ,
header - > filename ) ;
2014-02-26 20:29:05 +04:00
2016-08-26 10:34:58 +03:00
bar_count = chameleon_get_bar ( & p , mapbase , & cb ) ;
2017-08-29 15:47:25 +03:00
if ( bar_count < 0 ) {
ret = bar_count ;
2016-08-26 10:34:58 +03:00
goto free_header ;
2017-08-29 15:47:25 +03:00
}
2016-08-26 10:34:58 +03:00
2014-02-26 20:29:05 +04:00
for_each_chameleon_cell ( dtype , p ) {
switch ( dtype ) {
case CHAMELEON_DTYPE_GENERAL :
2016-08-26 10:34:58 +03:00
ret = chameleon_parse_gdd ( bus , cb , p , bar_count ) ;
2014-02-26 20:29:05 +04:00
if ( ret < 0 )
2016-08-26 10:34:58 +03:00
goto free_bar ;
2014-02-26 20:29:05 +04:00
p + = sizeof ( struct chameleon_gdd ) ;
break ;
case CHAMELEON_DTYPE_BRIDGE :
2016-08-26 10:34:58 +03:00
chameleon_parse_bdd ( bus , cb , p ) ;
2014-02-26 20:29:05 +04:00
p + = sizeof ( struct chameleon_bdd ) ;
break ;
case CHAMELEON_DTYPE_END :
break ;
default :
pr_err ( " Invalid chameleon descriptor type 0x%x \n " ,
dtype ) ;
2016-08-26 10:34:58 +03:00
ret = - EINVAL ;
goto free_bar ;
2014-02-26 20:29:05 +04:00
}
num_cells + + ;
}
2023-04-11 11:33:27 +03:00
if ( num_cells = = 0 ) {
ret = - EINVAL ;
goto free_bar ;
}
2014-02-26 20:29:05 +04:00
2023-04-11 11:33:27 +03:00
table_size = p - base ;
pr_debug ( " %d cell(s) found. Chameleon table size: 0x%04x bytes \n " , num_cells , table_size ) ;
2016-08-26 10:34:58 +03:00
kfree ( cb ) ;
2014-02-26 20:29:05 +04:00
kfree ( header ) ;
2023-04-11 11:33:27 +03:00
return table_size ;
2014-02-26 20:29:05 +04:00
2016-08-26 10:34:58 +03:00
free_bar :
kfree ( cb ) ;
free_header :
2014-02-26 20:29:05 +04:00
kfree ( header ) ;
2016-08-26 10:34:58 +03:00
2014-02-26 20:29:05 +04:00
return ret ;
}
2019-10-16 13:01:58 +03:00
EXPORT_SYMBOL_NS_GPL ( chameleon_parse_cells , MCB ) ;