github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/bcm2835/gpio.go (about)

     1  // BCM2835 SoC GPIO support
     2  // https://github.com/f-secure-foundry/tamago
     3  //
     4  // Copyright (c) the bcm2835 package authors
     5  //
     6  // Use of this source code is governed by the license
     7  // that can be found in the LICENSE file.
     8  
     9  package bcm2835
    10  
    11  import (
    12  	"fmt"
    13  	"sync"
    14  
    15  	"github.com/f-secure-foundry/tamago/arm"
    16  	"github.com/f-secure-foundry/tamago/internal/reg"
    17  )
    18  
    19  // GPIO registers
    20  const (
    21  	GPIO_BASE = 0x200000
    22  
    23  	GPFSEL0   = GPIO_BASE
    24  	GPFSEL1   = GPIO_BASE + 0x04
    25  	GPSET0    = GPIO_BASE + 0x1c
    26  	GPCLR0    = GPIO_BASE + 0x28
    27  	GPLEV0    = GPIO_BASE + 0x34
    28  	GPPUD     = GPIO_BASE + 0x94
    29  	GPPUDCLK0 = GPIO_BASE + 0x98
    30  )
    31  
    32  // GPIO function selections (p92, BCM2835 ARM Peripherals)
    33  const (
    34  	GPIO_INPUT  = 0b000
    35  	GPIO_OUTPUT = 0b001
    36  	GPIO_FN0    = 0b100
    37  	GPIO_FN1    = 0b101
    38  	GPIO_FN2    = 0b110
    39  	GPIO_FN3    = 0b111
    40  	GPIO_FN4    = 0b011
    41  	GPIO_FN5    = 0b010
    42  )
    43  
    44  // GPIO instance
    45  type GPIO struct {
    46  	num int
    47  }
    48  
    49  type GPIOFunction uint32
    50  
    51  var gpmux = sync.Mutex{}
    52  
    53  // NewGPIO gets access to a single GPIO line
    54  func NewGPIO(num int) (*GPIO, error) {
    55  	if num > 54 || num < 0 {
    56  		return nil, fmt.Errorf("invalid GPIO number %d", num)
    57  	}
    58  
    59  	return &GPIO{num: num}, nil
    60  }
    61  
    62  // Out configures a GPIO as output.
    63  func (gpio *GPIO) Out() {
    64  	gpio.SelectFunction(GPIO_OUTPUT)
    65  }
    66  
    67  // In configures a GPIO as input.
    68  func (gpio *GPIO) In() {
    69  	gpio.SelectFunction(GPIO_INPUT)
    70  }
    71  
    72  // SelectFunction selects the function of a GPIO line.
    73  func (gpio *GPIO) SelectFunction(n GPIOFunction) (err error) {
    74  	if n > 0b111 {
    75  		return fmt.Errorf("invalid GPIO function %d", n)
    76  	}
    77  
    78  	register := PeripheralAddress(GPFSEL0 + 4*uint32(gpio.num/10))
    79  	shift := uint32((gpio.num % 10) * 3)
    80  	mask := uint32(0x7 << shift)
    81  
    82  	val := reg.Read(register)
    83  	val &= ^(mask)
    84  	val |= (uint32(n) << shift) & mask
    85  
    86  	reg.Write(register, val)
    87  
    88  	return
    89  }
    90  
    91  // GetFunction gets the current function of a GPIO line
    92  func (gpio *GPIO) GetFunction(line int) GPIOFunction {
    93  	val := reg.Read(PeripheralAddress(GPFSEL0 + 4*uint32(gpio.num/10)))
    94  	shift := uint32((gpio.num % 10) * 3)
    95  
    96  	return GPIOFunction(val>>shift) & 0x7
    97  }
    98  
    99  // High configures a GPIO signal as high.
   100  func (gpio *GPIO) High() {
   101  	register := PeripheralAddress(GPSET0 + 4*uint32(gpio.num/32))
   102  	shift := uint32(gpio.num % 32)
   103  
   104  	reg.Write(register, 1<<shift)
   105  }
   106  
   107  // Low configures a GPIO signal as low.
   108  func (gpio *GPIO) Low() {
   109  	register := PeripheralAddress(GPCLR0 + 4*uint32(gpio.num/32))
   110  	shift := uint32(gpio.num % 32)
   111  
   112  	reg.Write(register, 1<<shift)
   113  }
   114  
   115  // Value returns the GPIO signal level.
   116  func (gpio *GPIO) Value() (high bool) {
   117  	register := PeripheralAddress(GPLEV0 + 4*uint32(gpio.num/32))
   118  	shift := uint32(gpio.num % 32)
   119  
   120  	return (reg.Read(register)>>shift)&0x1 != 0
   121  }
   122  
   123  // PullUpDown controls the pull-up or pull-down state of the line.
   124  //
   125  // The pull-up / pull-down state persists across power-down state
   126  // of the CPU (i.e. always set the pull-up / pull-down to desired
   127  // state before using a GPIO pin).
   128  func (gpio *GPIO) PullUpDown(val uint32) {
   129  	// The control registers are shared between GPIO pins, so
   130  	// hold a mutex for the period.
   131  	gpmux.Lock()
   132  	defer gpmux.Unlock()
   133  
   134  	// There is a very specific documented dance (likely related
   135  	// to the persistence over power-down):
   136  	//   1 - write to control register to indicate if wanting to
   137  	//       pull some pins up or down
   138  	//   2 - Wait at least 150 clock cycles for control to setup
   139  	//   3 - Enable the clock for the lines to be modified
   140  	//   4 - Wait at least 150 clock cycles to hold control signal
   141  	//   5 - Remove the control signal
   142  	//   6 - Remove the clock for the line to be modified
   143  
   144  	reg.Write(PeripheralAddress(GPPUD), uint32(val))
   145  	arm.Busyloop(150)
   146  
   147  	clkRegister := PeripheralAddress(GPPUDCLK0 + 4*uint32(gpio.num/32))
   148  	clkShift := uint32(gpio.num % 32)
   149  
   150  	reg.Write(clkRegister, 1<<clkShift)
   151  	arm.Busyloop(150)
   152  
   153  	reg.Write(PeripheralAddress(GPPUD), 0)
   154  	reg.Write(clkRegister, 0)
   155  }