2018-04-10 20:57:23 +03:00
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */
2020-03-09 21:57:04 +03:00
# include <linux/debugfs.h>
2018-04-10 20:57:23 +03:00
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_reserved_mem.h>
# include <linux/platform_device.h>
2020-03-09 21:57:04 +03:00
# include <linux/seq_file.h>
2018-04-10 20:57:23 +03:00
# include <linux/types.h>
# include <soc/qcom/cmd-db.h>
# define NUM_PRIORITY 2
# define MAX_SLV_ID 8
# define SLAVE_ID_MASK 0x7
# define SLAVE_ID_SHIFT 16
/**
* struct entry_header : header for each entry in cmddb
*
* @ id : resource ' s identifier
* @ priority : unused
* @ addr : the address of the resource
* @ len : length of the data
* @ offset : offset from : @ data_offset , start of the data
*/
struct entry_header {
2018-04-19 21:29:24 +03:00
u8 id [ 8 ] ;
__le32 priority [ NUM_PRIORITY ] ;
__le32 addr ;
__le16 len ;
__le16 offset ;
2018-04-10 20:57:23 +03:00
} ;
/**
* struct rsc_hdr : resource header information
*
* @ slv_id : id for the resource
* @ header_offset : entry ' s header at offset from the end of the cmd_db_header
* @ data_offset : entry ' s data at offset from the end of the cmd_db_header
* @ cnt : number of entries for HW type
* @ version : MSB is major , LSB is minor
* @ reserved : reserved for future use .
*/
struct rsc_hdr {
2018-04-19 21:29:24 +03:00
__le16 slv_id ;
__le16 header_offset ;
__le16 data_offset ;
__le16 cnt ;
__le16 version ;
__le16 reserved [ 3 ] ;
2018-04-10 20:57:23 +03:00
} ;
/**
* struct cmd_db_header : The DB header information
*
* @ version : The cmd db version
2018-04-19 21:29:24 +03:00
* @ magic : constant expected in the database
2018-04-10 20:57:23 +03:00
* @ header : array of resources
* @ checksum : checksum for the header . Unused .
* @ reserved : reserved memory
* @ data : driver specific data
*/
struct cmd_db_header {
2018-04-19 21:29:24 +03:00
__le32 version ;
u8 magic [ 4 ] ;
2018-04-10 20:57:23 +03:00
struct rsc_hdr header [ MAX_SLV_ID ] ;
2018-04-19 21:29:24 +03:00
__le32 checksum ;
__le32 reserved ;
2018-04-10 20:57:23 +03:00
u8 data [ ] ;
} ;
/**
* DOC : Description of the Command DB database .
*
* At the start of the command DB memory is the cmd_db_header structure .
* The cmd_db_header holds the version , checksum , magic key as well as an
* array for header for each slave ( depicted by the rsc_header ) . Each h / w
* based accelerator is a ' slave ' ( shared resource ) and has slave id indicating
* the type of accelerator . The rsc_header is the header for such individual
* slaves of a given type . The entries for each of these slaves begin at the
* rsc_hdr . header_offset . In addition each slave could have auxiliary data
* that may be needed by the driver . The data for the slave starts at the
* entry_header . offset to the location pointed to by the rsc_hdr . data_offset .
*
* Drivers have a stringified key to a slave / resource . They can query the slave
* information and get the slave id and the auxiliary data and the length of the
* data . Using this information , they can format the request to be sent to the
* h / w accelerator and request a resource state .
*/
2018-04-19 21:29:24 +03:00
static const u8 CMD_DB_MAGIC [ ] = { 0xdb , 0x30 , 0x03 , 0x0c } ;
static bool cmd_db_magic_matches ( const struct cmd_db_header * header )
{
const u8 * magic = header - > magic ;
return memcmp ( magic , CMD_DB_MAGIC , ARRAY_SIZE ( CMD_DB_MAGIC ) ) = = 0 ;
}
2018-04-10 20:57:23 +03:00
static struct cmd_db_header * cmd_db_header ;
2018-09-26 21:02:33 +03:00
static inline const void * rsc_to_entry_header ( const struct rsc_hdr * hdr )
2018-04-19 21:29:24 +03:00
{
u16 offset = le16_to_cpu ( hdr - > header_offset ) ;
return cmd_db_header - > data + offset ;
}
static inline void *
2018-09-26 21:02:33 +03:00
rsc_offset ( const struct rsc_hdr * hdr , const struct entry_header * ent )
2018-04-19 21:29:24 +03:00
{
u16 offset = le16_to_cpu ( hdr - > data_offset ) ;
u16 loffset = le16_to_cpu ( ent - > offset ) ;
return cmd_db_header - > data + offset + loffset ;
}
2018-04-10 20:57:23 +03:00
/**
* cmd_db_ready - Indicates if command DB is available
*
* Return : 0 on success , errno otherwise
*/
int cmd_db_ready ( void )
{
if ( cmd_db_header = = NULL )
return - EPROBE_DEFER ;
2018-04-19 21:29:24 +03:00
else if ( ! cmd_db_magic_matches ( cmd_db_header ) )
2018-04-10 20:57:23 +03:00
return - EINVAL ;
return 0 ;
}
EXPORT_SYMBOL ( cmd_db_ready ) ;
2018-09-26 21:02:33 +03:00
static int cmd_db_get_header ( const char * id , const struct entry_header * * eh ,
const struct rsc_hdr * * rh )
2018-04-10 20:57:23 +03:00
{
2018-09-26 21:02:33 +03:00
const struct rsc_hdr * rsc_hdr ;
const struct entry_header * ent ;
2018-04-10 20:57:23 +03:00
int ret , i , j ;
2018-04-19 21:29:24 +03:00
u8 query [ 8 ] ;
2018-04-10 20:57:23 +03:00
ret = cmd_db_ready ( ) ;
if ( ret )
return ret ;
2018-04-19 21:29:24 +03:00
/* Pad out query string to same length as in DB */
strncpy ( query , id , sizeof ( query ) ) ;
2018-04-10 20:57:23 +03:00
for ( i = 0 ; i < MAX_SLV_ID ; i + + ) {
rsc_hdr = & cmd_db_header - > header [ i ] ;
if ( ! rsc_hdr - > slv_id )
break ;
2018-04-19 21:29:24 +03:00
ent = rsc_to_entry_header ( rsc_hdr ) ;
for ( j = 0 ; j < le16_to_cpu ( rsc_hdr - > cnt ) ; j + + , ent + + ) {
2018-09-26 21:02:33 +03:00
if ( memcmp ( ent - > id , query , sizeof ( ent - > id ) ) = = 0 ) {
if ( eh )
* eh = ent ;
if ( rh )
* rh = rsc_hdr ;
return 0 ;
}
2018-04-10 20:57:23 +03:00
}
}
return - ENODEV ;
}
/**
* cmd_db_read_addr ( ) - Query command db for resource id address .
*
* @ id : resource id to query for address
*
* Return : resource address on success , 0 on error
*
* This is used to retrieve resource address based on resource
* id .
*/
u32 cmd_db_read_addr ( const char * id )
{
int ret ;
2018-09-26 21:02:33 +03:00
const struct entry_header * ent ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:33 +03:00
ret = cmd_db_get_header ( id , & ent , NULL ) ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:33 +03:00
return ret < 0 ? 0 : le32_to_cpu ( ent - > addr ) ;
2018-04-10 20:57:23 +03:00
}
EXPORT_SYMBOL ( cmd_db_read_addr ) ;
/**
* cmd_db_read_aux_data ( ) - Query command db for aux data .
*
2018-09-26 21:02:34 +03:00
* @ id : Resource to retrieve AUX Data on
* @ len : size of data buffer returned
2018-04-10 20:57:23 +03:00
*
2018-09-26 21:02:34 +03:00
* Return : pointer to data on success , error pointer otherwise
2018-04-10 20:57:23 +03:00
*/
2018-09-26 21:02:34 +03:00
const void * cmd_db_read_aux_data ( const char * id , size_t * len )
2018-04-10 20:57:23 +03:00
{
int ret ;
2018-09-26 21:02:33 +03:00
const struct entry_header * ent ;
const struct rsc_hdr * rsc_hdr ;
2018-04-10 20:57:23 +03:00
2018-04-19 21:29:24 +03:00
ret = cmd_db_get_header ( id , & ent , & rsc_hdr ) ;
2018-04-10 20:57:23 +03:00
if ( ret )
2018-09-26 21:02:34 +03:00
return ERR_PTR ( ret ) ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:34 +03:00
if ( len )
* len = le16_to_cpu ( ent - > len ) ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:34 +03:00
return rsc_offset ( rsc_hdr , ent ) ;
2018-04-10 20:57:23 +03:00
}
EXPORT_SYMBOL ( cmd_db_read_aux_data ) ;
/**
* cmd_db_read_slave_id - Get the slave ID for a given resource address
*
* @ id : Resource id to query the DB for version
*
* Return : cmd_db_hw_type enum on success , CMD_DB_HW_INVALID on error
*/
enum cmd_db_hw_type cmd_db_read_slave_id ( const char * id )
{
int ret ;
2018-09-26 21:02:33 +03:00
const struct entry_header * ent ;
2018-04-19 21:29:24 +03:00
u32 addr ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:33 +03:00
ret = cmd_db_get_header ( id , & ent , NULL ) ;
2018-04-19 21:29:24 +03:00
if ( ret < 0 )
return CMD_DB_HW_INVALID ;
2018-04-10 20:57:23 +03:00
2018-09-26 21:02:33 +03:00
addr = le32_to_cpu ( ent - > addr ) ;
2018-04-19 21:29:24 +03:00
return ( addr > > SLAVE_ID_SHIFT ) & SLAVE_ID_MASK ;
2018-04-10 20:57:23 +03:00
}
EXPORT_SYMBOL ( cmd_db_read_slave_id ) ;
2020-03-09 21:57:04 +03:00
# ifdef CONFIG_DEBUG_FS
static int cmd_db_debugfs_dump ( struct seq_file * seq , void * p )
{
int i , j ;
const struct rsc_hdr * rsc ;
const struct entry_header * ent ;
const char * name ;
u16 len , version ;
u8 major , minor ;
seq_puts ( seq , " Command DB DUMP \n " ) ;
for ( i = 0 ; i < MAX_SLV_ID ; i + + ) {
rsc = & cmd_db_header - > header [ i ] ;
if ( ! rsc - > slv_id )
break ;
2020-04-17 03:06:45 +03:00
switch ( le16_to_cpu ( rsc - > slv_id ) ) {
2020-03-09 21:57:04 +03:00
case CMD_DB_HW_ARC :
name = " ARC " ;
break ;
case CMD_DB_HW_VRM :
name = " VRM " ;
break ;
case CMD_DB_HW_BCM :
name = " BCM " ;
break ;
default :
name = " Unknown " ;
break ;
}
version = le16_to_cpu ( rsc - > version ) ;
major = version > > 8 ;
minor = version ;
seq_printf ( seq , " Slave %s (v%u.%u) \n " , name , major , minor ) ;
seq_puts ( seq , " ------------------------- \n " ) ;
ent = rsc_to_entry_header ( rsc ) ;
for ( j = 0 ; j < le16_to_cpu ( rsc - > cnt ) ; j + + , ent + + ) {
2020-04-15 22:29:16 +03:00
seq_printf ( seq , " 0x%05x: %*pEp " , le32_to_cpu ( ent - > addr ) ,
2020-04-15 09:20:33 +03:00
( int ) sizeof ( ent - > id ) , ent - > id ) ;
2020-03-09 21:57:04 +03:00
len = le16_to_cpu ( ent - > len ) ;
if ( len ) {
seq_printf ( seq , " [%*ph] " ,
len , rsc_offset ( rsc , ent ) ) ;
}
seq_putc ( seq , ' \n ' ) ;
}
}
return 0 ;
}
static int open_cmd_db_debugfs ( struct inode * inode , struct file * file )
{
return single_open ( file , cmd_db_debugfs_dump , inode - > i_private ) ;
}
# endif
static const struct file_operations cmd_db_debugfs_ops = {
# ifdef CONFIG_DEBUG_FS
. open = open_cmd_db_debugfs ,
# endif
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2018-04-10 20:57:23 +03:00
static int cmd_db_dev_probe ( struct platform_device * pdev )
{
struct reserved_mem * rmem ;
int ret = 0 ;
rmem = of_reserved_mem_lookup ( pdev - > dev . of_node ) ;
if ( ! rmem ) {
dev_err ( & pdev - > dev , " failed to acquire memory region \n " ) ;
return - EINVAL ;
}
cmd_db_header = memremap ( rmem - > base , rmem - > size , MEMREMAP_WB ) ;
2019-02-28 08:48:49 +03:00
if ( ! cmd_db_header ) {
ret = - ENOMEM ;
2018-04-10 20:57:23 +03:00
cmd_db_header = NULL ;
return ret ;
}
2018-04-19 21:29:24 +03:00
if ( ! cmd_db_magic_matches ( cmd_db_header ) ) {
2018-04-10 20:57:23 +03:00
dev_err ( & pdev - > dev , " Invalid Command DB Magic \n " ) ;
return - EINVAL ;
}
2020-03-09 21:57:04 +03:00
debugfs_create_file ( " cmd-db " , 0400 , NULL , NULL , & cmd_db_debugfs_ops ) ;
2018-04-10 20:57:23 +03:00
return 0 ;
}
static const struct of_device_id cmd_db_match_table [ ] = {
{ . compatible = " qcom,cmd-db " } ,
2020-03-09 21:57:04 +03:00
{ }
2018-04-10 20:57:23 +03:00
} ;
static struct platform_driver cmd_db_dev_driver = {
. probe = cmd_db_dev_probe ,
. driver = {
. name = " cmd-db " ,
. of_match_table = cmd_db_match_table ,
} ,
} ;
static int __init cmd_db_device_init ( void )
{
return platform_driver_register ( & cmd_db_dev_driver ) ;
}
arch_initcall ( cmd_db_device_init ) ;