Loading Documentation/devicetree/bindings/mhi/msm_mhi.txt +135 −51 Original line number Diff line number Diff line Loading @@ -5,40 +5,124 @@ 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: ============== Node Structure ============== Main node properties: - compatible Usage: required Value type: <string> Definition: "qcom,mhi" - qcom,pci-dev_id Usage: required Value type: <u32> Definition: Device id reported by modem - qcom,pci-domain Usage: required Value type: <u32> Definition: PCIE root complex device connected to - qcom,pci-bus Usage: required Value type: <u32> Definition: PCIE bus device connected to - qcom,pci-slot Usage: required Value type: <u32> Definition: PCIE slot (dev_id/function) device connected to - esoc-names Usage: optional Value type: <string> Definition: esoc name for the device - esoc-0 - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for below optional properties: 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: <u32> 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: <u32> Definition: Total number of master-slave pairs. Must be set to one. - qcom,msm-bus,vectors-KBps - mhi-chan-cfg-#: mhi channel configuration parameters for platform Usage: required if MHI is bus master Value type: Array of <u32> 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 <u32> Definition: mhi channel configuration parameters for platform defined as below <A B C D>: 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 - mhi-event-rings Usage: required Value type: <u32> Definition: Number of event rings device support - mhi-event-cfg-# Usage: required Value type: Array of <u32> Definition: mhi event ring configuration parameters for platform defined as below <A B C D E>: 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 Example: - qcom,mhi-address-window Usage: required Value type: Array of <u64> 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: <u32> Definition: Maximum size in bytes SBL image device support. - qcom,mhi-sg-size Usage: required if MHI host managing firmware download process Value type: <u32> Definition: Segment size in bytes for each segment in bytes. ======== Example: ======== mhi: qcom,mhi { compatible = "qcom,mhi"; qcom,pci-dev_id = <0x0301>; Loading drivers/platform/msm/mhi/mhi.h +48 −14 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 }; Loading drivers/platform/msm/mhi/mhi_bhi.c +361 −77 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ #include <linux/firmware.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> Loading @@ -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; 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; 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; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Total size:%lu total_seg:%d seg_size:%lu\n", size, segments, seg_size); if (count > BHI_MAX_IMAGE_SIZE) sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL); if (!sg_list) 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; 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, "Entered. User Image size 0x%zx\n", count); "BHI/E table successfully allocated\n"); return 0; bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1), 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; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "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); if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) { ret_val = -ENOMEM; goto bhi_copy_error; 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; } 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); 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); if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) { ret_val = -EIO; goto bhi_copy_error; /* 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, "Mapped image to DMA addr 0x%llx:\n", bhi_ctxt->phy_image_loc); "Image transfer complete\n"); return 0; } msleep(BHI_POLL_SLEEP_TIME_MS); } bhi_ctxt->image_size = count; 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, Loading @@ -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); Loading @@ -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 (0 != 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 = { Loading @@ -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)) { Loading @@ -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; } drivers/platform/msm/mhi/mhi_bhi.h +36 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 drivers/platform/msm/mhi/mhi_iface.c +41 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
Documentation/devicetree/bindings/mhi/msm_mhi.txt +135 −51 Original line number Diff line number Diff line Loading @@ -5,40 +5,124 @@ 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: ============== Node Structure ============== Main node properties: - compatible Usage: required Value type: <string> Definition: "qcom,mhi" - qcom,pci-dev_id Usage: required Value type: <u32> Definition: Device id reported by modem - qcom,pci-domain Usage: required Value type: <u32> Definition: PCIE root complex device connected to - qcom,pci-bus Usage: required Value type: <u32> Definition: PCIE bus device connected to - qcom,pci-slot Usage: required Value type: <u32> Definition: PCIE slot (dev_id/function) device connected to - esoc-names Usage: optional Value type: <string> Definition: esoc name for the device - esoc-0 - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for below optional properties: 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: <u32> 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: <u32> Definition: Total number of master-slave pairs. Must be set to one. - qcom,msm-bus,vectors-KBps - mhi-chan-cfg-#: mhi channel configuration parameters for platform Usage: required if MHI is bus master Value type: Array of <u32> 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 <u32> Definition: mhi channel configuration parameters for platform defined as below <A B C D>: 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 - mhi-event-rings Usage: required Value type: <u32> Definition: Number of event rings device support - mhi-event-cfg-# Usage: required Value type: Array of <u32> Definition: mhi event ring configuration parameters for platform defined as below <A B C D E>: 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 Example: - qcom,mhi-address-window Usage: required Value type: Array of <u64> 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: <u32> Definition: Maximum size in bytes SBL image device support. - qcom,mhi-sg-size Usage: required if MHI host managing firmware download process Value type: <u32> Definition: Segment size in bytes for each segment in bytes. ======== Example: ======== mhi: qcom,mhi { compatible = "qcom,mhi"; qcom,pci-dev_id = <0x0301>; Loading
drivers/platform/msm/mhi/mhi.h +48 −14 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 }; Loading
drivers/platform/msm/mhi/mhi_bhi.c +361 −77 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ #include <linux/firmware.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> Loading @@ -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; 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; 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; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Total size:%lu total_seg:%d seg_size:%lu\n", size, segments, seg_size); if (count > BHI_MAX_IMAGE_SIZE) sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL); if (!sg_list) 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; 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, "Entered. User Image size 0x%zx\n", count); "BHI/E table successfully allocated\n"); return 0; bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1), 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; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "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); if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) { ret_val = -ENOMEM; goto bhi_copy_error; 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; } 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); 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); if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) { ret_val = -EIO; goto bhi_copy_error; /* 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, "Mapped image to DMA addr 0x%llx:\n", bhi_ctxt->phy_image_loc); "Image transfer complete\n"); return 0; } msleep(BHI_POLL_SLEEP_TIME_MS); } bhi_ctxt->image_size = count; 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, Loading @@ -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); Loading @@ -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 (0 != 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 = { Loading @@ -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)) { Loading @@ -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; }
drivers/platform/msm/mhi/mhi_bhi.h +36 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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
drivers/platform/msm/mhi/mhi_iface.c +41 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes