Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f370e6a authored by Sujeev Dias's avatar Sujeev Dias
Browse files

mhi: core: add support for MHI host managing firmware upload



Add support for MHI host to directly upload firmware
to compatible devices using MHI protocol.

CRs-Fixed: 1095436
Change-Id: Iff7043f1f9afc4824edeaeccc46ed427ce7ee291
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent 2583f4c5
Loading
Loading
Loading
Loading
+135 −51
Original line number Diff line number Diff line
@@ -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>;
+48 −14
Original line number Diff line number Diff line
@@ -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
};

+361 −77
Original line number Diff line number Diff line
@@ -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>
@@ -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;
}
	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);
/* 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;

	if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) {
		ret_val = -EIO;
		goto bhi_copy_error;
	/* 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,
		"Mapped image to DMA addr 0x%llx:\n", bhi_ctxt->phy_image_loc);
			"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;
}

	bhi_ctxt->image_size = count;
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;
}
+36 −2
Original line number Diff line number Diff line
@@ -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
+41 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading