You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
427 lines
11 KiB
427 lines
11 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved.
|
|
* Description: emmc module driver
|
|
* Author: Hisilicon
|
|
* Create: 2020-10-15
|
|
*/
|
|
|
|
#define LARGEFILE64_SOURCE
|
|
#include "emmc_raw.h"
|
|
#include "soc_log.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
|
|
#include "uapi_flash.h"
|
|
#include "nand.h"
|
|
#include "flash_ext.h"
|
|
#include "cmdline_parts.h"
|
|
#include "securec.h"
|
|
|
|
static emmc_flash_s g_emmc_flash;
|
|
|
|
#define EMMC_RAW_AREA_START 0
|
|
|
|
td_s32 emmc_find_part_from_devname(const td_char *media_name, td_char *bootargs,
|
|
const td_char *devname, td_u64 *start, td_u64 *size)
|
|
{
|
|
td_u8 partnum;
|
|
td_char *tmp = NULL;
|
|
|
|
if ((media_name == NULL) || (*media_name == '\0')) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if ((devname == NULL) || (*devname == '\0')) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if ((bootargs == NULL) || (*bootargs == '\0')) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
tmp = strstr(bootargs, "blkdevparts=");
|
|
if (tmp == NULL) {
|
|
return TD_FAILURE;
|
|
}
|
|
tmp += strlen("blkdevparts=");
|
|
|
|
if (!strstr(bootargs, media_name)) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
tmp = strstr(devname, "mmcblk0p");
|
|
if (tmp == NULL) {
|
|
return TD_FAILURE;
|
|
}
|
|
tmp += strlen("mmcblk0p");
|
|
partnum = (td_u8)strtol(tmp, NULL, 10); /* 10 - number position */
|
|
if (get_part_info(partnum, start, size) != 0) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 emmc_raw_check_block(td_void)
|
|
{
|
|
td_s32 fd;
|
|
td_u8 buf[BUF_LINE_SIZE];
|
|
|
|
const td_char *device_node = "/dev/block/mmcblk0";
|
|
const td_char *device_node_linux = "/dev/mmcblk0";
|
|
|
|
if (access(device_node, F_OK) == -1) {
|
|
if (access(device_node_linux, F_OK) == -1) {
|
|
return TD_FAILURE;
|
|
}
|
|
fd = open(device_node_linux, O_RDWR);
|
|
} else {
|
|
fd = open(device_node, O_RDWR);
|
|
}
|
|
|
|
if (fd == -1) {
|
|
soc_print("open mmcblk0 failed, errno=%d\n", errno);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_emmc_flash.erase_size = EMMC_SECTOR_SIZE;
|
|
|
|
memset_s(buf, sizeof(buf), 0, sizeof(buf));
|
|
if (read(fd, buf, sizeof(buf) - 1) != ((ssize_t)sizeof(buf) - 1)) {
|
|
soc_print("Failed to read dev, errno=%d\n", errno);
|
|
close(fd);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
close(fd);
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 emmc_raw_check_size(td_void)
|
|
{
|
|
td_s32 fd;
|
|
td_u64 part_size;
|
|
td_u8 buf[BUF_LINE_SIZE];
|
|
|
|
/* Raw area start from 512, after MBR.. */
|
|
g_emmc_flash.raw_area_start = EMMC_RAW_AREA_START;
|
|
|
|
fd = open("/sys/block/mmcblk0/size", O_RDONLY);
|
|
if (fd < 0) {
|
|
soc_print("Fail to open the size of mmcblk0\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
memset_s(buf, sizeof(buf), 0, sizeof(buf));
|
|
if (read(fd, buf, sizeof(buf) - 1) <= 0) {
|
|
soc_print("Failed to read the size of mmcblk0\n");
|
|
close(fd);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
part_size = (td_u64)strtoull((td_char *)buf, NULL, STRTOULL_BASE_DEC);
|
|
if (part_size * EMMC_SECTOR_SIZE > MAX_U64) {
|
|
return TD_FAILURE;
|
|
}
|
|
g_emmc_flash.raw_area_size = part_size * EMMC_SECTOR_SIZE - EMMC_RAW_AREA_START;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 emmc_raw_init(td_char *bootargs)
|
|
{
|
|
if (bootargs == NULL) {
|
|
soc_print("Invalid parameter, bootargs NULL\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (cmdline_parts_init(bootargs) < 0) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (emmc_raw_check_block() == TD_FAILURE) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (emmc_raw_check_size() == TD_FAILURE) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 emmc_flash_probe(td_void)
|
|
{
|
|
td_s32 dev;
|
|
const td_char *device_node = "/dev/block/mmcblk0";
|
|
const td_char *device_node_linux = "/dev/mmcblk0";
|
|
|
|
dev = open(device_node, O_RDWR | O_SYNC);
|
|
if (dev == -1) {
|
|
dev = open(device_node_linux, O_RDWR | O_SYNC);
|
|
if (dev == -1) {
|
|
soc_print("Failed to open device '/dev/mmcblk0'");
|
|
return TD_FAILURE;
|
|
}
|
|
}
|
|
|
|
return (dev);
|
|
}
|
|
|
|
static td_s32 emmc_raw_open_check(td_u64 addr, td_u64 length)
|
|
{
|
|
/* Reject open, which are not block aligned */
|
|
if (((addr & (g_emmc_flash.erase_size - 1)) != 0) || ((length & (g_emmc_flash.erase_size - 1)) != 0)) {
|
|
soc_print("Attempt to open non block aligned, "
|
|
"eMMC blocksize: 0x%x, addr: 0x%08llx, length: 0x%08llx\n",
|
|
g_emmc_flash.erase_size,
|
|
addr,
|
|
length);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if ((addr > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size) ||
|
|
(length > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size) ||
|
|
((addr + length) > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size)) {
|
|
soc_print("Attempt to open outside the flash area, "
|
|
"eMMC chipsize: 0x%08llx, addr: 0x%08llx, length: 0x%08llx\n",
|
|
g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size,
|
|
addr,
|
|
length);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static emmc_cb_s *emmc_raw_malloc_cb(td_s32 fd, td_u64 addr, td_u64 length)
|
|
{
|
|
emmc_cb_s *emmc_cb = (emmc_cb_s *)malloc(sizeof(emmc_cb_s));
|
|
if (emmc_cb == NULL) {
|
|
soc_print("no many memory");
|
|
return NULL;
|
|
}
|
|
memset_s(emmc_cb, sizeof(emmc_cb_s), 0, sizeof(emmc_cb_s));
|
|
|
|
emmc_cb->address = addr;
|
|
emmc_cb->part_size = length;
|
|
emmc_cb->erase_size = g_emmc_flash.erase_size;
|
|
emmc_cb->fd = fd;
|
|
emmc_cb->part_type = EMMC_PART_TYPE_RAW;
|
|
|
|
return emmc_cb;
|
|
}
|
|
|
|
emmc_cb_s *emmc_raw_open(td_u64 addr, td_u64 length)
|
|
{
|
|
emmc_cb_s *emmc_cb = NULL;
|
|
td_s32 fd;
|
|
|
|
fd = emmc_flash_probe();
|
|
if (fd == -1) {
|
|
soc_print("no devices available");
|
|
return NULL;
|
|
}
|
|
|
|
if (emmc_raw_open_check(addr, length) != TD_SUCCESS) {
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
emmc_cb = emmc_raw_malloc_cb(fd, addr, length);
|
|
if (emmc_cb == NULL) {
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
return emmc_cb;
|
|
}
|
|
|
|
emmc_cb_s *emmc_node_open(const td_u8 *node)
|
|
{
|
|
emmc_cb_s *emmc_cb = NULL;
|
|
td_s32 fd = -1;
|
|
td_char tmp_node[PATH_MAX] = {0};
|
|
|
|
if (node == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (realpath((const td_char*)node, tmp_node) == NULL) {
|
|
soc_print("Failed to realpath %s.\n", node);
|
|
return NULL;
|
|
}
|
|
|
|
fd = open((const td_char*)tmp_node, O_RDWR);
|
|
if (fd == -1) {
|
|
soc_print("no devices available");
|
|
return NULL;
|
|
}
|
|
|
|
emmc_cb = (emmc_cb_s *)malloc(sizeof(emmc_cb_s));
|
|
if (emmc_cb == NULL) {
|
|
soc_print("No enough space");
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
memset_s(emmc_cb, sizeof(emmc_cb_s), 0, sizeof(emmc_cb_s));
|
|
|
|
emmc_cb->address = 0;
|
|
emmc_cb->part_size = 0;
|
|
emmc_cb->erase_size = g_emmc_flash.erase_size;
|
|
emmc_cb->fd = fd;
|
|
emmc_cb->part_type = EMMC_PART_TYPE_LOGIC;
|
|
|
|
return emmc_cb;
|
|
}
|
|
|
|
td_s32 emmc_block_read(td_s32 fd, td_u64 start, td_u32 len, td_u8 *buf)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (lseek64(fd, (off64_t)start, SEEK_SET) == -1) {
|
|
soc_print("Failed to lseek64");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (buf == NULL) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = (td_s32)read(fd, buf, len);
|
|
if (ret < 0) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
td_s32 emmc_block_write(td_s32 fd, td_u64 start, td_u32 len, const td_u8 *buf)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (lseek64(fd, (off64_t)start, SEEK_SET) == -1) {
|
|
soc_print("Failed to lseek64.\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (buf == NULL) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = (td_s32)write(fd, buf, len);
|
|
if (ret < 0) {
|
|
soc_print("Failed to write!\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 emmc_raw_forbid(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length)
|
|
{
|
|
return ((offset > emmc_cb->part_size) ||
|
|
(length > emmc_cb->part_size) || ((offset + length) > emmc_cb->part_size));
|
|
}
|
|
|
|
static td_s32 emmc_raw_rw_check(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, const td_u8 *buf)
|
|
{
|
|
if ((emmc_cb == NULL) || (buf == NULL)) {
|
|
soc_print("Pointer is null");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
/* Reject read, which are not block aligned */
|
|
if (emmc_cb->part_type == EMMC_PART_TYPE_RAW) {
|
|
if (emmc_raw_forbid(emmc_cb, offset, length) != 0) {
|
|
soc_print("Attempt to write outside the flash handle area, eMMC part size: 0x%08llx, offset: 0x%08llx, "
|
|
"length: 0x%08x.\n", emmc_cb->part_size, offset, length);
|
|
|
|
return TD_FAILURE;
|
|
}
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/* should be alignment with emmc block size */
|
|
td_s32 emmc_raw_read(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, td_u8 *buf)
|
|
{
|
|
td_u64 start;
|
|
|
|
if (emmc_cb == NULL) {
|
|
soc_print("emmc_cb is null");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (emmc_raw_rw_check(emmc_cb, offset, length, buf) != TD_SUCCESS) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
start = emmc_cb->address + offset;
|
|
return emmc_block_read(emmc_cb->fd, start, length, buf);
|
|
}
|
|
|
|
/* should be alignment with emmc block size */
|
|
td_s32 emmc_raw_write(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, const td_u8 *buf)
|
|
{
|
|
td_u64 start;
|
|
|
|
if (emmc_cb == NULL) {
|
|
soc_print("emmc_cb is null");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (emmc_raw_rw_check(emmc_cb, offset, length, buf) != TD_SUCCESS) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
start = emmc_cb->address + offset;
|
|
return emmc_block_write(emmc_cb->fd, start, length, buf);
|
|
}
|
|
|
|
td_s32 emmc_raw_close(emmc_cb_s *emmc_cb)
|
|
{
|
|
if (emmc_cb == NULL) {
|
|
soc_print("Pointer is null");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
close(emmc_cb->fd);
|
|
free(emmc_cb);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
#define MAX_HANDLE MAX_PARTS
|
|
|
|
td_void emmc_raw_get_info(td_handle flash, uapi_flash_info *flash_info, emmc_cb_s *emmc_cb)
|
|
{
|
|
if ((emmc_cb == NULL) || (flash_info == NULL)) {
|
|
soc_print("Pointer is null");
|
|
return;
|
|
}
|
|
|
|
if (flash >= MAX_HANDLE) {
|
|
return;
|
|
}
|
|
|
|
/* 16 - A block contains the number of erase */
|
|
if ((g_emmc_flash.erase_size * 16) > MAX_U32) {
|
|
return;
|
|
}
|
|
|
|
memset_s(flash_info, sizeof(*flash_info), 0, sizeof(*flash_info));
|
|
flash_info->total_size = g_emmc_flash.raw_area_size;
|
|
flash_info->oob_size = 0;
|
|
flash_info->block_size = (g_emmc_flash.erase_size * 16); /* 16 - A block contains the number of erase */
|
|
flash_info->page_size = 0;
|
|
flash_info->flash_type = UAPI_FLASH_TYPE_EMMC_0;
|
|
flash_info->open_addr = emmc_cb->address;
|
|
flash_info->open_len = emmc_cb->part_size;
|
|
}
|
|
|