// SPDX-License-Identifier: GPL-2.0-only /* * Common crypto library for storage encryption. * * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include "crypto-qti-ice-regs.h" #include "crypto-qti-platform.h" #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_QTI_CRYPTO_FDE) #define CRYPTO_ICE_TYPE_NAME_LEN 8 #define CRYPTO_ICE_ENCRYPT 0x1 #define CRYPTO_ICE_DECRYPT 0x2 #define CRYPTO_SECT_LEN_IN_BYTE 512 #define TOTAL_NUMBER_ICE_SLOTS 32 #define CRYPTO_ICE_UDEV_PARTITION_NAME 96 #define CRYPTO_ICE_FDE_KEY_INDEX 31 #define CRYPTO_UD_VOLNAME "userdata" #define CRYPTO_ICE_HASH_SIZE 32 #define CRYPTO_ICE_KEY_ID_SIZE 32 #define CRYPTO_ICE_FDE_UFS_KEYID "UFS ICE Full Disk Encryption " #define CRYPTO_ICE_FDE_EMMC_KEYID "SDCC ICE Full Disk Encryption " /* add the legacy key_id to support ICE OTA */ #define CRYPTO_ICE_FDE_UFS_LEGACY_KEYID "UFS ICE Full Disk Encryption" #define CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID "SDCC ICE Full Disk Encryption" #define CRYPTO_ICE_UFS_DEV_NAME "ufshcd-qcom" #define CRYPTO_ICE_EMMC_DEV_NAME "sdhci_msm" #define QSEECOM_KEY_ID_EXISTS -65 #define _INIT_ATTRIBUTE(_name, _mode) \ { \ .name = __stringify(_name), \ .mode = (_mode), \ } #define PART_CFG_ATTR_RW(_name) \ struct kobj_attribute part_cfg_attr_##_name = { \ .attr = _INIT_ATTRIBUTE(_name, 0660), \ .show = crypto_qti_ice_part_cfg_show, \ .store = crypto_qti_ice_part_cfg_store, \ } /* Set the "add_partition" node attributes, allow user to add partitions */ #define __ATTR_WR_PARTITIONS(_name) { \ .attr = { .name = __stringify(_name), .mode = 0220 }, \ .store = _name##_store, \ } /* * Encapsulates configuration information for each supported partition */ struct ice_part_cfg { struct list_head list; char volname[PARTITION_META_INFO_VOLNAMELTH + 1]; /* NULL terminated */ uint32_t key_slot; /*ICE keyslot 0..31 */ uint32_t add_partition_result; /*Add partition operation res*/ bool encr_bypass; bool decr_bypass; struct kobject kobj; struct kobj_type kobj_type; }; struct ice_clk_info { struct list_head list; struct clk *clk; const char *name; u32 max_freq; u32 min_freq; u32 curr_freq; bool enabled; }; static LIST_HEAD(ice_devices); /* * ICE HW device structure. */ struct ice_device { struct list_head list; struct kset *fde_partitions; struct mutex mutex; struct device *pdev; dev_t device_no; void __iomem *mmio; int irq; bool is_ice_enabled; ice_error_cb error_cb; void *host_controller_data; /* UFS/EMMC/other? */ struct list_head clk_list_head; u32 ice_hw_version; bool is_ice_clk_available; char ice_instance_type[CRYPTO_ICE_TYPE_NAME_LEN]; struct regulator *reg; bool is_regulator_available; bool sysfs_groups_created; /* Partition list for full disk encryption */ struct list_head part_cfg_list; struct kobj_type partitions_kobj_type; u32 num_fde_slots; u32 num_fde_slots_in_use; }; static int crypto_qti_ice_init(struct ice_device *ice_dev, void *host_controller_data, ice_error_cb error_cb); #endif /* CONFIG_QTI_CRYPTO_FDE */ static int ice_check_fuse_setting(void __iomem *ice_mmio) { uint32_t regval; uint32_t version, major, minor; version = ice_readl(ice_mmio, ICE_REGS_VERSION); major = (version & ICE_CORE_MAJOR_REV_MASK) >> ICE_CORE_MAJOR_REV; minor = (version & ICE_CORE_MINOR_REV_MASK) >> ICE_CORE_MINOR_REV; /* Check fuse setting is not supported on ICE 3.2 onwards */ if ((major == 0x03) && (minor >= 0x02)) return 0; regval = ice_readl(ice_mmio, ICE_REGS_FUSE_SETTING); regval &= (ICE_FUSE_SETTING_MASK | ICE_FORCE_HW_KEY0_SETTING_MASK | ICE_FORCE_HW_KEY1_SETTING_MASK); if (regval) { pr_err("%s: error: ICE_ERROR_HW_DISABLE_FUSE_BLOWN\n", __func__); return -EPERM; } return 0; } static int ice_check_version(void __iomem *ice_mmio) { uint32_t version, major, minor, step; version = ice_readl(ice_mmio, ICE_REGS_VERSION); major = (version & ICE_CORE_MAJOR_REV_MASK) >> ICE_CORE_MAJOR_REV; minor = (version & ICE_CORE_MINOR_REV_MASK) >> ICE_CORE_MINOR_REV; step = (version & ICE_CORE_STEP_REV_MASK) >> ICE_CORE_STEP_REV; if (major < ICE_CORE_CURRENT_MAJOR_VERSION) { pr_err("%s: Unknown ICE device at %lu, rev %d.%d.%d\n", __func__, (unsigned long)ice_mmio, major, minor, step); return -ENODEV; } return 0; } int crypto_qti_init_crypto(void *mmio_data) { int err = 0; void __iomem *ice_mmio = (void __iomem *) mmio_data; err = ice_check_version(ice_mmio); if (err) { pr_err("%s: check version failed, err %d\n", __func__, err); return err; } err = ice_check_fuse_setting(ice_mmio); if (err) pr_err("%s: check fuse failed, err %d\n", __func__, err); return err; } EXPORT_SYMBOL(crypto_qti_init_crypto); static void ice_low_power_and_optimization_enable(void __iomem *ice_mmio) { uint32_t regval; regval = ice_readl(ice_mmio, ICE_REGS_ADVANCED_CONTROL); /* Enable low power mode sequence * [0]-0,[1]-0,[2]-0,[3]-7,[4]-0,[5]-0,[6]-0,[7]-0, * Enable CONFIG_CLK_GATING, STREAM2_CLK_GATING and STREAM1_CLK_GATING */ regval |= 0x7000; /* Optimization enable sequence */ regval |= 0xD807100; ice_writel(ice_mmio, regval, ICE_REGS_ADVANCED_CONTROL); /* * Memory barrier - to ensure write completion before next transaction */ wmb(); } static int ice_wait_bist_status(void __iomem *ice_mmio) { int count; uint32_t regval; for (count = 0; count < QTI_ICE_MAX_BIST_CHECK_COUNT; count++) { regval = ice_readl(ice_mmio, ICE_REGS_BIST_STATUS); if (!(regval & ICE_BIST_STATUS_MASK)) break; udelay(50); } if (regval) { pr_err("%s: wait bist status failed, reg %d\n", __func__, regval); return -ETIMEDOUT; } return 0; } int crypto_qti_enable(void *mmio_data) { int err = 0; void __iomem *ice_mmio = (void __iomem *) mmio_data; ice_low_power_and_optimization_enable(ice_mmio); err = ice_wait_bist_status(ice_mmio); if (err) return err; return err; } EXPORT_SYMBOL(crypto_qti_enable); void crypto_qti_disable(void) { crypto_qti_disable_platform(); } EXPORT_SYMBOL(crypto_qti_disable); int crypto_qti_resume(void *mmio_data) { void __iomem *ice_mmio = (void __iomem *) mmio_data; return ice_wait_bist_status(ice_mmio); } EXPORT_SYMBOL(crypto_qti_resume); static void ice_dump_test_bus(void __iomem *ice_mmio) { uint32_t regval = 0x1; uint32_t val; uint8_t bus_selector; uint8_t stream_selector; pr_err("ICE TEST BUS DUMP:\n"); for (bus_selector = 0; bus_selector <= 0xF; bus_selector++) { regval = 0x1; /* enable test bus */ regval |= bus_selector << 28; if (bus_selector == 0xD) continue; ice_writel(ice_mmio, regval, ICE_REGS_TEST_BUS_CONTROL); /* * make sure test bus selector is written before reading * the test bus register */ wmb(); val = ice_readl(ice_mmio, ICE_REGS_TEST_BUS_REG); pr_err("ICE_TEST_BUS_CONTROL: 0x%08x | ICE_TEST_BUS_REG: 0x%08x\n", regval, val); } pr_err("ICE TEST BUS DUMP (ICE_STREAM1_DATAPATH_TEST_BUS):\n"); for (stream_selector = 0; stream_selector <= 0xF; stream_selector++) { regval = 0xD0000001; /* enable stream test bus */ regval |= stream_selector << 16; ice_writel(ice_mmio, regval, ICE_REGS_TEST_BUS_CONTROL); /* * make sure test bus selector is written before reading * the test bus register */ wmb(); val = ice_readl(ice_mmio, ICE_REGS_TEST_BUS_REG); pr_err("ICE_TEST_BUS_CONTROL: 0x%08x | ICE_TEST_BUS_REG: 0x%08x\n", regval, val); } } int crypto_qti_debug(const struct ice_mmio_data *mmio_data) { void __iomem *ice_mmio = mmio_data->ice_base_mmio; pr_err("ICE Control: 0x%08x | ICE Reset: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_CONTROL), ice_readl(ice_mmio, ICE_REGS_RESET)); pr_err("ICE Version: 0x%08x | ICE FUSE: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_VERSION), ice_readl(ice_mmio, ICE_REGS_FUSE_SETTING)); pr_err("%s: ICE Param1: 0x%08x | ICE Param2: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_PARAMETERS_1), ice_readl(ice_mmio, ICE_REGS_PARAMETERS_2)); pr_err("%s: ICE Param3: 0x%08x | ICE Param4: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_PARAMETERS_3), ice_readl(ice_mmio, ICE_REGS_PARAMETERS_4)); pr_err("%s: ICE Param5: 0x%08x | ICE IRQ STTS: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_PARAMETERS_5), ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_STTS)); pr_err("%s: ICE IRQ MASK: 0x%08x | ICE IRQ CLR: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_MASK), ice_readl(ice_mmio, ICE_REGS_NON_SEC_IRQ_CLR)); pr_err("%s: ICE INVALID CCFG ERR STTS: 0x%08x\n", ice_readl(ice_mmio, ICE_INVALID_CCFG_ERR_STTS)); pr_err("%s: ICE BIST Sts: 0x%08x | ICE Bypass Sts: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_BIST_STATUS), ice_readl(ice_mmio, ICE_REGS_BYPASS_STATUS)); pr_err("%s: ICE ADV CTRL: 0x%08x | ICE ENDIAN SWAP: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_ADVANCED_CONTROL), ice_readl(ice_mmio, ICE_REGS_ENDIAN_SWAP)); pr_err("%s: ICE_STM1_ERR_SYND1: 0x%08x | ICE_STM1_ERR_SYND2: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_ERROR_SYNDROME1), ice_readl(ice_mmio, ICE_REGS_STREAM1_ERROR_SYNDROME2)); pr_err("%s: ICE_STM2_ERR_SYND1: 0x%08x | ICE_STM2_ERR_SYND2: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_ERROR_SYNDROME1), ice_readl(ice_mmio, ICE_REGS_STREAM2_ERROR_SYNDROME2)); pr_err("%s: ICE_STM1_COUNTER1: 0x%08x | ICE_STM1_COUNTER2: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS1), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS2)); pr_err("%s: ICE_STM1_COUNTER3: 0x%08x | ICE_STM1_COUNTER4: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS3), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS4)); pr_err("%s: ICE_STM2_COUNTER1: 0x%08x | ICE_STM2_COUNTER2: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS1), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS2)); pr_err("%s: ICE_STM2_COUNTER3: 0x%08x | ICE_STM2_COUNTER4: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS3), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS4)); pr_err("%s: ICE_STM1_CTR5_MSB: 0x%08x | ICE_STM1_CTR5_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS5_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS5_LSB)); pr_err("%s: ICE_STM1_CTR6_MSB: 0x%08x | ICE_STM1_CTR6_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS6_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS6_LSB)); pr_err("%s: ICE_STM1_CTR7_MSB: 0x%08x | ICE_STM1_CTR7_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS7_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS7_LSB)); pr_err("%s: ICE_STM1_CTR8_MSB: 0x%08x | ICE_STM1_CTR8_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS8_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS8_LSB)); pr_err("%s: ICE_STM1_CTR9_MSB: 0x%08x | ICE_STM1_CTR9_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS9_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM1_COUNTERS9_LSB)); pr_err("%s: ICE_STM2_CTR5_MSB: 0x%08x | ICE_STM2_CTR5_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS5_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS5_LSB)); pr_err("%s: ICE_STM2_CTR6_MSB: 0x%08x | ICE_STM2_CTR6_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS6_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS6_LSB)); pr_err("%s: ICE_STM2_CTR7_MSB: 0x%08x | ICE_STM2_CTR7_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS7_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS7_LSB)); pr_err("%s: ICE_STM2_CTR8_MSB: 0x%08x | ICE_STM2_CTR8_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS8_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS8_LSB)); pr_err("%s: ICE_STM2_CTR9_MSB: 0x%08x | ICE_STM2_CTR9_LSB: 0x%08x\n", ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS9_MSB), ice_readl(ice_mmio, ICE_REGS_STREAM2_COUNTERS9_LSB)); ice_dump_test_bus(ice_mmio); return 0; } EXPORT_SYMBOL(crypto_qti_debug); int crypto_qti_keyslot_program(const struct ice_mmio_data *mmio_data, const struct blk_crypto_key *key, unsigned int slot, u8 data_unit_mask, int capid, int storage_type) { int err = 0; err = crypto_qti_program_key(mmio_data, key, slot, data_unit_mask, capid, storage_type); if (err) { pr_err("%s: program key failed with error %d\n", __func__, err); err = crypto_qti_invalidate_key(mmio_data, slot, storage_type); if (err) { pr_err("%s: invalidate key failed with error %d\n", __func__, err); return err; } } return err; } EXPORT_SYMBOL(crypto_qti_keyslot_program); int crypto_qti_keyslot_evict(const struct ice_mmio_data *mmio_data, unsigned int slot, int storage_type) { int err = 0; err = crypto_qti_invalidate_key(mmio_data, slot, storage_type); if (err) { pr_err("%s: invalidate key failed with error %d\n", __func__, err); } return err; } EXPORT_SYMBOL(crypto_qti_keyslot_evict); int crypto_qti_derive_raw_secret(const u8 *wrapped_key, unsigned int wrapped_key_size, u8 *secret, unsigned int secret_size) { int err = 0; if (wrapped_key_size <= RAW_SECRET_SIZE) { pr_err("%s: Invalid wrapped_key_size: %u\n", __func__, wrapped_key_size); err = -EINVAL; return err; } if (secret_size != RAW_SECRET_SIZE) { pr_err("%s: Invalid secret size: %u\n", __func__, secret_size); err = -EINVAL; return err; } if (wrapped_key_size > 64) err = crypto_qti_derive_raw_secret_platform(wrapped_key, wrapped_key_size, secret, secret_size); else memcpy(secret, wrapped_key, secret_size); return err; } EXPORT_SYMBOL(crypto_qti_derive_raw_secret); #if IS_ENABLED(CONFIG_QTI_CRYPTO_FDE) static int get_key_id(char *key_id, uint32_t usage, bool hash_enable) { /* only legacy hash will get the legacy key id, otherwise return new key id */ if (hash_enable) { switch (usage) { case QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION: memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_UFS_LEGACY_KEYID, strlen(CRYPTO_ICE_FDE_UFS_LEGACY_KEYID)); break; case QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION: memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID, strlen(CRYPTO_ICE_FDE_EMMC_LEGACY_KEYID)); break; default: pr_err("unsupported usage %d\n", usage); return -EINVAL; } } else { switch (usage) { case QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION: memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_UFS_KEYID, strlen(CRYPTO_ICE_FDE_UFS_KEYID)); break; case QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION: memcpy((void *)key_id, (void *)CRYPTO_ICE_FDE_EMMC_KEYID, strlen(CRYPTO_ICE_FDE_EMMC_KEYID)); break; default: pr_err("unsupported usage %d\n", usage); return -EINVAL; } } return 0; } static struct ice_part_cfg *crypto_qti_ice_get_part_cfg(struct ice_device *ice_dev, char const * const volname) { struct ice_part_cfg *part_cfg = NULL; if (!ice_dev) { pr_err_ratelimited("%s: %s: no ICE device\n", __func__, volname); return NULL; } list_for_each_entry(part_cfg, &ice_dev->part_cfg_list, list) { if (!strcmp(part_cfg->volname, volname)) return part_cfg; } return NULL; } /* * Stub function to satisfy kobject lifecycle */ static inline void crypto_qti_ice_release_kobj(struct kobject *kobj) { char const *name = kobject_name(kobj); if (name) pr_debug("Releasing %s\n", name); /* Nothing to do */ } static inline struct ice_part_cfg *to_ice_part_cfg(struct kobject *kobject) { return container_of(kobject, struct ice_part_cfg, kobj); } /* * Attribute "show" method for per-partition attributes. */ static ssize_t crypto_qti_ice_part_cfg_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct ice_part_cfg *cfg = to_ice_part_cfg(kobj); if (!strcmp(attr->attr.name, "decr_bypass")) return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->decr_bypass); if (!strcmp(attr->attr.name, "encr_bypass")) return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->encr_bypass); if (!strcmp(attr->attr.name, "add_partition_result")) return scnprintf(buf, PAGE_SIZE, "%d\n", cfg->add_partition_result); pr_err("%s: Unhandled attribute %s\n", __func__, buf); return -EFAULT; } /* * Attribute "store" method for per-partition attributes. */ static ssize_t crypto_qti_ice_part_cfg_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct ice_part_cfg *cfg = to_ice_part_cfg(kobj); int op_result = -EFAULT; /* * the decr_bypass/encr_bypass attribute can be configured * from userspace side, only "0" or "1" are accepted. */ if (count > 2) return -EINVAL; if (!strcmp(attr->attr.name, "decr_bypass")) op_result = kstrtobool(buf, &cfg->decr_bypass); else if (!strcmp(attr->attr.name, "encr_bypass")) op_result = kstrtobool(buf, &cfg->encr_bypass); else pr_err("%s: Unhandled attribute %s\n", __func__, buf); /* Notify userspace of the change */ if (!op_result) { pr_info("%s: Change %s:%s=%s\n", __func__, cfg->volname, attr->attr.name, buf); kobject_uevent(kobj, KOBJ_CHANGE); return count; } pr_err("%s: Failed %s:%s=%s : %d\n", __func__, cfg->volname, attr->attr.name, buf, op_result); return op_result; } /* * crypto_qti_ice_part_cfg_show and crypto_qti_ice_part_cfg_store must ahead of referring to them */ static PART_CFG_ATTR_RW(add_partition_result); static PART_CFG_ATTR_RW(decr_bypass); static PART_CFG_ATTR_RW(encr_bypass); static struct attribute *ice_part_cfg_attrs[] = { &part_cfg_attr_add_partition_result.attr, &part_cfg_attr_decr_bypass.attr, &part_cfg_attr_encr_bypass.attr, NULL, }; /** * Add a new partition for the ICE to manage * * Full encryption/decryption is enabled by default! * * @ice_dev: ICE device node * @new_volname: Null-terminated volume name partition * @slot: the ICE keyslot number * @new_key 0 is key exist, 1 is new key */ static int crypto_qti_ice_add_new_partition(struct ice_device *ice_dev, const char * const new_volname, unsigned int slot, bool new_key) { struct list_head *new_pos = NULL; struct ice_part_cfg *elem = NULL; int rc = -EINVAL; char *envp[2] = {0}; char part_name[CRYPTO_ICE_UDEV_PARTITION_NAME] = {0}; /* Check if the partition is already in the list */ list_for_each(new_pos, &ice_dev->part_cfg_list) { elem = list_entry(new_pos, struct ice_part_cfg, list); if (!strcmp(new_volname, elem->volname)) { rc = 0; goto out; /* Already in list, bail */ } } dev_info(ice_dev->pdev, "Adding %s to ICE partition list\n", new_volname); /* Didn't find it, add new entry at the end */ elem = kzalloc(sizeof(struct ice_part_cfg), GFP_KERNEL); if (!elem) { rc = -ENOMEM; dev_err(ice_dev->pdev, "%s: Error %d allocating memory for partition %s\n", __func__, rc, new_volname); goto out; } /* Add the new partition to the KSet */ elem->kobj.kset = ice_dev->fde_partitions; /* Set up the sysfs node */ elem->kobj_type.release = crypto_qti_ice_release_kobj; elem->kobj_type.sysfs_ops = &kobj_sysfs_ops; elem->kobj_type.default_attrs = ice_part_cfg_attrs; rc = kobject_init_and_add(&elem->kobj, &elem->kobj_type, NULL, new_volname); if (rc) { dev_err(ice_dev->pdev, "%s: Error %d adding sysfs for %s\n", __func__, rc, new_volname); kobject_put(&elem->kobj); kfree(elem); goto out; } /* Null terminated */ strscpy(elem->volname, new_volname, PARTITION_META_INFO_VOLNAMELTH + 1); /* Default - encryption is enabled */ elem->decr_bypass = false; elem->encr_bypass = false; /* Start using key slots from the end */ if (ice_dev->num_fde_slots_in_use >= TOTAL_NUMBER_ICE_SLOTS) { /* Shouldn't be here , just some defensive check */ rc = -ENOMEM; dev_err(ice_dev->pdev, "%s: Allocating more slots than available %s\n", __func__); kobject_put(&elem->kobj); kfree(elem); goto out; } elem->key_slot = slot; elem->add_partition_result = new_key; list_add_tail(&elem->list, new_pos); /* Generate the UEvent with the partition name to notify userpsace */ snprintf(part_name, CRYPTO_ICE_UDEV_PARTITION_NAME, "FDE_PARTITION=%s", new_volname); envp[0] = part_name; envp[1] = NULL; kobject_uevent_env(&elem->kobj, KOBJ_ADD, envp); out: return rc; } /* * Attribute "store" method for add_partition * * Add a partition to the list with default configuration. */ static ssize_t add_partition_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ice_device *ice_dev = dev_get_drvdata(dev); ssize_t ret = count; char label[PARTITION_META_INFO_VOLNAMELTH + 1] = {0}; char key_id[CRYPTO_ICE_KEY_ID_SIZE] = {0}; char *flag = "-hash"; char *hash_index = NULL; bool hash_enable = false; size_t partition_count = count; int rc = 0; struct list_head *new_pos = NULL; struct ice_part_cfg *elem = NULL; unsigned int slot = 0; bool new_key_generated = false; int key_res = 0; /* The count of the buf include hash size and flag size */ if (count > PARTITION_META_INFO_VOLNAMELTH + CRYPTO_ICE_HASH_SIZE + strlen(flag)) { dev_err(dev, "Invalid partition '%s' (%u)\n", buf, count); return -EINVAL; } if (!buf) { dev_err(dev, "Invalid buf\n"); return -EINVAL; } if (!ice_dev) { dev_err(dev, "Invalid ICE device!\n"); return -ENODEV; } /* * check if the userspace input the hash to generate ICE key * -hash means input hash with legacy key id */ hash_index = strnstr(buf, flag, count); if (hash_index) { /* * It must follow the right format to input partition name and hash, * partition name can't be null. */ if (hash_index > buf + 1) { hash_enable = true; /* skip the blank and get the partition name count */ partition_count = hash_index - buf - 1; /* skip to the header pointer of hash */ hash_index += strlen(flag) + 1; /* exclude "\r" that in the end of input buf, and check hash size */ if (strlen(hash_index) - 1 != CRYPTO_ICE_HASH_SIZE) { dev_err(dev, "Invalid hash\n"); return -EINVAL; } } else { dev_err(dev, "Invalid partition\n"); return -EINVAL; } } if (!hash_enable) { /* Copy into a temporary buffer, stripping out newlines */ partition_count = strcspn(buf, "\n\r"); if (!partition_count) { dev_err(dev, "Invalid partition '%s' (%u)\n", buf, partition_count); return -EINVAL; } } /* copy the partition name */ memcpy(label, buf, partition_count); label[partition_count] = '\0'; mutex_lock(&ice_dev->mutex); /* Check if the partition is already in the list */ list_for_each(new_pos, &ice_dev->part_cfg_list) { elem = list_entry(new_pos, struct ice_part_cfg, list); if (!strcmp(label, elem->volname)) { dev_info(dev, "partition exist already, needn't to add\n"); ret = -EINVAL; /* Already in the list , return */ goto out; } } /* New partition , check if we have a free slot available */ if (ice_dev->num_fde_slots_in_use >= ice_dev->num_fde_slots) { dev_err(dev, "All ICE slots are in use!\n"); ret = -EINVAL; /* Already in the list , return */ goto out; } #if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE) /* Don't increment , using same slot for all partitions */ slot = CRYPTO_ICE_FDE_KEY_INDEX; #else /* Increment number of slots in use */ ++ice_dev->num_fde_slots_in_use; slot = TOTAL_NUMBER_ICE_SLOTS - ice_dev->num_fde_slots_in_use; #endif /* * First need to generate/restore key and set it into the ice slot * Request is accroding to storage type UFS/eMMC * The unique key is generated via partition name , or legacy key if configured */ if (strcmp(ice_dev->ice_instance_type, "ufs") == 0) { #if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE) /* In legacy mode, only one common key is needed */ if (list_empty(&ice_dev->part_cfg_list)) { /* get the key_id that depends on if inhash_enable or not */ if (get_key_id(key_id, QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION, hash_enable)) { dev_err(dev, "Fail to get the ice key_id\n"); ret = -EINVAL; goto out; } key_res = qseecom_create_key_in_slot( QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION, slot, key_id, hash_index); } else { /* Key is already generated and set,contune */ key_res = QSEECOM_KEY_ID_EXISTS; } #else key_res = qseecom_create_key_in_slot( QSEECOM_KM_USAGE_UFS_ICE_DISK_ENCRYPTION, slot, label, NULL); #endif } else if (strcmp(ice_dev->ice_instance_type, "sdcc") == 0) { #if IS_ENABLED(CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE) /* In legacy mode, only one common key is needed */ if (list_empty(&ice_dev->part_cfg_list)) { /* get the key_id that depends on if inhash_enable or not */ if (get_key_id(key_id, QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION, hash_enable)) { dev_err(dev, "Fail to get the ice key_id\n"); ret = -EINVAL; goto out; } key_res = qseecom_create_key_in_slot( QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION, slot, key_id, hash_index); } else { /* Key is already generated and set,contune */ key_res = QSEECOM_KEY_ID_EXISTS; } #else key_res = qseecom_create_key_in_slot( QSEECOM_KM_USAGE_SDCC_ICE_DISK_ENCRYPTION, slot, label, NULL); #endif } else { dev_err(dev, "Not supported storage type!\n"); ret = -EINVAL; /* Already in the list , return */ goto out; } /* Check the qseecom_create_key_in_slot result */ if (key_res) { if (key_res == QSEECOM_KEY_ID_EXISTS) { dev_info(dev, "ICE key exist\n"); new_key_generated = true; } else { dev_err(dev, "Failed to generate and set key!\n"); #ifndef CONFIG_QTI_CRYPTO_LEGACY_KEY_FDE /* Take the slot back */ --ice_dev->num_fde_slots_in_use; #endif ret = -EINVAL; goto out; } } /* Error logged in function */ rc = crypto_qti_ice_add_new_partition(ice_dev, label, slot, new_key_generated); if (rc) ret = rc; out: mutex_unlock(&ice_dev->mutex); return ret; } /* add_partition_store must ahead of referring it */ static struct device_attribute dev_attr_add_partition = __ATTR_WR_PARTITIONS(add_partition); static struct attribute *ice_attrs[] = { &dev_attr_add_partition.attr, NULL, }; ATTRIBUTE_GROUPS(ice); unsigned int crypto_qti_ice_get_num_fde_slots(void) { struct ice_device *ice_dev = NULL; ice_dev = list_first_entry_or_null(&ice_devices, struct ice_device, list); if (ice_dev) return ice_dev->num_fde_slots; else return 0; } EXPORT_SYMBOL(crypto_qti_ice_get_num_fde_slots); static int crypto_qti_ice_get_vreg(struct ice_device *ice_dev) { int ret = 0; if (!ice_dev->is_regulator_available) return ret; if (ice_dev->reg) return ret; ice_dev->reg = devm_regulator_get(ice_dev->pdev, "vdd-hba"); if (IS_ERR(ice_dev->reg)) { ret = PTR_ERR(ice_dev->reg); dev_err(ice_dev->pdev, "%s: %s get failed, err=%d\n", __func__, "vdd-hba-supply", ret); } return ret; } /* configure setting to partition */ static int crypto_qti_ice_setting_config(struct request *req, struct ice_crypto_setting *crypto_data, struct ice_data_setting *setting, bool encr_bypass, bool decr_bypass) { if (!setting) return -EINVAL; if ((short)(crypto_data->key_index) >= 0) { memcpy(&setting->crypto_data, crypto_data, sizeof(setting->crypto_data)); if (rq_data_dir(req) == WRITE) { setting->encr_bypass = encr_bypass; } else if (rq_data_dir(req) == READ) { setting->decr_bypass = decr_bypass; } else { /* Should I say BUG_ON */ pr_err("%s unhandled request 0x%x\n", __func__, req->cmd_flags); setting->encr_bypass = true; setting->decr_bypass = true; } } return 0; } static void crypto_qti_ice_disable_intr(struct ice_device *ice_dev) { unsigned int reg; reg = crypto_qti_ice_readl(ice_dev, ICE_REGS_NON_SEC_IRQ_MASK); reg |= ICE_NON_SEC_IRQ_MASK; crypto_qti_ice_writel(ice_dev, reg, ICE_REGS_NON_SEC_IRQ_MASK); /* * Ensure previous instructions was completed before issuing next * ICE initialization/optimization instruction */ mb(); } static int crypto_qti_ice_parse_ice_instance_type(struct platform_device *pdev, struct ice_device *ice_dev) { int ret; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; const char *type; ret = of_property_read_string_index(np, "qcom,instance-type", 0, &type); if (ret) { pr_err("%s: Could not get ICE instance type\n", __func__); return ret; } strscpy(ice_dev->ice_instance_type, type, CRYPTO_ICE_TYPE_NAME_LEN); return 0; } static void crypto_qti_ice_parse_number_of_fde_slots(struct platform_device *pdev, struct ice_device *ice_dev) { int ret = 0; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; u32 num_of_slots = 0; ret = of_property_read_u32(np, "qcom,num-fde-slots", &num_of_slots); if (ret) { /* Backwards compatibility , assume one slot if DTS property is not deifined */ pr_info("%s: No num of FDE slots defined in dts,using 1 slot\n", __func__); ice_dev->num_fde_slots = 1; return; } if (num_of_slots >= TOTAL_NUMBER_ICE_SLOTS) { /* Limit the number of slots */ pr_info("%s: Limiting num of FDE slots\n", __func__); ice_dev->num_fde_slots = TOTAL_NUMBER_ICE_SLOTS - 1; } else { ice_dev->num_fde_slots = num_of_slots; } } static int crypto_qti_ice_parse_clock_info(struct platform_device *pdev, struct ice_device *ice_dev) { int cnt, i, len; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; char *name; struct ice_clk_info *clki; u32 *clkfreq = NULL; int ret = -EINVAL; if (!np) goto out; cnt = of_property_count_strings(np, "clock-names"); if (cnt <= 0) { dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", __func__); ret = cnt; goto out; } if (!of_get_property(np, "qcom,op-freq-hz", &len)) { dev_info(dev, "qcom,op-freq-hz property not specified\n"); goto out; } len = len/sizeof(*clkfreq); if (len != cnt) goto out; clkfreq = devm_kzalloc(dev, len * sizeof(*clkfreq), GFP_KERNEL); if (!clkfreq) { ret = -ENOMEM; goto out; } ret = of_property_read_u32_array(np, "qcom,op-freq-hz", clkfreq, len); INIT_LIST_HEAD(&ice_dev->clk_list_head); for (i = 0; i < cnt; i++) { ret = of_property_read_string_index(np, "clock-names", i, (const char **)&name); if (ret) goto out; clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); if (!clki) { ret = -ENOMEM; goto out; } clki->max_freq = clkfreq[i]; clki->name = kstrdup(name, GFP_KERNEL); list_add_tail(&clki->list, &ice_dev->clk_list_head); } out: return ret; } static int crypto_qti_ice_get_dts_data(struct platform_device *pdev, struct ice_device *ice_dev) { int ret = -EINVAL; ice_dev->mmio = NULL; if (!of_parse_phandle(pdev->dev.of_node, "vdd-hba-supply", 0)) { pr_err("%s: No vdd-hba-supply regulator, assuming not needed\n", __func__); ice_dev->is_regulator_available = false; } else { ice_dev->is_regulator_available = true; } ice_dev->is_ice_clk_available = of_property_read_bool((&pdev->dev)->of_node, "qcom,enable-ice-clk"); if (ice_dev->is_ice_clk_available) { ret = crypto_qti_ice_parse_clock_info(pdev, ice_dev); if (ret) { pr_err("%s: crypto_qti_ice_parse_clock_info failed (%d)\n", __func__, ret); return ret; } } ret = crypto_qti_ice_parse_ice_instance_type(pdev, ice_dev); if (ret) { pr_err("%s: fail to parse ice instance type (%d)\n", __func__, ret); return ret; } crypto_qti_ice_parse_number_of_fde_slots(pdev, ice_dev); return ret; } /* * ICE HW instance can exist in UFS or eMMC based storage HW * Userspace does not know what kind of ICE it is dealing with. * Though userspace can find which storage device it is booting * from but all kind of storage types dont support ICE from * beginning. So ICE device is created for user space to ping * if ICE exist for that kind of storage */ static const struct file_operations crypto_qti_ice_fops = { .owner = THIS_MODULE, }; /** * Remove all sysfs resources associated with the driver * * @ice_dev: ICE device node */ static void crypto_qti_ice_cleanup_sysfs(struct ice_device *ice_dev) { struct list_head *pos = NULL; struct list_head *n = NULL; struct ice_part_cfg *elem = NULL; /* Platform device attributes */ if (ice_dev->sysfs_groups_created) { sysfs_remove_groups(&ice_dev->pdev->kobj, ice_groups); ice_dev->sysfs_groups_created = false; } /* Partition configuration list and sysfs nodes */ list_for_each_safe(pos, n, &ice_dev->part_cfg_list) { elem = list_entry(pos, struct ice_part_cfg, list); dev_dbg(ice_dev->pdev, "Removing %s", elem->volname); if (elem->kobj.state_in_sysfs) kobject_put(&elem->kobj); list_del(pos); kfree(elem); } if (ice_dev->fde_partitions != NULL) kset_unregister(ice_dev->fde_partitions); } static int crypto_qti_ice_probe(struct platform_device *pdev) { struct ice_device *ice_dev; int rc = -EINVAL; if (!pdev) { pr_err("%s: Invalid platform_device passed\n", __func__); goto out; } ice_dev = kzalloc(sizeof(struct ice_device), GFP_KERNEL); if (!ice_dev) { rc = -ENOMEM; pr_err("%s: Error %d allocating memory for ICE device:\n", __func__, rc); goto out; } /* Initialize device data to a known state */ INIT_LIST_HEAD(&ice_dev->part_cfg_list); ice_dev->pdev = &pdev->dev; if (!ice_dev->pdev) { rc = -EINVAL; pr_err("%s: Invalid device passed in platform_device\n", __func__); goto err_ice_dev; } if (pdev->dev.of_node) rc = crypto_qti_ice_get_dts_data(pdev, ice_dev); else { rc = -EINVAL; pr_err("%s: ICE device node not found\n", __func__); } if (rc) goto err_ice_dev; /* * If ICE is enabled here, it would be waste of power. * We would enable ICE when first request for crypto * operation arrives. */ rc = crypto_qti_ice_init(ice_dev, NULL, NULL); if (rc) { pr_err("ice_init failed.\n"); goto err_ice_dev; } mutex_init(&ice_dev->mutex); ice_dev->is_ice_enabled = true; platform_set_drvdata(pdev, ice_dev); list_add_tail(&ice_dev->list, &ice_devices); dev_info(&pdev->dev, "Initialized OK\n"); goto out; err_ice_dev: crypto_qti_ice_cleanup_sysfs(ice_dev); kfree(ice_dev); out: return rc; } static int crypto_qti_ice_remove(struct platform_device *pdev) { struct ice_device *ice_dev; ice_dev = (struct ice_device *)platform_get_drvdata(pdev); if (!ice_dev) return 0; mutex_destroy(&ice_dev->mutex); crypto_qti_ice_cleanup_sysfs(ice_dev); crypto_qti_ice_disable_intr(ice_dev); device_init_wakeup(&pdev->dev, false); if (ice_dev->mmio) iounmap(ice_dev->mmio); list_del_init(&ice_dev->list); kfree(ice_dev); return -EINVAL; } int crypto_qti_ice_config_start(struct request *req, struct ice_data_setting *setting) { struct ice_crypto_setting ice_data = {0}; struct ice_device *ice_dev; struct ice_part_cfg *part_cfg; int ret = -EINVAL; if (!req) goto out; /* * It is not an error to have a request with no bio * Such requests must bypass ICE. So first set bypass and then * return if bio is not available in request */ if (setting) { setting->encr_bypass = true; setting->decr_bypass = true; } ice_dev = list_first_entry_or_null(&ice_devices, struct ice_device, list); if (req->part && req->part->bd_meta_info && req->part->bd_meta_info->volname[0]) { part_cfg = crypto_qti_ice_get_part_cfg(ice_dev, req->part->bd_meta_info->volname); if (part_cfg) { ice_data.key_index = part_cfg->key_slot; /* the partition info has remove __sector/nr_sects/start_sect, so don't * try to check the sector. TBD */ ret = crypto_qti_ice_setting_config(req, &ice_data, setting, part_cfg->encr_bypass, part_cfg->decr_bypass); } } out: return ret; } EXPORT_SYMBOL(crypto_qti_ice_config_start); static int crypto_qti_ice_enable_clocks(struct ice_device *ice, bool enable) { int ret = 0; struct ice_clk_info *clki = NULL; struct device *dev = ice->pdev; struct list_head *head = &ice->clk_list_head; if (!head || list_empty(head)) { dev_err(dev, "%s:ICE Clock list null/empty\n", __func__); ret = -EINVAL; goto out; } if (!ice->is_ice_clk_available) { dev_err(dev, "%s:ICE Clock not available\n", __func__); ret = -EINVAL; goto out; } list_for_each_entry(clki, head, list) { if (!clki->name) continue; if (enable) ret = clk_prepare_enable(clki->clk); else clk_disable_unprepare(clki->clk); if (ret) { dev_err(dev, "Unable to %s ICE core clk\n", enable?"enable":"disable"); goto out; } } out: return ret; } static struct ice_device *crypto_qti_get_ice_device_from_storage_type(const char *storage_type) { struct ice_device *ice_dev = NULL; if (list_empty(&ice_devices)) { ice_dev = ERR_PTR(-EPROBE_DEFER); goto out; } list_for_each_entry(ice_dev, &ice_devices, list) { if (!strcmp(ice_dev->ice_instance_type, storage_type)) { pr_debug("%s: ice device %pK\n", __func__, ice_dev); return ice_dev; } } out: return NULL; } static int crypto_qti_ice_enable_setup(struct ice_device *ice_dev) { int ret = -EINVAL; /* Setup Regulator */ if (ice_dev->is_regulator_available) { if (crypto_qti_ice_get_vreg(ice_dev)) { pr_err("%s: Could not get regulator\n", __func__); goto out; } ret = regulator_enable(ice_dev->reg); if (ret) { pr_err("%s:%pK: Could not enable regulator\n", __func__, ice_dev); goto out; } } else { pr_info("%s: No need to get regulator\n", __func__); ret = 0; } /* Setup Clocks */ if (crypto_qti_ice_enable_clocks(ice_dev, true)) { pr_err("%s:%pK:%s Could not enable clocks\n", __func__, ice_dev, ice_dev->ice_instance_type); if (ice_dev->is_regulator_available) { if (crypto_qti_ice_get_vreg(ice_dev)) { pr_err("%s: Could not get regulator\n", __func__); goto out; } ret = regulator_disable(ice_dev->reg); if (ret) pr_err("%s:%pK: Could not disable regulator\n", __func__, ice_dev); } } out: return ret; } static int crypto_qti_ice_disable_setup(struct ice_device *ice_dev) { int ret; /* Setup Clocks */ ret = crypto_qti_ice_enable_clocks(ice_dev, false); if (ret) { pr_err("%s:%pK:%s Could not disable clocks\n", __func__, ice_dev, ice_dev->ice_instance_type); goto out; } /* Setup Regulator */ if (ice_dev->is_regulator_available) { ret = crypto_qti_ice_get_vreg(ice_dev); if (ret) { pr_err("%s: Could not get regulator\n", __func__); goto out; } ret = regulator_disable(ice_dev->reg); if (ret) { pr_err("%s:%pK: Could not disable regulator\n", __func__, ice_dev); goto out; } } out: return ret; } static int crypto_qti_ice_init_clocks(struct ice_device *ice) { int ret = -EINVAL; struct ice_clk_info *clki = NULL; struct device *dev = ice->pdev; struct list_head *head = &ice->clk_list_head; if (!head || list_empty(head)) { dev_err(dev, "%s:ICE Clock list null/empty\n", __func__); goto out; } list_for_each_entry(clki, head, list) { if (!clki->name) continue; clki->clk = devm_clk_get(dev, clki->name); if (IS_ERR(clki->clk)) { ret = PTR_ERR(clki->clk); dev_err(dev, "%s: %s clk get failed, %d\n", __func__, clki->name, ret); goto out; } /* Not all clocks would have a rate to be set */ ret = 0; if (clki->max_freq) { ret = clk_set_rate(clki->clk, clki->max_freq); if (ret) { dev_err(dev, "%s: %s clk set rate(%dHz) failed, %d\n", __func__, clki->name, clki->max_freq, ret); goto out; } clki->curr_freq = clki->max_freq; dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__, clki->name, clk_get_rate(clki->clk)); } } out: return ret; } static int crypto_qti_ice_finish_init(struct ice_device *ice_dev) { int err = 0; if (!ice_dev) { pr_err("%s: Null data received\n", __func__); err = -ENODEV; goto out; } if (ice_dev->is_ice_clk_available) { err = crypto_qti_ice_init_clocks(ice_dev); if (err) goto out; } out: return err; } static int crypto_qti_ice_init(struct ice_device *ice_dev, void *host_controller_data, ice_error_cb error_cb) { /* * A completion event for host controller would be triggered upon * initialization completion * When ICE is initialized, it would put ICE into Global Bypass mode * When any request for data transfer is received, it would enable * the ICE for that particular request */ ice_dev->error_cb = error_cb; ice_dev->host_controller_data = host_controller_data; return crypto_qti_ice_finish_init(ice_dev); } int crypto_qti_ice_init_fde_node(struct device *dev) { struct ice_device *ice_dev = NULL; int rc = -EINVAL; if (!dev) { pr_err("%s: Invalid platform_device passed\n", __func__); rc = -EINVAL; goto out; } /* Only one ice device is supported , remove the extra one(DTS has more than one node) */ if (!strcmp(CRYPTO_ICE_UFS_DEV_NAME, dev_driver_string(dev))) { /* ufs found , remove eMMC node if exists */ ice_dev = crypto_qti_get_ice_device_from_storage_type("sdcc"); if (ice_dev) { mutex_destroy(&ice_dev->mutex); if (ice_dev->mmio) iounmap(ice_dev->mmio); list_del(&ice_dev->list); kfree(ice_dev); } /* Get UFS instance */ ice_dev = crypto_qti_get_ice_device_from_storage_type("ufs"); if (ice_dev == NULL) { pr_err("ice_dev NULL !\n"); rc = -EINVAL; goto out; } } if (!strcmp(CRYPTO_ICE_EMMC_DEV_NAME, dev_driver_string(dev))) { ice_dev = crypto_qti_get_ice_device_from_storage_type("ufs"); if (ice_dev) { /* eMMC found , remove UFS node if exists */ mutex_destroy(&ice_dev->mutex); if (ice_dev->mmio) iounmap(ice_dev->mmio); list_del(&ice_dev->list); kfree(ice_dev); } /* Get eMMC instance */ ice_dev = crypto_qti_get_ice_device_from_storage_type("sdcc"); if (ice_dev == NULL) { pr_err("ice_dev NULL !\n"); rc = -EINVAL; goto out; } } if (!ice_dev) { pr_err("%s: No ice device found\n", __func__); rc = -EINVAL; goto out; } /* Create a Kset for the encrypted partitions */ ice_dev->fde_partitions = kset_create_and_add("fde_partitions", NULL, &ice_dev->pdev->kobj); if (ice_dev->fde_partitions == NULL) { rc = -EINVAL; pr_err("%s: Failed to create partitons KSet\n", __func__); goto err_ice_dev; } /* Set up platform device attributes in sysfs */ rc = sysfs_create_groups(&ice_dev->pdev->kobj, ice_groups); if (rc) { pr_err("%s: Failed to add pdev attributes: %d\n", __func__, rc); goto err_ice_dev; } else { ice_dev->sysfs_groups_created = true; } /* Set up partition config folder in sysfs */ ice_dev->partitions_kobj_type.release = crypto_qti_ice_release_kobj; goto out; err_ice_dev: crypto_qti_ice_cleanup_sysfs(ice_dev); out: return rc; } EXPORT_SYMBOL(crypto_qti_ice_init_fde_node); int crypto_qti_ice_setup_ice_hw(const char *storage_type, int enable) { int ret = -EINVAL; struct ice_device *ice_dev = NULL; ice_dev = crypto_qti_get_ice_device_from_storage_type(storage_type); if (ice_dev == ERR_PTR(-EPROBE_DEFER)) return -EPROBE_DEFER; if (!ice_dev || !ice_dev->is_ice_enabled) return ret; if (enable) return crypto_qti_ice_enable_setup(ice_dev); else return crypto_qti_ice_disable_setup(ice_dev); } EXPORT_SYMBOL(crypto_qti_ice_setup_ice_hw); #else /* dumy ufsice driver */ static int crypto_qti_ice_probe(struct platform_device *pdev) { dev_info(&pdev->dev, "Proxy probing\n"); return 0; } static int crypto_qti_ice_remove(struct platform_device *pdev) { dev_info(&pdev->dev, "Proxy removing\n"); return 0; } #endif /* CONFIG_QTI_CRYPTO_FDE */ /* Following struct is required to match device with driver from dts file */ static const struct of_device_id crypto_qti_ice_match[] = { { .compatible = "qcom,ice" }, {}, }; MODULE_DEVICE_TABLE(of, crypto_qti_ice_match); static struct platform_driver crypto_qti_ice_driver = { .probe = crypto_qti_ice_probe, .remove = crypto_qti_ice_remove, .driver = { .name = "qcom_ice", .of_match_table = crypto_qti_ice_match, }, }; module_platform_driver(crypto_qti_ice_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Common crypto library for storage encryption");