Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 Google, Inc. |
| 3 | * |
| 4 | * This software is licensed under the terms of the GNU General Public |
| 5 | * License version 2, as published by the Free Software Foundation, and |
| 6 | * may be copied, distributed, and modified under those terms. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | */ |
| 13 | |
| 14 | /* |
| 15 | * This is a driver for the Whirlwind LED ring, which is equipped with two LED |
| 16 | * microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of |
| 17 | * them driving three multicolor LEDs. |
| 18 | * |
| 19 | * The only connection between the ring and the main board is an i2c bus. |
| 20 | * |
| 21 | * This driver imitates a depthcharge display device. On initialization the |
| 22 | * driver sets up the controllers to prepare them to accept programs to run. |
| 23 | * |
| 24 | * When a certain vboot state needs to be indicated, the program for that |
| 25 | * state is loaded into the controllers, resulting in the state appropriate |
| 26 | * LED behavior. |
| 27 | */ |
| 28 | |
| 29 | #include <console/console.h> |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 30 | #include <delay.h> |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 31 | #include <device/i2c.h> |
| 32 | #include <string.h> |
| 33 | |
Vadim Bendebury | 5fa5f99 | 2015-03-17 15:15:42 -0700 | [diff] [blame] | 34 | #include "drivers/i2c/ww_ring/ww_ring_programs.h" |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 35 | |
| 36 | /* I2c address of the first of the controllers, the rest are contiguous. */ |
| 37 | #define WW_RING_BASE_ADDR 0x32 |
| 38 | |
| 39 | /* Key lp55231 registers. */ |
| 40 | #define LP55231_ENGCTRL1_REG 0x00 |
| 41 | #define LP55231_ENGCTRL2_REG 0x01 |
| 42 | #define LP55231_D1_CRT_CTRL_REG 0x26 |
| 43 | #define LP55231_MISC_REG 0x36 |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 44 | #define LP55231_VARIABLE_REG 0x3c |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 45 | #define LP55231_RESET_REG 0x3d |
| 46 | #define LP55231_ENG1_PROG_START 0x4c |
| 47 | #define LP55231_PROG_PAGE_REG 0x4f |
| 48 | #define LP55231_PROG_BASE_REG 0x50 |
| 49 | |
| 50 | /* LP55231_D1_CRT_CTRL_REG, default value, applies to all nine of them */ |
| 51 | #define LP55231_CRT_CTRL_DEFAULT 0xaf |
| 52 | |
| 53 | /* LP55231_ENGCTRL1_REG fields */ |
| 54 | #define LP55231_ENGCTRL1_CHIP_EN 0x40 |
| 55 | #define LP55231_ENGCTRL1_ALL_ENG_GO 0x2a |
| 56 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 57 | /* LP55231_ENGCTRL2_REG fields. */ |
| 58 | #define LP55231_ENGCTRL2_ALL_DISABLE 0 |
| 59 | #define LP55231_ENGCTRL2_ALL_LOAD 0x15 |
| 60 | #define LP55231_ENGCTRL2_ALL_RUN 0x2a |
| 61 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 62 | /* LP55231_MISC_REG fields. */ |
| 63 | #define LP55231_MISC_AUTOINCR (1 << 6) |
| 64 | #define LP55231_MISC_PUMP_1X (1 << 3) |
| 65 | #define LP55231_MISC_INT_CLK (3 << 0) |
| 66 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 67 | /* |
| 68 | * LP55231_VARIABLE_REG cookie value. It indicates to depthcharge that the |
| 69 | * ring has been initialized by coreboot. |
| 70 | */ |
| 71 | #define LP55231_VARIABLE_COOKIE 0xb4 |
| 72 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 73 | /* Goes into LP55231_RESET_REG to reset the chip. */ |
| 74 | #define LP55231_RESET_VALUE 0xff |
| 75 | |
| 76 | /* |
| 77 | * The controller has 192 bytes of SRAM for code/data, availabe as six 32 byte |
| 78 | * pages. |
| 79 | */ |
| 80 | #define LP55231_PROG_PAGE_SIZE 32 |
| 81 | #define LP55231_PROG_PAGES 6 |
| 82 | #define LP55231_MAX_PROG_SIZE (LP55231_PROG_PAGE_SIZE * LP55231_PROG_PAGES) |
| 83 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 84 | /* |
| 85 | * Structure to cache data relevant to accessing one controller. I2c interface |
| 86 | * to use, device address on the i2c bus and a data buffer for write |
| 87 | * transactions. The most bytes sent at a time is the register address plus |
| 88 | * the program page size. |
| 89 | */ |
| 90 | typedef struct { |
| 91 | unsigned i2c_bus; |
| 92 | uint8_t dev_addr; |
| 93 | uint8_t data_buffer[LP55231_PROG_PAGE_SIZE + 1]; |
| 94 | } TiLp55231; |
| 95 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 96 | static void ww_ring_init(unsigned i2c_bus); |
| 97 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 98 | /* Controller descriptors. */ |
| 99 | static TiLp55231 lp55231s[WW_RING_NUM_LED_CONTROLLERS]; |
| 100 | |
| 101 | /* |
| 102 | * i2c transfer function for the driver. To keep things simple, the function |
| 103 | * repeats the transfer, if the first attempt fails. This is OK with the |
| 104 | * controller and makes it easier to handle errors. |
| 105 | * |
| 106 | * Note that the reset register accesses are expected to fail on writes, but |
| 107 | * due to a bug in the ipq806x i2c controller, the error is reported on the |
| 108 | * following read attempt. |
| 109 | * |
| 110 | * To work around this the driver writes and then reads the reset register, |
| 111 | * the transfer function ignores errors when accessing the reset register. |
| 112 | */ |
| 113 | |
| 114 | static int ledc_transfer(TiLp55231 *ledc, struct i2c_seg *segs, |
| 115 | int seg_count, int reset) |
| 116 | { |
| 117 | int rv, max_attempts = 2; |
| 118 | |
| 119 | while (max_attempts--) { |
| 120 | rv = i2c_transfer(ledc->i2c_bus, segs, seg_count); |
| 121 | |
| 122 | /* Accessing reset regsiter is expected to fail. */ |
| 123 | if (!rv || reset) |
| 124 | break; |
| 125 | } |
| 126 | |
| 127 | if (rv) { |
| 128 | if (!reset) |
| 129 | printk(BIOS_WARNING, |
| 130 | "%s: dev %#x, reg %#x, %s transaction error.\n", |
| 131 | __func__, segs->chip, segs->buf[0], |
| 132 | seg_count == 1 ? "write" : "read"); |
| 133 | else |
| 134 | rv = 0; |
| 135 | } |
| 136 | |
| 137 | return rv; |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | * The controller is programmed to autoincrement on writes, so up to page size |
| 142 | * bytes can be transmitted in one write transaction. |
| 143 | */ |
| 144 | static int ledc_write(TiLp55231 *ledc, uint8_t start_addr, |
| 145 | const uint8_t *data, unsigned count) |
| 146 | { |
| 147 | struct i2c_seg seg; |
| 148 | |
| 149 | if (count > (sizeof(ledc->data_buffer) - 1)) { |
| 150 | printk(BIOS_WARNING, "%s: transfer size too large (%d bytes)\n", |
| 151 | __func__, count); |
| 152 | return -1; |
| 153 | } |
| 154 | |
| 155 | memcpy(ledc->data_buffer + 1, data, count); |
| 156 | ledc->data_buffer[0] = start_addr; |
| 157 | |
| 158 | seg.read = 0; |
| 159 | seg.chip = ledc->dev_addr; |
| 160 | seg.buf = ledc->data_buffer; |
| 161 | seg.len = count + 1; |
| 162 | |
| 163 | return ledc_transfer(ledc, &seg, 1, start_addr == LP55231_RESET_REG); |
| 164 | } |
| 165 | |
| 166 | /* To keep things simple, read is limited to one byte at a time. */ |
| 167 | static int ledc_read(TiLp55231 *ledc, uint8_t addr, uint8_t *data) |
| 168 | { |
| 169 | struct i2c_seg seg[2]; |
| 170 | |
| 171 | seg[0].read = 0; |
| 172 | seg[0].chip = ledc->dev_addr; |
| 173 | seg[0].buf = &addr; |
| 174 | seg[0].len = 1; |
| 175 | |
| 176 | seg[1].read = 1; |
| 177 | seg[1].chip = ledc->dev_addr; |
| 178 | seg[1].buf = data; |
| 179 | seg[1].len = 1; |
| 180 | |
| 181 | return ledc_transfer(ledc, seg, ARRAY_SIZE(seg), |
| 182 | addr == LP55231_RESET_REG); |
| 183 | } |
| 184 | |
| 185 | /* |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 186 | * Reset transaction is expected to result in a failing i2c command. But even |
| 187 | * before trying it, read the reset register, which is supposed to always |
| 188 | * return 0. If this fails - there is no lp55231 at this address. |
| 189 | * |
| 190 | * Return 0 on success, -1 on failure to detect controller. |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 191 | */ |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 192 | static int ledc_reset(TiLp55231 *ledc) |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 193 | { |
| 194 | uint8_t data; |
| 195 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 196 | data = ~0; |
| 197 | ledc_read(ledc, LP55231_RESET_REG, &data); |
| 198 | if (data) { |
| 199 | printk(BIOS_WARNING, |
| 200 | "WW_RING: no controller found at address %#2.2x\n", |
| 201 | ledc->dev_addr); |
| 202 | return -1; |
| 203 | } |
| 204 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 205 | data = LP55231_RESET_VALUE; |
| 206 | ledc_write(ledc, LP55231_RESET_REG, &data, 1); |
| 207 | |
| 208 | /* |
| 209 | * This read is not necessary for the chip reset, but is required to |
| 210 | * work around the i2c driver bug where the missing ACK on the last |
| 211 | * byte of the write transaction is ignored, but the next transaction |
| 212 | * fails. |
| 213 | */ |
| 214 | ledc_read(ledc, LP55231_RESET_REG, &data); |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 215 | return 0; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | /* |
| 219 | * Write a program into the internal lp55231 memory. Split write transactions |
| 220 | * into sections fitting into memory pages. |
| 221 | */ |
| 222 | static void ledc_write_program(TiLp55231 *ledc, uint8_t load_addr, |
| 223 | const uint8_t *program, unsigned count) |
| 224 | { |
| 225 | uint8_t page_num = load_addr / LP55231_PROG_PAGE_SIZE; |
| 226 | unsigned page_offs = load_addr % LP55231_PROG_PAGE_SIZE; |
| 227 | |
| 228 | if ((load_addr + count) > LP55231_MAX_PROG_SIZE) { |
| 229 | printk(BIOS_WARNING, |
| 230 | "%s: program of size %#x does not fit at addr %#x\n", |
| 231 | __func__, count, load_addr); |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | while (count) { |
| 236 | unsigned segment_size = LP55231_PROG_PAGE_SIZE - page_offs; |
| 237 | |
| 238 | if (segment_size > count) |
| 239 | segment_size = count; |
| 240 | |
| 241 | ledc_write(ledc, LP55231_PROG_PAGE_REG, &page_num, 1); |
| 242 | ledc_write(ledc, LP55231_PROG_BASE_REG + page_offs, |
| 243 | program, segment_size); |
| 244 | |
| 245 | count -= segment_size; |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 246 | program += segment_size; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 247 | page_offs = 0; |
| 248 | page_num++; |
| 249 | } |
| 250 | } |
| 251 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 252 | static void ledc_write_engctrl2(TiLp55231 *ledc, uint8_t value) |
| 253 | { |
| 254 | ledc_write(ledc, LP55231_ENGCTRL2_REG, &value, 1); |
| 255 | udelay(1500); |
| 256 | } |
| 257 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 258 | /* Run an lp55231 program on a controller. */ |
| 259 | static void ledc_run_program(TiLp55231 *ledc, |
| 260 | const TiLp55231Program *program_desc) |
| 261 | { |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 262 | int i; |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 263 | uint8_t data; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 264 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 265 | /* All engines on hold. */ |
| 266 | data = LP55231_ENGCTRL1_CHIP_EN; |
| 267 | ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1); |
| 268 | |
| 269 | ledc_write_engctrl2(ledc, LP55231_ENGCTRL2_ALL_DISABLE); |
| 270 | ledc_write_engctrl2(ledc, LP55231_ENGCTRL2_ALL_LOAD); |
| 271 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 272 | ledc_write_program(ledc, program_desc->load_addr, |
| 273 | program_desc->program_text, |
| 274 | program_desc->program_size); |
| 275 | |
| 276 | for (i = 0; i < sizeof(program_desc->engine_start_addr); i++) |
| 277 | ledc_write(ledc, LP55231_ENG1_PROG_START + i, |
| 278 | program_desc->engine_start_addr + i, 1); |
| 279 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 280 | data = LP55231_ENGCTRL1_CHIP_EN | LP55231_ENGCTRL1_ALL_ENG_GO; |
| 281 | ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1); |
| 282 | ledc_write_engctrl2(ledc, LP55231_ENGCTRL2_ALL_RUN); |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | /* |
| 286 | * Initialize a controller to a state were it is ready to accept programs, and |
| 287 | * try to confirm that we are in fact talking to a lp55231 |
| 288 | */ |
| 289 | static int ledc_init_validate(TiLp55231 *ledc) |
| 290 | { |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 291 | uint8_t data; |
| 292 | int i; |
| 293 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 294 | if (ledc_reset(ledc)) |
| 295 | return -1; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 296 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 297 | /* Enable the chip, keep engines in hold state. */ |
| 298 | data = LP55231_ENGCTRL1_CHIP_EN; |
| 299 | ledc_write(ledc, LP55231_ENGCTRL1_REG, &data, 1); |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 300 | |
| 301 | /* |
| 302 | * Internal clock, 3.3V output (pump 1x), autoincrement on multibyte |
| 303 | * writes. |
| 304 | */ |
| 305 | data = LP55231_MISC_AUTOINCR | |
| 306 | LP55231_MISC_PUMP_1X | LP55231_MISC_INT_CLK; |
| 307 | ledc_write(ledc, LP55231_MISC_REG, &data, 1); |
| 308 | |
| 309 | /* |
| 310 | * All nine current control registers are supposed to return the same |
| 311 | * value at reset. |
| 312 | */ |
| 313 | for (i = 0; i < 9; i++) { |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 314 | ledc_read(ledc, LP55231_D1_CRT_CTRL_REG + i, &data); |
| 315 | if (data != LP55231_CRT_CTRL_DEFAULT) { |
| 316 | printk(BIOS_WARNING, |
| 317 | "%s: read %#2.2x from register %#x\n", __func__, |
| 318 | data, LP55231_D1_CRT_CTRL_REG + i); |
| 319 | return -1; |
| 320 | } |
| 321 | } |
| 322 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 323 | /* |
| 324 | * Signal Depthcharge that the controller has been initiazed by |
| 325 | * coreboot. |
| 326 | */ |
| 327 | data = LP55231_VARIABLE_COOKIE; |
| 328 | ledc_write(ledc, LP55231_VARIABLE_REG, &data, 1); |
| 329 | |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 330 | return 0; |
| 331 | } |
| 332 | |
| 333 | /* |
| 334 | * Find a program matching screen type, and run it on both controllers, if |
| 335 | * found. |
| 336 | */ |
Vadim Bendebury | 4e0de32 | 2015-03-17 15:37:54 -0700 | [diff] [blame] | 337 | int ww_ring_display_pattern(unsigned i2c_bus, enum display_pattern pattern) |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 338 | { |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 339 | static int initted; |
Vadim Bendebury | 5fa5f99 | 2015-03-17 15:15:42 -0700 | [diff] [blame] | 340 | const WwRingStateProg *wwr_prog; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 341 | |
| 342 | if (!initted) { |
| 343 | ww_ring_init(i2c_bus); |
| 344 | initted = 1; |
| 345 | } |
| 346 | |
Vadim Bendebury | 5fa5f99 | 2015-03-17 15:15:42 -0700 | [diff] [blame] | 347 | /* Last entry does not have any actual programs defined. */ |
| 348 | for (wwr_prog = wwr_state_programs; wwr_prog->programs[0]; wwr_prog++) |
| 349 | if (wwr_prog->led_pattern == pattern) { |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 350 | int j; |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 351 | |
Vadim Bendebury | 420fb5e | 2015-03-19 10:14:13 -0700 | [diff] [blame] | 352 | /* |
| 353 | * First stop all running programs to avoid |
| 354 | * inerference between the controllers. |
| 355 | */ |
| 356 | for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) { |
| 357 | if (!lp55231s[j].dev_addr) |
| 358 | continue; |
| 359 | ledc_write_engctrl2 |
| 360 | (lp55231s + j, |
| 361 | LP55231_ENGCTRL2_ALL_DISABLE); |
| 362 | } |
| 363 | |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 364 | for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) { |
| 365 | if (!lp55231s[j].dev_addr) |
| 366 | continue; |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 367 | ledc_run_program(lp55231s + j, |
Vadim Bendebury | 5fa5f99 | 2015-03-17 15:15:42 -0700 | [diff] [blame] | 368 | wwr_prog->programs[j]); |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 369 | } |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 370 | return 0; |
| 371 | } |
| 372 | |
Vadim Bendebury | 4e0de32 | 2015-03-17 15:37:54 -0700 | [diff] [blame] | 373 | printk(BIOS_WARNING, "%s: did not find program for pattern %d\n", |
| 374 | __func__, pattern); |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 375 | |
| 376 | return -1; |
| 377 | } |
| 378 | |
| 379 | |
| 380 | #define LP55231_I2C_BASE_ADDR 0x32 |
| 381 | |
| 382 | static void ww_ring_init(unsigned i2c_bus) |
| 383 | { |
| 384 | TiLp55231 *ledc; |
| 385 | int i, count = 0; |
| 386 | |
| 387 | for (i = 0, ledc = lp55231s; |
| 388 | i < WW_RING_NUM_LED_CONTROLLERS; |
| 389 | i++, ledc++) { |
| 390 | |
| 391 | ledc->i2c_bus = i2c_bus; |
| 392 | ledc->dev_addr = LP55231_I2C_BASE_ADDR + i; |
| 393 | |
| 394 | if (!ledc_init_validate(ledc)) |
| 395 | count++; |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 396 | else |
| 397 | ledc->dev_addr = 0; /* Mark disabled. */ |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 398 | } |
| 399 | |
| 400 | printk(BIOS_INFO, "WW_RING: initialized %d out of %d\n", count, i); |
Vadim Bendebury | 507a0cb | 2015-03-16 14:12:40 -0700 | [diff] [blame] | 401 | if (count != i) { |
| 402 | if (count) |
| 403 | printk(BIOS_WARNING, |
| 404 | "WW_RING: will keep going anyway\n"); |
| 405 | else |
| 406 | printk(BIOS_WARNING, |
| 407 | "WW_RING: LED ring not present\n"); |
| 408 | } |
Vadim Bendebury | 71558f5 | 2015-03-09 15:50:56 -0700 | [diff] [blame] | 409 | } |