diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 8ded5f72..cf843e7d 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. obj-m += ufs-tegra.o +ifeq ($(CONFIG_TEGRA_UFS_PROVISIONING),y) +ufs-tegra-objs += ufs-provision.o +endif diff --git a/drivers/scsi/ufs/ufs-provision.c b/drivers/scsi/ufs/ufs-provision.c new file mode 100644 index 00000000..eecd7c7f --- /dev/null +++ b/drivers/scsi/ufs/ufs-provision.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#include +#include "ufs-provision.h" +#include "ufs-tegra.h" +#ifdef CONFIG_DEBUG_FS + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) +#include +#else +#include +#endif + +#define CHECK_NULL(expr) \ + { \ + if (expr == NULL) { \ + err = -1; \ + goto out; \ + } \ + } + +#define START_PROVISIONING 1 +#define MAX_LUN_COUNT 8 +#define CONFIG_DESC_SIZE 0xe6 +#define UNIT_DESC_SIZE 26 +#define CONFIG_DESC_HEADER_SIZE 22 +#define ACTIVE_MODE 0x01 +#define EQUAL_PRIORITY 0x7F + +/* UNIT DESCRIPTOR FIELD OFFSETS */ +#define LUENABLE_OFFSET 0 +#define BOOTLUN_ID_OFFSET 1 +#define LU_WRITE_PROTECT_OFFSET 2 +#define MEMORY_TYPE_OFFSET 3 +#define NUM_ALLOC_UNITS_OFFSET 4 +#define DATA_RELIABILITY_OFFSET 8 +#define LOGICAL_BLK_SIZE_OFFSET 9 +#define PROV_TYPE_OFFSET 10 +#define CONTEXT_CAP_OFFSET 11 +#define WB_ALLOC_UNITS_OFFSET 22 + +/* CONFIGURATION DESCRIPTOR HEADER FIELDS OFFSETS */ +#define LENGTH_OFFSET 0 +#define DESC_TYPE_OFFFSET 1 +#define BOOT_ENABLE_OFFFSET 3 +#define DESCR_ACCESS_EN_OFFFSET 4 +#define INIT_PWR_MODE_OFFSET 5 +#define HIGH_PRIORITY_LUN_OFFSET 6 +#define WB_USER_SPACE_OFFSET 16 +#define WB_CONFIGURE_OFFSET 17 +#define WB_SHARED_BUFFER_OFFSET 18 + +static void populate_desc_header(struct ufs_tegra_host *ufs_tegra) +{ + u8 *lun_desc_buf; + + if ((ufs_tegra != NULL) && (ufs_tegra->lun_desc_buf != NULL)) { + lun_desc_buf = ufs_tegra->lun_desc_buf; + lun_desc_buf[LENGTH_OFFSET] = CONFIG_DESC_SIZE; + lun_desc_buf[DESC_TYPE_OFFFSET] = QUERY_DESC_IDN_CONFIGURATION; + lun_desc_buf[BOOT_ENABLE_OFFFSET] = ufs_tegra->boot_enable; + lun_desc_buf[DESCR_ACCESS_EN_OFFFSET] = + ufs_tegra->descr_access_en; + lun_desc_buf[INIT_PWR_MODE_OFFSET] = ACTIVE_MODE; + lun_desc_buf[HIGH_PRIORITY_LUN_OFFSET] = EQUAL_PRIORITY; + lun_desc_buf[WB_USER_SPACE_OFFSET] = ufs_tegra->enable_shared_wb; + lun_desc_buf[WB_CONFIGURE_OFFSET] = ufs_tegra->enable_shared_wb; + } else + dev_err(NULL, "lun_desc_buf is null\n"); +} + +static int validate_refclk_value(struct ufs_hba *hba, u32 refclk_value) +{ + if (refclk_value > 3) { + dev_err(hba->dev, "%s: Bad bRefClkFreq value\n" + "Input Value: 0x%02x\n" + "Valid Values are:\n" + "0x00: 19.2Mhz\n" + "0x01: 26Mhz\n" + "0x02: 38.4Mhz\n" + "0x03: 52Mhz\n" + , __func__, refclk_value); + return -EINVAL; + } + return 0; +} + +static int validate_bootlun_en_id_value(struct ufs_hba *hba, u32 bootlun_en_id) +{ + if (bootlun_en_id > 3) { + dev_err(hba->dev, "%s: Bad BootLUN ID\n" + "Input Value: 0x%02x\n" + "Valid Values are:\n" + "0x00: Boot Disble\n" + "0x01: Boot from BootLUN A\n" + "0x02: Boot from BootLUN B\n" + , __func__, bootlun_en_id); + return -EINVAL; + } + return 0; +} + +static int validate_desc_header(struct ufs_hba *hba, u8 *lun_desc_buf) +{ + int i, err = 0; + u8 desc_param; + + #define GET_PARAM_VAL(i, offset) \ + ((lun_desc_buf + CONFIG_DESC_HEADER_SIZE + (i * UNIT_DESC_SIZE))[(offset)]) + + if (lun_desc_buf == NULL) { + dev_err(hba->dev, "lun_desc_buf is null\n"); + return -EINVAL; + } + + if (lun_desc_buf[BOOT_ENABLE_OFFFSET] > 1) { + dev_err(hba->dev, + "%s: Bad bBootEnable:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 and 0x1\n" + , __func__, lun_desc_buf[BOOT_ENABLE_OFFFSET]); + err = -EINVAL; + } + + if (lun_desc_buf[DESCR_ACCESS_EN_OFFFSET] > 1) { + dev_err(hba->dev, + "%s: Bad bDescrAccessEn:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 and 0x1\n" + , __func__, + lun_desc_buf[DESCR_ACCESS_EN_OFFFSET]); + err = -EINVAL; + } + + for (i = 0; i < MAX_LUN_COUNT; i++) { + + desc_param = GET_PARAM_VAL(i, LUENABLE_OFFSET); + if (desc_param != 0 && desc_param != 1) { + dev_err(hba->dev, + "%s: Bad bLUenable for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 and 0x1\n" + , __func__, i, desc_param); + err = -EINVAL; + } + + desc_param = GET_PARAM_VAL(i, BOOTLUN_ID_OFFSET); + if (desc_param > 2) { + dev_err(hba->dev, + "%s: Bad bBootLunID for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 to 0x2\n" + , __func__, i, desc_param); + err = -EINVAL; + } + + desc_param = GET_PARAM_VAL(i, LU_WRITE_PROTECT_OFFSET); + if (desc_param > 3) { + dev_err(hba->dev, + "%s: Bad bLUWriteProtect for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 to 0x3\n" + , __func__, i, desc_param); + err = -EINVAL; + } + + desc_param = GET_PARAM_VAL(i, MEMORY_TYPE_OFFSET); + if (desc_param > 6) { + dev_err(hba->dev, "%s: Bad bMemoryType for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 to 0x6\n" + , __func__, i, desc_param); + err = -EINVAL; + } + + desc_param = GET_PARAM_VAL(i, DATA_RELIABILITY_OFFSET); + if (desc_param != 0 && desc_param != 1) { + dev_err(hba->dev, + "%s: Bad bDataReliability for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0 and 0x1\n" + , __func__, i, desc_param); + err = -EINVAL; + } + + desc_param = GET_PARAM_VAL(i, PROV_TYPE_OFFSET); + if (desc_param != 0 && desc_param != 2 && desc_param != 3) { + dev_err(hba->dev, + "%s: Bad bProvisioningType for LUN%d:\n" + "Input value: 0x%02x\n" + "Valid values are: 0x0, 0x2, and 0x3\n" + , __func__, i, desc_param); + err = -EINVAL; + } + } + + #undef GET_PARAM_VAL + + return err; +} + +static int provision_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t provision_debugfs_read(struct file *file, char __user *buf, + size_t count, loff_t *f_pos) +{ + return -EPERM; +} + +static ssize_t program_lun_debugfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *f_pos) +{ + int err = 0; + int i; + u32 lun_desc_len = CONFIG_DESC_SIZE; + ssize_t ret = 0; + char *kbuf = NULL; + u32 desc_lock; + struct ufs_hba *hba; + struct ufs_tegra_host *ufs_tegra; + + /* + * PROGRAM_LUN is set to 1 to trigeer programming of LUNS. + * Any write to PROGRAM_LUN after pogramming LUNs is discarded. + * Below check prevents reprogramming LUNs in same boot cycle. + */ + if ((file == NULL) || (file->private_data == NULL)) { + dev_err(NULL, "hba is null\n"); + return -EINVAL; + } + hba = file->private_data; + + if (hba->priv == NULL) { + dev_err(NULL, "ufs_tegra is null\n"); + return -EINVAL; + } + ufs_tegra = hba->priv; + + if (ufs_tegra->program_lun == 1) { + dev_err(hba->dev, "LUNs already programmed in this boot cycle\n" + "Reboot to program again\n"); + return -EPERM; + } + + kbuf = devm_kmalloc(hba->dev, sizeof(char)*count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = simple_write_to_buffer(kbuf, sizeof(char)*count, + f_pos, buf, count); + if (ret < 0) { + dev_err(hba->dev, "copy user space buffer failed"); + goto out_free; + } + + kbuf[count] = '\0'; + + err = kstrtol(kbuf, 10, &(ufs_tegra->program_lun)); + if (err) { + dev_err(hba->dev, "Value passed should be an integer\n"); + ret = err; + goto out_free; + } + + if (ufs_tegra->program_lun == START_PROVISIONING) { + + pm_runtime_get_sync(hba->dev); + + /* Read bConfigDescLock */ + if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_CONF_DESC_LOCK, 0, 0, &desc_lock)) { + dev_err(hba->dev, + "%s: Read bConfigDescrLock failed\n", __func__); + goto out; + } + + if (desc_lock != 0) { + dev_err(hba->dev, + "%s: Config Desciptor is locked\n" + "Cannot program LUNs on the device. Aborting\n" + , __func__); + goto out; + } + + /* Populate Config Desc Header */ + populate_desc_header(ufs_tegra); + + /* Print descriptor array created */ + dev_info(hba->dev, "Configuration Descriptor array:\n"); + for (i = 0; i < CONFIG_DESC_SIZE; i++) { + if ((i%16 == 0) && i) + pr_info("\n"); + pr_cont("0x%02x ", + (ufs_tegra->lun_desc_buf)[i]); + } + + /* Validate unit desc data given by user */ + err = validate_desc_header(hba, ufs_tegra->lun_desc_buf); + if (err) { + dev_err(hba->dev, + "%s: Descriptor Valdiation Failed\n", __func__); + ret = err; + goto out; + } + + /* Program LUN */ + err = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_WRITE_DESC, + QUERY_DESC_IDN_CONFIGURATION, 0, 0, ufs_tegra->lun_desc_buf, + &lun_desc_len); + if (err) { + dev_err(hba->dev, + "%s: Failed to program LUNs\n", __func__); + } else { + dev_info(hba->dev, + "%s: LUN Programming successful\n", __func__); + } + } else { + dev_info(hba->dev, "%s: Input value is not 0x01\n" + "Skip programming LUNs\n", __func__); + } +out: + err = pm_runtime_put_sync(hba->dev); + if (err) { + dev_err(hba->dev, + "pm_runtime_put_sync failed with error = %d\n", err); + } + +out_free: + devm_kfree(hba->dev, kbuf); + return ret; +} + +static const struct file_operations program_lun_debugfs_ops = { + .open = provision_debugfs_open, + .read = provision_debugfs_read, + .write = program_lun_debugfs_write, +}; + +static ssize_t program_refclk_debugfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *f_pos) +{ + int err = 0; + ssize_t ret = 0; + char *kbuf = NULL; + struct ufs_hba *hba; + struct ufs_tegra_host *ufs_tegra; + + if ((file == NULL) || (file->private_data == NULL)) { + dev_err(NULL, "hba is null\n"); + return -EINVAL; + } + hba = file->private_data; + + if (hba->priv == NULL) { + dev_err(NULL, "ufs_tegra is null\n"); + return -EINVAL; + } + ufs_tegra = hba->priv; + + kbuf = devm_kmalloc(hba->dev, sizeof(char)*count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = simple_write_to_buffer(kbuf, sizeof(char)*count, + f_pos, buf, count); + if (ret < 0) { + dev_err(hba->dev, "copy user space buffer failed"); + goto out; + } + + kbuf[count] = '\0'; + + err = kstrtol(kbuf, 10, &(ufs_tegra->program_refclk)); + if (err) { + dev_err(hba->dev, "Value passed should be an integer\n"); + ret = err; + goto out; + } + + if (ufs_tegra->program_refclk == START_PROVISIONING) { + + /* Validate refclk_value */ + err = validate_refclk_value(hba, ufs_tegra->refclk_value); + if (err) { + dev_err(hba->dev, + "%s: Refclkfreq value valdiation failed\n", + __func__); + ret = err; + goto out; + } + + pm_runtime_get_sync(hba->dev); + /* Write brefclkFreq value */ + err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &(ufs_tegra->refclk_value)); + if (err) { + dev_err(hba->dev, + "%s: Write bRefClkFreq failed %d\n", + __func__, err); + ret = err; + goto out; + } + + /* Read brefclkFreq value */ + err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &(ufs_tegra->refclk_value)); + if (err) { + dev_err(hba->dev, + "%s: Read bRefClkFreq failed %d\n", + __func__, err); + ret = err; + goto out; + } + dev_info(hba->dev, "%s: bRefclkFreq value is %d\n", + __func__, ufs_tegra->refclk_value); + } else { + dev_info(hba->dev, "%s: Input value is not 0x01\n" + "Skip progamming refclkfreq\n", __func__); + } + + err = pm_runtime_put_sync(hba->dev); + if (err) { + dev_err(hba->dev, + "%s: pm_runtime_put_sync failed with error = %d\n", __func__, err); + } + +out: + devm_kfree(hba->dev, kbuf); + return ret; +} + +static const struct file_operations refclk_debugfs_ops = { + .open = provision_debugfs_open, + .read = provision_debugfs_read, + .write = program_refclk_debugfs_write, +}; + + +static ssize_t program_bootlun_en_id_debugfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *f_pos) +{ + int err = 0; + ssize_t ret = 0; + char *kbuf = NULL; + struct ufs_hba *hba; + struct ufs_tegra_host *ufs_tegra; + + if ((file == NULL) || (file->private_data == NULL)) { + dev_err(NULL, "hba is null\n"); + return -EINVAL; + } + hba = file->private_data; + + if (hba->priv == NULL) { + dev_err(NULL, "ufs_tegra is null\n"); + return -EINVAL; + } + ufs_tegra = hba->priv; + + kbuf = devm_kmalloc(hba->dev, sizeof(char)*count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = simple_write_to_buffer(kbuf, sizeof(char)*count, + f_pos, buf, count); + if (ret < 0) + goto out; + + kbuf[count] = '\0'; + + err = kstrtol(kbuf, 10, &(ufs_tegra->program_bootlun_en_id)); + if (err) { + dev_err(hba->dev, "Value passed should be an integer\n"); + ret = err; + goto out; + } + + if (ufs_tegra->program_bootlun_en_id == START_PROVISIONING) { + + /* Validate bootlun_en_id */ + err = validate_bootlun_en_id_value(hba, ufs_tegra->bootlun_en_id); + if (err) { + dev_err(hba->dev, + "%s: BootLunEn value valdiation failed\n", + __func__); + ret = err; + goto out; + } + + err = pm_runtime_get_sync(hba->dev); + if (err < 0) { + dev_err(hba->dev, + "pm_runtime_get_sync failed with error = %d\n" + "Continuing UFS Provisioning\n", err); + } + + /* Write bBootLunEn value */ + if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, + &(ufs_tegra->bootlun_en_id))) { + dev_err(hba->dev, + "%s: Write bBootLunEn failed\n", __func__); + } + + /* Read bBootLunEn value */ + if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, + &(ufs_tegra->bootlun_en_id))) { + dev_err(hba->dev, + "%s: Read bBootLunEn failed\n", __func__); + } + dev_info(hba->dev, "%s: bBootLunEn value is %d\n", + __func__, ufs_tegra->bootlun_en_id); + } else { + dev_info(hba->dev, "%s: Input value is not 0x01\n" + "Skip progamming bBootLunEn\n", __func__); + } + err = pm_runtime_put_sync(hba->dev); + if (err) { + dev_err(hba->dev, + "%s: pm_runtime_put_sync failed with error = %d\n", + __func__, err); + } + +out: + devm_kfree(hba->dev, kbuf); + return ret; +} + +static const struct file_operations bootlun_en_id_debugfs_ops = { + .open = provision_debugfs_open, + .read = provision_debugfs_read, + .write = program_bootlun_en_id_debugfs_write, +}; + +static int create_desc_debugfs_nodes(struct dentry *parent_lun_root, + u8 *lun_desc_off) +{ + + debugfs_create_x8("bLUenable", 0644, parent_lun_root, + lun_desc_off + LUENABLE_OFFSET); + debugfs_create_x8("bBootLUNID", 0644, parent_lun_root, + lun_desc_off + BOOTLUN_ID_OFFSET); + debugfs_create_x8("bLUWriteProtect", 0644, parent_lun_root, + lun_desc_off + LU_WRITE_PROTECT_OFFSET); + debugfs_create_x8("bMemoryType", 0644, parent_lun_root, + lun_desc_off + MEMORY_TYPE_OFFSET); + debugfs_create_x32("dNumAllocUnits", 0644, parent_lun_root, + (u32 *)(lun_desc_off + NUM_ALLOC_UNITS_OFFSET)); + debugfs_create_x8("bDataReliability", 0644, parent_lun_root, + lun_desc_off + DATA_RELIABILITY_OFFSET); + debugfs_create_x8("bLogicalBlocksize", 0644, parent_lun_root, + lun_desc_off + LOGICAL_BLK_SIZE_OFFSET); + debugfs_create_x8("bProvisionType", 0644, parent_lun_root, + lun_desc_off + PROV_TYPE_OFFSET); + debugfs_create_x16("wContextCapabilities", 0644, + parent_lun_root, (u16 *)(lun_desc_off + CONTEXT_CAP_OFFSET)); + debugfs_create_x32("dWBAllocUnits", 0644, parent_lun_root, + (u32 *)(lun_desc_off + WB_ALLOC_UNITS_OFFSET)); + + return 0; +} + +void debugfs_provision_init(struct ufs_hba *hba, struct dentry *device_root) +{ + struct dentry *refclk_root = NULL, *bootlun_en_id_root = NULL, + *lun_root = NULL, *tmp_lun_root; + struct ufs_tegra_host *ufs_tegra = (struct ufs_tegra_host *)hba->priv; + char lun_name[5]; + int i, err; + + refclk_root = debugfs_create_dir("ufs_refclk", device_root); + CHECK_NULL(refclk_root); + debugfs_create_x32("refclkfreq_value", 0644, + refclk_root, &(ufs_tegra->refclk_value)); + debugfs_create_file("program_refclkfreq", 0644, + refclk_root, hba, &refclk_debugfs_ops); + + bootlun_en_id_root = debugfs_create_dir("ufs_bootlun_en_id", device_root); + CHECK_NULL(bootlun_en_id_root); + debugfs_create_x32("bootlun_en_id", 0644, + bootlun_en_id_root, &(ufs_tegra->bootlun_en_id)); + debugfs_create_file("program_bootlun_en_id", 0644, + bootlun_en_id_root, hba, &bootlun_en_id_debugfs_ops); + + /* Create debugfs for LUN programming */ + ufs_tegra->lun_desc_buf = devm_kzalloc(hba->dev, + CONFIG_DESC_SIZE, GFP_KERNEL); + if (!ufs_tegra->lun_desc_buf) { + dev_err(hba->dev, + "No memory for Configuration Descriptor Array\n"); + err = -ENOMEM; + goto out; + } + + lun_root = debugfs_create_dir("ufs_luns", device_root); + CHECK_NULL(lun_root); + debugfs_create_x32("boot_enable", 0644, + lun_root, &(ufs_tegra->boot_enable)); + debugfs_create_x32("descr_access_en", 0644, + lun_root, &(ufs_tegra->descr_access_en)); + debugfs_create_x32("shared_wb_alloc_units", 0644, + lun_root, (u32 *)(ufs_tegra->lun_desc_buf + WB_SHARED_BUFFER_OFFSET)); + debugfs_create_x8("enable_shared_wb", 0644, + lun_root, &(ufs_tegra->enable_shared_wb)); + debugfs_create_file("program_lun", 0644, + lun_root, hba, &program_lun_debugfs_ops); + + for (i = 0; i < MAX_LUN_COUNT; i++) { + + err = snprintf(lun_name, sizeof(lun_name), "lun%d", i); + if (err < 0) { + dev_err(hba->dev, "snprintf o/p error\n"); + goto out; + } + tmp_lun_root = debugfs_create_dir(lun_name, lun_root); + CHECK_NULL(tmp_lun_root); + + /* + * Skip CONFIG_DESC_HEADER_SIZE i.e 16 bytes of desciptor + * First 16 Bytes of descriptor are for config desc header + */ + err = create_desc_debugfs_nodes(tmp_lun_root, + ufs_tegra->lun_desc_buf + + CONFIG_DESC_HEADER_SIZE + i*UNIT_DESC_SIZE); + if (err) + goto out; + } +out: + if (err) { + dev_err(hba->dev, "Failed to create debugfs entries\n"); + debugfs_remove_recursive(lun_root); + debugfs_remove_recursive(refclk_root); + } +} + +void debugfs_provision_exit(struct ufs_hba *hba) +{ + struct ufs_tegra_host *ufs_tegra = (struct ufs_tegra_host *)hba->priv; + + /* Free config desc buffer */ + devm_kfree(hba->dev, ufs_tegra->lun_desc_buf); + ufs_tegra->lun_desc_buf = NULL; + +} +#endif + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/scsi/ufs/ufs-provision.h b/drivers/scsi/ufs/ufs-provision.h new file mode 100644 index 00000000..ef743203 --- /dev/null +++ b/drivers/scsi/ufs/ufs-provision.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only + */ +// Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#ifndef _UFS_PROVISION_H +#define _UFS_PROVISION_H + +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) +#include +#else +#include +#endif + +#ifdef CONFIG_DEBUG_FS +#include +void debugfs_provision_init(struct ufs_hba *hba, struct dentry *device_root); +void debugfs_provision_exit(struct ufs_hba *hba); +#endif + +#endif diff --git a/drivers/scsi/ufs/ufs-tegra.c b/drivers/scsi/ufs/ufs-tegra.c index 839cc380..96377f31 100644 --- a/drivers/scsi/ufs/ufs-tegra.c +++ b/drivers/scsi/ufs/ufs-tegra.c @@ -1,6 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2015-2022, NVIDIA CORPORATION. All rights reserved. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include #include @@ -37,6 +36,20 @@ #endif #include "ufs-tegra.h" +#include "ufs-provision.h" + +static void ufs_tegra_init_debugfs(struct ufs_hba *hba) +{ + struct dentry *device_root; + struct ufs_tegra_host *ufs_tegra = hba->priv; + + device_root = debugfs_create_dir(dev_name(hba->dev), NULL); + +#ifdef CONFIG_TEGRA_UFS_PROVISIONING + if (ufs_tegra->enable_ufs_provisioning) + debugfs_provision_init(hba, device_root); +#endif +} static void ufs_tegra_set_clk_div(struct ufs_hba *hba, u32 divider_val) { @@ -1536,6 +1549,10 @@ static int ufs_tegra_init(struct ufs_hba *hba) ufs_tegra_set_clk_div(hba, UFS_VNDR_HCLKDIV_1US_TICK); ufs_tegra_eq_timeout(ufs_tegra); +#ifdef CONFIG_DEBUG_FS + ufs_tegra_init_debugfs(hba); +#endif + return err; out_disable_mphylane_clks: @@ -1553,6 +1570,13 @@ static void ufs_tegra_exit(struct ufs_hba *hba) struct ufs_tegra_host *ufs_tegra = hba->priv; ufs_tegra_disable_mphylane_clks(ufs_tegra); + +#ifdef CONFIG_DEBUG_FS +#ifdef CONFIG_TEGRA_UFS_PROVISIONING + if (ufs_tegra->enable_ufs_provisioning) + debugfs_provision_exit(hba); +#endif +#endif } /** diff --git a/drivers/scsi/ufs/ufs-tegra.h b/drivers/scsi/ufs/ufs-tegra.h index 46bf5003..de57a996 100644 --- a/drivers/scsi/ufs/ufs-tegra.h +++ b/drivers/scsi/ufs/ufs-tegra.h @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2015-2022, NVIDIA CORPORATION. All rights reserved. +/* SPDX-License-Identifier: GPL-2.0-only */ +// Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #ifndef _UFS_TEGRA_H #define _UFS_TEGRA_H @@ -382,6 +382,7 @@ struct ufs_tegra_host { long program_bootlun_en_id; u32 boot_enable; u32 descr_access_en; + u8 enable_shared_wb; u8 *lun_desc_buf; long program_lun; #endif