Tom Rini | 83d290c | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Andrii Tseglytskyi | 4d0df9c | 2013-05-20 22:42:08 +0000 | [diff] [blame] | 2 | /* |
Andrii Tseglytskyi | 4d0df9c | 2013-05-20 22:42:08 +0000 | [diff] [blame] | 3 | * Adaptive Body Bias programming sequence for OMAP family |
| 4 | * |
| 5 | * (C) Copyright 2013 |
| 6 | * Texas Instruments, <www.ti.com> |
| 7 | * |
| 8 | * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com> |
Andrii Tseglytskyi | 4d0df9c | 2013-05-20 22:42:08 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
| 11 | #include <common.h> |
| 12 | #include <asm/omap_common.h> |
Nikita Kiryanov | 47b4bcf | 2013-12-08 14:29:19 +0200 | [diff] [blame] | 13 | #include <asm/arch/clock.h> |
Andrii Tseglytskyi | 4d0df9c | 2013-05-20 22:42:08 +0000 | [diff] [blame] | 14 | #include <asm/io.h> |
| 15 | #include <asm/arch/sys_proto.h> |
| 16 | |
| 17 | __weak s8 abb_setup_ldovbb(u32 fuse, u32 ldovbb) |
| 18 | { |
| 19 | return -1; |
| 20 | } |
| 21 | |
| 22 | static void abb_setup_timings(u32 setup) |
| 23 | { |
| 24 | u32 sys_rate, sr2_cnt, clk_cycles; |
| 25 | |
| 26 | /* |
| 27 | * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a |
| 28 | * transition and must be programmed with the correct time at boot. |
| 29 | * The value programmed into the register is the number of SYS_CLK |
| 30 | * clock cycles that match a given wall time profiled for the ldo. |
| 31 | * This value depends on: |
| 32 | * settling time of ldo in micro-seconds (varies per OMAP family), |
| 33 | * of clock cycles per SYS_CLK period (varies per OMAP family), |
| 34 | * the SYS_CLK frequency in MHz (varies per board) |
| 35 | * The formula is: |
| 36 | * |
| 37 | * ldo settling time (in micro-seconds) |
| 38 | * SR2_WTCNT_VALUE = ------------------------------------------ |
| 39 | * (# system clock cycles) * (sys_clk period) |
| 40 | * |
| 41 | * Put another way: |
| 42 | * |
| 43 | * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate)) |
| 44 | * |
| 45 | * To avoid dividing by zero multiply both "# clock cycles" and |
| 46 | * "settling time" by 10 such that the final result is the one we want. |
| 47 | */ |
| 48 | |
| 49 | /* calculate SR2_WTCNT_VALUE */ |
Masahiro Yamada | 4515992 | 2014-11-07 03:03:26 +0900 | [diff] [blame] | 50 | sys_rate = DIV_ROUND_CLOSEST(V_OSCK, 1000000); |
| 51 | clk_cycles = DIV_ROUND_CLOSEST(OMAP_ABB_CLOCK_CYCLES * 10, sys_rate); |
| 52 | sr2_cnt = DIV_ROUND_CLOSEST(OMAP_ABB_SETTLING_TIME * 10, clk_cycles); |
Andrii Tseglytskyi | 4d0df9c | 2013-05-20 22:42:08 +0000 | [diff] [blame] | 53 | |
| 54 | setbits_le32(setup, |
| 55 | sr2_cnt << (ffs(OMAP_ABB_SETUP_SR2_WTCNT_VALUE_MASK) - 1)); |
| 56 | } |
| 57 | |
| 58 | void abb_setup(u32 fuse, u32 ldovbb, u32 setup, u32 control, |
| 59 | u32 txdone, u32 txdone_mask, u32 opp) |
| 60 | { |
| 61 | u32 abb_type_mask, opp_sel_mask; |
| 62 | |
| 63 | /* sanity check */ |
| 64 | if (!setup || !control || !txdone) |
| 65 | return; |
| 66 | |
| 67 | /* setup ABB only in case of Fast or Slow OPP */ |
| 68 | switch (opp) { |
| 69 | case OMAP_ABB_FAST_OPP: |
| 70 | abb_type_mask = OMAP_ABB_SETUP_ACTIVE_FBB_SEL_MASK; |
| 71 | opp_sel_mask = OMAP_ABB_CONTROL_FAST_OPP_SEL_MASK; |
| 72 | break; |
| 73 | case OMAP_ABB_SLOW_OPP: |
| 74 | abb_type_mask = OMAP_ABB_SETUP_ACTIVE_RBB_SEL_MASK; |
| 75 | opp_sel_mask = OMAP_ABB_CONTROL_SLOW_OPP_SEL_MASK; |
| 76 | break; |
| 77 | default: |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | * For some OMAP silicons additional setup for LDOVBB register is |
| 83 | * required. This is determined by data retrieved from corresponding |
| 84 | * OPP EFUSE register. Data, which is retrieved from EFUSE - is |
| 85 | * ABB enable/disable flag and VSET value, which must be copied |
| 86 | * to LDOVBB register. If function call fails - return quietly, |
| 87 | * it means no ABB is required for such silicon. |
| 88 | * |
| 89 | * For silicons, which don't require LDOVBB setup "fuse" and |
| 90 | * "ldovbb" offsets are not defined. ABB will be initialized in |
| 91 | * the common way for them. |
| 92 | */ |
| 93 | if (fuse && ldovbb) { |
| 94 | if (abb_setup_ldovbb(fuse, ldovbb)) |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | /* clear ABB registers */ |
| 99 | writel(0, setup); |
| 100 | writel(0, control); |
| 101 | |
| 102 | /* configure timings, based on oscillator value */ |
| 103 | abb_setup_timings(setup); |
| 104 | |
| 105 | /* clear pending interrupts before setup */ |
| 106 | setbits_le32(txdone, txdone_mask); |
| 107 | |
| 108 | /* select ABB type */ |
| 109 | setbits_le32(setup, abb_type_mask | OMAP_ABB_SETUP_SR2EN_MASK); |
| 110 | |
| 111 | /* initiate ABB ldo change */ |
| 112 | setbits_le32(control, opp_sel_mask | OMAP_ABB_CONTROL_OPP_CHANGE_MASK); |
| 113 | |
| 114 | /* wait until transition complete */ |
| 115 | if (!wait_on_value(txdone_mask, txdone_mask, (void *)txdone, LDELAY)) |
| 116 | puts("Error: ABB txdone is not set\n"); |
| 117 | |
| 118 | /* clear ABB tranxdone */ |
| 119 | setbits_le32(txdone, txdone_mask); |
| 120 | } |