diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index f4413b4c..703d84cd 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -205,6 +205,7 @@ int dce_admin_init(struct tegra_dce *d) { int ret = 0; + d->boot_status |= DCE_EARLY_INIT_START; ret = dce_ipc_allocate_region(d); if (ret) { dce_err(d, "IPC region allocation failed"); @@ -217,11 +218,13 @@ int dce_admin_init(struct tegra_dce *d) goto err_channel_init; } + d->boot_status |= DCE_EARLY_INIT_DONE; return 0; err_channel_init: dce_ipc_free_region(d); err_ipc_reg_alloc: + d->boot_status |= DCE_EARLY_INIT_FAILED; return ret; } @@ -482,6 +485,7 @@ int dce_start_admin_seq(struct tegra_dce *d) if (!msg) return -1; + d->boot_status |= DCE_FW_ADMIN_SEQ_START; ret = dce_admin_send_cmd_ver(d, msg); if (ret) { dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); @@ -490,16 +494,19 @@ int dce_start_admin_seq(struct tegra_dce *d) ret = dce_admin_setup_clients_ipc(d, msg); if (ret) { - dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + dce_err(d, "RPC failed for DCE_ADMIN_CMD_IPC_CREATE"); goto out; } ret = dce_admin_send_rm_bootstrap(d, msg); if (ret) { - dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + dce_err(d, "RPC failed for DCE_ADMIN_CMD_RM_BOOTSTRAP"); goto out; } + d->boot_status |= DCE_FW_ADMIN_SEQ_DONE; out: dce_admin_free_message(d, msg); + if (ret) + d->boot_status |= DCE_FW_ADMIN_SEQ_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-ast.c b/drivers/platform/tegra/dce/dce-ast.c index 18d58dc2..cdfc1065 100644 --- a/drivers/platform/tegra/dce/dce-ast.c +++ b/drivers/platform/tegra/dce/dce-ast.c @@ -576,10 +576,12 @@ void dce_config_ast(struct tegra_dce *d) u32 slave_addr; u64 master_addr; + d->boot_status |= DCE_AST_CONFIG_START; slave_addr = dce_get_fw_dce_addr(d); if (!d->fw_data) { - dce_err(d, "No fw_data present"); + dce_err(d, "DCE_BOOT_FAILED: No fw_data present"); + d->boot_status |= DCE_AST_CONFIG_FAILED; return; } @@ -599,4 +601,5 @@ void dce_config_ast(struct tegra_dce *d) ast_slave_addr_fn[i][j](d, slave_addr); } } + d->boot_status |= DCE_AST_CONFIG_DONE; } diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 272ba6e7..6f0a3b17 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -96,7 +96,10 @@ int dce_wait_boot_complete(struct tegra_dce *d) boot_done: if (!ret) { dce_set_boot_complete(d, true); + d->boot_status |= DCE_FW_EARLY_BOOT_DONE; dce_info(d, "dce is ready to receive bootstrap commands"); + } else { + d->boot_status |= DCE_FW_EARLY_BOOT_FAILED; } return ret; } @@ -702,6 +705,7 @@ int dce_start_bootstrap_flow(struct tegra_dce *d) u32 val; int ret = 0; + d->boot_status |= DCE_FW_BOOTSTRAP_START; ret = dce_send_version_cmd(d); if (ret) { dce_err(d, "Sending of bootstrap cmd VERSION failed"); @@ -744,9 +748,11 @@ int dce_start_bootstrap_flow(struct tegra_dce *d) goto err_sending; } + d->boot_status |= DCE_FW_BOOTSTRAP_DONE; return 0; err_sending: dce_err(d, "Bootstrap process failed"); + d->boot_status |= DCE_FW_BOOTSTRAP_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index bfff504c..cb34ec18 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -289,6 +289,128 @@ static const struct file_operations boot_dce_fops = { .write = dbg_dce_boot_dce_fops_write, }; +static ssize_t dbg_dce_boot_status_fops_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + u32 last_status; + ssize_t len = 0; + unsigned long *addr; + struct tegra_dce *d = file->private_data; + u32 boot_status = d->boot_status; + hsp_sema_t ss = dce_ss_get_state(d, DCE_BOOT_SEMA); + + if (ss & DCE_BOOT_COMPLETE) + goto core_boot_done; + + /* Clear BOOT_COMPLETE bit and bits set by OS */ + ss &= ~(DCE_OS_BITMASK | DCE_BOOT_COMPLETE); + addr = (unsigned long *)&ss; + last_status = DCE_BIT(find_first_bit(addr, 32)); + + switch (last_status) { + case DCE_HALTED: + strcpy(buf, "DCE_HALTED"); + break; + case DCE_BOOT_TCM_COPY: + strcpy(buf, "TCM_COPY"); + break; + case DCE_BOOT_HW_INIT: + strcpy(buf, "HW_INIT"); + break; + case DCE_BOOT_MPU_INIT: + strcpy(buf, "MPU_INIT:"); + break; + case DCE_BOOT_CACHE_INIT: + strcpy(buf, "CACHE_INIT"); + break; + case DCE_BOOT_R5_INIT: + strcpy(buf, "R5_INIT"); + break; + case DCE_BOOT_DRIVER_INIT: + strcpy(buf, "DRIVER_INIT"); + break; + case DCE_BOOT_MAIN_STARTED: + strcpy(buf, "MAIN_STARTED"); + break; + case DCE_BOOT_TASK_INIT_START: + strcpy(buf, "TASK_INIT_STARTED"); + break; + case DCE_BOOT_TASK_INIT_DONE: + strcpy(buf, "TASK_INIT_DONE"); + break; + default: + strcpy(buf, "STATUS_UNKNOWN"); + break; + } + goto done; + +core_boot_done: + /* Clear DCE_STATUS_FAILED bit get actual failure reason*/ + boot_status &= ~DCE_STATUS_FAILED; + addr = (unsigned long *)&boot_status; + last_status = DCE_BIT(find_first_bit(addr, 32)); + + switch (last_status) { + case DCE_FW_BOOT_DONE: + strcpy(buf, "DCE_FW_BOOT_DONE"); + break; + case DCE_FW_ADMIN_SEQ_DONE: + strcpy(buf, "DCE_FW_ADMIN_SEQ_DONE"); + break; + case DCE_FW_ADMIN_SEQ_FAILED: + strcpy(buf, "DCE_FW_ADMIN_SEQ_FAILED"); + break; + case DCE_FW_ADMIN_SEQ_START: + strcpy(buf, "DCE_FW_ADMIN_SEQ_STARTED"); + break; + case DCE_FW_BOOTSTRAP_DONE: + strcpy(buf, "DCE_FW_BOOTSTRAP_DONE"); + break; + case DCE_FW_BOOTSTRAP_FAILED: + strcpy(buf, "DCE_FW_BOOTSTRAP_FAILED"); + break; + case DCE_FW_BOOTSTRAP_START: + strcpy(buf, "DCE_FW_BOOTSTRAP_STARTED"); + break; + case DCE_FW_EARLY_BOOT_FAILED: + strcpy(buf, "DCE_FW_EARLY_BOOT_FAILED"); + break; + case DCE_FW_EARLY_BOOT_DONE: + strcpy(buf, "DCE_FW_EARLY_BOOT_DONE"); + break; + case DCE_AST_CONFIG_DONE: + strcpy(buf, "DCE_AST_CONFIG_DONE"); + break; + case DCE_AST_CONFIG_START: + strcpy(buf, "DCE_AST_CONFIG_STARTED"); + break; + case DCE_EARLY_INIT_DONE: + strcpy(buf, "DCE_EARLY_INIT_DONE"); + break; + case DCE_EARLY_INIT_FAILED: + strcpy(buf, "DCE_EARLY_INIT_FAILED"); + break; + case DCE_EARLY_INIT_START: + strcpy(buf, "DCE_EARLY_INIT_STARTED"); + break; + default: + strcpy(buf, "STATUS_UNKNOWN"); + } + +done: + len = strlen(buf); + buf[len] = '\0'; + dce_info(d, "boot status:%s status_val:0x%x\n", buf, d->boot_status); + return simple_read_from_buffer(user_buf, count, ppos, buf, len + 1); +} + +static const struct file_operations boot_status_fops = { + .open = simple_open, + .read = dbg_dce_boot_status_fops_read, +}; + void dce_remove_debug(struct tegra_dce *d) { struct dce_device *d_dev = dce_device_from_dce(d); @@ -381,32 +503,32 @@ void dce_init_debug(struct tegra_dce *d) return; retval = debugfs_create_file("load_fw", 0444, - d_dev->debugfs, d, &load_firmware_fops); + d_dev->debugfs, d, &load_firmware_fops); if (!retval) goto err_handle; retval = debugfs_create_file("config_ast", 0444, - d_dev->debugfs, d, &config_ast_fops); + d_dev->debugfs, d, &config_ast_fops); if (!retval) goto err_handle; - retval = debugfs_create_file("reset_dce", 0444, - d_dev->debugfs, d, &reset_dce_fops); + retval = debugfs_create_file("reset", 0444, + d_dev->debugfs, d, &reset_dce_fops); if (!retval) goto err_handle; - retval = debugfs_create_file("boot_dce", 0444, - d_dev->debugfs, d, &boot_dce_fops); + retval = debugfs_create_file("boot", 0444, + d_dev->debugfs, d, &boot_dce_fops); if (!retval) goto err_handle; - retval = debugfs_create_bool("boot_status", 0644, - d_dev->debugfs, &d->boot_complete); + retval = debugfs_create_file("boot_status", 0444, + d_dev->debugfs, d, &boot_status_fops); if (!retval) goto err_handle; retval = debugfs_create_file("dump_hsp_regs", 0444, - d_dev->debugfs, d, &dump_hsp_regs_fops); + d_dev->debugfs, d, &dump_hsp_regs_fops); if (!retval) goto err_handle; diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 5ff9d8df..e6879537 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -51,6 +51,7 @@ err_worker_thread_init: err_admin_interface_init: dce_boot_interface_deinit(d); err_boot_interface_init: + d->boot_status |= DCE_STATUS_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 43551523..6a137607 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -192,15 +192,14 @@ static int dce_worker(void *arg) dce_info(d, "Starting DCE Worker Thread..."); ret = dce_wait_boot_complete(d); if (ret) { - dce_warn(d, "DCE Boot didn't complete"); + dce_warn(d, "DCE_BOOT_FAILED: Boot didn't complete"); goto worker_exit; } dce_info(d, "DCE Ready to bootstrap ..."); - ret = dce_start_bootstrap_flow(d); if (ret) { - dce_warn(d, "DCE Bootstrap flow didn't complete"); + dce_warn(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); goto worker_exit; } @@ -211,20 +210,28 @@ static int dce_worker(void *arg) dce_info(d, "DCE Admin Channel Reset Complete..."); ret = dce_start_admin_seq(d); - if (ret) - dce_warn(d, "DCE Admin flow didn't complete"); + if (ret) { + dce_warn(d, "DCE_BOOT_FAILED: Admin flow didn't complete"); + } else { + d->boot_status |= DCE_FW_BOOT_DONE; + dce_info(d, "DCE_BOOT_DONE"); + } dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); while ((w->c_state != STATE_DCE_WORKER_ABORTED) || (!dce_thread_should_stop(&w->wrk_thread))) { - if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) + if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) { dce_handle_dce_error(d); + d->boot_status |= DCE_STATUS_FAILED; + } } worker_exit: if (w->c_state == STATE_DCE_WORKER_ABORTED) dce_info(d, "Exiting Dce Worker Thread"); + if (ret) + d->boot_status |= DCE_STATUS_FAILED; return 0; } diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index dec9fa21..dbfa2a91 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -29,6 +29,33 @@ #define DCE_MAX_CPU_IRQS 4 +/** + * DCE Boot Status : DCE Driver Init States + * + * @DCE_EARLY_INIT_* : Driver Init Before Bootstrap + * @DCE_AST_CONFIG_* : Used When DCE-CPU Driver Loads the Firmware + */ +#define DCE_EARLY_INIT_START DCE_BIT(31) +#define DCE_EARLY_INIT_FAILED DCE_BIT(30) +#define DCE_EARLY_INIT_DONE DCE_BIT(29) +#define DCE_AST_CONFIG_START DCE_BIT(28) +#define DCE_AST_CONFIG_FAILED DCE_BIT(27) +#define DCE_AST_CONFIG_DONE DCE_BIT(26) +/** + * DCE Boot Status: FW Boot States + */ +#define DCE_FW_EARLY_BOOT_FAILED DCE_BIT(15) +#define DCE_FW_EARLY_BOOT_DONE DCE_BIT(14) +#define DCE_FW_BOOTSTRAP_START DCE_BIT(13) +#define DCE_FW_BOOTSTRAP_FAILED DCE_BIT(12) +#define DCE_FW_BOOTSTRAP_DONE DCE_BIT(11) +#define DCE_FW_ADMIN_SEQ_START DCE_BIT(10) +#define DCE_FW_ADMIN_SEQ_FAILED DCE_BIT(9) +#define DCE_FW_ADMIN_SEQ_DONE DCE_BIT(8) +#define DCE_FW_BOOT_DONE DCE_BIT(1) +#define DCE_STATUS_FAILED DCE_BIT(0) +#define DCE_STATUS_UNKNOWN ((u32)(0)) + struct tegra_dce; /** @@ -132,6 +159,10 @@ struct tegra_dce { * @d_clients - Stores all dce clients data. */ struct tegra_dce_client_ipc *d_clients[DCE_CLIENT_IPC_TYPE_MAX]; + /** + * @boot_status - u32 variable to store dce's boot status. + */ + u32 boot_status; /** * @boot_complete - Boolean variable to store dce's boot status. */ diff --git a/drivers/platform/tegra/dce/include/interface/dce-interface.h b/drivers/platform/tegra/dce/include/interface/dce-interface.h index 50a1512a..b8b9c0c1 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-interface.h +++ b/drivers/platform/tegra/dce/include/interface/dce-interface.h @@ -41,6 +41,7 @@ typedef uint32_t hsp_sema_t; #define DCE_WAIT_DEBUG DCE_BIT(30) // wait in debug loop #define DCE_SC7_RESUME DCE_BIT(29) // resume using saved SC7 state // rather than a full restart +#define DCE_OS_BITMASK (DCE_BOOT_INT | DCE_WAIT_DEBUG | DCE_SC7_RESUME) /* * Bits set by the R5 and examined by the OS