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  }