2019-05-29 07:18:00 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-10 18:00:26 -07:00
/*
* Copyright ( C ) 2012 Regents of the University of California
*/
2023-05-15 11:19:22 +05:30
# include <linux/acpi.h>
2022-07-27 10:08:29 +05:30
# include <linux/cpu.h>
2023-05-02 00:17:38 +08:00
# include <linux/ctype.h>
2017-07-10 18:00:26 -07:00
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/of.h>
2023-05-15 11:19:22 +05:30
# include <asm/acpi.h>
2023-04-07 16:10:58 -07:00
# include <asm/cpufeature.h>
2022-07-27 10:08:29 +05:30
# include <asm/csr.h>
2022-03-14 13:38:45 -07:00
# include <asm/hwcap.h>
2022-07-27 10:08:29 +05:30
# include <asm/sbi.h>
2018-10-02 12:15:05 -07:00
# include <asm/smp.h>
2021-12-06 11:46:52 +01:00
# include <asm/pgtable.h>
2017-07-10 18:00:26 -07:00
2018-10-02 12:15:00 -07:00
/*
2019-01-18 15:03:06 +01:00
* Returns the hart ID of the given device tree node , or - ENODEV if the node
* isn ' t an enabled and valid RISC - V hart node .
2018-10-02 12:15:00 -07:00
*/
2022-05-27 10:47:42 +05:30
int riscv_of_processor_hartid ( struct device_node * node , unsigned long * hart )
2023-06-07 21:28:26 +01:00
{
int cpu ;
* hart = ( unsigned long ) of_get_cpu_hwid ( node , 0 ) ;
if ( * hart = = ~ 0UL ) {
pr_warn ( " Found CPU without hart ID \n " ) ;
return - ENODEV ;
}
cpu = riscv_hartid_to_cpuid ( * hart ) ;
if ( cpu < 0 )
return cpu ;
if ( ! cpu_possible ( cpu ) )
return - ENODEV ;
return 0 ;
}
int riscv_early_of_processor_hartid ( struct device_node * node , unsigned long * hart )
2017-07-10 18:00:26 -07:00
{
2019-01-18 15:03:07 +01:00
const char * isa ;
2017-07-10 18:00:26 -07:00
if ( ! of_device_is_compatible ( node , " riscv " ) ) {
pr_warn ( " Found incompatible CPU \n " ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2023-06-07 21:28:26 +01:00
* hart = ( unsigned long ) of_get_cpu_hwid ( node , 0 ) ;
2022-05-27 10:47:42 +05:30
if ( * hart = = ~ 0UL ) {
2017-07-10 18:00:26 -07:00
pr_warn ( " Found CPU without hart ID \n " ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2019-01-18 15:03:07 +01:00
if ( ! of_device_is_available ( node ) ) {
2022-05-27 10:47:42 +05:30
pr_info ( " CPU with hartid=%lu is not available \n " , * hart ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
if ( of_property_read_string ( node , " riscv,isa " , & isa ) ) {
2022-05-27 10:47:42 +05:30
pr_warn ( " CPU with hartid=%lu has no \" riscv,isa \" property \n " , * hart ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2023-05-02 00:17:38 +08:00
if ( tolower ( isa [ 0 ] ) ! = ' r ' | | tolower ( isa [ 1 ] ) ! = ' v ' ) {
2022-05-27 10:47:42 +05:30
pr_warn ( " CPU with hartid=%lu has an invalid ISA of \" %s \" \n " , * hart , isa ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2022-05-27 10:47:42 +05:30
return 0 ;
2017-07-10 18:00:26 -07:00
}
2020-06-01 14:45:39 +05:30
/*
* Find hart ID of the CPU DT node under which given DT node falls .
*
* To achieve this , we walk up the DT tree until we find an active
* RISC - V core ( HART ) node and extract the cpuid from it .
*/
2022-05-27 10:47:42 +05:30
int riscv_of_parent_hartid ( struct device_node * node , unsigned long * hartid )
2020-06-01 14:45:39 +05:30
{
2022-05-27 10:47:42 +05:30
int rc ;
2020-06-01 14:45:39 +05:30
for ( ; node ; node = node - > parent ) {
2022-05-27 10:47:42 +05:30
if ( of_device_is_compatible ( node , " riscv " ) ) {
rc = riscv_of_processor_hartid ( node , hartid ) ;
if ( ! rc )
return 0 ;
}
2020-06-01 14:45:39 +05:30
}
return - 1 ;
}
2023-04-07 16:10:58 -07:00
DEFINE_PER_CPU ( struct riscv_cpuinfo , riscv_cpuinfo ) ;
2022-07-27 10:08:29 +05:30
2022-10-12 01:18:40 +02:00
unsigned long riscv_cached_mvendorid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > mvendorid ;
}
EXPORT_SYMBOL ( riscv_cached_mvendorid ) ;
unsigned long riscv_cached_marchid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > marchid ;
}
EXPORT_SYMBOL ( riscv_cached_marchid ) ;
unsigned long riscv_cached_mimpid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > mimpid ;
}
EXPORT_SYMBOL ( riscv_cached_mimpid ) ;
2022-07-27 10:08:29 +05:30
static int riscv_cpuinfo_starting ( unsigned int cpu )
{
struct riscv_cpuinfo * ci = this_cpu_ptr ( & riscv_cpuinfo ) ;
# if IS_ENABLED(CONFIG_RISCV_SBI)
ci - > mvendorid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_mvendorid ( ) ;
ci - > marchid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_marchid ( ) ;
ci - > mimpid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_mimpid ( ) ;
# elif IS_ENABLED(CONFIG_RISCV_M_MODE)
ci - > mvendorid = csr_read ( CSR_MVENDORID ) ;
ci - > marchid = csr_read ( CSR_MARCHID ) ;
ci - > mimpid = csr_read ( CSR_MIMPID ) ;
# else
ci - > mvendorid = 0 ;
ci - > marchid = 0 ;
ci - > mimpid = 0 ;
# endif
return 0 ;
}
static int __init riscv_cpuinfo_init ( void )
{
int ret ;
ret = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " riscv/cpuinfo:starting " ,
riscv_cpuinfo_starting , NULL ) ;
if ( ret < 0 ) {
pr_err ( " cpuinfo: failed to register hotplug callbacks. \n " ) ;
return ret ;
}
return 0 ;
}
2022-10-12 01:18:40 +02:00
arch_initcall ( riscv_cpuinfo_init ) ;
# ifdef CONFIG_PROC_FS
2022-07-27 10:08:29 +05:30
2022-03-14 13:38:45 -07:00
# define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \
{ \
. uprop = # UPROP , \
. isa_ext_id = EXTID , \
}
2022-12-05 14:45:24 +00:00
2022-03-28 15:04:17 -07:00
/*
2022-12-05 14:45:24 +00:00
* The canonical order of ISA extension names in the ISA string is defined in
* chapter 27 of the unprivileged specification .
*
* Ordinarily , for in - kernel data structures , this order is unimportant but
* isa_ext_arr defines the order of the ISA string in / proc / cpuinfo .
*
* The specification uses vague wording , such as should , when it comes to
* ordering , so for our purposes the following rules apply :
*
* 1. All multi - letter extensions must be separated from other extensions by an
* underscore .
*
* 2. Additional standard extensions ( starting with ' Z ' ) must be sorted after
* single - letter extensions and before any higher - privileged extensions .
* 3. The first letter following the ' Z ' conventionally indicates the most
2022-03-14 13:38:45 -07:00
* closely related alphabetical extension category , IMAFDQLCBKJTPVH .
2022-12-05 14:45:24 +00:00
* If multiple ' Z ' extensions are named , they must be ordered first by
* category , then alphabetically within a category .
*
* 3. Standard supervisor - level extensions ( starting with ' S ' ) must be listed
* after standard unprivileged extensions . If multiple supervisor - level
* extensions are listed , they must be ordered alphabetically .
*
* 4. Standard machine - level extensions ( starting with ' Zxm ' ) must be listed
* after any lower - privileged , standard extensions . If multiple
* machine - level extensions are listed , they must be ordered
2022-03-14 13:38:45 -07:00
* alphabetically .
2022-12-05 14:45:24 +00:00
*
* 5. Non - standard extensions ( starting with ' X ' ) must be listed after all
* standard extensions . If multiple non - standard extensions are listed , they
* must be ordered alphabetically .
*
* An example string following the order is :
* rv64imadc_zifoo_zigoo_zafoo_sbar_scar_zxmbaz_xqux_xrux
*
* New entries to this struct should follow the ordering rules described above .
2022-03-14 13:38:45 -07:00
*/
static struct riscv_isa_ext_data isa_ext_arr [ ] = {
2022-12-05 14:45:25 +00:00
__RISCV_ISA_EXT_DATA ( zicbom , RISCV_ISA_EXT_ZICBOM ) ,
2023-02-24 17:26:27 +01:00
__RISCV_ISA_EXT_DATA ( zicboz , RISCV_ISA_EXT_ZICBOZ ) ,
2022-12-05 14:45:25 +00:00
__RISCV_ISA_EXT_DATA ( zihintpause , RISCV_ISA_EXT_ZIHINTPAUSE ) ,
2023-02-08 23:53:27 +01:00
__RISCV_ISA_EXT_DATA ( zbb , RISCV_ISA_EXT_ZBB ) ,
2023-03-06 12:10:15 +05:30
__RISCV_ISA_EXT_DATA ( smaia , RISCV_ISA_EXT_SMAIA ) ,
__RISCV_ISA_EXT_DATA ( ssaia , RISCV_ISA_EXT_SSAIA ) ,
2022-02-18 16:46:58 -08:00
__RISCV_ISA_EXT_DATA ( sscofpmf , RISCV_ISA_EXT_SSCOFPMF ) ,
2022-09-20 13:45:18 -07:00
__RISCV_ISA_EXT_DATA ( sstc , RISCV_ISA_EXT_SSTC ) ,
2022-10-11 20:07:44 -07:00
__RISCV_ISA_EXT_DATA ( svinval , RISCV_ISA_EXT_SVINVAL ) ,
2023-02-09 21:16:45 +08:00
__RISCV_ISA_EXT_DATA ( svnapot , RISCV_ISA_EXT_SVNAPOT ) ,
2022-05-11 21:29:18 +02:00
__RISCV_ISA_EXT_DATA ( svpbmt , RISCV_ISA_EXT_SVPBMT ) ,
2022-03-14 13:38:45 -07:00
__RISCV_ISA_EXT_DATA ( " " , RISCV_ISA_EXT_MAX ) ,
} ;
static void print_isa_ext ( struct seq_file * f )
{
struct riscv_isa_ext_data * edata ;
int i = 0 , arr_sz ;
arr_sz = ARRAY_SIZE ( isa_ext_arr ) - 1 ;
/* No extension support available */
if ( arr_sz < = 0 )
return ;
for ( i = 0 ; i < = arr_sz ; i + + ) {
edata = & isa_ext_arr [ i ] ;
if ( ! __riscv_isa_extension_available ( NULL , edata - > isa_ext_id ) )
continue ;
seq_printf ( f , " _%s " , edata - > uprop ) ;
}
}
2022-03-28 15:04:17 -07:00
/*
2022-03-14 13:38:45 -07:00
* These are the only valid base ( single letter ) ISA extensions as per the spec .
* It also specifies the canonical order in which it appears in the spec .
* Some of the extension may just be a place holder for now ( B , K , P , J ) .
* This should be updated once corresponding extensions are ratified .
*/
static const char base_riscv_exts [ 13 ] = " imafdqcbkjpvh " ;
2017-07-10 18:00:26 -07:00
2019-10-09 15:00:57 -07:00
static void print_isa ( struct seq_file * f , const char * isa )
2018-10-02 12:14:56 -07:00
{
2022-03-14 13:38:45 -07:00
int i ;
2018-10-02 12:15:06 -07:00
seq_puts ( f , " isa \t \t : " ) ;
2022-03-14 13:38:45 -07:00
/* Print the rv[64/32] part */
seq_write ( f , isa , 4 ) ;
for ( i = 0 ; i < sizeof ( base_riscv_exts ) ; i + + ) {
if ( __riscv_isa_extension_available ( NULL , base_riscv_exts [ i ] - ' a ' ) )
/* Print only enabled the base ISA extensions */
seq_write ( f , & base_riscv_exts [ i ] , 1 ) ;
}
print_isa_ext ( f ) ;
2018-10-02 12:15:06 -07:00
seq_puts ( f , " \n " ) ;
2018-10-02 12:14:56 -07:00
}
2021-12-06 11:46:52 +01:00
static void print_mmu ( struct seq_file * f )
2018-10-02 12:14:56 -07:00
{
2021-12-06 11:46:52 +01:00
char sv_type [ 16 ] ;
2022-04-14 19:30:36 +02:00
# ifdef CONFIG_MMU
2018-10-02 12:14:56 -07:00
# if defined(CONFIG_32BIT)
2021-12-06 11:46:52 +01:00
strncpy ( sv_type , " sv32 " , 5 ) ;
2018-10-02 12:14:56 -07:00
# elif defined(CONFIG_64BIT)
2022-01-27 10:48:43 +08:00
if ( pgtable_l5_enabled )
strncpy ( sv_type , " sv57 " , 5 ) ;
else if ( pgtable_l4_enabled )
2021-12-06 11:46:52 +01:00
strncpy ( sv_type , " sv48 " , 5 ) ;
else
strncpy ( sv_type , " sv39 " , 5 ) ;
2018-10-02 12:14:56 -07:00
# endif
2022-04-14 19:30:36 +02:00
# else
strncpy ( sv_type , " none " , 5 ) ;
# endif /* CONFIG_MMU */
2021-12-06 11:46:52 +01:00
seq_printf ( f , " mmu \t \t : %s \n " , sv_type ) ;
2018-10-02 12:14:56 -07:00
}
2017-07-10 18:00:26 -07:00
static void * c_start ( struct seq_file * m , loff_t * pos )
{
2022-10-14 17:58:44 +02:00
if ( * pos = = nr_cpu_ids )
return NULL ;
2017-07-10 18:00:26 -07:00
* pos = cpumask_next ( * pos - 1 , cpu_online_mask ) ;
if ( ( * pos ) < nr_cpu_ids )
return ( void * ) ( uintptr_t ) ( 1 + * pos ) ;
return NULL ;
}
static void * c_next ( struct seq_file * m , void * v , loff_t * pos )
{
( * pos ) + + ;
return c_start ( m , pos ) ;
}
static void c_stop ( struct seq_file * m , void * v )
{
}
static int c_show ( struct seq_file * m , void * v )
{
2018-10-02 12:15:05 -07:00
unsigned long cpu_id = ( unsigned long ) v - 1 ;
2022-07-27 10:08:29 +05:30
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
2023-05-15 11:19:22 +05:30
struct device_node * node ;
2021-12-06 11:46:52 +01:00
const char * compat , * isa ;
2017-07-10 18:00:26 -07:00
2018-10-02 12:15:06 -07:00
seq_printf ( m , " processor \t : %lu \n " , cpu_id ) ;
seq_printf ( m , " hart \t \t : %lu \n " , cpuid_to_hartid_map ( cpu_id ) ) ;
2023-05-15 11:19:22 +05:30
if ( acpi_disabled ) {
node = of_get_cpu_node ( cpu_id , NULL ) ;
if ( ! of_property_read_string ( node , " riscv,isa " , & isa ) )
print_isa ( m , isa ) ;
print_mmu ( m ) ;
if ( ! of_property_read_string ( node , " compatible " , & compat ) & &
strcmp ( compat , " riscv " ) )
seq_printf ( m , " uarch \t \t : %s \n " , compat ) ;
of_node_put ( node ) ;
} else {
if ( ! acpi_get_riscv_isa ( NULL , cpu_id , & isa ) )
print_isa ( m , isa ) ;
print_mmu ( m ) ;
}
2022-07-27 10:08:29 +05:30
seq_printf ( m , " mvendorid \t : 0x%lx \n " , ci - > mvendorid ) ;
seq_printf ( m , " marchid \t \t : 0x%lx \n " , ci - > marchid ) ;
seq_printf ( m , " mimpid \t \t : 0x%lx \n " , ci - > mimpid ) ;
2017-07-10 18:00:26 -07:00
seq_puts ( m , " \n " ) ;
return 0 ;
}
const struct seq_operations cpuinfo_op = {
. start = c_start ,
. next = c_next ,
. stop = c_stop ,
. show = c_show
} ;
# endif /* CONFIG_PROC_FS */