Add indicators for fastboot mode

There were no indicators on Fairphone 2 for users to be in fastboot mode.
The device would appear to be stuck on boot logo, leading users to
think that something went wrong.
This commit adds a 3 fast vibrations on fastboot enter, plus a persistent
blinking blue LED while in this mode.

Issue: FP2N-342
Change-Id: I58c9da8c0d7e81a28ece741d49bf858a9e0fcc30
diff --git a/target/FP2/include/target/rgb_led.h b/target/FP2/include/target/rgb_led.h
new file mode 100644
index 0000000..b6f6fd6
--- /dev/null
+++ b/target/FP2/include/target/rgb_led.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 Fairphone B.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _TARGET_FP2_RGB_LED_H_
+#define _TARGET_FP2_RGB_LED_H_
+
+#include <pm8x41.h>
+
+// Slave-ID
+#define SLAVE_ID                  0x10000
+
+// RGB module registers
+#define RGB_DRIVER_BASE_ADDR      0xD000
+#define RGB_DRIVER_LED_SRC_SEL    0x45
+#define RGB_DRIVER_EN_CTL         0x46
+#define RGB_LED_VALUE_RED         0x80
+#define RGB_LED_VALUE_GREEN       0x40
+#define RGB_LED_VALUE_BLUE        0x20
+
+// LPG module registers
+#define LPG_DRIVER_BASE_ADDR      0xB000
+#define LPG_DRIVER_LED_RED        0x700
+#define LPG_DRIVER_LED_GREEN      0x600
+#define LPG_DRIVER_LED_BLUE       0x500
+#define LPG_PATTERN_CONFIG        0x40
+#define LPG_PWM_SIZE_CLK          0x41
+#define LPG_PWM_FREQ_PREDIV       0x42
+#define LPG_PWM_TYPE_CONFIG       0x43
+#define LPG_VALUE_LSB             0x44
+#define LPG_VALUE_MSB             0x45
+#define LPG_ENABLE_CONTROL        0x46
+
+typedef enum rgb_led_return_code
+{
+    RGB_LED_SUCCESS,
+    RGB_LED_GENERIC_ERROR,
+    RGB_LED_INVALID_PARAMETER
+} rgb_led_return_code;
+
+typedef enum rgb_led_brightness
+{
+    RGB_LED_BRIGHTNESS_LOW = 0x80,
+    RGB_LED_BRIGHTNESS_MID = 0x7F,
+    RGB_LED_BRIGHTNESS_HIG = 0xFF
+} rgb_led_brightness;
+
+rgb_led_return_code led_init(void);
+rgb_led_return_code led_enable(uint8_t led, rgb_led_brightness brightness);
+rgb_led_return_code led_blink_enable(uint8_t led, uint8_t pwm_freq, uint8_t duty_cycle);
+rgb_led_return_code led_disable(uint8_t led);
+
+rgb_led_return_code led_deinit(void);
+
+#endif  //_TARGET_FP2_RGB_LED_H_
diff --git a/target/FP2/init.c b/target/FP2/init.c
index c15bb59..1035038 100644
--- a/target/FP2/init.c
+++ b/target/FP2/init.c
@@ -1,4 +1,5 @@
 /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright 2018 Fairphone B.V.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -42,6 +43,7 @@
 #include <baseband.h>
 #include <dev/keys.h>
 #include <pm8x41.h>
+#include <target/rgb_led.h>
 #include <crypto5_wrapper.h>
 #include <hsusb.h>
 #include <clock.h>
@@ -465,6 +467,17 @@
 	clock_ce_enable(SSD_CE_INSTANCE_1);
 	ssd_load_keystore_from_emmc();
 #endif
+
+	/* Vibration pattern, 3 fast vibes in a row */
+	vibrator_enable();
+	thread_sleep(150);
+	vibrator_enable();
+	thread_sleep(150);
+	vibrator_enable();
+	thread_sleep(150);
+	/* Enable blinking of Blue LED */
+	led_init();
+	led_blink_enable(RGB_LED_VALUE_BLUE, 0x06, 0x2C); // 0x06 means 0.25 KHz PWM frequency (4 secs duty cycle duration), 0x2C means about 70% of the duty cycle
 }
 
 /* Detect the target type */
diff --git a/target/FP2/rgb_led.c b/target/FP2/rgb_led.c
new file mode 100644
index 0000000..3769f7b
--- /dev/null
+++ b/target/FP2/rgb_led.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2018 Fairphone B.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <target/rgb_led.h>
+
+rgb_led_return_code led_init()
+{
+    /* Enable RGB module, Select Vph_pwr as power source */
+    pm8x41_reg_write( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_LED_SRC_SEL, 0x01);
+
+    return RGB_LED_SUCCESS;
+}
+
+rgb_led_return_code led_enable(uint8_t led, rgb_led_brightness brightness)
+{
+    uint32_t led_lpg_value = 0;
+    switch(led)
+    {
+        case RGB_LED_VALUE_RED:
+            led_lpg_value = LPG_DRIVER_LED_RED;
+            break;
+        case RGB_LED_VALUE_GREEN:
+            led_lpg_value = LPG_DRIVER_LED_GREEN;
+            break;
+        case RGB_LED_VALUE_BLUE:
+            led_lpg_value = LPG_DRIVER_LED_BLUE;
+            break;
+        default:
+            return RGB_LED_INVALID_PARAMETER;
+    }
+    /* Building LED specific base address */
+    const uint32_t led_lpg_base_address = SLAVE_ID + LPG_DRIVER_BASE_ADDR + led_lpg_value;
+    /* Enable selected LED, preserving the previous value */
+    uint8_t val = pm8x41_reg_read( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL );
+    val |= led;
+    pm8x41_reg_write( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL, val);
+    /*
+    *  Enable PWM at requested duty cycle
+    *  For a always-on behaviour no pattern is needed.
+    *  PWM is set to 7-bit mode in order to be able to use both PWM channels (4 mA + 8 mA).
+    *  PWM frequency is set to 390 Hz (Pre-divide=5, Exponent=7).
+    *  The type config is not used here but is mandatory to set the register.
+    *  PWM value acts here as brightness parameter. 0xFF is the full-scale value.
+    *  PWM value MSB is useless here.
+    *  PWM is enabled by writing 0xE4 in the enable register.
+    */
+    pm8x41_reg_write( led_lpg_base_address + LPG_PATTERN_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_SIZE_CLK, 0x13);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_FREQ_PREDIV, 0x47);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_TYPE_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_LSB, brightness);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_MSB, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_ENABLE_CONTROL, 0xE4);
+
+    return RGB_LED_SUCCESS;
+}
+
+rgb_led_return_code led_blink_enable(uint8_t led, uint8_t pwm_freq, uint8_t duty_cycle)
+{
+    if(duty_cycle > 0x3F)
+    {
+        return RGB_LED_INVALID_PARAMETER;
+    }
+
+    uint32_t led_lpg_value = 0;
+    switch(led)
+    {
+        case RGB_LED_VALUE_RED:
+            led_lpg_value = LPG_DRIVER_LED_RED;
+            break;
+        case RGB_LED_VALUE_GREEN:
+            led_lpg_value = LPG_DRIVER_LED_GREEN;
+            break;
+        case RGB_LED_VALUE_BLUE:
+            led_lpg_value = LPG_DRIVER_LED_BLUE;
+            break;
+        default:
+            return RGB_LED_INVALID_PARAMETER;
+    }
+    /* Building LED specific base address */
+    const uint32_t led_lpg_base_address = SLAVE_ID + LPG_DRIVER_BASE_ADDR + led_lpg_value;
+    /* Enable selected LED, preserving the previous value */
+    uint8_t val = pm8x41_reg_read( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL );
+    val |= led;
+    pm8x41_reg_write( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL, val);
+    /*
+    *  Enable PWM at requested duty cycle
+    *  For a simple blinking behaviour no pattern is needed.
+    *  PWM is set to 6-bit mode. This results in a clock of 1 KHz.
+    *  PWM frequency is set as desidered. Refer to docs to properly set this parameter.
+    *  The type config is not used here but is mandatory to set the register.
+    *  PWM value acts here as duty cycle value. 0x3F is the full-scale value.
+    *  PWM value MSB is useless here.
+    *  PWM is enabled by writing 0xE4 in the enable register.
+    */
+    pm8x41_reg_write( led_lpg_base_address + LPG_PATTERN_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_SIZE_CLK, 0x01);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_FREQ_PREDIV, pwm_freq);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_TYPE_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_LSB, duty_cycle);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_MSB, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_ENABLE_CONTROL, 0xE4);
+
+    return RGB_LED_SUCCESS;
+}
+
+rgb_led_return_code led_disable(uint8_t led)
+{
+    uint32_t led_lpg_value = 0;
+    switch(led)
+    {
+        case RGB_LED_VALUE_RED:
+            led_lpg_value = LPG_DRIVER_LED_RED;
+            break;
+        case RGB_LED_VALUE_GREEN:
+            led_lpg_value = LPG_DRIVER_LED_GREEN;
+            break;
+        case RGB_LED_VALUE_BLUE:
+            led_lpg_value = LPG_DRIVER_LED_BLUE;
+            break;
+        default:
+            return RGB_LED_INVALID_PARAMETER;
+    }
+    /* Building LED specific base address */
+    const uint32_t led_lpg_base_address = SLAVE_ID + LPG_DRIVER_BASE_ADDR + led_lpg_value;
+    /* Disable selected LED, preserving the previous value */
+    uint8_t val = pm8x41_reg_read( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL );
+    val &= ~(led);
+    pm8x41_reg_write( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_EN_CTL, val);
+
+    /* Clear LPG registers */
+    pm8x41_reg_write( led_lpg_base_address + LPG_PATTERN_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_SIZE_CLK, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_FREQ_PREDIV, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_PWM_TYPE_CONFIG, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_LSB, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_VALUE_MSB, 0x00);
+    pm8x41_reg_write( led_lpg_base_address + LPG_ENABLE_CONTROL, 0x00);
+
+    return RGB_LED_SUCCESS;
+}
+
+rgb_led_return_code led_deinit()
+{
+    rgb_led_return_code rc;
+    rc = led_disable(RGB_LED_VALUE_RED);
+    if(rc != RGB_LED_SUCCESS)
+    {
+        return rc;
+    }
+    rc = led_disable(RGB_LED_VALUE_GREEN);
+    if(rc != RGB_LED_SUCCESS)
+    {
+        return rc;
+    }
+    rc = led_disable(RGB_LED_VALUE_BLUE);
+    if(rc != RGB_LED_SUCCESS)
+    {
+        return rc;
+    }
+
+    /* Power off the RGB module by setting no power source */
+    pm8x41_reg_write( SLAVE_ID + RGB_DRIVER_BASE_ADDR + RGB_DRIVER_LED_SRC_SEL, 0x00);
+
+    return RGB_LED_SUCCESS;
+}
diff --git a/target/FP2/rules.mk b/target/FP2/rules.mk
index ab9a431..8ed30db 100644
--- a/target/FP2/rules.mk
+++ b/target/FP2/rules.mk
@@ -41,4 +41,5 @@
     $(LOCAL_DIR)/init.o \
     $(LOCAL_DIR)/meminfo.o \
     $(LOCAL_DIR)/target_display.o \
-    $(LOCAL_DIR)/oem_panel.o
+    $(LOCAL_DIR)/oem_panel.o \
+    $(LOCAL_DIR)/rgb_led.o