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 <[email protected]>
---
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