* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifdef CONFIG_GENERIC_BUG
#undef CONFIG_GENERIC_BUG
#endif
#ifdef CONFIG_BUG
#undef CONFIG_BUG
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
#undef CONFIG_DEBUG_BUGVERBOSE
#endif
#include "ka_errno_pub.h"
#include "ka_fs_pub.h"
#include "ka_barrier_pub.h"
#include "ka_driver_pub.h"
#include "ka_common_pub.h"
#include "devdrv_device_load.h"
#include "devdrv_ctrl.h"
#include "devdrv_pci.h"
#include "devdrv_dma.h"
#include "devdrv_util.h"
#include "devdrv_mem_alloc.h"
#ifdef DRV_UT
#define STATIC
#else
#define STATIC static
#endif
STATIC int g_file_check_count[HISI_CHIP_NUM] = {0, 0, 0, 0, 0};
STATIC char g_line_buf[DEVDRV_STR_MAX_LEN] = {0};
STATIC char g_devdrv_sdk_path[DEVDRV_STR_MAX_LEN] = {0};
STATIC char *g_davinci_config_file = "/lib/davinci.conf";
struct devdrv_load_file g_load_file[HISI_CHIP_NUM][DEVDRV_BLOCKS_NUM];
void devdrv_set_device_boot_status(struct devdrv_pci_ctrl *pci_ctrl, u32 status);
#ifndef ka_mm_writeq
static inline void ka_mm_writeq(u64 value, volatile void *addr)
{
*(volatile u64 *)addr = value;
}
#endif
#ifndef ka_mm_readq
static inline u64 ka_mm_readq(void __ka_mm_iomem *addr)
{
return ka_mm_readl(addr) + ((u64)ka_mm_readl(addr + 4) << 32);
}
#endif
STATIC void *devdrv_load_mem_alloc(ka_device_t *dev, size_t size, ka_dma_addr_t *dma_addr, ka_gfp_t gfp)
{
int connect_protocol = devdrv_get_connect_protocol_by_dev(dev);
void *addr = NULL;
if (connect_protocol == CONNECT_PROTOCOL_PCIE) {
addr = devdrv_ka_dma_alloc_coherent(dev, size, dma_addr, gfp);
} else {
addr = devdrv_kzalloc(size, gfp);
if (addr == NULL) {
return NULL;
}
*dma_addr = ka_mm_dma_map_single(dev, addr, size, KA_DMA_BIDIRECTIONAL);
if (ka_mm_dma_mapping_error(dev, *dma_addr) != 0) {
devdrv_kfree(addr);
return NULL;
}
}
return addr;
}
STATIC void devdrv_load_mem_free(ka_device_t *dev, size_t size, void *addr, ka_dma_addr_t dma_addr)
{
int connect_protocol = devdrv_get_connect_protocol_by_dev(dev);
if (connect_protocol == CONNECT_PROTOCOL_PCIE) {
devdrv_ka_dma_free_coherent(dev, size, addr, dma_addr);
} else {
ka_mm_dma_unmap_single(dev, dma_addr, size, KA_DMA_BIDIRECTIONAL);
devdrv_kfree(addr);
}
}
STATIC void devdrv_load_free_one(ka_device_t *dev, struct devdrv_load_addr_pair *addr_pair, int num)
{
int i;
for (i = 0; i < num; i++) {
devdrv_load_mem_free(dev, addr_pair[i].size, addr_pair[i].addr, addr_pair[i].dma_addr);
addr_pair[i].addr = NULL;
}
return;
}
STATIC int devdrv_load_dma_alloc(ka_device_t *dev, struct devdrv_load_addr_pair *addr_pair, size_t size, int depth)
{
struct devdrv_load_addr_pair *p_addr = addr_pair;
size_t part1_size;
size_t part2_size;
int block_num;
int num;
depth--;
p_addr->addr = devdrv_load_mem_alloc(dev, size, &p_addr->dma_addr, KA_GFP_KERNEL);
if (p_addr->addr == NULL) {
if (depth <= 0) {
devdrv_err("Alloc dma coherent failed. (driver_name=\"%s\"; size=%llu)\n",
ka_driver_dev_driver_string(dev), (u64)size);
goto direct_out;
}
* otherwise bios will be wrong when data copy */
part1_size = size >> 1;
part1_size = DEVDRV_ALIGN(part1_size, DEVDRV_ADDR_ALIGN);
if (part1_size >= size) {
devdrv_err("Dma memory not enough. (driver_name=\"%s\")\n", ka_driver_dev_driver_string(dev));
goto direct_out;
}
num = devdrv_load_dma_alloc(dev, p_addr, part1_size, depth);
if (num < 0) {
goto direct_out;
}
block_num = num;
p_addr += num;
part2_size = size - part1_size;
num = devdrv_load_dma_alloc(dev, p_addr, part2_size, depth);
if (num < 0) {
goto free_out;
}
block_num += num;
} else {
p_addr->size = (u64)size;
block_num = 1;
}
return block_num;
free_out:
devdrv_load_free_one(dev, addr_pair, block_num);
direct_out:
return -ENOMEM;
}
STATIC int devdrv_load_contiguous_alloc(ka_device_t *dev, struct devdrv_load_addr_pair *load_addr, int len,
size_t size)
{
struct devdrv_load_addr_pair *addr_pair = NULL;
size_t remain_size;
size_t alloc_size;
int pairs_num;
int num;
int i;
addr_pair = devdrv_kzalloc(sizeof(*addr_pair) * DEVDRV_DMA_CACHE_NUM, KA_GFP_KERNEL);
if (addr_pair == NULL) {
devdrv_err("addr_pair devdrv_kzalloc failed.\n");
goto direct_out;
}
remain_size = size;
pairs_num = 0;
while (remain_size > 0) {
alloc_size = (remain_size > DEVDRV_BLOCKS_SIZE) ? DEVDRV_BLOCKS_SIZE : remain_size;
remain_size -= alloc_size;
num = devdrv_load_dma_alloc(dev, addr_pair, alloc_size, DEVDRV_DMA_ALLOC_DEPTH);
if (num <= 0) {
devdrv_err("Call devdrv_load_dma_alloc failed.\n");
goto dma_alloc_err;
}
if (pairs_num + num >= len) {
devdrv_err("Dma buffer is not enough.\n");
goto need_more_buf_err;
}
for (i = 0; i < num; i++, pairs_num++) {
load_addr[pairs_num].addr = addr_pair[i].addr;
load_addr[pairs_num].dma_addr = addr_pair[i].dma_addr;
load_addr[pairs_num].size = addr_pair[i].size;
}
}
devdrv_kfree(addr_pair);
addr_pair = NULL;
return pairs_num;
need_more_buf_err:
devdrv_load_free_one(dev, addr_pair, num);
dma_alloc_err:
devdrv_load_free_one(dev, load_addr, pairs_num);
devdrv_kfree(addr_pair);
addr_pair = NULL;
direct_out:
return -ENOMEM;
}
STATIC loff_t devdrv_get_i_size_read(ka_file_t *p_file)
{
return ka_fs_i_size_read(ka_fs_file_inode(p_file));
}
* read & write register with little endian.
*/
STATIC void devdrv_load_notice(struct devdrv_agent_load *loader, struct devdrv_load_blocks *blocks, u64 flag)
{
void __ka_mm_iomem *sram_complet_addr = NULL;
void __ka_mm_iomem *sram_reg = NULL;
u64 value = 0;
u64 j;
sram_complet_addr = loader->mem_sram_base;
sram_reg = sram_complet_addr + sizeof(u64);
if (blocks->blocks_num == 0) {
devdrv_err("blocks_num is zero. (dev_id=%u)\n", loader->dev_id);
return;
}
ka_mm_writeq(blocks->blocks_valid_num, sram_reg);
value = ka_mm_readq(sram_reg);
if (value != blocks->blocks_valid_num) {
devdrv_err("block_num read back error. (dev_id=%u; block_num=0x%llx; readback=0x%llx)\n",
loader->dev_id, blocks->blocks_valid_num, value);
return;
}
sram_reg = sram_reg + sizeof(u64);
for (j = 0; j < blocks->blocks_valid_num; j++) {
ka_mm_writeq(blocks->blocks_addr[j].dma_addr, sram_reg);
value = ka_mm_readq(sram_reg);
if (value != blocks->blocks_addr[j].dma_addr) {
devdrv_err("dma_addr read back error. (dev_id=%u; dma_addr=0x%llx; readback=0x%llx)\n",
loader->dev_id, blocks->blocks_addr[j].dma_addr, value);
return;
}
sram_reg = sram_reg + sizeof(u64);
ka_mm_writeq(blocks->blocks_addr[j].data_size, sram_reg);
value = ka_mm_readq(sram_reg);
if (value != blocks->blocks_addr[j].data_size) {
devdrv_err("data_size read back error. (dev_id=%u; data_size=0x%llx; readback=0x%llx)\n",
loader->dev_id, blocks->blocks_addr[j].data_size, value);
return;
}
sram_reg = sram_reg + sizeof(u64);
}
ka_wmb();
ka_mm_writeq(flag, sram_complet_addr);
value = ka_mm_readq(sram_complet_addr);
if (value != flag) {
devdrv_warn("flag read back. (dev_id=%u; flag=0x%llx; readback=0x%llx)\n", loader->dev_id, flag, value);
}
return;
}
STATIC void devdrv_get_data_size(struct devdrv_load_blocks *blocks, u64 file_size, u64 cnt)
{
if (cnt >= DEVDRV_BLOCKS_ADDR_PAIR_NUM) {
devdrv_err("Input parameter is invalid. (cnt=%lld)", cnt);
return;
}
if (file_size > blocks->blocks_addr[cnt].size) {
blocks->blocks_addr[cnt].data_size = blocks->blocks_addr[cnt].size;
} else {
blocks->blocks_addr[cnt].data_size = file_size;
}
}
STATIC u64 devdrv_get_wr_flag(u64 remain_size, u64 trans_size)
{
u64 flag_w;
if (remain_size == trans_size) {
flag_w = DEVDRV_SEND_FINISH;
} else {
flag_w = DEVDRV_SEND_PATT_FINISH;
}
return flag_w;
}
STATIC void devdrv_check_bar_space_cfg(struct devdrv_pci_ctrl *pci_ctrl)
{
u32 bar_offset_h = 0;
u32 bar_offset_l = 0;
u64 mem_bar_offset;
u64 io_bar_offset;
u64 rsv_mem_bar_offset;
u32 cfg_cmdsts = 0;
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_CFG_CMDSTS_REG, &cfg_cmdsts);
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR0_CFG_REG, &bar_offset_l);
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR1_CFG_REG, &bar_offset_h);
mem_bar_offset = ((u64)bar_offset_h << DEVDRV_BAR_CFG_OFFSET) | bar_offset_l;
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR2_CFG_REG, &bar_offset_l);
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR3_CFG_REG, &bar_offset_h);
io_bar_offset = ((u64)bar_offset_h << DEVDRV_BAR_CFG_OFFSET) | bar_offset_l;
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR4_CFG_REG, &bar_offset_l);
ka_pci_read_config_dword(pci_ctrl->pdev, DEVDRV_PCIE_BAR5_CFG_REG, &bar_offset_h);
rsv_mem_bar_offset = ((u64)bar_offset_h << DEVDRV_BAR_CFG_OFFSET) | bar_offset_l;
devdrv_info("Get bar info. (dev_id=%u; bar_cfg=0x%x; offset_bar0=0x%llx; val=0x%x; "
"bar2=0x%llx; val=0x%x; bar4=0x%llx)\n",
pci_ctrl->dev_id, cfg_cmdsts, mem_bar_offset, ka_mm_readl((volatile unsigned char *)pci_ctrl->io_base),
io_bar_offset, ka_mm_readl((volatile unsigned char *)pci_ctrl->msi_base), rsv_mem_bar_offset);
}
STATIC void devdrv_check_bar_space_access(struct devdrv_pci_ctrl *pci_ctrl)
{
devdrv_check_bar_space_cfg(pci_ctrl);
}
STATIC int devdrv_get_boot_mode_flag(struct devdrv_pci_ctrl *pci_ctrl)
{
struct devdrv_agent_load *loader = NULL;
u64 *sram_complet_addr = NULL;
u64 flag_r = 0;
int count = 0;
enum devdrv_load_wait_mode load_wait_mode;
loader = pci_ctrl->agent_loader;
sram_complet_addr = loader->mem_sram_base;
load_wait_mode = pci_ctrl->ops.get_load_wait_mode(pci_ctrl);
if (load_wait_mode != DEVDRV_LOAD_WAIT_UNKNOWN) {
loader->load_wait_mode = (int)load_wait_mode;
return 0;
}
while (1) {
flag_r = ka_mm_readq(sram_complet_addr);
if ((flag_r == DEVDRV_ABNORMAL_BOOT_MODE) || (flag_r == DEVDRV_NORMAL_BOOT_MODE) ||
(flag_r == DEVDRV_SLOW_BOOT_MODE)) {
break;
}
count++;
if (count % DEVDRV_GET_FLAG_COUNT == 0) {
devdrv_info("Wait boot mode from bios. (dev_id=%u; flag_r=0x%llx)\n", loader->dev_id, flag_r);
}
if (count >= DEVDRV_GET_FLAG_COUNT) {
break;
}
ka_system_msleep(DEVDRV_DELAY_TIME);
}
devdrv_info("Get boot mode from bios. (dev_id=%u; flag_r=0x%llx)\n", loader->dev_id, flag_r);
if (flag_r == DEVDRV_SLOW_BOOT_MODE) {
loader->load_wait_mode = DEVDRV_LOAD_WAIT_FOREVER;
} else if (flag_r == DEVDRV_NORMAL_BOOT_MODE) {
loader->load_wait_mode = DEVDRV_LOAD_WAIT_INTERVAL;
} else {
devdrv_err("Get boot mode from bios error. dev_id=%u; flag_r=0x%llx)\n", loader->dev_id, flag_r);
if (flag_r == DEVDRV_ABNORMAL_BOOT_MODE) {
devdrv_check_bar_space_access(pci_ctrl);
}
return -EINVAL;
}
return 0;
}
STATIC void devdrv_check_load_file(u32 devid, u64 flag)
{
if (flag == DEVDRV_TEE_CHECK_FAIL) {
devdrv_err("Load file check tee failed. (dev_id=%u)\n", devid);
} else if (flag == DEVDRV_IMAGE_CHECK_FAIL) {
devdrv_err("Load file check image failed. (dev_id=%u)\n", devid);
} else if (flag == DEVDRV_FILESYSTEM_CHECK_FAIL) {
devdrv_err("Load file check filesystem failed. (dev_id=%u)\n", devid);
} else {
return;
}
return;
}
STATIC void devdrv_get_rd_flag(struct devdrv_agent_load *loader, u64 *addr)
{
u64 flag_r = 0;
int count = 0;
int ret;
int count_f = 0;
When device boot, the rdy would change. If 0xA20000 is change from 66 77 to
others, then consider device has boot, so it's ready. */
while (1) {
if (loader->load_abort == DEVDRV_LOAD_ABORT) {
devdrv_info("Load abort. (dev_id=%u)\n", loader->dev_id);
return;
}
if (ka_base_atomic_read(&loader->load_flag) == DEVDRV_LOAD_SUCCESS) {
devdrv_info("Device load success. (dev_id=%u)\n", loader->dev_id);
return;
}
flag_r = ka_mm_readq(addr);
ret = (flag_r == DEVDRV_RECV_FINISH) || (flag_r == DEVDRV_TEE_CHECK_FAIL) ||
(flag_r == DEVDRV_IMAGE_CHECK_FAIL) || (flag_r == DEVDRV_FILESYSTEM_CHECK_FAIL);
if (ret != 0) {
devdrv_info("Receive BIOS flag. (dev_id=%u; flag=0x%llx; count=%d)\n", loader->dev_id, flag_r, count);
break;
}
count_f++;
if ((count_f >= DEVDRV_GET_FLAG_COUNT) &&
((flag_r != DEVDRV_SEND_FINISH) && (flag_r != DEVDRV_SEND_PATT_FINISH))) {
devdrv_info("Check flag change, after 2 second. (dev_id=%u; flag=0x%llx)\n", loader->dev_id, flag_r);
break;
}
if (loader->load_wait_mode == DEVDRV_LOAD_WAIT_INTERVAL) {
count++;
if (count >= DEVDRV_LOAD_FILE_CHECK_CNT) {
devdrv_err("Load timeout. (dev_id=%u; flag_r=0x%llx)\n", loader->dev_id, flag_r);
devdrv_notify_blackbox_err(loader->dev_id, DEVDRV_SYSTEM_START_FAIL);
break;
}
}
ka_system_msleep(DEVDRV_LOAD_FILE_CHECK_TIME);
}
devdrv_check_load_file(loader->dev_id, flag_r);
}
STATIC u64 devdrv_get_load_block_size(u64 remain_size, struct devdrv_load_blocks *blocks)
{
u64 translated_size;
u64 file_size;
u64 i;
if (remain_size >= DEVDRV_BLOCKS_STATIC_SIZE) {
blocks->blocks_valid_num = blocks->blocks_num;
translated_size = DEVDRV_BLOCKS_STATIC_SIZE;
for (i = 0; i < blocks->blocks_valid_num; i++) {
blocks->blocks_addr[i].data_size = blocks->blocks_addr[i].size;
}
} else {
translated_size = remain_size;
file_size = remain_size;
i = 0;
while ((file_size > 0) && (i < DEVDRV_BLOCKS_ADDR_PAIR_NUM)) {
devdrv_get_data_size(blocks, file_size, i);
file_size = file_size - blocks->blocks_addr[i].data_size;
i++;
}
blocks->blocks_valid_num = i;
}
return translated_size;
}
STATIC bool devdrv_chk_load_abort(int status)
{
return (status == DEVDRV_LOAD_ABORT);
}
STATIC int devdrv_load_file_copy(struct devdrv_agent_load *loader, const char *name, struct devdrv_load_blocks *blocks)
{
u64 *sram_complet_addr = NULL;
ka_file_t *p_file = NULL;
u64 translated_size, remain_size;
loff_t offset, size;
ssize_t len;
u64 flag_w;
int ret;
u64 i;
static int file_exist_flag = 0;
int count = DEVDRV_WAIT_LOAD_FILE_TIME;
int wait_file_cnt = 0;
if (devdrv_chk_load_abort(loader->load_abort)) {
devdrv_info("Load abort. (dev_id=%u)\n", loader->dev_id);
return 0;
}
sram_complet_addr = loader->mem_sram_base;
retry:
p_file = ka_fs_filp_open(name, KA_O_RDONLY | KA_O_LARGEFILE, 0);
if (KA_IS_ERR_OR_NULL(p_file)) {
if ((wait_file_cnt < count) && (file_exist_flag == 0)) {
wait_file_cnt++;
ka_system_ssleep(1);
goto retry;
}
ret = KA_PTR_ERR(p_file);
goto direct_out;
}
file_exist_flag = 1;
size = devdrv_get_i_size_read(p_file);
devdrv_info("Get file size. (dev_id=%u; file_name=\"%s\"; size=%lld)\n", loader->dev_id, name, size);
if (size <= 0) {
ret = -EIO;
devdrv_err("Get file size error. (dev_id=%u; file_name=\"%s\"; size=%lld)\n", loader->dev_id, name, size);
goto close_out;
}
remain_size = (u64)size;
offset = 0;
while (remain_size > 0) {
devdrv_get_rd_flag(loader, sram_complet_addr);
if (devdrv_chk_load_abort(loader->load_abort)) {
devdrv_info("Device load abort, not read file. (dev_id=%u)\n", loader->dev_id);
ret = 0;
goto free_out;
}
translated_size = devdrv_get_load_block_size(remain_size, blocks);
for (i = 0; i < blocks->blocks_valid_num; i++) {
len = ka_fs_read_file(p_file, &offset, blocks->blocks_addr[i].addr,
(size_t)blocks->blocks_addr[i].data_size);
if (len < 0) {
ret = -EIO;
devdrv_err("File read error. (dev_id=%u; len=%ld)\n", loader->dev_id, (long)len);
goto free_out;
}
if ((u64)len != blocks->blocks_addr[i].data_size) {
ret = -EIO;
devdrv_err("File read error. (dev_id=%u; len=%ld; size=%llu)\n",
loader->dev_id, (long)len, blocks->blocks_addr[i].size);
goto free_out;
}
}
flag_w = devdrv_get_wr_flag(remain_size, translated_size);
devdrv_load_notice(loader, blocks, flag_w);
devdrv_info("Notice BIOS to Load file. (dev_id=%u; file_name=\"%s\"; size=%llu; flag=0x%llx)\n",
loader->dev_id, name, blocks->blocks_valid_num, flag_w);
remain_size -= translated_size;
}
if (offset != size) {
ret = -EIO;
devdrv_err("File read error. (dev_id=%u; offset=%lld; size=%lld)\n", loader->dev_id, offset, size);
goto free_out;
}
ka_fs_filp_close(p_file, NULL);
p_file = NULL;
return 0;
free_out:
close_out:
ka_fs_filp_close(p_file, NULL);
p_file = NULL;
direct_out:
return ret;
}
STATIC void devdrv_load_blocks_free(struct devdrv_agent_load *loader)
{
struct devdrv_load_blocks *blocks = loader->blocks;
ka_device_t *dev = loader->dev;
if (loader->blocks == NULL) {
return;
}
if (blocks->blocks_num == 0) {
return;
}
devdrv_load_free_one(dev, blocks->blocks_addr, (int)blocks->blocks_num);
blocks->blocks_num = 0;
devdrv_kfree(blocks);
blocks = NULL;
loader->blocks = NULL;
return;
}
STATIC int devdrv_load_blocks_alloc(struct devdrv_agent_load *loader)
{
struct devdrv_load_blocks *blocks = NULL;
ka_device_t *dev = loader->dev;
int ret;
blocks = devdrv_kzalloc(sizeof(*blocks), KA_GFP_KERNEL);
if (blocks == NULL) {
devdrv_err("Blocks devdrv_kzalloc failed. (dev_id=%u)\n", loader->dev_id);
return -ENOMEM;
}
ret = devdrv_load_contiguous_alloc(dev, blocks->blocks_addr, DEVDRV_BLOCKS_ADDR_PAIR_NUM,
(size_t)DEVDRV_BLOCKS_STATIC_SIZE);
devdrv_info("Get block size. (dev_id=%u; block_num=%d; size=0x%x)\n",
loader->dev_id, ret, (u32)DEVDRV_BLOCKS_STATIC_SIZE);
if (ret <= 0) {
devdrv_err("Load file comm dma alloc failed. (dev_id=%u; ret=%d)\n", loader->dev_id, ret);
devdrv_kfree(blocks);
blocks = NULL;
return -ENOMEM;
}
loader->blocks = blocks;
blocks->blocks_num = (u64)ret;
return 0;
}
void devdrv_set_load_abort(struct devdrv_agent_load *agent_loader)
{
agent_loader->load_abort = DEVDRV_LOAD_ABORT;
}
STATIC int devdrv_load_file_trans(struct devdrv_agent_load *loader)
{
struct devdrv_load_blocks *blocks = NULL;
struct devdrv_pci_ctrl *pci_ctrl = loader->load_work.ctrl;
u32 chip_type = pci_ctrl->chip_type;
int i, ret;
ret = devdrv_load_blocks_alloc(loader);
if (ret != 0) {
devdrv_err("Blocks alloc failed. (dev_id=%u)\n", loader->dev_id);
return ret;
}
blocks = loader->blocks;
devdrv_info("Alloc block memory. (dev_id=%d; blocks_num=%llu; chip_type=%u)\n",
loader->dev_id, blocks->blocks_num, chip_type);
ret = devdrv_get_boot_mode_flag(pci_ctrl);
if (ret != 0) {
devdrv_err("Get boot mode flag failed, stop probe. (dev_id=%u)\n", loader->dev_id);
devdrv_load_blocks_free(loader);
return -ENOMEM;
}
devdrv_set_device_boot_status(pci_ctrl, DSMI_BOOT_STATUS_BIOS);
ka_mm_writeq(DEVDRV_RECV_FINISH, loader->mem_sram_base);
for (i = 0; i < DEVDRV_BLOCKS_NUM; i++) {
if (ka_base_strcmp(g_load_file[chip_type][i].file_name, "") == 0) {
continue;
}
ret = devdrv_load_file_copy(loader, g_load_file[chip_type][i].file_name, blocks);
if (ret < 0) {
if (g_load_file[chip_type][i].file_type == DEVDRV_CRITICAL_FILE) {
devdrv_err("File copy error. (dev_id=%u; file=%d; name=\"%s\"; %d)\n",
loader->dev_id, i, g_load_file[chip_type][i].file_name, ret);
devdrv_load_blocks_free(loader);
return ret;
}
if (g_load_file[chip_type][i].fail_mode == DEVDRV_NOTICE_BIOS) {
devdrv_get_rd_flag(loader, (u64 *)loader->mem_sram_base);
blocks->blocks_valid_num = 0;
devdrv_load_notice(loader, blocks, DEVDRV_SEND_FINISH);
devdrv_info("Skip file. (dev_id=%u; file=%d; name=\"%s\")\n",
loader->dev_id, i, g_load_file[chip_type][i].file_name);
}
}
}
devdrv_set_device_boot_status(pci_ctrl, DSMI_BOOT_STATUS_OS);
return 0;
}
STATIC void devdrv_load_finish(ka_work_struct_t *p_work)
{
struct devdrv_load_work *load_work = ka_container_of(p_work, struct devdrv_load_work, work);
struct devdrv_pci_ctrl *pci_ctrl = load_work->ctrl;
struct devdrv_agent_load *loader = pci_ctrl->agent_loader;
if (loader->load_timer.function != NULL) {
ka_system_del_timer_sync(&loader->load_timer);
} else {
devdrv_warn("Irq fb before timer build. (dev_id=%u)\n", pci_ctrl->dev_id);
}
loader->timer_expires = 0;
devdrv_load_blocks_free(loader);
devdrv_load_half_probe(pci_ctrl);
}
void devdrv_notify_blackbox_err(u32 devid, u32 code)
{
ka_timespec_t stamp;
stamp = ka_system_current_kernel_time();
if (g_black_box.callback != NULL) {
devdrv_info("Get blaclbox code. (dev_id=%u; blaclbox_code=%u)\n", devid, code);
g_black_box.callback(devid, code, stamp);
}
}
STATIC void devdrv_timer_task(ka_timer_list_t *t)
{
struct devdrv_agent_load *loader = ka_system_from_timer(loader, t, load_timer);
u32 device_id = loader->dev_id;
if (ka_base_atomic_read(&loader->load_flag) == DEVDRV_LOAD_SUCCESS) {
devdrv_info("Device os load success. (dev_id=%u)\n", loader->dev_id);
return;
}
if (loader->timer_remain-- > 0) {
loader->load_timer.expires = ka_jiffies + loader->timer_expires;
ka_system_add_timer(&loader->load_timer);
} else {
devdrv_err("Device os load failed. (loader_devid=%u; dev_id=%u)\n", loader->dev_id, device_id);
devdrv_notify_blackbox_err(device_id, DEVDRV_SYSTEM_START_FAIL);
}
return;
}
STATIC void devdrv_load_timer(struct devdrv_pci_ctrl *pci_ctrl)
{
struct devdrv_agent_load *loader = pci_ctrl->agent_loader;
loader->timer_remain = DEVDRV_TIMER_SCHEDULE_TIMES;
loader->timer_expires = DEVDRV_TIMER_EXPIRES;
ka_system_timer_setup(&loader->load_timer, devdrv_timer_task, 0);
loader->load_timer.expires = ka_jiffies + loader->timer_expires;
ka_system_add_timer(&loader->load_timer);
devdrv_info("Load timer add. (dev_id=%u)\n", loader->dev_id);
return;
}
ka_irqreturn_t devdrv_load_irq(int irq, void *data)
{
struct devdrv_pci_ctrl *pci_ctrl = (struct devdrv_pci_ctrl *)data;
struct devdrv_agent_load *loader = pci_ctrl->agent_loader;
u32 status = 0;
int ret;
ret = devdrv_get_device_boot_status_inner(pci_ctrl->dev_id, &status);
if ((ret != 0) || (status != DSMI_BOOT_STATUS_OS)) {
devdrv_warn("Recv irq before load files finish. (dev_id=%u; status=%u)\n", pci_ctrl->dev_id, status);
return KA_IRQ_HANDLED;
}
pci_ctrl->load_status_flag = DEVDRV_LOAD_SUCCESS_STATUS;
devdrv_notify_dev_init_status(pci_ctrl);
if (ka_base_atomic_read(&loader->load_flag) == DEVDRV_LOAD_SUCCESS) {
return KA_IRQ_HANDLED;
} else {
ka_base_atomic_set(&loader->load_flag, (int)DEVDRV_LOAD_SUCCESS);
}
ka_task_schedule_work(&loader->load_work.work);
return KA_IRQ_HANDLED;
}
STATIC char *devdrv_get_one_line(char *file_buf, u32 file_buf_len, char *line_buf_tmp, u32 buf_len)
{
u32 i;
u32 offset = 0;
*line_buf_tmp = '\0';
if (*file_buf == '\0') {
return NULL;
}
while ((*file_buf == ' ') || (*file_buf == '\t')) {
file_buf++;
offset++;
}
for (i = 0; i < buf_len; i++) {
if (*file_buf == '\n') {
line_buf_tmp[i] = '\0';
file_buf = file_buf + 1;
offset += 1;
goto exit;
}
if (*file_buf == '\0') {
line_buf_tmp[i] = '\0';
goto exit;
}
line_buf_tmp[i] = *file_buf;
file_buf++;
offset++;
}
line_buf_tmp[buf_len - 1] = '\0';
file_buf = file_buf + 1;
offset += 1;
exit:
if (offset >= file_buf_len) {
devdrv_err("File buf out of bound. (offset=%d; file_buf_len=%d)\n", offset, file_buf_len);
return NULL;
}
return file_buf;
}
STATIC void devdrv_get_val(const char *env_buf, u32 buf_len, char *env_val, u32 val_len)
{
const char *buf = env_buf;
u32 i;
*env_val = '\0';
for (i = 0; (i < val_len) && (i < buf_len); i++) {
if ((*buf == ' ') || (*buf == '\t') || (*buf == '\r') || (*buf == '\n') || (*buf == '\0')) {
env_val[i] = '\0';
break;
}
env_val[i] = *buf;
buf++;
}
env_val[val_len - 1] = '\0';
}
STATIC u32 devdrv_get_env_value(char *env_buf, u32 buf_len, const char *env_name, char *env_val, u32 val_len)
{
const char *buf = env_buf;
u32 len;
u32 offset = 0;
if (*buf == '#') {
return DEVDRV_CONFIG_FAIL;
}
len = (u32)ka_base_strlen(env_name);
if (ka_base_strncmp(buf, env_name, len) != 0) {
return DEVDRV_CONFIG_FAIL;
}
buf += len;
offset += len;
if ((*buf != ' ') && (*buf != '\t') && (*buf != '=')) {
return DEVDRV_CONFIG_FAIL;
}
while ((*buf == ' ') || (*buf == '\t')) {
buf++;
offset++;
}
if (*buf != '=') {
devdrv_err("Get value error.\n");
return DEVDRV_CONFIG_FAIL;
}
buf++;
offset++;
while ((*buf == ' ') || (*buf == '\t')) {
buf++;
offset++;
}
if (offset >= buf_len) {
devdrv_err("Buffer out of bound. (offset=%d; buf_len=%d)\n", offset, buf_len);
return DEVDRV_CONFIG_FAIL;
}
devdrv_get_val(buf, buf_len - offset, env_val, val_len);
return DEVDRV_CONFIG_OK;
}
STATIC int devdrv_read_cfg_file(char *file, char *buf, u32 buf_size)
{
ka_file_t *fp = NULL;
u32 len;
ssize_t len_integer;
int filesize;
loff_t pos = 0;
static int wait_cnt = 0;
retry:
fp = ka_fs_filp_open(file, KA_O_RDONLY, 0);
if (KA_IS_ERR(fp) || (fp == NULL)) {
if (wait_cnt < DEVDRV_OPEN_CFG_FILE_COUNT) {
wait_cnt++;
ka_system_msleep(DEVDRV_OPEN_CFG_FILE_TIME_MS);
goto retry;
}
devdrv_info("Can not open file. (file=\"%s\")\n", file);
return -EIO;
}
filesize = (int)devdrv_get_i_size_read(fp);
if (filesize >= (int)buf_size) {
devdrv_err("File is too large. (file=\"%s\")\n", file);
ka_fs_filp_close(fp, NULL);
fp = NULL;
return -EIO;
}
len_integer = ka_fs_read_file(fp, &pos, buf, (size_t)filesize);
if (len_integer != (ssize_t)(filesize)) {
devdrv_err("Read file fail. (file_size=%d)\n", filesize);
ka_fs_filp_close(fp, NULL);
fp = NULL;
return -EIO;
}
len = (u32)len_integer;
buf[len] = '\0';
ka_fs_filp_close(fp, NULL);
fp = NULL;
return filesize;
}
STATIC int devdrv_get_env_value_from_buf(char *buf, u32 filesize, const char *env_name,
char *env_val, u32 env_val_len)
{
u32 ret, len;
char *tmp_buf = NULL;
char tmp_val[DEVDRV_STR_MAX_LEN];
int ret_val;
u32 match_flag = 0;
tmp_buf = buf;
while (1) {
tmp_buf = devdrv_get_one_line(tmp_buf, filesize + 1, g_line_buf, (u32)sizeof(g_line_buf));
if (tmp_buf == NULL) {
break;
}
ret = devdrv_get_env_value(g_line_buf, DEVDRV_STR_MAX_LEN, env_name, tmp_val, (u32)sizeof(tmp_val));
if (ret != 0) {
continue;
}
len = (u32)ka_base_strlen(tmp_val);
if (env_val_len < (len + 1)) {
devdrv_err("Parameter env_val_len failed.\n");
return -EINVAL;
}
ret_val = strcpy_s(env_val, env_val_len, tmp_val);
if (ret_val != 0) {
devdrv_err("strcpy_s failed. (ret=%d)\n", ret_val);
return -EINVAL;
}
env_val[env_val_len - 1] = '\0';
match_flag = 1;
break;
}
return (match_flag == 1) ? 0 : -EINVAL;
}
u32 devdrv_get_env_value_from_file(char *file, const char *env_name, char *env_val, u32 env_val_len)
{
u32 ret;
int filesize, ret_val;
char *buf = NULL;
buf = (char*)devdrv_vzalloc(DEVDRV_MAX_FILE_SIZE);
if (buf == NULL) {
devdrv_err("Vzalloc failed\n");
return DEVDRV_CONFIG_FAIL;
}
filesize = devdrv_read_cfg_file(file, buf, DEVDRV_MAX_FILE_SIZE);
if (filesize < 0) {
ret = DEVDRV_CONFIG_FAIL;
goto out;
}
ret_val = devdrv_get_env_value_from_buf(buf, (u32)filesize, env_name, env_val, env_val_len);
if (ret_val != 0) {
ret = DEVDRV_CONFIG_NO_MATCH;
goto out;
}
ret = DEVDRV_CONFIG_OK;
out:
devdrv_vfree(buf);
buf = NULL;
return ret;
}
char *devdrv_get_config_file(void)
{
return g_davinci_config_file;
}
STATIC u32 devdrv_get_sdk_path(char *sdk_path, u32 path_buf_len)
{
return devdrv_get_env_value_from_file(g_davinci_config_file, "DAVINCI_HOME_PATH", sdk_path, path_buf_len);
}
STATIC void devdrv_load_file(ka_work_struct_t *p_work)
{
struct devdrv_load_work *load_work = ka_container_of(p_work, struct devdrv_load_work, work);
struct devdrv_pci_ctrl *pci_ctrl = load_work->ctrl;
struct devdrv_agent_load *loader = pci_ctrl->agent_loader;
int ret;
devdrv_info("Enter trans file work queue. (dev_id=%u)\n", pci_ctrl->dev_id);
ret = devdrv_load_file_trans(loader);
if (ret != 0) {
devdrv_err("Trans file to agent bios failed. (dev_id=%u)\n", pci_ctrl->dev_id);
return;
}
devdrv_load_timer(pci_ctrl);
devdrv_info("Leave trans file work queue. (dev_id=%u)\n", pci_ctrl->dev_id);
devdrv_get_rd_flag(loader, loader->mem_sram_base);
ka_mm_writeq(0x0, loader->mem_sram_base);
}
STATIC int devdrv_sdk_path_init(struct devdrv_pci_ctrl *pci_ctrl)
{
int block_num = pci_ctrl->res.load_file.load_file_num;
struct devdrv_load_file_cfg *load_file_cfg = pci_ctrl->res.load_file.load_file_cfg;
struct devdrv_load_file_cfg *file_cfg = NULL;
int i, ret;
u32 chip_type = pci_ctrl->chip_type;
ret = (int)devdrv_get_sdk_path(g_devdrv_sdk_path, (u32)sizeof(g_devdrv_sdk_path));
if (ret != 0) {
ret = strcpy_s(g_devdrv_sdk_path, DEVDRV_STR_MAX_LEN, DEVDRV_HOST_FILE_PATH);
if (ret != 0) {
devdrv_err("strcpy base path failed.\n");
return ret;
}
}
devdrv_info("Device load file path init begin. (chip_type=%d)\n", chip_type);
for (i = 0; i < DEVDRV_BLOCKS_NUM; i++) {
g_load_file[chip_type][i].file_name[0] = '\0';
}
for (i = 0; i < block_num; i++) {
ret = strcpy_s(g_load_file[chip_type][i].file_name, DEVDRV_STR_MAX_LEN, g_devdrv_sdk_path);
if (ret != 0) {
devdrv_err("strcpy base path to file name failed.\n");
return ret;
}
file_cfg = &load_file_cfg[i];
if (ka_base_strlen(g_devdrv_sdk_path) + ka_base_strlen(file_cfg->file_name) >= DEVDRV_STR_MAX_LEN) {
devdrv_err("String len error. (file_len=%ld; base_len=%ld)\n", ka_base_strlen(file_cfg->file_name),
ka_base_strlen(g_devdrv_sdk_path));
return -EFAULT;
}
ret = strcat_s(g_load_file[chip_type][i].file_name, DEVDRV_STR_MAX_LEN, file_cfg->file_name);
if (ret != 0) {
devdrv_err("strcat filed. (file_len=%ld; base_len=%ld)\n", ka_base_strlen(file_cfg->file_name),
ka_base_strlen(g_devdrv_sdk_path));
return ret;
}
g_load_file[chip_type][i].file_type = file_cfg->file_type;
g_load_file[chip_type][i].fail_mode = file_cfg->fail_mode;
}
return 0;
}
int devdrv_load_device(struct devdrv_pci_ctrl *pci_ctrl)
{
struct devdrv_agent_load *loader = NULL;
ka_device_t *dev = ka_pci_get_dev(pci_ctrl->pdev);
int ret;
u32 chip_type = pci_ctrl->chip_type;
devdrv_info("Device os load start. (dev_id=%u)\n", pci_ctrl->dev_id);
if (g_file_check_count[chip_type] == 0) {
ret = devdrv_sdk_path_init(pci_ctrl);
if (ret != 0) {
devdrv_err("Device sdk_path init failed. (dev_id=%u)\n", pci_ctrl->dev_id);
return ret;
}
g_file_check_count[chip_type]++;
}
loader = devdrv_kzalloc(sizeof(*loader), KA_GFP_KERNEL);
if (loader == NULL) {
devdrv_err("agent_loader devdrv_kzalloc failed. (dev_id=%u)\n", pci_ctrl->dev_id);
return -ENOMEM;
}
pci_ctrl->agent_loader = loader;
loader->dev_id = pci_ctrl->dev_id;
loader->mem_sram_base = pci_ctrl->res.load_sram_base;
ka_base_atomic_set(&loader->load_flag, 0);
loader->dev = dev;
loader->load_work.ctrl = pci_ctrl;
KA_TASK_INIT_WORK(&loader->load_work.work, devdrv_load_finish);
pci_ctrl->load_work.ctrl = pci_ctrl;
KA_TASK_INIT_WORK(&pci_ctrl->load_work.work, devdrv_load_file);
ka_task_schedule_work(&pci_ctrl->load_work.work);
devdrv_info("Load write dma addr finish, wait device os start. (dev_id=%u)\n", pci_ctrl->dev_id);
return 0;
}
void devdrv_load_exit(struct devdrv_pci_ctrl *pci_ctrl)
{
struct devdrv_agent_load *loader = NULL;
if (pci_ctrl->agent_loader == NULL) {
return;
}
loader = pci_ctrl->agent_loader;
if ((loader != NULL) && (pci_ctrl->load_work.work.func != NULL)) {
devdrv_set_load_abort(loader);
(void)ka_task_cancel_work_sync(&pci_ctrl->load_work.work);
}
if ((loader != NULL) && (pci_ctrl->agent_loader->load_work.work.func != NULL)) {
(void)ka_task_cancel_work_sync(&pci_ctrl->agent_loader->load_work.work);
}
devdrv_load_blocks_free(loader);
if (loader->timer_expires != 0) {
ka_system_del_timer_sync(&loader->load_timer);
loader->timer_expires = 0;
}
devdrv_kfree(loader);
loader = NULL;
devdrv_info("devdrv_load_exit finish. (dev_id=%u)\n", pci_ctrl->dev_id);
}