root/arch/arm/mach-tegra/sleep-tegra20.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0-only */
   2 /*
   3  * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
   4  * Copyright (c) 2011, Google, Inc.
   5  *
   6  * Author: Colin Cross <ccross@android.com>
   7  *         Gary King <gking@nvidia.com>
   8  */
   9 
  10 #include <linux/linkage.h>
  11 
  12 #include <soc/tegra/flowctrl.h>
  13 
  14 #include <asm/assembler.h>
  15 #include <asm/proc-fns.h>
  16 #include <asm/cp15.h>
  17 #include <asm/cache.h>
  18 
  19 #include "irammap.h"
  20 #include "reset.h"
  21 #include "sleep.h"
  22 
  23 #define EMC_CFG                         0xc
  24 #define EMC_ADR_CFG                     0x10
  25 #define EMC_NOP                         0xdc
  26 #define EMC_SELF_REF                    0xe0
  27 #define EMC_REQ_CTRL                    0x2b0
  28 #define EMC_EMC_STATUS                  0x2b4
  29 
  30 #define CLK_RESET_CCLK_BURST            0x20
  31 #define CLK_RESET_CCLK_DIVIDER          0x24
  32 #define CLK_RESET_SCLK_BURST            0x28
  33 #define CLK_RESET_SCLK_DIVIDER          0x2c
  34 #define CLK_RESET_PLLC_BASE             0x80
  35 #define CLK_RESET_PLLM_BASE             0x90
  36 #define CLK_RESET_PLLP_BASE             0xa0
  37 
  38 #define APB_MISC_XM2CFGCPADCTRL         0x8c8
  39 #define APB_MISC_XM2CFGDPADCTRL         0x8cc
  40 #define APB_MISC_XM2CLKCFGPADCTRL       0x8d0
  41 #define APB_MISC_XM2COMPPADCTRL         0x8d4
  42 #define APB_MISC_XM2VTTGENPADCTRL       0x8d8
  43 #define APB_MISC_XM2CFGCPADCTRL2        0x8e4
  44 #define APB_MISC_XM2CFGDPADCTRL2        0x8e8
  45 
  46 #define __tegra20_cpu1_resettable_status_offset \
  47         (__tegra_cpu_reset_handler_data_offset + RESET_DATA(RESETTABLE_STATUS))
  48 
  49 .macro pll_enable, rd, r_car_base, pll_base
  50         ldr     \rd, [\r_car_base, #\pll_base]
  51         tst     \rd, #(1 << 30)
  52         orreq   \rd, \rd, #(1 << 30)
  53         streq   \rd, [\r_car_base, #\pll_base]
  54 .endm
  55 
  56 .macro emc_device_mask, rd, base
  57         ldr     \rd, [\base, #EMC_ADR_CFG]
  58         tst     \rd, #(0x3 << 24)
  59         moveq   \rd, #(0x1 << 8)                @ just 1 device
  60         movne   \rd, #(0x3 << 8)                @ 2 devices
  61 .endm
  62 
  63 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
  64 /*
  65  * tegra20_hotplug_shutdown(void)
  66  *
  67  * puts the current cpu in reset
  68  * should never return
  69  */
  70 ENTRY(tegra20_hotplug_shutdown)
  71         /* Put this CPU down */
  72         cpu_id  r0
  73         bl      tegra20_cpu_shutdown
  74         ret     lr                      @ should never get here
  75 ENDPROC(tegra20_hotplug_shutdown)
  76 
  77 /*
  78  * tegra20_cpu_shutdown(int cpu)
  79  *
  80  * r0 is cpu to reset
  81  *
  82  * puts the specified CPU in wait-for-event mode on the flow controller
  83  * and puts the CPU in reset
  84  * can be called on the current cpu or another cpu
  85  * if called on the current cpu, does not return
  86  * MUST NOT BE CALLED FOR CPU 0.
  87  *
  88  * corrupts r0-r3, r12
  89  */
  90 ENTRY(tegra20_cpu_shutdown)
  91         cmp     r0, #0
  92         reteq   lr                      @ must not be called for CPU 0
  93         mov32   r1, TEGRA_IRAM_RESET_BASE_VIRT
  94         ldr     r2, =__tegra20_cpu1_resettable_status_offset
  95         mov     r12, #CPU_RESETTABLE
  96         strb    r12, [r1, r2]
  97 
  98         cpu_to_halt_reg r1, r0
  99         ldr     r3, =TEGRA_FLOW_CTRL_VIRT
 100         mov     r2, #FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME
 101         str     r2, [r3, r1]            @ put flow controller in wait event mode
 102         ldr     r2, [r3, r1]
 103         isb
 104         dsb
 105         movw    r1, 0x1011
 106         mov     r1, r1, lsl r0
 107         ldr     r3, =TEGRA_CLK_RESET_VIRT
 108         str     r1, [r3, #0x340]        @ put slave CPU in reset
 109         isb
 110         dsb
 111         cpu_id  r3
 112         cmp     r3, r0
 113         beq     .
 114         ret     lr
 115 ENDPROC(tegra20_cpu_shutdown)
 116 #endif
 117 
 118 #ifdef CONFIG_PM_SLEEP
 119 /*
 120  * tegra_pen_lock
 121  *
 122  * spinlock implementation with no atomic test-and-set and no coherence
 123  * using Peterson's algorithm on strongly-ordered registers
 124  * used to synchronize a cpu waking up from wfi with entering lp2 on idle
 125  *
 126  * The reference link of Peterson's algorithm:
 127  * http://en.wikipedia.org/wiki/Peterson's_algorithm
 128  *
 129  * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
 130  * on cpu 0:
 131  * r2 = flag[0] (in SCRATCH38)
 132  * r3 = flag[1] (in SCRATCH39)
 133  * on cpu1:
 134  * r2 = flag[1] (in SCRATCH39)
 135  * r3 = flag[0] (in SCRATCH38)
 136  *
 137  * must be called with MMU on
 138  * corrupts r0-r3, r12
 139  */
 140 ENTRY(tegra_pen_lock)
 141         mov32   r3, TEGRA_PMC_VIRT
 142         cpu_id  r0
 143         add     r1, r3, #PMC_SCRATCH37
 144         cmp     r0, #0
 145         addeq   r2, r3, #PMC_SCRATCH38
 146         addeq   r3, r3, #PMC_SCRATCH39
 147         addne   r2, r3, #PMC_SCRATCH39
 148         addne   r3, r3, #PMC_SCRATCH38
 149 
 150         mov     r12, #1
 151         str     r12, [r2]               @ flag[cpu] = 1
 152         dsb
 153         str     r12, [r1]               @ !turn = cpu
 154 1:      dsb
 155         ldr     r12, [r3]
 156         cmp     r12, #1                 @ flag[!cpu] == 1?
 157         ldreq   r12, [r1]
 158         cmpeq   r12, r0                 @ !turn == cpu?
 159         beq     1b                      @ while !turn == cpu && flag[!cpu] == 1
 160 
 161         ret     lr                      @ locked
 162 ENDPROC(tegra_pen_lock)
 163 
 164 ENTRY(tegra_pen_unlock)
 165         dsb
 166         mov32   r3, TEGRA_PMC_VIRT
 167         cpu_id  r0
 168         cmp     r0, #0
 169         addeq   r2, r3, #PMC_SCRATCH38
 170         addne   r2, r3, #PMC_SCRATCH39
 171         mov     r12, #0
 172         str     r12, [r2]
 173         ret     lr
 174 ENDPROC(tegra_pen_unlock)
 175 
 176 /*
 177  * tegra20_cpu_clear_resettable(void)
 178  *
 179  * Called to clear the "resettable soon" flag in IRAM variable when
 180  * it is expected that the secondary CPU will be idle soon.
 181  */
 182 ENTRY(tegra20_cpu_clear_resettable)
 183         mov32   r1, TEGRA_IRAM_RESET_BASE_VIRT
 184         ldr     r2, =__tegra20_cpu1_resettable_status_offset
 185         mov     r12, #CPU_NOT_RESETTABLE
 186         strb    r12, [r1, r2]
 187         ret     lr
 188 ENDPROC(tegra20_cpu_clear_resettable)
 189 
 190 /*
 191  * tegra20_cpu_set_resettable_soon(void)
 192  *
 193  * Called to set the "resettable soon" flag in IRAM variable when
 194  * it is expected that the secondary CPU will be idle soon.
 195  */
 196 ENTRY(tegra20_cpu_set_resettable_soon)
 197         mov32   r1, TEGRA_IRAM_RESET_BASE_VIRT
 198         ldr     r2, =__tegra20_cpu1_resettable_status_offset
 199         mov     r12, #CPU_RESETTABLE_SOON
 200         strb    r12, [r1, r2]
 201         ret     lr
 202 ENDPROC(tegra20_cpu_set_resettable_soon)
 203 
 204 /*
 205  * tegra20_cpu_is_resettable_soon(void)
 206  *
 207  * Returns true if the "resettable soon" flag in IRAM variable has been
 208  * set because it is expected that the secondary CPU will be idle soon.
 209  */
 210 ENTRY(tegra20_cpu_is_resettable_soon)
 211         mov32   r1, TEGRA_IRAM_RESET_BASE_VIRT
 212         ldr     r2, =__tegra20_cpu1_resettable_status_offset
 213         ldrb    r12, [r1, r2]
 214         cmp     r12, #CPU_RESETTABLE_SOON
 215         moveq   r0, #1
 216         movne   r0, #0
 217         ret     lr
 218 ENDPROC(tegra20_cpu_is_resettable_soon)
 219 
 220 /*
 221  * tegra20_sleep_core_finish(unsigned long v2p)
 222  *
 223  * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to
 224  * tegra20_tear_down_core in IRAM
 225  */
 226 ENTRY(tegra20_sleep_core_finish)
 227         mov     r4, r0
 228         /* Flush, disable the L1 data cache and exit SMP */
 229         mov     r0, #TEGRA_FLUSH_CACHE_ALL
 230         bl      tegra_disable_clean_inv_dcache
 231         mov     r0, r4
 232 
 233         mov32   r3, tegra_shut_off_mmu
 234         add     r3, r3, r0
 235 
 236         mov32   r0, tegra20_tear_down_core
 237         mov32   r1, tegra20_iram_start
 238         sub     r0, r0, r1
 239         mov32   r1, TEGRA_IRAM_LPx_RESUME_AREA
 240         add     r0, r0, r1
 241 
 242         ret     r3
 243 ENDPROC(tegra20_sleep_core_finish)
 244 
 245 /*
 246  * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
 247  *
 248  * Enters WFI on secondary CPU by exiting coherency.
 249  */
 250 ENTRY(tegra20_sleep_cpu_secondary_finish)
 251         stmfd   sp!, {r4-r11, lr}
 252 
 253         mrc     p15, 0, r11, c1, c0, 1  @ save actlr before exiting coherency
 254 
 255         /* Flush and disable the L1 data cache */
 256         mov     r0, #TEGRA_FLUSH_CACHE_LOUIS
 257         bl      tegra_disable_clean_inv_dcache
 258 
 259         mov32   r0, TEGRA_IRAM_RESET_BASE_VIRT
 260         ldr     r4, =__tegra20_cpu1_resettable_status_offset
 261         mov     r3, #CPU_RESETTABLE
 262         strb    r3, [r0, r4]
 263 
 264         bl      tegra_cpu_do_idle
 265 
 266         /*
 267          * cpu may be reset while in wfi, which will return through
 268          * tegra_resume to cpu_resume
 269          * or interrupt may wake wfi, which will return here
 270          * cpu state is unchanged - MMU is on, cache is on, coherency
 271          * is off, and the data cache is off
 272          *
 273          * r11 contains the original actlr
 274          */
 275 
 276         bl      tegra_pen_lock
 277 
 278         mov32   r0, TEGRA_IRAM_RESET_BASE_VIRT
 279         ldr     r4, =__tegra20_cpu1_resettable_status_offset
 280         mov     r3, #CPU_NOT_RESETTABLE
 281         strb    r3, [r0, r4]
 282 
 283         bl      tegra_pen_unlock
 284 
 285         /* Re-enable the data cache */
 286         mrc     p15, 0, r10, c1, c0, 0
 287         orr     r10, r10, #CR_C
 288         mcr     p15, 0, r10, c1, c0, 0
 289         isb
 290 
 291         mcr     p15, 0, r11, c1, c0, 1  @ reenable coherency
 292 
 293         /* Invalidate the TLBs & BTAC */
 294         mov     r1, #0
 295         mcr     p15, 0, r1, c8, c3, 0   @ invalidate shared TLBs
 296         mcr     p15, 0, r1, c7, c1, 6   @ invalidate shared BTAC
 297         dsb
 298         isb
 299 
 300         /* the cpu was running with coherency disabled,
 301          * caches may be out of date */
 302         bl      v7_flush_kern_cache_louis
 303 
 304         ldmfd   sp!, {r4 - r11, pc}
 305 ENDPROC(tegra20_sleep_cpu_secondary_finish)
 306 
 307 /*
 308  * tegra20_tear_down_cpu
 309  *
 310  * Switches the CPU cluster to PLL-P and enters sleep.
 311  */
 312 ENTRY(tegra20_tear_down_cpu)
 313         bl      tegra_switch_cpu_to_pllp
 314         b       tegra20_enter_sleep
 315 ENDPROC(tegra20_tear_down_cpu)
 316 
 317 /* START OF ROUTINES COPIED TO IRAM */
 318         .align L1_CACHE_SHIFT
 319         .globl tegra20_iram_start
 320 tegra20_iram_start:
 321 
 322 /*
 323  * tegra20_lp1_reset
 324  *
 325  * reset vector for LP1 restore; copied into IRAM during suspend.
 326  * Brings the system back up to a safe staring point (SDRAM out of
 327  * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
 328  * system clock running on the same PLL that it suspended at), and
 329  * jumps to tegra_resume to restore virtual addressing and PLLX.
 330  * The physical address of tegra_resume expected to be stored in
 331  * PMC_SCRATCH41.
 332  *
 333  * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_LPx_RESUME_AREA.
 334  */
 335 ENTRY(tegra20_lp1_reset)
 336         /*
 337          * The CPU and system bus are running at 32KHz and executing from
 338          * IRAM when this code is executed; immediately switch to CLKM and
 339          * enable PLLM, PLLP, PLLC.
 340          */
 341         mov32   r0, TEGRA_CLK_RESET_BASE
 342 
 343         mov     r1, #(1 << 28)
 344         str     r1, [r0, #CLK_RESET_SCLK_BURST]
 345         str     r1, [r0, #CLK_RESET_CCLK_BURST]
 346         mov     r1, #0
 347         str     r1, [r0, #CLK_RESET_CCLK_DIVIDER]
 348         str     r1, [r0, #CLK_RESET_SCLK_DIVIDER]
 349 
 350         pll_enable r1, r0, CLK_RESET_PLLM_BASE
 351         pll_enable r1, r0, CLK_RESET_PLLP_BASE
 352         pll_enable r1, r0, CLK_RESET_PLLC_BASE
 353 
 354         adr     r2, tegra20_sdram_pad_address
 355         adr     r4, tegra20_sdram_pad_save
 356         mov     r5, #0
 357 
 358         ldr     r6, tegra20_sdram_pad_size
 359 padload:
 360         ldr     r7, [r2, r5]            @ r7 is the addr in the pad_address
 361 
 362         ldr     r1, [r4, r5]
 363         str     r1, [r7]                @ restore the value in pad_save
 364 
 365         add     r5, r5, #4
 366         cmp     r6, r5
 367         bne     padload
 368 
 369 padload_done:
 370         /* 255uS delay for PLL stabilization */
 371         mov32   r7, TEGRA_TMRUS_BASE
 372         ldr     r1, [r7]
 373         add     r1, r1, #0xff
 374         wait_until r1, r7, r9
 375 
 376         adr     r4, tegra20_sclk_save
 377         ldr     r4, [r4]
 378         str     r4, [r0, #CLK_RESET_SCLK_BURST]
 379         mov32   r4, ((1 << 28) | (4))   @ burst policy is PLLP
 380         str     r4, [r0, #CLK_RESET_CCLK_BURST]
 381 
 382         mov32   r0, TEGRA_EMC_BASE
 383         ldr     r1, [r0, #EMC_CFG]
 384         bic     r1, r1, #(1 << 31)      @ disable DRAM_CLK_STOP
 385         str     r1, [r0, #EMC_CFG]
 386 
 387         mov     r1, #0
 388         str     r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh
 389         mov     r1, #1
 390         str     r1, [r0, #EMC_NOP]
 391         str     r1, [r0, #EMC_NOP]
 392 
 393         emc_device_mask r1, r0
 394 
 395 exit_selfrefresh_loop:
 396         ldr     r2, [r0, #EMC_EMC_STATUS]
 397         ands    r2, r2, r1
 398         bne     exit_selfrefresh_loop
 399 
 400         mov     r1, #0                  @ unstall all transactions
 401         str     r1, [r0, #EMC_REQ_CTRL]
 402 
 403         mov32   r0, TEGRA_PMC_BASE
 404         ldr     r0, [r0, #PMC_SCRATCH41]
 405         ret     r0                      @ jump to tegra_resume
 406 ENDPROC(tegra20_lp1_reset)
 407 
 408 /*
 409  * tegra20_tear_down_core
 410  *
 411  * copied into and executed from IRAM
 412  * puts memory in self-refresh for LP0 and LP1
 413  */
 414 tegra20_tear_down_core:
 415         bl      tegra20_sdram_self_refresh
 416         bl      tegra20_switch_cpu_to_clk32k
 417         b       tegra20_enter_sleep
 418 
 419 /*
 420  * tegra20_switch_cpu_to_clk32k
 421  *
 422  * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock
 423  * to the 32KHz clock.
 424  */
 425 tegra20_switch_cpu_to_clk32k:
 426         /*
 427          * start by switching to CLKM to safely disable PLLs, then switch to
 428          * CLKS.
 429          */
 430         mov     r0, #(1 << 28)
 431         str     r0, [r5, #CLK_RESET_SCLK_BURST]
 432         str     r0, [r5, #CLK_RESET_CCLK_BURST]
 433         mov     r0, #0
 434         str     r0, [r5, #CLK_RESET_CCLK_DIVIDER]
 435         str     r0, [r5, #CLK_RESET_SCLK_DIVIDER]
 436 
 437         /* 2uS delay delay between changing SCLK and disabling PLLs */
 438         mov32   r7, TEGRA_TMRUS_BASE
 439         ldr     r1, [r7]
 440         add     r1, r1, #2
 441         wait_until r1, r7, r9
 442 
 443         /* disable PLLM, PLLP and PLLC */
 444         ldr     r0, [r5, #CLK_RESET_PLLM_BASE]
 445         bic     r0, r0, #(1 << 30)
 446         str     r0, [r5, #CLK_RESET_PLLM_BASE]
 447         ldr     r0, [r5, #CLK_RESET_PLLP_BASE]
 448         bic     r0, r0, #(1 << 30)
 449         str     r0, [r5, #CLK_RESET_PLLP_BASE]
 450         ldr     r0, [r5, #CLK_RESET_PLLC_BASE]
 451         bic     r0, r0, #(1 << 30)
 452         str     r0, [r5, #CLK_RESET_PLLC_BASE]
 453 
 454         /* switch to CLKS */
 455         mov     r0, #0  /* brust policy = 32KHz */
 456         str     r0, [r5, #CLK_RESET_SCLK_BURST]
 457 
 458         ret     lr
 459 
 460 /*
 461  * tegra20_enter_sleep
 462  *
 463  * uses flow controller to enter sleep state
 464  * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
 465  * executes from SDRAM with target state is LP2
 466  */
 467 tegra20_enter_sleep:
 468         mov32   r6, TEGRA_FLOW_CTRL_BASE
 469 
 470         mov     r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
 471         orr     r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
 472         cpu_id  r1
 473         cpu_to_halt_reg r1, r1
 474         str     r0, [r6, r1]
 475         dsb
 476         ldr     r0, [r6, r1] /* memory barrier */
 477 
 478 halted:
 479         dsb
 480         wfe     /* CPU should be power gated here */
 481         isb
 482         b       halted
 483 
 484 /*
 485  * tegra20_sdram_self_refresh
 486  *
 487  * called with MMU off and caches disabled
 488  * puts sdram in self refresh
 489  * must be executed from IRAM
 490  */
 491 tegra20_sdram_self_refresh:
 492         mov32   r1, TEGRA_EMC_BASE      @ r1 reserved for emc base addr
 493 
 494         mov     r2, #3
 495         str     r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests
 496 
 497 emcidle:
 498         ldr     r2, [r1, #EMC_EMC_STATUS]
 499         tst     r2, #4
 500         beq     emcidle
 501 
 502         mov     r2, #1
 503         str     r2, [r1, #EMC_SELF_REF]
 504 
 505         emc_device_mask r2, r1
 506 
 507 emcself:
 508         ldr     r3, [r1, #EMC_EMC_STATUS]
 509         and     r3, r3, r2
 510         cmp     r3, r2
 511         bne     emcself                 @ loop until DDR in self-refresh
 512 
 513         adr     r2, tegra20_sdram_pad_address
 514         adr     r3, tegra20_sdram_pad_safe
 515         adr     r4, tegra20_sdram_pad_save
 516         mov     r5, #0
 517 
 518         ldr     r6, tegra20_sdram_pad_size
 519 padsave:
 520         ldr     r0, [r2, r5]            @ r0 is the addr in the pad_address
 521 
 522         ldr     r1, [r0]
 523         str     r1, [r4, r5]            @ save the content of the addr
 524 
 525         ldr     r1, [r3, r5]
 526         str     r1, [r0]                @ set the save val to the addr
 527 
 528         add     r5, r5, #4
 529         cmp     r6, r5
 530         bne     padsave
 531 padsave_done:
 532 
 533         mov32   r5, TEGRA_CLK_RESET_BASE
 534         ldr     r0, [r5, #CLK_RESET_SCLK_BURST]
 535         adr     r2, tegra20_sclk_save
 536         str     r0, [r2]
 537         dsb
 538         ret     lr
 539 
 540 tegra20_sdram_pad_address:
 541         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL
 542         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL
 543         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL
 544         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL
 545         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL
 546         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2
 547         .word   TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2
 548 
 549 tegra20_sdram_pad_size:
 550         .word   tegra20_sdram_pad_size - tegra20_sdram_pad_address
 551 
 552 tegra20_sdram_pad_safe:
 553         .word   0x8
 554         .word   0x8
 555         .word   0x0
 556         .word   0x8
 557         .word   0x5500
 558         .word   0x08080040
 559         .word   0x0
 560 
 561 tegra20_sclk_save:
 562         .word   0x0
 563 
 564 tegra20_sdram_pad_save:
 565         .rept (tegra20_sdram_pad_size - tegra20_sdram_pad_address) / 4
 566         .long   0
 567         .endr
 568 
 569         .ltorg
 570 /* dummy symbol for end of IRAM */
 571         .align L1_CACHE_SHIFT
 572         .globl tegra20_iram_end
 573 tegra20_iram_end:
 574         b       .
 575 #endif

/* [<][>][^][v][top][bottom][index][help] */