Add configfs support to provision ufs device at runtime.
Usage:
echo <desc_buf> > /config/ufshcd/ufs_provision
To check provisioning status:
cat /config/ufshcd/ufs_provision
1 -> Success (Reboot device to check updated provisioning)
Signed-off-by: Sayali Lokhande <[email protected]>
---
Documentation/ABI/testing/configfs-driver-ufs | 14 ++
drivers/scsi/ufs/Makefile | 1 +
drivers/scsi/ufs/ufs-configfs.c | 191 ++++++++++++++++++++++++++
drivers/scsi/ufs/ufs.h | 2 +
drivers/scsi/ufs/ufshcd.c | 2 +
drivers/scsi/ufs/ufshcd.h | 7 +
6 files changed, 217 insertions(+)
create mode 100644 Documentation/ABI/testing/configfs-driver-ufs
create mode 100644 drivers/scsi/ufs/ufs-configfs.c
diff --git a/Documentation/ABI/testing/configfs-driver-ufs b/Documentation/ABI/testing/configfs-driver-ufs
new file mode 100644
index 0000000..a17fe15
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-driver-ufs
@@ -0,0 +1,14 @@
+What: /config/ufshcd/ufs_provision
+Date: Jun 2018
+KernelVersion: 4.14
+Description:
+ This file shows the status of runtime ufs provisioning.
+ This can be used to provision ufs device if bConfigDescrLock is 0.
+ Configuration buffer needs to be written in space separated format
+ specificied as:
+ echo <bNumberLU> <bBootEnable> <bDescrAccessEn> <bInitPowerMode>
+ <bHighPriorityLUN> <bSecureRemovalType> <bInitActiveICCLevel>
+ <wPeriodicRTCUpdate> <bConfigDescrLock> <LUNum> <bLUEnable>
+ <bBootLunID> <size_in_kb> <bDataReliability> <bLUWriteProtect>
+ <bMemoryType> <bLogicalBlockSize> <bProvisioningType>
+ <wContextCapabilities> > /config/ufshcd/ufs_provision
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 918f579..d438e74 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -5,5 +5,6 @@ obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-d
obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
ufshcd-core-objs := ufshcd.o ufs-sysfs.o
+obj-$(CONFIG_CONFIGFS_FS) += ufs-configfs.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs-configfs.c b/drivers/scsi/ufs/ufs-configfs.c
new file mode 100644
index 0000000..7655e6b
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-configfs.c
@@ -0,0 +1,191 @@
+/*
+ * drivers/scsi/ufs/ufs-configfs.c
+ *
+ * Copyright (c) 2018, Qualcomm Technologies, Inc.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/string.h>
+#include <asm/unaligned.h>
+#include <linux/configfs.h>
+
+#include "ufs.h"
+#include "ufshcd.h"
+
+struct ufs_hba *hba;
+
+static ssize_t ufs_provision_show(struct config_item *item, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "provision_enabled = %x\n",
+ hba->provision_enabled);
+}
+
+ssize_t ufshcd_desc_configfs_store(const char *buf, size_t count)
+{
+ 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);
+ memset(desc_buf, 0, count);
+
+ /* Just return if bConfigDescrLock is already set */
+ ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_CONF_DESC_LOCK, 0, 0, &cfg->bConfigDescrLock);
+ if (ret) {
+ dev_err(hba->dev, "%s: Failed reading bConfigDescrLock %d, cannot re-provision device!\n",
+ __func__, ret);
+ hba->provision_enabled = 0;
+ goto out;
+ }
+ if (cfg->bConfigDescrLock == 1) {
+ dev_err(hba->dev, "%s: bConfigDescrLock already set to %u, cannot re-provision device!\n",
+ __func__, cfg->bConfigDescrLock);
+ hba->provision_enabled = 0;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ token = strsep(&strbuf, " ");
+ if (!token && i) {
+ num_luns = desc_buf[i-1];
+ dev_dbg(hba->dev, "%s: token %s, num_luns %d\n",
+ __func__, token, num_luns);
+ if (num_luns > 8) {
+ dev_err(hba->dev, "%s: Invalid num_luns %d\n",
+ __func__, num_luns);
+ hba->provision_enabled = 0;
+ goto out;
+ }
+ 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->provision_enabled = 1;
+ dev_err(hba->dev,
+ "%s: UFS Provisioning completed,num_luns %u, reboot now !\n",
+ __func__, cfg->num_luns);
+ }
+ } else
+ dev_err(hba->dev, "%s: Invalid commit %u\n", __func__, commit);
+out:
+ kfree(strbuf_copy);
+ return count;
+}
+
+static ssize_t ufs_provision_store(struct config_item *item,
+ const char *buf, size_t count)
+{
+ return ufshcd_desc_configfs_store(buf, count);
+}
+
+static struct configfs_attribute ufshcd_attr_provision = {
+ .ca_name = "ufs_provision",
+ .ca_mode = S_IRUGO | S_IWUGO,
+ .ca_owner = THIS_MODULE,
+ .show = ufs_provision_show,
+ .store = ufs_provision_store,
+};
+
+static struct configfs_attribute *ufshcd_attrs[] = {
+ &ufshcd_attr_provision,
+ NULL,
+};
+
+static struct config_item_type ufscfg_type = {
+ .ct_attrs = ufshcd_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem ufscfg_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "ufshcd",
+ .ci_type = &ufscfg_type,
+ },
+ },
+};
+
+int ufshcd_configfs_init(struct ufs_hba *hba_ufs)
+{
+ int ret;
+ struct configfs_subsystem *subsys = &ufscfg_subsys;
+ hba = hba_ufs;
+
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+ ret = configfs_register_subsystem(subsys);
+ if (ret) {
+ pr_err("Error %d while registering subsystem %s\n",
+ ret,
+ subsys->su_group.cg_item.ci_namebuf);
+ }
+ return ret;
+}
+
+void ufshcd_configfs_exit(void)
+{
+ configfs_unregister_subsystem(&ufscfg_subsys);
+}
\ No newline at end of file
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 c0235a4..63d6532 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -7854,6 +7854,7 @@ int ufshcd_shutdown(struct ufs_hba *hba)
void ufshcd_remove(struct ufs_hba *hba)
{
ufs_sysfs_remove_nodes(hba->dev);
+ ufshcd_configfs_exit();
scsi_remove_host(hba->host);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
@@ -8107,6 +8108,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
async_schedule(ufshcd_async_scan, hba);
ufs_sysfs_add_nodes(hba->dev);
+ ufshcd_configfs_init(hba);
return 0;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 816b674..fb254c8 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -41,6 +41,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/configfs.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -550,6 +551,7 @@ struct ufs_hba {
bool is_irq_enabled;
u32 dev_ref_clk_freq;
struct ufs_config_descr cfgs;
+ bool provision_enabled;
/* Interrupt aggregation support is broken */
#define UFSHCD_QUIRK_BROKEN_INTR_AGGR 0x1
@@ -867,7 +869,12 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
int ufshcd_hold(struct ufs_hba *hba, bool async);
void ufshcd_release(struct ufs_hba *hba);
+
+/* Expose UFS configfs API's */
int ufshcd_do_config_device(struct ufs_hba *hba);
+ssize_t ufshcd_desc_configfs_store(const char *buf, size_t count);
+int ufshcd_configfs_init(struct ufs_hba *hba_ufs);
+void ufshcd_configfs_exit(void);
int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
int *desc_length);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Hi Sayali,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on mkp-scsi/for-next]
[also build test ERROR on v4.17 next-20180614]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Sayali-Lokhande/Add-ufs-provisioning-support-in-driver/20180614-211513
base: https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 8.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=8.1.0 make.cross ARCH=ia64
All errors (new ones prefixed by >>):
ERROR: "ia64_delay_loop" [drivers/spi/spi-thunderx.ko] undefined!
>> ERROR: "ufshcd_configfs_init" [drivers/scsi/ufs/ufshcd-core.ko] undefined!
>> ERROR: "ufshcd_configfs_exit" [drivers/scsi/ufs/ufshcd-core.ko] undefined!
ERROR: "__sw_hweight8" [drivers/net/wireless/mediatek/mt76/mt76.ko] undefined!
ERROR: "ia64_delay_loop" [drivers/net/phy/mdio-cavium.ko] undefined!
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Sayali,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on mkp-scsi/for-next]
[also build test ERROR on v4.17 next-20180614]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Sayali-Lokhande/Add-ufs-provisioning-support-in-driver/20180614-211513
base: https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next
config: x86_64-randconfig-s5-06142227 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/scsi/ufs/ufshcd.o: In function `ufshcd_init':
>> drivers/scsi/ufs/ufshcd.c:8311: undefined reference to `ufshcd_configfs_init'
drivers/scsi/ufs/ufshcd.o: In function `ufshcd_remove':
>> drivers/scsi/ufs/ufshcd.c:8057: undefined reference to `ufshcd_configfs_exit'
vim +8311 drivers/scsi/ufs/ufshcd.c
8133
8134 /**
8135 * ufshcd_init - Driver initialization routine
8136 * @hba: per-adapter instance
8137 * @mmio_base: base register address
8138 * @irq: Interrupt line of device
8139 * Returns 0 on success, non-zero value on failure
8140 */
8141 int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
8142 {
8143 int err;
8144 struct Scsi_Host *host = hba->host;
8145 struct device *dev = hba->dev;
8146
8147 if (!mmio_base) {
8148 dev_err(hba->dev,
8149 "Invalid memory reference for mmio_base is NULL\n");
8150 err = -ENODEV;
8151 goto out_error;
8152 }
8153
8154 hba->mmio_base = mmio_base;
8155 hba->irq = irq;
8156
8157 /* Set descriptor lengths to specification defaults */
8158 ufshcd_def_desc_sizes(hba);
8159
8160 err = ufshcd_hba_init(hba);
8161 if (err)
8162 goto out_error;
8163
8164 /* Read capabilities registers */
8165 ufshcd_hba_capabilities(hba);
8166
8167 /* Get UFS version supported by the controller */
8168 hba->ufs_version = ufshcd_get_ufs_version(hba);
8169
8170 if ((hba->ufs_version != UFSHCI_VERSION_10) &&
8171 (hba->ufs_version != UFSHCI_VERSION_11) &&
8172 (hba->ufs_version != UFSHCI_VERSION_20) &&
8173 (hba->ufs_version != UFSHCI_VERSION_21))
8174 dev_err(hba->dev, "invalid UFS version 0x%x\n",
8175 hba->ufs_version);
8176
8177 /* Get Interrupt bit mask per version */
8178 hba->intr_mask = ufshcd_get_intr_mask(hba);
8179
8180 err = ufshcd_set_dma_mask(hba);
8181 if (err) {
8182 dev_err(hba->dev, "set dma mask failed\n");
8183 goto out_disable;
8184 }
8185
8186 /* Allocate memory for host memory space */
8187 err = ufshcd_memory_alloc(hba);
8188 if (err) {
8189 dev_err(hba->dev, "Memory allocation failed\n");
8190 goto out_disable;
8191 }
8192
8193 /* Configure LRB */
8194 ufshcd_host_memory_configure(hba);
8195
8196 host->can_queue = hba->nutrs;
8197 host->cmd_per_lun = hba->nutrs;
8198 host->max_id = UFSHCD_MAX_ID;
8199 host->max_lun = UFS_MAX_LUNS;
8200 host->max_channel = UFSHCD_MAX_CHANNEL;
8201 host->unique_id = host->host_no;
8202 host->max_cmd_len = MAX_CDB_SIZE;
8203
8204 hba->max_pwr_info.is_valid = false;
8205
8206 /* Initailize wait queue for task management */
8207 init_waitqueue_head(&hba->tm_wq);
8208 init_waitqueue_head(&hba->tm_tag_wq);
8209
8210 /* Initialize work queues */
8211 INIT_WORK(&hba->eh_work, ufshcd_err_handler);
8212 INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
8213
8214 /* Initialize UIC command mutex */
8215 mutex_init(&hba->uic_cmd_mutex);
8216
8217 /* Initialize mutex for device management commands */
8218 mutex_init(&hba->dev_cmd.lock);
8219
8220 init_rwsem(&hba->clk_scaling_lock);
8221
8222 /* Initialize device management tag acquire wait queue */
8223 init_waitqueue_head(&hba->dev_cmd.tag_wq);
8224
8225 ufshcd_init_clk_gating(hba);
8226
8227 /*
8228 * In order to avoid any spurious interrupt immediately after
8229 * registering UFS controller interrupt handler, clear any pending UFS
8230 * interrupt status and disable all the UFS interrupts.
8231 */
8232 ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
8233 REG_INTERRUPT_STATUS);
8234 ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
8235 /*
8236 * Make sure that UFS interrupts are disabled and any pending interrupt
8237 * status is cleared before registering UFS interrupt handler.
8238 */
8239 mb();
8240
8241 /* IRQ registration */
8242 err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
8243 if (err) {
8244 dev_err(hba->dev, "request irq failed\n");
8245 goto exit_gating;
8246 } else {
8247 hba->is_irq_enabled = true;
8248 }
8249
8250 err = scsi_add_host(host, hba->dev);
8251 if (err) {
8252 dev_err(hba->dev, "scsi_add_host failed\n");
8253 goto exit_gating;
8254 }
8255
8256 /* Host controller enable */
8257 err = ufshcd_hba_enable(hba);
8258 if (err) {
8259 dev_err(hba->dev, "Host controller enable failed\n");
8260 ufshcd_print_host_regs(hba);
8261 ufshcd_print_host_state(hba);
8262 goto out_remove_scsi_host;
8263 }
8264
8265 if (ufshcd_is_clkscaling_supported(hba)) {
8266 char wq_name[sizeof("ufs_clkscaling_00")];
8267
8268 INIT_WORK(&hba->clk_scaling.suspend_work,
8269 ufshcd_clk_scaling_suspend_work);
8270 INIT_WORK(&hba->clk_scaling.resume_work,
8271 ufshcd_clk_scaling_resume_work);
8272
8273 snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d",
8274 host->host_no);
8275 hba->clk_scaling.workq = create_singlethread_workqueue(wq_name);
8276
8277 ufshcd_clkscaling_init_sysfs(hba);
8278 }
8279
8280 /*
8281 * Set the default power management level for runtime and system PM.
8282 * Default power saving mode is to keep UFS link in Hibern8 state
8283 * and UFS device in sleep state.
8284 */
8285 hba->rpm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
8286 UFS_SLEEP_PWR_MODE,
8287 UIC_LINK_HIBERN8_STATE);
8288 hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
8289 UFS_SLEEP_PWR_MODE,
8290 UIC_LINK_HIBERN8_STATE);
8291
8292 /* Set the default auto-hiberate idle timer value to 150 ms */
8293 if (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) {
8294 hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 150) |
8295 FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
8296 }
8297
8298 /* Hold auto suspend until async scan completes */
8299 pm_runtime_get_sync(dev);
8300 atomic_set(&hba->scsi_block_reqs_cnt, 0);
8301 /*
8302 * We are assuming that device wasn't put in sleep/power-down
8303 * state exclusively during the boot stage before kernel.
8304 * This assumption helps avoid doing link startup twice during
8305 * ufshcd_probe_hba().
8306 */
8307 ufshcd_set_ufs_dev_active(hba);
8308
8309 async_schedule(ufshcd_async_scan, hba);
8310 ufs_sysfs_add_nodes(hba->dev);
> 8311 ufshcd_configfs_init(hba);
8312
8313 return 0;
8314
8315 out_remove_scsi_host:
8316 scsi_remove_host(hba->host);
8317 exit_gating:
8318 ufshcd_exit_clk_gating(hba);
8319 out_disable:
8320 hba->is_irq_enabled = false;
8321 ufshcd_hba_exit(hba);
8322 out_error:
8323 return err;
8324 }
8325 EXPORT_SYMBOL_GPL(ufshcd_init);
8326
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation