From 5280f3f697641a3c5001d3fe814f1cea3a2b9ff9 Mon Sep 17 00:00:00 2001 From: Yuefu Su Date: Wed, 22 Oct 2025 17:24:15 +0800 Subject: [PATCH 1/4] phy: rockchip: csi2-dphy: initialize sensor format request Set pad and which before forwarding get_fmt to the terminal sensor. This prevents stale caller values from selecting an invalid pad or format state. Signed-off-by: Yuefu Su Signed-off-by: Conghao Hu --- drivers/phy/rockchip/phy-rockchip-csi2-dphy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c index e83672d665494..609a547068dcb 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c @@ -749,6 +749,8 @@ static int csi2_dphy_get_set_fmt(struct v4l2_subdev *sd, sensor = sd_to_sensor(dphy, sensor_sd); if (!sensor) return -ENODEV; + fmt->pad = 0; + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, fmt); if (!ret && fmt->pad == 0 && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) sensor->format = fmt->format; From 3a3f987abd17ddfd730ce9583a5a091c0a50821b Mon Sep 17 00:00:00 2001 From: Conghao Hu Date: Thu, 20 Aug 2026 13:16:42 +0800 Subject: [PATCH 2/4] media: rockchip: cif: zero-initialize subdev format Linux 6.6 added the stream field to struct v4l2_subdev_format. Leaving the request uninitialized can pass a random stream ID to get_fmt and make the terminal sensor format lookup fail. Zero-initialize the request so unspecified fields, including stream, use their default value. Signed-off-by: Conghao Hu --- drivers/media/platform/rockchip/cif/capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 635b5ce79b33a..85dd8da0cb9c5 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -982,7 +982,7 @@ const struct cif_input_fmt *rkcif_get_input_fmt(struct rkcif_device *dev, struct v4l2_rect *rect, u32 pad_id, struct csi_channel_info *csi_info) { - struct v4l2_subdev_format fmt; + struct v4l2_subdev_format fmt = {0}; struct v4l2_subdev *sd = dev->terminal_sensor.sd; struct rkmodule_channel_info ch_info = {0}; struct rkmodule_capture_info capture_info; From 94ed40a5748c9f1bfe0fc62b922d276fdd96b5bb Mon Sep 17 00:00:00 2001 From: Conghao Hu Date: Thu, 20 Aug 2026 09:21:06 +0800 Subject: [PATCH 3/4] media: rockchip: cif: guard against NULL input formats rkcif_get_input_fmt() can return NULL when the terminal sensor format lookup fails. Reject the error before storing or dereferencing the format, and keep the output format check defensive for its other callers. Signed-off-by: Conghao Hu --- drivers/media/platform/rockchip/cif/capture.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 85dd8da0cb9c5..d632a4121460f 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -638,6 +638,11 @@ static int rkcif_output_fmt_check(struct rkcif_stream *stream, int ret = -EINVAL; stream->rounding_bit = 0; + if (!input_fmt) { + v4l2_err(&stream->cifdev->v4l2_dev, "Input fmt is NULL\n"); + return -EINVAL; + } + switch (input_fmt->mbus_code) { case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: @@ -8805,6 +8810,11 @@ int rkcif_set_fmt(struct rkcif_stream *stream, cif_fmt_in = rkcif_get_input_fmt(dev, &input_rect, stream->id, channel_info); + if (!cif_fmt_in) { + v4l2_err(&stream->cifdev->v4l2_dev, + "terminal sensor fmt invalid\n"); + return -EINVAL; + } stream->cif_fmt_in = cif_fmt_in; } else { v4l2_err(&stream->cifdev->v4l2_dev, From 922130e451822114fd0220ad991f6f9f6f9b63c5 Mon Sep 17 00:00:00 2001 From: Conghao Hu Date: Mon, 24 Aug 2026 17:35:49 +0800 Subject: [PATCH 4/4] soc: rockchip: restore eMMC vendor storage support Re-enable the existing eMMC vendor storage implementation and connect it to the MMC block card lifecycle so vendor storage can access eMMC-backed data on Linux 6.6. Signed-off-by: Conghao Hu --- drivers/mmc/core/block.c | 16 + drivers/mmc/core/block.h | 3 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/rk_sdmmc_ops.c | 208 +++++++ drivers/mmc/host/rk_sdmmc_ops.h | 11 + drivers/soc/rockchip/Kconfig | 9 + drivers/soc/rockchip/Makefile | 1 + drivers/soc/rockchip/sdmmc_vendor_storage.c | 586 ++++++++++++++++++++ 8 files changed, 835 insertions(+) create mode 100644 drivers/mmc/host/rk_sdmmc_ops.c create mode 100644 drivers/mmc/host/rk_sdmmc_ops.h create mode 100644 drivers/soc/rockchip/sdmmc_vendor_storage.c diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 624f1d7ec87e6..73d6cb60f817c 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -3010,6 +3010,11 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card, #endif /* CONFIG_DEBUG_FS */ +#if IS_ENABLED(CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE) +struct mmc_card *this_card; +EXPORT_SYMBOL(this_card); +#endif + static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md; @@ -3040,6 +3045,11 @@ static int mmc_blk_probe(struct mmc_card *card) if (ret) goto out; +#if IS_ENABLED(CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE) + if (mmc_card_mmc(card)) + this_card = card; +#endif + /* Add two debugfs entries */ mmc_blk_add_debugfs(card, md); @@ -3070,6 +3080,12 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = dev_get_drvdata(&card->dev); mmc_blk_remove_debugfs(card, md); + +#if IS_ENABLED(CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE) + if (mmc_card_mmc(card)) + this_card = NULL; +#endif + mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); if (md->part_curr != md->part_type) { diff --git a/drivers/mmc/core/block.h b/drivers/mmc/core/block.h index 31153f656f412..441cc9234b3a8 100644 --- a/drivers/mmc/core/block.h +++ b/drivers/mmc/core/block.h @@ -3,8 +3,11 @@ #define _MMC_CORE_BLOCK_H struct mmc_queue; +struct mmc_card; struct request; +extern struct mmc_card *this_card; + void mmc_blk_cqe_recovery(struct mmc_queue *mq); enum mmc_issued; diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index a693fa3d3f1cc..18dfa79f5893c 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o +obj-$(CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE) += rk_sdmmc_ops.o obj-$(CONFIG_MMC_DW_STARFIVE) += dw_mmc-starfive.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o diff --git a/drivers/mmc/host/rk_sdmmc_ops.c b/drivers/mmc/host/rk_sdmmc_ops.c new file mode 100644 index 0000000000000..7986daad7618c --- /dev/null +++ b/drivers/mmc/host/rk_sdmmc_ops.c @@ -0,0 +1,208 @@ +/* + * linux/drivers/mmchost/rkemmc_ops.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include /* For nr_free_buffer_pages() */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include "../core/block.h" +#include "../core/card.h" +#include "../core/core.h" +#include "../core/mmc_ops.h" +#include "rk_sdmmc_ops.h" + +#define BLKSZ 512 + +enum emmc_area_type { + MMC_DATA_AREA_MAIN, + MMC_DATA_AREA_BOOT1, + MMC_DATA_AREA_BOOT2, + MMC_DATA_AREA_RPMB, +}; + +static int rk_emmc_set_areatype(enum emmc_area_type areatype) +{ + int err; + u8 part_config; + + part_config = this_card->ext_csd.part_config; + part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + part_config |= (u8)areatype; + err = mmc_switch(this_card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONFIG, part_config, + this_card->ext_csd.part_time); + + return err; +} + +/* + * Fill in the mmc_request structure given a set of transfer parameters. + */ +static void rk_emmc_prepare_mrq(struct mmc_request *mrq, struct scatterlist *sg, + unsigned sg_len, unsigned dev_addr, unsigned blocks, unsigned blksz, int write) +{ + BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop); + + if (blocks > 1) { + mrq->cmd->opcode = write ? + MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK; + } else { + mrq->cmd->opcode = write ? + MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK; + } + + mrq->cmd->arg = dev_addr; + if (!mmc_card_blockaddr(this_card)) + mrq->cmd->arg <<= 9; + + mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + if (blocks == 1) + mrq->stop = NULL; + else { + mrq->stop->opcode = MMC_STOP_TRANSMISSION; + mrq->stop->arg = 0; + mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } + + mrq->data->blksz = blksz; + mrq->data->blocks = blocks; + mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mrq->data->sg = sg; + mrq->data->sg_len = sg_len; + mmc_set_data_timeout(mrq->data, this_card); +} + +static int rk_emmc_busy(struct mmc_command *cmd) +{ + return !(cmd->resp[0] & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(cmd->resp[0]) == 7); +} + +/* + * Wait for the card to finish the busy state + */ +static int rk_emmc_wait_busy(void) +{ + int ret, busy; + struct mmc_command cmd = {0}; + + busy = 0; + do { + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = this_card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + ret = mmc_wait_for_cmd(this_card->host, &cmd, 0); + if (ret) + break; + + if (!busy && rk_emmc_busy(&cmd)) { + busy = 1; + if (this_card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) + pr_info("%s: Warning: Host did not " + "wait for busy state to end.\n", + mmc_hostname(this_card->host)); + } + } while (rk_emmc_busy(&cmd)); + + return ret; +} + +/* + * Transfer a single sector of kernel addressable data + */ +int rk_emmc_transfer(u8 *buffer, unsigned int addr, unsigned int datasz, int write) +{ + int ret = 0; + enum emmc_area_type areatype; + + struct mmc_request mrq = {0}; + struct mmc_command cmd = {0}; + struct mmc_command stop = {0}; + struct mmc_data data = {0}; + + struct scatterlist sg; + + if(!this_card) + return -EIO; + + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop = &stop; + + sg_init_one(&sg, buffer, datasz); + + rk_emmc_prepare_mrq(&mrq, &sg, 1, addr, datasz / BLKSZ, BLKSZ, write); + + pm_runtime_get_sync(&this_card->dev); + mmc_claim_host(this_card->host); + + if (this_card->ext_csd.cmdq_en) { + ret = mmc_cmdq_disable(this_card); + if (ret) + goto exit; + } + + areatype = (enum emmc_area_type)this_card->ext_csd.part_config + & EXT_CSD_PART_CONFIG_ACC_MASK; + if (areatype != MMC_DATA_AREA_MAIN) { + ret = rk_emmc_set_areatype(MMC_DATA_AREA_MAIN); + if (ret) { + pr_err("rk_emmc_set_areatype error!.\n"); + goto exit; + } + } + + mmc_wait_for_req(this_card->host, &mrq); + + if (cmd.error){ + ret = cmd.error; + goto exit; + } + if (data.error){ + ret = data.error; + goto exit; + } + + ret = rk_emmc_wait_busy(); + + if (areatype != MMC_DATA_AREA_MAIN) { + ret = rk_emmc_set_areatype(areatype); + if (ret) + pr_err("rk_emmc_set_areatype error!.\n"); + } + +exit: + if (this_card->reenable_cmdq && !this_card->ext_csd.cmdq_en) + mmc_cmdq_enable(this_card); + + mmc_release_host(this_card->host); + pm_runtime_put(&this_card->dev); + + return ret; +} +EXPORT_SYMBOL(rk_emmc_transfer); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/rk_sdmmc_ops.h b/drivers/mmc/host/rk_sdmmc_ops.h new file mode 100644 index 0000000000000..4f68c1a359515 --- /dev/null +++ b/drivers/mmc/host/rk_sdmmc_ops.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ + +#ifndef _RK_SDMMC_OPS_H_ +#define _RK_SDMMC_OPS_H_ + +int rk_emmc_transfer(u8 *buffer, unsigned int addr, unsigned int datasz, int write); + +#endif diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index c1e9fdfe6b61b..855cbf333d141 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -210,6 +210,15 @@ config ROCKCHIP_VENDOR_STORAGE help Say y here to enable rockchip vendor storage support. +config ROCKCHIP_MMC_VENDOR_STORAGE + tristate "Rockchip mmc vendor storage support" + depends on ROCKCHIP_VENDOR_STORAGE && MMC_BLOCK + help + Enable Rockchip vendor storage backed by an eMMC device. The driver + uses the MMC block card to read and write persistent vendor data and + exposes it through the Rockchip vendor storage interface. Select this + on systems that keep Rockchip vendor data in eMMC. + config ROCKCHIP_FLASH_VENDOR_STORAGE tristate "Rockchip flash vendor storage support" depends on ROCKCHIP_VENDOR_STORAGE && RK_FLASH diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index b35333cf5fe3b..4e9b80e40ea9a 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_ROCKCHIP_IOMUX) += iomux.o obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o obj-$(CONFIG_ROCKCHIP_FIQ_DEBUGGER) += fiq_debugger/ obj-$(CONFIG_ROCKCHIP_VENDOR_STORAGE) += rk_vendor_storage.o +obj-$(CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE) += sdmmc_vendor_storage.o obj-$(CONFIG_ROCKCHIP_FLASH_VENDOR_STORAGE) += flash_vendor_storage.o obj-$(CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE) += mtd_vendor_storage.o obj-$(CONFIG_ROCKCHIP_RAM_VENDOR_STORAGE) += ram_vendor_storage.o diff --git a/drivers/soc/rockchip/sdmmc_vendor_storage.c b/drivers/soc/rockchip/sdmmc_vendor_storage.c new file mode 100644 index 0000000000000..5c14c484cd6a9 --- /dev/null +++ b/drivers/soc/rockchip/sdmmc_vendor_storage.c @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../mmc/host/rk_sdmmc_ops.h" + +#define EMMC_IDB_PART_OFFSET 64 +#define EMMC_SYS_PART_OFFSET 8064 +#define EMMC_BOOT_PART_SIZE 1024 +#define EMMC_VENDOR_PART_START (1024 * 7) +#define EMMC_VENDOR_PART_SIZE VENDOR_PART_SIZE +#define EMMC_VENDOR_PART_NUM 4 +#define EMMC_VENDOR_TAG VENDOR_HEAD_TAG + +#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER +#define READ_SECTOR_IO _IOW('r', 0x04, unsigned int) +#define WRITE_SECTOR_IO _IOW('r', 0x05, unsigned int) +#define END_WRITE_SECTOR_IO _IOW('r', 0x52, unsigned int) +#define GET_FLASH_INFO_IO _IOW('r', 0x1A, unsigned int) +#define GET_BAD_BLOCK_IO _IOW('r', 0x03, unsigned int) +#define GET_LOCK_FLAG_IO _IOW('r', 0x53, unsigned int) +#endif + +static u8 *g_idb_buffer; +static struct vendor_info *g_vendor; +static DEFINE_MUTEX(vendor_ops_mutex); + +static int emmc_vendor_ops(u8 *buffer, u32 addr, u32 n_sec, int write) +{ + return rk_emmc_transfer(buffer, addr, n_sec << 9, write); +} + +static int emmc_vendor_storage_init(void) +{ + u32 i, max_ver, max_index; + u8 *p_buf; + + max_ver = 0; + max_index = 0; + for (i = 0; i < EMMC_VENDOR_PART_NUM; i++) { + /* read first 512 bytes */ + p_buf = (u8 *)g_vendor; + if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START + + EMMC_VENDOR_PART_SIZE * i, 512, 0)) + goto error_exit; + /* read last 512 bytes */ + p_buf += (EMMC_VENDOR_PART_SIZE - 1) * 512; + if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START + + EMMC_VENDOR_PART_SIZE * (i + 1) - 1, + 512, 0)) + goto error_exit; + + if (g_vendor->tag == EMMC_VENDOR_TAG && + g_vendor->version2 == g_vendor->version) { + if (max_ver < g_vendor->version) { + max_index = i; + max_ver = g_vendor->version; + } + } + } + if (max_ver) { + if (emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START + + EMMC_VENDOR_PART_SIZE * max_index, + EMMC_VENDOR_PART_SIZE, 0)) + goto error_exit; + g_vendor->free_size = sizeof(g_vendor->data) - g_vendor->free_offset; + } else { + memset((void *)g_vendor, 0, sizeof(*g_vendor)); + g_vendor->version = 1; + g_vendor->tag = EMMC_VENDOR_TAG; + g_vendor->version2 = g_vendor->version; + g_vendor->free_offset = 0; + g_vendor->free_size = sizeof(g_vendor->data); + } + return 0; +error_exit: + return -1; +} + +static int emmc_vendor_read(u32 id, void *pbuf, u32 size) +{ + u32 i; + + if (!g_vendor) + return -ENOMEM; + + for (i = 0; i < g_vendor->item_num; i++) { + if (g_vendor->item[i].id == id) { + if (size > g_vendor->item[i].size) + size = g_vendor->item[i].size; + memcpy(pbuf, + &g_vendor->data[g_vendor->item[i].offset], + size); + return size; + } + } + return (-1); +} + +static int emmc_vendor_write(u32 id, void *pbuf, u32 size) +{ + u32 i, j, next_index, align_size, alloc_size, item_num; + u32 offset, next_size; + u8 *p_data; + struct vendor_item *item; + struct vendor_item *next_item; + + if (!g_vendor) + return -ENOMEM; + + p_data = g_vendor->data; + item_num = g_vendor->item_num; + align_size = ALIGN(size, 0x40); /* align to 64 bytes*/ + next_index = g_vendor->next_index; + for (i = 0; i < item_num; i++) { + item = &g_vendor->item[i]; + if (item->id == id) { + alloc_size = ALIGN(item->size, 0x40); + if (size > alloc_size) { + if (g_vendor->free_size < align_size) + return -1; + offset = item->offset; + for (j = i; j < item_num - 1; j++) { + item = &g_vendor->item[j]; + next_item = &g_vendor->item[j + 1]; + item->id = next_item->id; + item->size = next_item->size; + item->offset = offset; + next_size = ALIGN(next_item->size, + 0x40); + memcpy(&p_data[offset], + &p_data[next_item->offset], + next_size); + offset += next_size; + } + item = &g_vendor->item[j]; + item->id = id; + item->offset = offset; + item->size = size; + memcpy(&p_data[item->offset], pbuf, size); + g_vendor->free_offset = offset + align_size; + g_vendor->free_size = sizeof(g_vendor->data) - g_vendor->free_offset; + } else { + memcpy(&p_data[item->offset], + pbuf, + size); + g_vendor->item[i].size = size; + } + g_vendor->version++; + g_vendor->version2 = g_vendor->version; + g_vendor->next_index++; + if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM) + g_vendor->next_index = 0; + emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START + + EMMC_VENDOR_PART_SIZE * next_index, + EMMC_VENDOR_PART_SIZE, 1); + return 0; + } + } + + if (g_vendor->free_size >= align_size) { + item = &g_vendor->item[g_vendor->item_num]; + item->id = id; + item->offset = g_vendor->free_offset; + item->size = size; + g_vendor->free_offset += align_size; + g_vendor->free_size -= align_size; + memcpy(&g_vendor->data[item->offset], pbuf, size); + g_vendor->item_num++; + g_vendor->version++; + g_vendor->version2 = g_vendor->version; + g_vendor->next_index++; + if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM) + g_vendor->next_index = 0; + emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START + + EMMC_VENDOR_PART_SIZE * next_index, + EMMC_VENDOR_PART_SIZE, 1); + return 0; + } + return(-1); +} + +#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER +static int id_blk_read_data(u32 index, u32 n_sec, u8 *buf) +{ + if (index + n_sec >= 1024 * 5) + return 0; + index = index + EMMC_IDB_PART_OFFSET; + + return rk_emmc_transfer(buf, index, n_sec << 9, 0); +} + +static int id_blk_write_data(u32 index, u32 n_sec, u8 *buf) +{ + if (index + n_sec >= 1024 * 5) + return 0; + index = index + EMMC_IDB_PART_OFFSET; + + return rk_emmc_transfer(buf, index, n_sec << 9, 1); +} + +static int emmc_write_idblock(u32 size, u8 *buf, u32 *id_blk_tbl) +{ + u32 i, totle_sec, j; + u32 totle_write_count = 0; + u32 *p_raw_data = (u32 *)buf; + u32 *p_check_buf = kmalloc(EMMC_BOOT_PART_SIZE * 512, GFP_KERNEL); + + if (!p_check_buf) + return -ENOMEM; + + totle_sec = (size + 511) >> 9; + if (totle_sec <= 8) + totle_sec = 8; + + for (i = 0; i < 5; i++) { + memset(p_check_buf, 0, 512); + id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1, + (u8 *)p_check_buf); + id_blk_write_data(EMMC_BOOT_PART_SIZE * i + 1, + totle_sec - 1, buf + 512); + id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1, buf); + id_blk_read_data(EMMC_BOOT_PART_SIZE * i, totle_sec, + (u8 *)p_check_buf); + for (j = 0; j < totle_sec * 128; j++) { + if (p_check_buf[j] != p_raw_data[j]) { + memset(p_check_buf, 0, 512); + id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1, + (u8 *)p_check_buf); + break; + } + } + if (j >= totle_sec * 128) + totle_write_count++; + } + kfree(p_check_buf); + if (totle_write_count) + return 0; + return (-1); +} +#endif + +static int vendor_storage_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int vendor_storage_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER +static const u32 g_crc32_tbl[256] = { + 0x00000000, 0x04c10db7, 0x09821b6e, 0x0d4316d9, + 0x130436dc, 0x17c53b6b, 0x1a862db2, 0x1e472005, + 0x26086db8, 0x22c9600f, 0x2f8a76d6, 0x2b4b7b61, + 0x350c5b64, 0x31cd56d3, 0x3c8e400a, 0x384f4dbd, + 0x4c10db70, 0x48d1d6c7, 0x4592c01e, 0x4153cda9, + 0x5f14edac, 0x5bd5e01b, 0x5696f6c2, 0x5257fb75, + 0x6a18b6c8, 0x6ed9bb7f, 0x639aada6, 0x675ba011, + 0x791c8014, 0x7ddd8da3, 0x709e9b7a, 0x745f96cd, + 0x9821b6e0, 0x9ce0bb57, 0x91a3ad8e, 0x9562a039, + 0x8b25803c, 0x8fe48d8b, 0x82a79b52, 0x866696e5, + 0xbe29db58, 0xbae8d6ef, 0xb7abc036, 0xb36acd81, + 0xad2ded84, 0xa9ece033, 0xa4aff6ea, 0xa06efb5d, + 0xd4316d90, 0xd0f06027, 0xddb376fe, 0xd9727b49, + 0xc7355b4c, 0xc3f456fb, 0xceb74022, 0xca764d95, + 0xf2390028, 0xf6f80d9f, 0xfbbb1b46, 0xff7a16f1, + 0xe13d36f4, 0xe5fc3b43, 0xe8bf2d9a, 0xec7e202d, + 0x34826077, 0x30436dc0, 0x3d007b19, 0x39c176ae, + 0x278656ab, 0x23475b1c, 0x2e044dc5, 0x2ac54072, + 0x128a0dcf, 0x164b0078, 0x1b0816a1, 0x1fc91b16, + 0x018e3b13, 0x054f36a4, 0x080c207d, 0x0ccd2dca, + 0x7892bb07, 0x7c53b6b0, 0x7110a069, 0x75d1adde, + 0x6b968ddb, 0x6f57806c, 0x621496b5, 0x66d59b02, + 0x5e9ad6bf, 0x5a5bdb08, 0x5718cdd1, 0x53d9c066, + 0x4d9ee063, 0x495fedd4, 0x441cfb0d, 0x40ddf6ba, + 0xaca3d697, 0xa862db20, 0xa521cdf9, 0xa1e0c04e, + 0xbfa7e04b, 0xbb66edfc, 0xb625fb25, 0xb2e4f692, + 0x8aabbb2f, 0x8e6ab698, 0x8329a041, 0x87e8adf6, + 0x99af8df3, 0x9d6e8044, 0x902d969d, 0x94ec9b2a, + 0xe0b30de7, 0xe4720050, 0xe9311689, 0xedf01b3e, + 0xf3b73b3b, 0xf776368c, 0xfa352055, 0xfef42de2, + 0xc6bb605f, 0xc27a6de8, 0xcf397b31, 0xcbf87686, + 0xd5bf5683, 0xd17e5b34, 0xdc3d4ded, 0xd8fc405a, + 0x6904c0ee, 0x6dc5cd59, 0x6086db80, 0x6447d637, + 0x7a00f632, 0x7ec1fb85, 0x7382ed5c, 0x7743e0eb, + 0x4f0cad56, 0x4bcda0e1, 0x468eb638, 0x424fbb8f, + 0x5c089b8a, 0x58c9963d, 0x558a80e4, 0x514b8d53, + 0x25141b9e, 0x21d51629, 0x2c9600f0, 0x28570d47, + 0x36102d42, 0x32d120f5, 0x3f92362c, 0x3b533b9b, + 0x031c7626, 0x07dd7b91, 0x0a9e6d48, 0x0e5f60ff, + 0x101840fa, 0x14d94d4d, 0x199a5b94, 0x1d5b5623, + 0xf125760e, 0xf5e47bb9, 0xf8a76d60, 0xfc6660d7, + 0xe22140d2, 0xe6e04d65, 0xeba35bbc, 0xef62560b, + 0xd72d1bb6, 0xd3ec1601, 0xdeaf00d8, 0xda6e0d6f, + 0xc4292d6a, 0xc0e820dd, 0xcdab3604, 0xc96a3bb3, + 0xbd35ad7e, 0xb9f4a0c9, 0xb4b7b610, 0xb076bba7, + 0xae319ba2, 0xaaf09615, 0xa7b380cc, 0xa3728d7b, + 0x9b3dc0c6, 0x9ffccd71, 0x92bfdba8, 0x967ed61f, + 0x8839f61a, 0x8cf8fbad, 0x81bbed74, 0x857ae0c3, + 0x5d86a099, 0x5947ad2e, 0x5404bbf7, 0x50c5b640, + 0x4e829645, 0x4a439bf2, 0x47008d2b, 0x43c1809c, + 0x7b8ecd21, 0x7f4fc096, 0x720cd64f, 0x76cddbf8, + 0x688afbfd, 0x6c4bf64a, 0x6108e093, 0x65c9ed24, + 0x11967be9, 0x1557765e, 0x18146087, 0x1cd56d30, + 0x02924d35, 0x06534082, 0x0b10565b, 0x0fd15bec, + 0x379e1651, 0x335f1be6, 0x3e1c0d3f, 0x3add0088, + 0x249a208d, 0x205b2d3a, 0x2d183be3, 0x29d93654, + 0xc5a71679, 0xc1661bce, 0xcc250d17, 0xc8e400a0, + 0xd6a320a5, 0xd2622d12, 0xdf213bcb, 0xdbe0367c, + 0xe3af7bc1, 0xe76e7676, 0xea2d60af, 0xeeec6d18, + 0xf0ab4d1d, 0xf46a40aa, 0xf9295673, 0xfde85bc4, + 0x89b7cd09, 0x8d76c0be, 0x8035d667, 0x84f4dbd0, + 0x9ab3fbd5, 0x9e72f662, 0x9331e0bb, 0x97f0ed0c, + 0xafbfa0b1, 0xab7ead06, 0xa63dbbdf, 0xa2fcb668, + 0xbcbb966d, 0xb87a9bda, 0xb5398d03, 0xb1f880b4, +}; + +static u32 rk_crc_32(unsigned char *buf, u32 len) +{ + u32 i; + u32 crc = 0; + + for (i = 0; i < len; i++) + crc = (crc << 8) ^ g_crc32_tbl[(crc >> 24) ^ *buf++]; + return crc; +} +#endif + +static long vendor_storage_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long ret = -1; + int size; + struct RK_VENDOR_REQ *v_req; + u32 *page_buf; + + page_buf = kmalloc(4096, GFP_KERNEL); + if (!page_buf) + return -ENOMEM; + + mutex_lock(&vendor_ops_mutex); + + v_req = (struct RK_VENDOR_REQ *)page_buf; + + switch (cmd) { + case VENDOR_READ_IO: + { + if (copy_from_user(page_buf, (void __user *)arg, 8)) { + ret = -EFAULT; + break; + } + if (v_req->tag == VENDOR_REQ_TAG) { + size = emmc_vendor_read(v_req->id, v_req->data, + v_req->len); + if (size != -1) { + v_req->len = size; + ret = 0; + if (copy_to_user((void __user *)arg, + page_buf, + v_req->len + 8)) + ret = -EFAULT; + } + } + } break; + case VENDOR_WRITE_IO: + { + if (copy_from_user(page_buf, (void __user *)arg, 8)) { + ret = -EFAULT; + break; + } + if (v_req->tag == VENDOR_REQ_TAG && (v_req->len < 4096 - 8)) { + if (copy_from_user(page_buf, (void __user *)arg, + v_req->len + 8)) { + ret = -EFAULT; + break; + } + ret = emmc_vendor_write(v_req->id, + v_req->data, + v_req->len); + } + } break; + +#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER + case READ_SECTOR_IO: + { + if (copy_from_user(page_buf, (void __user *)arg, 512)) { + ret = -EFAULT; + goto exit; + } + + size = page_buf[1]; + if (size <= 8) { + id_blk_read_data(page_buf[0], size, (u8 *)page_buf); + if (copy_to_user((void __user *)arg, page_buf, + size * 512)) { + ret = -EFAULT; + goto exit; + } + } else { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; + + case WRITE_SECTOR_IO: + { + if (copy_from_user(page_buf, (void __user *)arg, 4096)) { + ret = -EFAULT; + goto exit; + } + if (!g_idb_buffer) { + g_idb_buffer = kmalloc(4096 + EMMC_BOOT_PART_SIZE * 512, + GFP_KERNEL); + if (!g_idb_buffer) { + ret = -EFAULT; + goto exit; + } + } + if (page_buf[1] <= 4088 && page_buf[0] <= + (EMMC_BOOT_PART_SIZE * 512 - 4096)) { + memcpy(g_idb_buffer + page_buf[0], page_buf + 2, + page_buf[1]); + } else { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; + + case END_WRITE_SECTOR_IO: + { + if (copy_from_user(page_buf, (void __user *)arg, 28)) { + ret = -EFAULT; + goto exit; + } + if (page_buf[0] <= (EMMC_BOOT_PART_SIZE * 512)) { + if (!g_idb_buffer) { + ret = -EFAULT; + goto exit; + } + if (page_buf[1] != + rk_crc_32(g_idb_buffer, page_buf[0])) { + ret = -2; + goto exit; + } + ret = emmc_write_idblock(page_buf[0], + (u8 *)g_idb_buffer, + &page_buf[2]); + kfree(g_idb_buffer); + g_idb_buffer = NULL; + } else { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; + + case GET_BAD_BLOCK_IO: + { + memset(page_buf, 0, 64); + if (copy_to_user((void __user *)arg, page_buf, 64)) { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; + + case GET_LOCK_FLAG_IO: + { + page_buf[0] = 0; + if (copy_to_user((void __user *)arg, page_buf, 4)) { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; + + case GET_FLASH_INFO_IO: + { + page_buf[0] = 0x00800000; + page_buf[1] = 0x00040400; + page_buf[2] = 0x00010028; + if (copy_to_user((void __user *)arg, page_buf, 11)) { + ret = -EFAULT; + goto exit; + } + ret = 0; + } break; +#endif + + default: + ret = -EINVAL; + goto exit; + } +exit: + mutex_unlock(&vendor_ops_mutex); + kfree(page_buf); + return ret; +} + +static const struct file_operations vendor_storage_fops = { + .open = vendor_storage_open, + .compat_ioctl = vendor_storage_ioctl, + .unlocked_ioctl = vendor_storage_ioctl, + .release = vendor_storage_release, +}; + +static struct miscdevice vender_storage_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vendor_storage", + .fops = &vendor_storage_fops, +}; + +static int vendor_init_thread(void *arg) +{ + int ret; + unsigned long timeout = jiffies + 3 * HZ; + + g_vendor = kmalloc(sizeof(*g_vendor), GFP_KERNEL | GFP_DMA); + if (!g_vendor) + return -ENOMEM; + + do { + ret = emmc_vendor_storage_init(); + if (!ret || time_after(jiffies, timeout)) + break; + /* sleep wait emmc initialize completed */ + msleep(100); + } while (1); + + if (!ret) { + ret = misc_register(&vender_storage_dev); + rk_vendor_register(emmc_vendor_read, emmc_vendor_write); + } else { + kfree(g_vendor); + g_vendor = NULL; + } + pr_info("vendor storage:20190527 ret = %d\n", ret); + return ret; +} + +static int __init vendor_storage_init(void) +{ + g_idb_buffer = NULL; + kthread_run(vendor_init_thread, (void *)NULL, "vendor_storage_init"); + return 0; +} + +static __exit void vendor_storage_deinit(void) +{ + if (g_vendor) { + misc_deregister(&vender_storage_dev); + kfree(g_vendor); + g_vendor = NULL; + } +} + +device_initcall_sync(vendor_storage_init); +module_exit(vendor_storage_deinit); +MODULE_LICENSE("GPL");