Add inductive charging control module

This module controls the inductive charging transmitter. For now, the
policy is to charge whenever possible.

BUG=chrome-os-partner:31392
TEST=Unit test passed
BRANCH=None

Change-Id: Ie48a38ad92fe2bc3329c4962e96572f2bc40b4e6
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/212715
diff --git a/board/host/board.c b/board/host/board.c
index 055fad8..c43a36a 100644
--- a/board/host/board.c
+++ b/board/host/board.c
@@ -7,6 +7,7 @@
 #include "button.h"
 #include "extpower.h"
 #include "gpio.h"
+#include "inductive_charging.h"
 #include "lid_switch.h"
 #include "motion_sense.h"
 #include "power_button.h"
diff --git a/board/host/board.h b/board/host/board.h
index b8ce985..19890b6 100644
--- a/board/host/board.h
+++ b/board/host/board.h
@@ -15,6 +15,7 @@
 #define CONFIG_POWER_BUTTON
 #undef CONFIG_WATCHDOG
 #define CONFIG_SWITCH
+#define CONFIG_INDUCTIVE_CHARGING
 
 #undef CONFIG_CONSOLE_HISTORY
 #define CONFIG_CONSOLE_HISTORY 4
diff --git a/board/host/gpio.inc b/board/host/gpio.inc
index 578e97c..9e0df82 100644
--- a/board/host/gpio.inc
+++ b/board/host/gpio.inc
@@ -15,3 +15,8 @@
 GPIO(ENABLE_BACKLIGHT,     0, 0, 0,             NULL)
 GPIO(BUTTON_VOLUME_DOWN_L, 0, 0, GPIO_INT_BOTH, button_interrupt)
 GPIO(BUTTON_VOLUME_UP,     0, 0, GPIO_INT_BOTH, button_interrupt)
+
+/* Inductive charging */
+GPIO(CHARGE_EN,            0, 0, 0,             NULL)
+GPIO(CHARGE_DONE,          0, 0, GPIO_INT_BOTH, inductive_charging_interrupt)
+GPIO(BASE_CHG_VDD_EN,      0, 0, 0,             NULL)
diff --git a/common/build.mk b/common/build.mk
index eeb0791..c44711a 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -45,6 +45,7 @@
 common-$(CONFIG_FMAP)+=fmap.o
 common-$(CONFIG_I2C)+=i2c.o
 common-$(CONFIG_I2C_ARBITRATION)+=i2c_arbitration.o
+common-$(CONFIG_INDUCTIVE_CHARGING)+=inductive_charging.o
 common-$(CONFIG_KEYBOARD_PROTOCOL_8042)+=keyboard_8042.o
 common-$(CONFIG_KEYBOARD_PROTOCOL_MKBP)+=keyboard_mkbp.o
 common-$(CONFIG_KEYBOARD_TEST)+=keyboard_test.o
diff --git a/common/inductive_charging.c b/common/inductive_charging.c
new file mode 100644
index 0000000..3a24489
--- /dev/null
+++ b/common/inductive_charging.c
@@ -0,0 +1,37 @@
+/* Copyright 2014 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.
+ */
+
+/* Inductive charging control */
+
+#include "common.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "lid_switch.h"
+
+void inductive_charging_interrupt(enum gpio_signal signal)
+{
+	int charger_enabled = gpio_get_level(GPIO_BASE_CHG_VDD_EN);
+	int charge_done = gpio_get_level(GPIO_CHARGE_DONE);
+
+	if (!charger_enabled || charge_done)
+		gpio_set_level(GPIO_CHARGE_EN, 0);
+	else
+		gpio_set_level(GPIO_CHARGE_EN, 1);
+}
+
+static void inductive_charging_lid_update(void)
+{
+	int lid_open = lid_is_open();
+	gpio_set_level(GPIO_BASE_CHG_VDD_EN, !lid_open);
+	inductive_charging_interrupt(GPIO_LID_OPEN);
+}
+DECLARE_HOOK(HOOK_LID_CHANGE, inductive_charging_lid_update, HOOK_PRIO_DEFAULT);
+
+static void inductive_charging_init(void)
+{
+	gpio_enable_interrupt(GPIO_CHARGE_DONE);
+	inductive_charging_lid_update();
+}
+DECLARE_HOOK(HOOK_INIT, inductive_charging_init, HOOK_PRIO_DEFAULT);
diff --git a/include/config.h b/include/config.h
index 97a08ae..8337c8c 100644
--- a/include/config.h
+++ b/include/config.h
@@ -570,6 +570,12 @@
 #undef CONFIG_I2C_PASSTHRU_RESTRICTED
 
 /*****************************************************************************/
+/* Inductive charging */
+
+/* Enable inductive charging support */
+#undef CONFIG_INDUCTIVE_CHARGING
+
+/*****************************************************************************/
 
 /* Number of IRQs supported on the EC chip */
 #undef CONFIG_IRQ_COUNT
diff --git a/include/inductive_charging.h b/include/inductive_charging.h
new file mode 100644
index 0000000..6087291
--- /dev/null
+++ b/include/inductive_charging.h
@@ -0,0 +1,20 @@
+/* Copyright 2014 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.
+ */
+
+/* Inductive charging control */
+
+#include "gpio.h"
+
+#ifndef __INDUCTIVE_CHARGING_H
+#define __INDUCTIVE_CHARGING_H
+
+/*
+ * Interrupt handler for inductive charging signal.
+ *
+ * @param signal  Signal which triggered the interrupt.
+ */
+void inductive_charging_interrupt(enum gpio_signal);
+
+#endif
diff --git a/test/build.mk b/test/build.mk
index a985a00..a2094ba 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -31,7 +31,7 @@
 test-list-host+=sbs_charging adapter host_command thermal_falco led_spring
 test-list-host+=bklight_lid bklight_passthru interrupt timer_dos button
 test-list-host+=motion_sense math_util sbs_charging_v2 battery_get_params_smart
-test-list-host+=lightbar
+test-list-host+=lightbar inductive_charging
 
 adapter-y=adapter.o
 button-y=button.o
@@ -42,8 +42,9 @@
 flash-y=flash.o
 hooks-y=hooks.o
 host_command-y=host_command.o
-kb_8042-y=kb_8042.o
+inductive_charging-y=inductive_charging.o
 interrupt-y=interrupt.o
+kb_8042-y=kb_8042.o
 kb_mkbp-y=kb_mkbp.o
 kb_scan-y=kb_scan.o
 led_spring-y=led_spring.o led_spring_impl.o
diff --git a/test/inductive_charging.c b/test/inductive_charging.c
new file mode 100644
index 0000000..8a4a94d
--- /dev/null
+++ b/test/inductive_charging.c
@@ -0,0 +1,103 @@
+/* Copyright 2014 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.
+ *
+ * Test inductive charging module.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "inductive_charging.h"
+#include "lid_switch.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+
+static void wait_for_lid_debounce(void)
+{
+	while (lid_is_open() != gpio_get_level(GPIO_LID_OPEN))
+		msleep(20);
+}
+
+static void set_lid_open(int lid_open)
+{
+	gpio_set_level(GPIO_LID_OPEN, lid_open);
+	wait_for_lid_debounce();
+}
+
+static int test_lid(void)
+{
+	/* Lid is open initially */
+	set_lid_open(1);
+	gpio_set_level(GPIO_CHARGE_DONE, 0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 0);
+
+	/* Close the lid. Transmitter should be enabled. */
+	set_lid_open(0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 1);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 1);
+
+	/* Open the lid. Charging should stop. */
+	set_lid_open(1);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 0);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 0);
+
+	return EC_SUCCESS;
+}
+
+static int test_charge_done(void)
+{
+	/* Close the lid to start charging */
+	set_lid_open(0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 1);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 1);
+
+	/* Charging is done. Stop charging, but don't turn off transmitter. */
+	gpio_set_level(GPIO_CHARGE_DONE, 1);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 1);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 0);
+
+	/* Oops, need charging again. */
+	gpio_set_level(GPIO_CHARGE_DONE, 0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 1);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 1);
+
+	return EC_SUCCESS;
+}
+
+static int test_lid_open_during_charging(void)
+{
+	/* Close the lid. Start charging. */
+	set_lid_open(0);
+	gpio_set_level(GPIO_CHARGE_DONE, 0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 1);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 1);
+
+	/* Open the lid. Transmitter should be turned off. */
+	set_lid_open(1);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 0);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 0);
+
+	/* Toggle charge done signal. Charging should not start. */
+	gpio_set_level(GPIO_CHARGE_DONE, 1);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 0);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 0);
+	gpio_set_level(GPIO_CHARGE_DONE, 0);
+	TEST_ASSERT(gpio_get_level(GPIO_BASE_CHG_VDD_EN) == 0);
+	TEST_ASSERT(gpio_get_level(GPIO_CHARGE_EN) == 0);
+
+	return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+	test_reset();
+
+	RUN_TEST(test_lid);
+	RUN_TEST(test_charge_done);
+	RUN_TEST(test_lid_open_during_charging);
+
+	test_print_result();
+}
diff --git a/test/inductive_charging.tasklist b/test/inductive_charging.tasklist
new file mode 100644
index 0000000..7077d82
--- /dev/null
+++ b/test/inductive_charging.tasklist
@@ -0,0 +1,17 @@
+/* Copyright 2014 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST  /* No test task */