diff --git a/pkg/hal/bcm2711/hal_bcm2711.go b/pkg/hal/bcm2711/hal_bcm2711.go index 0d26cf7..6fef31a 100644 --- a/pkg/hal/bcm2711/hal_bcm2711.go +++ b/pkg/hal/bcm2711/hal_bcm2711.go @@ -12,7 +12,7 @@ import ( const ( bcm2711PeripheryBaseAddr = 0xFE000000 - bcm2711PwmAddr = bcm2711PeripheryBaseAddr + 0x20C000 + bcm2711RegPwmAddr = bcm2711PeripheryBaseAddr + 0x20C000 bcm2711GpioAddr = bcm2711PeripheryBaseAddr + 0x200000 bcm2711ClkAddr = bcm2711PeripheryBaseAddr + 0x101000 bcm2711ClkManagerPwd = (0x5A << 24) //(31 - 24) on CM_GP0CTL/CM_GP1CTL/CM_GP2CTL regs @@ -20,44 +20,32 @@ const ( bcm2711FrontButtonPin = 20 bcm2711StealthPin = 21 - bcm2711PwmTachPin = 13 + bcm2711RegPwmTachPin = 13 - GPFSEL0 = 0x00 - GPFSEL1 = 0x01 - GPFSEL2 = 0x02 + bcm2711RegGpfsel0 = 0x00 + bcm2711RegGpfsel1 = 0x01 + bcm2711RegGpfsel2 = 0x02 - PWM_CTL = 0x00 - PWM_STA = 0x01 - PWM_DMAC = 0x02 - PWM_RNG1 = 0x04 - PWM_DAT1 = 0x05 - PWM_FIF1 = 0x06 + bcm2711RegPwmCtl = 0x00 + bcm2711RegPwmSta = 0x01 + bcm2711RegPwmRng1 = 0x04 + bcm2711RegPwmFif1 = 0x06 - PWM_CTL_PWEN2 = 8 // Enable (pwm2) - PWM_CTL_CLRF1 = 6 // Clear FIFO - PWM_CTL_MSEN1 = 7 // Use M/S algorithm - PWM_CTL_USEF1 = 5 // Use FIFO - PWM_CTL_POLA1 = 4 // Invert polarity - PWM_CTL_SBIT1 = 3 // Line level when not transmitting - PWM_CTL_RPTL1 = 2 // Repeat last data when FIFO is empty - PWM_CTL_MODE1 = 1 // Mode; 0: PWM, 1: Serializer - PWM_CTL_PWEN1 = 0 // Enable (pwm1) + bcm2711RegPwmCtlBitPwen2 = 8 // Enable (pwm2) + bcm2711RegPwmCtlBitClrf1 = 6 // Clear FIFO + bcm2711RegPwmCtlBitUsef1 = 5 // Use FIFO + bcm2711RegPwmCtlBitSbit1 = 3 // Line level when not transmitting + bcm2711RegPwmCtlBitRptl1 = 2 // Repeat last data when FIFO is empty + bcm2711RegPwmCtlBitMode1 = 1 // Mode; 0: PWM, 1: Serializer + bcm2711RegPwmCtlBitPwen1 = 0 // Enable (pwm1) - PWM_STA_STA1 = 9 // Status - PWM_STA_BERR = 8 // Bus Error - PWM_STA_GAPO1 = 4 // Gap detected - PWM_STA_RERR1 = 3 // FIFO Read Error - PWM_STA_WERR1 = 2 // FIFO Write Error - PWM_STA_EMPT1 = 1 // FIFO Empty - PWM_STA_FULL1 = 0 // FIFO Full - - PWMCLK_CNTL = 0x28 - PWMCLK_CNTL_SRC_OSC = 0 - PWMCLK_CNTL_ENABLE = 4 - PWMCLK_DIV = 0x29 + bcm2711RegPwmclkCntrl = 0x28 + bcm2711RegPwmclkDiv = 0x29 + bcm2711RegPwmclkCntrlBitSrcOsc = 0 + bcm2711RegPwmclkCntrlBitEnable = 4 ) -type bcm2711bcm struct { +type bcm2711 struct { // Config options opts hal.ComputeBladeHalOpts @@ -76,7 +64,7 @@ type bcm2711bcm struct { clkMem []uint32 } -func New(opts hal.ComputeBladeHalOpts) (*bcm2711bcm, error) { +func New(opts hal.ComputeBladeHalOpts) (*bcm2711, error) { // /dev/gpiomem doesn't allow complex operations for PWM fan control or WS281x devmem, err := os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, os.ModePerm) if err != nil { @@ -94,7 +82,7 @@ func New(opts hal.ComputeBladeHalOpts) (*bcm2711bcm, error) { if err != nil { return nil, err } - pwmMem, pwmMem8, err := mmap(devmem, bcm2711PwmAddr, bcm2711PageSize) + pwmMem, pwmMem8, err := mmap(devmem, bcm2711RegPwmAddr, bcm2711PageSize) if err != nil { return nil, err } @@ -103,7 +91,7 @@ func New(opts hal.ComputeBladeHalOpts) (*bcm2711bcm, error) { return nil, err } - return &bcm2711bcm{ + return &bcm2711{ devmem: devmem, mbox: mbox, gpioMem: gpioMem, @@ -117,7 +105,7 @@ func New(opts hal.ComputeBladeHalOpts) (*bcm2711bcm, error) { } // Close cleans all memory mappings -func (bcm *bcm2711bcm) Close() error { +func (bcm *bcm2711) Close() error { return errors.Join( syscall.Munmap(bcm.gpioMem8), syscall.Munmap(bcm.pwmMem8), @@ -128,51 +116,53 @@ func (bcm *bcm2711bcm) Close() error { } // Init initialises GPIOs and sets sane defaults -func (bcm *bcm2711bcm) Init() { +func (bcm *bcm2711) Init() { bcm.InitGPIO() - // bcm.SetFanSpeed(bcm.opts.DefaultFanSpeed) - bcm.SetStealthMode(bcm.opts.DefaultStealthModeEnabled) + bcm.SetFanSpeed(bcm.opts.Defaults.FanSpeed) + bcm.SetStealthMode(bcm.opts.Defaults.StealthModeEnabled) + bcm.SetLEDs(bcm.opts.Defaults.TopLedColor, bcm.opts.Defaults.EdgeLedColor) } // InitGPIO initalises GPIO configuration -func (bcm *bcm2711bcm) InitGPIO() { +func (bcm *bcm2711) InitGPIO() { // based on https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf bcm.wrMutex.Lock() defer bcm.wrMutex.Unlock() // Edge Button (GPIO 20) - // -> GPFSEL2 2:0, input - bcm.gpioMem[GPFSEL2] = (bcm.gpioMem[GPFSEL2] &^ (0b111 << 0)) | (0b000 << 0) + // -> bcm2711RegGpfsel2 2:0, input + bcm.gpioMem[bcm2711RegGpfsel2] = (bcm.gpioMem[bcm2711RegGpfsel2] &^ (0b111 << 0)) | (0b000 << 0) // PoE at detection (GPIO 23) - // -> GPFSEL2 2:0, input - bcm.gpioMem[GPFSEL2] = (bcm.gpioMem[GPFSEL2] &^ (0b111 << 0)) | (0b000 << 0) + // -> bcm2711RegGpfsel2 2:0, input + bcm.gpioMem[bcm2711RegGpfsel2] = (bcm.gpioMem[bcm2711RegGpfsel2] &^ (0b111 << 9)) | (0b000 << 9) // Stealth Mode Output (GPIO 21) - // -> GPFSEL2 5:3, output - bcm.gpioMem[GPFSEL2] = (bcm.gpioMem[GPFSEL2] &^ (0b111 << 3)) | (0b001 << 3) + // -> bcm2711RegGpfsel2 5:3, output + bcm.gpioMem[bcm2711RegGpfsel2] = (bcm.gpioMem[bcm2711RegGpfsel2] &^ (0b111 << 3)) | (0b001 << 3) // FAN PWM output for standard fan unit (GPIO 12) - if bcm.opts.FanUnit == hal.FAN_UNIT_STANDARD { - // -> GPFSEL1 8:6, alt0 - bcm.gpioMem[GPFSEL1] = (bcm.gpioMem[GPFSEL1] &^ (0b111 << 6)) | (0b100 << 6) - bcm.setFanSpeedPWM(bcm.opts.DefaultFanSpeed) + if bcm.opts.FanUnit == hal.FanUnitStandard { + // -> bcm2711RegGpfsel1 8:6, alt0 + bcm.gpioMem[bcm2711RegGpfsel1] = (bcm.gpioMem[bcm2711RegGpfsel1] &^ (0b111 << 6)) | (0b100 << 6) } // FAN TACH input for standard fan unit (GPIO 13) - if bcm.opts.FanUnit == hal.FAN_UNIT_STANDARD { - // -> GPFSEL1 11:9, input - bcm.gpioMem[GPFSEL1] = (bcm.gpioMem[GPFSEL1] &^ (0b111 << 9)) | (0b000 << 9) + if bcm.opts.FanUnit == hal.FanUnitStandard { + // -> bcm2711RegGpfsel1 11:9, input + bcm.gpioMem[bcm2711RegGpfsel1] = (bcm.gpioMem[bcm2711RegGpfsel1] &^ (0b111 << 9)) | (0b000 << 9) } // Set WS2812 output (GPIO 18) - // -> GPFSEL1 24:26, set as regular output by default. On-demand, it's mapped to pwm0 - bcm.gpioMem[GPFSEL1] = (bcm.gpioMem[GPFSEL1] &^ (0b111 << 24)) | (0b001 << 24) - - // FIXME add edge button + // -> bcm2711RegGpfsel1 24:26, set as regular output by default. On-demand, it's mapped to pwm0 + bcm.gpioMem[bcm2711RegGpfsel1] = (bcm.gpioMem[bcm2711RegGpfsel1] &^ (0b111 << 24)) | (0b001 << 24) } -func (bcm *bcm2711bcm) setPwm0Freq(targetFrequency uint64) error { +func (bcm *bcm2711) GetPoeStatus() { + +} + +func (bcm *bcm2711) setPwm0Freq(targetFrequency uint64) error { // Calculate PWM divisor based on target frequency divisor := 54000000 / targetFrequency realDivisor := divisor & 0xfff // 12 bits @@ -181,42 +171,42 @@ func (bcm *bcm2711bcm) setPwm0Freq(targetFrequency uint64) error { } // Stop pwm for both channels; this is required to set the new configuration - bcm.pwmMem[PWM_CTL] &^= (1 << PWM_CTL_PWEN1) | (1 << PWM_CTL_PWEN2) + bcm.pwmMem[bcm2711RegPwmCtl] &^= (1 << bcm2711RegPwmCtlBitPwen1) | (1 << bcm2711RegPwmCtlBitPwen2) time.Sleep(time.Microsecond * 10) // Stop clock w/o any changes, they cannot be made in the same step - bcm.clkMem[PWMCLK_CNTL] = bcm2711ClkManagerPwd | (bcm.clkMem[PWMCLK_CNTL] &^ (1 << 4)) + bcm.clkMem[bcm2711RegPwmclkCntrl] = bcm2711ClkManagerPwd | (bcm.clkMem[bcm2711RegPwmclkCntrl] &^ (1 << 4)) time.Sleep(time.Microsecond * 10) // Wait for the clock to not be busy so we can perform the changes - for bcm.clkMem[PWMCLK_CNTL]&(1<<7) != 0 { + for bcm.clkMem[bcm2711RegPwmclkCntrl]&(1<<7) != 0 { time.Sleep(time.Microsecond * 10) } // passwd, disabled, source (oscillator) - bcm.clkMem[PWMCLK_CNTL] = bcm2711ClkManagerPwd | (0 << PWMCLK_CNTL_ENABLE) | (1 << PWMCLK_CNTL_SRC_OSC) + bcm.clkMem[bcm2711RegPwmclkCntrl] = bcm2711ClkManagerPwd | (0 << bcm2711RegPwmclkCntrlBitEnable) | (1 << bcm2711RegPwmclkCntrlBitSrcOsc) time.Sleep(time.Microsecond * 10) - bcm.clkMem[PWMCLK_DIV] = bcm2711ClkManagerPwd | (uint32(divisor) << 12) + bcm.clkMem[bcm2711RegPwmclkDiv] = bcm2711ClkManagerPwd | (uint32(divisor) << 12) time.Sleep(time.Microsecond * 10) // Start clock (passwd, enable, source) - bcm.clkMem[PWMCLK_CNTL] = bcm2711ClkManagerPwd | (1 << PWMCLK_CNTL_ENABLE) | (1 << PWMCLK_CNTL_SRC_OSC) + bcm.clkMem[bcm2711RegPwmclkCntrl] = bcm2711ClkManagerPwd | (1 << bcm2711RegPwmclkCntrlBitEnable) | (1 << bcm2711RegPwmclkCntrlBitSrcOsc) time.Sleep(time.Microsecond * 10) // Start pwm for both channels again - bcm.pwmMem[PWM_CTL] &= (1 << PWM_CTL_PWEN1) + bcm.pwmMem[bcm2711RegPwmCtl] &= (1 << bcm2711RegPwmCtlBitPwen1) time.Sleep(time.Microsecond * 10) return nil } // SetFanSpeed sets the fanspeed of a blade in percent (standard fan unit) -func (bcm *bcm2711bcm) SetFanSpeed(speed uint8) { +func (bcm *bcm2711) SetFanSpeed(speed uint8) { bcm.setFanSpeedPWM(speed) } -func (bcm *bcm2711bcm) setFanSpeedPWM(speed uint8) { +func (bcm *bcm2711) setFanSpeedPWM(speed uint8) { // Noctua fans are expecting a 25khz signal, where duty cycle controls fan on/speed/off // With the usage of the FIFO, we can alter the duty cycle by the number of bits set in the FIFO, maximum of 32. // We therefore need a frequency of 32*25khz = 800khz, which is a divisor of 67.5 (thus we'll use 68). @@ -240,23 +230,17 @@ func (bcm *bcm2711bcm) setFanSpeedPWM(speed uint8) { } // Use fifo, repeat, ... - bcm.pwmMem[PWM_CTL] = (1 << PWM_CTL_PWEN1) | (1 << PWM_CTL_MODE1) | (1 << PWM_CTL_RPTL1) | (1 << PWM_CTL_USEF1) + bcm.pwmMem[bcm2711RegPwmCtl] = (1 << bcm2711RegPwmCtlBitPwen1) | (1 << bcm2711RegPwmCtlBitMode1) | (1 << bcm2711RegPwmCtlBitRptl1) | (1 << bcm2711RegPwmCtlBitUsef1) time.Sleep(10 * time.Microsecond) - bcm.pwmMem[PWM_RNG1] = 32 + bcm.pwmMem[bcm2711RegPwmRng1] = 32 time.Sleep(10 * time.Microsecond) - bcm.pwmMem[PWM_FIF1] = targetvalue + bcm.pwmMem[bcm2711RegPwmFif1] = targetvalue // Store fan speed for later use bcm.currFanSpeed = speed } -type LedColor struct { - Red uint8 - Green uint8 - Blue uint8 -} - -func (bcm *bcm2711bcm) SetStealthMode(enable bool) { +func (bcm *bcm2711) SetStealthMode(enable bool) { bcm.wrMutex.Lock() defer bcm.wrMutex.Unlock() @@ -286,7 +270,7 @@ func serializePwmDataFrame(data uint8) uint32 { } // SetLEDs sets the color of the WS281x LEDs -func (bcm *bcm2711bcm) SetLEDs(top LedColor, edge LedColor) { +func (bcm *bcm2711) SetLEDs(top hal.LedColor, edge hal.LedColor) { bcm.wrMutex.Lock() defer bcm.wrMutex.Unlock() @@ -296,41 +280,42 @@ func (bcm *bcm2711bcm) SetLEDs(top LedColor, edge LedColor) { time.Sleep(10 * time.Microsecond) // WS281x Output (GPIO 18) - // -> GPFSEL1 24:26, regular output; it's configured as alt5 whenever pixel data is sent. + // -> bcm2711RegGpfsel1 24:26, regular output; it's configured as alt5 whenever pixel data is sent. // This is not optimal but required as the pwm0 peripheral is shared between fan and data line for the LEDs. time.Sleep(10 * time.Microsecond) - bcm.gpioMem[GPFSEL1] = (bcm.gpioMem[GPFSEL1] &^ (0b111 << 24)) | (0b010 << 24) + bcm.gpioMem[bcm2711RegGpfsel1] = (bcm.gpioMem[bcm2711RegGpfsel1] &^ (0b111 << 24)) | (0b010 << 24) time.Sleep(10 * time.Microsecond) defer func() { // Set to regular output again so the PWM signal doesn't confuse the WS2812 - bcm.gpioMem[GPFSEL1] = (bcm.gpioMem[GPFSEL1] &^ (0b111 << 24)) | (0b001 << 24) + bcm.gpioMem[bcm2711RegGpfsel1] = (bcm.gpioMem[bcm2711RegGpfsel1] &^ (0b111 << 24)) | (0b001 << 24) bcm.setFanSpeedPWM(bcm.currFanSpeed) }() - bcm.pwmMem[PWM_CTL] = (1 << PWM_CTL_MODE1) | (1 << PWM_CTL_RPTL1) | (0 << PWM_CTL_SBIT1) | (1 << PWM_CTL_USEF1) | (1 << PWM_CTL_CLRF1) + bcm.pwmMem[bcm2711RegPwmCtl] = (1 << bcm2711RegPwmCtlBitMode1) | (1 << bcm2711RegPwmCtlBitRptl1) | (0 << bcm2711RegPwmCtlBitSbit1) | (1 << bcm2711RegPwmCtlBitUsef1) | (1 << bcm2711RegPwmCtlBitClrf1) time.Sleep(10 * time.Microsecond) - // bcm.pwmMem[PWM_RNG1] = 32 - bcm.pwmMem[PWM_RNG1] = 24 // we only need 24 bits per LED + // bcm.pwmMem[bcm2711RegPwmRng1] = 32 + bcm.pwmMem[bcm2711RegPwmRng1] = 24 // we only need 24 bits per LED time.Sleep(10 * time.Microsecond) - // Add sufficient padding to clear - bcm.pwmMem[PWM_FIF1] = 0 - bcm.pwmMem[PWM_FIF1] = 0 - bcm.pwmMem[PWM_FIF1] = 0 + // Add sufficient padding to clear 50us of silence with ~412.5ns per bit -> at least 121 bits -> let's be safe and send 6*24=144 bits of silence + bcm.pwmMem[bcm2711RegPwmFif1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 // Write top LED data - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(top.Red) << 8 - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(top.Green) << 8 - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(top.Blue) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(top.Red) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(top.Green) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(top.Blue) << 8 // Write edge LED data - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(edge.Red) << 8 - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(edge.Green) << 8 - bcm.pwmMem[PWM_FIF1] = serializePwmDataFrame(edge.Blue) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(edge.Red) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(edge.Green) << 8 + bcm.pwmMem[bcm2711RegPwmFif1] = serializePwmDataFrame(edge.Blue) << 8 // make sure there's >50us of silence - bcm.pwmMem[PWM_FIF1] = 0 - bcm.pwmMem[PWM_FIF1] = 0 - bcm.pwmMem[PWM_FIF1] = 0 + bcm.pwmMem[bcm2711RegPwmFif1] = 0 // auto-repeated, so no need to feed the FIFO further. - bcm.pwmMem[PWM_CTL] = (1 << PWM_CTL_PWEN1) | (1 << PWM_CTL_MODE1) | (1 << PWM_CTL_RPTL1) | (0 << PWM_CTL_SBIT1) | (1 << PWM_CTL_USEF1) - // sleep for 4*50us to ensure the data is sent. + bcm.pwmMem[bcm2711RegPwmCtl] = (1 << bcm2711RegPwmCtlBitPwen1) | (1 << bcm2711RegPwmCtlBitMode1) | (1 << bcm2711RegPwmCtlBitRptl1) | (0 << bcm2711RegPwmCtlBitSbit1) | (1 << bcm2711RegPwmCtlBitUsef1) + // sleep for 4*50us to ensure the data is sent. This is probably a bit too gracious but does not have a significant impact, so let's be safe data gets out. time.Sleep(200 * time.Microsecond) } diff --git a/pkg/hal/hal.go b/pkg/hal/hal.go index 510442b..9ebc4da 100644 --- a/pkg/hal/hal.go +++ b/pkg/hal/hal.go @@ -2,16 +2,34 @@ package hal type FanUnit uint8 type ComputeModule uint8 +type PowerStatus uint8 const ( - FAN_UNIT_STANDARD = iota - FAN_UNIT_ADVANCED + FanUnitStandard = iota + FanUnitSmart ) +const ( + PoeOrUsbC = iota + Poe802at +) + +type LedColor struct { + Red uint8 + Green uint8 + Blue uint8 +} + +type ComputeBladeHalOptsDefault struct { + StealthModeEnabled bool + FanSpeed uint8 + TopLedColor LedColor + EdgeLedColor LedColor +} + type ComputeBladeHalOpts struct { - FanUnit FanUnit - DefaultFanSpeed uint8 - DefaultStealthModeEnabled bool + FanUnit FanUnit + Defaults ComputeBladeHalOptsDefault } // COmputeBladeHal abstracts hardware details of the Compute Blade and provides a simple interface @@ -20,4 +38,6 @@ type ComputeBladeHal interface { Close() error SetFanSpeed(speed uint8) SetStealthMode(enabled bool) + GetPowerStatus() PowerStatus + SetLEDs(top, edge LedColor) }