xref: /openbmc/linux/drivers/bus/mhi/host/boot.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a0f5a630SManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0
2a0f5a630SManivannan Sadhasivam /*
3a0f5a630SManivannan Sadhasivam  * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
4a0f5a630SManivannan Sadhasivam  *
5a0f5a630SManivannan Sadhasivam  */
6a0f5a630SManivannan Sadhasivam 
7a0f5a630SManivannan Sadhasivam #include <linux/delay.h>
8a0f5a630SManivannan Sadhasivam #include <linux/device.h>
9a0f5a630SManivannan Sadhasivam #include <linux/dma-direction.h>
10a0f5a630SManivannan Sadhasivam #include <linux/dma-mapping.h>
11a0f5a630SManivannan Sadhasivam #include <linux/firmware.h>
12a0f5a630SManivannan Sadhasivam #include <linux/interrupt.h>
13a0f5a630SManivannan Sadhasivam #include <linux/list.h>
14a0f5a630SManivannan Sadhasivam #include <linux/mhi.h>
15a0f5a630SManivannan Sadhasivam #include <linux/module.h>
16a0f5a630SManivannan Sadhasivam #include <linux/random.h>
17a0f5a630SManivannan Sadhasivam #include <linux/slab.h>
18a0f5a630SManivannan Sadhasivam #include <linux/wait.h>
19a0f5a630SManivannan Sadhasivam #include "internal.h"
20a0f5a630SManivannan Sadhasivam 
21a0f5a630SManivannan Sadhasivam /* Setup RDDM vector table for RDDM transfer and program RXVEC */
mhi_rddm_prepare(struct mhi_controller * mhi_cntrl,struct image_info * img_info)220bca889fSBhaumik Bhatt int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
23a0f5a630SManivannan Sadhasivam 		     struct image_info *img_info)
24a0f5a630SManivannan Sadhasivam {
25a0f5a630SManivannan Sadhasivam 	struct mhi_buf *mhi_buf = img_info->mhi_buf;
26a0f5a630SManivannan Sadhasivam 	struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
27a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->bhie;
28a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
29a0f5a630SManivannan Sadhasivam 	u32 sequence_id;
30a0f5a630SManivannan Sadhasivam 	unsigned int i;
310bca889fSBhaumik Bhatt 	int ret;
32a0f5a630SManivannan Sadhasivam 
33a0f5a630SManivannan Sadhasivam 	for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
34a0f5a630SManivannan Sadhasivam 		bhi_vec->dma_addr = mhi_buf->dma_addr;
35a0f5a630SManivannan Sadhasivam 		bhi_vec->size = mhi_buf->len;
36a0f5a630SManivannan Sadhasivam 	}
37a0f5a630SManivannan Sadhasivam 
38a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "BHIe programming for RDDM\n");
39a0f5a630SManivannan Sadhasivam 
40a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
41a0f5a630SManivannan Sadhasivam 		      upper_32_bits(mhi_buf->dma_addr));
42a0f5a630SManivannan Sadhasivam 
43a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
44a0f5a630SManivannan Sadhasivam 		      lower_32_bits(mhi_buf->dma_addr));
45a0f5a630SManivannan Sadhasivam 
46a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
47a0f5a630SManivannan Sadhasivam 	sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);
48a0f5a630SManivannan Sadhasivam 
490bca889fSBhaumik Bhatt 	ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
50d28cab4dSManivannan Sadhasivam 				  BHIE_RXVECDB_SEQNUM_BMSK, sequence_id);
510bca889fSBhaumik Bhatt 	if (ret) {
520bca889fSBhaumik Bhatt 		dev_err(dev, "Failed to write sequence ID for BHIE_RXVECDB\n");
530bca889fSBhaumik Bhatt 		return ret;
540bca889fSBhaumik Bhatt 	}
55a0f5a630SManivannan Sadhasivam 
56a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n",
57a0f5a630SManivannan Sadhasivam 		&mhi_buf->dma_addr, mhi_buf->len, sequence_id);
580bca889fSBhaumik Bhatt 
590bca889fSBhaumik Bhatt 	return 0;
60a0f5a630SManivannan Sadhasivam }
61a0f5a630SManivannan Sadhasivam 
62a0f5a630SManivannan Sadhasivam /* Collect RDDM buffer during kernel panic */
__mhi_download_rddm_in_panic(struct mhi_controller * mhi_cntrl)63a0f5a630SManivannan Sadhasivam static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
64a0f5a630SManivannan Sadhasivam {
65a0f5a630SManivannan Sadhasivam 	int ret;
66a0f5a630SManivannan Sadhasivam 	u32 rx_status;
67a0f5a630SManivannan Sadhasivam 	enum mhi_ee_type ee;
68a0f5a630SManivannan Sadhasivam 	const u32 delayus = 2000;
69a0f5a630SManivannan Sadhasivam 	u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
70a0f5a630SManivannan Sadhasivam 	const u32 rddm_timeout_us = 200000;
71a0f5a630SManivannan Sadhasivam 	int rddm_retry = rddm_timeout_us / delayus;
72a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->bhie;
73a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
74a0f5a630SManivannan Sadhasivam 
75a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n",
76a0f5a630SManivannan Sadhasivam 		to_mhi_pm_state_str(mhi_cntrl->pm_state),
773a1b8e28SManivannan Sadhasivam 		mhi_state_str(mhi_cntrl->dev_state),
78a0f5a630SManivannan Sadhasivam 		TO_MHI_EXEC_STR(mhi_cntrl->ee));
79a0f5a630SManivannan Sadhasivam 
80a0f5a630SManivannan Sadhasivam 	/*
81a0f5a630SManivannan Sadhasivam 	 * This should only be executing during a kernel panic, we expect all
82a0f5a630SManivannan Sadhasivam 	 * other cores to shutdown while we're collecting RDDM buffer. After
83a0f5a630SManivannan Sadhasivam 	 * returning from this function, we expect the device to reset.
84a0f5a630SManivannan Sadhasivam 	 *
85a0f5a630SManivannan Sadhasivam 	 * Normaly, we read/write pm_state only after grabbing the
86a0f5a630SManivannan Sadhasivam 	 * pm_lock, since we're in a panic, skipping it. Also there is no
87a0f5a630SManivannan Sadhasivam 	 * gurantee that this state change would take effect since
88a0f5a630SManivannan Sadhasivam 	 * we're setting it w/o grabbing pm_lock
89a0f5a630SManivannan Sadhasivam 	 */
90a0f5a630SManivannan Sadhasivam 	mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
91a0f5a630SManivannan Sadhasivam 	/* update should take the effect immediately */
92a0f5a630SManivannan Sadhasivam 	smp_wmb();
93a0f5a630SManivannan Sadhasivam 
94a0f5a630SManivannan Sadhasivam 	/*
95a0f5a630SManivannan Sadhasivam 	 * Make sure device is not already in RDDM. In case the device asserts
96a0f5a630SManivannan Sadhasivam 	 * and a kernel panic follows, device will already be in RDDM.
97a0f5a630SManivannan Sadhasivam 	 * Do not trigger SYS ERR again and proceed with waiting for
98a0f5a630SManivannan Sadhasivam 	 * image download completion.
99a0f5a630SManivannan Sadhasivam 	 */
100a0f5a630SManivannan Sadhasivam 	ee = mhi_get_exec_env(mhi_cntrl);
101a0f5a630SManivannan Sadhasivam 	if (ee == MHI_EE_MAX)
102a0f5a630SManivannan Sadhasivam 		goto error_exit_rddm;
103a0f5a630SManivannan Sadhasivam 
104a0f5a630SManivannan Sadhasivam 	if (ee != MHI_EE_RDDM) {
105a0f5a630SManivannan Sadhasivam 		dev_dbg(dev, "Trigger device into RDDM mode using SYS ERR\n");
106a0f5a630SManivannan Sadhasivam 		mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
107a0f5a630SManivannan Sadhasivam 
108a0f5a630SManivannan Sadhasivam 		dev_dbg(dev, "Waiting for device to enter RDDM\n");
109a0f5a630SManivannan Sadhasivam 		while (rddm_retry--) {
110a0f5a630SManivannan Sadhasivam 			ee = mhi_get_exec_env(mhi_cntrl);
111a0f5a630SManivannan Sadhasivam 			if (ee == MHI_EE_RDDM)
112a0f5a630SManivannan Sadhasivam 				break;
113a0f5a630SManivannan Sadhasivam 
114a0f5a630SManivannan Sadhasivam 			udelay(delayus);
115a0f5a630SManivannan Sadhasivam 		}
116a0f5a630SManivannan Sadhasivam 
117a0f5a630SManivannan Sadhasivam 		if (rddm_retry <= 0) {
118a0f5a630SManivannan Sadhasivam 			/* Hardware reset so force device to enter RDDM */
119a0f5a630SManivannan Sadhasivam 			dev_dbg(dev,
120a0f5a630SManivannan Sadhasivam 				"Did not enter RDDM, do a host req reset\n");
12146db0ba1SQiang Yu 			mhi_soc_reset(mhi_cntrl);
122a0f5a630SManivannan Sadhasivam 			udelay(delayus);
123a0f5a630SManivannan Sadhasivam 		}
124a0f5a630SManivannan Sadhasivam 
125a0f5a630SManivannan Sadhasivam 		ee = mhi_get_exec_env(mhi_cntrl);
126a0f5a630SManivannan Sadhasivam 	}
127a0f5a630SManivannan Sadhasivam 
128a0f5a630SManivannan Sadhasivam 	dev_dbg(dev,
129a0f5a630SManivannan Sadhasivam 		"Waiting for RDDM image download via BHIe, current EE:%s\n",
130a0f5a630SManivannan Sadhasivam 		TO_MHI_EXEC_STR(ee));
131a0f5a630SManivannan Sadhasivam 
132a0f5a630SManivannan Sadhasivam 	while (retry--) {
133a0f5a630SManivannan Sadhasivam 		ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
134d28cab4dSManivannan Sadhasivam 					 BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status);
135a0f5a630SManivannan Sadhasivam 		if (ret)
136a0f5a630SManivannan Sadhasivam 			return -EIO;
137a0f5a630SManivannan Sadhasivam 
138a0f5a630SManivannan Sadhasivam 		if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL)
139a0f5a630SManivannan Sadhasivam 			return 0;
140a0f5a630SManivannan Sadhasivam 
141a0f5a630SManivannan Sadhasivam 		udelay(delayus);
142a0f5a630SManivannan Sadhasivam 	}
143a0f5a630SManivannan Sadhasivam 
144a0f5a630SManivannan Sadhasivam 	ee = mhi_get_exec_env(mhi_cntrl);
145a0f5a630SManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status);
146a0f5a630SManivannan Sadhasivam 
147a0f5a630SManivannan Sadhasivam 	dev_err(dev, "RXVEC_STATUS: 0x%x\n", rx_status);
148a0f5a630SManivannan Sadhasivam 
149a0f5a630SManivannan Sadhasivam error_exit_rddm:
150a0f5a630SManivannan Sadhasivam 	dev_err(dev, "RDDM transfer failed. Current EE: %s\n",
151a0f5a630SManivannan Sadhasivam 		TO_MHI_EXEC_STR(ee));
152a0f5a630SManivannan Sadhasivam 
153a0f5a630SManivannan Sadhasivam 	return -EIO;
154a0f5a630SManivannan Sadhasivam }
155a0f5a630SManivannan Sadhasivam 
156a0f5a630SManivannan Sadhasivam /* Download RDDM image from device */
mhi_download_rddm_image(struct mhi_controller * mhi_cntrl,bool in_panic)157a0f5a630SManivannan Sadhasivam int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
158a0f5a630SManivannan Sadhasivam {
159a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->bhie;
160a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
161a0f5a630SManivannan Sadhasivam 	u32 rx_status;
162a0f5a630SManivannan Sadhasivam 
163a0f5a630SManivannan Sadhasivam 	if (in_panic)
164a0f5a630SManivannan Sadhasivam 		return __mhi_download_rddm_in_panic(mhi_cntrl);
165a0f5a630SManivannan Sadhasivam 
166a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Waiting for RDDM image download via BHIe\n");
167a0f5a630SManivannan Sadhasivam 
168a0f5a630SManivannan Sadhasivam 	/* Wait for the image download to complete */
169a0f5a630SManivannan Sadhasivam 	wait_event_timeout(mhi_cntrl->state_event,
170a0f5a630SManivannan Sadhasivam 			   mhi_read_reg_field(mhi_cntrl, base,
171a0f5a630SManivannan Sadhasivam 					      BHIE_RXVECSTATUS_OFFS,
172a0f5a630SManivannan Sadhasivam 					      BHIE_RXVECSTATUS_STATUS_BMSK,
173a0f5a630SManivannan Sadhasivam 					      &rx_status) || rx_status,
174a0f5a630SManivannan Sadhasivam 			   msecs_to_jiffies(mhi_cntrl->timeout_ms));
175a0f5a630SManivannan Sadhasivam 
176a0f5a630SManivannan Sadhasivam 	return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
177a0f5a630SManivannan Sadhasivam }
178a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_download_rddm_image);
179a0f5a630SManivannan Sadhasivam 
mhi_fw_load_bhie(struct mhi_controller * mhi_cntrl,const struct mhi_buf * mhi_buf)180a0f5a630SManivannan Sadhasivam static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
181a0f5a630SManivannan Sadhasivam 			    const struct mhi_buf *mhi_buf)
182a0f5a630SManivannan Sadhasivam {
183a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->bhie;
184a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
185a0f5a630SManivannan Sadhasivam 	rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
186a0f5a630SManivannan Sadhasivam 	u32 tx_status, sequence_id;
187a0f5a630SManivannan Sadhasivam 	int ret;
188a0f5a630SManivannan Sadhasivam 
189a0f5a630SManivannan Sadhasivam 	read_lock_bh(pm_lock);
190a0f5a630SManivannan Sadhasivam 	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
191a0f5a630SManivannan Sadhasivam 		read_unlock_bh(pm_lock);
192a0f5a630SManivannan Sadhasivam 		return -EIO;
193a0f5a630SManivannan Sadhasivam 	}
194a0f5a630SManivannan Sadhasivam 
195a0f5a630SManivannan Sadhasivam 	sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK);
196a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Starting image download via BHIe. Sequence ID: %u\n",
197a0f5a630SManivannan Sadhasivam 		sequence_id);
198a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
199a0f5a630SManivannan Sadhasivam 		      upper_32_bits(mhi_buf->dma_addr));
200a0f5a630SManivannan Sadhasivam 
201a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
202a0f5a630SManivannan Sadhasivam 		      lower_32_bits(mhi_buf->dma_addr));
203a0f5a630SManivannan Sadhasivam 
204a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
205a0f5a630SManivannan Sadhasivam 
2060bca889fSBhaumik Bhatt 	ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
207d28cab4dSManivannan Sadhasivam 				  BHIE_TXVECDB_SEQNUM_BMSK, sequence_id);
208a0f5a630SManivannan Sadhasivam 	read_unlock_bh(pm_lock);
209a0f5a630SManivannan Sadhasivam 
2100bca889fSBhaumik Bhatt 	if (ret)
2110bca889fSBhaumik Bhatt 		return ret;
2120bca889fSBhaumik Bhatt 
213a0f5a630SManivannan Sadhasivam 	/* Wait for the image download to complete */
214a0f5a630SManivannan Sadhasivam 	ret = wait_event_timeout(mhi_cntrl->state_event,
215a0f5a630SManivannan Sadhasivam 				 MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
216a0f5a630SManivannan Sadhasivam 				 mhi_read_reg_field(mhi_cntrl, base,
217a0f5a630SManivannan Sadhasivam 						   BHIE_TXVECSTATUS_OFFS,
218a0f5a630SManivannan Sadhasivam 						   BHIE_TXVECSTATUS_STATUS_BMSK,
219a0f5a630SManivannan Sadhasivam 						   &tx_status) || tx_status,
220a0f5a630SManivannan Sadhasivam 				 msecs_to_jiffies(mhi_cntrl->timeout_ms));
221a0f5a630SManivannan Sadhasivam 	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
222a0f5a630SManivannan Sadhasivam 	    tx_status != BHIE_TXVECSTATUS_STATUS_XFER_COMPL)
223a0f5a630SManivannan Sadhasivam 		return -EIO;
224a0f5a630SManivannan Sadhasivam 
225a0f5a630SManivannan Sadhasivam 	return (!ret) ? -ETIMEDOUT : 0;
226a0f5a630SManivannan Sadhasivam }
227a0f5a630SManivannan Sadhasivam 
mhi_fw_load_bhi(struct mhi_controller * mhi_cntrl,dma_addr_t dma_addr,size_t size)228a0f5a630SManivannan Sadhasivam static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
229a0f5a630SManivannan Sadhasivam 			   dma_addr_t dma_addr,
230a0f5a630SManivannan Sadhasivam 			   size_t size)
231a0f5a630SManivannan Sadhasivam {
232a0f5a630SManivannan Sadhasivam 	u32 tx_status, val, session_id;
233a0f5a630SManivannan Sadhasivam 	int i, ret;
234a0f5a630SManivannan Sadhasivam 	void __iomem *base = mhi_cntrl->bhi;
235a0f5a630SManivannan Sadhasivam 	rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
236a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
237a0f5a630SManivannan Sadhasivam 	struct {
238a0f5a630SManivannan Sadhasivam 		char *name;
239a0f5a630SManivannan Sadhasivam 		u32 offset;
240a0f5a630SManivannan Sadhasivam 	} error_reg[] = {
241a0f5a630SManivannan Sadhasivam 		{ "ERROR_CODE", BHI_ERRCODE },
242a0f5a630SManivannan Sadhasivam 		{ "ERROR_DBG1", BHI_ERRDBG1 },
243a0f5a630SManivannan Sadhasivam 		{ "ERROR_DBG2", BHI_ERRDBG2 },
244a0f5a630SManivannan Sadhasivam 		{ "ERROR_DBG3", BHI_ERRDBG3 },
245a0f5a630SManivannan Sadhasivam 		{ NULL },
246a0f5a630SManivannan Sadhasivam 	};
247a0f5a630SManivannan Sadhasivam 
248a0f5a630SManivannan Sadhasivam 	read_lock_bh(pm_lock);
249a0f5a630SManivannan Sadhasivam 	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
250a0f5a630SManivannan Sadhasivam 		read_unlock_bh(pm_lock);
251a0f5a630SManivannan Sadhasivam 		goto invalid_pm_state;
252a0f5a630SManivannan Sadhasivam 	}
253a0f5a630SManivannan Sadhasivam 
254a0f5a630SManivannan Sadhasivam 	session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK);
255a0f5a630SManivannan Sadhasivam 	dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n",
256a0f5a630SManivannan Sadhasivam 		session_id);
257a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
258a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
259a0f5a630SManivannan Sadhasivam 		      upper_32_bits(dma_addr));
260a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
261a0f5a630SManivannan Sadhasivam 		      lower_32_bits(dma_addr));
262a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
263a0f5a630SManivannan Sadhasivam 	mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
264a0f5a630SManivannan Sadhasivam 	read_unlock_bh(pm_lock);
265a0f5a630SManivannan Sadhasivam 
266a0f5a630SManivannan Sadhasivam 	/* Wait for the image download to complete */
267a0f5a630SManivannan Sadhasivam 	ret = wait_event_timeout(mhi_cntrl->state_event,
268a0f5a630SManivannan Sadhasivam 			   MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
269a0f5a630SManivannan Sadhasivam 			   mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
270d28cab4dSManivannan Sadhasivam 					      BHI_STATUS_MASK, &tx_status) || tx_status,
271a0f5a630SManivannan Sadhasivam 			   msecs_to_jiffies(mhi_cntrl->timeout_ms));
272a0f5a630SManivannan Sadhasivam 	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
273a0f5a630SManivannan Sadhasivam 		goto invalid_pm_state;
274a0f5a630SManivannan Sadhasivam 
275a0f5a630SManivannan Sadhasivam 	if (tx_status == BHI_STATUS_ERROR) {
276a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Image transfer failed\n");
277a0f5a630SManivannan Sadhasivam 		read_lock_bh(pm_lock);
278a0f5a630SManivannan Sadhasivam 		if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
279a0f5a630SManivannan Sadhasivam 			for (i = 0; error_reg[i].name; i++) {
280a0f5a630SManivannan Sadhasivam 				ret = mhi_read_reg(mhi_cntrl, base,
281a0f5a630SManivannan Sadhasivam 						   error_reg[i].offset, &val);
282a0f5a630SManivannan Sadhasivam 				if (ret)
283a0f5a630SManivannan Sadhasivam 					break;
284a0f5a630SManivannan Sadhasivam 				dev_err(dev, "Reg: %s value: 0x%x\n",
285a0f5a630SManivannan Sadhasivam 					error_reg[i].name, val);
286a0f5a630SManivannan Sadhasivam 			}
287a0f5a630SManivannan Sadhasivam 		}
288a0f5a630SManivannan Sadhasivam 		read_unlock_bh(pm_lock);
289a0f5a630SManivannan Sadhasivam 		goto invalid_pm_state;
290a0f5a630SManivannan Sadhasivam 	}
291a0f5a630SManivannan Sadhasivam 
292a0f5a630SManivannan Sadhasivam 	return (!ret) ? -ETIMEDOUT : 0;
293a0f5a630SManivannan Sadhasivam 
294a0f5a630SManivannan Sadhasivam invalid_pm_state:
295a0f5a630SManivannan Sadhasivam 
296a0f5a630SManivannan Sadhasivam 	return -EIO;
297a0f5a630SManivannan Sadhasivam }
298a0f5a630SManivannan Sadhasivam 
mhi_free_bhie_table(struct mhi_controller * mhi_cntrl,struct image_info * image_info)299a0f5a630SManivannan Sadhasivam void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
300a0f5a630SManivannan Sadhasivam 			 struct image_info *image_info)
301a0f5a630SManivannan Sadhasivam {
302a0f5a630SManivannan Sadhasivam 	int i;
303a0f5a630SManivannan Sadhasivam 	struct mhi_buf *mhi_buf = image_info->mhi_buf;
304a0f5a630SManivannan Sadhasivam 
305a0f5a630SManivannan Sadhasivam 	for (i = 0; i < image_info->entries; i++, mhi_buf++)
306a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
307a0f5a630SManivannan Sadhasivam 				  mhi_buf->buf, mhi_buf->dma_addr);
308a0f5a630SManivannan Sadhasivam 
309a0f5a630SManivannan Sadhasivam 	kfree(image_info->mhi_buf);
310a0f5a630SManivannan Sadhasivam 	kfree(image_info);
311a0f5a630SManivannan Sadhasivam }
312a0f5a630SManivannan Sadhasivam 
mhi_alloc_bhie_table(struct mhi_controller * mhi_cntrl,struct image_info ** image_info,size_t alloc_size)313a0f5a630SManivannan Sadhasivam int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
314a0f5a630SManivannan Sadhasivam 			 struct image_info **image_info,
315a0f5a630SManivannan Sadhasivam 			 size_t alloc_size)
316a0f5a630SManivannan Sadhasivam {
317a0f5a630SManivannan Sadhasivam 	size_t seg_size = mhi_cntrl->seg_len;
318a0f5a630SManivannan Sadhasivam 	int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
319a0f5a630SManivannan Sadhasivam 	int i;
320a0f5a630SManivannan Sadhasivam 	struct image_info *img_info;
321a0f5a630SManivannan Sadhasivam 	struct mhi_buf *mhi_buf;
322a0f5a630SManivannan Sadhasivam 
323a0f5a630SManivannan Sadhasivam 	img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
324a0f5a630SManivannan Sadhasivam 	if (!img_info)
325a0f5a630SManivannan Sadhasivam 		return -ENOMEM;
326a0f5a630SManivannan Sadhasivam 
327a0f5a630SManivannan Sadhasivam 	/* Allocate memory for entries */
328a0f5a630SManivannan Sadhasivam 	img_info->mhi_buf = kcalloc(segments, sizeof(*img_info->mhi_buf),
329a0f5a630SManivannan Sadhasivam 				    GFP_KERNEL);
330a0f5a630SManivannan Sadhasivam 	if (!img_info->mhi_buf)
331a0f5a630SManivannan Sadhasivam 		goto error_alloc_mhi_buf;
332a0f5a630SManivannan Sadhasivam 
333a0f5a630SManivannan Sadhasivam 	/* Allocate and populate vector table */
334a0f5a630SManivannan Sadhasivam 	mhi_buf = img_info->mhi_buf;
335a0f5a630SManivannan Sadhasivam 	for (i = 0; i < segments; i++, mhi_buf++) {
336a0f5a630SManivannan Sadhasivam 		size_t vec_size = seg_size;
337a0f5a630SManivannan Sadhasivam 
338a0f5a630SManivannan Sadhasivam 		/* Vector table is the last entry */
339a0f5a630SManivannan Sadhasivam 		if (i == segments - 1)
340a0f5a630SManivannan Sadhasivam 			vec_size = sizeof(struct bhi_vec_entry) * i;
341a0f5a630SManivannan Sadhasivam 
342a0f5a630SManivannan Sadhasivam 		mhi_buf->len = vec_size;
343a0f5a630SManivannan Sadhasivam 		mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
344a0f5a630SManivannan Sadhasivam 						  vec_size, &mhi_buf->dma_addr,
345a0f5a630SManivannan Sadhasivam 						  GFP_KERNEL);
346a0f5a630SManivannan Sadhasivam 		if (!mhi_buf->buf)
347a0f5a630SManivannan Sadhasivam 			goto error_alloc_segment;
348a0f5a630SManivannan Sadhasivam 	}
349a0f5a630SManivannan Sadhasivam 
350a0f5a630SManivannan Sadhasivam 	img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
351a0f5a630SManivannan Sadhasivam 	img_info->entries = segments;
352a0f5a630SManivannan Sadhasivam 	*image_info = img_info;
353a0f5a630SManivannan Sadhasivam 
354a0f5a630SManivannan Sadhasivam 	return 0;
355a0f5a630SManivannan Sadhasivam 
356a0f5a630SManivannan Sadhasivam error_alloc_segment:
357a0f5a630SManivannan Sadhasivam 	for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
358a0f5a630SManivannan Sadhasivam 		dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
359a0f5a630SManivannan Sadhasivam 				  mhi_buf->buf, mhi_buf->dma_addr);
360a0f5a630SManivannan Sadhasivam 
361a0f5a630SManivannan Sadhasivam error_alloc_mhi_buf:
362a0f5a630SManivannan Sadhasivam 	kfree(img_info);
363a0f5a630SManivannan Sadhasivam 
364a0f5a630SManivannan Sadhasivam 	return -ENOMEM;
365a0f5a630SManivannan Sadhasivam }
366a0f5a630SManivannan Sadhasivam 
mhi_firmware_copy(struct mhi_controller * mhi_cntrl,const u8 * buf,size_t remainder,struct image_info * img_info)367a0f5a630SManivannan Sadhasivam static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
368*efe47a18SKalle Valo 			      const u8 *buf, size_t remainder,
369a0f5a630SManivannan Sadhasivam 			      struct image_info *img_info)
370a0f5a630SManivannan Sadhasivam {
371a0f5a630SManivannan Sadhasivam 	size_t to_cpy;
372a0f5a630SManivannan Sadhasivam 	struct mhi_buf *mhi_buf = img_info->mhi_buf;
373a0f5a630SManivannan Sadhasivam 	struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
374a0f5a630SManivannan Sadhasivam 
375a0f5a630SManivannan Sadhasivam 	while (remainder) {
376a0f5a630SManivannan Sadhasivam 		to_cpy = min(remainder, mhi_buf->len);
377a0f5a630SManivannan Sadhasivam 		memcpy(mhi_buf->buf, buf, to_cpy);
378a0f5a630SManivannan Sadhasivam 		bhi_vec->dma_addr = mhi_buf->dma_addr;
379a0f5a630SManivannan Sadhasivam 		bhi_vec->size = to_cpy;
380a0f5a630SManivannan Sadhasivam 
381a0f5a630SManivannan Sadhasivam 		buf += to_cpy;
382a0f5a630SManivannan Sadhasivam 		remainder -= to_cpy;
383a0f5a630SManivannan Sadhasivam 		bhi_vec++;
384a0f5a630SManivannan Sadhasivam 		mhi_buf++;
385a0f5a630SManivannan Sadhasivam 	}
386a0f5a630SManivannan Sadhasivam }
387a0f5a630SManivannan Sadhasivam 
mhi_fw_load_handler(struct mhi_controller * mhi_cntrl)388a0f5a630SManivannan Sadhasivam void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
389a0f5a630SManivannan Sadhasivam {
390a0f5a630SManivannan Sadhasivam 	const struct firmware *firmware = NULL;
391a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
3921d1493bdSJeffrey Hugo 	enum mhi_pm_state new_state;
393a0f5a630SManivannan Sadhasivam 	const char *fw_name;
394*efe47a18SKalle Valo 	const u8 *fw_data;
395a0f5a630SManivannan Sadhasivam 	void *buf;
396a0f5a630SManivannan Sadhasivam 	dma_addr_t dma_addr;
397*efe47a18SKalle Valo 	size_t size, fw_sz;
398a0f5a630SManivannan Sadhasivam 	int i, ret;
399a0f5a630SManivannan Sadhasivam 
400a0f5a630SManivannan Sadhasivam 	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
401a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Device MHI is not in valid state\n");
402a0f5a630SManivannan Sadhasivam 		return;
403a0f5a630SManivannan Sadhasivam 	}
404a0f5a630SManivannan Sadhasivam 
405a0f5a630SManivannan Sadhasivam 	/* save hardware info from BHI */
406a0f5a630SManivannan Sadhasivam 	ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU,
407a0f5a630SManivannan Sadhasivam 			   &mhi_cntrl->serial_number);
408a0f5a630SManivannan Sadhasivam 	if (ret)
409a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Could not capture serial number via BHI\n");
410a0f5a630SManivannan Sadhasivam 
411a0f5a630SManivannan Sadhasivam 	for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) {
412a0f5a630SManivannan Sadhasivam 		ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i),
413a0f5a630SManivannan Sadhasivam 				   &mhi_cntrl->oem_pk_hash[i]);
414a0f5a630SManivannan Sadhasivam 		if (ret) {
415a0f5a630SManivannan Sadhasivam 			dev_err(dev, "Could not capture OEM PK HASH via BHI\n");
416a0f5a630SManivannan Sadhasivam 			break;
417a0f5a630SManivannan Sadhasivam 		}
418a0f5a630SManivannan Sadhasivam 	}
419a0f5a630SManivannan Sadhasivam 
420a0f5a630SManivannan Sadhasivam 	/* wait for ready on pass through or any other execution environment */
421a0f5a630SManivannan Sadhasivam 	if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee))
422a0f5a630SManivannan Sadhasivam 		goto fw_load_ready_state;
423a0f5a630SManivannan Sadhasivam 
424a0f5a630SManivannan Sadhasivam 	fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
425a0f5a630SManivannan Sadhasivam 		mhi_cntrl->edl_image : mhi_cntrl->fw_image;
426a0f5a630SManivannan Sadhasivam 
427*efe47a18SKalle Valo 	/* check if the driver has already provided the firmware data */
428*efe47a18SKalle Valo 	if (!fw_name && mhi_cntrl->fbc_download &&
429*efe47a18SKalle Valo 	    mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
430*efe47a18SKalle Valo 		if (!mhi_cntrl->sbl_size) {
431*efe47a18SKalle Valo 			dev_err(dev, "fw_data provided but no sbl_size\n");
432*efe47a18SKalle Valo 			goto error_fw_load;
433*efe47a18SKalle Valo 		}
434*efe47a18SKalle Valo 
435*efe47a18SKalle Valo 		size = mhi_cntrl->sbl_size;
436*efe47a18SKalle Valo 		fw_data = mhi_cntrl->fw_data;
437*efe47a18SKalle Valo 		fw_sz = mhi_cntrl->fw_sz;
438*efe47a18SKalle Valo 		goto skip_req_fw;
439*efe47a18SKalle Valo 	}
440*efe47a18SKalle Valo 
441a0f5a630SManivannan Sadhasivam 	if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
442a0f5a630SManivannan Sadhasivam 						     !mhi_cntrl->seg_len))) {
443a0f5a630SManivannan Sadhasivam 		dev_err(dev,
444a0f5a630SManivannan Sadhasivam 			"No firmware image defined or !sbl_size || !seg_len\n");
445a0f5a630SManivannan Sadhasivam 		goto error_fw_load;
446a0f5a630SManivannan Sadhasivam 	}
447a0f5a630SManivannan Sadhasivam 
448a0f5a630SManivannan Sadhasivam 	ret = request_firmware(&firmware, fw_name, dev);
449a0f5a630SManivannan Sadhasivam 	if (ret) {
450a0f5a630SManivannan Sadhasivam 		dev_err(dev, "Error loading firmware: %d\n", ret);
451a0f5a630SManivannan Sadhasivam 		goto error_fw_load;
452a0f5a630SManivannan Sadhasivam 	}
453a0f5a630SManivannan Sadhasivam 
454a0f5a630SManivannan Sadhasivam 	size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
455a0f5a630SManivannan Sadhasivam 
456a0f5a630SManivannan Sadhasivam 	/* SBL size provided is maximum size, not necessarily the image size */
457a0f5a630SManivannan Sadhasivam 	if (size > firmware->size)
458a0f5a630SManivannan Sadhasivam 		size = firmware->size;
459a0f5a630SManivannan Sadhasivam 
460*efe47a18SKalle Valo 	fw_data = firmware->data;
461*efe47a18SKalle Valo 	fw_sz = firmware->size;
462*efe47a18SKalle Valo 
463*efe47a18SKalle Valo skip_req_fw:
464a0f5a630SManivannan Sadhasivam 	buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, size, &dma_addr,
465a0f5a630SManivannan Sadhasivam 				 GFP_KERNEL);
466a0f5a630SManivannan Sadhasivam 	if (!buf) {
467a0f5a630SManivannan Sadhasivam 		release_firmware(firmware);
468a0f5a630SManivannan Sadhasivam 		goto error_fw_load;
469a0f5a630SManivannan Sadhasivam 	}
470a0f5a630SManivannan Sadhasivam 
471a0f5a630SManivannan Sadhasivam 	/* Download image using BHI */
472*efe47a18SKalle Valo 	memcpy(buf, fw_data, size);
473a0f5a630SManivannan Sadhasivam 	ret = mhi_fw_load_bhi(mhi_cntrl, dma_addr, size);
474a0f5a630SManivannan Sadhasivam 	dma_free_coherent(mhi_cntrl->cntrl_dev, size, buf, dma_addr);
475a0f5a630SManivannan Sadhasivam 
476a0f5a630SManivannan Sadhasivam 	/* Error or in EDL mode, we're done */
477a0f5a630SManivannan Sadhasivam 	if (ret) {
478a0f5a630SManivannan Sadhasivam 		dev_err(dev, "MHI did not load image over BHI, ret: %d\n", ret);
479a0f5a630SManivannan Sadhasivam 		release_firmware(firmware);
480a0f5a630SManivannan Sadhasivam 		goto error_fw_load;
481a0f5a630SManivannan Sadhasivam 	}
482a0f5a630SManivannan Sadhasivam 
483a0f5a630SManivannan Sadhasivam 	/* Wait for ready since EDL image was loaded */
484*efe47a18SKalle Valo 	if (fw_name && fw_name == mhi_cntrl->edl_image) {
485a0f5a630SManivannan Sadhasivam 		release_firmware(firmware);
486a0f5a630SManivannan Sadhasivam 		goto fw_load_ready_state;
487a0f5a630SManivannan Sadhasivam 	}
488a0f5a630SManivannan Sadhasivam 
489a0f5a630SManivannan Sadhasivam 	write_lock_irq(&mhi_cntrl->pm_lock);
490a0f5a630SManivannan Sadhasivam 	mhi_cntrl->dev_state = MHI_STATE_RESET;
491a0f5a630SManivannan Sadhasivam 	write_unlock_irq(&mhi_cntrl->pm_lock);
492a0f5a630SManivannan Sadhasivam 
493a0f5a630SManivannan Sadhasivam 	/*
494a0f5a630SManivannan Sadhasivam 	 * If we're doing fbc, populate vector tables while
495a0f5a630SManivannan Sadhasivam 	 * device transitioning into MHI READY state
496a0f5a630SManivannan Sadhasivam 	 */
497a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->fbc_download) {
498*efe47a18SKalle Valo 		ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
499a0f5a630SManivannan Sadhasivam 		if (ret) {
500a0f5a630SManivannan Sadhasivam 			release_firmware(firmware);
501a0f5a630SManivannan Sadhasivam 			goto error_fw_load;
502a0f5a630SManivannan Sadhasivam 		}
503a0f5a630SManivannan Sadhasivam 
504a0f5a630SManivannan Sadhasivam 		/* Load the firmware into BHIE vec table */
505*efe47a18SKalle Valo 		mhi_firmware_copy(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
506a0f5a630SManivannan Sadhasivam 	}
507a0f5a630SManivannan Sadhasivam 
508a0f5a630SManivannan Sadhasivam 	release_firmware(firmware);
509a0f5a630SManivannan Sadhasivam 
510a0f5a630SManivannan Sadhasivam fw_load_ready_state:
511a0f5a630SManivannan Sadhasivam 	/* Transitioning into MHI RESET->READY state */
512a0f5a630SManivannan Sadhasivam 	ret = mhi_ready_state_transition(mhi_cntrl);
513a0f5a630SManivannan Sadhasivam 	if (ret) {
514a0f5a630SManivannan Sadhasivam 		dev_err(dev, "MHI did not enter READY state\n");
515a0f5a630SManivannan Sadhasivam 		goto error_ready_state;
516a0f5a630SManivannan Sadhasivam 	}
517a0f5a630SManivannan Sadhasivam 
518a0f5a630SManivannan Sadhasivam 	dev_info(dev, "Wait for device to enter SBL or Mission mode\n");
519a0f5a630SManivannan Sadhasivam 	return;
520a0f5a630SManivannan Sadhasivam 
521a0f5a630SManivannan Sadhasivam error_ready_state:
522a0f5a630SManivannan Sadhasivam 	if (mhi_cntrl->fbc_download) {
523a0f5a630SManivannan Sadhasivam 		mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
524a0f5a630SManivannan Sadhasivam 		mhi_cntrl->fbc_image = NULL;
525a0f5a630SManivannan Sadhasivam 	}
526a0f5a630SManivannan Sadhasivam 
527a0f5a630SManivannan Sadhasivam error_fw_load:
5281d1493bdSJeffrey Hugo 	write_lock_irq(&mhi_cntrl->pm_lock);
5291d1493bdSJeffrey Hugo 	new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
5301d1493bdSJeffrey Hugo 	write_unlock_irq(&mhi_cntrl->pm_lock);
5311d1493bdSJeffrey Hugo 	if (new_state == MHI_PM_FW_DL_ERR)
532a0f5a630SManivannan Sadhasivam 		wake_up_all(&mhi_cntrl->state_event);
533a0f5a630SManivannan Sadhasivam }
534a0f5a630SManivannan Sadhasivam 
mhi_download_amss_image(struct mhi_controller * mhi_cntrl)535a0f5a630SManivannan Sadhasivam int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
536a0f5a630SManivannan Sadhasivam {
537a0f5a630SManivannan Sadhasivam 	struct image_info *image_info = mhi_cntrl->fbc_image;
538a0f5a630SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
5391d1493bdSJeffrey Hugo 	enum mhi_pm_state new_state;
540a0f5a630SManivannan Sadhasivam 	int ret;
541a0f5a630SManivannan Sadhasivam 
542a0f5a630SManivannan Sadhasivam 	if (!image_info)
543a0f5a630SManivannan Sadhasivam 		return -EIO;
544a0f5a630SManivannan Sadhasivam 
545a0f5a630SManivannan Sadhasivam 	ret = mhi_fw_load_bhie(mhi_cntrl,
546a0f5a630SManivannan Sadhasivam 			       /* Vector table is the last entry */
547a0f5a630SManivannan Sadhasivam 			       &image_info->mhi_buf[image_info->entries - 1]);
548a0f5a630SManivannan Sadhasivam 	if (ret) {
549a0f5a630SManivannan Sadhasivam 		dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
5501d1493bdSJeffrey Hugo 		write_lock_irq(&mhi_cntrl->pm_lock);
5511d1493bdSJeffrey Hugo 		new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
5521d1493bdSJeffrey Hugo 		write_unlock_irq(&mhi_cntrl->pm_lock);
5531d1493bdSJeffrey Hugo 		if (new_state == MHI_PM_FW_DL_ERR)
554a0f5a630SManivannan Sadhasivam 			wake_up_all(&mhi_cntrl->state_event);
555a0f5a630SManivannan Sadhasivam 	}
556a0f5a630SManivannan Sadhasivam 
557a0f5a630SManivannan Sadhasivam 	return ret;
558a0f5a630SManivannan Sadhasivam }
559