Esempio n. 1
0
/**
 * 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);
}
Esempio n. 3
0
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]);
		}
	}
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
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);
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
/**
 * 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;
}
Esempio n. 8
0
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();
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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");
		}
	}
}
Esempio n. 11
0
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;
}
Esempio n. 12
0
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();
}