/* Copyright 2020 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * INT_AP_L extension */ #include "config.h" #include "console.h" #include "gpio.h" #include "registers.h" #include "task.h" #include "timer.h" /* * Minimum time length (in microseconds) of INT_AP_L pulse required by CPU. * It is set to be 100 microseconds, which a requirement of INTL TGL, GPIO * in low power mode. */ #define PULSE_LENGTH 100ul #define USEC_TO_TIMEHS_TICKS(usec) ((usec) * (PCLK_FREQ / SEC_UL)) /* INT_AP_L pulse length in TIMEHS ticks. 0 means the extension is disabled. */ static uint32_t pulse_length; /* true if INT_AP_L is asserted, or false if it is deasserted. */ static bool int_ap_asserted_; /* * true if INT_AP assertion was requested but delayed for a process, or * false if there is no delayed INT_AP assertion request. */ static bool assertion_delayed_; /* * true if the high-speed timer for int_ap pulse (timehs0_timer1) is enabled, or * false otherwise. */ static bool timer_running_; /* function that should be called when INT_AP_L extension is enabled. */ static void (*interface_func_enable_)(void); /* * Change INT_AP_L level, and set a timer for PULSE_LENGTH * @param do_assert true asserts INT_AP_L (LOW) * false deasserts INT_AP_L (HIGH) */ static void set_int_ap_(bool do_assert) { /* Set INT_AP_L level. */ int_ap_asserted_ = do_assert; /* It is asserted when low. */ gpio_set_level(GPIO_INT_AP_L, !int_ap_asserted_); /* Schedule to toggle INT_AP_L. */ GR_TIMEHS_LOAD(0, 1) = pulse_length; GR_TIMEHS_CONTROL(0, 1) = GC_TIMEHS_TIMER1CONTROL_ONESHOT_MASK | GC_TIMEHS_TIMER1CONTROL_SIZE_MASK | GC_TIMEHS_TIMER1CONTROL_INTENABLE_MASK | GC_TIMEHS_TIMER1CONTROL_ENABLE_MASK; timer_running_ = true; } static void disable_timer_(void) { /* Disable Timer. */ GR_TIMEHS_CONTROL(0, 1) = 0; /* Clear interrupt status of TIMEHS0 TIMER1. */ GR_TIMEHS_INTCLR(0, 1) = 1; timer_running_ = false; } /* Interrupt handler of timehs0_timint1, a timer for INT_AP_L extension. */ void timer_int_ap_irq_handler(void) { disable_timer_(); if (!int_ap_asserted_) { /* * While INT_AP_L is being deasserted, if assertion was * requested (and delayed), then toggle the signal. * Otherwise, just return without changing INT_AP_L level. */ if (!assertion_delayed_) return; assertion_delayed_ = false; } /* Toggle the INT_AP_L level. */ set_int_ap_(!int_ap_asserted_); } DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT1, timer_int_ap_irq_handler, 1); int assert_int_ap(void) { #ifdef CR50_DEV if (!in_interrupt_context()) ccprintf("WARN: %s in non-ISR ctx.", __func__); #endif /* If the INT_AP_L extension is not enabled, then just return 0. */ if (!pulse_length) return 0; if (int_ap_asserted_) { #ifdef CR50_DEV ccprintf("WARN: INT_AP_L assertion request is duplicated."); #endif return 1; } /* * If the timer is running, it means INT_AP_L deassertion pulse isn't * long enough yet. If so, let's delay to assert. * Otherwise, assert INT_AP_L immediately. */ if (timer_running_) assertion_delayed_ = true; else set_int_ap_(true); return 1; } void deassert_int_ap(void) { #ifdef CR50_DEV if (!in_interrupt_context()) ccprintf("WARN: %s in non-ISR ctx", __func__); #endif /* If INT_AP_L is deasserted already, do nothing. */ if (!int_ap_asserted_) { assertion_delayed_ = false; return; } timer_int_ap_irq_handler(); } void int_ap_register(void (*func_enable)(void)) { interface_func_enable_ = func_enable; } void int_ap_extension_enable(void) { int_ap_extension_stop_pulse(); pulse_length = USEC_TO_TIMEHS_TICKS(PULSE_LENGTH); task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT1); if (interface_func_enable_) interface_func_enable_(); } void int_ap_extension_stop_pulse(void) { disable_timer_(); /* Initialize INT_AP_L status. */ int_ap_asserted_ = false; gpio_set_level(GPIO_INT_AP_L, 1); assertion_delayed_ = false; }