static void usart_tx_interrupt_handler(struct usart_config const *config) { intptr_t base = config->hw->base; uint8_t byte; if (!(STM32_USART_SR(base) & STM32_USART_SR_TXE)) return; if (queue_remove_unit(config->consumer.queue, &byte)) { STM32_USART_TDR(base) = byte; /* * Make sure the TXE interrupt is enabled and that we won't go * into deep sleep. This invocation of the USART interrupt * handler may have been manually triggered to start * transmission. */ disable_sleep(SLEEP_MASK_UART); STM32_USART_CR1(base) |= STM32_USART_CR1_TXEIE; } else { /* * The TX queue is empty, disable the TXE interrupt and enable * deep sleep mode. The TXE interrupt will remain disabled * until a write call happens. */ enable_sleep(SLEEP_MASK_UART); STM32_USART_CR1(base) &= ~STM32_USART_CR1_TXEIE; } }
enum power_state power_chipset_init(void) { /* * If we're switching between images without rebooting, see if the x86 * is already powered on; if so, leave it there instead of cycling * through G3. */ if (system_jumped_to_this_image()) { if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { /* Disable idle task deep sleep when in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); CPRINTS("already in S0"); return POWER_S0; } else { /* Force all signals to their G3 states */ CPRINTS("forcing G3"); gpio_set_level(GPIO_PCH_PWROK, 0); gpio_set_level(GPIO_SYS_PWROK, 0); gpio_set_level(GPIO_PP1050_EN, 0); gpio_set_level(GPIO_PP1200_EN, 0); gpio_set_level(GPIO_PP1800_EN, 0); gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0); gpio_set_level(GPIO_PP5000_USB_EN, 0); gpio_set_level(GPIO_PP5000_EN, 0); gpio_set_level(GPIO_PCH_DPWROK, 0); gpio_set_level(GPIO_PP3300_DSW_EN, 0); wireless_set_state(WIRELESS_OFF); } } return POWER_G3; }
/** * Modify and print the sleep mask which controls access to deep sleep * mode in the idle task. */ static int command_sleepmask(int argc, char **argv) { int v; if (argc >= 2) { if (parse_bool(argv[1], &v)) { if (v) disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP); else enable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP); } else { char *e; v = strtoi(argv[1], &e, 10); if (*e) return EC_ERROR_PARAM1; /* Set sleep mask directly. */ sleep_mask = v; } } ccprintf("sleep mask: %08x\n", sleep_mask); return EC_SUCCESS; }
/* Initialize board. */ static void board_init(void) { /* * Default no low power idle for EVB, * use console command "sleepmask" to enable it if necessary. */ disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP); }
static void discharge_voltage(int target_volt) { discharge_enable(); discharge_deadline.val = get_time().val + DISCHARGE_TIMEOUT; /* Monitor VBUS voltage */ target_volt -= DISCHARGE_OVERSHOOT_MV; disable_sleep(SLEEP_MASK_USB_PWR); adc_enable_watchdog(ADC_CH_V_SENSE, 0xFFF, target_volt); }
void i2c_lock(int port, int lock) { if (lock) { /* Don't allow deep sleep when I2C port is locked */ disable_sleep(SLEEP_MASK_I2C); mutex_lock(port_mutex + port); } else { mutex_unlock(port_mutex + port); /* Allow deep sleep again after I2C port is unlocked */ enable_sleep(SLEEP_MASK_I2C); } }
static void enable_serial_wakeup(int enable) { static uint32_t save_exticr; if (enable) { /** * allow to wake up from serial port (RX on pin PA10) * by setting it as a GPIO with an external interrupt. */ save_exticr = STM32_AFIO_EXTICR(10 / 4); STM32_AFIO_EXTICR(10 / 4) = (save_exticr & ~(0xf << 8)); } else { /* serial port wake up : don't go back to sleep */ if (STM32_EXTI_PR & (1 << 10)) disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP); /* restore keyboard external IT on PC10 */ STM32_AFIO_EXTICR(10 / 4) = save_exticr; } }
void uart_tx_start(void) { /* If interrupt is already enabled, nothing to do */ if (GR_UART_ICTRL(0) & GC_UART_ICTRL_TX_MASK) return; /* Do not allow deep sleep while transmit in progress */ disable_sleep(SLEEP_MASK_UART); /* * Re-enable the transmit interrupt, then forcibly trigger the * interrupt. This works around a hardware problem with the * UART where the FIFO only triggers the interrupt when its * threshold is _crossed_, not just met. */ /* TODO(crosbug.com/p/33819): Do we need this hack here? Find out. */ REG_WRITE_MLV(GR_UART_ICTRL(0), GC_UART_ICTRL_TX_MASK, GC_UART_ICTRL_TX_LSB, 1); task_trigger_irq(GC_IRQNUM_UART0_TXINT); }
enum power_state power_chipset_init(void) { /* * If we're switching between images without rebooting, see if the x86 * is already powered on; if so, leave it there instead of cycling * through G3. */ if (system_jumped_to_this_image()) { if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { /* Disable idle task deep sleep when in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); CPRINTS("already in S0"); return POWER_S0; } else { /* Force all signals to their G3 states */ chipset_force_g3(); } } return POWER_G3; }
static void i2c_event_handler(int port) { int i2c_isr; static int rx_pending, buf_idx; #ifdef TCPCI_I2C_SLAVE int addr; #endif i2c_isr = STM32_I2C_ISR(port); /* * Check for error conditions. Note, arbitration loss and bus error * are the only two errors we can get as a slave allowing clock * stretching and in non-SMBus mode. */ if (i2c_isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR)) { rx_pending = 0; tx_pending = 0; /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear error status bits */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_BERRCF | STM32_I2C_ICR_ARLOCF; } /* Transfer matched our slave address */ if (i2c_isr & STM32_I2C_ISR_ADDR) { if (i2c_isr & STM32_I2C_ISR_DIR) { /* Transmitter slave */ /* Clear transmit buffer */ STM32_I2C_ISR(port) |= STM32_I2C_ISR_TXE; /* Enable txis interrupt to start response */ STM32_I2C_CR1(port) |= STM32_I2C_CR1_TXIE; } else { /* Receiver slave */ buf_idx = 0; rx_pending = 1; } /* Clear ADDR bit by writing to ADDRCF bit */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_ADDRCF; /* Inhibit stop mode when addressed until STOPF flag is set */ disable_sleep(SLEEP_MASK_I2C_SLAVE); } /* Stop condition on bus */ if (i2c_isr & STM32_I2C_ISR_STOP) { #ifdef TCPCI_I2C_SLAVE /* * if tcpc is being addressed, and we received a stop * while rx is pending, then this is a write only to * the tcpc. */ addr = STM32_I2C_ISR_ADDCODE(STM32_I2C_ISR(port)); if (rx_pending && ADDR_IS_TCPC(addr)) i2c_process_tcpc_command(0, addr, buf_idx); #endif rx_pending = 0; tx_pending = 0; /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear STOPF bit by writing to STOPCF bit */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_STOPCF; /* No longer inhibit deep sleep after stop condition */ enable_sleep(SLEEP_MASK_I2C_SLAVE); } /* Receiver full event */ if (i2c_isr & STM32_I2C_ISR_RXNE) host_buffer[buf_idx++] = STM32_I2C_RXDR(port); /* Master requested STOP or RESTART */ if (i2c_isr & STM32_I2C_ISR_NACK) { /* Make sure TXIS interrupt is disabled */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; /* Clear NACK */ STM32_I2C_ICR(port) |= STM32_I2C_ICR_NACKCF; /* Resend last byte on RESTART */ if (port == I2C_PORT_EC && tx_index) tx_index--; } /* Transmitter empty event */ if (i2c_isr & STM32_I2C_ISR_TXIS) { if (port == I2C_PORT_EC) { /* host is waiting for PD response */ if (tx_pending) { if (tx_index < tx_end) { STM32_I2C_TXDR(port) = host_buffer[tx_index++]; } else { STM32_I2C_TXDR(port) = 0xec; /* * Set tx_index = 0 to prevent NACK * handler resending last buffer byte. */ tx_index = 0; tx_end = 0; /* No pending data */ tx_pending = 0; } } else if (rx_pending) { host_i2c_resp_port = port; /* * Disable TXIS interrupt, transmission will * be prepared by host command task. */ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; #ifdef TCPCI_I2C_SLAVE addr = STM32_I2C_ISR_ADDCODE( STM32_I2C_ISR(port)); if (ADDR_IS_TCPC(addr)) i2c_process_tcpc_command(1, addr, buf_idx); else #endif i2c_process_command(); /* Reset host buffer after end of transfer */ rx_pending = 0; tx_pending = 1; } else { STM32_I2C_TXDR(port) = 0xec; } } } }
enum power_state power_handle_state(enum power_state state) { /* * Pass through RSMRST asynchronously, as PCH may not react * immediately to power changes. */ int rsmrst_in = gpio_get_level(GPIO_RSMRST_L_PGOOD); int rsmrst_out = gpio_get_level(GPIO_PCH_RSMRST_L); #ifdef GLADOS_BOARD_V2 int tries = 0; #endif if (rsmrst_in != rsmrst_out) { /* * Wait at least 10ms between power signals going high * and deasserting RSMRST to PCH. */ if (rsmrst_in) msleep(10); gpio_set_level(GPIO_PCH_RSMRST_L, rsmrst_in); CPRINTS("RSMRST: %d", rsmrst_in); } switch (state) { case POWER_G3: if (forcing_shutdown) { power_button_pch_release(); forcing_shutdown = 0; } break; case POWER_S5: if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1) return POWER_S5S3; /* Power up to next state */ break; case POWER_S3: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S3S5; } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { /* Power up to next state */ return POWER_S3S0; } else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) { /* Power down to next state */ return POWER_S3S5; } break; case POWER_S0: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { chipset_force_shutdown(); return POWER_S0S3; } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { /* Power down to next state */ return POWER_S0S3; } break; case POWER_G3S5: /* Call hooks to initialize PMIC */ hook_notify(HOOK_CHIPSET_PRE_INIT); if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) { chipset_force_shutdown(); return POWER_G3; } #ifdef GLADOS_BOARD_V2 /* * Allow up to 1s for charger to be initialized, in case * we're trying to boot the AP with no battery. */ while (charge_prevent_power_on() && tries++ < CHARGER_INITIALIZED_TRIES) { msleep(CHARGER_INITIALIZED_DELAY_MS); } /* Return to G3 if battery level is too low */ if (charge_want_shutdown() || tries == CHARGER_INITIALIZED_TRIES) { CPRINTS("power-up inhibited"); chipset_force_shutdown(); return POWER_G3; } /* Allow AP to power on */ gpio_set_level(GPIO_PMIC_SLP_SUS_L, 1); gpio_set_level(GPIO_PCH_BATLOW_L, 1); #endif return POWER_S5; case POWER_S5S3: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S5G3; } /* Enable TP + USB so that they can wake the system */ gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); gpio_set_level(GPIO_USB1_ENABLE, 1); gpio_set_level(GPIO_USB2_ENABLE, 1); /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_STARTUP); return POWER_S3; case POWER_S3S0: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S3S5; } gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1); /* Enable wireless */ wireless_set_state(WIRELESS_ON); /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_RESUME); /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); /* * Throttle CPU if necessary. This should only be asserted * when +VCCP is powered (it is by now). */ gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); return POWER_S0; case POWER_S0S3: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SUSPEND); gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0); /* Suspend wireless */ wireless_set_state(WIRELESS_SUSPEND); /* * Enable idle task deep sleep. Allow the low power idle task * to go into deep sleep in S3 or lower. */ enable_sleep(SLEEP_MASK_AP_RUN); return POWER_S3; case POWER_S3S5: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN); /* Disable wireless */ wireless_set_state(WIRELESS_OFF); gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); gpio_set_level(GPIO_USB1_ENABLE, 0); gpio_set_level(GPIO_USB2_ENABLE, 0); return POWER_S5G3; case POWER_S5G3: #ifdef CONFIG_G3_SLEEP gpio_set_level(GPIO_G3_SLEEP_EN, 1); #endif chipset_force_g3(); return POWER_G3; default: break; } return state; }
enum power_state power_handle_state(enum power_state state) { switch (state) { case POWER_G3: break; case POWER_S5: if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) return POWER_S5S3; /* Power up to next state */ break; case POWER_S3: /* Check for state transitions */ if (!power_has_signals(IN_PGOOD_S3)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S3S5; } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { /* Power up to next state */ return POWER_S3S0; } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { /* Power down to next state */ return POWER_S3S5; } break; case POWER_S0: if (!power_has_signals(IN_PGOOD_S0)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S0S3; } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { /* Power down to next state */ return POWER_S0S3; } break; case POWER_G3S5: /* Enable 3.3V DSW */ gpio_set_level(GPIO_PP3300_DSW_EN, 1); /* * Wait 10ms after +3VALW good, since that powers VccDSW and * VccSUS. */ msleep(10); /* Enable PP5000 (5V) rail as 1.05V and 1.2V rails need 5V * rail to regulate properly. */ gpio_set_level(GPIO_PP5000_EN, 1); /* Wait for PP1050/PP1200 PGOOD to go LOW to * indicate that PP5000 is stable */ while ((power_get_signals() & IN_PGOOD_PP5000) != 0) { if (task_wait_event(SECOND) == TASK_EVENT_TIMER) { CPRINTS("timeout waiting for PP5000"); gpio_set_level(GPIO_PP5000_EN, 0); chipset_force_shutdown(); return POWER_G3; } } /* Turn on 3.3V DSW gated rail for core regulator */ gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 1); /* Assert DPWROK */ gpio_set_level(GPIO_PCH_DPWROK, 1); /* Enable PP1050 rail. */ gpio_set_level(GPIO_PP1050_EN, 1); /* Wait for 1.05V to come up and CPU to notice */ if (power_wait_signals(IN_PGOOD_PP1050 | IN_PCH_SLP_SUS_DEASSERTED)) { gpio_set_level(GPIO_PP1050_EN, 0); gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0); gpio_set_level(GPIO_PP5000_EN, 0); chipset_force_shutdown(); return POWER_G3; } /* Wait 5ms for SUSCLK to stabilize */ msleep(5); /* Call hook to indicate out of G3 state */ hook_notify(HOOK_CHIPSET_PRE_INIT); return POWER_S5; case POWER_S5S3: /* Turn on power to RAM */ gpio_set_level(GPIO_PP1800_EN, 1); gpio_set_level(GPIO_PP1200_EN, 1); if (power_wait_signals(IN_PGOOD_S3)) { gpio_set_level(GPIO_PP1800_EN, 0); gpio_set_level(GPIO_PP1200_EN, 0); chipset_force_shutdown(); return POWER_S5; } /* * Take lightbar out of reset, now that +5VALW is * available and we won't leak +3VALW through the reset * line. */ gpio_set_level(GPIO_LIGHTBAR_RESET_L, 1); /* * Enable touchpad power so it can wake the system from * suspend. */ gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); /* Turn on USB power rail. */ gpio_set_level(GPIO_PP5000_USB_EN, 1); /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_STARTUP); return POWER_S3; case POWER_S3S0: /* Wait 20ms before allowing VCCST_PGOOD to rise. */ msleep(20); /* Enable wireless. */ wireless_set_state(WIRELESS_ON); /* Make sure the touchscreen is on, too. */ gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 1); /* Wait for non-core power rails good */ if (power_wait_signals(IN_PGOOD_S0)) { chipset_force_shutdown(); wireless_set_state(WIRELESS_OFF); return POWER_S3; } /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_RESUME); /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); /* Wait 99ms after all voltages good */ msleep(99); /* * Throttle CPU if necessary. This should only be asserted * when +VCCP is powered (it is by now). */ gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); /* Set PCH_PWROK */ gpio_set_level(GPIO_PCH_PWROK, 1); gpio_set_level(GPIO_SYS_PWROK, 1); return POWER_S0; case POWER_S0S3: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SUSPEND); /* Clear PCH_PWROK */ gpio_set_level(GPIO_SYS_PWROK, 0); gpio_set_level(GPIO_PCH_PWROK, 0); /* Wait 40ns */ udelay(1); /* Suspend wireless */ wireless_set_state(WIRELESS_SUSPEND); /* * Enable idle task deep sleep. Allow the low power idle task * to go into deep sleep in S3 or lower. */ enable_sleep(SLEEP_MASK_AP_RUN); /* * Deassert prochot since CPU is off and we're about to drop * +VCCP. */ gpio_set_level(GPIO_CPU_PROCHOT, 0); return POWER_S3; case POWER_S3S5: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN); /* Disable wireless */ wireless_set_state(WIRELESS_OFF); /* Disable peripheral power */ gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); gpio_set_level(GPIO_PP5000_USB_EN, 0); /* Turn off power to RAM */ gpio_set_level(GPIO_PP1800_EN, 0); gpio_set_level(GPIO_PP1200_EN, 0); /* * Put touchscreen and lightbar in reset, so we won't * leak +3VALW through the reset line to chips powered * by +5VALW. * * (Note that we're no longer powering down +5VALW due * to crosbug.com/p/16600, but to minimize side effects * of that change we'll still reset these components in * S5.) */ gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0); gpio_set_level(GPIO_LIGHTBAR_RESET_L, 0); return pause_in_s5 ? POWER_S5 : POWER_S5G3; case POWER_S5G3: /* Deassert DPWROK */ gpio_set_level(GPIO_PCH_DPWROK, 0); /* Turn off power rails enabled in S5 */ gpio_set_level(GPIO_PP1050_EN, 0); gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0); gpio_set_level(GPIO_PP5000_EN, 0); /* Disable 3.3V DSW */ gpio_set_level(GPIO_PP3300_DSW_EN, 0); return POWER_G3; } return state; }
void usb_interrupt(void) { uint32_t status = GR_USB_GINTSTS; uint32_t oepint = status & GINTSTS(OEPINT); uint32_t iepint = status & GINTSTS(IEPINT); int ep; print_later("interrupt: GINTSTS 0x%08x", status, 0, 0, 0, 0); /* We can suspend if the host stops talking to us. But if anything else * comes along (even ERLYSUSP), we should NOT suspend. */ if (status & GINTSTS(USBSUSP)) { print_later("usb_suspend()", 0, 0, 0, 0, 0); enable_sleep(SLEEP_MASK_USB_DEVICE); } else { disable_sleep(SLEEP_MASK_USB_DEVICE); } #ifdef DEBUG_ME if (status & GINTSTS(ERLYSUSP)) print_later("usb_early_suspend()", 0, 0, 0, 0, 0); if (status & GINTSTS(WKUPINT)) print_later("usb_wakeup()", 0, 0, 0, 0, 0); if (status & GINTSTS(ENUMDONE)) print_later("usb_enumdone()", 0, 0, 0, 0, 0); #endif if (status & (GINTSTS(RESETDET) | GINTSTS(USBRST))) usb_reset(); /* Initialize the SOF clock calibrator only on the first SOF */ if (GR_USB_GINTMSK & GINTMSK(SOF) && status & GINTSTS(SOF)) { init_sof_clock(); GR_USB_GINTMSK &= ~GINTMSK(SOF); } /* Endpoint interrupts */ if (oepint || iepint) { /* Note: It seems that the DAINT bits are only trustworthy for * identifying interrupts when selected by the corresponding * OEPINT and IEPINT bits from GINTSTS. */ uint32_t daint = GR_USB_DAINT; print_later(" oepint%c iepint%c daint 0x%08x", oepint ? '!' : '_', iepint ? '!' : '_', daint, 0, 0); /* EP0 has a combined IN/OUT handler. Only call it once, but * let it know which direction(s) had an interrupt. */ if (daint & (DAINT_OUTEP(0) | DAINT_INEP(0))) { uint32_t intr_on_out = (oepint && (daint & DAINT_OUTEP(0))); uint32_t intr_on_in = (iepint && (daint & DAINT_INEP(0))); ep0_interrupt(intr_on_out, intr_on_in); } /* Invoke the unidirectional IN and OUT functions for the other * endpoints. Each handler must clear their own bits in * DIEPINTn/DOEPINTn. */ for (ep = 1; ep < USB_EP_COUNT; ep++) { if (oepint && (daint & DAINT_OUTEP(ep))) usb_ep_rx[ep](); if (iepint && (daint & DAINT_INEP(ep))) usb_ep_tx[ep](); } } if (status & GINTSTS(GOUTNAKEFF)) GR_USB_DCTL |= DCTL_CGOUTNAK; if (status & GINTSTS(GINNAKEFF)) GR_USB_DCTL |= DCTL_CGNPINNAK; GR_USB_GINTSTS = status; print_later("end of interrupt", 0, 0, 0, 0, 0); }
/** * Handle an event on the NSS pin * * A falling edge of NSS indicates that the master is starting a new * transaction. A rising edge indicates that we have finsihed * * @param signal GPIO signal for the NSS pin */ void spi_event(enum gpio_signal signal) { stm32_dma_chan_t *rxdma; uint16_t *nss_reg; uint32_t nss_mask; uint16_t i; /* If not enabled, ignore glitches on NSS */ if (!enabled) return; /* Check chip select. If it's high, the AP ended a transaction. */ nss_reg = gpio_get_level_reg(GPIO_SPI1_NSS, &nss_mask); if (REG16(nss_reg) & nss_mask) { enable_sleep(SLEEP_MASK_SPI); /* * If the buffer is still used by the host command, postpone * the DMA rx setup. */ if (state == SPI_STATE_PROCESSING) { setup_transaction_later = 1; return; } /* Set up for the next transaction */ spi_init(); /* Fix for bug chrome-os-partner:31390 */ return; } disable_sleep(SLEEP_MASK_SPI); /* Chip select is low = asserted */ if (state != SPI_STATE_READY_TO_RX) { /* * AP started a transaction but we weren't ready for it. * Tell AP we weren't ready, and ignore the received data. */ CPRINTS("SPI not ready"); tx_status(EC_SPI_NOT_READY); state = SPI_STATE_RX_BAD; return; } /* We're now inside a transaction */ state = SPI_STATE_RECEIVING; tx_status(EC_SPI_RECEIVING); rxdma = dma_get_channel(STM32_DMAC_SPI1_RX); /* Wait for version, command, length bytes */ if (wait_for_bytes(rxdma, 3, nss_reg, nss_mask)) goto spi_event_error; if (in_msg[0] == EC_HOST_REQUEST_VERSION) { /* Protocol version 3 */ struct ec_host_request *r = (struct ec_host_request *)in_msg; int pkt_size; /* Wait for the rest of the command header */ if (wait_for_bytes(rxdma, sizeof(*r), nss_reg, nss_mask)) goto spi_event_error; /* * Check how big the packet should be. We can't just wait to * see how much data the host sends, because it will keep * sending dummy data until we respond. */ pkt_size = host_request_expected_size(r); if (pkt_size == 0 || pkt_size > sizeof(in_msg)) goto spi_event_error; /* Wait for the packet data */ if (wait_for_bytes(rxdma, pkt_size, nss_reg, nss_mask)) goto spi_event_error; spi_packet.send_response = spi_send_response_packet; spi_packet.request = in_msg; spi_packet.request_temp = NULL; spi_packet.request_max = sizeof(in_msg); spi_packet.request_size = pkt_size; /* Response must start with the preamble */ memcpy(out_msg, out_preamble, sizeof(out_preamble)); spi_packet.response = out_msg + sizeof(out_preamble); /* Reserve space for the preamble and trailing past-end byte */ spi_packet.response_max = sizeof(out_msg) - sizeof(out_preamble) - EC_SPI_PAST_END_LENGTH; spi_packet.response_size = 0; spi_packet.driver_result = EC_RES_SUCCESS; /* Move to processing state */ state = SPI_STATE_PROCESSING; tx_status(EC_SPI_PROCESSING); host_packet_receive(&spi_packet); return; } else if (in_msg[0] >= EC_CMD_VERSION0) { /* * Protocol version 2 * * TODO(crosbug.com/p/20257): Remove once kernel supports * version 3. */ #ifdef CHIP_FAMILY_STM32F0 CPRINTS("WARNING: Protocol version 2 is not supported on the F0" " line due to crbug.com/31390"); #endif args.version = in_msg[0] - EC_CMD_VERSION0; args.command = in_msg[1]; args.params_size = in_msg[2]; /* Wait for parameters */ if (wait_for_bytes(rxdma, 3 + args.params_size, nss_reg, nss_mask)) goto spi_event_error; /* * Params are not 32-bit aligned in protocol version 2. As a * workaround, move them to the beginning of the input buffer * so they are aligned. */ if (args.params_size) memmove(in_msg, in_msg + 3, args.params_size); args.params = in_msg; args.send_response = spi_send_response; /* Allow room for the header bytes */ args.response = out_msg + SPI_PROTO2_OFFSET; args.response_max = sizeof(out_msg) - SPI_PROTO2_OVERHEAD; args.response_size = 0; args.result = EC_RES_SUCCESS; /* Move to processing state */ state = SPI_STATE_PROCESSING; tx_status(EC_SPI_PROCESSING); host_command_received(&args); return; } spi_event_error: /* Error, timeout, or protocol we can't handle. Ignore data. */ tx_status(EC_SPI_RX_BAD_DATA); state = SPI_STATE_RX_BAD; CPRINTS("SPI rx bad data"); CPRINTF("in_msg=["); for (i = 0; i < dma_bytes_done(rxdma, sizeof(in_msg)); i++) CPRINTF("%02x ", in_msg[i]); CPRINTF("]\n"); }
static enum power_state _power_handle_state(enum power_state state) { int tries = 0; switch (state) { case POWER_G3: break; case POWER_S5: if (forcing_shutdown) { power_button_pch_release(); forcing_shutdown = 0; } #ifdef CONFIG_BOARD_HAS_RTC_RESET /* Wait for S5 exit and attempt RTC reset it supported */ if (power_s5_up) return power_wait_s5_rtc_reset(); #endif if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1) return POWER_S5S3; /* Power up to next state */ break; case POWER_S3: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S3S5; } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { /* Power up to next state */ return POWER_S3S0; } else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) { /* Power down to next state */ return POWER_S3S5; } break; case POWER_S0: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { chipset_force_shutdown(); return POWER_S0S3; #ifdef CONFIG_POWER_S0IX } else if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 0) && (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) { return POWER_S0S0ix; #endif } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { /* Power down to next state */ return POWER_S0S3; } break; #ifdef CONFIG_POWER_S0IX case POWER_S0ix: /* * TODO: add code for unexpected power loss */ if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 1) && (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) { return POWER_S0ixS0; } break; #endif case POWER_G3S5: /* Call hooks to initialize PMIC */ hook_notify(HOOK_CHIPSET_PRE_INIT); /* * Allow up to 1s for charger to be initialized, in case * we're trying to boot the AP with no battery. */ while (charge_prevent_power_on(0) && tries++ < CHARGER_INITIALIZED_TRIES) { msleep(CHARGER_INITIALIZED_DELAY_MS); } /* Return to G3 if battery level is too low */ if (charge_want_shutdown() || tries > CHARGER_INITIALIZED_TRIES) { CPRINTS("power-up inhibited"); chipset_force_shutdown(); return POWER_G3; } if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) { chipset_force_shutdown(); return POWER_G3; } power_s5_up = 1; return POWER_S5; case POWER_S5S3: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S5G3; } /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_STARTUP); return POWER_S3; case POWER_S3S0: if (!power_has_signals(IN_PGOOD_ALL_CORE)) { /* Required rail went away */ chipset_force_shutdown(); return POWER_S3S5; } gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1); /* Enable wireless */ wireless_set_state(WIRELESS_ON); /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_RESUME); /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); /* * Throttle CPU if necessary. This should only be asserted * when +VCCP is powered (it is by now). */ gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); return POWER_S0; case POWER_S0S3: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SUSPEND); gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0); /* Suspend wireless */ wireless_set_state(WIRELESS_SUSPEND); /* * Enable idle task deep sleep. Allow the low power idle task * to go into deep sleep in S3 or lower. */ enable_sleep(SLEEP_MASK_AP_RUN); return POWER_S3; #ifdef CONFIG_POWER_S0IX case POWER_S0S0ix: /* call hooks before standby */ hook_notify(HOOK_CHIPSET_SUSPEND); lpc_enable_wake_mask_for_lid_open(); /* * Enable idle task deep sleep. Allow the low power idle task * to go into deep sleep in S0ix. */ enable_sleep(SLEEP_MASK_AP_RUN); return POWER_S0ix; case POWER_S0ixS0: lpc_disable_wake_mask_for_lid_open(); /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_RESUME); /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); return POWER_S0; #endif case POWER_S3S5: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN); /* Disable wireless */ wireless_set_state(WIRELESS_OFF); /* Always enter into S5 state. The S5 state is required to * correctly handle global resets which have a bit of delay * while the SLP_Sx_L signals are asserted then deasserted. */ power_s5_up = 0; return POWER_S5; case POWER_S5G3: chipset_force_g3(); return POWER_G3; default: break; } return state; }
int pd_board_checks(void) { #ifdef CONFIG_HIBERNATE static timestamp_t hib_to; static int hib_to_ready; #endif int vbus_volt; int ovp_idx; /* Reload the watchdog */ STM32_IWDG_KR = STM32_IWDG_KR_RELOAD; #ifdef CONFIG_HIBERNATE /* If output is disabled for long enough, then hibernate */ if (!pd_is_connected(0) && hib_to_ready) { if (get_time().val >= hib_to.val) { debug_printf("hib\n"); __enter_hibernate(0, 0); } } else { hib_to.val = get_time().val + 60*SECOND; hib_to_ready = 1; } #endif /* if it's been a while since last RX edge, then allow deep sleep */ if (get_time_since_last_edge(0) > PD_RX_SLEEP_TIMEOUT) enable_sleep(SLEEP_MASK_USB_PD); vbus_volt = adc_read_channel(ADC_CH_V_SENSE); vbus_amp = adc_read_channel(ADC_CH_A_SENSE); if (fault == FAULT_FAST_OCP) { debug_printf("Fast OCP\n"); pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_FAST_OCP, NULL); fault = FAULT_OCP; /* reset over-current after 1 second */ fault_deadline.val = get_time().val + OCP_TIMEOUT; return EC_ERROR_INVAL; } if (vbus_amp > MAX_CURRENT) { /* 3 more samples to check whether this is just a transient */ int count; for (count = 0; count < 3; count++) if (adc_read_channel(ADC_CH_A_SENSE) < MAX_CURRENT) break; /* trigger the slow OCP iff all 4 samples are above the max */ if (count == 3) { debug_printf("OCP %d mA\n", vbus_amp * VDDA_MV / CURR_GAIN * 1000 / R_SENSE / ADC_SCALE); pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_OCP, NULL); fault = FAULT_OCP; /* reset over-current after 1 second */ fault_deadline.val = get_time().val + OCP_TIMEOUT; return EC_ERROR_INVAL; } } /* * Optimize power consumption when the sink is idle : * Enable STOP mode while we are connected, * this kills fast OCP as the actual ADC conversion for the analog * watchdog will happen on the next wake-up (x0 ms latency). */ if (vbus_amp < SINK_IDLE_CURRENT && !discharge_is_enabled()) /* override the PD state machine sleep mask */ enable_sleep(SLEEP_MASK_USB_PWR); else if (vbus_amp > SINK_IDLE_CURRENT) disable_sleep(SLEEP_MASK_USB_PWR); /* * Set the voltage index to use for checking OVP. During a down step * transition, use the previous voltage index to check for OVP. */ ovp_idx = discharge_is_enabled() ? last_volt_idx : volt_idx; if ((output_is_enabled() && (vbus_volt > voltages[ovp_idx].ovp)) || (fault && (vbus_volt > voltages[ovp_idx].ovp_rec))) { if (!fault) { debug_printf("OVP %d mV\n", ADC_TO_VOLT_MV(vbus_volt)); pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_OVP, NULL); } fault = FAULT_OVP; /* no timeout */ fault_deadline.val = get_time().val; return EC_ERROR_INVAL; } /* the discharge did not work properly */ if (discharge_is_enabled() && (get_time().val > discharge_deadline.val)) { /* ensure we always finish a 2-step discharge */ volt_idx = discharge_volt_idx; set_output_voltage(voltages[volt_idx].select); /* stop it */ discharge_disable(); /* enable over-current monitoring */ adc_enable_watchdog(ADC_CH_A_SENSE, MAX_CURRENT_FAST, 0); debug_printf("Disch FAIL %d mV\n", ADC_TO_VOLT_MV(vbus_volt)); pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_DISCH, NULL); fault = FAULT_DISCHARGE; /* reset it after 1 second */ fault_deadline.val = get_time().val + OCP_TIMEOUT; return EC_ERROR_INVAL; } /* everything is good *and* the error condition has expired */ if ((fault != FAULT_OK) && (get_time().val > fault_deadline.val)) { fault = FAULT_OK; debug_printf("Reset fault\n"); /* * Reset the PD state and communication on both side, * so we can now re-negociate a voltage. */ return EC_ERROR_INVAL; } return EC_SUCCESS; }
void charger_task(void) { int next_state; int wait_time = T1_USEC; timestamp_t pre_chg_start = get_time(); pmu_init(); /* Enable low current charging */ pmu_low_current_charging(1); /* Enable charger interrupt */ gpio_enable_interrupt(GPIO_CHARGER_INT_L); /* * EC STOP mode support * The charging loop can be stopped in idle state with AC unplugged. * Charging loop will be resumed by TPSCHROME interrupt. */ enable_charging(0); disable_sleep(SLEEP_MASK_CHARGING); while (1) { last_waken = get_time(); pmu_clear_irq(); #ifdef CONFIG_PMU_TPS65090_CHARGING_LED update_battery_led(); #endif /* * When battery is extremely low, the internal voltage can not * power on its gas guage IC. Charging loop will enable the * charger and turn on trickle charging. For safty reason, * charger should be disabled if the communication to battery * failed. */ if (current_state == ST_PRE_CHARGING && get_time().val - pre_chg_start.val >= PRE_CHARGING_TIMEOUT) next_state = ST_CHARGING_ERROR; else next_state = calc_next_state(current_state); if (next_state != current_state) { /* Reset state of charge moving average window */ rsoc_moving_average(-1); CPRINTS("batt state %s -> %s", state_list[current_state], state_list[next_state]); current_state = next_state; switch (current_state) { case ST_PRE_CHARGING: pre_chg_start = get_time(); /* Fall through */ case ST_CHARGING: if (pmu_blink_led(0)) next_state = ST_CHARGING_ERROR; else enable_charging(1); break; case ST_CHARGING_ERROR: /* * Enable hardware charging circuit after set * PMU to hardware error state. */ if (pmu_blink_led(1)) enable_charging(0); else enable_charging(1); break; case ST_IDLE: case ST_IDLE0: case ST_BAD_COND: case ST_DISCHARGING: enable_charging(0); /* Ignore charger error when discharging */ pmu_blink_led(0); break; } } switch (current_state) { case ST_CHARGING: case ST_CHARGING_ERROR: wait_time = T2_USEC; break; case ST_DISCHARGING: wait_time = T3_USEC; break; case ST_PRE_CHARGING: wait_time = T1_USEC; if (get_time().val - pre_chg_start.val >= PRE_CHARGING_TIMEOUT) enable_charging(0); break; default: if (extpower_is_present()) { wait_time = T1_USEC; break; } else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { wait_time = T1_OFF_USEC; enable_sleep(SLEEP_MASK_CHARGING); } else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { wait_time = T1_SUSPEND_USEC; } else { wait_time = T1_USEC; } } if (!has_pending_event) { task_wait_event(wait_time); disable_sleep(SLEEP_MASK_CHARGING); } else { has_pending_event = 0; } } }
void usb_init(void) { int i, resume; /* USB is in use */ disable_sleep(SLEEP_MASK_USB_DEVICE); /* * Resuming from a deep sleep is a lot like a cold boot, but there are * few things that we need to do slightly differently. However, we ONLY * do them if we're really resuming due to a USB wakeup. If we're woken * for some other reason, we just do a normal USB reset. The host * doesn't mind. */ resume = ((system_get_reset_flags() & RESET_FLAG_USB_RESUME) && (GR_USB_GINTSTS & GC_USB_GINTSTS_WKUPINT_MASK)); /* TODO(crosbug.com/p/46813): Clean this up. Do only what's needed, and * use meaningful constants instead of magic numbers. */ GREG32(GLOBALSEC, DDMA0_REGION0_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION1_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION2_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION3_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION0_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION1_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION2_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION3_CTRL) = 0xffffffff; /* Enable clocks */ clock_enable_module(MODULE_USB, 1); /* TODO(crbug.com/496888): set up pinmux */ gpio_config_module(MODULE_USB, 1); /* Make sure interrupts are disabled */ GR_USB_GINTMSK = 0; GR_USB_DAINTMSK = 0; GR_USB_DIEPMSK = 0; GR_USB_DOEPMSK = 0; /* Select the correct PHY */ usb_select_phy(which_phy); /* Full-Speed Serial PHY */ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN | GUSBCFG_TOUTCAL(7) /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ | GUSBCFG_USBTRDTIM(14); if (!resume) /* Don't reset on resume, because some preserved internal state * will be lost and there's no way to restore it. */ usb_softreset(); GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN | GUSBCFG_TOUTCAL(7) /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ | GUSBCFG_USBTRDTIM(14); /* Global + DMA configuration */ /* TODO: What about the AHB Burst Length Field? It's 0 now. */ GR_USB_GAHBCFG = GAHBCFG_DMA_EN | GAHBCFG_GLB_INTR_EN | GAHBCFG_NP_TXF_EMP_LVL; /* Be in disconnected state until we are ready */ if (!resume) usb_disconnect(); if (resume) /* DEVADDR is preserved in the USB module during deep sleep, * but it doesn't show up in USB_DCFG on resume. If we don't * restore it manually too, it doesn't work. */ GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18); else /* Init: USB2 FS, Scatter/Gather DMA, DEVADDR = 0x00 */ GR_USB_DCFG |= DCFG_DEVSPD_FS48 | DCFG_DESCDMA; /* If we've restored a nonzero device address, update our state. */ if (GR_USB_DCFG & GC_USB_DCFG_DEVADDR_MASK) { /* Caution: We only have one config TODAY, so there's no real * difference between DS_CONFIGURED and DS_ADDRESS. */ device_state = DS_CONFIGURED; configuration_value = 1; } else { device_state = DS_DEFAULT; configuration_value = 0; } /* Now that DCFG.DesDMA is accurate, prepare the FIFOs */ setup_data_fifos(); /* If resuming, reinitialize the endpoints now. For a cold boot we'll * do this as part of handling the host-driven reset. */ if (resume) usb_init_endpoints(); /* Clear any pending interrupts */ for (i = 0; i < 16; i++) { GR_USB_DIEPINT(i) = 0xffffffff; GR_USB_DOEPINT(i) = 0xffffffff; } GR_USB_GINTSTS = 0xFFFFFFFF; /* Unmask some endpoint interrupt causes */ GR_USB_DIEPMSK = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK; GR_USB_DOEPMSK = DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK | DOEPMSK_SETUPMSK; /* Enable interrupt handlers */ task_enable_irq(GC_IRQNUM_USB0_USBINTR); /* Allow USB interrupts to come in */ GR_USB_GINTMSK = /* NAK bits that must be cleared by the DCTL register */ GINTMSK(GOUTNAKEFF) | GINTMSK(GINNAKEFF) | /* Initialization events */ GINTMSK(USBRST) | GINTMSK(ENUMDONE) | /* Endpoint activity, cleared by the DOEPINT/DIEPINT regs */ GINTMSK(OEPINT) | GINTMSK(IEPINT) | /* Reset detected while suspended. Need to wake up. */ GINTMSK(RESETDET) | /* TODO: Do we need this? */ /* Idle, Suspend detected. Should go to sleep. */ GINTMSK(ERLYSUSP) | GINTMSK(USBSUSP) | /* Watch for first SOF */ GINTMSK(SOF); /* Device registers have been setup */ GR_USB_DCTL |= DCTL_PWRONPRGDONE; udelay(10); GR_USB_DCTL &= ~DCTL_PWRONPRGDONE; /* Clear global NAKs */ GR_USB_DCTL |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; #ifndef CONFIG_USB_INHIBIT_CONNECT /* Indicate our presence to the USB host */ if (!resume) usb_connect(); #endif }
enum power_state power_handle_state(enum power_state state) { int value; static int boot_from_g3; switch (state) { case POWER_G3: boot_from_g3 = check_for_power_on_event(); if (boot_from_g3) return POWER_G3S5; break; case POWER_G3S5: return POWER_S5; case POWER_S5: if (boot_from_g3) { value = boot_from_g3; boot_from_g3 = 0; } else { value = check_for_power_on_event(); } if (value) { CPRINTS("power on %d", value); return POWER_S5S3; } return state; case POWER_S5S3: hook_notify(HOOK_CHIPSET_PRE_INIT); power_on(); disable_sleep(SLEEP_MASK_AP_RUN); powerled_set_state(POWERLED_STATE_ON); if (power_wait_signals(IN_POWER_GOOD) == EC_SUCCESS) { CPRINTS("POWER_GOOD seen"); if (power_button_wait_for_release( DELAY_SHUTDOWN_ON_POWER_HOLD) == EC_SUCCESS) { power_button_was_pressed = 0; set_pmic_pwron(0); /* setup misc gpio for S3/S0 functionality */ gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT | GPIO_INT_BOTH | GPIO_PULL_DOWN); gpio_set_flags(GPIO_EC_INT_L, GPIO_OUTPUT | GPIO_OUT_HIGH); /* Call hooks now that AP is running */ hook_notify(HOOK_CHIPSET_STARTUP); return POWER_S3; } else { CPRINTS("long-press button, shutdown"); power_off(); /* * Since the AP may be up already, return S0S3 * state to go through the suspend hook. */ return POWER_S0S3; } } else { CPRINTS("POWER_GOOD not seen in time"); } chipset_turn_off_power_rails(); return POWER_S5; case POWER_S3: if (!(power_get_signals() & IN_POWER_GOOD)) return POWER_S3S5; else if (!(power_get_signals() & IN_SUSPEND)) return POWER_S3S0; return state; case POWER_S3S0: powerled_set_state(POWERLED_STATE_ON); hook_notify(HOOK_CHIPSET_RESUME); return POWER_S0; case POWER_S0: value = check_for_power_off_event(); if (value) { CPRINTS("power off %d", value); power_off(); return POWER_S0S3; } else if (power_get_signals() & IN_SUSPEND) return POWER_S0S3; return state; case POWER_S0S3: if (lid_is_open()) powerled_set_state(POWERLED_STATE_SUSPEND); else powerled_set_state(POWERLED_STATE_OFF); /* Call hooks here since we don't know it prior to AP suspend */ hook_notify(HOOK_CHIPSET_SUSPEND); return POWER_S3; case POWER_S3S5: power_button_wait_for_release(-1); power_button_was_pressed = 0; return POWER_S5; case POWER_S5G3: return POWER_G3; } return state; }
/* actual RX FIFO handler (runs in interrupt context) */ static void process_rx_data(uint8_t *data, size_t data_size) { /* We're receiving some bytes, so don't sleep */ disable_sleep(SLEEP_MASK_SPI); if ((rxbuf_count + data_size) > RXBUF_MAX) { CPRINTS("TPM SPI input overflow: %d + %d > %d in state %d", rxbuf_count, data_size, RXBUF_MAX, sps_tpm_state); sps_tx_status(TPM_STALL_DEASSERT); sps_tpm_state = SPS_TPM_STATE_RX_BAD; /* In this state, this function won't be called again until * after the CS deasserts and we've prepared for a new * transaction. */ return; } memcpy(rxbuf + rxbuf_count, data, data_size); rxbuf_count += data_size; /* Wait until we have enough. */ if (rxbuf_count < rxbuf_needed) return; /* Okay, we have enough. Now what? */ if (sps_tpm_state == SPS_TPM_STATE_RECEIVING_HEADER) { uint32_t old_wrptr, wrptr; /* Got the header. What's it say to do? */ if (header_says_to_read(rxbuf, ®addr, &bytecount)) { /* Send the stall deassert manually */ txbuf[0] = TPM_STALL_DEASSERT; /* Copy the register contents into the TXFIFO */ /* TODO: This is blindly assuming TXFIFO has enough * room. What can we do if it doesn't? */ tpm_register_get(regaddr - TPM_LOCALITY_0_SPI_BASE, txbuf + 1, bytecount); sps_transmit(txbuf, bytecount + 1); sps_tpm_state = SPS_TPM_STATE_PONDERING; return; } /* * Master is writing, we will need more data. * * This is a tricky part, as we do not know how many dummy * bytes the master has already written. And the actual data * of course will start arriving only after we change the idle * byte to set the LSB to 1. * * What we do know is that the idle byte repeatedly sent on * the MISO line is sampled at the same time as the RX FIFO * write pointer is written by the chip, after clocking in the * next byte. That is, we can synchronize with the line by * waiting for the RX FIFO write pointer to change. Then we * can change the idle byte to indicate that the slave is * ready to receive the rest of the data, and take note of the * RX FIFO write pointer, as the first byte of the message * will show up in the receive FIFO 2 bytes later. */ /* * Let's wait til the start of the next byte cycle. This must * be done in a tight loop (with interrupts disabled?). */ old_wrptr = sps_rx_fifo_wrptr(); do { wrptr = sps_rx_fifo_wrptr(); } while (old_wrptr == wrptr); /* * Write the new idle byte value, it will start transmitting * *next* after the current byte. */ sps_tx_status(TPM_STALL_DEASSERT); /* * Verify that we managed to change the idle byte value within * the required time (RX FIFO write pointer has not changed) */ if (sps_rx_fifo_wrptr() != wrptr) { CPRINTS("%s:" " ERROR: failed to change idle byte in time", __func__); sps_tpm_state = SPS_TPM_STATE_PONDERING; } else { /* * Ok, we're good. Remember where in the receive * stream the actual data will start showing up. It is * two bytes after the current one (the current idle * byte still has the LSB set to zero, the next one * will have the LSB set to one, only after receiving * it the master will start sending the actual data. */ stall_threshold = ((wrptr - rx_fifo_base) & SPS_FIFO_MASK) + 2; rxbuf_needed = stall_threshold + bytecount; sps_tpm_state = SPS_TPM_STATE_RECEIVING_WRITE_DATA; } return; } if (sps_tpm_state == SPS_TPM_STATE_RECEIVING_WRITE_DATA) { /* Ok, we have all the write data. */ tpm_register_put(regaddr - TPM_LOCALITY_0_SPI_BASE, rxbuf + stall_threshold, bytecount); sps_tpm_state = SPS_TPM_STATE_PONDERING; } }