linuxOS_D21X/source/uboot-2021.10/drivers/aicupg/upg_main.c
2024-11-29 16:33:21 +08:00

594 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2021 ArtInChip Technology Co., Ltd
*/
#include <common.h>
#include <command.h>
#include <env.h>
#include <artinchip/aicupg.h>
#include "upg_internal.h"
#define MAX_PART_NAME 32
#define GPT_HEADER_SIZE (34 * 512)
#define GPT_CMD_BUF_SIZE 2048
struct upg_internal upg_info = {
.cur_cmd = NULL,
.dev_type = UPG_DEV_TYPE_RAM,
.dev_id = 0,
.cfg = {
.mode = 0,
}
};
struct aicupg_gpt_partition {
char name[32];
u64 start;
u64 size;
struct aicupg_gpt_partition *next;
};
static bool __check_cmd_header(struct cmd_header *h)
{
u32 sum;
if (h->magic != UPG_CMD_HEADER_MAGIC)
return false;
if (h->protocol != UPG_PROTO_TYPE)
return false;
if (h->version != UPG_PROTO_VERSION)
return false;
sum = 0;
sum += h->magic;
sum += ((h->reserved << 24) | (h->command << 16) | (h->version << 8) |
h->protocol);
sum += h->data_length;
if (sum != h->checksum)
return false;
return true;
}
void aicupg_gen_resp(struct resp_header *h, u8 cmd, u8 sts, u32 len)
{
u32 sum;
h->magic = UPG_CMD_RESP_MAGIC;
h->protocol = UPG_PROTO_TYPE;
h->version = UPG_PROTO_VERSION;
h->command = cmd;
h->status = sts;
h->data_length = len;
sum = 0;
sum += h->magic;
sum += ((h->status << 24) | (h->command << 16) | (h->version << 8) |
h->protocol);
sum += h->data_length;
h->checksum = sum;
}
static char *get_upg_mode_name(int mode)
{
char *modes[] = {
"Full disk upgrade",
"Partition upgrade",
"Burn UserID",
"Dump partition",
"Force upgrade",
"Burn frozen",
};
char *invalid = "Invalid mode";
if (mode < 0 || mode >= UPG_MODE_INVALID)
return invalid;
return modes[mode];
}
void aicupg_show_upg_cfg_mode(int mode)
{
printf("UPGMODE: %s\n", get_upg_mode_name(mode));
}
void aicupg_show_init_cfg_mode(int mode_bits)
{
int i;
printf("Init UPGMODE:\n");
for (i = 0; i < UPG_MODE_INVALID; i++) {
if (mode_bits & (1 << i))
printf(" %s\n", get_upg_mode_name(i));
}
}
s32 aicupg_set_upg_cfg(struct upg_cfg *cfg)
{
if (!cfg) {
pr_info("Invalide parameter.\n");
return -1;
}
memcpy(&upg_info.cfg, cfg, sizeof(*cfg));
aicupg_show_upg_cfg_mode(upg_info.cfg.mode);
return 0;
}
s32 aicupg_initialize(struct upg_init *param)
{
upg_info.init.mode_bits = param->mode_bits;
aicupg_show_init_cfg_mode(upg_info.init.mode_bits);
return 0;
}
s32 aicupg_get_upg_mode(void)
{
return (s32)upg_info.cfg.mode;
}
void set_current_command(struct upg_cmd *cmd)
{
upg_info.cur_cmd = cmd;
}
struct upg_cmd *get_current_command(void)
{
return upg_info.cur_cmd;
}
enum upg_cmd_state get_current_command_state(void)
{
if (upg_info.cur_cmd)
return upg_info.cur_cmd->state;
return CMD_STATE_IDLE;
}
void set_current_device_type(enum upg_dev_type type)
{
upg_info.dev_type = type;
}
enum upg_dev_type get_current_device_type(void)
{
return upg_info.dev_type;
}
const char *get_current_device_name(enum upg_dev_type type)
{
char *dev_list[] = {
"RAM",
"MMC",
"SPI_NAND",
"SPI_NOR",
"RAW_NAND",
"UNKNOWN",
};
return dev_list[type];
}
void set_current_device_id(int id)
{
upg_info.dev_id = id;
}
int get_current_device_id(void)
{
return upg_info.dev_id;
}
static struct upg_cmd *find_command(struct cmd_header *h)
{
struct upg_cmd *cmd = NULL;
cmd = find_basic_command(h);
if (cmd)
return cmd;
/* Not basic command, maybe it is FWC relative command. */
cmd = find_fwc_command(h);
return cmd;
}
s32 aicupg_data_packet_write(u8 *data, s32 len)
{
struct cmd_header h;
struct upg_cmd *cmd;
u32 clen;
clen = 0;
if (len >= sizeof(struct cmd_header))
memcpy(&h, data, sizeof(struct cmd_header));
if ((len >= sizeof(struct cmd_header)) &&
(__check_cmd_header(&h) == true)) {
/*
* Command start packet, find the command handler
*/
cmd = find_command(&h);
set_current_command(cmd);
if (cmd)
cmd->start(cmd, h.data_length);
clen = sizeof(struct cmd_header);
}
/* Maybe this packet is cmd_header only */
if (clen == len)
return clen;
/*
* There is command data
*/
cmd = get_current_command();
if (cmd && cmd->write_input_data)
clen += cmd->write_input_data(cmd, data, len - clen);
/* End CMD after CSW is sent */
if (get_current_command_state() == CMD_STATE_END)
cmd->end(cmd);
pr_debug("%s, l: %d\n", __func__, __LINE__);
return clen;
}
s32 aicupg_data_packet_read(u8 *data, s32 len)
{
struct upg_cmd *cmd;
s32 rlen = 0;
/*
* Host read data from device
*/
cmd = get_current_command();
if (cmd && cmd->read_output_data)
rlen = cmd->read_output_data(cmd, data, len);
/* End CMD before CSW is sent */
if (get_current_command_state() == CMD_STATE_END)
cmd->end(cmd);
return rlen;
}
int aicupg_get_fwc_attr(struct fwc_info *fwc)
{
int attr = 0;
if (!fwc)
return 0;
if (strstr(fwc->meta.attr, "required"))
attr |= FWC_ATTR_REQUIRED;
else if (strstr(fwc->meta.attr, "optional"))
attr |= FWC_ATTR_OPTIONAL;
if (strstr(fwc->meta.attr, "run"))
attr |= FWC_ATTR_ACTION_RUN;
else if (strstr(fwc->meta.attr, "burn"))
attr |= FWC_ATTR_ACTION_BURN;
if (strstr(fwc->meta.attr, "block"))
attr |= FWC_ATTR_DEV_BLOCK;
else if (strstr(fwc->meta.attr, "mtd"))
attr |= FWC_ATTR_DEV_MTD;
else if (strstr(fwc->meta.attr, "ubi"))
attr |= FWC_ATTR_DEV_UBI;
return attr;
}
/*
*Init fwc and config fwc->meta
*/
void fwc_meta_config(struct fwc_info *fwc, struct fwc_meta *pmeta)
{
memset((void *)fwc, 0, sizeof(struct fwc_info));
memcpy(&fwc->meta, pmeta, sizeof(struct fwc_meta));
}
/*
*Get memory type by header
*- Determine the memory type of the current image
*/
static enum upg_dev_type media_type_get(struct image_header_upgrade *header)
{
static enum upg_dev_type type;
pr_debug("%s, %s\n", __func__, header->media_type);
if (strcmp(header->media_type, "mmc") == 0)
type = UPG_DEV_TYPE_MMC;
else if (strcmp(header->media_type, "raw-nand") == 0)
type = UPG_DEV_TYPE_RAW_NAND;
else if (strcmp(header->media_type, "spi-nand") == 0)
type = UPG_DEV_TYPE_SPI_NAND;
else if (strcmp(header->media_type, "spi-nor") == 0)
type = UPG_DEV_TYPE_SPI_NOR;
else
type = UPG_DEV_TYPE_UNKNOWN;
return type;
}
/*
*Prepare write data
*- Select function based on type
*/
s32 media_device_prepare(struct fwc_info *fwc, struct image_header_upgrade *header)
{
s32 ret = 0;
enum upg_dev_type type;
/*get device type*/
type = media_type_get(header);
/*config upg_info*/
set_current_device_type(type);
set_current_device_id(header->media_dev_id);
switch (type) {
#ifdef CONFIG_AICUPG_NOR_ARTINCHIP
case UPG_DEV_TYPE_SPI_NOR:
ret = nor_fwc_prepare(fwc, header->media_dev_id);
break;
#endif
#ifdef CONFIG_AICUPG_NAND_ARTINCHIP
case UPG_DEV_TYPE_SPI_NAND:
if (get_nand_prepare_status() != true) {
ret = nand_fwc_prepare(fwc, header->media_dev_id);
}
break;
#endif
#ifdef CONFIG_AICUPG_MMC_ARTINCHIP
case UPG_DEV_TYPE_MMC:
ret = mmc_fwc_prepare(fwc, header->media_dev_id);
break;
#endif
default:
pr_err("device type is not support!...\n");
ret = -1;
break;
}
return ret;
}
/*
*Start write data
*- Select function based on type
*/
void media_data_write_start(struct fwc_info *fwc)
{
enum upg_dev_type type;
type = get_current_device_type();
switch (type) {
#ifdef CONFIG_AICUPG_NOR_ARTINCHIP
case UPG_DEV_TYPE_SPI_NOR:
nor_fwc_start(fwc);
break;
#endif
#ifdef CONFIG_AICUPG_NAND_ARTINCHIP
case UPG_DEV_TYPE_SPI_NAND:
nand_fwc_start(fwc);
break;
#endif
#ifdef CONFIG_AICUPG_MMC_ARTINCHIP
case UPG_DEV_TYPE_MMC:
mmc_fwc_start(fwc);
break;
#endif
default:
pr_err("device type is not support!...\n");
break;
}
}
/*
*Write data to memory device
*- Make the data size into whole block
*- Select function based on type
*/
s32 media_data_write(struct fwc_info *fwc, u8 *buf, u32 len)
{
s32 ret;
s32 len_to_write;
enum upg_dev_type type;
type = get_current_device_type();
if (len % fwc->block_size)
len_to_write = len + fwc->block_size - (len % fwc->block_size);
else
len_to_write = len;
switch (type) {
#ifdef CONFIG_AICUPG_NOR_ARTINCHIP
case UPG_DEV_TYPE_SPI_NOR:
ret = nor_fwc_data_write(fwc, buf, len_to_write);
break;
#endif
#ifdef CONFIG_AICUPG_NAND_ARTINCHIP
case UPG_DEV_TYPE_SPI_NAND:
ret = nand_fwc_data_write(fwc, buf, len_to_write);
break;
#endif
#ifdef CONFIG_AICUPG_MMC_ARTINCHIP
case UPG_DEV_TYPE_MMC:
ret = mmc_fwc_data_write(fwc, buf, len_to_write);
break;
#endif
default:
ret = 0;
pr_err("device type is not support!...\n");
break;
}
/*The size of the data we actually write is len*/
if (ret != len_to_write)
ret = 0;
else
ret = len;
return ret;
}
/*
*End write data
*- Select function based on type
*/
void media_data_write_end(struct fwc_info *fwc)
{
enum upg_dev_type type;
type = get_current_device_type();
switch (type) {
#ifdef CONFIG_AICUPG_NOR_ARTINCHIP
case UPG_DEV_TYPE_SPI_NOR:
nor_fwc_data_end(fwc);
break;
#endif
#ifdef CONFIG_AICUPG_NAND_ARTINCHIP
case UPG_DEV_TYPE_SPI_NAND:
nand_fwc_data_end(fwc);
break;
#endif
#ifdef CONFIG_AICUPG_MMC_ARTINCHIP
case UPG_DEV_TYPE_MMC:
mmc_fwc_data_end(fwc);
break;
#endif
default:
pr_err("device type is not support!...\n");
break;
}
}
static struct aicupg_gpt_partition *new_partition(char *s, u64 start)
{
struct aicupg_gpt_partition *part = NULL;
int cnt = 0;
char *p;
part = (struct aicupg_gpt_partition *)malloc(sizeof(struct aicupg_gpt_partition));
if (!part)
return NULL;
memset(part, 0, sizeof(struct aicupg_gpt_partition));
p = s;
part->start = start;
if (*p == '-') {
/* All remain space */
part->size = 0;
p++;
} else {
part->size = ustrtoull(p, &p, 0);
}
if (*p == '@') {
p++;
part->start = ustrtoull(p, &p, 0);
}
if (*p != '(') {
pr_err("Partition name should be next of size.\n");
goto err;
}
p++;
while (*p != ')') {
if (cnt >= MAX_PART_NAME)
break;
part->name[cnt++] = *p++;
}
p++;
if (*p == ',') {
p++;
part->next = new_partition(p, part->start + part->size);
}
pr_info("part: %s, start %lld, size %lld\n", part->name, part->start,
part->size);
return part;
err:
if (part)
free(part);
return NULL;
}
static void free_gpt_partition(struct aicupg_gpt_partition *part)
{
struct aicupg_gpt_partition *next;
if (!part)
return;
next = part->next;
free(part);
free_gpt_partition(next);
}
s32 aicupg_mmc_create_gpt_part(u32 mmc_id, bool is_sdupg)
{
struct aicupg_gpt_partition *parts = NULL, *item;
char *parts_mmc, *cmdbuf, *p;
int ret = 0, limit;
/*
* Step1: Ensure MMC device is exist
*/
if (mmc_id >= get_mmc_num()) {
pr_err("Invalid mmc dev %d\n", mmc_id);
return -ENODEV;
}
/*
* Step2: Get partitions table from ENV
* if upgrade - parts_mmc
* if SD card upgrade -burn_mmc
* If it is USB upgrade, Host tool already transferred env.bin to DRAM
* during BROM stage.
*/
if (is_sdupg)
parts_mmc = env_get("burn_mmc");
else
parts_mmc = env_get("GPT");
if (!parts_mmc) {
pr_err("Get gpt partition table from ENV failed.\n");
return -ENODEV;
}
parts = new_partition(parts_mmc, (GPT_HEADER_SIZE));
if (!parts)
return -1;
if (parts->start != GPT_HEADER_SIZE) {
pr_err("First partition start offset is not correct\n");
return -1;
}
cmdbuf = (char *)malloc(GPT_CMD_BUF_SIZE);
if (!cmdbuf) {
ret = -1;
goto out;
}
limit = GPT_CMD_BUF_SIZE;
p = cmdbuf;
snprintf(p, limit, "gpt write mmc %d \"", mmc_id);
p = cmdbuf + strlen(cmdbuf);
item = parts;
while (item) {
if (item->size > 0)
snprintf(p, limit, "name=%s,start=%lld,size=%lld;",
item->name, item->start, item->size);
else
snprintf(p, limit, "name=%s,start=%lld,size=-;",
item->name, item->start);
p = cmdbuf + strlen(cmdbuf);
item = item->next;
}
snprintf(p, limit, "\"");
/*
* Step3: Create GPT partitions
*/
ret = run_command(cmdbuf, 0);
out:
if (parts)
free_gpt_partition(parts);
if (cmdbuf)
free(cmdbuf);
return ret;
}