mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 17:11:42 +00:00
On some boards, it was seen that SuzyQable wasn't detected by the rdd block. The voltage around 0.4V is marginal with a Vbus around 4.75V. This commit simply adjust the 0.4V comparator reference voltage to 0.3V in order to make the detection work. BUG=b:64847312 BRANCH=cr50 TEST=Find a soraka where SuzyQable didn't work. Verify with this patch, it does work. Additionally, verify that servo_v4 continues works. Change-Id: If54630ec469408031cd84ffb93ef5fea42bdee3b Signed-off-by: Aseda Aboagye <aaboagye@google.com> Reviewed-on: https://chromium-review.googlesource.com/633403 Commit-Ready: Aseda Aboagye <aaboagye@chromium.org> Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Mary Ruthven <mruthven@chromium.org>
242 lines
7.2 KiB
C
242 lines
7.2 KiB
C
/* Copyright 2016 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.
|
|
*/
|
|
|
|
#include "clock.h"
|
|
#include "console.h"
|
|
#include "gpio.h"
|
|
#include "hooks.h"
|
|
#include "rdd.h"
|
|
#include "registers.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "usb_api.h"
|
|
|
|
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
|
|
|
|
/*
|
|
* The default PROG_DEBUG_STATE_MAP value. Used to tell the to controller send
|
|
* an interrupt when CC1/2 are detected to be in the defined voltage range of a
|
|
* debug accessory.
|
|
*/
|
|
#define DETECT_DEBUG 0x420
|
|
|
|
/*
|
|
* The interrupt only triggers when the debug state is detected. If we want to
|
|
* trigger an interrupt when the debug state is *not* detected, we need to
|
|
* program the bit-inverse.
|
|
*/
|
|
#define DETECT_DISCONNECT (~DETECT_DEBUG & 0xffff)
|
|
|
|
/* State of RDD CC detection */
|
|
static enum device_state state = DEVICE_STATE_DISCONNECTED;
|
|
|
|
/* Force detecting a debug accessory (ignore RDD CC detect hardware) */
|
|
static int force_detected;
|
|
|
|
/**
|
|
* Get instantaneous cable detect state
|
|
*
|
|
* @return 1 if debug accessory is detected, 0 if not detected
|
|
*/
|
|
static int rdd_is_detected(void)
|
|
{
|
|
uint8_t cc1 = GREAD_FIELD(RDD, INPUT_PIN_VALUES, CC1);
|
|
uint8_t cc2 = GREAD_FIELD(RDD, INPUT_PIN_VALUES, CC2);
|
|
|
|
return (cc1 == cc2 && (cc1 == 3 || cc1 == 1));
|
|
}
|
|
|
|
/**
|
|
* Handle debug accessory disconnecting
|
|
*/
|
|
static void rdd_disconnect(void)
|
|
{
|
|
CPRINTS("Debug accessory disconnect");
|
|
state = DEVICE_STATE_DISCONNECTED;
|
|
|
|
/*
|
|
* Stop pulling CCD_MODE_L low. The internal pullup configured in the
|
|
* pinmux will pull the signal back high, unless the EC is also pulling
|
|
* it low.
|
|
*
|
|
* This disables the SBUx muxes, if we were the only one driving
|
|
* CCD_MODE_L.
|
|
*/
|
|
gpio_set_flags(GPIO_CCD_MODE_L, GPIO_INPUT);
|
|
}
|
|
|
|
/**
|
|
* Handle debug accessory connecting
|
|
*
|
|
* This can be deferred from both rdd_detect() and the interrupt handler, so
|
|
* it needs to check the current state to determine whether we're already
|
|
* connected.
|
|
*/
|
|
static void rdd_connect(void)
|
|
{
|
|
/* If we were debouncing, we're done, and still connected */
|
|
if (state == DEVICE_STATE_DEBOUNCING)
|
|
state = DEVICE_STATE_CONNECTED;
|
|
|
|
/* If we're already connected, done */
|
|
if (state == DEVICE_STATE_CONNECTED)
|
|
return;
|
|
|
|
/* We were previously disconnected, so connect */
|
|
CPRINTS("Debug accessory connect");
|
|
state = DEVICE_STATE_CONNECTED;
|
|
|
|
/* Start pulling CCD_MODE_L low to enable the SBUx muxes */
|
|
gpio_set_flags(GPIO_CCD_MODE_L, GPIO_OUT_LOW);
|
|
}
|
|
DECLARE_DEFERRED(rdd_connect);
|
|
|
|
/**
|
|
* Debug accessory detect interrupt
|
|
*/
|
|
static void rdd_interrupt(void)
|
|
{
|
|
/*
|
|
* The Rdd detector is level-sensitive with debounce. That is, it
|
|
* samples the RDCCx pin states. If they're different, it resets the
|
|
* wait counter. If they're the same, it decrements the wait counter.
|
|
* Then if the counter is zero, and the state we're looking for matches
|
|
* the map, it fires the interrupt.
|
|
*
|
|
* Note that the counter *remains* zero until the pin states change.
|
|
*
|
|
* If we want to be able to wake on Rdd change, then interrupts need to
|
|
* remain enabled. Each time we get an interrupt, we'll toggle the map
|
|
* we're looking for to the opposite state. That stops the interrupt
|
|
* from continuing to fire on the current state. When the pins settle
|
|
* into a new state, we'll fire the interrupt again.
|
|
*
|
|
* Even with that, we can still get a double interrupt now and then,
|
|
* because the Rdd module runs on a different clock than we do. So the
|
|
* write we do to change the state map may not be picked up until the
|
|
* next clock, when the Rdd module has already generated its next
|
|
* interrupt based on the old map. This is harmless, because we're
|
|
* unlikely to actually trigger the deferred function twice, and it
|
|
* doesn't care if we do anyway because on the second call it'll
|
|
* already be in the connected state.
|
|
*
|
|
*/
|
|
if (rdd_is_detected()) {
|
|
/* Accessory detected; toggle to looking for disconnect */
|
|
GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DISCONNECT);
|
|
|
|
/*
|
|
* Trigger the deferred handler so that we move back into the
|
|
* connected state before our debounce interval expires.
|
|
*/
|
|
hook_call_deferred(&rdd_connect_data, 0);
|
|
} else {
|
|
/*
|
|
* Not detected; toggle to looking for connect. We'll start
|
|
* debouncing disconnect the next time HOOK_SECOND triggers
|
|
* rdd_detect() below.
|
|
*/
|
|
GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DEBUG);
|
|
}
|
|
|
|
/* Make sure we stay awake long enough to advance the state machine */
|
|
delay_sleep_by(1 * SECOND);
|
|
|
|
/* Clear the interrupt */
|
|
GWRITE_FIELD(RDD, INT_STATE, INTR_DEBUG_STATE_DETECTED, 1);
|
|
}
|
|
DECLARE_IRQ(GC_IRQNUM_RDD0_INTR_DEBUG_STATE_DETECTED_INT, rdd_interrupt, 1);
|
|
|
|
/**
|
|
* RDD CC detect state machine
|
|
*/
|
|
static void rdd_detect(void)
|
|
{
|
|
/* Handle detecting device */
|
|
if (force_detected || rdd_is_detected()) {
|
|
rdd_connect();
|
|
return;
|
|
}
|
|
|
|
/* CC wasn't detected. If we're already disconnected, done. */
|
|
if (state == DEVICE_STATE_DISCONNECTED)
|
|
return;
|
|
|
|
/* If we were debouncing, we're now sure we're disconnected */
|
|
if (state == DEVICE_STATE_DEBOUNCING) {
|
|
rdd_disconnect();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, we were connected but the accessory seems to be
|
|
* disconnected right now. PD negotiation (e.g. during EC reset or
|
|
* sysjump) can alter the RDCCx voltages, so we need to debounce this
|
|
* signal for longer than the Rdd hardware does to make sure it's
|
|
* really disconnected before we deassert CCD_MODE_L.
|
|
*/
|
|
state = DEVICE_STATE_DEBOUNCING;
|
|
}
|
|
/*
|
|
* Bump up priority so this runs before the CCD_MODE_L state machine, because
|
|
* we can change CCD_MODE_L.
|
|
*/
|
|
DECLARE_HOOK(HOOK_SECOND, rdd_detect, HOOK_PRIO_DEFAULT - 1);
|
|
|
|
void init_rdd_state(void)
|
|
{
|
|
/* Enable RDD hardware */
|
|
clock_enable_module(MODULE_RDD, 1);
|
|
GWRITE(RDD, POWER_DOWN_B, 1);
|
|
|
|
/*
|
|
* Note that there is currently (ha, see what I did there) a leakage
|
|
* path out of Cr50 into the CC lines. On some systems, this can cause
|
|
* false Rdd detection when the TCPCs are turned off. This may require
|
|
* a software workaround where RDD hardware must be powered down
|
|
* whenever the TCPCs are off, and can only be powered up for brief
|
|
* periods to do a quick check. See b/38019839 and b/64582597.
|
|
*/
|
|
|
|
/* Configure to detect accessory connected */
|
|
GWRITE(RDD, PROG_DEBUG_STATE_MAP, DETECT_DEBUG);
|
|
|
|
/*
|
|
* Set the 0.4V comparator reference to 0.3V instead. The voltage is
|
|
* marginal near 0.4V for example with VBUS at 4.75V and a SuzyQable See
|
|
* b/64847312.
|
|
*/
|
|
GWRITE_FIELD(RDD, REF_ADJ, LVL0P4V, 0x2);
|
|
|
|
/*
|
|
* Enable interrupt for detecting CC. This minimizes the time before
|
|
* we transition to cable-detected at boot, and will cause us to wake
|
|
* from deep sleep if a cable is plugged in.
|
|
*/
|
|
task_enable_irq(GC_IRQNUM_RDD0_INTR_DEBUG_STATE_DETECTED_INT);
|
|
GWRITE_FIELD(RDD, INT_STATE, INTR_DEBUG_STATE_DETECTED, 1);
|
|
GWRITE_FIELD(RDD, INT_ENABLE, INTR_DEBUG_STATE_DETECTED, 1);
|
|
}
|
|
|
|
void force_rdd_detect(int enable)
|
|
{
|
|
force_detected = enable;
|
|
|
|
/*
|
|
* If we're forcing detection, trigger then connect handler early.
|
|
*
|
|
* Otherwise, we'll revert to the normal logic of checking the RDD
|
|
* hardware CC state.
|
|
*/
|
|
if (force_detected)
|
|
hook_call_deferred(&rdd_connect_data, 0);
|
|
}
|
|
|
|
int rdd_detect_is_forced(void)
|
|
{
|
|
return force_detected;
|
|
}
|