summaryrefslogtreecommitdiff
path: root/src/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/i2c')
-rw-r--r--src/drivers/i2c/ww_ring/Makefile.inc1
-rw-r--r--src/drivers/i2c/ww_ring/ww_ring.c64
-rw-r--r--src/drivers/i2c/ww_ring/ww_ring_programs.c326
-rw-r--r--src/drivers/i2c/ww_ring/ww_ring_programs.h62
4 files changed, 395 insertions, 58 deletions
diff --git a/src/drivers/i2c/ww_ring/Makefile.inc b/src/drivers/i2c/ww_ring/Makefile.inc
index 963411ae1a..56de190fce 100644
--- a/src/drivers/i2c/ww_ring/Makefile.inc
+++ b/src/drivers/i2c/ww_ring/Makefile.inc
@@ -1 +1,2 @@
verstage-$(CONFIG_DRIVERS_I2C_WW_RING) += ww_ring.c
+verstage-$(CONFIG_DRIVERS_I2C_WW_RING) += ww_ring_programs.c
diff --git a/src/drivers/i2c/ww_ring/ww_ring.c b/src/drivers/i2c/ww_ring/ww_ring.c
index 243543a021..780d0db0bb 100644
--- a/src/drivers/i2c/ww_ring/ww_ring.c
+++ b/src/drivers/i2c/ww_ring/ww_ring.c
@@ -31,10 +31,7 @@
#include <device/i2c.h>
#include <string.h>
-#include "drivers/i2c/ww_ring/ww_ring.h"
-
-/* Number of lp55321 controllers on the ring */
-#define WW_RING_NUM_LED_CONTROLLERS 2
+#include "drivers/i2c/ww_ring/ww_ring_programs.h"
/* I2c address of the first of the controllers, the rest are contiguous. */
#define WW_RING_BASE_ADDR 0x32
@@ -84,9 +81,6 @@
#define LP55231_PROG_PAGES 6
#define LP55231_MAX_PROG_SIZE (LP55231_PROG_PAGE_SIZE * LP55231_PROG_PAGES)
-/* There are threee independent engines/cores in the controller. */
-#define LP55231_NUM_OF_ENGINES 3
-
/*
* Structure to cache data relevant to accessing one controller. I2c interface
* to use, device address on the i2c bus and a data buffer for write
@@ -99,55 +93,8 @@ typedef struct {
uint8_t data_buffer[LP55231_PROG_PAGE_SIZE + 1];
} TiLp55231;
-/*
- * Structure to describe an lp55231 program: pointer to the text of the
- * program, its size and load address (load addr + size sould not exceed
- * LP55231_MAX_PROG_SIZE), and start addresses for all of the three
- * engines.
- */
-typedef struct {
- const uint8_t *program_text;
- uint8_t program_size;
- uint8_t load_addr;
- uint8_t engine_start_addr[LP55231_NUM_OF_ENGINES];
-} TiLp55231Program;
-
-/* A structure to bind controller programs to a vboot state. */
-typedef struct {
- enum display_pattern led_pattern;
- const TiLp55231Program * programs[WW_RING_NUM_LED_CONTROLLERS];
-} WwRingStateProg;
-
static void ww_ring_init(unsigned i2c_bus);
-/****************************************************************/
-/* LED ring program definitions for different vboot states. */
-
-static const uint8_t blink_program_text[] = {
- 0x40, 0x40, 0x9D, 0x04, 0x40, 0x40, 0x7E,
- 0x00, 0x9D, 0x07, 0x40, 0x00, 0x9D, 0x04,
- 0x40, 0x00, 0x7E, 0x00, 0xA0, 0x00, 0x00,
- 0x00 };
-
-static const TiLp55231Program led_blink_program = {
- blink_program_text,
- sizeof(blink_program_text),
- 0,
- {0,
- sizeof(blink_program_text) - 2,
- sizeof(blink_program_text) - 2}
-};
-
-static const WwRingStateProg state_programs[] = {
- /*
- * for test purposes the blank screen program is set to blinking, will
- * be changed soon.
- */
- {WWR_ALL_OFF, {&led_blink_program, &led_blink_program} },
-};
-/* */
-/****************************************************************/
-
/* Controller descriptors. */
static TiLp55231 lp55231s[WW_RING_NUM_LED_CONTROLLERS];
@@ -389,23 +336,24 @@ static int ledc_init_validate(TiLp55231 *ledc)
*/
int ww_ring_display_pattern(unsigned i2c_bus, enum display_pattern pattern)
{
- int i;
static int initted;
+ const WwRingStateProg *wwr_prog;
if (!initted) {
ww_ring_init(i2c_bus);
initted = 1;
}
- for (i = 0; i < ARRAY_SIZE(state_programs); i++)
- if (state_programs[i].led_pattern == pattern) {
+ /* Last entry does not have any actual programs defined. */
+ for (wwr_prog = wwr_state_programs; wwr_prog->programs[0]; wwr_prog++)
+ if (wwr_prog->led_pattern == pattern) {
int j;
for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) {
if (!lp55231s[j].dev_addr)
continue;
ledc_run_program(lp55231s + j,
- state_programs[i].programs[j]);
+ wwr_prog->programs[j]);
}
return 0;
}
diff --git a/src/drivers/i2c/ww_ring/ww_ring_programs.c b/src/drivers/i2c/ww_ring/ww_ring_programs.c
new file mode 100644
index 0000000000..81c18915ad
--- /dev/null
+++ b/src/drivers/i2c/ww_ring/ww_ring_programs.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/*
+ * This is a driver for the Whirlwind LED ring, which is equipped with two LED
+ * microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of
+ * them driving three multicolor LEDs.
+ *
+ * The only connection between the ring and the main board is an i2c bus.
+ *
+ * This driver imitates a depthcharge display device. On initialization the
+ * driver sets up the controllers to prepare them to accept programs to run.
+ *
+ * When a certain vboot state needs to be indicated, the program for that
+ * state is loaded into the controllers, resulting in the state appropriate
+ * LED behavior.
+ */
+
+#include "drivers/i2c/ww_ring/ww_ring_programs.h"
+
+/****************************************************************
+ * LED ring program definitions for different patterns.
+ *
+ * Comments below are real lp55231 source code, they are compiled using
+ * lasm.exe, the TI tool available from their Web site (search for lp55231)
+ * and running only on Windows :P.
+ *
+ * Different hex dumps are results of tweaking the source code parameters to
+ * achieve desirable LED ring behavior. It is possible to use just one code
+ * dump and replace certain values in the body to achieve different behaviour
+ * with the same basic dump, but keeping track of location to tweak with every
+ * code change would be quite tedious.
+ */
+
+/*
+ * Solid LED display, the arguments of the set_pwm commands set intensity and
+ * color of the display:
+
+row_red: dw 0000000001001001b
+row_green: dw 0000000010010010b
+row_blue: dw 0000000100100100b
+
+.segment program1
+ mux_map_addr row_red
+ set_pwm 1
+ end
+
+.segment program2
+ mux_map_addr row_green
+ set_pwm 1
+ end
+
+.segment program3
+ mux_map_addr row_blue
+ set_pwm 1
+ end
+*/
+
+/* RGB set to 000000, resulting in all LEDs off. */
+static const uint8_t solid_000000_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x9F, 0x80,
+ 0x40, 0x00, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0x00,
+ 0xC0, 0x00, 0x9F, 0x82, 0x40, 0x00, 0xC0, 0x00
+};
+
+/* RGB set to 010101, resulting in a bleak greyish color. */
+static const uint8_t solid_010101_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x9F, 0x80,
+ 0x40, 0x01, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0x01,
+ 0xC0, 0x00, 0x9F, 0x82, 0x40, 0x01, 0xC0, 0x00
+};
+
+static const TiLp55231Program solid_010101_program = {
+ solid_010101_text,
+ sizeof(solid_010101_text),
+ 0,
+ { 3, 6, 9 }
+};
+
+static const TiLp55231Program solid_000000_program = {
+ solid_000000_text,
+ sizeof(solid_000000_text),
+ 0,
+ { 3, 6, 9 }
+};
+
+/*
+ * Blinking patterns are much tricker then solid ones.
+ *
+ * The three internal engines seem to be competing for resources and get out
+ * of sync in seconds if left running asynchronously.
+ *
+ * When there are two separate controllers, with three engine each, they all
+ * run away from each other in no time, resulting in some completely chaotic
+ * LED behavior.
+ *
+ * To keep the ring in check internal and external triggers are used which
+ * makes programs for controller1 and controller2 sligtly different.
+ *
+ * The first controller is loaded and started first, but it sits waiting for
+ * the trigger from the second controller to actually start the cycle.
+ *
+ * In the middle of the cycle the first controller sends a sync back to the
+ * second one. Both controllers' engine1 also synchs up their respective
+ * engines 2 and 3.
+ *
+ * The maximum timer duration of lp55231 is .48 seconds. To achieve longer
+ * blinking intervals the loops delays are deployed.
+ *
+ * The granularity is set at .1 second (see commands 'wait 0.1' in the code,
+ * and then the loop counters can be set up to 63 (registers rb and rc), which
+ * allows to generate intervals up to 6.3 seconds in .1 second increments.
+ */
+/* blink_solid1.src
+row_red: dw 0000000001001001b
+row_green: dw 0000000010010010b
+row_blue: dw 0000000100100100b
+
+.segment program1
+ ld ra, 255 ; red intensity
+ ld rb, 2 ; up time 200 ms
+ ld rc, 2 ; down time 200 ms
+
+ mux_map_addr row_red
+loop1: trigger w{e} ; wait for external trigger from 2nd controller
+ trigger s{2|3}
+ set_pwm ra
+common1:
+ wait 0.1
+ branch rb, common1
+ trigger s{2|3|e}
+ set_pwm 0
+wait1:
+ wait 0.1
+ branch rc, wait1
+ branch 0, loop1
+
+
+.segment program2
+ mux_map_addr row_green
+ ld ra, 255 ; green intensity
+loop2: trigger w{1}
+ set_pwm ra
+common2:
+ ; engine 2 and 3 intervals are controlled by sync with engine 1
+ wait 0.1
+ branch 1, common2
+ trigger w{1}
+ set_pwm 0
+wait2:
+ wait 0.1
+ branch 1, wait2
+ branch 0, loop2
+
+
+.segment program3
+ ld ra, 0 ; blue intensity
+loop3: trigger w{1}
+ set_pwm ra
+common3:
+ wait 0.1
+ branch 1, common3
+ trigger w{1}
+ set_pwm 0
+wait3:
+ wait 0.1
+ branch 1, wait3
+ branch 0, loop3
+*/
+/* blink_solid2.src
+row_red: dw 0000000001001001b
+row_green: dw 0000000010010010b
+row_blue: dw 0000000100100100b
+
+.segment program1
+ ld ra, 255 ; red intensity
+ ld rb, 2 ; up time
+ ld rc, 2 ; down time
+ mux_map_addr row_red
+loop1: trigger s{2|3|e} ; send trigger to own engines and the first controller
+ set_pwm ra
+common1:
+ wait 0.1
+ branch rb, common1
+ trigger w{e}
+ trigger s{2|3}
+ set_pwm 0
+wait1:
+ wait 0.1
+ branch rc, wait1
+ branch 0, loop1
+
+
+.segment program2
+ mux_map_addr row_green
+ ld ra, 255
+loop2: trigger w{1}
+ set_pwm ra
+common2:
+ wait 0.1
+ branch 1, common2
+ trigger w{1}
+ set_pwm 0
+wait2:
+ wait 0.1
+ branch 1, wait2
+ branch 0, loop2
+
+
+.segment program3
+ mux_map_addr row_blue
+ ld ra, 0
+loop3: trigger w{1}
+ set_pwm ra
+common3:
+ wait 0.1
+ branch 1, common3
+ trigger w{1}
+ set_pwm 0
+wait3:
+ wait 0.1
+ branch 1, wait3
+ branch 0, loop3
+ */
+static const uint8_t blink_wipeout1_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
+ 0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xf0, 0x00,
+ 0xe0, 0x0c, 0x84, 0x60, 0x4c, 0x00, 0x86, 0x1d,
+ 0xe0, 0x4c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
+ 0xa0, 0x04, 0x9f, 0x81, 0x90, 0xff, 0xe0, 0x80,
+ 0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
+ 0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
+ 0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
+ 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
+ 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
+};
+
+static const uint8_t blink_wipeout2_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
+ 0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xe0, 0x4c,
+ 0x84, 0x60, 0x4c, 0x00, 0x86, 0x19, 0xf0, 0x00,
+ 0xe0, 0x0c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
+ 0xa0, 0x04, 0x9f, 0x81, 0x90, 0xff, 0xe0, 0x80,
+ 0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
+ 0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
+ 0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
+ 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
+ 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
+};
+
+static const TiLp55231Program blink_wipeout1_program = {
+ blink_wipeout1_text,
+ sizeof(blink_wipeout1_text),
+ 0,
+ { 3, 17, 28, }
+};
+static const TiLp55231Program blink_wipeout2_program = {
+ blink_wipeout2_text,
+ sizeof(blink_wipeout2_text),
+ 0,
+ { 3, 17, 28, }
+};
+
+static const uint8_t blink_recovery1_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
+ 0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xf0, 0x00,
+ 0xe0, 0x0c, 0x84, 0x60, 0x4c, 0x00, 0x86, 0x1d,
+ 0xe0, 0x4c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
+ 0xa0, 0x04, 0x9f, 0x81, 0x90, 0x3d, 0xe0, 0x80,
+ 0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
+ 0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
+ 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60, 0x4c, 0x00,
+ 0xa0, 0x83, 0xe0, 0x80, 0x40, 0x00, 0x4c, 0x00,
+ 0xa0, 0x87, 0xa0, 0x01, 0x00, 0x00, 0x00,
+};
+static const TiLp55231Program blink_recovery1_program = {
+ blink_recovery1_text,
+ sizeof(blink_recovery1_text),
+ 0,
+ { 3, 17, 28, }
+};
+static const uint8_t blink_recovery2_text[] = {
+ 0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
+ 0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xe0, 0x4c,
+ 0x84, 0x60, 0x4c, 0x00, 0x86, 0x19, 0xf0, 0x00,
+ 0xe0, 0x0c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
+ 0xa0, 0x04, 0x9f, 0x81, 0x90, 0x3d, 0xe0, 0x80,
+ 0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
+ 0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
+ 0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
+ 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
+ 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
+ 0x00,
+};
+static const TiLp55231Program blink_recovery2_program = {
+ blink_recovery2_text,
+ sizeof(blink_recovery2_text),
+ 0,
+ { 3, 17, 28, }
+};
+
+
+const WwRingStateProg wwr_state_programs[] = {
+ /*
+ * for test purposes the blank screen program is set to blinking, will
+ * be changed soon.
+ */
+ {WWR_ALL_OFF, {&solid_000000_program, &solid_000000_program} },
+ {WWR_RECOVERY_PUSHED, {&solid_010101_program, &solid_010101_program} },
+ {WWR_WIPEOUT_REQUEST, {&blink_wipeout1_program,
+ &blink_wipeout2_program} },
+ {WWR_RECOVERY_REQUEST, {&blink_recovery1_program,
+ &blink_recovery2_program} },
+ {}, /* Empty record to mark the end of the table. */
+};
+
diff --git a/src/drivers/i2c/ww_ring/ww_ring_programs.h b/src/drivers/i2c/ww_ring/ww_ring_programs.h
new file mode 100644
index 0000000000..9f4b928941
--- /dev/null
+++ b/src/drivers/i2c/ww_ring/ww_ring_programs.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/*
+ * This is a driver for the Whirlwind LED ring, which is equipped with two LED
+ * microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of
+ * them driving three multicolor LEDs.
+ *
+ * The only connection between the ring and the main board is an i2c bus.
+ *
+ * This driver imitates a depthcharge display device. On initialization the
+ * driver sets up the controllers to prepare them to accept programs to run.
+ *
+ * When a certain vboot state needs to be indicated, the program for that
+ * state is loaded into the controllers, resulting in the state appropriate
+ * LED behavior.
+ */
+
+#ifndef __THIRD_PARTY_COREBOOT_SRC_DRIVERS_I2C_WW_RING_WW_RING_PROGRAMS_H__
+#define __THIRD_PARTY_COREBOOT_SRC_DRIVERS_I2C_WW_RING_WW_RING_PROGRAMS_H__
+
+#include <stdint.h>
+#include "drivers/i2c/ww_ring/ww_ring.h"
+
+/* There are threee independent engines/cores in the controller. */
+#define LP55231_NUM_OF_ENGINES 3
+
+/* Number of lp55321 controllers on the ring */
+#define WW_RING_NUM_LED_CONTROLLERS 2
+
+/*
+ * Structure to describe an lp55231 program: pointer to the text of the
+ * program, its size and load address (load addr + size sould not exceed
+ * LP55231_MAX_PROG_SIZE), and start addresses for all of the three
+ * engines.
+ */
+typedef struct {
+ const uint8_t *program_text;
+ uint8_t program_size;
+ uint8_t load_addr;
+ uint8_t engine_start_addr[LP55231_NUM_OF_ENGINES];
+} TiLp55231Program;
+
+/* A structure to bind controller programs to a vboot state. */
+typedef struct {
+ enum display_pattern led_pattern;
+ const TiLp55231Program *programs[WW_RING_NUM_LED_CONTROLLERS];
+} WwRingStateProg;
+
+extern const WwRingStateProg wwr_state_programs[];
+
+#endif