summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Broch <tbroch@chromium.org>2014-10-13 15:45:50 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-30 21:48:06 +0000
commita3913d99d7a40062144b96d505bba56a62986faa (patch)
tree35b6531d59c0889a36d77253faa30bfa1ea86b4e
parentd4f3279db9d31159624449828362e45062bd7703 (diff)
downloadchrome-ec-a3913d99d7a40062144b96d505bba56a62986faa.tar.gz
pd: dingdong/hoho: HPD over USB PD.
HPD needs to be transported of USB PD as both SBU lines are consumed for differential AUX signalling. This CL does the following: 1. Enables GPIO DP_HPD as interrupt 2. Sends debounced HPD across CC via the SVDM DP status message BRANCH=none BUG=chrome-os-partner:31192,chrome-os-partner:31193 TEST=manual, From servo w/ GPIO attached to HPD drove the following transactions after inserting with HPD low initially: # e1: hpd_high # e2: hpd_low # non-registered glitch # e3: hpd_high followed by hpd_low # e4: hpd high # non-registered glitch # e5: hpd_irq # e6: hpd_irq # e7: hpd_irq # e8: hpd_low followed by hpd_high From fruitpie console (marked up to show result of above) ----> enter-mode, dp status, dp config [6.774108 DONE] SVDM/1 [4] ff018144 [6.777467 DONE] SVDM/2 [16] ff018150 00000002 [6.780637 DONE] SVDM/1 [17] ff018051 ----> attentions start arriving ----> e1 [18.966741 DONE] SVDM/2 [6] ff018106 0000008a ----> e2 [33.724367 DONE] SVDM/2 [6] ff018106 0000000a ----> e3 [64.550398 DONE] SVDM/2 [6] ff018106 0000008a ----> e3 [64.752452 DONE] SVDM/2 [6] ff018106 0000000a ----> e4 [74.247127 DONE] SVDM/2 [6] ff018106 0000008a ----> e5 [88.906254 DONE] SVDM/2 [6] ff018106 0000010a ----> e6 [100.938738 DONE] SVDM/2 [6] ff018106 0000010a ----> e7 [123.693414 DONE] SVDM/2 [6] ff018106 0000010a ----> e8 [130.050074 DONE] SVDM/2 [6] ff018106 0000000a ----> e8 [130.254087 DONE] SVDM/2 [6] ff018106 0000008a Change-Id: I976c268467ece84cedab7ba4943fb59d1e48c113 Signed-off-by: Todd Broch <tbroch@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/223262 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/dingdong/board.c83
-rw-r--r--board/dingdong/gpio.inc2
-rw-r--r--board/dingdong/usb_pd_policy.c11
-rw-r--r--board/fruitpie/usb_pd_policy.c1
-rw-r--r--board/hoho/board.c81
-rw-r--r--board/hoho/gpio.inc2
-rw-r--r--board/hoho/usb_pd_policy.c13
-rw-r--r--common/usb_pd_policy.c9
-rw-r--r--common/usb_pd_protocol.c28
-rw-r--r--include/usb_pd.h27
10 files changed, 244 insertions, 13 deletions
diff --git a/board/dingdong/board.c b/board/dingdong/board.c
index bf5333cc08..a6e1723a9b 100644
--- a/board/dingdong/board.c
+++ b/board/dingdong/board.c
@@ -8,14 +8,86 @@
#include "adc_chip.h"
#include "common.h"
#include "gpio.h"
+#include "hooks.h"
#include "registers.h"
#include "usb.h"
#include "usb_bb.h"
#include "usb_pd.h"
+#include "task.h"
+#include "timer.h"
#include "util.h"
+static volatile uint64_t hpd_prev_ts;
+static volatile int hpd_prev_level;
+
+void hpd_event(enum gpio_signal signal);
#include "gpio_list.h"
+/**
+ * Hotplug detect deferred task
+ *
+ * Called after level change on hpd GPIO to evaluate (and debounce) what event
+ * has occurred. There are 3 events that occur on HPD:
+ * 1. low : downstream display sink is deattached
+ * 2. high : downstream display sink is attached
+ * 3. irq : downstream display sink signalling an interrupt.
+ *
+ * The debounce times for these various events are:
+ * 100MSEC : min pulse width of level value.
+ * 2MSEC : min pulse width of IRQ low pulse. Max is level debounce min.
+ *
+ * lvl(n-2) lvl(n-1) lvl prev_delta now_delta event
+ * ----------------------------------------------------
+ * 1 0 1 <2ms n/a low glitch (ignore)
+ * 1 0 1 >2ms <100ms irq
+ * x 0 1 n/a >100ms high
+ * 0 1 0 <100ms n/a high glitch (ignore)
+ * x 1 0 n/a >100ms low
+ */
+
+void hpd_irq_deferred(void)
+{
+ pd_send_hpd(0, hpd_irq);
+}
+DECLARE_DEFERRED(hpd_irq_deferred);
+
+void hpd_lvl_deferred(void)
+{
+ int level = gpio_get_level(GPIO_DP_HPD);
+
+ if (level != hpd_prev_level)
+ /* It's a glitch while in deferred or canceled action */
+ return;
+
+ pd_send_hpd(0, (level) ? hpd_high : hpd_low);
+}
+DECLARE_DEFERRED(hpd_lvl_deferred);
+
+void hpd_event(enum gpio_signal signal)
+{
+ timestamp_t now = get_time();
+ int level = gpio_get_level(signal);
+ uint64_t cur_delta = now.val - hpd_prev_ts;
+
+ /* store current time */
+ hpd_prev_ts = now.val;
+
+ /* All previous hpd level events need to be re-triggered */
+ hook_call_deferred(hpd_lvl_deferred, -1);
+
+ /* It's a glitch. Previous time moves but level is the same. */
+ if (cur_delta < HPD_DEBOUNCE_IRQ)
+ return;
+
+ if ((!hpd_prev_level && level) && (cur_delta < HPD_DEBOUNCE_LVL))
+ /* It's an irq */
+ hook_call_deferred(hpd_irq_deferred, 0);
+ else if (cur_delta >= HPD_DEBOUNCE_LVL)
+ hook_call_deferred(hpd_lvl_deferred, HPD_DEBOUNCE_LVL);
+
+ hpd_prev_level = level;
+}
+
/* Initialize board. */
void board_config_pre_init(void)
{
@@ -25,6 +97,17 @@ void board_config_pre_init(void)
STM32_SYSCFG_CFGR1 |= (1 << 9) | (1 << 10);/* Remap USART1 RX/TX DMA */
}
+/* Initialize board. */
+static void board_init(void)
+{
+ timestamp_t now = get_time();
+ hpd_prev_level = gpio_get_level(GPIO_DP_HPD);
+ hpd_prev_ts = now.val;
+ gpio_enable_interrupt(GPIO_DP_HPD);
+}
+
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
/* ADC channels */
const struct adc_t adc_channels[] = {
/* USB PD CC lines sensing. Converted to mV (3300mV/4096). */
diff --git a/board/dingdong/gpio.inc b/board/dingdong/gpio.inc
index 67f9d66365..72a96f9e1f 100644
--- a/board/dingdong/gpio.inc
+++ b/board/dingdong/gpio.inc
@@ -4,7 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-GPIO(DP_HPD, A, 0, GPIO_INPUT, NULL)
+GPIO(DP_HPD, A, 0, GPIO_INT_BOTH, hpd_event)
GPIO(USB_C_CC1_PD, A, 1, GPIO_ANALOG, NULL)
GPIO(PD_DAC_REF, A, 4, GPIO_ANALOG, NULL)
GPIO(DP_AUX_N, A, 5, GPIO_INPUT, NULL)
diff --git a/board/dingdong/usb_pd_policy.c b/board/dingdong/usb_pd_policy.c
index 97504a1cbf..10b5ab48e1 100644
--- a/board/dingdong/usb_pd_policy.c
+++ b/board/dingdong/usb_pd_policy.c
@@ -32,6 +32,9 @@ const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
/* Desired voltage requested as a sink (in millivolts) */
static unsigned select_mv = 5000;
+/* Whether alternate mode has been entered or not */
+static int alt_mode;
+
int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
uint32_t *curr_limit, uint32_t *supply_voltage)
{
@@ -183,12 +186,20 @@ static int svdm_enter_mode(int port, uint32_t *payload)
if ((PD_VDO_VID(payload[0]) != USB_SID_DISPLAYPORT) ||
(PD_VDO_OPOS(payload[0]) != OPOS))
return 0; /* will generate NAK */
+
+ alt_mode = OPOS;
return 1;
}
+int pd_alt_mode(int port)
+{
+ return alt_mode;
+}
+
static int svdm_exit_mode(int port, uint32_t *payload)
{
gpio_set_level(GPIO_PD_SBU_ENABLE, 0);
+ alt_mode = 0;
return 1; /* Must return ACK */
}
diff --git a/board/fruitpie/usb_pd_policy.c b/board/fruitpie/usb_pd_policy.c
index e99e195804..06a8a44ba7 100644
--- a/board/fruitpie/usb_pd_policy.c
+++ b/board/fruitpie/usb_pd_policy.c
@@ -232,7 +232,6 @@ static int svdm_dp_config(int port, uint32_t *payload)
static int svdm_dp_attention(int port, uint32_t *payload)
{
- CPRINTF("dp sts:%08x\n", payload[1]);
return 1; /* ack */
}
diff --git a/board/hoho/board.c b/board/hoho/board.c
index c521d2c6f8..f7ce18da4b 100644
--- a/board/hoho/board.c
+++ b/board/hoho/board.c
@@ -7,7 +7,6 @@
#include "adc.h"
#include "adc_chip.h"
#include "common.h"
-#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
@@ -16,10 +15,80 @@
#include "usb.h"
#include "usb_bb.h"
#include "usb_pd.h"
+#include "timer.h"
#include "util.h"
+static volatile uint64_t hpd_prev_ts;
+static volatile int hpd_prev_level;
+
+void hpd_event(enum gpio_signal signal);
#include "gpio_list.h"
+/**
+ * Hotplug detect deferred task
+ *
+ * Called after level change on hpd GPIO to evaluate (and debounce) what event
+ * has occurred. There are 3 events that occur on HPD:
+ * 1. low : downstream display sink is deattached
+ * 2. high : downstream display sink is attached
+ * 3. irq : downstream display sink signalling an interrupt.
+ *
+ * The debounce times for these various events are:
+ * 100MSEC : min pulse width of level value.
+ * 2MSEC : min pulse width of IRQ low pulse. Max is level debounce min.
+ *
+ * lvl(n-2) lvl(n-1) lvl prev_delta now_delta event
+ * ----------------------------------------------------
+ * 1 0 1 <2ms n/a low glitch (ignore)
+ * 1 0 1 >2ms <100ms irq
+ * x 0 1 n/a >100ms high
+ * 0 1 0 <100ms n/a high glitch (ignore)
+ * x 1 0 n/a >100ms low
+ */
+
+void hpd_irq_deferred(void)
+{
+ pd_send_hpd(0, hpd_irq);
+}
+DECLARE_DEFERRED(hpd_irq_deferred);
+
+void hpd_lvl_deferred(void)
+{
+ int level = gpio_get_level(GPIO_DP_HPD);
+
+ if (level != hpd_prev_level)
+ /* It's a glitch while in deferred or canceled action */
+ return;
+
+ pd_send_hpd(0, (level) ? hpd_high : hpd_low);
+}
+DECLARE_DEFERRED(hpd_lvl_deferred);
+
+void hpd_event(enum gpio_signal signal)
+{
+ timestamp_t now = get_time();
+ int level = gpio_get_level(signal);
+ uint64_t cur_delta = now.val - hpd_prev_ts;
+
+ /* store current time */
+ hpd_prev_ts = now.val;
+
+ /* All previous hpd level events need to be re-triggered */
+ hook_call_deferred(hpd_lvl_deferred, -1);
+
+ /* It's a glitch. Previous time moves but level is the same. */
+ if (cur_delta < HPD_DEBOUNCE_IRQ)
+ return;
+
+ if ((!hpd_prev_level && level) && (cur_delta < HPD_DEBOUNCE_LVL))
+ /* It's an irq */
+ hook_call_deferred(hpd_irq_deferred, 0);
+ else if (cur_delta >= HPD_DEBOUNCE_LVL)
+ hook_call_deferred(hpd_lvl_deferred, HPD_DEBOUNCE_LVL);
+
+ hpd_prev_level = level;
+}
+
/* Initialize board. */
void board_config_pre_init(void)
{
@@ -60,14 +129,22 @@ static void board_init_spi2(void)
/* Enable clocks to SPI2 module */
STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
}
+#endif /* CONFIG_SPI_FLASH */
/* Initialize board. */
static void board_init(void)
{
+ timestamp_t now;
+#ifdef CONFIG_SPI_FLASH
board_init_spi2();
+#endif
+ now = get_time();
+ hpd_prev_level = gpio_get_level(GPIO_DP_HPD);
+ hpd_prev_ts = now.val;
+ gpio_enable_interrupt(GPIO_DP_HPD);
}
+
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
-#endif /* CONFIG_SPI_FLASH */
/* ADC channels */
const struct adc_t adc_channels[] = {
diff --git a/board/hoho/gpio.inc b/board/hoho/gpio.inc
index 204a1ac332..0aad148307 100644
--- a/board/hoho/gpio.inc
+++ b/board/hoho/gpio.inc
@@ -4,7 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-GPIO(DP_HPD, A, 0, GPIO_INPUT, NULL)
+GPIO(DP_HPD, A, 0, GPIO_INT_BOTH, hpd_event)
GPIO(USB_C_CC1_PD, A, 1, GPIO_ANALOG, NULL)
GPIO(MCDP_RESET_L, A, 3, GPIO_OUT_HIGH, NULL)
GPIO(PD_DAC_REF, A, 4, GPIO_ANALOG, NULL)
diff --git a/board/hoho/usb_pd_policy.c b/board/hoho/usb_pd_policy.c
index b747766aa9..f52416b19d 100644
--- a/board/hoho/usb_pd_policy.c
+++ b/board/hoho/usb_pd_policy.c
@@ -12,8 +12,8 @@
#include "registers.h"
#include "task.h"
#include "timer.h"
-#include "util.h"
#include "usb_pd.h"
+#include "util.h"
#include "version.h"
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
@@ -32,6 +32,9 @@ const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
/* Desired voltage requested as a sink (in millivolts) */
static unsigned select_mv = 5000;
+/* Whether alternate mode has been entered or not */
+static int alt_mode;
+
int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
uint32_t *curr_limit, uint32_t *supply_voltage)
{
@@ -182,12 +185,20 @@ static int svdm_enter_mode(int port, uint32_t *payload)
if ((PD_VDO_VID(payload[0]) != USB_SID_DISPLAYPORT) ||
(PD_VDO_OPOS(payload[0]) != OPOS))
return 0; /* will generate a NAK */
+
+ alt_mode = OPOS;
return 1;
}
+int pd_alt_mode(int port)
+{
+ return alt_mode;
+}
+
static int svdm_exit_mode(int port, uint32_t *payload)
{
gpio_set_level(GPIO_PD_SBU_ENABLE, 0);
+ alt_mode = 0;
return 1; /* Must return ACK */
}
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c
index 3149934d33..f0ae2cffab 100644
--- a/common/usb_pd_policy.c
+++ b/common/usb_pd_policy.c
@@ -267,7 +267,6 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
break;
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
case CMD_ATTENTION:
- /* This is DFP response */
func = &dfp_consume_attention;
break;
#endif
@@ -288,9 +287,9 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
payload[0] |= VDO_CMDT(CMDT_RSP_BUSY);
rsize = 1;
}
-#ifdef CONFIG_USB_PD_ALT_MODE_DFP
} else if (cmd_type == CMDT_RSP_ACK) {
switch (cmd) {
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
case CMD_DISCOVER_IDENT:
dfp_consume_identity(port, payload);
rsize = dfp_discover_svids(port, payload);
@@ -333,6 +332,7 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
/* no response after DFPs ack */
rsize = 0;
break;
+#endif
case CMD_ATTENTION:
/* no response after DFPs ack */
rsize = 0;
@@ -343,6 +343,7 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
}
payload[0] |= VDO_CMDT(CMDT_INIT);
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
} else if (cmd_type == CMDT_RSP_BUSY) {
switch (cmd) {
case CMD_DISCOVER_IDENT:
@@ -381,6 +382,10 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
return 0;
}
+int pd_alt_mode(int port)
+{
+ return 0;
+}
#endif /* CONFIG_USB_PD_ALT_MODE */
#ifndef CONFIG_USB_PD_CUSTOM_VDM
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 34ef385621..c91893c4c5 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -1053,7 +1053,9 @@ void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
return;
}
- pd[port].vdo_data[0] = VDO(vid, (vid == USB_SID_PD) ? 1 : 0, cmd);
+ pd[port].vdo_data[0] = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ?
+ 1 : 0, cmd);
+
pd[port].vdo_count = count + 1;
for (i = 1; i < count + 1; i++)
pd[port].vdo_data[i] = data[i-1];
@@ -1744,6 +1746,30 @@ static int remote_flashing(int argc, char **argv)
return EC_SUCCESS;
}
+#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP)
+void pd_send_hpd(int port, enum hpd_event hpd)
+{
+ uint32_t data[1];
+ int opos = pd_alt_mode(port);
+ if (!opos)
+ return;
+
+ data[0] = VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */
+ (hpd == hpd_high), /* HPD_HI|LOW */
+ 0, /* request exit DP */
+ 0, /* request exit USB */
+ 0, /* MF pref */
+ gpio_get_level(GPIO_PD_SBU_ENABLE),
+ 0, /* power low */
+ 0x2);
+ pd_send_vdm(port, USB_SID_DISPLAYPORT,
+ VDO_OPOS(opos) | CMD_ATTENTION, data, 1);
+ /* Wait until VDM is done. */
+ while (pd[0].vdm_state > 0)
+ task_wait_event(USB_PD_RX_TMOUT_US * (PD_RETRY_COUNT + 1));
+}
+#endif
+
void pd_request_source_voltage(int port, int mv)
{
pd_set_max_voltage(mv);
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 986f5ef47d..8f231a754f 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -150,10 +150,11 @@ struct svdm_amode_data {
uint32_t mode_caps;
};
-enum hpd_level {
- hpd_unknown = -1,
- hpd_low = 0,
+enum hpd_event {
+ hpd_none,
+ hpd_low,
hpd_high,
+ hpd_irq,
};
/* Policy structure for driving alternate mode */
@@ -414,6 +415,9 @@ struct pd_policy {
#define PD_VDO_HPD_IRQ(x) ((x >> 8) & 1)
#define PD_VDO_HPD_LVL(x) ((x >> 7) & 1)
+
+#define HPD_DEBOUNCE_LVL (100*MSEC)
+#define HPD_DEBOUNCE_IRQ (2*MSEC)
/*
* DisplayPort Configure VDO
* -------------------------
@@ -716,7 +720,7 @@ void pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash);
* @param vid Vendor ID
* @param cmd VDO command number
* @param data Pointer to payload to send
- * @param data number of data objects in payload
+ * @param count number of data objects in payload
*/
void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
int count);
@@ -775,6 +779,21 @@ int board_get_usb_mux(int port, const char **dp_str, const char **usb_str);
*/
void board_flip_usb_mux(int port);
+/**
+ * Determine if in alternate mode or not.
+ *
+ * @param port port number.
+ * @return object position of mode chosen in alternate mode otherwise zero.
+ */
+int pd_alt_mode(int port);
+
+/**
+ * Send hpd over USB PD.
+ *
+ * @param port port number.
+ * @param hpd hotplug detect type.
+ */
+void pd_send_hpd(int port, enum hpd_event hpd);
/* --- Physical layer functions : chip specific --- */
/* Packet preparation/retrieval */