blob: d07ee10d1c62fe4b2c5ca558bd4522b7c22341bc [file] [log] [blame]
/*
* 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 <libpayload.h>
#include "base/container_of.h"
#include "drivers/video/ww_ring_programs.h"
/* I2c address of the first of the controllers, the rest are contiguous. */
#define WW_RING_BASE_ADDR 0x32
/* Key lp55231 registers. */
#define LP55231_ENGCTRL1_REG 0x00
#define LP55231_ENGCTRL2_REG 0x01
#define LP55231_D1_CRT_CTRL_REG 0x26
#define LP55231_MISC_REG 0x36
#define LP55231_VARIABLE_REG 0x3c
#define LP55231_RESET_REG 0x3d
#define LP55231_ENG1_PROG_START 0x4c
#define LP55231_PROG_PAGE_REG 0x4f
#define LP55231_PROG_BASE_REG 0x50
/* LP55231_D1_CRT_CTRL_REG, default value, applies to all nine of them */
#define LP55231_CRT_CTRL_DEFAULT 0xaf
/* LP55231_ENGCTRL1_REG fields */
#define LP55231_ENGCTRL1_CHIP_EN 0x40
#define LP55231_ENGCTRL1_ALL_ENG_GO 0x2a
/* LP55231_ENGCTRL2_REG fields. */
#define LP55231_ENGCTRL2_ALL_DISABLE 0
#define LP55231_ENGCTRL2_ALL_LOAD 0x15
#define LP55231_ENGCTRL2_ALL_RUN 0x2a
/* LP55231_MISC_REG fields. */
#define LP55231_MISC_AUTOINCR (1 << 6)
#define LP55231_MISC_PUMP_1X (1 << 3)
#define LP55231_MISC_INT_CLK (3 << 0)
/*
* LP55231_VARIABLE_REG cookie value. It indicates to depthcharge that the
* ring has been initialized by coreboot.
*/
#define LP55231_VARIABLE_COOKIE 0xb4
/* Goes into LP55231_RESET_REG to reset the chip. */
#define LP55231_RESET_VALUE 0xff
/*
* The controller has 192 bytes of SRAM for code/data, availabe as six 32 byte
* pages.
*/
#define LP55231_PROG_PAGE_SIZE 32
#define LP55231_PROG_PAGES 6
#define LP55231_MAX_PROG_SIZE (LP55231_PROG_PAGE_SIZE * LP55231_PROG_PAGES)
/*
* 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
* transactions. The most bytes sent at a time is the register address plus
* the program page size.
*/
typedef struct {
I2cOps *ops;
uint8_t dev_addr;
uint8_t data_buffer[LP55231_PROG_PAGE_SIZE + 1];
} TiLp55231;
/* Dynamicaly allocated descriptors, one per controller */
static TiLp55231 *lp55231s;
/*
* i2c transfer function for the driver. To keep things simple, the function
* repeats the transfer, if the first attempt fails. This is OK with the
* controller and makes it easier to handle errors.
*
* Note that the reset register accesses are expected to fail on writes, but
* due to a bug in the ipq806x i2c controller, the error is reported on the
* following read attempt.
*
* To work around this the driver writes and then reads the reset register,
* the transfer function ignores errors when accessing the reset register.
*/
static int ledc_transfer(TiLp55231 *ledc, I2cSeg *segs,
int seg_count, int reset)
{
int rv, max_attempts = 2;
max_attempts = 2;
while (max_attempts--) {
rv = ledc->ops->transfer(ledc->ops, segs, seg_count);
/* Accessing reset regsiter is expected to fail. */
if (!rv || reset)
break;
}
if (rv) {
if (!reset)
printf("%s: dev %#x, reg %#x, %s transaction error.\n",
__func__, segs->chip, segs->buf[0],
seg_count == 1 ? "write" : "read");
else
rv = 0;
}
return rv;
}
/*
* The controller is programmed to autoincrement on writes, so up to page size
* bytes can be transmitted in one write transaction.
*/
static int ledc_write(TiLp55231 *ledc, uint8_t start_addr,
const uint8_t *data, unsigned count)
{
I2cSeg seg;
if (count > (sizeof(ledc->data_buffer) - 1)) {
printf("%s: transfer size too large (%d bytes)\n",
__func__, count);
return -1;
}
memcpy(ledc->data_buffer + 1, data, count);
ledc->data_buffer[0] = start_addr;
seg.read = 0;
seg.chip = ledc->dev_addr;
seg.buf = ledc->data_buffer;
seg.len = count + 1;
return ledc_transfer(ledc, &seg, 1, start_addr == LP55231_RESET_REG);
}
/* To keep things simple, read is limited to one byte at a time. */
static int ledc_read(TiLp55231 *ledc, uint8_t addr, uint8_t *data)
{
I2cSeg seg[2];
seg[0].read = 0;
seg[0].chip = ledc->dev_addr;
seg[0].buf = &addr;
seg[0].len =1;
seg[1].read = 1;
seg[1].chip = ledc->dev_addr;
seg[1].buf = data;
seg[1].len =1;
return ledc_transfer(ledc, seg, ARRAY_SIZE(seg),
addr == LP55231_RESET_REG);
}
/*
* Reset the LED ring if necessary.
*
* This function detects three conditions:
* - LED controller not present
* - LED controller present, but not initialized by coreboot
* - LED controller present and initialized by coreboot
*
* In case the controller is present but notinitialized, the reset command is
* issued. It is expected to result in a failing i2c transaction, the next
* read command is restoring the i2c bus condition.
*
* Return -1 on failure to detect controller, 0 on finding an uninitialized
* controller, 1on finding an initialized controller.
*/
static int ledc_reset(TiLp55231 *ledc)
{
uint8_t data;
data = ~0;
ledc_read(ledc, LP55231_RESET_REG, &data);
if (data) {
printf("WW_RING: no controller found at %#2.2x\n",
ledc->dev_addr);
return -1;
}
ledc_read(ledc, LP55231_VARIABLE_REG, &data);
if (data == LP55231_VARIABLE_COOKIE) {
data = 0;
printf("WW_RING: initialized controller found at %#2.2x\n",
ledc->dev_addr);
/* make sure this condition does not persist. */
ledc_write(ledc, LP55231_VARIABLE_REG, &data, 1);
return 1;
}
data = LP55231_RESET_VALUE;
ledc_write(ledc, LP55231_RESET_REG, &data, 1);
/*
* This read is not necessary for the chip reset, but is required to
* work around the i2c driver bug where the missing ACK on the last
* byte of the write transaction is ignored, but the next transaction
* fails.
*/
ledc_read(ledc, LP55231_RESET_REG, &data);
return 0;
}
/*
* Write a program into the internal lp55231 memory. Split write transactions
* into sections fitting into memory pages.
*/
static void ledc_write_program(TiLp55231 *ledc, uint8_t load_addr,
const uint8_t *program, unsigned count)
{
uint8_t page_num = load_addr / LP55231_PROG_PAGE_SIZE;
unsigned page_offs = load_addr % LP55231_PROG_PAGE_SIZE;
if ((load_addr + count) > LP55231_MAX_PROG_SIZE) {
printf("%s: program of size %#x does not fit at addr %#x\n",
__func__, count, load_addr);
return;
}
while (count) {
unsigned segment_size = LP55231_PROG_PAGE_SIZE - page_offs;
if (segment_size > count)
segment_size = count;
ledc_write(ledc, LP55231_PROG_PAGE_REG, &page_num, 1);
ledc_write(ledc, LP55231_PROG_BASE_REG + page_offs,
program, segment_size);
count -= segment_size;
program += segment_size;
page_offs = 0;
page_num++;
}
}
static void ledc_write_engctrl2(TiLp55231 *ledc, uint8_t value)
{
ledc_write(ledc, LP55231_ENGCTRL2_REG, &value, 1);
udelay(1500);
}
/* Run an lp55231 program on a controller. */
static void ledc_run_program(TiLp55231 *ledc,
const TiLp55231Program *program_desc)
{
int i;
uint8_t data;
/* All engines on hold. */
data = LP55231_ENGCTRL1_CHIP_EN;
ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1);
ledc_write_engctrl2(ledc, LP55231_ENGCTRL2_ALL_LOAD);
ledc_write_program(ledc, program_desc->load_addr,
program_desc->program_text,
program_desc->program_size);
for (i = 0; i < sizeof(program_desc->engine_start_addr); i++)
ledc_write(ledc, LP55231_ENG1_PROG_START + i,
program_desc->engine_start_addr + i, 1);
data = LP55231_ENGCTRL1_CHIP_EN | LP55231_ENGCTRL1_ALL_ENG_GO;
ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1);
ledc_write_engctrl2(ledc, LP55231_ENGCTRL2_ALL_RUN);
}
/*
* Initialize a controller to a state were it is ready to accept programs, and
* try to confirm that we are in fact talking to a lp55231
*/
static int ledc_init_validate(TiLp55231 *ledc)
{
uint8_t data;
int i;
switch (ledc_reset(ledc)) {
case -1:
return -1;
case 1:
return 0;
default:
break;
}
/* Enable the chip, keep engines in hold state. */
data = LP55231_ENGCTRL1_CHIP_EN;
ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1);
/*
* Internal clock, 3.3V output (pump 1x), autoincrement on multibyte
* writes.
*/
data = LP55231_MISC_AUTOINCR |
LP55231_MISC_PUMP_1X | LP55231_MISC_INT_CLK;
ledc_write(ledc, LP55231_MISC_REG, &data, 1);
/*
* All nine current control registers are supposed to return the same
* value at reset.
*/
for (i = 0; i < 9; i++) {
ledc_read(ledc, LP55231_D1_CRT_CTRL_REG + i, &data);
if (data != LP55231_CRT_CTRL_DEFAULT) {
printf("%s: read %#2.2x from register %#x\n", __func__,
data, LP55231_D1_CRT_CTRL_REG + i);
return -1;
}
}
return 0;
}
/*
* Find a program matching screen type, and run it on both controllers, if
* found.
*/
static int ww_ring_display_screen(DisplayOps *me,
enum VbScreenType_t screen_type)
{
const WwRingStateProg *state_program;
for (state_program = wwr_state_programs;
state_program->programs[0];
state_program++)
if (state_program->vb_screen == screen_type) {
int j;
/*
* First stop all running programs to avoid
* inerference between the controllers.
*/
for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) {
if (!lp55231s[j].dev_addr)
continue;
ledc_write_engctrl2
(lp55231s + j,
LP55231_ENGCTRL2_ALL_DISABLE);
}
for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) {
if (!lp55231s[j].dev_addr)
continue;
ledc_run_program (lp55231s + j,
state_program->programs[j]);
}
return 0;
}
printf("%s: did not find program for screen %d\n",
__func__, screen_type);
return -1;
}
static int ww_ring_init(DisplayOps *me)
{
WwRingDisplayOps *display;
TiLp55231 *ledc;
int i, count = 0;
if (lp55231s)
return 0; /* Already initialized. */
lp55231s = xzalloc(sizeof(*ledc) * WW_RING_NUM_LED_CONTROLLERS);
display = container_of(me, WwRingDisplayOps, wwr_display_ops);
for (i = 0, ledc = lp55231s;
i < WW_RING_NUM_LED_CONTROLLERS;
i++, ledc++) {
ledc->ops = display->wwr_i2c_ops;
ledc->dev_addr = display->wwr_base_addr + i;
if (!ledc_init_validate(ledc))
count++;
else
ledc->dev_addr = 0; /* Mark disabled. */
}
printf("WW_RING: initialized %d out of %d\n", count, i);
if (count != i) {
if (count)
printf("WW_RING: will keep going anyway\n");
else
printf("WW_RING: LED ring not present\n");
}
return 0;
}
DisplayOps *new_ww_ring_display(I2cOps *i2cOps, uint8_t base_addr)
{
WwRingDisplayOps *wwr_display_ops = xzalloc
(sizeof(*wwr_display_ops));
wwr_display_ops->wwr_display_ops.display_screen =
ww_ring_display_screen;
wwr_display_ops->wwr_display_ops.init = ww_ring_init;
wwr_display_ops->wwr_i2c_ops = i2cOps;
wwr_display_ops->wwr_base_addr = base_addr;
return &wwr_display_ops->wwr_display_ops;
}