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