/** * ipc_read: Host -> ISH communication * * 1. Host SW checks HOST2ISH doorbell bit[31] to ensure it is cleared. * 2. Host SW writes data to HOST2ISH message registers (upto 128 bytes). * 3. Host SW writes to HOST2ISH doorbell register, setting bit [31]. * 4. ISH FW recieves interrupt, checks PISR[0] to realize the event. * 5. After reading data, ISH FW clears HOST2ISH DB bit[31]. * 6. Host SW recieves interrupt, reads Host PISR bit[8] to realize * the message was consumed by ISH FW. */ static int ipc_read(uint8_t peer_id, void *out_buff, uint32_t buff_size) { #ifdef ISH_DEBUG int i; #endif struct ipc_if_ctx *ctx; int retval = EC_SUCCESS; ctx = &ipc_peer_ctxs[peer_id]; if (buff_size > IPC_MSG_MAX_SIZE) retval = IPC_FAILURE; if (retval >= 0) { /* Copy message to out buffer. */ memcpy(out_buff, (const uint32_t *)ctx->in_msg_reg, buff_size); retval = buff_size; #ifdef ISH_DEBUG CPRINTF("ipc_read, len=0x%0x [", buff_size); for (i = 0; i < buff_size; i++) CPRINTF("0x%0x ", (uint8_t) ((char *)out_buff)[i]); CPUTS("]\n"); #endif } REG32(ctx->in_drbl_reg) = 0; ipc_set_pimr(peer_id, SET_PIMR, PIMR_SIGNAL_IN); return retval; }
/** * Print debug output for the host command request, before it's processed. * * @param args Host command args */ static void host_command_debug_request(struct host_cmd_handler_args *args) { static int hc_prev_cmd; static uint64_t hc_prev_time; /* * In normal output mode, skip printing repeats of the same command * that occur in rapid succession - such as flash commands during * software sync. */ if (hcdebug == HCDEBUG_NORMAL) { uint64_t t = get_time().val; if (args->command == hc_prev_cmd && t - hc_prev_time < HCDEBUG_MAX_REPEAT_DELAY) { hc_prev_time = t; CPUTS("+"); return; } hc_prev_time = t; hc_prev_cmd = args->command; } if (hcdebug >= HCDEBUG_PARAMS && args->params_size) CPRINTS("HC 0x%02x.%d:%.*h", args->command, args->version, args->params_size, args->params); else CPRINTS("HC 0x%02x", args->command); }
void system_print_reset_flags(void) { int count = 0; int i; if (!reset_flags) { CPUTS("unknown"); return; } for (i = 0; i < ARRAY_SIZE(reset_flag_descs); i++) { if (reset_flags & (1 << i)) { if (count++) CPUTS(" "); CPUTS(reset_flag_descs[i]); } } }
void i2c_recovery(int controller) { CPUTS("RECOVERY\r\n"); /* Abort data, generating STOP condition */ if (i2c_abort_data(controller) == 1 && i2c_stsobjs[controller].err_code == SMB_MASTER_NO_ADDRESS_MATCH) return; /* Reset i2c controller by re-enable i2c controller*/ i2c_reset(controller); }
void i2c_master_int_handler (int controller) { volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* Condition 1 : A Bus Error has been identified */ if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_BER)) { /* Generate a STOP condition */ I2C_STOP(controller); CPUTS("-SP"); /* Clear BER Bit */ SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_BER); /* Set error code */ p_status->err_code = SMB_BUS_ERROR; /* Notify upper layer */ p_status->oper_state = SMB_IDLE; task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-BER"); } /* Condition 2: A negative acknowledge has occurred */ if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_NEGACK)) { /* Generate a STOP condition */ I2C_STOP(controller); CPUTS("-SP"); /* Clear NEGACK Bit */ SET_BIT(NPCX_SMBST(controller), NPCX_SMBST_NEGACK); /* Set error code */ p_status->err_code = SMB_MASTER_NO_ADDRESS_MATCH; /* Notify upper layer */ p_status->oper_state = SMB_IDLE; task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-NA"); } /* Condition 3: SDA status is set - transmit or receive */ if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST)) i2c_handle_sda_irq(controller); }
static int printrx(const char *desc, const uint8_t *txdata, int txlen, int rxlen) { uint8_t rxdata[32]; int rv; int i; rv = spi_transaction(SPI_FLASH_DEVICE, txdata, txlen, rxdata, rxlen); if (rv) return rv; CPRINTS("%-12s:", desc); for (i = 0; i < rxlen; i++) CPRINTS(" 0x%02x", rxdata[i]); CPUTS("\n"); return EC_SUCCESS; }
/** * ipc_write: ISH -> Host Communication * * 1. ISH FW ensures ISH2HOST doorbell busy bit [31] is cleared. * 2. ISH FW writes data (upto 128 bytes) to ISH2HOST message registers. * 3. ISH FW writes to ISH2HOST doorbell, busy bit (31) is set. * 4. Host SW receives interrupt, reads host PISR[0] to realize event. * 5. Upon reading data, Host driver clears ISH2HOST doorbell busy bit. This * de-asserts the interrupt. * 6. ISH FW also receieves an interrupt for the clear event. */ static int ipc_write(uint8_t peer_id, void *buff, uint32_t buff_size) { struct ipc_if_ctx *ctx; uint32_t drbl_val = 0; #ifdef ISH_DEBUG int i; #endif ctx = &ipc_peer_ctxs[peer_id]; if (ipc_wait_until_msg_consumed(ctx, IPC_TIMEOUT)) { /* timeout */ return IPC_FAILURE; } #ifdef ISH_DEBUG CPRINTF("ipc_write, len=0x%0x [", buff_size); for (i = 0; i < buff_size; i++) CPRINTF("0x%0x ", (uint8_t) ((char *)buff)[i]); CPUTS("]\n"); #endif /* write message */ if (buff_size <= IPC_MSG_MAX_SIZE) { /* write to message register */ memcpy((uint32_t *) ctx->out_msg_reg, buff, buff_size); drbl_val = IPC_BUILD_HEADER(buff_size, IPC_PROTOCOL_ECP, SET_BUSY); } else { return IPC_FAILURE; } /* write doorbell */ REG32(ctx->out_drbl_reg) = drbl_val; return EC_SUCCESS; }
test_mockable int main(void) { /* * Pre-initialization (pre-verified boot) stage. Initialization at * this level should do as little as possible, because verified boot * may need to jump to another image, which will repeat this * initialization. In particular, modules should NOT enable * interrupts. */ #ifdef CONFIG_BOARD_PRE_INIT board_config_pre_init(); #endif #ifdef CONFIG_MPU mpu_pre_init(); #endif /* Configure the pin multiplexers and GPIOs */ jtag_pre_init(); gpio_pre_init(); #ifdef CONFIG_BOARD_POST_GPIO_INIT board_config_post_gpio_init(); #endif /* * Initialize interrupts, but don't enable any of them. Note that * task scheduling is not enabled until task_start() below. */ task_pre_init(); /* * Initialize the system module. This enables the hibernate clock * source we need to calibrate the internal oscillator. */ system_pre_init(); system_common_pre_init(); #ifdef CONFIG_FLASH /* * Initialize flash and apply write protect if necessary. Requires * the reset flags calculated by system initialization. */ flash_pre_init(); #endif /* Set the CPU clocks / PLLs. System is now running at full speed. */ clock_init(); /* * Initialize timer. Everything after this can be benchmarked. * get_time() and udelay() may now be used. usleep() requires task * scheduling, so cannot be used yet. Note that interrupts declared * via DECLARE_IRQ() call timer routines when profiling is enabled, so * timer init() must be before uart_init(). */ timer_init(); /* Main initialization stage. Modules may enable interrupts here. */ cpu_init(); #ifdef CONFIG_DMA /* Initialize DMA. Must be before UART. */ dma_init(); #endif /* Initialize UART. Console output functions may now be used. */ uart_init(); if (system_jumped_to_this_image()) { CPRINTS("UART initialized after sysjump"); } else { CPUTS("\n\n--- UART initialized after reboot ---\n"); CPUTS("[Reset cause: "); system_print_reset_flags(); CPUTS("]\n"); } CPRINTF("[Image: %s, %s]\n", system_get_image_copy_string(), system_get_build_info()); #ifdef CONFIG_WATCHDOG /* * Intialize watchdog timer. All lengthy operations between now and * task_start() must periodically call watchdog_reload() to avoid * triggering a watchdog reboot. (This pretty much applies only to * verified boot, because all *other* lengthy operations should be done * by tasks.) */ watchdog_init(); #endif /* * Verified boot needs to read the initial keyboard state and EEPROM * contents. EEPROM must be up first, so keyboard_scan can toggle * debugging settings via keys held at boot. */ #ifdef CONFIG_EEPROM eeprom_init(); #endif #ifdef CONFIG_EOPTION eoption_init(); #endif #ifdef HAS_TASK_KEYSCAN keyboard_scan_init(); #endif /* Initialize the hook library. This calls HOOK_INIT hooks. */ hook_init(); /* * Print the init time. Not completely accurate because it can't take * into account the time before timer_init(), but it'll at least catch * the majority of the time. */ CPRINTS("Inits done"); /* Launch task scheduling (never returns) */ return task_start(); }
int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { int ctrl = i2c_port_to_controller(port); volatile struct i2c_status *p_status = i2c_stsobjs + ctrl; if (out_size == 0 && in_size == 0) return EC_SUCCESS; interrupt_disable(); /* make sure bus is not occupied by the other task */ if (p_status->task_waiting != TASK_ID_INVALID) { interrupt_enable(); return EC_ERROR_BUSY; } /* Assign current task ID */ p_status->task_waiting = task_get_current(); interrupt_enable(); /* Select port for multi-ports i2c controller */ i2c_select_port(port); /* Copy data to controller struct */ p_status->flags = flags; p_status->tx_buf = out; p_status->sz_txbuf = out_size; p_status->rx_buf = in; p_status->sz_rxbuf = in_size; #if I2C_7BITS_ADDR /* Set slave address from 7-bits to 8-bits */ p_status->slave_addr = (slave_addr<<1); #else /* Set slave address (8-bits) */ p_status->slave_addr = slave_addr; #endif /* Reset index & error */ p_status->idx_buf = 0; p_status->err_code = SMB_OK; /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_bus_busy(ctrl) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the controller. */ i2c_unwedge(ctrl); /* recovery i2c controller */ i2c_recovery(ctrl); /* Select port again for recovery */ i2c_select_port(port); } CPUTS("\n"); /* Start master transaction */ i2c_master_transaction(ctrl); /* Reset task ID */ p_status->task_waiting = TASK_ID_INVALID; /* Disable SMB interrupt and New Address Match interrupt source */ i2c_interrupt(ctrl, 0); CPRINTS("-Err:0x%02x\n", p_status->err_code); return (p_status->err_code == SMB_OK) ? EC_SUCCESS : EC_ERROR_UNKNOWN; }
inline void i2c_handle_sda_irq(int controller) { volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* 1 Issue Start is successful ie. write address byte */ if (p_status->oper_state == SMB_MASTER_START || p_status->oper_state == SMB_REPEAT_START) { uint8_t addr = p_status->slave_addr; /* Prepare address byte */ if (p_status->sz_txbuf == 0) {/* Receive mode */ p_status->oper_state = SMB_READ_OPER; /* * Receiving one byte only - set nack just * before writing address byte */ if (p_status->sz_rxbuf == 1) I2C_NACK(controller); /* Write the address to the bus R bit*/ I2C_WRITE_BYTE(controller, (addr | 0x1)); CPRINTS("-ARR-0x%02x", addr); } else {/* Transmit mode */ p_status->oper_state = SMB_WRITE_OPER; /* Write the address to the bus W bit*/ I2C_WRITE_BYTE(controller, addr); CPRINTS("-ARW-0x%02x", addr); } /* Completed handling START condition */ return; } /* 2 Handle master write operation */ else if (p_status->oper_state == SMB_WRITE_OPER) { /* all bytes have been written, in a pure write operation */ if (p_status->idx_buf == p_status->sz_txbuf) { /* no more message */ if (p_status->sz_rxbuf == 0) { /* need to STOP or not */ if (p_status->flags & I2C_XFER_STOP) { /* Issue a STOP condition on the bus */ I2C_STOP(controller); CPUTS("-SP"); /* Clear SDAST by writing dummy byte */ I2C_WRITE_BYTE(controller, 0xFF); } /* Set error code */ p_status->err_code = SMB_OK; /* Set SMB status if we need stall bus */ p_status->oper_state = (p_status->flags & I2C_XFER_STOP) ? SMB_IDLE : SMB_WRITE_SUSPEND; /* * Disable interrupt for i2c master stall SCL * and forbid SDAST generate interrupt * until common layer start other transactions */ if (p_status->oper_state == SMB_WRITE_SUSPEND) i2c_interrupt(controller, 0); /* Notify upper layer */ task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-END"); } /* need to restart & send slave address immediately */ else { uint8_t addr_byte = p_status->slave_addr; /* * Prepare address byte * and start to receive bytes */ p_status->oper_state = SMB_READ_OPER; /* Reset index of buffer */ p_status->idx_buf = 0; /* * Generate (Repeated) Start * upon next write to SDA */ I2C_START(controller); CPUTS("-RST"); /* * Receiving one byte only - set nack just * before writing address byte */ if (p_status->sz_rxbuf == 1) { I2C_NACK(controller); CPUTS("-GNA"); } /* Write the address to the bus R bit*/ I2C_WRITE_BYTE(controller, (addr_byte | 0x1)); CPUTS("-ARR"); } } /* write next byte (not last byte and not slave address */ else { I2C_WRITE_BYTE(controller, p_status->tx_buf[p_status->idx_buf++]); CPRINTS("-W(%02x)", p_status->tx_buf[p_status->idx_buf-1]); } } /* 3 Handle master read operation (read or after a write operation) */ else if (p_status->oper_state == SMB_READ_OPER) { uint8_t data; /* last byte is about to be read - end of transaction */ if (p_status->idx_buf == (p_status->sz_rxbuf - 1)) { /* need to STOP or not */ if (p_status->flags & I2C_XFER_STOP) { /* Stop should set before reading last byte */ I2C_STOP(controller); CPUTS("-SP"); } } /* Check if byte-before-last is about to be read */ else if (p_status->idx_buf == (p_status->sz_rxbuf - 2)) { /* * Set nack before reading byte-before-last, * so that nack will be generated after receive * of last byte */ if (p_status->flags & I2C_XFER_STOP) { I2C_NACK(controller); CPUTS("-GNA"); } } /* Read last byte but flag don't include I2C_XFER_STOP */ if (p_status->idx_buf == p_status->sz_rxbuf-1) { /* * Disable interrupt before i2c master read SDA reg * (stall SCL) and forbid SDAST generate interrupt * until common layer start other transactions */ if (!(p_status->flags & I2C_XFER_STOP)) i2c_interrupt(controller, 0); } /* Read data for SMBSDA */ I2C_READ_BYTE(controller, data); CPRINTS("-R(%02x)", data); /* Read to buffer */ p_status->rx_buf[p_status->idx_buf++] = data; /* last byte is read - end of transaction */ if (p_status->idx_buf == p_status->sz_rxbuf) { /* Set current status */ p_status->oper_state = (p_status->flags & I2C_XFER_STOP) ? SMB_IDLE : SMB_READ_SUSPEND; /* Set error code */ p_status->err_code = SMB_OK; /* Notify upper layer of missing data */ task_set_event(p_status->task_waiting, TASK_EVENT_I2C_IDLE, 0); CPUTS("-END"); } } }
enum smb_error i2c_master_transaction(int controller) { /* Set i2c mode to object */ int events = 0; volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* Assign current SMB status of controller */ if (p_status->oper_state == SMB_IDLE) { /* New transaction */ p_status->oper_state = SMB_MASTER_START; } else if (p_status->oper_state == SMB_WRITE_SUSPEND) { if (p_status->sz_txbuf == 0) { /* Read bytes from next transaction */ p_status->oper_state = SMB_REPEAT_START; CPUTS("R"); } else { /* Continue to write the other bytes */ p_status->oper_state = SMB_WRITE_OPER; I2C_WRITE_BYTE(controller, p_status->tx_buf[p_status->idx_buf++]); CPRINTS("-W(%02x)", p_status->tx_buf[p_status->idx_buf-1]); } } else if (p_status->oper_state == SMB_READ_SUSPEND) { /* Need to read the other bytes from next transaction */ uint8_t data; uint8_t timeout = 10; /* unit: us */ p_status->oper_state = SMB_READ_OPER; /* wait for SDAST issue */ while (timeout > 0) { if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST)) break; if (--timeout > 0) usleep(10); } if (timeout == 0) return EC_ERROR_TIMEOUT; /* * Read first byte from SMBSDA in case SDAST interrupt occurs * immediately before task_wait_event_mask() func */ I2C_READ_BYTE(controller, data); CPRINTS("-R(%02x)", data); /* Read to buffer */ p_status->rx_buf[p_status->idx_buf++] = data; } /* Generate a START condition */ if (p_status->oper_state == SMB_MASTER_START || p_status->oper_state == SMB_REPEAT_START) { I2C_START(controller); CPUTS("ST"); } /* Enable SMB interrupt and New Address Match interrupt source */ i2c_interrupt(controller, 1); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, p_status->timeout_us); /* Handle bus timeout */ if ((events & TASK_EVENT_I2C_IDLE) == 0) { /* Recovery I2C controller */ i2c_recovery(controller); p_status->err_code = SMB_TIMEOUT_ERROR; } /* * In slave write operation, NACK is OK, otherwise it is a problem */ else if (p_status->err_code == SMB_BUS_ERROR || p_status->err_code == SMB_MASTER_NO_ADDRESS_MATCH){ i2c_recovery(controller); } /* Wait till STOP condition is generated */ if (p_status->err_code == SMB_OK && i2c_wait_stop_completed(controller, I2C_MIN_TIMEOUT) != EC_SUCCESS) { cprints(CC_I2C, "STOP fail! scl %02x is held by slave device!", controller); p_status->err_code = SMB_TIMEOUT_ERROR; } return p_status->err_code; }
test_mockable __keep int main(void) { #ifdef CONFIG_REPLACE_LOADER_WITH_BSS_SLOW /* * Now that we have started execution, we no longer need the loader. * Instead, variables placed in the .bss.slow section will use this * space. Therefore, clear out this region now. */ memset((void *)(CONFIG_PROGRAM_MEMORY_BASE + CONFIG_LOADER_MEM_OFF), 0, CONFIG_LOADER_SIZE); #endif /* defined(CONFIG_REPLACE_LOADER_WITH_BSS_SLOW) */ /* * Pre-initialization (pre-verified boot) stage. Initialization at * this level should do as little as possible, because verified boot * may need to jump to another image, which will repeat this * initialization. In particular, modules should NOT enable * interrupts. */ #ifdef CONFIG_BOARD_PRE_INIT board_config_pre_init(); #endif #ifdef CONFIG_MPU mpu_pre_init(); #endif /* Configure the pin multiplexers and GPIOs */ jtag_pre_init(); gpio_pre_init(); #ifdef CONFIG_BOARD_POST_GPIO_INIT board_config_post_gpio_init(); #endif /* * Initialize interrupts, but don't enable any of them. Note that * task scheduling is not enabled until task_start() below. */ task_pre_init(); /* * Initialize the system module. This enables the hibernate clock * source we need to calibrate the internal oscillator. */ system_pre_init(); system_common_pre_init(); #ifdef CONFIG_FLASH /* * Initialize flash and apply write protect if necessary. Requires * the reset flags calculated by system initialization. */ flash_pre_init(); #endif #if defined(CONFIG_CASE_CLOSED_DEBUG) /* * If the device is locked we assert PD_NO_DEBUG, preventing the EC * from interfering with the AP's access to the SPI flash. * The PD_NO_DEBUG signal is latched in hardware, so changing this * GPIO later has no effect. */ gpio_set_level(GPIO_PD_DISABLE_DEBUG, system_is_locked()); #endif /* Set the CPU clocks / PLLs. System is now running at full speed. */ clock_init(); /* * Initialize timer. Everything after this can be benchmarked. * get_time() and udelay() may now be used. usleep() requires task * scheduling, so cannot be used yet. Note that interrupts declared * via DECLARE_IRQ() call timer routines when profiling is enabled, so * timer init() must be before uart_init(). */ timer_init(); /* Main initialization stage. Modules may enable interrupts here. */ cpu_init(); #ifdef CONFIG_DMA /* Initialize DMA. Must be before UART. */ dma_init(); #endif /* Initialize UART. Console output functions may now be used. */ uart_init(); if (system_jumped_to_this_image()) { CPRINTS("UART initialized after sysjump"); } else { CPUTS("\n\n--- UART initialized after reboot ---\n"); CPUTS("[Reset cause: "); system_print_reset_flags(); CPUTS("]\n"); } CPRINTF("[Image: %s, %s]\n", system_get_image_copy_string(), system_get_build_info()); #ifdef CONFIG_BRINGUP ccprintf("\n\nWARNING: BRINGUP BUILD\n\n\n"); #endif #ifdef CONFIG_WATCHDOG /* * Intialize watchdog timer. All lengthy operations between now and * task_start() must periodically call watchdog_reload() to avoid * triggering a watchdog reboot. (This pretty much applies only to * verified boot, because all *other* lengthy operations should be done * by tasks.) */ watchdog_init(); #endif /* * Verified boot needs to read the initial keyboard state and EEPROM * contents. EEPROM must be up first, so keyboard_scan can toggle * debugging settings via keys held at boot. */ #ifdef CONFIG_EEPROM eeprom_init(); #endif #ifdef HAS_TASK_KEYSCAN keyboard_scan_init(); #endif #ifdef CONFIG_RWSIG /* * Check the RW firmware signature * and eventually jump to it if it is good. */ check_rw_signature(); #endif /* * Print the init time. Not completely accurate because it can't take * into account the time before timer_init(), but it'll at least catch * the majority of the time. */ CPRINTS("Inits done"); /* Launch task scheduling (never returns) */ return task_start(); }