In this version of UFSHCD Query requests and power management
 functionality are not implemented.
 
+4. BSG Support
+------------------
+
+This transport driver supports exchanging UFS protocol information units
+(UPIUs) with a UFS device. Typically, user space will allocate
+struct ufs_bsg_request and struct ufs_bsg_reply (see ufs_bsg.h) as
+request_upiu and reply_upiu respectively.  Filling those UPIUs should
+be done in accordance with JEDEC spec UFS2.1 paragraph 10.7.
+*Caveat emptor*: The driver makes no further input validations and sends the
+UPIU to the device as it is.  Open the bsg device in /dev/ufs-bsg and
+send SG_IO with the applicable sg_io_v4:
+
+       io_hdr_v4.guard = 'Q';
+       io_hdr_v4.protocol = BSG_PROTOCOL_SCSI;
+       io_hdr_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
+       io_hdr_v4.response = (__u64)reply_upiu;
+       io_hdr_v4.max_response_len = reply_len;
+       io_hdr_v4.request_len = request_len;
+       io_hdr_v4.request = (__u64)request_upiu;
+
 UFS Specifications can be found at,
 UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf
 UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf
 
 
          Select this if you have UFS controller on Hisilicon chipset.
          If unsure, say N.
+
+config SCSI_UFS_BSG
+       bool "Universal Flash Storage BSG device node"
+       depends on SCSI_UFSHCD
+       select BLK_DEV_BSGLIB
+       help
+         Universal Flash Storage (UFS) is SCSI transport specification for
+         accessing flash storage on digital cameras, mobile phones and
+         consumer electronic devices.
+         A UFS controller communicates with a UFS device by exchanging
+         UFS Protocol Information Units (UPIUs).
+         UPIUs can not only be used as a transport layer for the SCSI protocol
+         but are also used by the UFS native command set.
+         This transport driver supports exchanging UFS protocol information units
+         with a UFS device. See also the ufshcd driver, which is a SCSI driver
+         that supports UFS devices.
+
+         Select this if you need a bsg device node for your UFS controller.
+         If unsure, say N.
 
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
 obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
-ufshcd-core-objs := ufshcd.o ufs-sysfs.o
+ufshcd-core-y                          += ufshcd.o ufs-sysfs.o
+ufshcd-core-$(CONFIG_SCSI_UFS_BSG)     += ufs_bsg.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
 obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * bsg endpoint that supports UPIUs
+ *
+ * Copyright (C) 2018 Western Digital Corporation
+ */
+#include "ufs_bsg.h"
+
+
+static int ufs_bsg_request(struct bsg_job *job)
+{
+       struct ufs_bsg_request *bsg_request = job->request;
+       struct ufs_bsg_reply *bsg_reply = job->reply;
+       int ret = -ENOTSUPP;
+
+       bsg_reply->reply_payload_rcv_len = 0;
+
+       /* Do Nothing for now */
+       dev_err(job->dev, "unsupported message_code 0x%x\n",
+               bsg_request->msgcode);
+
+       bsg_reply->result = ret;
+       job->reply_len = sizeof(struct ufs_bsg_reply) +
+                        bsg_reply->reply_payload_rcv_len;
+
+       bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
+
+       return ret;
+}
+
+/**
+ * ufs_bsg_remove - detach and remove the added ufs-bsg node
+ *
+ * Should be called when unloading the driver.
+ */
+void ufs_bsg_remove(struct ufs_hba *hba)
+{
+       struct device *bsg_dev = &hba->bsg_dev;
+
+       if (!hba->bsg_queue)
+               return;
+
+       bsg_unregister_queue(hba->bsg_queue);
+
+       device_del(bsg_dev);
+       put_device(bsg_dev);
+}
+
+static inline void ufs_bsg_node_release(struct device *dev)
+{
+       put_device(dev->parent);
+}
+
+/**
+ * ufs_bsg_probe - Add ufs bsg device node
+ * @hba: per adapter object
+ *
+ * Called during initial loading of the driver, and before scsi_scan_host.
+ */
+int ufs_bsg_probe(struct ufs_hba *hba)
+{
+       struct device *bsg_dev = &hba->bsg_dev;
+       struct Scsi_Host *shost = hba->host;
+       struct device *parent = &shost->shost_gendev;
+       struct request_queue *q;
+       int ret;
+
+       device_initialize(bsg_dev);
+
+       bsg_dev->parent = get_device(parent);
+       bsg_dev->release = ufs_bsg_node_release;
+
+       dev_set_name(bsg_dev, "ufs-bsg");
+
+       ret = device_add(bsg_dev);
+       if (ret)
+               goto out;
+
+       q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, 0);
+       if (IS_ERR(q)) {
+               ret = PTR_ERR(q);
+               goto out;
+       }
+
+       hba->bsg_queue = q;
+
+       return 0;
+
+out:
+       dev_err(bsg_dev, "fail to initialize a bsg dev %d\n", shost->host_no);
+       put_device(bsg_dev);
+       return ret;
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Western Digital Corporation
+ */
+#ifndef UFS_BSG_H
+#define UFS_BSG_H
+
+#include <linux/bsg-lib.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include "ufshcd.h"
+#include "ufs.h"
+
+#ifdef CONFIG_SCSI_UFS_BSG
+void ufs_bsg_remove(struct ufs_hba *hba);
+int ufs_bsg_probe(struct ufs_hba *hba);
+#else
+static inline void ufs_bsg_remove(struct ufs_hba *hba) {}
+static inline int ufs_bsg_probe(struct ufs_hba *hba) {return 0; }
+#endif
+
+#endif /* UFS_BSG_H */
 
 #include "ufs_quirks.h"
 #include "unipro.h"
 #include "ufs-sysfs.h"
+#include "ufs_bsg.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ufs.h>
                        hba->clk_scaling.is_allowed = true;
                }
 
+               ufs_bsg_probe(hba);
+
                scsi_scan_host(hba->host);
                pm_runtime_put_sync(hba->dev);
        }
  */
 void ufshcd_remove(struct ufs_hba *hba)
 {
+       ufs_bsg_remove(hba);
        ufs_sysfs_remove_nodes(hba->dev);
        scsi_remove_host(hba->host);
        /* disable interrupts */
 
        struct rw_semaphore clk_scaling_lock;
        struct ufs_desc_size desc_size;
        atomic_t scsi_block_reqs_cnt;
+
+       struct device           bsg_dev;
+       struct request_queue    *bsg_queue;
 };
 
 /* Returns true if clocks can be gated. Otherwise false */
 
  * UFS Transport SGIO v4 BSG Message Support
  *
  * Copyright (C) 2011-2013 Samsung India Software Operations
+ * Copyright (C) 2018 Western Digital Corporation
  */
 #ifndef SCSI_BSG_UFS_H
 #define SCSI_BSG_UFS_H
        union {
                struct utp_upiu_cmd             sc;
                struct utp_upiu_query           qr;
+               struct utp_upiu_query           tr;
+               /* use utp_upiu_query to host the 4 dwords of uic command */
+               struct utp_upiu_query           uc;
        };
 };
+
+/* request (CDB) structure of the sg_io_v4 */
+struct ufs_bsg_request {
+       uint32_t msgcode;
+       struct utp_upiu_req upiu_req;
+};
+
+/* response (request sense data) structure of the sg_io_v4 */
+struct ufs_bsg_reply {
+       /*
+        * The completion result. Result exists in two forms:
+        * if negative, it is an -Exxx system errno value. There will
+        * be no further reply information supplied.
+        * else, it's the 4-byte scsi error result, with driver, host,
+        * msg and status fields. The per-msgcode reply structure
+        * will contain valid data.
+        */
+       uint32_t result;
+
+       /* If there was reply_payload, how much was received? */
+       uint32_t reply_payload_rcv_len;
+
+       struct utp_upiu_req upiu_rsp;
+};
 #endif /* UFS_BSG_H */