diff options
author | Todd Broch <tbroch@chromium.org> | 2014-10-13 15:45:50 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-10-30 21:48:06 +0000 |
commit | a3913d99d7a40062144b96d505bba56a62986faa (patch) | |
tree | 35b6531d59c0889a36d77253faa30bfa1ea86b4e | |
parent | d4f3279db9d31159624449828362e45062bd7703 (diff) | |
download | chrome-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.c | 83 | ||||
-rw-r--r-- | board/dingdong/gpio.inc | 2 | ||||
-rw-r--r-- | board/dingdong/usb_pd_policy.c | 11 | ||||
-rw-r--r-- | board/fruitpie/usb_pd_policy.c | 1 | ||||
-rw-r--r-- | board/hoho/board.c | 81 | ||||
-rw-r--r-- | board/hoho/gpio.inc | 2 | ||||
-rw-r--r-- | board/hoho/usb_pd_policy.c | 13 | ||||
-rw-r--r-- | common/usb_pd_policy.c | 9 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 28 | ||||
-rw-r--r-- | include/usb_pd.h | 27 |
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 */ |