/* * The driver of sunxi emce. * * Copyright (C) 2016 Allwinner. * * zhouhuacai * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include #include #include #include #include #include #include #include #include #include #include "sunxi_emce.h" #ifdef CONFIG_OF #include #include #include static const struct of_device_id sunxi_emce_of_match[] = { {.compatible = "allwinner,sunxi-emce",}, {}, }; MODULE_DEVICE_TABLE(of, sunxi_emce_of_match); #endif #define STORAGE_NAND 0 #define STORAGE_EMMC 2 static int boot_type_cmdline = -1; struct sunxi_emce_t *emce_dev; struct task_des *task_des_buf; dma_addr_t task_des_addr; static unsigned int boot_type_parse(char *boot_type) { long int ret; int tmp; tmp = kstrtol(boot_type, 0, &ret); if (ret == STORAGE_NAND) return 0; if (ret == STORAGE_EMMC) return 1; return -1; } static int __init early_boot_type(char *boot_type) { pr_debug("%s(%s)\n", __func__, boot_type); boot_type_cmdline = boot_type_parse(boot_type); return 0; } early_param("boot_type", early_boot_type); void sunxi_emce_dump(void *addr, u32 size) { int i; pr_cont("=========dump:size:0x%x=========\n", size); for (i = 0; i < size; i += 4) { if (!(i&0xf)) pr_cont("\n0x%p : ", (addr + i)); pr_cont("%08x ", readl(addr + i)); } pr_cont("\n"); } static DEFINE_MUTEX(emce_lock); static void emce_dev_lock(void) { mutex_lock(&emce_lock); } static void emce_dev_unlock(void) { mutex_unlock(&emce_lock); } static int sunxi_emce_gen_salt(const u8 *mkey, u32 mklen, u8 *skey, u32 *sklen) { int ret = 0; struct scatterlist sg = {0}; struct crypto_ahash *tfm = NULL; struct ahash_request *req = NULL; EMCE_ENTER(); tfm = crypto_alloc_ahash("sha256", CRYPTO_ALG_TYPE_AHASH, CRYPTO_ALG_TYPE_AHASH_MASK); if (IS_ERR(tfm)) { ret = PTR_ERR(tfm); EMCE_ERR("Failed to alloc sha256 tfm. %p\n", tfm); goto out2; } req = ahash_request_alloc(tfm, GFP_KERNEL); if (!req) EMCE_ERR("Failed to alloc ahash request!\n"); ahash_request_set_callback(req, 0, NULL, NULL); sg_init_one(&sg, mkey, mklen); ahash_request_set_crypt(req, &sg, skey, mklen); ret = crypto_ahash_digest(req); if (ret) { EMCE_ERR("Failed to do SHA256(), mklen: %d\n", mklen); goto out1; } *sklen = EMCE_SALT_KEY_LEN; out1: ahash_request_free(req); crypto_free_ahash(tfm); out2: return ret; } static inline void sunxi_emce_set_bit(void __iomem *reg_addr, u32 mask, u32 bit, u32 val) { u32 reg_val = 0; reg_val = readl(reg_addr); reg_val &= (~mask); reg_val |= (val << bit); writel(reg_val, reg_addr); } static inline u32 sunxi_emce_get_bit(void __iomem *reg_addr, u32 mask, u32 bit) { u32 reg_val = 0; reg_val = readl(reg_addr); reg_val &= mask; return reg_val >> bit; } static void emce_get_key_len(u32 key1_len, u32 key2_len, struct sunxi_emce_regs *reg) { if (reg) { switch (key1_len) { case 128: reg->key1_len = EMCE_KEY_128_BIT; break; case 192: reg->key1_len = EMCE_KEY_192_BIT; break; case 256: reg->key1_len = EMCE_KEY_256_BIT; break; default: break; } switch (key2_len) { case 128: reg->key2_len = EMCE_KEY_128_BIT; break; case 192: reg->key2_len = EMCE_KEY_192_BIT; break; case 256: reg->key2_len = EMCE_KEY_256_BIT; break; default: break; } } } static int sunxi_emce_set_cfg(enum cfg_cmd cmd, u32 para) { int ret = 0; void __iomem *reg_addr = emce_dev->base_addr + EMCE_MODE; EMCE_ENTER(); emce_dev_lock(); switch (cmd) { case EMCE_SET_SECTOR_SIZE: sunxi_emce_set_bit(reg_addr, EMCE_SECTOR_SIZE_MASK, EMCE_SECTOR_SIZE_BIT, para); break; case EMCE_SET_IV_LEN: sunxi_emce_set_bit(reg_addr, EMCE_IV_LEN_MASK, EMCE_IV_LEN_BIT, para); break; case EMCE_SET_KEY_LEN: sunxi_emce_set_bit(reg_addr, EMCE_KEY_LEN_MASK, EMCE_KEY_LEN_BIT, para); break; case EMCE_SET_MODE: sunxi_emce_set_bit(reg_addr, EMCE_MODE_MASK, EMCE_MODE_BIT, para); break; default: EMCE_ERR("Unsupport cmd %d\n", cmd); break; } emce_dev_unlock(); return ret; } void sunxi_emce_set_mode(u32 para) { if (emce_dev->emce_vision == EMCE_VISION_1) sunxi_emce_set_cfg(EMCE_SET_MODE, para); else emce_dev->regs.crypto_mode = para; } EXPORT_SYMBOL_GPL(sunxi_emce_set_mode); void sunxi_emce_set_task_des(int data_len, int bypass) { struct sunxi_emce_regs *reg = &emce_dev->regs; int i; if (emce_dev->emce_vision == EMCE_VISION_1) return; sunxi_emce_set_mode(0x1); task_des_buf->com_ctrl = (reg->key2_len << 12) | (reg->key1_len << 8) | (reg->crypto_mode << 4) | bypass; task_des_buf->data_len = data_len / 4; for (i = 0; i < 8; i++) { task_des_buf->key1[i] = reg->emce_key[i]; task_des_buf->key2[i] = reg->emce_salt[i]; } task_des_buf->next_task = 0x0; emce_writel(emce_dev, EMCE_TASK_DES_ADD, (u32)(task_des_addr)); udelay(10); } EXPORT_SYMBOL_GPL(sunxi_emce_set_task_des); void sunxi_emce_set_task_load(int para) { if (emce_dev->emce_vision == EMCE_VISION_1) return; emce_writel(emce_dev, EMCE_TASK_LOAD, para); } EXPORT_SYMBOL_GPL(sunxi_emce_set_task_load); void sunxi_emce_set_sector_size(u32 para) { sunxi_emce_set_cfg(EMCE_SET_SECTOR_SIZE, para); } EXPORT_SYMBOL_GPL(sunxi_emce_set_sector_size); void sunxi_emce_set_key_len(u32 para) { sunxi_emce_set_cfg(EMCE_SET_KEY_LEN, para); sunxi_emce_set_cfg(EMCE_SET_IV_LEN, para); } static void sunxi_emce_set_cur_controller(struct sunxi_emce_t *emce_pdev, u32 para) { void __iomem *reg_addr = emce_pdev->base_addr + EMCE_MODE; sunxi_emce_set_bit(reg_addr, EMCE_CUR_CTR_MASK, EMCE_CUR_CTR_BIT, para); } static void sunxi_emce_set_masterkey(const u8 *mkey, u32 klen) { int i = 0; void __iomem *reg_addr = emce_dev->base_addr + EMCE_MASTER_KEY_OFFSET; if ((mkey == NULL) || (klen <= 0)) EMCE_ERR("sunxi emce masterkey error\n "); for (i = 0; i < klen; i = i + 4) writel(*(u32 *)(mkey + i), (reg_addr + i)); } static void sunxi_emce_set_saltkey(const u8 *skey, u32 klen) { int i = 0; void __iomem *reg_addr = emce_dev->base_addr + EMCE_SALT_KEY_OFFSET; if ((skey == NULL) || (klen <= 0)) EMCE_ERR("sunxi emce saltkey error\n "); for (i = 0; i < klen; i = i + 4) writel((*(u32 *)(skey + i)), reg_addr + i); } int sunxi_emce_set_key(const u8 *mkey, u32 len) { u8 saltkey[EMCE_SALT_KEY_LEN]; u32 skey_len = 0; struct sunxi_emce_regs *reg = &emce_dev->regs; int ret = 0; int i; EMCE_ENTER(); if ((mkey == NULL) || (len <= 0)) EMCE_ERR("sunxi emce key error\n "); if (emce_dev->emce_vision == EMCE_VISION_1) { ret = sunxi_emce_gen_salt(mkey, len, saltkey, &skey_len); if (!ret) { sunxi_emce_set_saltkey(saltkey, skey_len); sunxi_emce_set_masterkey(mkey, len); } } else { ret = sunxi_emce_gen_salt(mkey, len, saltkey, &skey_len); if (!ret) { for (i = 0; i < len; i = i + 4) reg->emce_key[(i >> 2)] = *(u32 *)(mkey + i); for (i = 0; i < skey_len; i = i + 4) reg->emce_salt[(i >> 2)] = *(u32 *)(saltkey + i); } emce_get_key_len(len, skey_len, reg); } return ret; } EXPORT_SYMBOL_GPL(sunxi_emce_set_key); static int sunxi_emce_get_version(void) { emce_dev->emce_vision = readl(emce_dev->base_addr + EMCE_VER); pr_info("[EMCE]:VER:0x%x\n", emce_dev->emce_vision); return emce_dev->emce_vision; } static void sunxi_emce_show_cur_controller(u32 cur_controller) { if (cur_controller) pr_info("[EMCE]:Current Controller is EMMC!\n"); else pr_info("[EMCE]:Current Controller is NAND!\n"); } static int sunxi_emce_hw_init(struct sunxi_emce_t *emce) { struct device_node *pnode = emce->pdev->dev.of_node; struct clk *pclk = NULL; int ret = 0; #if (defined CONFIG_FPGA_V4_PLATFORM) | (defined CONFIG_FPGA_V7_PLATFORM) emce->mclk = of_clk_get(pnode, 0); if (IS_ERR_OR_NULL(emce->mclk)) { EMCE_ERR("Unable to get mclk, return %x\n", PTR_RET(emce->mclk)); return PTR_RET(emce->mclk); } #else pclk = of_clk_get(pnode, 1); if (IS_ERR_OR_NULL(pclk)) { EMCE_ERR("Unable to get pclk, return %x\n", PTR_RET(pclk)); return PTR_RET(pclk); } emce->mclk = of_clk_get(pnode, 0); if (IS_ERR_OR_NULL(emce->mclk)) { EMCE_ERR("Unable to get mclk, return %x\n", PTR_RET(emce->mclk)); return PTR_RET(emce->mclk); } if (of_property_read_u32(pnode, "clock-frequency", &emce->gen_clkrate)) { EMCE_ERR("Unable to get clock-frequency.\n"); return -EINVAL; } ret = clk_set_parent(emce->mclk, pclk); if (ret) { EMCE_ERR("clk_set_parent() failed! return %d\n", ret); return ret; } ret = clk_set_rate(emce->mclk, emce->gen_clkrate); if (ret) { EMCE_ERR("clk_set_rate(%d) failed! return %d\n", emce->gen_clkrate, ret); return ret; } EMCE_DBG("[EMCE]: mclk %luMHz, pclk %luMHz\n", clk_get_rate(emce->mclk)/1000000, clk_get_rate(pclk)/1000000); #endif if (clk_prepare_enable(emce->mclk)) { EMCE_ERR("Couldn't enable module clock\n"); return -EBUSY; } /*just for fpga*/ #if (defined CONFIG_FPGA_V4_PLATFORM) | (defined CONFIG_FPGA_V7_PLATFORM) void __iomem *ccmu_gat_reg = NULL; ccmu_gat_reg = ioremap(0x3001804, 0x1); writel((readl(ccmu_gat_reg) | (0x1 << 24)), ccmu_gat_reg); pr_info("[EMCE] ccmu mask gating %08x\n", readl(ccmu_gat_reg)); #endif if (boot_type_cmdline != -1) { sunxi_emce_set_cur_controller(emce, boot_type_cmdline); sunxi_emce_show_cur_controller(boot_type_cmdline); } else { pr_info("[EMCE]:Unsupport Current Storage!\n"); return -EINVAL; } clk_put(pclk); return ret; } static int sunxi_emce_hw_exit(struct sunxi_emce_t *emce) { EMCE_EXIT(); clk_disable_unprepare(emce->mclk); clk_put(emce->mclk); emce->mclk = NULL; return 0; } static int sunxi_emce_res_request(struct platform_device *pdev) { struct device_node *np = NULL; struct sunxi_emce_t *emce = platform_get_drvdata(pdev); np = pdev->dev.of_node; if (!of_device_is_available(np)) { EMCE_ERR("sunxi emce is disable\n"); return -EPERM; } emce->base_addr = of_iomap(np, 0); if (emce->base_addr != 0) EMCE_DBG("sunxi emce reg base: %p !\n", emce->base_addr); else { EMCE_ERR("Failed to ioremap() io memory region.\n"); return -EBUSY; } return 0; } static int sunxi_emce_res_release(struct sunxi_emce_t *emce) { iounmap(emce->base_addr); return 0; } static void emce_get_cur_ctl_str(struct sunxi_emce_t *emce, char *str) { if (str == NULL) return; if (!sunxi_emce_get_bit(emce->base_addr + EMCE_MODE, EMCE_CUR_CTR_MASK, EMCE_CUR_CTR_BIT)) sprintf(str, "%s", "NDFC"); else sprintf(str, "%s", "SMHC"); } static void emce_get_key_len_str(u32 val, char *str) { if (str == NULL) return; if (emce_dev->emce_vision == EMCE_VISION_1) { switch (val) { case EMCE_KEY_128_BIT: sprintf(str, "%s", "128bit"); break; case EMCE_KEY_192_BIT: sprintf(str, "%s", "192bit"); break; case EMCE_KEY_256_BIT: sprintf(str, "%s", "256bit"); break; default: sprintf(str, "%s", "reserved"); break; } } } static void emce_get_mode(struct sunxi_emce_t *emce, char *str) { if (str == NULL) return; if (emce_dev->emce_vision == EMCE_VISION_1) { if (!sunxi_emce_get_bit(emce->base_addr + EMCE_MODE, EMCE_MODE_MASK, EMCE_MODE_BIT)) sprintf(str, "%s", "ECB"); else sprintf(str, "%s", "CBC"); } } static ssize_t sunxi_emce_info_show(struct device *dev, struct device_attribute *attr, char *buf) { u32 iv_len; u32 key_len; char cur_ctl_str[MAXNAME]; char iv_len_str[MAXNAME]; char key_len_str[MAXNAME]; char mode_str[MAXNAME]; struct platform_device *pdev; struct sunxi_emce_t *emce; pdev = container_of(dev, struct platform_device, dev); emce = platform_get_drvdata(pdev); iv_len = sunxi_emce_get_bit(emce->base_addr + EMCE_MODE, EMCE_IV_LEN_MASK, EMCE_IV_LEN_BIT), key_len = sunxi_emce_get_bit(emce->base_addr + EMCE_MODE, EMCE_KEY_LEN_MASK, EMCE_KEY_LEN_BIT), emce_get_cur_ctl_str(emce, cur_ctl_str); emce_get_mode(emce, mode_str); emce_get_key_len_str(iv_len, iv_len_str); emce_get_key_len_str(key_len, key_len_str); return snprintf(buf, PAGE_SIZE, "pdev->id = %d\n" "pdev->name = %s\n" "module clk rate = %ld Mhz\n" "IO membase = 0x%p\n" "sector size = 0x%x\n" "cur controller = %s\n" "AES IV len = %s\n" "key len = %s\n" "mode = %s\n" "emce version = 0x%x\n", pdev->id, pdev->name, (clk_get_rate(emce->mclk)/1000000), emce->base_addr, sunxi_emce_get_bit(emce->base_addr + EMCE_MODE, EMCE_SECTOR_SIZE_MASK, EMCE_SECTOR_SIZE_BIT), cur_ctl_str, iv_len_str, key_len_str, mode_str, readl(emce->base_addr + EMCE_VER)); } static struct device_attribute sunxi_emce_info_attr = __ATTR(info, S_IRUGO, sunxi_emce_info_show, NULL); static int sunxi_emce_sysfs_create(struct platform_device *_pdev) { int ret; ret = device_create_file(&_pdev->dev, &sunxi_emce_info_attr); return ret; } static void sunxi_emce_sysfs_remove(struct platform_device *_pdev) { device_remove_file(&_pdev->dev, &sunxi_emce_info_attr); } static int sunxi_emce_probe(struct platform_device *pdev) { u32 ret = 0; struct sunxi_emce_t *emce = NULL; emce = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_emce_t), GFP_KERNEL); if (emce == NULL) { EMCE_ERR("Unable to allocate emce_data\n"); return -ENOMEM; } snprintf(emce->dev_name, sizeof(emce->dev_name), SUNXI_EMCE_DEV_NAME); platform_set_drvdata(pdev, emce); ret = sunxi_emce_res_request(pdev); if (ret != 0) goto err0; emce->pdev = pdev; ret = sunxi_emce_hw_init(emce); if (ret != 0) { EMCE_ERR("emce hw init failed!\n"); goto err1; } emce_dev = emce; ret = sunxi_emce_get_version(); if (!ret) { EMCE_ERR("sunxi emce version error\n"); ret = -ENXIO; goto err1; } task_des_buf = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &task_des_addr, GFP_KERNEL); if (!task_des_buf) { EMCE_ERR("%s: Counld not allocate buffer\n", __func__); ret = -ENOMEM; goto err1; } ret = sunxi_emce_sysfs_create(pdev); if (ret) { EMCE_ERR("%s:create sys fs failed\n", __func__); goto err_free_buf; } return 0; err_free_buf: dma_free_coherent(&pdev->dev, PAGE_SIZE, task_des_buf, task_des_addr); err1: sunxi_emce_res_release(emce); err0: platform_set_drvdata(pdev, NULL); return ret; } static int sunxi_emce_remove(struct platform_device *pdev) { struct sunxi_emce_t *emce = platform_get_drvdata(pdev); sunxi_emce_sysfs_remove(pdev); sunxi_emce_hw_exit(emce); dma_free_coherent(&pdev->dev, PAGE_SIZE, task_des_buf, task_des_addr); sunxi_emce_res_release(emce); platform_set_drvdata(pdev, NULL); return 0; } static void sunxi_emce_regs_save(struct sunxi_emce_t *emce) { int i; struct sunxi_emce_regs *regs = &emce->regs; if (emce_dev->emce_vision == EMCE_VISION_1) { for (i = 0; i < 8; i++) { regs->emce_key[i] = emce_readl(emce, (EMCE_MASTER_KEY_OFFSET + 4 * i)); regs->emce_salt[i] = emce_readl(emce, (EMCE_SALT_KEY_OFFSET + 4 * i)); } } regs->emce_mode = emce_readl(emce, EMCE_MODE); } static void sunxi_emce_regs_restore(struct sunxi_emce_t *emce) { int i; struct sunxi_emce_regs *regs = &emce->regs; if (emce_dev->emce_vision == EMCE_VISION_1) { for (i = 0; i < 8; i++) { emce_writel(emce, (EMCE_MASTER_KEY_OFFSET + 4 * i), regs->emce_key[i]); emce_writel(emce, (EMCE_SALT_KEY_OFFSET + 4 * i), regs->emce_salt[i]); } } emce_writel(emce, EMCE_MODE, regs->emce_mode); } #ifdef CONFIG_PM static int sunxi_emce_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sunxi_emce_t *emce = platform_get_drvdata(pdev); pr_info("[EMCE]:suspend enter...\n"); sunxi_emce_regs_save(emce); sunxi_emce_hw_exit(emce); return 0; } static int sunxi_emce_resume(struct device *dev) { int ret = 0; struct platform_device *pdev = to_platform_device(dev); struct sunxi_emce_t *emce = platform_get_drvdata(pdev); pr_info("[EMCE]:resume enter...\n"); ret = sunxi_emce_hw_init(emce); sunxi_emce_regs_restore(emce); return ret; } static const struct dev_pm_ops sunxi_emce_dev_pm_ops = { .suspend = sunxi_emce_suspend, .resume = sunxi_emce_resume, }; #define SUNXI_EMCE_DEV_PM_OPS (&sunxi_emce_dev_pm_ops) #else #define SUNXI_EMCE_DEV_PM_OPS NULL #endif /* CONFIG_PM */ static struct platform_driver sunxi_emce_driver = { .probe = sunxi_emce_probe, .remove = sunxi_emce_remove, .driver = { .name = SUNXI_EMCE_DEV_NAME, .owner = THIS_MODULE, .pm = SUNXI_EMCE_DEV_PM_OPS, .of_match_table = sunxi_emce_of_match, }, }; static int __init sunxi_emce_init(void) { int ret = 0; EMCE_DBG("Sunxi EMCE init ...\n"); ret = platform_driver_register(&sunxi_emce_driver); if (ret < 0) EMCE_ERR("platform_driver_register() failed, return %d\n", ret); return ret; } static void __exit sunxi_emce_exit(void) { platform_driver_unregister(&sunxi_emce_driver); } module_init(sunxi_emce_init); module_exit(sunxi_emce_exit); MODULE_AUTHOR("zhouhuacai"); MODULE_DESCRIPTION("SUNXI EMCE Driver"); MODULE_ALIAS("platform:"SUNXI_EMCE_DEV_NAME); MODULE_LICENSE("GPL");