Merge "Ensure lockdown fan runs at 23KHz."
diff --git a/gpio-mailbox/brcm-direct.c b/gpio-mailbox/brcm-direct.c
index 6de0934..8abacbb 100644
--- a/gpio-mailbox/brcm-direct.c
+++ b/gpio-mailbox/brcm-direct.c
@@ -13,8 +13,6 @@
 #define UNUSED        __attribute__((unused))
 #define DEVMEM       "/dev/mem"
 
-const int PWM_CYCLE_PERIOD = 0x63;
-
 static volatile void* mmap_addr = MAP_FAILED;
 static size_t mmap_size = 0;
 static int mmap_fd = -1;
@@ -104,6 +102,7 @@
       .offset_data = 0x6580,            // PWM_CTRL ...
       .channel = 0,
       .open_drain = 1,
+      .period = 0x63,
     },
     .temp_monitor = {
       .is_present = 1,                  // 7425 AVS_RO_REGISTERS_0
@@ -177,6 +176,7 @@
       .offset_data = 0x6580,            // PWM_CTRL ...
       .channel = 0,
       .open_drain = 1,
+      .period = 0x63,
     },
     .temp_monitor = {
       .is_present = 1,                  // 7425 AVS_RO_REGISTERS_0
@@ -293,6 +293,7 @@
         .offset_data = 0x9000,          // PWM_2
         .channel = 0,
         .old_percent = -1,
+        .period = 0x63,
       },
     },
     .reset_button = {
@@ -325,6 +326,7 @@
       .offset_data = 0x9000,            // PWM_CTRL ...
       .channel = 1,
       .open_drain = 0,
+      .period = 0x91,
     },
     .temp_monitor = {
       .is_present = 1,                  // 7252 AVS_RO_REGISTERS_0
@@ -421,8 +423,9 @@
   // The fan is connected to PWM3, the register PWM3_CWORD_LSB is set to 1,
   // this is the frequency of the PWM, the other pwm register control
   // the duty cycle.
-  reg = mmap_addr + 0x9014;       // PWM3_CWORD_LSB
-  reg[0] = 1;
+  reg = mmap_addr + 0x9010;       // PWM3_CWORD_MSB
+  reg[0] = 0x20;
+  reg[1] = 0x0;                   // PWM3_CWORD_LSB
 
   // LEDs are connected to PWM2. Setting CWORD_LSB to 0xf to control
   // the output freq of the var rate clock.
@@ -571,6 +574,21 @@
   return (value == g->on_value);
 }
 
+/*
+  Set the PWM duty duty cycle. Percent bounded [0, 100].
+
+  The output period of the constant-freq PWM is calculated
+  by (period_programmed + 1) / Fv, where Fv is the output
+  of the variable-frequency PWM (in mhz).
+
+  Fv is calculated by the following formula:
+
+    Fv = (cword) * 2^-16 * 27MHz
+
+  cword is the programmed frequency control word.
+
+  The fan on lockdown must stay at a constant 23KHz
+*/
 void set_pwm(struct PwmControl *f, int percent) {
   volatile uint32_t* reg;
   uint32_t mask0, val0, mask1, val1, on;
@@ -600,21 +618,8 @@
   }
   reg[0] = (reg[0] & mask0) | val0;
   reg[1] = (reg[1] & mask1) | val1;
-  reg[on] = (PWM_CYCLE_PERIOD * percent)/100;         // 0x63 is what old code used
-  reg[on+1] = PWM_CYCLE_PERIOD;
-}
-
-/* PWM operates on either channel 0 or 1. We want to get the duty cycle value
-   by calculating it from the "ON" register, located offset 6 for channel 0
-   and 8 for channel 1.
-
-   Duty cycle is calculated by ON / Period.
-*/
-int get_pwm(struct PwmControl *f) {
-  volatile uint32_t* reg = mmap_addr + f->offset_data;
-  uint8_t offset = f->channel ? 8 : 6;
-  uint32_t val = reg[offset];
-  return ((uint64_t)val * 100) / PWM_CYCLE_PERIOD;
+  reg[on] = (f->period * percent)/100;
+  reg[on+1] = f->period;
 }
 
 void set_direction(struct Gpio *g) {
diff --git a/gpio-mailbox/brcm-nexus.c b/gpio-mailbox/brcm-nexus.c
index d52e5f1..f559c1b 100644
--- a/gpio-mailbox/brcm-nexus.c
+++ b/gpio-mailbox/brcm-nexus.c
@@ -24,8 +24,6 @@
 static double get_avs_voltage_7252(struct Voltage* v);
 static double get_avs_temperature_7252(struct Temp* t);
 
-const int PWM_CYCLE_PERIOD = 0x63;
-
 /* This is an array. Of structs! It contains structs of
    the type platform_info. The platform_info struct provides
    much useful information for use in all sorts of fun
@@ -192,10 +190,10 @@
 
   NEXUS_Pwm_CloseChannel(pwm);
 
-  /* Set the control word for the fan to 0x1. */
+  /* Set the control word for the fan to 0x2000. */
   NEXUS_Pwm_GetDefaultChannelSettings(&pwmSettings);
   pwm = NEXUS_Pwm_OpenChannel(3, &pwmSettings);
-  if (NEXUS_Pwm_SetControlWord(pwm, 0x1)) {
+  if (NEXUS_Pwm_SetControlWord(pwm, 0x2000)) {
     fprintf(stderr, "Failed setting control word for PWM.\n");
     platform_cleanup();
     exit(EXIT_FAILURE);
@@ -292,22 +290,27 @@
   return status.value != NEXUS_GpioValue_eLow;
 }
 
+/*
+  Set the pwm. See set_pwm in brcm-direct.c
+  for details.
+*/
 void set_pwm(struct PwmControl *f, int percent) {
   if (percent < 0) percent = 0;
   if (percent > 100) percent = 100;
   if (percent == f->old_percent) return;
   f->old_percent = percent;
+  uint32_t period = f->pwm_index % 2 ? 0x91 : 0x63;
 
   NEXUS_PwmChannelSettings pwmSettings;
   NEXUS_PwmChannelHandle pwm;
-  uint16_t onInterval = (PWM_CYCLE_PERIOD * percent)/100;
+  uint16_t onInterval = (period * percent)/100;
 
   NEXUS_Pwm_GetDefaultChannelSettings(&pwmSettings);
   pwmSettings.openDrain = f->open_drain;
   pwmSettings.eFreqMode = NEXUS_PwmFreqModeType_eConstant;
   pwm = NEXUS_Pwm_OpenChannel(f->pwm_index, &pwmSettings);
 
-  if (NEXUS_Pwm_SetOnAndPeriodInterval(pwm, onInterval, PWM_CYCLE_PERIOD)) {
+  if (NEXUS_Pwm_SetOnAndPeriodInterval(pwm, onInterval, period)) {
     fprintf(stderr, "Could not set ON and PERIOD for PWM. Aborting...\n");
     platform_cleanup();
     exit(EXIT_FAILURE);
diff --git a/gpio-mailbox/brcm-platform.h b/gpio-mailbox/brcm-platform.h
index 1322c60..4b53be9 100644
--- a/gpio-mailbox/brcm-platform.h
+++ b/gpio-mailbox/brcm-platform.h
@@ -41,6 +41,7 @@
   unsigned int pwm_index;               // index of this pwm.
   unsigned int channel;
   int old_percent;
+  int period;
 };
 
 struct Temp {
@@ -101,9 +102,6 @@
 /* Set the provided PWM to the given duty cycle percent */
 extern void set_pwm(struct PwmControl *, int);
 
-/* Return the duty cycle for the given PWM */
-extern int get_pwm(struct PwmControl *);
-
 /* Init GPIO to input or output. */
 extern void set_direction(struct Gpio *);