summaryrefslogtreecommitdiff
path: root/chip/lm4/pwm_kblight.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/lm4/pwm_kblight.c')
-rw-r--r--chip/lm4/pwm_kblight.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/chip/lm4/pwm_kblight.c b/chip/lm4/pwm_kblight.c
new file mode 100644
index 0000000000..9382a8e35f
--- /dev/null
+++ b/chip/lm4/pwm_kblight.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* PWM control module for Chromebook keyboard backlight. */
+
+#include "clock.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "pwm.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "thermal.h"
+#include "timer.h"
+#include "util.h"
+
+/* Max PWM for controller */
+#define MAX_PWM 0x1ff
+
+#define PWMKBD_SYSJUMP_TAG 0x504b /* "PK" */
+#define PWM_HOOK_VERSION 1
+/* Saved PWM state across sysjumps */
+struct pwm_kbd_state {
+ uint8_t kblight_en;
+ uint8_t kblight_percent;
+ uint8_t pad0, pad1; /* Pad to multiple of 4 bytes. */
+};
+
+/**
+ * Configure the GPIOs for the pwm module.
+ */
+static void configure_kblight_gpios(void)
+{
+ /* HEY: This for Link only */
+ /* PK6 alternate function 1 = channel 1 PWM */
+ gpio_set_alternate_function(LM4_GPIO_K, 0x40, 1);
+}
+
+void pwm_enable_keyboard_backlight(int enable)
+{
+ if (enable)
+ LM4_FAN_FANCTL |= (1 << FAN_CH_KBLIGHT);
+ else
+ LM4_FAN_FANCTL &= ~(1 << FAN_CH_KBLIGHT);
+}
+
+int pwm_get_keyboard_backlight_enabled(void)
+{
+ return (LM4_FAN_FANCTL & (1 << FAN_CH_KBLIGHT)) ? 1 : 0;
+}
+
+int pwm_get_keyboard_backlight(void)
+{
+ return ((LM4_FAN_FANCMD(FAN_CH_KBLIGHT) >> 16) * 100 +
+ MAX_PWM / 2) / MAX_PWM;
+}
+
+void pwm_set_keyboard_backlight(int percent)
+{
+ if (percent < 0)
+ percent = 0;
+ else if (percent > 100)
+ percent = 100;
+
+ LM4_FAN_FANCMD(FAN_CH_KBLIGHT) = ((percent * MAX_PWM + 50) / 100) << 16;
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_kblight(int argc, char **argv)
+{
+ if (argc >= 2) {
+ char *e;
+ int i = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+ pwm_set_keyboard_backlight(i);
+ }
+
+ ccprintf("Keyboard backlight: %d%%\n", pwm_get_keyboard_backlight());
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(kblight, command_kblight,
+ "percent",
+ "Set keyboard backlight",
+ NULL);
+
+/*****************************************************************************/
+/* Host commands */
+
+int pwm_command_get_keyboard_backlight(struct host_cmd_handler_args *args)
+{
+ struct ec_response_pwm_get_keyboard_backlight *r = args->response;
+
+ r->percent = pwm_get_keyboard_backlight();
+ r->enabled = pwm_get_keyboard_backlight_enabled();
+ args->response_size = sizeof(*r);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT,
+ pwm_command_get_keyboard_backlight,
+ EC_VER_MASK(0));
+
+int pwm_command_set_keyboard_backlight(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_pwm_set_keyboard_backlight *p = args->params;
+
+ pwm_set_keyboard_backlight(p->percent);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT,
+ pwm_command_set_keyboard_backlight,
+ EC_VER_MASK(0));
+
+/*****************************************************************************/
+/* Hooks */
+
+static void pwm_kblight_init(void)
+{
+ const struct pwm_kbd_state *prev;
+ int version, size;
+
+ /* Enable the fan module and delay a few clocks */
+ LM4_SYSTEM_RCGCFAN = 1;
+ clock_wait_cycles(3);
+
+ /* Configure GPIOs */
+ configure_kblight_gpios();
+
+ /* Disable all fans */
+ LM4_FAN_FANCTL = 0;
+
+ /*
+ * Configure keyboard backlight:
+ * 0x0000 = bit 15 = auto-restart
+ * 0x0000 = bit 14 = slow acceleration
+ * 0x0000 = bits 13:11 = no hysteresis
+ * 0x0000 = bits 10:8 = start period (2<<0) edges
+ * 0x0000 = bits 7:6 = no fast start
+ * 0x0000 = bits 5:4 = average 4 edges when calculating RPM
+ * 0x0000 = bits 3:2 = 4 pulses per revolution
+ * 0x0001 = bit 0 = manual control
+ */
+ LM4_FAN_FANCH(FAN_CH_KBLIGHT) = 0x0001;
+
+ prev = (const struct pwm_kbd_state *)
+ system_get_jump_tag(PWMKBD_SYSJUMP_TAG, &version, &size);
+ if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) {
+ /* Restore previous state. */
+ pwm_enable_keyboard_backlight(prev->kblight_en);
+ pwm_set_keyboard_backlight(prev->kblight_percent);
+ } else {
+ /* Enable keyboard backlight control, turned down */
+ pwm_set_keyboard_backlight(0);
+ pwm_enable_keyboard_backlight(1);
+ }
+}
+DECLARE_HOOK(HOOK_INIT, pwm_kblight_init, HOOK_PRIO_DEFAULT);
+
+static void pwm_kblight_preserve_state(void)
+{
+ struct pwm_kbd_state state;
+
+ state.kblight_en = pwm_get_keyboard_backlight_enabled();
+ state.kblight_percent = pwm_get_keyboard_backlight();
+
+ system_add_jump_tag(PWMKBD_SYSJUMP_TAG, PWM_HOOK_VERSION,
+ sizeof(state), &state);
+}
+DECLARE_HOOK(HOOK_SYSJUMP, pwm_kblight_preserve_state, HOOK_PRIO_DEFAULT);
+
+static void pwm_kblight_suspend(void)
+{
+ pwm_set_keyboard_backlight(0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_kblight_suspend, HOOK_PRIO_DEFAULT);
+
+static void pwm_kblight_shutdown(void)
+{
+ pwm_set_keyboard_backlight(0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_kblight_shutdown, HOOK_PRIO_DEFAULT);