| /* 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 "inductive_charging.h" |
| #include "lid_switch.h" |
| #include "timer.h" |
| |
| /* |
| * The inductive charger is controlled with two signals: |
| * - BASE_CHG_VDD_EN controls whether the charger is powered. |
| * - CHARGE_EN controls whether to enable charging. |
| * Charging status is reported via CHARGE_DONE, but in a tricky way: |
| * - It's 0 if: |
| * + The charger is unpowered. (i.e. BASE_CHG_VDD_EN = 0) |
| * + Or charging is disabled. (i.e. CHARGE_EN = 0) |
| * + Or the charging current is small enough. |
| * - Otherwise, it's 1. |
| */ |
| |
| /* Whether we want to process interrupts on CHARGE_DONE or not. */ |
| static int monitor_charge_done; |
| |
| /* |
| * Start monitoring CHARGE_DONE and fires the interrupt once so that |
| * we react to the current value. |
| */ |
| static void inductive_charging_monitor_charge(void) |
| { |
| monitor_charge_done = 1; |
| inductive_charging_interrupt(GPIO_CHARGE_DONE); |
| } |
| DECLARE_DEFERRED(inductive_charging_monitor_charge); |
| |
| 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); |
| static int charge_already_done; |
| |
| if (!monitor_charge_done && signal == GPIO_CHARGE_DONE) |
| return; |
| |
| if (signal == GPIO_LID_OPEN) { |
| /* The lid has been opened. Clear all states. */ |
| charge_done = 0; |
| charge_already_done = 0; |
| monitor_charge_done = 0; |
| } else if (signal == GPIO_CHARGE_DONE) { |
| /* |
| * Once we see CHARGE_DONE=1, we ignore any change on |
| * CHARGE_DONE until the next time the lid is opened. |
| */ |
| if (charge_done == 1) |
| charge_already_done = 1; |
| else if (charge_already_done) |
| return; |
| } |
| |
| if (!charger_enabled || charge_done) { |
| gpio_set_level(GPIO_CHARGE_EN, 0); |
| } else { |
| gpio_set_level(GPIO_CHARGE_EN, 1); |
| /* |
| * When the charging is just enabled, there might be a |
| * blip on CHARGE_DONE. Wait for a second before we start |
| * looking at CHARGE_DONE. |
| */ |
| if (!monitor_charge_done) |
| hook_call_deferred( |
| &inductive_charging_monitor_charge_data, |
| SECOND); |
| } |
| } |
| |
| static void inductive_charging_deferred_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_DEFERRED(inductive_charging_deferred_update); |
| |
| static void inductive_charging_lid_update(void) |
| { |
| /* |
| * When the lid close signal changes, the coils might still be |
| * unaligned. Delay here to give the coils time to align before |
| * we try to clear CHARGE_DONE. |
| */ |
| hook_call_deferred(&inductive_charging_deferred_update_data, |
| 5 * SECOND); |
| } |
| 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); |