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 #include -#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 +#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