2019-06-04 11:11:33 +03:00
/* SPDX-License-Identifier: GPL-2.0-only */
2005-04-17 02:20:36 +04:00
/ *
* linux/ a r c h / a r m / m m / p r o c - v6 . S
*
* Copyright ( C ) 2 0 0 1 D e e p B l u e S o l u t i o n s L t d .
2006-06-28 17:10:01 +04:00
* Modified b y C a t a l i n M a r i n a s f o r n o M M U s u p p o r t
2005-04-17 02:20:36 +04:00
*
* This i s t h e " s h e l l " o f t h e A R M v6 p r o c e s s o r s u p p o r t .
* /
2009-04-27 22:02:22 +04:00
# include < l i n u x / i n i t . h >
2024-04-23 10:45:47 +03:00
# include < l i n u x / c f i _ t y p e s . h >
2005-04-17 02:20:36 +04:00
# include < l i n u x / l i n k a g e . h >
2020-06-09 07:32:42 +03:00
# include < l i n u x / p g t a b l e . h >
2005-04-17 02:20:36 +04:00
# include < a s m / a s s e m b l e r . h >
2005-09-09 23:08:59 +04:00
# include < a s m / a s m - o f f s e t s . h >
2008-09-07 22:15:31 +04:00
# include < a s m / h w c a p . h >
2006-03-16 17:44:36 +03:00
# include < a s m / p g t a b l e - h w d e f . h >
2005-04-17 02:20:36 +04:00
# include " p r o c - m a c r o s . S "
# define D _ C A C H E _ L I N E _ S I Z E 3 2
2006-03-27 19:59:07 +04:00
# define T T B _ C ( 1 < < 0 )
# define T T B _ S ( 1 < < 1 )
# define T T B _ I M P ( 1 < < 2 )
# define T T B _ R G N _ N C ( 0 < < 3 )
# define T T B _ R G N _ W B W A ( 1 < < 3 )
# define T T B _ R G N _ W T ( 2 < < 3 )
# define T T B _ R G N _ W B ( 3 < < 3 )
2010-09-04 13:47:48 +04:00
# define T T B _ F L A G S _ U P T T B _ R G N _ W B W A
# define P M D _ F L A G S _ U P P M D _ S E C T _ W B
# define T T B _ F L A G S _ S M P T T B _ R G N _ W B W A | T T B _ S
# define P M D _ F L A G S _ S M P P M D _ S E C T _ W B W A | P M D _ S E C T _ S
2007-02-08 23:46:20 +03:00
ARM: 9263/1: use .arch directives instead of assembler command line flags
Similar to commit a6c30873ee4a ("ARM: 8989/1: use .fpu assembler
directives instead of assembler arguments").
GCC and GNU binutils support setting the "sub arch" via -march=,
-Wa,-march, target function attribute, and .arch assembler directive.
Clang was missing support for -Wa,-march=, but this was implemented in
clang-13.
The behavior of both GCC and Clang is to
prefer -Wa,-march= over -march= for assembler and assembler-with-cpp
sources, but Clang will warn about the -march= being unused.
clang: warning: argument unused during compilation: '-march=armv6k'
[-Wunused-command-line-argument]
Since most assembler is non-conditionally assembled with one sub arch
(modulo arch/arm/delay-loop.S which conditionally is assembled as armv4
based on CONFIG_ARCH_RPC, and arch/arm/mach-at91/pm-suspend.S which is
conditionally assembled as armv7-a based on CONFIG_CPU_V7), prefer the
.arch assembler directive.
Add a few more instances found in compile testing as found by Arnd and
Nathan.
Link: https://github.com/llvm/llvm-project/commit/1d51c699b9e2ebc5bcfdbe85c74cc871426333d4
Link: https://bugs.llvm.org/show_bug.cgi?id=48894
Link: https://github.com/ClangBuiltLinux/linux/issues/1195
Link: https://github.com/ClangBuiltLinux/linux/issues/1315
Suggested-by: Arnd Bergmann <arnd@arndb.de>
Suggested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
2022-10-24 22:44:41 +03:00
.arch armv6
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ p r o c _ i n i t )
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ p r o c _ i n i t )
2005-04-17 02:20:36 +04:00
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ p r o c _ f i n )
2005-10-20 02:00:56 +04:00
mrc p15 , 0 , r0 , c1 , c0 , 0 @ ctrl register
bic r0 , r0 , #0x1000 @ ...i............
bic r0 , r0 , #0x0006 @ .............ca.
mcr p15 , 0 , r0 , c1 , c0 , 0 @ disable caches
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ p r o c _ f i n )
2005-04-17 02:20:36 +04:00
/ *
* cpu_ v6 _ r e s e t ( l o c )
*
* Perform a s o f t r e s e t o f t h e s y s t e m . P u t t h e C P U i n t o t h e
* same s t a t e a s i t w o u l d b e i f i t h a d b e e n r e s e t , a n d b r a n c h
* to w h a t w o u l d b e t h e r e s e t v e c t o r .
*
* - loc - l o c a t i o n t o j u m p t o f o r s o f t r e s e t
* /
.align 5
2011-11-15 17:25:04 +04:00
.pushsection .idmap .text , " ax"
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ r e s e t )
2011-06-06 15:27:34 +04:00
mrc p15 , 0 , r1 , c1 , c0 , 0 @ ctrl register
bic r1 , r1 , #0x1 @ ...............m
mcr p15 , 0 , r1 , c1 , c0 , 0 @ disable MMU
mov r1 , #0
mcr p15 , 0 , r1 , c7 , c5 , 4 @ ISB
2014-06-30 19:29:12 +04:00
ret r0
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ r e s e t )
2011-11-15 17:25:04 +04:00
.popsection
2005-04-17 02:20:36 +04:00
/ *
* cpu_ v6 _ d o _ i d l e ( )
*
* Idle t h e p r o c e s s o r ( e g , w a i t f o r i n t e r r u p t ) .
*
* IRQs a r e a l r e a d y d i s a b l e d .
* /
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ d o _ i d l e )
2008-11-10 17:14:11 +03:00
mov r1 , #0
mcr p15 , 0 , r1 , c7 , c10 , 4 @ DWB - WFI may enter a low-power mode
2005-04-17 02:20:36 +04:00
mcr p15 , 0 , r1 , c7 , c0 , 4 @ wait for interrupt
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ d o _ i d l e )
2005-04-17 02:20:36 +04:00
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ d c a c h e _ c l e a n _ a r e a )
2005-04-17 02:20:36 +04:00
1 : mcr p15 , 0 , r0 , c7 , c10 , 1 @ clean D entry
add r0 , r0 , #D _ C A C H E _ L I N E _ S I Z E
subs r1 , r1 , #D _ C A C H E _ L I N E _ S I Z E
bhi 1 b
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ d c a c h e _ c l e a n _ a r e a )
2005-04-17 02:20:36 +04:00
/ *
2012-11-08 00:22:08 +04:00
* cpu_ v6 _ s w i t c h _ m m ( p g d _ p h y s , t s k )
2005-04-17 02:20:36 +04:00
*
* Set t h e t r a n s l a t i o n t a b l e b a s e p o i n t e r t o b e p g d _ p h y s
*
* - pgd_ p h y s - p h y s i c a l a d d r e s s o f n e w T T B
*
* It i s a s s u m e d t h a t :
* - we a r e n o t u s i n g s p l i t p a g e t a b l e s
* /
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ s w i t c h _ m m )
2006-06-28 17:10:01 +04:00
# ifdef C O N F I G _ M M U
2005-04-17 02:20:36 +04:00
mov r2 , #0
2013-02-11 15:25:05 +04:00
mmid r1 , r1 @ get mm->context.id
2010-09-04 13:47:48 +04:00
ALT_ S M P ( o r r r0 , r0 , #T T B _ F L A G S _ S M P )
ALT_ U P ( o r r r0 , r0 , #T T B _ F L A G S _ U P )
2005-08-15 19:53:38 +04:00
mcr p15 , 0 , r2 , c7 , c5 , 6 @ flush BTAC/BTB
2005-04-17 02:20:36 +04:00
mcr p15 , 0 , r2 , c7 , c10 , 4 @ drain write buffer
mcr p15 , 0 , r0 , c2 , c0 , 0 @ set TTB 0
2012-07-06 18:43:03 +04:00
# ifdef C O N F I G _ P I D _ I N _ C O N T E X T I D R
mrc p15 , 0 , r2 , c13 , c0 , 1 @ read current context ID
bic r2 , r2 , #0xff @ extract the PID
and r1 , r1 , #0xff
orr r1 , r1 , r2 @ insert into new context ID
# endif
2005-04-17 02:20:36 +04:00
mcr p15 , 0 , r1 , c13 , c0 , 1 @ set context ID
2006-06-28 17:10:01 +04:00
# endif
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ s w i t c h _ m m )
2005-04-17 02:20:36 +04:00
/ *
2006-12-13 17:34:43 +03:00
* cpu_ v6 _ s e t _ p t e _ e x t ( p t e p , p t e , e x t )
2005-04-17 02:20:36 +04:00
*
* Set a l e v e l 2 t r a n s l a t i o n t a b l e e n t r y .
*
* - ptep - p o i n t e r t o l e v e l 2 t r a n s l a t i o n t a b l e e n t r y
* ( hardware v e r s i o n i s s t o r e d a t - 1 0 2 4 b y t e s )
* - pte - P T E v a l u e t o s t o r e
2006-12-13 17:34:43 +03:00
* - ext - v a l u e f o r e x t e n d e d P T E b i t s
2005-04-17 02:20:36 +04:00
* /
2008-09-07 00:07:45 +04:00
armv6 _ m t _ t a b l e c p u _ v6
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ s e t _ p t e _ e x t )
2006-06-28 17:10:01 +04:00
# ifdef C O N F I G _ M M U
2008-09-07 00:07:45 +04:00
armv6 _ s e t _ p t e _ e x t c p u _ v6
2006-06-28 17:10:01 +04:00
# endif
2014-06-30 19:29:12 +04:00
ret l r
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ s e t _ p t e _ e x t )
2005-04-17 02:20:36 +04:00
2011-02-06 18:48:39 +03:00
/* Suspend/resume support: taken from arch/arm/mach-s3c64xx/sleep.S */
.globl cpu_v6_suspend_size
2011-08-28 13:30:34 +04:00
.equ cpu_ v6 _ s u s p e n d _ s i z e , 4 * 6
2013-04-08 14:44:57 +04:00
# ifdef C O N F I G _ A R M _ C P U _ S U S P E N D
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ d o _ s u s p e n d )
2011-08-28 13:30:34 +04:00
stmfd s p ! , { r4 - r9 , l r }
2011-02-06 18:48:39 +03:00
mrc p15 , 0 , r4 , c13 , c0 , 0 @ FCSE/PID
2012-02-23 17:51:38 +04:00
# ifdef C O N F I G _ M M U
2011-08-28 13:30:34 +04:00
mrc p15 , 0 , r5 , c3 , c0 , 0 @ Domain ID
mrc p15 , 0 , r6 , c2 , c0 , 1 @ Translation table base 1
2012-02-23 17:51:38 +04:00
# endif
2011-08-28 13:30:34 +04:00
mrc p15 , 0 , r7 , c1 , c0 , 1 @ auxiliary control register
mrc p15 , 0 , r8 , c1 , c0 , 2 @ co-processor access control
mrc p15 , 0 , r9 , c1 , c0 , 0 @ control register
stmia r0 , { r4 - r9 }
ldmfd s p ! , { r4 - r9 , p c }
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ d o _ s u s p e n d )
2011-02-06 18:48:39 +03:00
2024-04-23 10:45:47 +03:00
SYM_ T Y P E D _ F U N C _ S T A R T ( c p u _ v6 _ d o _ r e s u m e )
2011-02-06 18:48:39 +03:00
mov i p , #0
mcr p15 , 0 , i p , c7 , c14 , 0 @ clean+invalidate D cache
mcr p15 , 0 , i p , c7 , c5 , 0 @ invalidate I cache
mcr p15 , 0 , i p , c7 , c15 , 0 @ clean+invalidate cache
mcr p15 , 0 , i p , c7 , c10 , 4 @ drain write buffer
2011-08-28 13:30:34 +04:00
mcr p15 , 0 , i p , c13 , c0 , 1 @ set reserved context ID
ldmia r0 , { r4 - r9 }
2011-02-06 18:48:39 +03:00
mcr p15 , 0 , r4 , c13 , c0 , 0 @ FCSE/PID
2012-02-23 17:51:38 +04:00
# ifdef C O N F I G _ M M U
2011-08-28 13:30:34 +04:00
mcr p15 , 0 , r5 , c3 , c0 , 0 @ Domain ID
2011-08-28 01:39:09 +04:00
ALT_ S M P ( o r r r1 , r1 , #T T B _ F L A G S _ S M P )
ALT_ U P ( o r r r1 , r1 , #T T B _ F L A G S _ U P )
mcr p15 , 0 , r1 , c2 , c0 , 0 @ Translation table base 0
2011-08-28 13:30:34 +04:00
mcr p15 , 0 , r6 , c2 , c0 , 1 @ Translation table base 1
2012-02-23 17:51:38 +04:00
mcr p15 , 0 , i p , c2 , c0 , 2 @ TTB control register
# endif
2011-08-28 13:30:34 +04:00
mcr p15 , 0 , r7 , c1 , c0 , 1 @ auxiliary control register
mcr p15 , 0 , r8 , c1 , c0 , 2 @ co-processor access control
2011-02-06 18:48:39 +03:00
mcr p15 , 0 , i p , c7 , c5 , 4 @ ISB
2011-08-28 13:30:34 +04:00
mov r0 , r9 @ control register
2011-02-06 18:48:39 +03:00
b c p u _ r e s u m e _ m m u
2024-04-23 10:45:47 +03:00
SYM_ F U N C _ E N D ( c p u _ v6 _ d o _ r e s u m e )
2011-02-06 18:48:39 +03:00
# endif
2005-04-17 02:20:36 +04:00
2011-06-23 20:25:46 +04:00
string c p u _ v6 _ n a m e , " A R M v6 - c o m p a t i b l e p r o c e s s o r "
2009-08-06 16:12:43 +04:00
2005-04-17 02:20:36 +04:00
.align
/ *
* _ _ v6 _ s e t u p
*
* Initialise T L B , C a c h e s , a n d M M U s t a t e r e a d y t o s w i t c h t h e M M U
* on. R e t u r n i n r0 t h e n e w C P 1 5 C 1 c o n t r o l r e g i s t e r s e t t i n g .
*
* We a u t o m a t i c a l l y d e t e c t i f w e h a v e a H a r v a r d c a c h e , a n d u s e t h e
* Harvard c a c h e c o n t r o l i n s t r u c t i o n s i n s e a d o f t h e u n i f i e d c a c h e
* control i n s t r u c t i o n s .
*
* This s h o u l d b e a b l e t o c o v e r a l l A R M v6 c o r e s .
*
* It i s a s s u m e d t h a t :
* - cache t y p e r e g i s t e r i s i m p l e m e n t e d
* /
__v6_setup :
2005-11-08 00:05:42 +03:00
# ifdef C O N F I G _ S M P
2010-09-04 13:47:48 +04:00
ALT_ S M P ( m r c p15 , 0 , r0 , c1 , c0 , 1 ) @ Enable SMP/nAMP mode
ALT_ U P ( n o p )
2005-11-08 00:05:42 +03:00
orr r0 , r0 , #0x20
2010-09-04 13:47:48 +04:00
ALT_ S M P ( m c r p15 , 0 , r0 , c1 , c0 , 1 )
ALT_ U P ( n o p )
2005-11-08 00:05:42 +03:00
# endif
2005-04-17 02:20:36 +04:00
mov r0 , #0
mcr p15 , 0 , r0 , c7 , c14 , 0 @ clean+invalidate D cache
mcr p15 , 0 , r0 , c7 , c5 , 0 @ invalidate I cache
mcr p15 , 0 , r0 , c7 , c15 , 0 @ clean+invalidate cache
2006-06-28 17:10:01 +04:00
# ifdef C O N F I G _ M M U
2005-04-17 02:20:36 +04:00
mcr p15 , 0 , r0 , c8 , c7 , 0 @ invalidate I + D TLBs
mcr p15 , 0 , r0 , c2 , c0 , 2 @ TTB control register
2010-09-04 13:47:48 +04:00
ALT_ S M P ( o r r r4 , r4 , #T T B _ F L A G S _ S M P )
ALT_ U P ( o r r r4 , r4 , #T T B _ F L A G S _ U P )
2011-05-26 14:22:44 +04:00
ALT_ S M P ( o r r r8 , r8 , #T T B _ F L A G S _ S M P )
ALT_ U P ( o r r r8 , r8 , #T T B _ F L A G S _ U P )
mcr p15 , 0 , r8 , c2 , c0 , 1 @ load TTB1
2006-06-28 17:10:01 +04:00
# endif / * C O N F I G _ M M U * /
2014-02-07 22:12:20 +04:00
mcr p15 , 0 , r0 , c7 , c10 , 4 @ drain write buffer and
@ complete invalidations
2006-06-29 18:09:57 +04:00
adr r5 , v6 _ c r v a l
ldmia r5 , { r5 , r6 }
2013-02-12 22:59:57 +04:00
ARM_ B E 8 ( o r r r6 , r6 , #1 < < 2 5 ) @ big-endian page tables
2005-04-17 02:20:36 +04:00
mrc p15 , 0 , r0 , c1 , c0 , 0 @ read control register
bic r0 , r0 , r5 @ clear bits them
2006-06-29 18:09:57 +04:00
orr r0 , r0 , r6 @ set them
2011-08-15 14:04:41 +04:00
# ifdef C O N F I G _ A R M _ E R R A T A _ 3 6 4 2 9 6
/ *
* Workaround f o r t h e 3 6 4 2 9 6 A R M 1 1 3 6 r0 p2 e r r a t u m ( p o s s i b l e c a c h e d a t a
* corruption w i t h h i t - u n d e r - m i s s e n a b l e d ) . T h e c o n d i t i o n a l c o d e b e l o w
* ( setting t h e u n d o c u m e n t e d b i t 3 1 i n t h e a u x i l i a r y c o n t r o l r e g i s t e r
* and t h e F I b i t i n t h e c o n t r o l r e g i s t e r ) d i s a b l e s h i t - u n d e r - m i s s
* without p u t t i n g t h e p r o c e s s o r i n t o f u l l l o w i n t e r r u p t l a t e n c y m o d e .
* /
ldr r6 , =0x4107b362 @ id for ARM1136 r0p2
mrc p15 , 0 , r5 , c0 , c0 , 0 @ get processor id
teq r5 , r6 @ check for the faulty core
mrceq p15 , 0 , r5 , c1 , c0 , 1 @ load aux control reg
orreq r5 , r5 , #( 1 < < 3 1 ) @ set the undocumented bit 31
mcreq p15 , 0 , r5 , c1 , c0 , 1 @ write aux control reg
orreq r0 , r0 , #( 1 < < 2 1 ) @ low interrupt latency configuration
# endif
2014-06-30 19:29:12 +04:00
ret l r @ return to head.S:__ret
2005-04-17 02:20:36 +04:00
/ *
* V X F I D L R
* . . . . . . .E PUI. . T . T 4 R V I Z F R S B L D P W C A M
* rrrr r r r x x x x0 0 1 0 1 x x x x x x x x x11 1 x x x x < f o r c e d
* 0 1 1 0 0 0 1 1 1 .00 .111 1101 < we w a n t
* /
2006-06-29 18:09:57 +04:00
.type v6 _ c r v a l , #o b j e c t
v6_crval :
crval c l e a r =0x01e0fb7f , m m u s e t =0x00c0387d , u c s e t =0x00c0187c
2005-04-17 02:20:36 +04:00
2010-10-01 18:37:05 +04:00
_ _ INITDATA
2011-06-23 20:25:46 +04:00
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
define_ p r o c e s s o r _ f u n c t i o n s v6 , d a b o r t =v6_early_abort , p a b o r t =v6_pabort , s u s p e n d =1
2005-04-17 02:20:36 +04:00
2010-10-01 18:37:05 +04:00
.section " .rodata "
2011-06-23 20:25:46 +04:00
string c p u _ a r c h _ n a m e , " a r m v6 "
string c p u _ e l f _ n a m e , " v6 "
2005-04-17 02:20:36 +04:00
.align
2019-11-04 21:31:45 +03:00
.section " .proc .info .init " , " a"
2005-04-17 02:20:36 +04:00
/ *
* Match a n y A R M v6 p r o c e s s o r c o r e .
* /
.type _ _ v6 _ p r o c _ i n f o , #o b j e c t
__v6_proc_info :
.long 0x0007b000
.long 0x0007f000
2010-09-04 13:47:48 +04:00
ALT_ S M P ( . l o n g \
PMD_ T Y P E _ S E C T | \
PMD_ S E C T _ A P _ W R I T E | \
PMD_ S E C T _ A P _ R E A D | \
PMD_ F L A G S _ S M P )
ALT_ U P ( . l o n g \
PMD_ T Y P E _ S E C T | \
2005-04-17 02:20:36 +04:00
PMD_ S E C T _ A P _ W R I T E | \
2009-11-01 20:44:24 +03:00
PMD_ S E C T _ A P _ R E A D | \
2010-09-04 13:47:48 +04:00
PMD_ F L A G S _ U P )
2006-06-29 21:24:21 +04:00
.long PMD_TYPE_SECT | \
PMD_ S E C T _ X N | \
PMD_ S E C T _ A P _ W R I T E | \
PMD_ S E C T _ A P _ R E A D
2015-03-18 09:29:32 +03:00
initfn _ _ v6 _ s e t u p , _ _ v6 _ p r o c _ i n f o
2005-04-17 02:20:36 +04:00
.long cpu_arch_name
.long cpu_elf_name
2010-07-05 17:53:10 +04:00
/* See also feat_v6_fixup() for HWCAP_TLS */
.long HWCAP_ S W P | H W C A P _ H A L F | H W C A P _ T H U M B | H W C A P _ F A S T _ M U L T | H W C A P _ E D S P | H W C A P _ J A V A | H W C A P _ T L S
2005-04-17 02:20:36 +04:00
.long cpu_v6_name
.long v6_processor_functions
.long v6wbi_tlb_fns
.long v6_user_fns
.long v6_cache_fns
.size _ _ v6 _ p r o c _ i n f o , . - _ _ v6 _ p r o c _ i n f o