github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/flash/flash.go (about)

     1  // Copyright 2021 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package flash provides higher-level functions such as reading, erasing,
     6  // writing and programming the flash chip.
     7  package flash
     8  
     9  import (
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  
    14  	"github.com/mvdan/u-root-coreutils/pkg/flash/op"
    15  	"github.com/mvdan/u-root-coreutils/pkg/flash/sfdp"
    16  	"github.com/mvdan/u-root-coreutils/pkg/spidev"
    17  )
    18  
    19  // sfdpMaxAddress is the highest possible SFDP address (24 bit address space).
    20  const sfdpMaxAddress = (1 << 24) - 1
    21  
    22  // SPI interface for the underlying calls to the SPI driver.
    23  type SPI interface {
    24  	Transfer(transfers []spidev.Transfer) error
    25  }
    26  
    27  // Flash provides operations for SPI flash chips.
    28  type Flash struct {
    29  	// spi is the underlying SPI device.
    30  	spi SPI
    31  
    32  	// is4ba is true if 4-byte addressing mode is enabled.
    33  	is4ba bool
    34  
    35  	// size is the size of the flash chip in bytes.
    36  	size int64
    37  
    38  	// sfdp is cached.
    39  	sfdp *sfdp.SFDP
    40  
    41  	// JEDEC ID is cached.
    42  	id uint32
    43  
    44  	pageSize   int64
    45  	sectorSize int64
    46  	blockSize  int64
    47  }
    48  
    49  // New creates a new flash device from a SPI interface.
    50  func New(spi SPI) (*Flash, error) {
    51  	f := &Flash{
    52  		spi: spi,
    53  	}
    54  
    55  	var err error
    56  	f.sfdp, err = sfdp.Read(f.SFDPReader())
    57  	if err != nil {
    58  		return nil, fmt.Errorf("could not read sfdp: %v", err)
    59  	}
    60  
    61  	density, err := f.SFDP().Param(sfdp.ParamFlashMemoryDensity)
    62  	if err != nil {
    63  		return nil, fmt.Errorf("flash chip SFDP does not have density param")
    64  	}
    65  	if density >= 0x80000000 {
    66  		return nil, fmt.Errorf("unsupported flash density: %#x", density)
    67  	}
    68  	f.size = (density + 1) / 8
    69  
    70  	// Assume 4ba if address if size requires 4 bytes.
    71  	if f.size >= 0x1000000 {
    72  		f.is4ba = true
    73  	}
    74  
    75  	// TODO
    76  	f.pageSize = 256
    77  	f.sectorSize = 4096
    78  	f.blockSize = 65536
    79  
    80  	return f, nil
    81  }
    82  
    83  // Size returns the size of the flash chip in bytes.
    84  func (f *Flash) Size() int64 {
    85  	return f.size
    86  }
    87  
    88  const maxTransferSize = 4096
    89  
    90  func min(x, y int64) int64 {
    91  	if x < y {
    92  		return x
    93  	}
    94  	return y
    95  }
    96  
    97  // prepareAddress converts an address to the 3- or 4-byte addressing mode.
    98  func (f *Flash) prepareAddress(addr int64) []byte {
    99  	data := make([]byte, 4)
   100  	binary.BigEndian.PutUint32(data, uint32(addr))
   101  	if f.is4ba {
   102  		return data
   103  	}
   104  	return data[1:]
   105  }
   106  
   107  // ReadAt reads from the flash chip.
   108  func (f *Flash) ReadAt(p []byte, off int64) (int, error) {
   109  	// This is a valid implementation of io.ReaderAt.
   110  	if off < 0 || off > f.size {
   111  		return 0, io.EOF
   112  	}
   113  	p = p[:min(int64(len(p)), f.size-off)]
   114  
   115  	// Split the transfer into maxTransferSize chunks.
   116  	for i := 0; i < len(p); i += maxTransferSize {
   117  		if err := f.spi.Transfer([]spidev.Transfer{
   118  			{Tx: append([]byte{op.Read}, f.prepareAddress(off+int64(i))...)},
   119  			{Rx: p[i:min(int64(i)+maxTransferSize, int64(len(p)))]},
   120  		}); err != nil {
   121  			return i, err
   122  		}
   123  	}
   124  	return len(p), nil
   125  }
   126  
   127  // writeAt performs a write operation without any care for page sizes or
   128  // alignment.
   129  func (f *Flash) writeAt(p []byte, off int64) (int, error) {
   130  	if err := f.spi.Transfer([]spidev.Transfer{
   131  		// Enable writing.
   132  		{Tx: []byte{op.WriteEnable}, CSChange: true},
   133  		// Send the address.
   134  		{Tx: append([]byte{op.PageProgram}, f.prepareAddress(off)...)},
   135  		// Send the data.
   136  		{Tx: p},
   137  	}); err != nil {
   138  		return 0, err
   139  	}
   140  	return len(p), nil
   141  }
   142  
   143  // WriteAt writes to the flash chip.
   144  //
   145  // For optimal performance, call this function on boundaries of pageSize.
   146  //
   147  // NOTE: This will not erase before writing! The ProgramAt function is probably
   148  // what you want instead!
   149  func (f *Flash) WriteAt(p []byte, off int64) (int, error) {
   150  	// This is a valid implementation of io.WriterAt.
   151  	if off < 0 || off > f.size {
   152  		return 0, io.EOF
   153  	}
   154  	p = p[:min(int64(len(p)), f.size-off)]
   155  
   156  	// Special case where no page boundaries are crossed.
   157  	if off%f.pageSize+int64(len(p)) <= f.pageSize {
   158  		return f.writeAt(p, off)
   159  	}
   160  
   161  	// Otherwise, there are three regions:
   162  	// 1. A partial page before the first aligned offset. (optional)
   163  	// 2. All the aligned pages in the middle.
   164  	// 3. A partial page after the last aligned offset. (optional)
   165  	firstAlignedOff := (off + f.pageSize - 1) / f.pageSize * f.pageSize
   166  	lastAlignedOff := (off + int64(len(p))) / f.pageSize * f.pageSize
   167  
   168  	if off != firstAlignedOff {
   169  		if n, err := f.writeAt(p[:firstAlignedOff-off], off); err != nil {
   170  			return n, err
   171  		}
   172  	}
   173  	for i := firstAlignedOff; i < lastAlignedOff; i += f.pageSize {
   174  		if _, err := f.writeAt(p[i:i+f.pageSize], off+i); err != nil {
   175  			return int(i), err
   176  		}
   177  	}
   178  	if off+int64(len(p)) != lastAlignedOff {
   179  		if _, err := f.writeAt(p[lastAlignedOff-off:], lastAlignedOff); err != nil {
   180  			return int(lastAlignedOff - off), err
   181  		}
   182  	}
   183  	return len(p), nil
   184  }
   185  
   186  func (f *Flash) ProgramAt(p []byte, off int64) (int, error) {
   187  	// TODO
   188  	return 0, nil
   189  }
   190  
   191  // EraseAt erases n bytes from offset off. Both parameters must be aligned to
   192  // sectorSize.
   193  func (f *Flash) EraseAt(n int64, off int64) (int64, error) {
   194  	if off < 0 || off > f.size || off+n > f.size {
   195  		return 0, io.EOF
   196  	}
   197  
   198  	if (off%f.sectorSize != 0) || (n%f.sectorSize != 0) {
   199  		return 0, fmt.Errorf("len(p) and off must be multiple of the sector size")
   200  	}
   201  
   202  	for i := int64(0); i < n; {
   203  		opcode := op.SectorErase
   204  		eraseSize := f.sectorSize
   205  
   206  		// Optimization to erase faster.
   207  		if i%f.blockSize == 0 && n-i > f.blockSize {
   208  			opcode = op.BlockErase
   209  			eraseSize = f.blockSize
   210  		}
   211  
   212  		if err := f.spi.Transfer([]spidev.Transfer{
   213  			// Enable writing.
   214  			{
   215  				Tx:       []byte{op.WriteEnable},
   216  				CSChange: true,
   217  			},
   218  			// Send the address.
   219  			{Tx: append([]byte{opcode}, f.prepareAddress(off+i)...)},
   220  		}); err != nil {
   221  			return i, err
   222  		}
   223  
   224  		i += eraseSize
   225  	}
   226  	return n, nil
   227  }
   228  
   229  // ReadJEDECID reads the flash chip's JEDEC ID.
   230  func (f *Flash) ReadJEDECID() (uint32, error) {
   231  	tx := []byte{op.ReadJEDECID}
   232  	rx := make([]byte, 3)
   233  
   234  	if err := f.spi.Transfer([]spidev.Transfer{
   235  		{Tx: tx},
   236  		{Rx: rx},
   237  	}); err != nil {
   238  		return 0, err
   239  	}
   240  
   241  	// Little-endian
   242  	return (uint32(rx[0]) << 16) | (uint32(rx[1]) << 8) | uint32(rx[2]), nil
   243  }
   244  
   245  // SFDPReader is used to read from the SFDP address space.
   246  func (f *Flash) SFDPReader() *SFDPReader {
   247  	return (*SFDPReader)(f)
   248  }
   249  
   250  // SFDP returns all the SFDP tables from the flash chip. The value is cached.
   251  func (f *Flash) SFDP() *sfdp.SFDP {
   252  	return f.sfdp
   253  }
   254  
   255  // SFDPReader is a wrapper around Flash where the ReadAt function reads from
   256  // the SFDP address space.
   257  type SFDPReader Flash
   258  
   259  // ReadAt reads from the given offset in the SFDP address space.
   260  func (f *SFDPReader) ReadAt(p []byte, off int64) (int, error) {
   261  	if off < 0 || off > sfdpMaxAddress {
   262  		return 0, io.EOF
   263  	}
   264  	p = p[:min(int64(len(p)), sfdpMaxAddress-off)]
   265  	tx := []byte{
   266  		op.ReadSFDP,
   267  		// offset, 3-bytes, big-endian
   268  		byte((off >> 16) & 0xff), byte((off >> 8) & 0xff), byte(off & 0xff),
   269  		// dummy 0xff
   270  		0xff,
   271  	}
   272  	if err := f.spi.Transfer([]spidev.Transfer{
   273  		{Tx: tx},
   274  		{Rx: p},
   275  	}); err != nil {
   276  		return 0, err
   277  	}
   278  	return len(p), nil
   279  }