diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index e299ca4a0c9b..7c9e7e768928 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -140,6 +140,12 @@ config ROCKCHIP_THUNDER_BOOT_SFC help Say y if boot from SPI Flash from SFC controller. +config ROCKCHIP_THUNDER_BOOT_CRYPTO + bool "Rockchip Thunder Boot support hardware crypto" + depends on ROCKCHIP_THUNDER_BOOT + help + Say y if boot need hardware crypto. + config ROCKCHIP_SCHED_PERFORMANCE_BIAS bool "Rockchip task scheduler optimization" default y diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index d42756408be0..9b6fb071213a 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_ROCKCHIP_PVTM) += rockchip_pvtm.o obj-$(CONFIG_ROCKCHIP_RAMDISK) += rockchip_ramdisk.o obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o obj-$(CONFIG_ROCKCHIP_SYSTEM_MONITOR) += rockchip_system_monitor.o +obj-$(CONFIG_ROCKCHIP_THUNDER_BOOT_CRYPTO) += rockchip_thunderboot_crypto.o obj-$(CONFIG_ROCKCHIP_THUNDER_BOOT_MMC) += rockchip_thunderboot_mmc.o obj-$(CONFIG_ROCKCHIP_THUNDER_BOOT_SFC) += rockchip_thunderboot_sfc.o obj-$(CONFIG_ROCKCHIP_DEBUG) += rockchip_debug.o diff --git a/drivers/soc/rockchip/rockchip_thunderboot_crypto.c b/drivers/soc/rockchip/rockchip_thunderboot_crypto.c new file mode 100644 index 000000000000..7a3c93a0304e --- /dev/null +++ b/drivers/soc/rockchip/rockchip_thunderboot_crypto.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHA256_PROBE_TIMEOUT 1000 +#define SHA256_COMPARE_TIMEOUT 2000 +#define SHA256_HASH_SIZE 32 +#define _SBF(s, v) ((v) << (s)) +#define CRYPTO_WRITE_MASK_SHIFT (16) +#define CRYPTO_WRITE_MASK_ALL ((0xffffu << CRYPTO_WRITE_MASK_SHIFT)) + +/* Crypto DMA control registers*/ +#define CRYPTO_DMA_INT_EN 0x0008 +#define CRYPTO_ZERO_ERR_INT_EN BIT(6) +#define CRYPTO_LIST_ERR_INT_EN BIT(5) +#define CRYPTO_SRC_ERR_INT_EN BIT(4) +#define CRYPTO_DST_ERR_INT_EN BIT(3) +#define CRYPTO_SRC_ITEM_INT_EN BIT(2) +#define CRYPTO_DST_ITEM_DONE_INT_EN BIT(1) +#define CRYPTO_LIST_DONE_INT_EN BIT(0) + +#define CRYPTO_DMA_INT_ST 0x000C +#define CRYPTO_ZERO_LEN_INT_ST BIT(6) +#define CRYPTO_LIST_ERR_INT_ST BIT(5) +#define CRYPTO_SRC_ERR_INT_ST BIT(4) +#define CRYPTO_DST_ERR_INT_ST BIT(3) +#define CRYPTO_SRC_ITEM_DONE_INT_ST BIT(2) +#define CRYPTO_DST_ITEM_DONE_INT_ST BIT(1) +#define CRYPTO_LIST_DONE_INT_ST BIT(0) + +#define CRYPTO_DMA_CTL 0x0010 +#define CRYPTO_DMA_RESTART BIT(1) +#define CRYPTO_DMA_START BIT(0) + +/* DMA LIST Start Address Register */ +#define CRYPTO_DMA_LLI_ADDR 0x0014 + +#define CRYPTO_FIFO_CTL 0x0040 +#define CRYPTO_DOUT_BYTESWAP BIT(1) +#define CRYPTO_DOIN_BYTESWAP BIT(0) + +/* Hash Control Register */ +#define CRYPTO_HASH_CTL 0x0048 +#define CRYPTO_SHA1 _SBF(4, 0x00) +#define CRYPTO_MD5 _SBF(4, 0x01) +#define CRYPTO_SHA256 _SBF(4, 0x02) +#define CRYPTO_SHA224 _SBF(4, 0x03) +#define CRYPTO_SM3 _SBF(4, 0x06) +#define CRYPTO_SHA512 _SBF(4, 0x08) +#define CRYPTO_SHA384 _SBF(4, 0x09) +#define CRYPTO_SHA512_224 _SBF(4, 0x0A) +#define CRYPTO_SHA512_256 _SBF(4, 0x0B) +#define CRYPTO_HMAC_ENABLE BIT(3) +#define CRYPTO_HW_PAD_ENABLE BIT(2) +#define CRYPTO_HASH_SRC_SEL BIT(1) +#define CRYPTO_HASH_ENABLE BIT(0) + +#define CRYPTO_HASH_DOUT_0 0x03a0 +#define CRYPTO_HASH_DOUT_1 0x03a4 +#define CRYPTO_HASH_DOUT_2 0x03a8 +#define CRYPTO_HASH_DOUT_3 0x03ac +#define CRYPTO_HASH_DOUT_4 0x03b0 +#define CRYPTO_HASH_DOUT_5 0x03b4 +#define CRYPTO_HASH_DOUT_6 0x03b8 +#define CRYPTO_HASH_DOUT_7 0x03bc +#define CRYPTO_HASH_DOUT_8 0x03c0 +#define CRYPTO_HASH_DOUT_9 0x03c4 +#define CRYPTO_HASH_DOUT_10 0x03c8 +#define CRYPTO_HASH_DOUT_11 0x03cc +#define CRYPTO_HASH_DOUT_12 0x03d0 +#define CRYPTO_HASH_DOUT_13 0x03d4 +#define CRYPTO_HASH_DOUT_14 0x03d8 +#define CRYPTO_HASH_DOUT_15 0x03dc + +#define CRYPTO_HASH_VALID 0x03e4 +#define CRYPTO_HASH_IS_VALID BIT(0) + +#define LLI_DMA_CTRL_LAST BIT(0) +#define LLI_DMA_CTRL_PAUSE BIT(1) +#define LLI_DMA_CTRL_LIST_DONE BIT(8) +#define LLI_DMA_CTRL_DST_DONE BIT(9) +#define LLI_DMA_CTRL_SRC_DONE BIT(10) + +#define LLI_USER_CPIHER_START BIT(0) +#define LLI_USER_STRING_START BIT(1) +#define LLI_USER_STRING_LAST BIT(2) +#define LLI_USER_STRING_ADA BIT(3) +#define LLI_USER_PRIVACY_KEY BIT(7) +#define LLI_USER_ROOT_KEY BIT(8) + +#define CRYPTO_READ(dev, offset) \ + readl_relaxed(((dev)->reg + (offset))) +#define CRYPTO_WRITE(dev, offset, val) \ + writel_relaxed((val), ((dev)->reg + (offset))) + +#ifdef DEBUG +#define CRYPTO_TRACE(format, ...) pr_err("[%s, %05d]-trace: " format "\n", \ + __func__, __LINE__, ##__VA_ARGS__) +#define CRYPTO_MSG(format, ...) pr_err("[%s, %05d]-msg:" format "\n", \ + __func__, __LINE__, ##__VA_ARGS__) +#define CRYPTO_DUMPHEX(var_name, data, len) \ + print_hex_dump(KERN_CONT, (var_name), \ + DUMP_PREFIX_OFFSET, \ + 16, 1, (data), (len), false) +#else +#define CRYPTO_TRACE(format, ...) +#define CRYPTO_MSG(format, ...) +#define CRYPTO_DUMPHEX(var_name, data, len) +#endif + +struct crypto_lli_desc { + u32 src_addr; + u32 src_len; + u32 dst_addr; + u32 dst_len; + u32 user_define; + u32 reserve; + u32 dma_ctrl; + u32 next_addr; +}; + +struct crypto_data { + struct device *dev; + void __iomem *reg; + int irq; + int clks_num; + struct clk_bulk_data *clk_bulks; + struct crypto_lli_desc *desc; + dma_addr_t desc_dma; + int calc_ret; + void (*done_cb)(void *user_data, + int hash_ret, + u8 *hash_val); + void *cb_data; + u8 *hash; +}; + +enum endian_mode { + BIG_ENDIAN = 0, + LITTLE_ENDIAN +}; + +static struct crypto_data *g_crypto_info; +static DECLARE_COMPLETION(sha256_probe_complete); + +static DECLARE_WAIT_QUEUE_HEAD(crypto_sha256_compare_done); +static bool compare_done; + +int __init rk_tb_crypto_sha256_wait_compare_done(void) +{ + if (wait_event_timeout(crypto_sha256_compare_done, compare_done, + SHA256_COMPARE_TIMEOUT)) + return 0; + + return -ETIMEDOUT; +} + +static void word2byte(u32 word, u8 *ch, u32 endian) +{ + /* 0: Big-Endian 1: Little-Endian */ + if (endian == BIG_ENDIAN) { + ch[0] = (word >> 24) & 0xff; + ch[1] = (word >> 16) & 0xff; + ch[2] = (word >> 8) & 0xff; + ch[3] = (word >> 0) & 0xff; + } else if (endian == LITTLE_ENDIAN) { + ch[0] = (word >> 0) & 0xff; + ch[1] = (word >> 8) & 0xff; + ch[2] = (word >> 16) & 0xff; + ch[3] = (word >> 24) & 0xff; + } else { + ch[0] = 0; + ch[1] = 0; + ch[2] = 0; + ch[3] = 0; + } +} + +static void sha256_done_cb(void *user_data, int hash_ret, u8 *hash_val) +{ + CRYPTO_TRACE(); + if (!memcmp(user_data, hash_val, 32)) { + compare_done = true; + wake_up(&crypto_sha256_compare_done); + } +} + +static inline void clear_hash_out_reg(struct crypto_data *dev) +{ + int i; + + /*clear out register*/ + for (i = 0; i < 16; i++) + CRYPTO_WRITE(dev, CRYPTO_HASH_DOUT_0 + 4 * i, 0); +} + +static int get_hash_value(struct crypto_data *dev, u8 *data, u32 data_len) +{ + int ret = 0; + u32 i, offset; + + offset = CRYPTO_HASH_DOUT_0; + for (i = 0; i < data_len / 4; i++, offset += 4) + word2byte(CRYPTO_READ(dev, offset), data + i * 4, BIG_ENDIAN); + + if (data_len % 4) { + uint8_t tmp_buf[4]; + + word2byte(CRYPTO_READ(dev, offset), tmp_buf, BIG_ENDIAN); + memcpy(data + i * 4, tmp_buf, data_len % 4); + } + + CRYPTO_WRITE(dev, CRYPTO_HASH_VALID, CRYPTO_HASH_IS_VALID); + + return ret; +} + +static void rk_tb_crypto_disable_clk(struct crypto_data *dev) +{ + dev_dbg(dev->dev, "clk_bulk_disable_unprepare.\n"); + + clk_bulk_disable_unprepare(dev->clks_num, dev->clk_bulks); +} + +static irqreturn_t rk_tb_crypto_irq_handle(int irq, void *dev_id) +{ + struct crypto_data *crypto_info = platform_get_drvdata(dev_id); + + CRYPTO_TRACE("xxxxxxxxxx irq xxxxxxxxxx"); + + if (crypto_info) { + u32 interrupt_status; + + get_hash_value(crypto_info, crypto_info->hash, SHA256_HASH_SIZE); + CRYPTO_WRITE(crypto_info, CRYPTO_HASH_CTL, CRYPTO_WRITE_MASK_ALL | 0); + interrupt_status = CRYPTO_READ(crypto_info, CRYPTO_DMA_INT_ST); + CRYPTO_WRITE(crypto_info, CRYPTO_DMA_INT_ST, interrupt_status); + if (interrupt_status == CRYPTO_LIST_DONE_INT_ST) + crypto_info->calc_ret = 0; + + CRYPTO_TRACE("interrupt_status = %08x", interrupt_status); + if (crypto_info->done_cb) + crypto_info->done_cb(crypto_info->cb_data, + crypto_info->calc_ret, + crypto_info->hash); + + rk_tb_crypto_disable_clk(crypto_info); + } + + return IRQ_HANDLED; +} + +int rk_tb_sha256(dma_addr_t data, size_t data_len, void *user_data) +{ + u32 reg_ctrl = 0; + struct crypto_data *crypto_info; + + wait_for_completion_interruptible_timeout(&sha256_probe_complete, + SHA256_PROBE_TIMEOUT); + crypto_info = g_crypto_info; + if (!crypto_info) + return -ENODEV; + + if (data % 4) + return -EINVAL; + + clear_hash_out_reg(crypto_info); + + reg_ctrl = CRYPTO_SHA256 | CRYPTO_HW_PAD_ENABLE; + CRYPTO_WRITE(crypto_info, CRYPTO_HASH_CTL, + reg_ctrl | CRYPTO_WRITE_MASK_ALL); + + reg_ctrl = CRYPTO_ZERO_ERR_INT_EN | + CRYPTO_LIST_ERR_INT_EN | + CRYPTO_SRC_ERR_INT_EN | + CRYPTO_DST_ERR_INT_EN | + CRYPTO_LIST_DONE_INT_EN; + + CRYPTO_WRITE(crypto_info, CRYPTO_FIFO_CTL, 0x00030003); + CRYPTO_WRITE(crypto_info, CRYPTO_DMA_INT_EN, reg_ctrl); + + memset(crypto_info->desc, 0x00, sizeof(*crypto_info->desc)); + + crypto_info->desc->src_addr = (u32)data; + crypto_info->desc->src_len = data_len; + crypto_info->desc->next_addr = 0; + crypto_info->desc->dma_ctrl = LLI_DMA_CTRL_LIST_DONE | + LLI_DMA_CTRL_LAST; + crypto_info->desc->user_define = LLI_USER_CPIHER_START | + LLI_USER_STRING_START | + LLI_USER_STRING_LAST; +#ifdef CONFIG_ARM64 + __flush_dcache_area((void *)crypto_info->desc, + sizeof(struct crypto_data)); +#else + __cpuc_flush_dcache_area((void *)crypto_info->desc, + sizeof(struct crypto_data)); +#endif + CRYPTO_WRITE(crypto_info, CRYPTO_DMA_LLI_ADDR, crypto_info->desc_dma); + CRYPTO_WRITE(crypto_info, CRYPTO_HASH_CTL, + (CRYPTO_HASH_ENABLE << + CRYPTO_WRITE_MASK_SHIFT) | + CRYPTO_HASH_ENABLE); + + CRYPTO_WRITE(crypto_info, CRYPTO_DMA_CTL, 0x00010001); /* start */ + + crypto_info->calc_ret = -1; + + crypto_info->done_cb = sha256_done_cb; + crypto_info->cb_data = user_data; + crypto_info->hash = devm_kzalloc(crypto_info->dev, 32, GFP_KERNEL); + if (!crypto_info->hash) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(rk_tb_sha256); + +static int __init rk_tb_crypto_probe(struct platform_device *pdev) +{ + struct crypto_data *crypto_info; + struct resource *res; + int ret = 0; + + CRYPTO_TRACE(); + + crypto_info = devm_kzalloc(&pdev->dev, sizeof(*crypto_info), + GFP_KERNEL); + if (!crypto_info) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + crypto_info->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(crypto_info->reg)) { + dev_err(crypto_info->dev, + "devm_ioremap_resource crypto reg error.\n"); + ret = PTR_ERR(crypto_info->reg); + goto exit; + } + + crypto_info->dev = &pdev->dev; + crypto_info->clks_num = + devm_clk_bulk_get_all(&pdev->dev, &crypto_info->clk_bulks); + if (crypto_info->clks_num < 0) { + dev_err(&pdev->dev, "failed to get clks property\n"); + ret = -ENODEV; + goto exit; + } + + ret = clk_bulk_prepare_enable(crypto_info->clks_num, crypto_info->clk_bulks); + if (ret) { + dev_err(&pdev->dev, "failed to enable clks\n"); + goto exit; + } + + crypto_info->irq = platform_get_irq(pdev, 0); + if (crypto_info->irq < 0) { + dev_err(crypto_info->dev, + "control Interrupt is not available.\n"); + ret = crypto_info->irq; + goto exit; + } + + ret = devm_request_irq(&pdev->dev, crypto_info->irq, + rk_tb_crypto_irq_handle, IRQF_SHARED, + "rk-tb-crypto", pdev); + + if (ret) { + dev_err(crypto_info->dev, "irq request failed.\n"); + goto exit; + } + + crypto_info->desc = devm_kzalloc(&pdev->dev, sizeof(struct crypto_data), + GFP_KERNEL | GFP_DMA); + crypto_info->desc_dma = (dma_addr_t)virt_to_phys(crypto_info->desc); + if (!crypto_info->desc) { + dev_err(crypto_info->dev, "desc alloc failed.\n"); + ret = -ENOMEM; + goto exit; + } + + g_crypto_info = crypto_info; + platform_set_drvdata(pdev, crypto_info); + complete(&sha256_probe_complete); +exit: + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id rk_tb_crypto_dt_match[] = { + { .compatible = "rockchip,rv1126-crypto" }, + {}, +}; +#endif + +static struct platform_driver rk_tb_crypto_driver = { + .driver = { + .name = "rockchip_thunder_boot_crypto", + .of_match_table = rk_tb_crypto_dt_match, + }, +}; + +static int __init rk_tb_crypto_init(void) +{ + struct device_node *node; + + CRYPTO_TRACE(); + + node = of_find_matching_node(NULL, rk_tb_crypto_dt_match); + if (node) { + of_platform_device_create(node, NULL, NULL); + of_node_put(node); + return platform_driver_probe(&rk_tb_crypto_driver, + rk_tb_crypto_probe); + } + + CRYPTO_TRACE(); + + return 0; +} + +pure_initcall(rk_tb_crypto_init); diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h index 0e9b15a00975..26953171620f 100644 --- a/include/linux/initramfs.h +++ b/include/linux/initramfs.h @@ -33,4 +33,8 @@ int __init default_rootfs(void); void __init wait_initrd_hw_decom_done(void); #endif +#if defined(CONFIG_ROCKCHIP_THUNDER_BOOT_CRYPTO) +int __init rk_tb_crypto_sha256_wait_compare_done(void); +#endif + #endif /* _LINUX_INITRAMFS_H */ diff --git a/include/linux/soc/rockchip/rockchip_thunderboot_crypto.h b/include/linux/soc/rockchip/rockchip_thunderboot_crypto.h new file mode 100644 index 000000000000..2fe176649409 --- /dev/null +++ b/include/linux/soc/rockchip/rockchip_thunderboot_crypto.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2021 Rockchip Electronics Co., Ltd */ + +#ifndef _ROCKCHIP_THUNDERBOOT_CRYPTO_ +#define _ROCKCHIP_THUNDERBOOT_CRYPTO_ + +int rk_tb_sha256(dma_addr_t data, size_t data_len, void *user_data); + +#endif