| /* Copyright 2017 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. |
| */ |
| /* Coffeecake dock configuration */ |
| |
| #include "adc.h" |
| #include "adc_chip.h" |
| #include "charger/sy21612.h" |
| #include "clock.h" |
| #include "common.h" |
| #include "ec_commands.h" |
| #include "ec_version.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "i2c.h" |
| #include "mcdp28x0.h" |
| #include "registers.h" |
| #include "task.h" |
| #include "usb_bb.h" |
| #include "usb_descriptor.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); |
| void vbus_event(enum gpio_signal signal); |
| #include "gpio_list.h" |
| |
| /* I2C ports */ |
| const struct i2c_port_t i2c_ports[] = { |
| {"charger", I2C_PORT_SY21612, 400, GPIO_I2C0_SCL, GPIO_I2C0_SDA}, |
| }; |
| const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); |
| |
| /** |
| * 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: |
| * HPD_USTREAM_DEBOUNCE_LVL : min pulse width of level value. |
| * HPD_USTREAM_DEBOUNCE_IRQ : min pulse width of IRQ low pulse. |
| * |
| * lvl(n-2) lvl(n-1) lvl prev_delta now_delta event |
| * ---------------------------------------------------- |
| * 1 0 1 <IRQ n/a low glitch (ignore) |
| * 1 0 1 >IRQ <LVL irq |
| * x 0 1 n/a >LVL high |
| * 0 1 0 <LVL n/a high glitch (ignore) |
| * x 1 0 n/a >LVL 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_data, -1); |
| |
| /* It's a glitch. Previous time moves but level is the same. */ |
| if (cur_delta < HPD_USTREAM_DEBOUNCE_IRQ) |
| return; |
| |
| if ((!hpd_prev_level && level) && |
| (cur_delta < HPD_USTREAM_DEBOUNCE_LVL)) |
| /* It's an irq */ |
| hook_call_deferred(&hpd_irq_deferred_data, 0); |
| else if (cur_delta >= HPD_USTREAM_DEBOUNCE_LVL) |
| hook_call_deferred(&hpd_lvl_deferred_data, |
| HPD_USTREAM_DEBOUNCE_LVL); |
| |
| hpd_prev_level = level; |
| } |
| |
| /* Proto 0 workaround */ |
| void vbus_event(enum gpio_signal signal) |
| { |
| /* Discharge VBUS on DET_L high */ |
| gpio_set_level(GPIO_PD_DISCHARGE, gpio_get_level(signal)); |
| } |
| |
| /* USB C VBUS output selection */ |
| void board_set_usb_output_voltage(int mv) |
| { |
| const int ra = 40200; |
| const int rb = 10000; |
| const int rc = 6650; |
| int dac_mv; |
| uint32_t dac_val; |
| |
| if (mv >= 0) { |
| /* vbat = 1.0 * ra/rb + 1.0 - (vdac - 1.0) * ra/rc */ |
| dac_mv = 1000 + (1000 * rc / rb) + ((1000 - mv) * rc / ra); |
| if (dac_mv < 0) |
| dac_mv = 0; |
| |
| /* Set voltage Vout=Vdac with Vref = 3.3v */ |
| /* TODO: use Vdda instead */ |
| dac_val = dac_mv * 4096 / 3300; |
| /* Start DAC channel 2 */ |
| STM32_DAC_DHR12RD = dac_val << 16; |
| STM32_DAC_CR = STM32_DAC_CR_EN2; |
| } else { |
| STM32_DAC_CR = 0; |
| } |
| } |
| |
| /* Initialize board. */ |
| void board_config_pre_init(void) |
| { |
| /* Enable SYSCFG clock */ |
| STM32_RCC_APB2ENR |= 1 << 0; |
| /* Enable DAC interface clock. */ |
| STM32_RCC_APB1ENR |= (1 << 29); |
| /* Delay 1 APB clock cycle after the clock is enabled */ |
| clock_wait_bus_cycles(BUS_APB, 1); |
| /* Set 5Vsafe Vdac */ |
| board_set_usb_output_voltage(5000); |
| /* Remap USART DMA to match the USART driver */ |
| STM32_SYSCFG_CFGR1 |= (1 << 9) | (1 << 10);/* Remap USART1 RX/TX DMA */ |
| } |
| |
| #ifdef CONFIG_SPI_FLASH |
| |
| static void board_init_spi2(void) |
| { |
| /* Remap SPI2 to DMA channels 6 and 7 */ |
| STM32_SYSCFG_CFGR1 |= (1 << 24); |
| |
| /* Set pin NSS to general purpose output mode (01b). */ |
| /* Set pins SCK, MISO, and MOSI to alternate function (10b). */ |
| STM32_GPIO_MODER(GPIO_B) &= ~0xff000000; |
| STM32_GPIO_MODER(GPIO_B) |= 0xa9000000; |
| |
| /* Set all four pins to alternate function 0 */ |
| STM32_GPIO_AFRH(GPIO_B) &= ~(0xffff0000); |
| |
| /* Set all four pins to output push-pull */ |
| STM32_GPIO_OTYPER(GPIO_B) &= ~(0xf000); |
| |
| /* Set pullup on NSS */ |
| STM32_GPIO_PUPDR(GPIO_B) |= 0x1000000; |
| |
| /* Set all four pins to high speed */ |
| STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000; |
| |
| /* Reset SPI2 */ |
| STM32_RCC_APB1RSTR |= (1 << 14); |
| STM32_RCC_APB1RSTR &= ~(1 << 14); |
| |
| /* Enable clocks to SPI2 module */ |
| STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2; |
| } |
| #endif /* CONFIG_SPI_FLASH */ |
| |
| static void factory_validation_deferred(void) |
| { |
| struct mcdp_info info; |
| |
| mcdp_enable(); |
| |
| /* test mcdp via serial to validate function */ |
| if (!mcdp_get_info(&info) && (MCDP_FAMILY(info.family) == 0x0010) && |
| (MCDP_CHIPID(info.chipid) == 0x2850)) { |
| pd_log_event(PD_EVENT_VIDEO_CODEC, |
| PD_LOG_PORT_SIZE(0, sizeof(info)), |
| 0, &info); |
| } |
| |
| mcdp_disable(); |
| } |
| DECLARE_DEFERRED(factory_validation_deferred); |
| |
| static void board_post_init(void) |
| { |
| sy21612_enable_regulator(1); |
| /* |
| * AC powered - DRP SOURCE |
| * DUT powered - DRP SINK |
| */ |
| pd_set_dual_role(gpio_get_level(GPIO_AC_PRESENT_L) ? |
| PD_DRP_FORCE_SINK : PD_DRP_FORCE_SOURCE); |
| } |
| DECLARE_DEFERRED(board_post_init); |
| |
| /* 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); |
| gpio_enable_interrupt(GPIO_CHARGER_INT); |
| gpio_enable_interrupt(GPIO_USB_C_VBUS_DET_L); |
| /* Set PD_DISCHARGE initial state */ |
| gpio_set_level(GPIO_PD_DISCHARGE, gpio_get_level(GPIO_USB_C_VBUS_DET_L)); |
| |
| /* Delay needed to allow HDMI MCU to boot. */ |
| hook_call_deferred(&factory_validation_deferred_data, 200*MSEC); |
| /* Initialize buck-boost converter */ |
| hook_call_deferred(&board_post_init_data, 0); |
| } |
| |
| 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). */ |
| [ADC_CH_CC1_PD] = {"USB_C_CC1_PD", 3300, 4096, 0, STM32_AIN(1)}, |
| [ADC_VBUS_MON] = {"VBUS_MON", 13200, 4096, 0, STM32_AIN(2)}, |
| [ADC_DAC_REF_TP28] = {"DAC_REF_TP28", 3300, 4096, 0, STM32_AIN(4)}, |
| [ADC_DAC_VOLT] = {"DAC_VOLT", 3300, 4096, 0, STM32_AIN(5)}, |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); |
| |
| const void * const usb_strings[] = { |
| [USB_STR_DESC] = usb_string_desc, |
| [USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."), |
| [USB_STR_PRODUCT] = USB_STRING_DESC("Hoho"), |
| [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32), |
| [USB_STR_BB_URL] = USB_STRING_DESC(USB_GOOGLE_TYPEC_URL), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); |
| |
| /** |
| * USB configuration |
| * Any type-C device with alternate mode capabilities must have the following |
| * set of descriptors. |
| * |
| * 1. Standard Device |
| * 2. BOS |
| * 2a. Container ID |
| * 2b. Billboard Caps |
| */ |
| struct my_bos { |
| struct usb_bos_hdr_descriptor bos; |
| struct usb_contid_caps_descriptor contid_caps; |
| struct usb_bb_caps_base_descriptor bb_caps; |
| struct usb_bb_caps_svid_descriptor bb_caps_svids[1]; |
| }; |
| |
| static struct my_bos bos_desc = { |
| .bos = { |
| .bLength = USB_DT_BOS_SIZE, |
| .bDescriptorType = USB_DT_BOS, |
| .wTotalLength = (USB_DT_BOS_SIZE + USB_DT_CONTID_SIZE + |
| USB_BB_CAPS_BASE_SIZE + |
| USB_BB_CAPS_SVID_SIZE * 1), |
| .bNumDeviceCaps = 2, /* contid + bb_caps */ |
| }, |
| .contid_caps = { |
| .bLength = USB_DT_CONTID_SIZE, |
| .bDescriptorType = USB_DT_DEVICE_CAPABILITY, |
| .bDevCapabilityType = USB_DC_DTYPE_CONTID, |
| .bReserved = 0, |
| .ContainerID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| }, |
| .bb_caps = { |
| .bLength = (USB_BB_CAPS_BASE_SIZE + USB_BB_CAPS_SVID_SIZE * 1), |
| .bDescriptorType = USB_DT_DEVICE_CAPABILITY, |
| .bDevCapabilityType = USB_DC_DTYPE_BILLBOARD, |
| .iAdditionalInfoURL = USB_STR_BB_URL, |
| .bNumberOfAlternateModes = 1, |
| .bPreferredAlternateMode = 1, |
| .VconnPower = 0, |
| .bmConfigured = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| .bReserved = 0, |
| }, |
| .bb_caps_svids = { |
| { |
| .wSVID = 0xff01, /* TODO(tbroch) def'd in other CL remove hardcode */ |
| .bAlternateMode = 1, |
| .iAlternateModeString = USB_STR_BB_URL, /* TODO(crosbug.com/p/32687) */ |
| }, |
| }, |
| }; |
| |
| const struct bos_context bos_ctx = { |
| .descp = (void *)&bos_desc, |
| .size = sizeof(struct my_bos), |
| }; |