diff --git a/Documentation/devicetree/bindings/mhi/msm_mhi.txt b/Documentation/devicetree/bindings/mhi/msm_mhi.txt
index 7620116da346..ba333cfd9fab 100644
--- a/Documentation/devicetree/bindings/mhi/msm_mhi.txt
+++ b/Documentation/devicetree/bindings/mhi/msm_mhi.txt
@@ -5,56 +5,140 @@ Modem Host Interface protocol. The bindings referred to below, enable
the correct configuration of the interface and required sideband
signals.
-Required properties:
- - compatible: should be "qcom,mhi"
- - qcom,pci-dev_id: device id reported by modem
- - qcom,pci-domain: pci root complex device connected to
- - qcom,pci-bus: pci bus device connected to
- - qcom,pci-slot: pci slot device connected to
- - Refer to "Documentation/devicetree/bindings/esoc/esoc_client.txt" for
- below properties:
- - esoc-names
- - esoc-0
- - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
- below optional properties:
- - qcom,msm-bus,name
- - qcom,msm-bus,num-cases
- - qcom,msm-bus,num-paths
- - qcom,msm-bus,vectors-KBps
- - mhi-chan-cfg-#: mhi channel configuration parameters for platform
- defined as below :
- A = chan number
- B = maximum descriptors
- C = event ring associated with channel
- D = flags defined by mhi_macros.h GET_CHAN_PROPS
- - mhi-event-cfg-#: mhi event ring configuration parameters for platform
- defined as below :
- A = maximum event descriptors
- B = MSI associated with event
- C = interrupt moderation (see MHI specification)
- D = Associated channel
- E = flags defined by mhi_macros.h GET_EV_PROPS
- - mhi-event-rings: number of event rings supported by platform
- - qcom,mhi-address-window: range of the MHI device addressing window
+==============
+Node Structure
+==============
+Main node properties:
+
+- compatible
+ Usage: required
+ Value type:
+ Definition: "qcom,mhi"
+
+- qcom,pci-dev_id
+ Usage: required
+ Value type:
+ Definition: Device id reported by modem
+
+- qcom,pci-domain
+ Usage: required
+ Value type:
+ Definition: PCIE root complex device connected to
+
+- qcom,pci-bus
+ Usage: required
+ Value type:
+ Definition: PCIE bus device connected to
+
+- qcom,pci-slot
+ Usage: required
+ Value type:
+ Definition: PCIE slot (dev_id/function) device connected to
+
+- esoc-names
+ Usage: optional
+ Value type:
+ Definition: esoc name for the device
+
+- esoc-0
+ Usage: required if "esoc-names" is defined
+ Value type: phandle
+ Definition: A phandle pointing to the esoc node.
+
+- qcom,msm-bus,name
+ Usage: required if MHI is bus master
+ Value type: string
+ Definition: string representing the client name
+
+- qcom,msm-bus,num-cases
+ Usage: required if MHI is bus master
+ Value type:
+ Definition: Number of use cases MHI support. Must be set to 2.
+
+- qcom,msm-bus,num-paths
+ Usage: required if MHI is bus master
+ Value type:
+ Definition: Total number of master-slave pairs. Must be set to one.
+
+- qcom,msm-bus,vectors-KBps
+ Usage: required if MHI is bus master
+ Value type: Array of
+ Definition: Array of tuples which define the bus bandwidth requirements.
+ Each tuple is of length 4, values are master-id, slave-id,
+ arbitrated bandwidth in KBps, and instantaneous bandwidth in
+ KBps.
+
+- mhi-chan-cfg-#
+ Usage: required
+ Value type: Array of
+ Definition: mhi channel configuration parameters for platform
+ defined as below :
+ A = chan number
+ B = maximum descriptors
+ C = event ring associated with channel
+ D = flags defined by mhi_macros.h GET_CHAN_PROPS
+
+- mhi-event-rings
+ Usage: required
+ Value type:
+ Definition: Number of event rings device support
+
+- mhi-event-cfg-#
+ Usage: required
+ Value type: Array of
+ Definition: mhi event ring configuration parameters for platform
+ defined as below :
+ A = maximum event descriptors
+ B = MSI associated with event
+ C = interrupt moderation (see MHI specification)
+ D = Associated channel
+ E = flags defined by mhi_macros.h GET_EV_PROPS
+
+- qcom,mhi-address-window
+ Usage: required
+ Value type: Array of
+ Definition: start DDR address and ending DDR address device can access.
+
+- qcom,mhi-manage-boot
+ Usage: optional
+ Value type: bool
+ Definition: Determine whether MHI host manages firmware download to device.
+
+- qcom,mhi-fw-image
+ Usage: required if MHI host managing firmware download process
+ Value type: string
+ Definition: firmware image name
+
+- qcom,mhi-max-sbl
+ Usage: required if MHI host managing firmware download process
+ Value type:
+ Definition: Maximum size in bytes SBL image device support.
+
+- qcom,mhi-sg-size
+ Usage: required if MHI host managing firmware download process
+ Value type:
+ Definition: Segment size in bytes for each segment in bytes.
+
+========
Example:
-
- mhi: qcom,mhi {
- compatible = "qcom,mhi";
- qcom,pci-dev_id = <0x0301>;
- qcom,pci-domain = <2>;
- qcom,pci-bus = <4>;
- qcom,pci-slot = <0>;
- qcom,mhi-address-window = <0x0 0x80000000 0x0 0xbfffffff>;
- esoc-names = "mdm";
- esoc-0 = <&mdm1>;
- qcom,msm-bus,name = "mhi";
- qcom,msm-bus,num-cases = <2>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps =
- <100 512 0 0>,
- <100 512 1200000000 1200000000>;
- mhi-event-rings = <1>;
- mhi-chan-cfg-102 = <0x66 0x80 0x5 0x62>;
- mhi-event-cfg-0 = <0x80 0x0 0x0 0x0 0x11>;
- };
+========
+mhi: qcom,mhi {
+ compatible = "qcom,mhi";
+ qcom,pci-dev_id = <0x0301>;
+ qcom,pci-domain = <2>;
+ qcom,pci-bus = <4>;
+ qcom,pci-slot = <0>;
+ qcom,mhi-address-window = <0x0 0x80000000 0x0 0xbfffffff>;
+ esoc-names = "mdm";
+ esoc-0 = <&mdm1>;
+ qcom,msm-bus,name = "mhi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <100 512 0 0>,
+ <100 512 1200000000 1200000000>;
+ mhi-event-rings = <1>;
+ mhi-chan-cfg-102 = <0x66 0x80 0x5 0x62>;
+ mhi-event-cfg-0 = <0x80 0x0 0x0 0x0 0x11>;
+};
diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h
index 9d3862707595..412e776b9a7b 100644
--- a/drivers/platform/msm/mhi/mhi.h
+++ b/drivers/platform/msm/mhi/mhi.h
@@ -57,15 +57,47 @@ struct pcie_core_info {
bool pci_master;
};
+struct firmware_info {
+ const char *fw_image;
+ size_t max_sbl_len;
+ size_t segment_size;
+};
+
+struct bhie_mem_info {
+ void *pre_aligned;
+ void *aligned;
+ size_t alloc_size;
+ size_t size;
+ phys_addr_t phys_addr;
+ dma_addr_t dma_handle;
+};
+
+struct bhie_vec_table {
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info;
+ struct bhi_vec_entry *bhi_vec_entry;
+ unsigned segment_count;
+ u32 sequence; /* sequence to indicate new xfer */
+};
+
struct bhi_ctxt_t {
void __iomem *bhi_base;
+ void *unaligned_image_loc;
+ dma_addr_t dma_handle;
+ size_t alloc_size;
void *image_loc;
dma_addr_t phy_image_loc;
size_t image_size;
- void *unaligned_image_loc;
dev_t bhi_dev;
struct cdev cdev;
struct device *dev;
+ u32 alignment;
+ u32 poll_timeout;
+ /* BHI/E vector table */
+ bool manage_boot; /* fw download done by MHI host */
+ struct work_struct fw_load_work;
+ struct firmware_info firmware_info;
+ struct bhie_vec_table fw_table;
};
enum MHI_CHAN_DIR {
@@ -344,25 +376,27 @@ enum MHI_INIT_ERROR_STAGE {
};
enum STATE_TRANSITION {
- STATE_TRANSITION_RESET = 0x0,
- STATE_TRANSITION_READY = 0x1,
- STATE_TRANSITION_M0 = 0x2,
- STATE_TRANSITION_M1 = 0x3,
- STATE_TRANSITION_M2 = 0x4,
- STATE_TRANSITION_M3 = 0x5,
- STATE_TRANSITION_BHI = 0x6,
- STATE_TRANSITION_SBL = 0x7,
- STATE_TRANSITION_AMSS = 0x8,
- STATE_TRANSITION_LINK_DOWN = 0x9,
- STATE_TRANSITION_WAKE = 0xA,
- STATE_TRANSITION_SYS_ERR = 0xFF,
- STATE_TRANSITION_reserved = 0x80000000
+ STATE_TRANSITION_RESET = MHI_STATE_RESET,
+ STATE_TRANSITION_READY = MHI_STATE_READY,
+ STATE_TRANSITION_M0 = MHI_STATE_M0,
+ STATE_TRANSITION_M1 = MHI_STATE_M1,
+ STATE_TRANSITION_M2 = MHI_STATE_M2,
+ STATE_TRANSITION_M3 = MHI_STATE_M3,
+ STATE_TRANSITION_BHI,
+ STATE_TRANSITION_SBL,
+ STATE_TRANSITION_AMSS,
+ STATE_TRANSITION_LINK_DOWN,
+ STATE_TRANSITION_WAKE,
+ STATE_TRANSITION_BHIE,
+ STATE_TRANSITION_SYS_ERR,
+ STATE_TRANSITION_MAX
};
enum MHI_EXEC_ENV {
MHI_EXEC_ENV_PBL = 0x0,
MHI_EXEC_ENV_SBL = 0x1,
MHI_EXEC_ENV_AMSS = 0x2,
+ MHI_EXEC_ENV_BHIE = 0x3,
MHI_EXEC_ENV_reserved = 0x80000000
};
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 447d52bc7a46..87a393f9571f 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include
#include
#include
#include
@@ -32,79 +33,198 @@ static int bhi_open(struct inode *mhi_inode, struct file *file_handle)
return 0;
}
-static ssize_t bhi_write(struct file *file,
- const char __user *buf,
- size_t count, loff_t *offp)
+static int bhi_alloc_bhie_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size,
+ struct bhie_vec_table *vec_table)
{
- int ret_val = 0;
- u32 pcie_word_val = 0;
- u32 i = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data;
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
-
- size_t amount_copied = 0;
- uintptr_t align_len = 0x1000;
- u32 tx_db_val = 0;
- rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
- const long bhi_timeout_ms = 1000;
- long timeout;
-
- if (buf == NULL || 0 == count)
- return -EIO;
-
- if (count > BHI_MAX_IMAGE_SIZE)
- return -ENOMEM;
-
- timeout = wait_event_interruptible_timeout(
- *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
- msecs_to_jiffies(bhi_timeout_ms));
- if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI)
- return -EIO;
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+ const u32 align = bhi_ctxt->alignment - 1;
+ size_t seg_size = bhi_ctxt->firmware_info.segment_size;
+ /* We need one additional entry for Vector Table */
+ int segments = DIV_ROUND_UP(size, seg_size) + 1;
+ int i;
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info, *info;
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Entered. User Image size 0x%zx\n", count);
+ "Total size:%lu total_seg:%d seg_size:%lu\n",
+ size, segments, seg_size);
- bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1),
- GFP_KERNEL);
+ sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL);
+ if (!sg_list)
+ return -ENOMEM;
+
+ bhie_mem_info = kcalloc(segments, sizeof(*bhie_mem_info), GFP_KERNEL);
+ if (!bhie_mem_info)
+ goto alloc_bhi_mem_info_error;
+
+ /* Allocate buffers for bhi/e vector table */
+ for (i = 0; i < segments; i++) {
+ size_t size = seg_size;
+
+ /* Last entry if for vector table */
+ if (i == segments - 1)
+ size = sizeof(struct bhi_vec_entry) * i;
+ info = &bhie_mem_info[i];
+ info->size = size;
+ info->alloc_size = info->size + align;
+ info->pre_aligned =
+ dma_alloc_coherent(dev, info->alloc_size,
+ &info->dma_handle, GFP_KERNEL);
+ if (!info->pre_aligned)
+ goto alloc_dma_error;
+
+ info->phys_addr = (info->dma_handle + align) & ~align;
+ info->aligned = info->pre_aligned +
+ (info->phys_addr - info->dma_handle);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Seg:%d unaligned Img: 0x%llx aligned:0x%llx\n",
+ i, info->dma_handle, info->phys_addr);
+ }
+
+ sg_init_table(sg_list, segments);
+ sg_set_buf(sg_list, info->aligned, info->size);
+ sg_dma_address(sg_list) = info->phys_addr;
+ sg_dma_len(sg_list) = info->size;
+ vec_table->sg_list = sg_list;
+ vec_table->bhie_mem_info = bhie_mem_info;
+ vec_table->bhi_vec_entry = info->aligned;
+ vec_table->segment_count = segments;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "BHI/E table successfully allocated\n");
+ return 0;
+
+alloc_dma_error:
+ for (i = i - 1; i >= 0; i--)
+ dma_free_coherent(dev,
+ bhie_mem_info[i].alloc_size,
+ bhie_mem_info[i].pre_aligned,
+ bhie_mem_info[i].dma_handle);
+ kfree(bhie_mem_info);
+alloc_bhi_mem_info_error:
+ kfree(sg_list);
+ return -ENOMEM;
+}
+
+static int bhi_alloc_pbl_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ const u32 align_len = bhi_ctxt->alignment;
+ size_t alloc_size = size + (align_len - 1);
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+
+ bhi_ctxt->unaligned_image_loc =
+ dma_alloc_coherent(dev, alloc_size, &bhi_ctxt->dma_handle,
+ GFP_KERNEL);
if (bhi_ctxt->unaligned_image_loc == NULL)
return -ENOMEM;
- bhi_ctxt->image_loc =
- (void *)((uintptr_t)bhi_ctxt->unaligned_image_loc +
- (align_len - (((uintptr_t)bhi_ctxt->unaligned_image_loc) %
- align_len)));
+ bhi_ctxt->alloc_size = alloc_size;
+ bhi_ctxt->phy_image_loc = (bhi_ctxt->dma_handle + (align_len - 1)) &
+ ~(align_len - 1);
+ bhi_ctxt->image_loc = bhi_ctxt->unaligned_image_loc +
+ (bhi_ctxt->phy_image_loc - bhi_ctxt->dma_handle);
+ bhi_ctxt->image_size = size;
- bhi_ctxt->image_size = count;
-
- if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) {
- ret_val = -ENOMEM;
- goto bhi_copy_error;
- }
- amount_copied = count;
- /* Flush the writes, in anticipation for a device read */
- wmb();
-
- bhi_ctxt->phy_image_loc = dma_map_single(
- &mhi_dev_ctxt->plat_dev->dev,
- bhi_ctxt->image_loc,
- bhi_ctxt->image_size,
- DMA_TO_DEVICE);
-
- if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) {
- ret_val = -EIO;
- goto bhi_copy_error;
- }
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
- "Mapped image to DMA addr 0x%llx:\n", bhi_ctxt->phy_image_loc);
+ "alloc_size:%lu image_size:%lu unal_addr:0x%llx0x al_addr:0x%llx\n",
+ bhi_ctxt->alloc_size, bhi_ctxt->image_size,
+ bhi_ctxt->dma_handle, bhi_ctxt->phy_image_loc);
- bhi_ctxt->image_size = count;
+ return 0;
+}
+
+/* Load firmware via bhie protocol */
+static int bhi_load_bhie_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ const struct bhie_mem_info *bhie_mem_info =
+ &fw_table->bhie_mem_info[fw_table->segment_count - 1];
+ u32 val;
+ const u32 tx_sequence = fw_table->sequence++;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+
+ /* Program TX/RX Vector table */
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
+ }
+
+ val = HIGH_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECADDR_HIGH_OFFS,
+ val);
+ val = LOW_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECADDR_LOW_OFFS,
+ val);
+ val = (u32)bhie_mem_info->size;
+ mhi_reg_write(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECSIZE_OFFS,
+ val);
+
+ /* Ring DB to begin Xfer */
+ mhi_reg_write_field(mhi_dev_ctxt,
+ bhi_ctxt->bhi_base,
+ BHIE_TXVECDB_OFFS,
+ BHIE_TXVECDB_SEQNUM_BMSK,
+ BHIE_TXVECDB_SEQNUM_SHFT,
+ tx_sequence);
+ read_unlock_bh(pm_xfer_lock);
+
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
+ u32 current_seq, status;
+
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
+ }
+ val = mhi_reg_read(bhi_ctxt->bhi_base, BHIE_TXVECSTATUS_OFFS);
+ read_unlock_bh(pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "TXVEC_STATUS:0x%x\n", val);
+ current_seq = (val & BHIE_TXVECSTATUS_SEQNUM_BMSK) >>
+ BHIE_TXVECSTATUS_SEQNUM_SHFT;
+ status = (val & BHIE_TXVECSTATUS_STATUS_BMSK) >>
+ BHIE_TXVECSTATUS_STATUS_SHFT;
+ if ((status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) &&
+ (current_seq == tx_sequence)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Image transfer complete\n");
+ return 0;
+ }
+ msleep(BHI_POLL_SLEEP_TIME_MS);
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error xfering image via BHIE\n");
+ return -EIO;
+}
+
+static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ u32 pcie_word_val = 0;
+ u32 tx_db_val = 0;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
/* Write the image size */
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
pcie_word_val = HIGH_WORD(bhi_ctxt->phy_image_loc);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
@@ -128,16 +248,15 @@ static ssize_t bhi_write(struct file *file,
pcie_word_val = mhi_reg_read(bhi_ctxt->bhi_base, BHI_IMGTXDB);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val);
-
- mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_INTVEC, 0);
read_unlock_bh(pm_xfer_lock);
- for (i = 0; i < BHI_POLL_NR_RETRIES; ++i) {
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
u32 err = 0, errdbg1 = 0, errdbg2 = 0, errdbg3 = 0;
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
err = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRCODE);
errdbg1 = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRDBG1);
@@ -148,34 +267,83 @@ static ssize_t bhi_write(struct file *file,
BHI_STATUS_MASK,
BHI_STATUS_SHIFT);
read_unlock_bh(pm_xfer_lock);
- mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
- "BHI STATUS 0x%x, err:0x%x errdbg1:0x%x errdbg2:0x%x errdbg3:0x%x\n",
- tx_db_val, err, errdbg1, errdbg2, errdbg3);
- if (BHI_STATUS_SUCCESS != tx_db_val)
- mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
- "Incorrect BHI status: %d retry: %d\n",
- tx_db_val, i);
- else
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "%s 0x%x %s:0x%x %s:0x%x %s:0x%x %s:0x%x\n",
+ "BHI STATUS", tx_db_val,
+ "err", err,
+ "errdbg1", errdbg1,
+ "errdbg2", errdbg2,
+ "errdbg3", errdbg3);
+ if (tx_db_val == BHI_STATUS_SUCCESS)
break;
- usleep_range(20000, 25000);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "retrying...\n");
+ msleep(BHI_POLL_SLEEP_TIME_MS);
}
- dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev,
- bhi_ctxt->phy_image_loc,
- bhi_ctxt->image_size, DMA_TO_DEVICE);
- kfree(bhi_ctxt->unaligned_image_loc);
+ return (tx_db_val == BHI_STATUS_SUCCESS) ? 0 : -EIO;
+}
+static ssize_t bhi_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ int ret_val = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ long timeout;
+
+ if (buf == NULL || 0 == count)
+ return -EIO;
+
+ if (count > BHI_MAX_IMAGE_SIZE)
+ return -ENOMEM;
+
+ ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, count);
+ if (ret_val)
+ return -ENOMEM;
+
+ if (copy_from_user(bhi_ctxt->image_loc, buf, count)) {
+ ret_val = -ENOMEM;
+ goto bhi_copy_error;
+ }
+
+ timeout = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ ret_val = -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Timed out waiting for BHI\n");
+ goto bhi_copy_error;
+ }
+
+ ret_val = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to load bhi image\n");
+ }
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ /* Regardless of failure set to RESET state */
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_RESET);
if (ret_val) {
- mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to start state change event\n");
}
- return amount_copied;
+ return count;
bhi_copy_error:
- kfree(bhi_ctxt->unaligned_image_loc);
- return amount_copied;
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ return ret_val;
}
static const struct file_operations bhi_fops = {
@@ -183,16 +351,14 @@ static const struct file_operations bhi_fops = {
.open = bhi_open,
};
-int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
+int bhi_expose_dev_bhi(struct mhi_device_ctxt *mhi_dev_ctxt)
{
+ int ret_val;
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
const struct pcie_core_info *core = &mhi_dev_ctxt->core;
- int ret_val = 0;
- int r;
char node_name[32];
- if (bhi_ctxt->bhi_base == NULL)
- return -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Creating dev node\n");
ret_val = alloc_chrdev_region(&bhi_ctxt->bhi_dev, 0, 1, "bhi");
if (IS_ERR_VALUE(ret_val)) {
@@ -214,12 +380,130 @@ int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
if (IS_ERR(bhi_ctxt->dev)) {
mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to add bhi cdev\n");
- r = PTR_RET(bhi_ctxt->dev);
+ ret_val = PTR_RET(bhi_ctxt->dev);
goto err_dev_create;
}
return 0;
+
err_dev_create:
cdev_del(&bhi_ctxt->cdev);
unregister_chrdev_region(MAJOR(bhi_ctxt->bhi_dev), 1);
- return r;
+ return ret_val;
+}
+
+void bhi_firmware_download(struct work_struct *work)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+ struct bhi_ctxt_t *bhi_ctxt;
+ int ret;
+ long timeout;
+
+ mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt,
+ bhi_ctxt.fw_load_work);
+ bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
+
+ wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
+
+ ret = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load sbl firmware\n");
+ return;
+ }
+ mhi_init_state_transition(mhi_dev_ctxt,
+ STATE_TRANSITION_RESET);
+
+ timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (!timeout) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Enter EXEC_ENV_BHIE\n");
+ return;
+ }
+
+ ret = bhi_load_bhie_firmware(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load amss firmware\n");
+ }
+}
+
+int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ const struct firmware *firmware;
+ struct scatterlist *itr;
+ int ret, i;
+ size_t remainder;
+ const u8 *image;
+
+ /* expose dev node to userspace */
+ if (bhi_ctxt->manage_boot == false)
+ return bhi_expose_dev_bhi(mhi_dev_ctxt);
+
+ /* Make sure minimum buffer we allocate for BHI/E is >= sbl image */
+ while (fw_info->segment_size < fw_info->max_sbl_len)
+ fw_info->segment_size <<= 1;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "max sbl image size:%lu segment size:%lu\n",
+ fw_info->max_sbl_len, fw_info->segment_size);
+
+ /* Read the fw image */
+ ret = request_firmware(&firmware, fw_info->fw_image,
+ &mhi_dev_ctxt->plat_dev->dev);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error request firmware for:%s ret:%d\n",
+ fw_info->fw_image, ret);
+ return ret;
+ }
+
+ ret = bhi_alloc_bhie_xfer(mhi_dev_ctxt,
+ firmware->size,
+ fw_table);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error Allocating memory for firmware image\n");
+ release_firmware(firmware);
+ return ret;
+ }
+
+ /* Copy the fw image to vector table */
+ remainder = firmware->size;
+ image = firmware->data;
+ for (i = 0, itr = &fw_table->sg_list[1];
+ i < fw_table->segment_count - 1; i++, itr++) {
+ size_t to_copy = min(remainder, fw_info->segment_size);
+
+ memcpy(fw_table->bhie_mem_info[i].aligned, image, to_copy);
+ fw_table->bhi_vec_entry[i].phys_addr =
+ fw_table->bhie_mem_info[i].phys_addr;
+ fw_table->bhi_vec_entry[i].size = to_copy;
+ sg_set_buf(itr, fw_table->bhie_mem_info[i].aligned, to_copy);
+ sg_dma_address(itr) = fw_table->bhie_mem_info[i].phys_addr;
+ sg_dma_len(itr) = to_copy;
+ remainder -= to_copy;
+ image += to_copy;
+ }
+
+ /*
+ * Re-use BHI/E pointer for BHI since we guranteed BHI/E segment
+ * is >= to SBL image.
+ */
+ bhi_ctxt->phy_image_loc = sg_dma_address(&fw_table->sg_list[1]);
+ bhi_ctxt->image_size = fw_info->max_sbl_len;
+
+ fw_table->sequence++;
+ release_firmware(firmware);
+
+ /* Schedule a worker thread and wait for BHI Event */
+ schedule_work(&bhi_ctxt->fw_load_work);
+ return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h
index 943226408ce3..15137ba5dfdf 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.h
+++ b/drivers/platform/msm/mhi/mhi_bhi.h
@@ -42,6 +42,38 @@
#define BHI_STATUS_SUCCESS (2)
#define BHI_STATUS_RESET (0)
+/* BHIE Offsets */
+#define BHIE_OFFSET (0x0124) /* BHIE register space offset from BHI base */
+#define BHIE_MSMSOCID_OFFS (BHIE_OFFSET + 0x0000)
+#define BHIE_TXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x002C)
+#define BHIE_TXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0030)
+#define BHIE_TXVECSIZE_OFFS (BHIE_OFFSET + 0x0034)
+#define BHIE_TXVECDB_OFFS (BHIE_OFFSET + 0x003C)
+#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECDB_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_OFFS (BHIE_OFFSET + 0x0044)
+#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_TXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_TXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03)
+#define BHIE_RXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x0060)
+#define BHIE_RXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0064)
+#define BHIE_RXVECSIZE_OFFS (BHIE_OFFSET + 0x0068)
+#define BHIE_RXVECDB_OFFS (BHIE_OFFSET + 0x0070)
+#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECDB_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_OFFS (BHIE_OFFSET + 0x0078)
+#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_RXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_RXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)
+
#define BHI_MAJOR_VERSION 0x0
#define BHI_MINOR_VERSION 0x1
@@ -51,10 +83,12 @@
#define BHI_READBUF_SIZE sizeof(bhi_info_type)
#define BHI_MAX_IMAGE_SIZE (256 * 1024)
+#define BHI_DEFAULT_ALIGNMENT (0x1000)
-#define BHI_POLL_SLEEP_TIME 1000
-#define BHI_POLL_NR_RETRIES 10
+#define BHI_POLL_SLEEP_TIME_MS 100
+#define BHI_POLL_TIMEOUT_MS 2000
int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt);
+void bhi_firmware_download(struct work_struct *work);
#endif
diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c
index 606388e78dbd..6bb26430be7b 100644
--- a/drivers/platform/msm/mhi/mhi_iface.c
+++ b/drivers/platform/msm/mhi/mhi_iface.c
@@ -415,6 +415,47 @@ static int mhi_plat_probe(struct platform_device *pdev)
mhi_dev_ctxt->dev_space.start_win_addr,
mhi_dev_ctxt->dev_space.end_win_addr);
+ r = of_property_read_u32(of_node, "qcom,bhi-alignment",
+ &mhi_dev_ctxt->bhi_ctxt.alignment);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.alignment = BHI_DEFAULT_ALIGNMENT;
+
+ r = of_property_read_u32(of_node, "qcom,bhi-poll-timeout",
+ &mhi_dev_ctxt->bhi_ctxt.poll_timeout);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.poll_timeout = BHI_POLL_TIMEOUT_MS;
+
+ mhi_dev_ctxt->bhi_ctxt.manage_boot =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mhi-manage-boot");
+ if (mhi_dev_ctxt->bhi_ctxt.manage_boot) {
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+
+ r = of_property_read_string(of_node, "qcom,mhi-fw-image",
+ &fw_info->fw_image);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-fw-image'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-max-sbl",
+ (u32 *)&fw_info->max_sbl_len);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-max-sbl'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-sg-size",
+ (u32 *)&fw_info->segment_size);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-sg-size'\n");
+ return r;
+ }
+ INIT_WORK(&bhi_ctxt->fw_load_work, bhi_firmware_download);
+ }
+
mhi_dev_ctxt->plat_dev = pdev;
platform_set_drvdata(pdev, mhi_dev_ctxt);
diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c
index 7c83c3f2b5e0..5bd0dcf300d7 100644
--- a/drivers/platform/msm/mhi/mhi_isr.c
+++ b/drivers/platform/msm/mhi/mhi_isr.c
@@ -165,6 +165,10 @@ static int mhi_process_event_ring(
mhi_init_state_transition(mhi_dev_ctxt,
new_state);
break;
+ case MHI_EXEC_ENV_BHIE:
+ new_state = STATE_TRANSITION_BHIE;
+ mhi_init_state_transition(mhi_dev_ctxt,
+ new_state);
}
break;
}
diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c
index 5c1386f6d30c..f73ed37b8591 100644
--- a/drivers/platform/msm/mhi/mhi_states.c
+++ b/drivers/platform/msm/mhi/mhi_states.c
@@ -19,24 +19,24 @@
const char *state_transition_str(enum STATE_TRANSITION state)
{
- static const char * const mhi_states_transition_str[] = {
- "RESET",
- "READY",
- "M0",
- "M1",
- "M2",
- "M3",
- "BHI",
- "SBL",
- "AMSS",
- "LINK_DOWN",
- "WAKE"
+ static const char * const
+ mhi_states_transition_str[STATE_TRANSITION_MAX] = {
+ [STATE_TRANSITION_RESET] = "RESET",
+ [STATE_TRANSITION_READY] = "READY",
+ [STATE_TRANSITION_M0] = "M0",
+ [STATE_TRANSITION_M1] = "M1",
+ [STATE_TRANSITION_M2] = "M2",
+ [STATE_TRANSITION_M3] = "M3",
+ [STATE_TRANSITION_BHI] = "BHI",
+ [STATE_TRANSITION_SBL] = "SBL",
+ [STATE_TRANSITION_AMSS] = "AMSS",
+ [STATE_TRANSITION_LINK_DOWN] = "LINK_DOWN",
+ [STATE_TRANSITION_WAKE] = "WAKE",
+ [STATE_TRANSITION_BHIE] = "BHIE",
+ [STATE_TRANSITION_SYS_ERR] = "SYS_ERR",
};
- if (state == STATE_TRANSITION_SYS_ERR)
- return "SYS_ERR";
-
- return (state <= STATE_TRANSITION_WAKE) ?
+ return (state < STATE_TRANSITION_MAX) ?
mhi_states_transition_str[state] : "Invalid";
}
@@ -158,6 +158,17 @@ static void ring_all_ev_dbs(struct mhi_device_ctxt *mhi_dev_ctxt)
}
}
+static int process_bhie_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
+ enum STATE_TRANSITION cur_work_item)
+{
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_BHIE;
+ wake_up(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
+ return 0;
+}
+
static int process_m0_transition(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
@@ -579,6 +590,9 @@ static int process_stt_work_item(
case STATE_TRANSITION_WAKE:
r = process_wake_transition(mhi_dev_ctxt, cur_work_item);
break;
+ case STATE_TRANSITION_BHIE:
+ r = process_bhie_transition(mhi_dev_ctxt, cur_work_item);
+ break;
default:
mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Unrecongized state: %s\n",
diff --git a/include/linux/msm_mhi.h b/include/linux/msm_mhi.h
index b0350f930a40..d5325e038e07 100644
--- a/include/linux/msm_mhi.h
+++ b/include/linux/msm_mhi.h
@@ -117,6 +117,11 @@ struct mhi_client_handle {
struct mhi_client_config *client_config;
};
+struct __packed bhi_vec_entry {
+ u64 phys_addr;
+ u64 size;
+};
+
/**
* mhi_is_device_ready - Check if MHI is ready to register clients
*