Add sysfs support to trigger ufs provisioning at runtime. Usage : echo <desc_buf> > /sys/bus/platform/devices/1d84000.ufshcd/ufs_provision To check provisioning status: cat /sys/bus/platform/devices/1d84000.ufshc/ufs_provision 1 -> Success (Reboot device to check updated provisioning) Signed-off-by: Sayali Lokhande <sayalil@xxxxxxxxxxxxxx> --- drivers/scsi/ufs/ufs.h | 2 + drivers/scsi/ufs/ufshcd.c | 125 +++++++++++++++++++++++++++++++++++++++++++++- drivers/scsi/ufs/ufshcd.h | 6 +++ 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 1f99904..0b497fc 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -427,6 +427,7 @@ enum { }; struct ufs_unit_desc { + u8 LUNum; u8 bLUEnable; /* 1 for enabled LU */ u8 bBootLunID; /* 0 for using this LU for boot */ u8 bLUWriteProtect; /* 1 = power on WP, 2 = permanent WP */ @@ -451,6 +452,7 @@ struct ufs_config_descr { u32 qVendorConfigCode; /* Vendor specific configuration code */ struct ufs_unit_desc unit[8]; u8 lun_to_grow; + u8 num_luns; }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9ae64e2..0c94885 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1580,6 +1580,106 @@ void ufshcd_release(struct ufs_hba *hba) } EXPORT_SYMBOL_GPL(ufshcd_release); +static ssize_t ufshcd_desc_config_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", hba->ufs_provision.is_enabled); +} + +static ssize_t ufshcd_desc_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + struct ufs_config_descr *cfg = &hba->cfgs; + char *strbuf; + char *strbuf_copy; + int desc_buf[count]; + int *pt; + char *token; + int i, ret; + int value, commit = 0; + int num_luns = 0; + int KB_per_block = 4; + + /* reserve one byte for null termination */ + strbuf = kmalloc(count + 1, GFP_KERNEL); + if (!strbuf) + return -ENOMEM; + + strbuf_copy = strbuf; + strlcpy(strbuf, buf, count + 1); + + for (i = 0; i < count; i++) { + token = strsep(&strbuf, " "); + if (!token) { + num_luns = desc_buf[i-1]; + dev_dbg(hba->dev, "%s: token %s, num_luns %d\n", + __func__, token, num_luns); + break; + } + + ret = kstrtoint(token, 0, &value); + if (ret) { + dev_err(hba->dev, "%s: kstrtoint failed %d %s\n", + __func__, ret, token); + break; + } + desc_buf[i] = value; + dev_dbg(hba->dev, " desc_buf[%d] 0x%x", i, desc_buf[i]); + } + + /* Fill in the descriptors with parsed configuration data */ + pt = desc_buf; + cfg->bNumberLU = *pt++; + cfg->bBootEnable = *pt++; + cfg->bDescrAccessEn = *pt++; + cfg->bInitPowerMode = *pt++; + cfg->bHighPriorityLUN = *pt++; + cfg->bSecureRemovalType = *pt++; + cfg->bInitActiveICCLevel = *pt++; + cfg->wPeriodicRTCUpdate = *pt++; + cfg->bConfigDescrLock = *pt++; + dev_dbg(hba->dev, "%s: %u %u %u %u %u %u %u %u %u\n", __func__, + cfg->bNumberLU, cfg->bBootEnable, cfg->bDescrAccessEn, + cfg->bInitPowerMode, cfg->bHighPriorityLUN, cfg->bSecureRemovalType, + cfg->bInitActiveICCLevel, cfg->wPeriodicRTCUpdate, + cfg->bConfigDescrLock); + + for (i = 0; i < num_luns; i++) { + cfg->unit[i].LUNum = *pt++; + cfg->unit[i].bLUEnable = *pt++; + cfg->unit[i].bBootLunID = *pt++; + /* dNumAllocUnits = size_in_kb/KB_per_block */ + cfg->unit[i].dNumAllocUnits = (u32)(*pt++ / KB_per_block); + cfg->unit[i].bDataReliability = *pt++; + cfg->unit[i].bLUWriteProtect = *pt++; + cfg->unit[i].bMemoryType = *pt++; + cfg->unit[i].bLogicalBlockSize = *pt++; + cfg->unit[i].bProvisioningType = *pt++; + cfg->unit[i].wContextCapabilities = *pt++; + } + + cfg->lun_to_grow = *pt++; + commit = *pt++; + cfg->num_luns = *pt; + dev_dbg(hba->dev, "%s: lun_to_grow %u, commit %u num_luns %u\n", + __func__, cfg->lun_to_grow, commit, cfg->num_luns); + if (commit == 1) { + ret = ufshcd_do_config_device(hba); + if (!ret) { + hba->ufs_provision.is_enabled = 1; + dev_err(hba->dev, + "%s: UFS Provisioning completed,num_luns %u, reboot now !\n", + __func__, cfg->num_luns); + } + } + + kfree(strbuf_copy); + return count; +} + static ssize_t ufshcd_clkgate_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1638,6 +1738,23 @@ static ssize_t ufshcd_clkgate_enable_store(struct device *dev, return count; } +static void ufshcd_init_ufs_provision(struct ufs_hba *hba) +{ + hba->ufs_provision.enable_attr.show = ufshcd_desc_config_show; + hba->ufs_provision.enable_attr.store = ufshcd_desc_config_store; + sysfs_attr_init(&hba->ufs_provision.enable_attr.attr); + hba->ufs_provision.enable_attr.attr.name = "ufs_provision"; + hba->ufs_provision.enable_attr.attr.mode = 0644; + if (device_create_file(hba->dev, &hba->ufs_provision.enable_attr)) + dev_err(hba->dev, "%s: Failed to create sysfs for ufs_provision\n", + __func__); +} + +static void ufshcd_exit_ufs_provision(struct ufs_hba *hba) +{ + device_remove_file(hba->dev, &hba->ufs_provision.enable_attr); +} + static void ufshcd_init_clk_gating(struct ufs_hba *hba) { if (!ufshcd_is_clkgating_allowed(hba)) @@ -6383,7 +6500,7 @@ static int ufshcd_do_config_device(struct ufs_hba *hba) u32 blocks_per_alloc_unit = 1024; int geo_len = hba->desc_size.geom_desc; u8 geo_buf[hba->desc_size.geom_desc]; - unsigned int max_partitions = 9; + unsigned int max_partitions = 8; WARN_ON(!hba || !cfg); ufshcd_set_dev_ref_clk(hba); @@ -6530,6 +6647,9 @@ static int ufshcd_do_config_device(struct ufs_hba *hba) pt = pt + 3; // Reserved fields set to 0 } + for (i = 0; i < buff_len; i++) + dev_dbg(hba->dev, " desc_buf[%d] 0x%x", i, desc_buf[i]); + ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_WRITE_DESC, QUERY_DESC_IDN_CONFIGURATION, 0, 0, desc_buf, &buff_len); @@ -7888,6 +8008,7 @@ void ufshcd_remove(struct ufs_hba *hba) ufshcd_hba_stop(hba, true); ufshcd_exit_clk_gating(hba); + ufshcd_exit_ufs_provision(hba); if (ufshcd_is_clkscaling_supported(hba)) device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); ufshcd_hba_exit(hba); @@ -8050,6 +8171,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) init_waitqueue_head(&hba->dev_cmd.tag_wq); ufshcd_init_clk_gating(hba); + ufshcd_init_ufs_provision(hba); /* * In order to avoid any spurious interrupt immediately after @@ -8142,6 +8264,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) scsi_remove_host(hba->host); exit_gating: ufshcd_exit_clk_gating(hba); + ufshcd_exit_ufs_provision(hba); out_disable: hba->is_irq_enabled = false; ufshcd_hba_exit(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 0c4b683..3580631 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -364,6 +364,11 @@ struct ufs_clk_gating { int active_reqs; }; +struct ufs_provisioning { + struct device_attribute enable_attr; + bool is_enabled; +}; + struct ufs_saved_pwr_info { struct ufs_pa_layer_attr info; bool is_valid; @@ -652,6 +657,7 @@ struct ufs_hba { struct ufs_pwr_mode_info max_pwr_info; struct ufs_clk_gating clk_gating; + struct ufs_provisioning ufs_provision; /* Control to enable/disable host capabilities */ u32 caps; /* Allow dynamic clk gating */ -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project