github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/ocotp/ocotp.go (about) 1 // NXP i.MX6 On-Chip OTP Controller (OCOTP_CTRL) driver 2 // https://github.com/f-secure-foundry/tamago 3 // 4 // Copyright (c) F-Secure Corporation 5 // https://foundry.f-secure.com 6 // 7 // Use of this source code is governed by the license 8 // that can be found in the LICENSE file. 9 10 // Package ocotp implements a driver for the NXP On-Chip OTP Controller 11 // (OCOTP_CTRL), included in i.MX6 series SoCs to interface with on-chip fuses, 12 // including write operation. 13 // 14 // WARNING: Fusing SoC OTPs is an **irreversible** action that permanently 15 // fuses values on the device. This means that any errors in the process, or 16 // lost fused data such as cryptographic key material, might result in a 17 // **bricked** device. 18 // 19 // The use of this package is therefore **at your own risk**. 20 // 21 // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as 22 // supported by the TamaGo framework for bare metal Go on ARM SoCs, see 23 // https://github.com/f-secure-foundry/tamago. 24 package ocotp 25 26 import ( 27 "errors" 28 "sync" 29 "time" 30 31 "github.com/f-secure-foundry/tamago/internal/reg" 32 "github.com/f-secure-foundry/tamago/soc/imx6" 33 ) 34 35 // OCOTP registers 36 // (p2388, 37.5 OCOTP Memory Map/Register Definition, IMX6ULLRM). 37 const ( 38 OCOTP_BASE = 0x021bc000 39 40 OCOTP_CTRL = OCOTP_BASE 41 CTRL_WRUNLOCK = 16 42 CTRL_RELOAD_SHADOWS = 10 43 CTRL_ERROR = 9 44 CTRL_BUSY = 8 45 CTRL_ADDR = 0 46 47 OCOTP_CTRL_CLR = OCOTP_BASE + 0x0008 48 OCOTP_DATA = OCOTP_BASE + 0x0020 49 50 OCOTP_VERSION = OCOTP_BASE + 0x0090 51 VERSION_MAJOR = 24 52 VERSION_MINOR = 16 53 VERSION_STEP = 0 54 55 OCOTP_BANK0_WORD0 = OCOTP_BASE + 0x0400 56 ) 57 58 // Configuration constants 59 const ( 60 // WordSize represents the number of bytes per OTP word. 61 WordSize = 4 62 // BankSize represents the number of words per OTP bank. 63 BankSize = 8 64 ) 65 66 var mux sync.Mutex 67 68 // Timeout for OCOTP controller operations 69 var Timeout = 10 * time.Millisecond 70 71 // Init initializes the OCOTP controller instance. 72 func Init() (err error) { 73 mux.Lock() 74 defer mux.Unlock() 75 76 // enable clock 77 reg.SetN(imx6.CCM_CCGR2, imx6.CCGRx_CG6, 0b11, 0b11) 78 79 return 80 } 81 82 // Read returns the value in the argument bank and word location. 83 func Read(bank int, word int) (value uint32, err error) { 84 var banks int 85 86 switch imx6.Model() { 87 case "i.MX6UL": 88 banks = 16 89 case "i.MX6ULL": 90 banks = 8 91 } 92 93 if bank > banks || word > BankSize { 94 return 0, errors.New("invalid argument") 95 } 96 97 // Within the shadow register address map the addresses are spaced 0x10 98 // apart. 99 offset := 0x10 * uint32(BankSize*bank+word) 100 101 // Account for the gap in shadow registers address map between bank 5 102 // and bank 6. 103 if bank > 5 { 104 offset += 0x100 105 } 106 107 mux.Lock() 108 defer mux.Unlock() 109 110 value = reg.Read(OCOTP_BANK0_WORD0 + offset) 111 112 return 113 } 114 115 // Blow fuses a value in the argument bank and word location. 116 // (p2384, 37.3.1.3 Fuse and Shadow Register Writes, IMX6ULLRM). 117 // 118 // WARNING: Fusing SoC OTPs is an **irreversible** action that permanently 119 // fuses values on the device. This means that any errors in the process, or 120 // lost fused data such as cryptographic key material, might result in a 121 // **bricked** device. 122 // 123 // The use of this function is therefore **at your own risk**. 124 func Blow(bank int, word int, value uint32) (err error) { 125 mux.Lock() 126 defer mux.Unlock() 127 128 if !reg.WaitFor(Timeout, OCOTP_CTRL, CTRL_BUSY, 1, 0) { 129 return errors.New("OCOTP controller busy") 130 } 131 132 // FIXME: configure OCOTP_TIMING register. Timings depend on 133 // IPG_CLK_ROOT frequency. Default values work for default frequency of 134 // 66 MHz. 135 136 // p2393, OCOTP_CTRLn field descriptions, IMX6ULLRM 137 138 // clear error bit 139 reg.Set(OCOTP_CTRL_CLR, CTRL_ERROR) 140 // set OTP write register 141 reg.SetN(OCOTP_CTRL, CTRL_ADDR, 0x7f, uint32(BankSize*bank+word)) 142 // enable OTP write access 143 reg.SetN(OCOTP_CTRL, CTRL_WRUNLOCK, 0xffff, 0x3e77) 144 145 // blow the fuse 146 reg.Write(OCOTP_DATA, value) 147 148 if err = checkOp(); err != nil { 149 return 150 } 151 152 // 2385, 37.3.1.4 Write Postamble, IMX6ULLRM 153 time.Sleep(2 * time.Microsecond) 154 155 // ensure update of shadow registers 156 err = shadowReload() 157 158 return 159 } 160 161 func checkOp() (err error) { 162 if !reg.WaitFor(Timeout, OCOTP_CTRL, CTRL_BUSY, 1, 0) { 163 return errors.New("operation timeout") 164 } 165 166 if reg.Get(OCOTP_CTRL, CTRL_ERROR, 1) != 0 { 167 return errors.New("operation error") 168 } 169 170 return 171 } 172 173 // shadowReload reloads memory mapped shadow registers from OTP fuse banks 174 // (p2383, 37.3.1.1 Shadow Register Reload, IMX6ULLRM). 175 func shadowReload() (err error) { 176 if !reg.WaitFor(Timeout, OCOTP_CTRL, CTRL_BUSY, 1, 0) { 177 return errors.New("OCOTP controller busy") 178 } 179 180 // clear error bit 181 reg.Set(OCOTP_CTRL_CLR, CTRL_ERROR) 182 // force re-loading of shadow registers 183 reg.Set(OCOTP_CTRL, CTRL_RELOAD_SHADOWS) 184 185 err = checkOp() 186 187 return 188 }