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 *