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/interrupt.h>
12a0f5a630SManivannan Sadhasivam #include <linux/list.h>
13a0f5a630SManivannan Sadhasivam #include <linux/mhi.h>
14a0f5a630SManivannan Sadhasivam #include <linux/module.h>
15a0f5a630SManivannan Sadhasivam #include <linux/slab.h>
16a0f5a630SManivannan Sadhasivam #include <linux/wait.h>
17a0f5a630SManivannan Sadhasivam #include "internal.h"
18a0f5a630SManivannan Sadhasivam
19a0f5a630SManivannan Sadhasivam /*
20a0f5a630SManivannan Sadhasivam * Not all MHI state transitions are synchronous. Transitions like Linkdown,
21a0f5a630SManivannan Sadhasivam * SYS_ERR, and shutdown can happen anytime asynchronously. This function will
22a0f5a630SManivannan Sadhasivam * transition to a new state only if we're allowed to.
23a0f5a630SManivannan Sadhasivam *
24a0f5a630SManivannan Sadhasivam * Priority increases as we go down. For instance, from any state in L0, the
25a0f5a630SManivannan Sadhasivam * transition can be made to states in L1, L2 and L3. A notable exception to
26a0f5a630SManivannan Sadhasivam * this rule is state DISABLE. From DISABLE state we can only transition to
27a0f5a630SManivannan Sadhasivam * POR state. Also, while in L2 state, user cannot jump back to previous
28a0f5a630SManivannan Sadhasivam * L1 or L0 states.
29a0f5a630SManivannan Sadhasivam *
30a0f5a630SManivannan Sadhasivam * Valid transitions:
31a0f5a630SManivannan Sadhasivam * L0: DISABLE <--> POR
32a0f5a630SManivannan Sadhasivam * POR <--> POR
33a0f5a630SManivannan Sadhasivam * POR -> M0 -> M2 --> M0
34a0f5a630SManivannan Sadhasivam * POR -> FW_DL_ERR
35a0f5a630SManivannan Sadhasivam * FW_DL_ERR <--> FW_DL_ERR
36a0f5a630SManivannan Sadhasivam * M0 <--> M0
37a0f5a630SManivannan Sadhasivam * M0 -> FW_DL_ERR
38a0f5a630SManivannan Sadhasivam * M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0
39*ac191bcbSJeffrey Hugo * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS
40*ac191bcbSJeffrey Hugo * SYS_ERR_PROCESS -> SYS_ERR_FAIL
41*ac191bcbSJeffrey Hugo * SYS_ERR_FAIL -> SYS_ERR_DETECT
42*ac191bcbSJeffrey Hugo * SYS_ERR_PROCESS --> POR
43a0f5a630SManivannan Sadhasivam * L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT
44a0f5a630SManivannan Sadhasivam * SHUTDOWN_PROCESS -> DISABLE
45a0f5a630SManivannan Sadhasivam * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
46a0f5a630SManivannan Sadhasivam * LD_ERR_FATAL_DETECT -> DISABLE
47a0f5a630SManivannan Sadhasivam */
48a0f5a630SManivannan Sadhasivam static const struct mhi_pm_transitions dev_state_transitions[] = {
49a0f5a630SManivannan Sadhasivam /* L0 States */
50a0f5a630SManivannan Sadhasivam {
51a0f5a630SManivannan Sadhasivam MHI_PM_DISABLE,
52a0f5a630SManivannan Sadhasivam MHI_PM_POR
53a0f5a630SManivannan Sadhasivam },
54a0f5a630SManivannan Sadhasivam {
55a0f5a630SManivannan Sadhasivam MHI_PM_POR,
56a0f5a630SManivannan Sadhasivam MHI_PM_POR | MHI_PM_DISABLE | MHI_PM_M0 |
57a0f5a630SManivannan Sadhasivam MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
58a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_FW_DL_ERR
59a0f5a630SManivannan Sadhasivam },
60a0f5a630SManivannan Sadhasivam {
61a0f5a630SManivannan Sadhasivam MHI_PM_M0,
62a0f5a630SManivannan Sadhasivam MHI_PM_M0 | MHI_PM_M2 | MHI_PM_M3_ENTER |
63a0f5a630SManivannan Sadhasivam MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
64a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_FW_DL_ERR
65a0f5a630SManivannan Sadhasivam },
66a0f5a630SManivannan Sadhasivam {
67a0f5a630SManivannan Sadhasivam MHI_PM_M2,
68a0f5a630SManivannan Sadhasivam MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
69a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
70a0f5a630SManivannan Sadhasivam },
71a0f5a630SManivannan Sadhasivam {
72a0f5a630SManivannan Sadhasivam MHI_PM_M3_ENTER,
73a0f5a630SManivannan Sadhasivam MHI_PM_M3 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
74a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
75a0f5a630SManivannan Sadhasivam },
76a0f5a630SManivannan Sadhasivam {
77a0f5a630SManivannan Sadhasivam MHI_PM_M3,
78a0f5a630SManivannan Sadhasivam MHI_PM_M3_EXIT | MHI_PM_SYS_ERR_DETECT |
79a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
80a0f5a630SManivannan Sadhasivam },
81a0f5a630SManivannan Sadhasivam {
82a0f5a630SManivannan Sadhasivam MHI_PM_M3_EXIT,
83a0f5a630SManivannan Sadhasivam MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
84a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
85a0f5a630SManivannan Sadhasivam },
86a0f5a630SManivannan Sadhasivam {
87a0f5a630SManivannan Sadhasivam MHI_PM_FW_DL_ERR,
88a0f5a630SManivannan Sadhasivam MHI_PM_FW_DL_ERR | MHI_PM_SYS_ERR_DETECT |
89a0f5a630SManivannan Sadhasivam MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT
90a0f5a630SManivannan Sadhasivam },
91a0f5a630SManivannan Sadhasivam /* L1 States */
92a0f5a630SManivannan Sadhasivam {
93a0f5a630SManivannan Sadhasivam MHI_PM_SYS_ERR_DETECT,
94a0f5a630SManivannan Sadhasivam MHI_PM_SYS_ERR_PROCESS | MHI_PM_SHUTDOWN_PROCESS |
95a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
96a0f5a630SManivannan Sadhasivam },
97a0f5a630SManivannan Sadhasivam {
98a0f5a630SManivannan Sadhasivam MHI_PM_SYS_ERR_PROCESS,
99*ac191bcbSJeffrey Hugo MHI_PM_POR | MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS |
100*ac191bcbSJeffrey Hugo MHI_PM_LD_ERR_FATAL_DETECT
101*ac191bcbSJeffrey Hugo },
102*ac191bcbSJeffrey Hugo {
103*ac191bcbSJeffrey Hugo MHI_PM_SYS_ERR_FAIL,
104*ac191bcbSJeffrey Hugo MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
105a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT
106a0f5a630SManivannan Sadhasivam },
107a0f5a630SManivannan Sadhasivam /* L2 States */
108a0f5a630SManivannan Sadhasivam {
109a0f5a630SManivannan Sadhasivam MHI_PM_SHUTDOWN_PROCESS,
110a0f5a630SManivannan Sadhasivam MHI_PM_DISABLE | MHI_PM_LD_ERR_FATAL_DETECT
111a0f5a630SManivannan Sadhasivam },
112a0f5a630SManivannan Sadhasivam /* L3 States */
113a0f5a630SManivannan Sadhasivam {
114a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT,
115a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_DISABLE
116a0f5a630SManivannan Sadhasivam },
117a0f5a630SManivannan Sadhasivam };
118a0f5a630SManivannan Sadhasivam
mhi_tryset_pm_state(struct mhi_controller * mhi_cntrl,enum mhi_pm_state state)119a0f5a630SManivannan Sadhasivam enum mhi_pm_state __must_check mhi_tryset_pm_state(struct mhi_controller *mhi_cntrl,
120a0f5a630SManivannan Sadhasivam enum mhi_pm_state state)
121a0f5a630SManivannan Sadhasivam {
122a0f5a630SManivannan Sadhasivam unsigned long cur_state = mhi_cntrl->pm_state;
123a0f5a630SManivannan Sadhasivam int index = find_last_bit(&cur_state, 32);
124a0f5a630SManivannan Sadhasivam
125a0f5a630SManivannan Sadhasivam if (unlikely(index >= ARRAY_SIZE(dev_state_transitions)))
126a0f5a630SManivannan Sadhasivam return cur_state;
127a0f5a630SManivannan Sadhasivam
128a0f5a630SManivannan Sadhasivam if (unlikely(dev_state_transitions[index].from_state != cur_state))
129a0f5a630SManivannan Sadhasivam return cur_state;
130a0f5a630SManivannan Sadhasivam
131a0f5a630SManivannan Sadhasivam if (unlikely(!(dev_state_transitions[index].to_states & state)))
132a0f5a630SManivannan Sadhasivam return cur_state;
133a0f5a630SManivannan Sadhasivam
134a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = state;
135a0f5a630SManivannan Sadhasivam return mhi_cntrl->pm_state;
136a0f5a630SManivannan Sadhasivam }
137a0f5a630SManivannan Sadhasivam
mhi_set_mhi_state(struct mhi_controller * mhi_cntrl,enum mhi_state state)138a0f5a630SManivannan Sadhasivam void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum mhi_state state)
139a0f5a630SManivannan Sadhasivam {
1400bca889fSBhaumik Bhatt struct device *dev = &mhi_cntrl->mhi_dev->dev;
1410bca889fSBhaumik Bhatt int ret;
1420bca889fSBhaumik Bhatt
143a0f5a630SManivannan Sadhasivam if (state == MHI_STATE_RESET) {
1440bca889fSBhaumik Bhatt ret = mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
145d28cab4dSManivannan Sadhasivam MHICTRL_RESET_MASK, 1);
146a0f5a630SManivannan Sadhasivam } else {
1470bca889fSBhaumik Bhatt ret = mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
148d28cab4dSManivannan Sadhasivam MHICTRL_MHISTATE_MASK, state);
149a0f5a630SManivannan Sadhasivam }
1500bca889fSBhaumik Bhatt
1510bca889fSBhaumik Bhatt if (ret)
1520bca889fSBhaumik Bhatt dev_err(dev, "Failed to set MHI state to: %s\n",
1530bca889fSBhaumik Bhatt mhi_state_str(state));
154a0f5a630SManivannan Sadhasivam }
155a0f5a630SManivannan Sadhasivam
156a0f5a630SManivannan Sadhasivam /* NOP for backward compatibility, host allowed to ring DB in M2 state */
mhi_toggle_dev_wake_nop(struct mhi_controller * mhi_cntrl)157a0f5a630SManivannan Sadhasivam static void mhi_toggle_dev_wake_nop(struct mhi_controller *mhi_cntrl)
158a0f5a630SManivannan Sadhasivam {
159a0f5a630SManivannan Sadhasivam }
160a0f5a630SManivannan Sadhasivam
mhi_toggle_dev_wake(struct mhi_controller * mhi_cntrl)161a0f5a630SManivannan Sadhasivam static void mhi_toggle_dev_wake(struct mhi_controller *mhi_cntrl)
162a0f5a630SManivannan Sadhasivam {
163a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, false);
164a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, true);
165a0f5a630SManivannan Sadhasivam }
166a0f5a630SManivannan Sadhasivam
167a0f5a630SManivannan Sadhasivam /* Handle device ready state transition */
mhi_ready_state_transition(struct mhi_controller * mhi_cntrl)168a0f5a630SManivannan Sadhasivam int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl)
169a0f5a630SManivannan Sadhasivam {
170a0f5a630SManivannan Sadhasivam struct mhi_event *mhi_event;
171a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state;
172a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
173a0f5a630SManivannan Sadhasivam u32 interval_us = 25000; /* poll register field every 25 milliseconds */
174a0f5a630SManivannan Sadhasivam int ret, i;
175a0f5a630SManivannan Sadhasivam
176a0f5a630SManivannan Sadhasivam /* Check if device entered error state */
177a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
178a0f5a630SManivannan Sadhasivam dev_err(dev, "Device link is not accessible\n");
179a0f5a630SManivannan Sadhasivam return -EIO;
180a0f5a630SManivannan Sadhasivam }
181a0f5a630SManivannan Sadhasivam
182a0f5a630SManivannan Sadhasivam /* Wait for RESET to be cleared and READY bit to be set by the device */
183a0f5a630SManivannan Sadhasivam ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
184d28cab4dSManivannan Sadhasivam MHICTRL_RESET_MASK, 0, interval_us);
185a0f5a630SManivannan Sadhasivam if (ret) {
186a0f5a630SManivannan Sadhasivam dev_err(dev, "Device failed to clear MHI Reset\n");
187a0f5a630SManivannan Sadhasivam return ret;
188a0f5a630SManivannan Sadhasivam }
189a0f5a630SManivannan Sadhasivam
190a0f5a630SManivannan Sadhasivam ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
191d28cab4dSManivannan Sadhasivam MHISTATUS_READY_MASK, 1, interval_us);
192a0f5a630SManivannan Sadhasivam if (ret) {
193a0f5a630SManivannan Sadhasivam dev_err(dev, "Device failed to enter MHI Ready\n");
194a0f5a630SManivannan Sadhasivam return ret;
195a0f5a630SManivannan Sadhasivam }
196a0f5a630SManivannan Sadhasivam
197a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Device in READY State\n");
198a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
199a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR);
200a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_READY;
201a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
202a0f5a630SManivannan Sadhasivam
203a0f5a630SManivannan Sadhasivam if (cur_state != MHI_PM_POR) {
204a0f5a630SManivannan Sadhasivam dev_err(dev, "Error moving to state %s from %s\n",
205a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_POR),
206a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(cur_state));
207a0f5a630SManivannan Sadhasivam return -EIO;
208a0f5a630SManivannan Sadhasivam }
209a0f5a630SManivannan Sadhasivam
210a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
211a0f5a630SManivannan Sadhasivam if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
212a0f5a630SManivannan Sadhasivam dev_err(dev, "Device registers not accessible\n");
213a0f5a630SManivannan Sadhasivam goto error_mmio;
214a0f5a630SManivannan Sadhasivam }
215a0f5a630SManivannan Sadhasivam
216a0f5a630SManivannan Sadhasivam /* Configure MMIO registers */
217a0f5a630SManivannan Sadhasivam ret = mhi_init_mmio(mhi_cntrl);
218a0f5a630SManivannan Sadhasivam if (ret) {
219a0f5a630SManivannan Sadhasivam dev_err(dev, "Error configuring MMIO registers\n");
220a0f5a630SManivannan Sadhasivam goto error_mmio;
221a0f5a630SManivannan Sadhasivam }
222a0f5a630SManivannan Sadhasivam
223a0f5a630SManivannan Sadhasivam /* Add elements to all SW event rings */
224a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
225a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
226a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_event->ring;
227a0f5a630SManivannan Sadhasivam
228a0f5a630SManivannan Sadhasivam /* Skip if this is an offload or HW event */
229a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev || mhi_event->hw_ring)
230a0f5a630SManivannan Sadhasivam continue;
231a0f5a630SManivannan Sadhasivam
232a0f5a630SManivannan Sadhasivam ring->wp = ring->base + ring->len - ring->el_size;
233a0f5a630SManivannan Sadhasivam *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size);
234a0f5a630SManivannan Sadhasivam /* Update all cores */
235a0f5a630SManivannan Sadhasivam smp_wmb();
236a0f5a630SManivannan Sadhasivam
237a0f5a630SManivannan Sadhasivam /* Ring the event ring db */
238a0f5a630SManivannan Sadhasivam spin_lock_irq(&mhi_event->lock);
239a0f5a630SManivannan Sadhasivam mhi_ring_er_db(mhi_event);
240a0f5a630SManivannan Sadhasivam spin_unlock_irq(&mhi_event->lock);
241a0f5a630SManivannan Sadhasivam }
242a0f5a630SManivannan Sadhasivam
243a0f5a630SManivannan Sadhasivam /* Set MHI to M0 state */
244a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
245a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
246a0f5a630SManivannan Sadhasivam
247a0f5a630SManivannan Sadhasivam return 0;
248a0f5a630SManivannan Sadhasivam
249a0f5a630SManivannan Sadhasivam error_mmio:
250a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
251a0f5a630SManivannan Sadhasivam
252a0f5a630SManivannan Sadhasivam return -EIO;
253a0f5a630SManivannan Sadhasivam }
254a0f5a630SManivannan Sadhasivam
mhi_pm_m0_transition(struct mhi_controller * mhi_cntrl)255a0f5a630SManivannan Sadhasivam int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl)
256a0f5a630SManivannan Sadhasivam {
257a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state;
258a0f5a630SManivannan Sadhasivam struct mhi_chan *mhi_chan;
259a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
260a0f5a630SManivannan Sadhasivam int i;
261a0f5a630SManivannan Sadhasivam
262a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
263a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_M0;
264a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M0);
265a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
266a0f5a630SManivannan Sadhasivam if (unlikely(cur_state != MHI_PM_M0)) {
267a0f5a630SManivannan Sadhasivam dev_err(dev, "Unable to transition to M0 state\n");
268a0f5a630SManivannan Sadhasivam return -EIO;
269a0f5a630SManivannan Sadhasivam }
270a0f5a630SManivannan Sadhasivam mhi_cntrl->M0++;
271a0f5a630SManivannan Sadhasivam
272a0f5a630SManivannan Sadhasivam /* Wake up the device */
273a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
274a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, true);
275a0f5a630SManivannan Sadhasivam
276a0f5a630SManivannan Sadhasivam /* Ring all event rings and CMD ring only if we're in mission mode */
277a0f5a630SManivannan Sadhasivam if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) {
278a0f5a630SManivannan Sadhasivam struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
279a0f5a630SManivannan Sadhasivam struct mhi_cmd *mhi_cmd =
280a0f5a630SManivannan Sadhasivam &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
281a0f5a630SManivannan Sadhasivam
282a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
283a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev)
284a0f5a630SManivannan Sadhasivam continue;
285a0f5a630SManivannan Sadhasivam
286a0f5a630SManivannan Sadhasivam spin_lock_irq(&mhi_event->lock);
287a0f5a630SManivannan Sadhasivam mhi_ring_er_db(mhi_event);
288a0f5a630SManivannan Sadhasivam spin_unlock_irq(&mhi_event->lock);
289a0f5a630SManivannan Sadhasivam }
290a0f5a630SManivannan Sadhasivam
291a0f5a630SManivannan Sadhasivam /* Only ring primary cmd ring if ring is not empty */
292a0f5a630SManivannan Sadhasivam spin_lock_irq(&mhi_cmd->lock);
293a0f5a630SManivannan Sadhasivam if (mhi_cmd->ring.rp != mhi_cmd->ring.wp)
294a0f5a630SManivannan Sadhasivam mhi_ring_cmd_db(mhi_cntrl, mhi_cmd);
295a0f5a630SManivannan Sadhasivam spin_unlock_irq(&mhi_cmd->lock);
296a0f5a630SManivannan Sadhasivam }
297a0f5a630SManivannan Sadhasivam
298a0f5a630SManivannan Sadhasivam /* Ring channel DB registers */
299a0f5a630SManivannan Sadhasivam mhi_chan = mhi_cntrl->mhi_chan;
300a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) {
301a0f5a630SManivannan Sadhasivam struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
302a0f5a630SManivannan Sadhasivam
303a0f5a630SManivannan Sadhasivam if (mhi_chan->db_cfg.reset_req) {
304a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_chan->lock);
305a0f5a630SManivannan Sadhasivam mhi_chan->db_cfg.db_mode = true;
306a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_chan->lock);
307a0f5a630SManivannan Sadhasivam }
308a0f5a630SManivannan Sadhasivam
309a0f5a630SManivannan Sadhasivam read_lock_irq(&mhi_chan->lock);
310a0f5a630SManivannan Sadhasivam
311a0f5a630SManivannan Sadhasivam /* Only ring DB if ring is not empty */
312869a9990SQiang Yu if (tre_ring->base && tre_ring->wp != tre_ring->rp &&
313869a9990SQiang Yu mhi_chan->ch_state == MHI_CH_STATE_ENABLED)
314a0f5a630SManivannan Sadhasivam mhi_ring_chan_db(mhi_cntrl, mhi_chan);
315a0f5a630SManivannan Sadhasivam read_unlock_irq(&mhi_chan->lock);
316a0f5a630SManivannan Sadhasivam }
317a0f5a630SManivannan Sadhasivam
318a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, false);
319a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
320a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
321a0f5a630SManivannan Sadhasivam
322a0f5a630SManivannan Sadhasivam return 0;
323a0f5a630SManivannan Sadhasivam }
324a0f5a630SManivannan Sadhasivam
325a0f5a630SManivannan Sadhasivam /*
326a0f5a630SManivannan Sadhasivam * After receiving the MHI state change event from the device indicating the
327a0f5a630SManivannan Sadhasivam * transition to M1 state, the host can transition the device to M2 state
328a0f5a630SManivannan Sadhasivam * for keeping it in low power state.
329a0f5a630SManivannan Sadhasivam */
mhi_pm_m1_transition(struct mhi_controller * mhi_cntrl)330a0f5a630SManivannan Sadhasivam void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl)
331a0f5a630SManivannan Sadhasivam {
332a0f5a630SManivannan Sadhasivam enum mhi_pm_state state;
333a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
334a0f5a630SManivannan Sadhasivam
335a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
336a0f5a630SManivannan Sadhasivam state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M2);
337a0f5a630SManivannan Sadhasivam if (state == MHI_PM_M2) {
338a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M2);
339a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_M2;
340a0f5a630SManivannan Sadhasivam
341a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
342a0f5a630SManivannan Sadhasivam
343a0f5a630SManivannan Sadhasivam mhi_cntrl->M2++;
344a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
345a0f5a630SManivannan Sadhasivam
346a0f5a630SManivannan Sadhasivam /* If there are any pending resources, exit M2 immediately */
347a0f5a630SManivannan Sadhasivam if (unlikely(atomic_read(&mhi_cntrl->pending_pkts) ||
348a0f5a630SManivannan Sadhasivam atomic_read(&mhi_cntrl->dev_wake))) {
349a0f5a630SManivannan Sadhasivam dev_dbg(dev,
350a0f5a630SManivannan Sadhasivam "Exiting M2, pending_pkts: %d dev_wake: %d\n",
351a0f5a630SManivannan Sadhasivam atomic_read(&mhi_cntrl->pending_pkts),
352a0f5a630SManivannan Sadhasivam atomic_read(&mhi_cntrl->dev_wake));
353a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
354a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, true);
355a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, true);
356a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
357a0f5a630SManivannan Sadhasivam } else {
358a0f5a630SManivannan Sadhasivam mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_IDLE);
359a0f5a630SManivannan Sadhasivam }
360a0f5a630SManivannan Sadhasivam } else {
361a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
362a0f5a630SManivannan Sadhasivam }
363a0f5a630SManivannan Sadhasivam }
364a0f5a630SManivannan Sadhasivam
365a0f5a630SManivannan Sadhasivam /* MHI M3 completion handler */
mhi_pm_m3_transition(struct mhi_controller * mhi_cntrl)366a0f5a630SManivannan Sadhasivam int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl)
367a0f5a630SManivannan Sadhasivam {
368a0f5a630SManivannan Sadhasivam enum mhi_pm_state state;
369a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
370a0f5a630SManivannan Sadhasivam
371a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
372a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_M3;
373a0f5a630SManivannan Sadhasivam state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3);
374a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
375a0f5a630SManivannan Sadhasivam if (state != MHI_PM_M3) {
376a0f5a630SManivannan Sadhasivam dev_err(dev, "Unable to transition to M3 state\n");
377a0f5a630SManivannan Sadhasivam return -EIO;
378a0f5a630SManivannan Sadhasivam }
379a0f5a630SManivannan Sadhasivam
380a0f5a630SManivannan Sadhasivam mhi_cntrl->M3++;
381a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
382a0f5a630SManivannan Sadhasivam
383a0f5a630SManivannan Sadhasivam return 0;
384a0f5a630SManivannan Sadhasivam }
385a0f5a630SManivannan Sadhasivam
386a0f5a630SManivannan Sadhasivam /* Handle device Mission Mode transition */
mhi_pm_mission_mode_transition(struct mhi_controller * mhi_cntrl)387a0f5a630SManivannan Sadhasivam static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
388a0f5a630SManivannan Sadhasivam {
389a0f5a630SManivannan Sadhasivam struct mhi_event *mhi_event;
390a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
391a0f5a630SManivannan Sadhasivam enum mhi_ee_type ee = MHI_EE_MAX, current_ee = mhi_cntrl->ee;
392a0f5a630SManivannan Sadhasivam int i, ret;
393a0f5a630SManivannan Sadhasivam
394a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Processing Mission Mode transition\n");
395a0f5a630SManivannan Sadhasivam
396a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
397a0f5a630SManivannan Sadhasivam if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
398a0f5a630SManivannan Sadhasivam ee = mhi_get_exec_env(mhi_cntrl);
399a0f5a630SManivannan Sadhasivam
400a0f5a630SManivannan Sadhasivam if (!MHI_IN_MISSION_MODE(ee)) {
401a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
402a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
403a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
404a0f5a630SManivannan Sadhasivam return -EIO;
405a0f5a630SManivannan Sadhasivam }
406a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = ee;
407a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
408a0f5a630SManivannan Sadhasivam
409a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
410a0f5a630SManivannan Sadhasivam
411a0f5a630SManivannan Sadhasivam device_for_each_child(&mhi_cntrl->mhi_dev->dev, ¤t_ee,
412a0f5a630SManivannan Sadhasivam mhi_destroy_device);
413a0f5a630SManivannan Sadhasivam mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_MISSION_MODE);
414a0f5a630SManivannan Sadhasivam
415a0f5a630SManivannan Sadhasivam /* Force MHI to be in M0 state before continuing */
416a0f5a630SManivannan Sadhasivam ret = __mhi_device_get_sync(mhi_cntrl);
417a0f5a630SManivannan Sadhasivam if (ret)
418a0f5a630SManivannan Sadhasivam return ret;
419a0f5a630SManivannan Sadhasivam
420a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
421a0f5a630SManivannan Sadhasivam
422a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
423a0f5a630SManivannan Sadhasivam ret = -EIO;
424a0f5a630SManivannan Sadhasivam goto error_mission_mode;
425a0f5a630SManivannan Sadhasivam }
426a0f5a630SManivannan Sadhasivam
427a0f5a630SManivannan Sadhasivam /* Add elements to all HW event rings */
428a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
429a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
430a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_event->ring;
431a0f5a630SManivannan Sadhasivam
432a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev || !mhi_event->hw_ring)
433a0f5a630SManivannan Sadhasivam continue;
434a0f5a630SManivannan Sadhasivam
435a0f5a630SManivannan Sadhasivam ring->wp = ring->base + ring->len - ring->el_size;
436a0f5a630SManivannan Sadhasivam *ring->ctxt_wp = cpu_to_le64(ring->iommu_base + ring->len - ring->el_size);
437a0f5a630SManivannan Sadhasivam /* Update to all cores */
438a0f5a630SManivannan Sadhasivam smp_wmb();
439a0f5a630SManivannan Sadhasivam
440a0f5a630SManivannan Sadhasivam spin_lock_irq(&mhi_event->lock);
441a0f5a630SManivannan Sadhasivam if (MHI_DB_ACCESS_VALID(mhi_cntrl))
442a0f5a630SManivannan Sadhasivam mhi_ring_er_db(mhi_event);
443a0f5a630SManivannan Sadhasivam spin_unlock_irq(&mhi_event->lock);
444a0f5a630SManivannan Sadhasivam }
445a0f5a630SManivannan Sadhasivam
446a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
447a0f5a630SManivannan Sadhasivam
448a0f5a630SManivannan Sadhasivam /*
449a0f5a630SManivannan Sadhasivam * The MHI devices are only created when the client device switches its
450a0f5a630SManivannan Sadhasivam * Execution Environment (EE) to either SBL or AMSS states
451a0f5a630SManivannan Sadhasivam */
452a0f5a630SManivannan Sadhasivam mhi_create_devices(mhi_cntrl);
453a0f5a630SManivannan Sadhasivam
454a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
455a0f5a630SManivannan Sadhasivam
456a0f5a630SManivannan Sadhasivam error_mission_mode:
457a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, false);
458a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
459a0f5a630SManivannan Sadhasivam
460a0f5a630SManivannan Sadhasivam return ret;
461a0f5a630SManivannan Sadhasivam }
462a0f5a630SManivannan Sadhasivam
463a0f5a630SManivannan Sadhasivam /* Handle shutdown transitions */
mhi_pm_disable_transition(struct mhi_controller * mhi_cntrl)464a0f5a630SManivannan Sadhasivam static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
465a0f5a630SManivannan Sadhasivam {
466a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state;
467a0f5a630SManivannan Sadhasivam struct mhi_event *mhi_event;
468a0f5a630SManivannan Sadhasivam struct mhi_cmd_ctxt *cmd_ctxt;
469a0f5a630SManivannan Sadhasivam struct mhi_cmd *mhi_cmd;
470a0f5a630SManivannan Sadhasivam struct mhi_event_ctxt *er_ctxt;
471a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
472a0f5a630SManivannan Sadhasivam int ret, i;
473a0f5a630SManivannan Sadhasivam
474a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Processing disable transition with PM state: %s\n",
475a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
476a0f5a630SManivannan Sadhasivam
477a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
478a0f5a630SManivannan Sadhasivam
479a0f5a630SManivannan Sadhasivam /* Trigger MHI RESET so that the device will not access host memory */
480a0f5a630SManivannan Sadhasivam if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
481cabce92dSQiang Yu /* Skip MHI RESET if in RDDM state */
482cabce92dSQiang Yu if (mhi_cntrl->rddm_image && mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM)
483cabce92dSQiang Yu goto skip_mhi_reset;
484cabce92dSQiang Yu
485a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Triggering MHI Reset in device\n");
486a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
487a0f5a630SManivannan Sadhasivam
488a0f5a630SManivannan Sadhasivam /* Wait for the reset bit to be cleared by the device */
489a0f5a630SManivannan Sadhasivam ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
490d28cab4dSManivannan Sadhasivam MHICTRL_RESET_MASK, 0, 25000);
491a0f5a630SManivannan Sadhasivam if (ret)
492a0f5a630SManivannan Sadhasivam dev_err(dev, "Device failed to clear MHI Reset\n");
493a0f5a630SManivannan Sadhasivam
494a0f5a630SManivannan Sadhasivam /*
495a0f5a630SManivannan Sadhasivam * Device will clear BHI_INTVEC as a part of RESET processing,
496a0f5a630SManivannan Sadhasivam * hence re-program it
497a0f5a630SManivannan Sadhasivam */
498a0f5a630SManivannan Sadhasivam mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
49936e5505dSJeffrey Hugo
50036e5505dSJeffrey Hugo if (!MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) {
50136e5505dSJeffrey Hugo /* wait for ready to be set */
50236e5505dSJeffrey Hugo ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs,
50336e5505dSJeffrey Hugo MHISTATUS,
50436e5505dSJeffrey Hugo MHISTATUS_READY_MASK, 1, 25000);
50536e5505dSJeffrey Hugo if (ret)
50636e5505dSJeffrey Hugo dev_err(dev, "Device failed to enter READY state\n");
50736e5505dSJeffrey Hugo }
508a0f5a630SManivannan Sadhasivam }
509a0f5a630SManivannan Sadhasivam
510cabce92dSQiang Yu skip_mhi_reset:
511a0f5a630SManivannan Sadhasivam dev_dbg(dev,
512a0f5a630SManivannan Sadhasivam "Waiting for all pending event ring processing to complete\n");
513a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
514a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
515a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev)
516a0f5a630SManivannan Sadhasivam continue;
5171227d2a2SQiang Yu disable_irq(mhi_cntrl->irq[mhi_event->irq]);
518a0f5a630SManivannan Sadhasivam tasklet_kill(&mhi_event->task);
519a0f5a630SManivannan Sadhasivam }
520a0f5a630SManivannan Sadhasivam
521a0f5a630SManivannan Sadhasivam /* Release lock and wait for all pending threads to complete */
522a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
523a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Waiting for all pending threads to complete\n");
524a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
525a0f5a630SManivannan Sadhasivam
526a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
527a0f5a630SManivannan Sadhasivam device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
528a0f5a630SManivannan Sadhasivam
529a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
530a0f5a630SManivannan Sadhasivam
531a0f5a630SManivannan Sadhasivam WARN_ON(atomic_read(&mhi_cntrl->dev_wake));
532a0f5a630SManivannan Sadhasivam WARN_ON(atomic_read(&mhi_cntrl->pending_pkts));
533a0f5a630SManivannan Sadhasivam
534a0f5a630SManivannan Sadhasivam /* Reset the ev rings and cmd rings */
535a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Resetting EV CTXT and CMD CTXT\n");
536a0f5a630SManivannan Sadhasivam mhi_cmd = mhi_cntrl->mhi_cmd;
537a0f5a630SManivannan Sadhasivam cmd_ctxt = mhi_cntrl->mhi_ctxt->cmd_ctxt;
538a0f5a630SManivannan Sadhasivam for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) {
539a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_cmd->ring;
540a0f5a630SManivannan Sadhasivam
541a0f5a630SManivannan Sadhasivam ring->rp = ring->base;
542a0f5a630SManivannan Sadhasivam ring->wp = ring->base;
543a0f5a630SManivannan Sadhasivam cmd_ctxt->rp = cmd_ctxt->rbase;
544a0f5a630SManivannan Sadhasivam cmd_ctxt->wp = cmd_ctxt->rbase;
545a0f5a630SManivannan Sadhasivam }
546a0f5a630SManivannan Sadhasivam
547a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
548a0f5a630SManivannan Sadhasivam er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;
549a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++,
550a0f5a630SManivannan Sadhasivam mhi_event++) {
551a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_event->ring;
552a0f5a630SManivannan Sadhasivam
553a0f5a630SManivannan Sadhasivam /* Skip offload events */
554a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev)
555a0f5a630SManivannan Sadhasivam continue;
556a0f5a630SManivannan Sadhasivam
557a0f5a630SManivannan Sadhasivam ring->rp = ring->base;
558a0f5a630SManivannan Sadhasivam ring->wp = ring->base;
559a0f5a630SManivannan Sadhasivam er_ctxt->rp = er_ctxt->rbase;
560a0f5a630SManivannan Sadhasivam er_ctxt->wp = er_ctxt->rbase;
561a0f5a630SManivannan Sadhasivam }
562a0f5a630SManivannan Sadhasivam
563a0f5a630SManivannan Sadhasivam /* Move to disable state */
564a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
565a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_DISABLE);
566a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
567a0f5a630SManivannan Sadhasivam if (unlikely(cur_state != MHI_PM_DISABLE))
568a0f5a630SManivannan Sadhasivam dev_err(dev, "Error moving from PM state: %s to: %s\n",
569a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(cur_state),
570a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_DISABLE));
571a0f5a630SManivannan Sadhasivam
572a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n",
573a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state),
5743a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_cntrl->dev_state));
575a0f5a630SManivannan Sadhasivam
576a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
577a0f5a630SManivannan Sadhasivam }
578a0f5a630SManivannan Sadhasivam
579a0f5a630SManivannan Sadhasivam /* Handle system error transitions */
mhi_pm_sys_error_transition(struct mhi_controller * mhi_cntrl)580a0f5a630SManivannan Sadhasivam static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
581a0f5a630SManivannan Sadhasivam {
582a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state, prev_state;
583a0f5a630SManivannan Sadhasivam enum dev_st_transition next_state;
584a0f5a630SManivannan Sadhasivam struct mhi_event *mhi_event;
585a0f5a630SManivannan Sadhasivam struct mhi_cmd_ctxt *cmd_ctxt;
586a0f5a630SManivannan Sadhasivam struct mhi_cmd *mhi_cmd;
587a0f5a630SManivannan Sadhasivam struct mhi_event_ctxt *er_ctxt;
588a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
589a0f5a630SManivannan Sadhasivam int ret, i;
590a0f5a630SManivannan Sadhasivam
591a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Transitioning from PM state: %s to: %s\n",
592a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state),
593a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_SYS_ERR_PROCESS));
594a0f5a630SManivannan Sadhasivam
595a0f5a630SManivannan Sadhasivam /* We must notify MHI control driver so it can clean up first */
596a0f5a630SManivannan Sadhasivam mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_SYS_ERROR);
597a0f5a630SManivannan Sadhasivam
598a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
599a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
600a0f5a630SManivannan Sadhasivam prev_state = mhi_cntrl->pm_state;
601a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_SYS_ERR_PROCESS);
602a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
603a0f5a630SManivannan Sadhasivam
604a0f5a630SManivannan Sadhasivam if (cur_state != MHI_PM_SYS_ERR_PROCESS) {
605a0f5a630SManivannan Sadhasivam dev_err(dev, "Failed to transition from PM state: %s to: %s\n",
606a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(cur_state),
607a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_SYS_ERR_PROCESS));
608a0f5a630SManivannan Sadhasivam goto exit_sys_error_transition;
609a0f5a630SManivannan Sadhasivam }
610a0f5a630SManivannan Sadhasivam
611a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = MHI_EE_DISABLE_TRANSITION;
612a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_RESET;
613a0f5a630SManivannan Sadhasivam
614a0f5a630SManivannan Sadhasivam /* Wake up threads waiting for state transition */
615a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
616a0f5a630SManivannan Sadhasivam
617a0f5a630SManivannan Sadhasivam /* Trigger MHI RESET so that the device will not access host memory */
618a0f5a630SManivannan Sadhasivam if (MHI_REG_ACCESS_VALID(prev_state)) {
619a0f5a630SManivannan Sadhasivam u32 in_reset = -1;
620a0f5a630SManivannan Sadhasivam unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms);
621a0f5a630SManivannan Sadhasivam
622a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Triggering MHI Reset in device\n");
623a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
624a0f5a630SManivannan Sadhasivam
625a0f5a630SManivannan Sadhasivam /* Wait for the reset bit to be cleared by the device */
626a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
627a0f5a630SManivannan Sadhasivam mhi_read_reg_field(mhi_cntrl,
628a0f5a630SManivannan Sadhasivam mhi_cntrl->regs,
629a0f5a630SManivannan Sadhasivam MHICTRL,
630a0f5a630SManivannan Sadhasivam MHICTRL_RESET_MASK,
631a0f5a630SManivannan Sadhasivam &in_reset) ||
632a0f5a630SManivannan Sadhasivam !in_reset, timeout);
633a0f5a630SManivannan Sadhasivam if (!ret || in_reset) {
634a0f5a630SManivannan Sadhasivam dev_err(dev, "Device failed to exit MHI Reset state\n");
635*ac191bcbSJeffrey Hugo write_lock_irq(&mhi_cntrl->pm_lock);
636*ac191bcbSJeffrey Hugo cur_state = mhi_tryset_pm_state(mhi_cntrl,
637*ac191bcbSJeffrey Hugo MHI_PM_SYS_ERR_FAIL);
638*ac191bcbSJeffrey Hugo write_unlock_irq(&mhi_cntrl->pm_lock);
639*ac191bcbSJeffrey Hugo /* Shutdown may have occurred, otherwise cleanup now */
640*ac191bcbSJeffrey Hugo if (cur_state != MHI_PM_SYS_ERR_FAIL)
641a0f5a630SManivannan Sadhasivam goto exit_sys_error_transition;
642a0f5a630SManivannan Sadhasivam }
643a0f5a630SManivannan Sadhasivam
644a0f5a630SManivannan Sadhasivam /*
645a0f5a630SManivannan Sadhasivam * Device will clear BHI_INTVEC as a part of RESET processing,
646a0f5a630SManivannan Sadhasivam * hence re-program it
647a0f5a630SManivannan Sadhasivam */
648a0f5a630SManivannan Sadhasivam mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
649a0f5a630SManivannan Sadhasivam }
650a0f5a630SManivannan Sadhasivam
651a0f5a630SManivannan Sadhasivam dev_dbg(dev,
652a0f5a630SManivannan Sadhasivam "Waiting for all pending event ring processing to complete\n");
653a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
654a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
655a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev)
656a0f5a630SManivannan Sadhasivam continue;
657a0f5a630SManivannan Sadhasivam tasklet_kill(&mhi_event->task);
658a0f5a630SManivannan Sadhasivam }
659a0f5a630SManivannan Sadhasivam
660a0f5a630SManivannan Sadhasivam /* Release lock and wait for all pending threads to complete */
661a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
662a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Waiting for all pending threads to complete\n");
663a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
664a0f5a630SManivannan Sadhasivam
665a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
666a0f5a630SManivannan Sadhasivam device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
667a0f5a630SManivannan Sadhasivam
668a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
669a0f5a630SManivannan Sadhasivam
670a0f5a630SManivannan Sadhasivam WARN_ON(atomic_read(&mhi_cntrl->dev_wake));
671a0f5a630SManivannan Sadhasivam WARN_ON(atomic_read(&mhi_cntrl->pending_pkts));
672a0f5a630SManivannan Sadhasivam
673a0f5a630SManivannan Sadhasivam /* Reset the ev rings and cmd rings */
674a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Resetting EV CTXT and CMD CTXT\n");
675a0f5a630SManivannan Sadhasivam mhi_cmd = mhi_cntrl->mhi_cmd;
676a0f5a630SManivannan Sadhasivam cmd_ctxt = mhi_cntrl->mhi_ctxt->cmd_ctxt;
677a0f5a630SManivannan Sadhasivam for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) {
678a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_cmd->ring;
679a0f5a630SManivannan Sadhasivam
680a0f5a630SManivannan Sadhasivam ring->rp = ring->base;
681a0f5a630SManivannan Sadhasivam ring->wp = ring->base;
682a0f5a630SManivannan Sadhasivam cmd_ctxt->rp = cmd_ctxt->rbase;
683a0f5a630SManivannan Sadhasivam cmd_ctxt->wp = cmd_ctxt->rbase;
684a0f5a630SManivannan Sadhasivam }
685a0f5a630SManivannan Sadhasivam
686a0f5a630SManivannan Sadhasivam mhi_event = mhi_cntrl->mhi_event;
687a0f5a630SManivannan Sadhasivam er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;
688a0f5a630SManivannan Sadhasivam for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++,
689a0f5a630SManivannan Sadhasivam mhi_event++) {
690a0f5a630SManivannan Sadhasivam struct mhi_ring *ring = &mhi_event->ring;
691a0f5a630SManivannan Sadhasivam
692a0f5a630SManivannan Sadhasivam /* Skip offload events */
693a0f5a630SManivannan Sadhasivam if (mhi_event->offload_ev)
694a0f5a630SManivannan Sadhasivam continue;
695a0f5a630SManivannan Sadhasivam
696a0f5a630SManivannan Sadhasivam ring->rp = ring->base;
697a0f5a630SManivannan Sadhasivam ring->wp = ring->base;
698a0f5a630SManivannan Sadhasivam er_ctxt->rp = er_ctxt->rbase;
699a0f5a630SManivannan Sadhasivam er_ctxt->wp = er_ctxt->rbase;
700a0f5a630SManivannan Sadhasivam }
701a0f5a630SManivannan Sadhasivam
702a0f5a630SManivannan Sadhasivam /* Transition to next state */
703a0f5a630SManivannan Sadhasivam if (MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) {
704a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
705a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR);
706a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
707a0f5a630SManivannan Sadhasivam if (cur_state != MHI_PM_POR) {
708a0f5a630SManivannan Sadhasivam dev_err(dev, "Error moving to state %s from %s\n",
709a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_POR),
710a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(cur_state));
711a0f5a630SManivannan Sadhasivam goto exit_sys_error_transition;
712a0f5a630SManivannan Sadhasivam }
713a0f5a630SManivannan Sadhasivam next_state = DEV_ST_TRANSITION_PBL;
714a0f5a630SManivannan Sadhasivam } else {
715a0f5a630SManivannan Sadhasivam next_state = DEV_ST_TRANSITION_READY;
716a0f5a630SManivannan Sadhasivam }
717a0f5a630SManivannan Sadhasivam
718a0f5a630SManivannan Sadhasivam mhi_queue_state_transition(mhi_cntrl, next_state);
719a0f5a630SManivannan Sadhasivam
720a0f5a630SManivannan Sadhasivam exit_sys_error_transition:
721a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n",
722a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state),
7233a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_cntrl->dev_state));
724a0f5a630SManivannan Sadhasivam
725a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
726a0f5a630SManivannan Sadhasivam }
727a0f5a630SManivannan Sadhasivam
728a0f5a630SManivannan Sadhasivam /* Queue a new work item and schedule work */
mhi_queue_state_transition(struct mhi_controller * mhi_cntrl,enum dev_st_transition state)729a0f5a630SManivannan Sadhasivam int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
730a0f5a630SManivannan Sadhasivam enum dev_st_transition state)
731a0f5a630SManivannan Sadhasivam {
732a0f5a630SManivannan Sadhasivam struct state_transition *item = kmalloc(sizeof(*item), GFP_ATOMIC);
733a0f5a630SManivannan Sadhasivam unsigned long flags;
734a0f5a630SManivannan Sadhasivam
735a0f5a630SManivannan Sadhasivam if (!item)
736a0f5a630SManivannan Sadhasivam return -ENOMEM;
737a0f5a630SManivannan Sadhasivam
738a0f5a630SManivannan Sadhasivam item->state = state;
739a0f5a630SManivannan Sadhasivam spin_lock_irqsave(&mhi_cntrl->transition_lock, flags);
740a0f5a630SManivannan Sadhasivam list_add_tail(&item->node, &mhi_cntrl->transition_list);
741a0f5a630SManivannan Sadhasivam spin_unlock_irqrestore(&mhi_cntrl->transition_lock, flags);
742a0f5a630SManivannan Sadhasivam
743a0f5a630SManivannan Sadhasivam queue_work(mhi_cntrl->hiprio_wq, &mhi_cntrl->st_worker);
744a0f5a630SManivannan Sadhasivam
745a0f5a630SManivannan Sadhasivam return 0;
746a0f5a630SManivannan Sadhasivam }
747a0f5a630SManivannan Sadhasivam
748a0f5a630SManivannan Sadhasivam /* SYS_ERR worker */
mhi_pm_sys_err_handler(struct mhi_controller * mhi_cntrl)749a0f5a630SManivannan Sadhasivam void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl)
750a0f5a630SManivannan Sadhasivam {
751a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
752a0f5a630SManivannan Sadhasivam
753a0f5a630SManivannan Sadhasivam /* skip if controller supports RDDM */
754a0f5a630SManivannan Sadhasivam if (mhi_cntrl->rddm_image) {
755a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Controller supports RDDM, skip SYS_ERROR\n");
756a0f5a630SManivannan Sadhasivam return;
757a0f5a630SManivannan Sadhasivam }
758a0f5a630SManivannan Sadhasivam
759a0f5a630SManivannan Sadhasivam mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_SYS_ERR);
760a0f5a630SManivannan Sadhasivam }
761a0f5a630SManivannan Sadhasivam
762a0f5a630SManivannan Sadhasivam /* Device State Transition worker */
mhi_pm_st_worker(struct work_struct * work)763a0f5a630SManivannan Sadhasivam void mhi_pm_st_worker(struct work_struct *work)
764a0f5a630SManivannan Sadhasivam {
765a0f5a630SManivannan Sadhasivam struct state_transition *itr, *tmp;
766a0f5a630SManivannan Sadhasivam LIST_HEAD(head);
767a0f5a630SManivannan Sadhasivam struct mhi_controller *mhi_cntrl = container_of(work,
768a0f5a630SManivannan Sadhasivam struct mhi_controller,
769a0f5a630SManivannan Sadhasivam st_worker);
770a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
771a0f5a630SManivannan Sadhasivam
772a0f5a630SManivannan Sadhasivam spin_lock_irq(&mhi_cntrl->transition_lock);
773a0f5a630SManivannan Sadhasivam list_splice_tail_init(&mhi_cntrl->transition_list, &head);
774a0f5a630SManivannan Sadhasivam spin_unlock_irq(&mhi_cntrl->transition_lock);
775a0f5a630SManivannan Sadhasivam
776a0f5a630SManivannan Sadhasivam list_for_each_entry_safe(itr, tmp, &head, node) {
777a0f5a630SManivannan Sadhasivam list_del(&itr->node);
778a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Handling state transition: %s\n",
779a0f5a630SManivannan Sadhasivam TO_DEV_STATE_TRANS_STR(itr->state));
780a0f5a630SManivannan Sadhasivam
781a0f5a630SManivannan Sadhasivam switch (itr->state) {
782a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_PBL:
783a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
784a0f5a630SManivannan Sadhasivam if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
785a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
786a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
787a0f5a630SManivannan Sadhasivam mhi_fw_load_handler(mhi_cntrl);
788a0f5a630SManivannan Sadhasivam break;
789a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_SBL:
790a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
791a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = MHI_EE_SBL;
792a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
793a0f5a630SManivannan Sadhasivam /*
794a0f5a630SManivannan Sadhasivam * The MHI devices are only created when the client
795a0f5a630SManivannan Sadhasivam * device switches its Execution Environment (EE) to
796a0f5a630SManivannan Sadhasivam * either SBL or AMSS states
797a0f5a630SManivannan Sadhasivam */
798a0f5a630SManivannan Sadhasivam mhi_create_devices(mhi_cntrl);
799a0f5a630SManivannan Sadhasivam if (mhi_cntrl->fbc_download)
800a0f5a630SManivannan Sadhasivam mhi_download_amss_image(mhi_cntrl);
801a0f5a630SManivannan Sadhasivam break;
802a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_MISSION_MODE:
803a0f5a630SManivannan Sadhasivam mhi_pm_mission_mode_transition(mhi_cntrl);
804a0f5a630SManivannan Sadhasivam break;
805a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_FP:
806a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
807a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = MHI_EE_FP;
808a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
809a0f5a630SManivannan Sadhasivam mhi_create_devices(mhi_cntrl);
810a0f5a630SManivannan Sadhasivam break;
811a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_READY:
812a0f5a630SManivannan Sadhasivam mhi_ready_state_transition(mhi_cntrl);
813a0f5a630SManivannan Sadhasivam break;
814a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_SYS_ERR:
815a0f5a630SManivannan Sadhasivam mhi_pm_sys_error_transition(mhi_cntrl);
816a0f5a630SManivannan Sadhasivam break;
817a0f5a630SManivannan Sadhasivam case DEV_ST_TRANSITION_DISABLE:
818a0f5a630SManivannan Sadhasivam mhi_pm_disable_transition(mhi_cntrl);
819a0f5a630SManivannan Sadhasivam break;
820a0f5a630SManivannan Sadhasivam default:
821a0f5a630SManivannan Sadhasivam break;
822a0f5a630SManivannan Sadhasivam }
823a0f5a630SManivannan Sadhasivam kfree(itr);
824a0f5a630SManivannan Sadhasivam }
825a0f5a630SManivannan Sadhasivam }
826a0f5a630SManivannan Sadhasivam
mhi_pm_suspend(struct mhi_controller * mhi_cntrl)827a0f5a630SManivannan Sadhasivam int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
828a0f5a630SManivannan Sadhasivam {
829a0f5a630SManivannan Sadhasivam struct mhi_chan *itr, *tmp;
830a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
831a0f5a630SManivannan Sadhasivam enum mhi_pm_state new_state;
832a0f5a630SManivannan Sadhasivam int ret;
833a0f5a630SManivannan Sadhasivam
834a0f5a630SManivannan Sadhasivam if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
835a0f5a630SManivannan Sadhasivam return -EINVAL;
836a0f5a630SManivannan Sadhasivam
837a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
838a0f5a630SManivannan Sadhasivam return -EIO;
839a0f5a630SManivannan Sadhasivam
840a0f5a630SManivannan Sadhasivam /* Return busy if there are any pending resources */
841a0f5a630SManivannan Sadhasivam if (atomic_read(&mhi_cntrl->dev_wake) ||
842a0f5a630SManivannan Sadhasivam atomic_read(&mhi_cntrl->pending_pkts))
843a0f5a630SManivannan Sadhasivam return -EBUSY;
844a0f5a630SManivannan Sadhasivam
845a0f5a630SManivannan Sadhasivam /* Take MHI out of M2 state */
846a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
847a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, false);
848a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
849a0f5a630SManivannan Sadhasivam
850a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
851a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state == MHI_STATE_M0 ||
852a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state == MHI_STATE_M1 ||
853a0f5a630SManivannan Sadhasivam MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
854a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
855a0f5a630SManivannan Sadhasivam
856a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
857a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, false);
858a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
859a0f5a630SManivannan Sadhasivam
860a0f5a630SManivannan Sadhasivam if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
861a0f5a630SManivannan Sadhasivam dev_err(dev,
862a0f5a630SManivannan Sadhasivam "Could not enter M0/M1 state");
863a0f5a630SManivannan Sadhasivam return -EIO;
864a0f5a630SManivannan Sadhasivam }
865a0f5a630SManivannan Sadhasivam
866a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
867a0f5a630SManivannan Sadhasivam
868a0f5a630SManivannan Sadhasivam if (atomic_read(&mhi_cntrl->dev_wake) ||
869a0f5a630SManivannan Sadhasivam atomic_read(&mhi_cntrl->pending_pkts)) {
870a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
871a0f5a630SManivannan Sadhasivam return -EBUSY;
872a0f5a630SManivannan Sadhasivam }
873a0f5a630SManivannan Sadhasivam
874a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Allowing M3 transition\n");
875a0f5a630SManivannan Sadhasivam new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_ENTER);
876a0f5a630SManivannan Sadhasivam if (new_state != MHI_PM_M3_ENTER) {
877a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
878a0f5a630SManivannan Sadhasivam dev_err(dev,
879a0f5a630SManivannan Sadhasivam "Error setting to PM state: %s from: %s\n",
880a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_M3_ENTER),
881a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
882a0f5a630SManivannan Sadhasivam return -EIO;
883a0f5a630SManivannan Sadhasivam }
884a0f5a630SManivannan Sadhasivam
885a0f5a630SManivannan Sadhasivam /* Set MHI to M3 and wait for completion */
886a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
887a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
888a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Waiting for M3 completion\n");
889a0f5a630SManivannan Sadhasivam
890a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
891a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state == MHI_STATE_M3 ||
892a0f5a630SManivannan Sadhasivam MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
893a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
894a0f5a630SManivannan Sadhasivam
895a0f5a630SManivannan Sadhasivam if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
896a0f5a630SManivannan Sadhasivam dev_err(dev,
897a0f5a630SManivannan Sadhasivam "Did not enter M3 state, MHI state: %s, PM state: %s\n",
8983a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_cntrl->dev_state),
899a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
900a0f5a630SManivannan Sadhasivam return -EIO;
901a0f5a630SManivannan Sadhasivam }
902a0f5a630SManivannan Sadhasivam
903a0f5a630SManivannan Sadhasivam /* Notify clients about entering LPM */
904a0f5a630SManivannan Sadhasivam list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) {
905a0f5a630SManivannan Sadhasivam mutex_lock(&itr->mutex);
906a0f5a630SManivannan Sadhasivam if (itr->mhi_dev)
907a0f5a630SManivannan Sadhasivam mhi_notify(itr->mhi_dev, MHI_CB_LPM_ENTER);
908a0f5a630SManivannan Sadhasivam mutex_unlock(&itr->mutex);
909a0f5a630SManivannan Sadhasivam }
910a0f5a630SManivannan Sadhasivam
911a0f5a630SManivannan Sadhasivam return 0;
912a0f5a630SManivannan Sadhasivam }
913a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_pm_suspend);
914a0f5a630SManivannan Sadhasivam
__mhi_pm_resume(struct mhi_controller * mhi_cntrl,bool force)915a0f5a630SManivannan Sadhasivam static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force)
916a0f5a630SManivannan Sadhasivam {
917a0f5a630SManivannan Sadhasivam struct mhi_chan *itr, *tmp;
918a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
919a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state;
920a0f5a630SManivannan Sadhasivam int ret;
921a0f5a630SManivannan Sadhasivam
922a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Entered with PM state: %s, MHI state: %s\n",
923a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state),
9243a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_cntrl->dev_state));
925a0f5a630SManivannan Sadhasivam
926a0f5a630SManivannan Sadhasivam if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
927a0f5a630SManivannan Sadhasivam return 0;
928a0f5a630SManivannan Sadhasivam
929a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
930a0f5a630SManivannan Sadhasivam return -EIO;
931a0f5a630SManivannan Sadhasivam
932a0f5a630SManivannan Sadhasivam if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) {
933a0f5a630SManivannan Sadhasivam dev_warn(dev, "Resuming from non M3 state (%s)\n",
9343a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_get_mhi_state(mhi_cntrl)));
935a0f5a630SManivannan Sadhasivam if (!force)
936a0f5a630SManivannan Sadhasivam return -EINVAL;
937a0f5a630SManivannan Sadhasivam }
938a0f5a630SManivannan Sadhasivam
939a0f5a630SManivannan Sadhasivam /* Notify clients about exiting LPM */
940a0f5a630SManivannan Sadhasivam list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) {
941a0f5a630SManivannan Sadhasivam mutex_lock(&itr->mutex);
942a0f5a630SManivannan Sadhasivam if (itr->mhi_dev)
943a0f5a630SManivannan Sadhasivam mhi_notify(itr->mhi_dev, MHI_CB_LPM_EXIT);
944a0f5a630SManivannan Sadhasivam mutex_unlock(&itr->mutex);
945a0f5a630SManivannan Sadhasivam }
946a0f5a630SManivannan Sadhasivam
947a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
948a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_EXIT);
949a0f5a630SManivannan Sadhasivam if (cur_state != MHI_PM_M3_EXIT) {
950a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
951a0f5a630SManivannan Sadhasivam dev_info(dev,
952a0f5a630SManivannan Sadhasivam "Error setting to PM state: %s from: %s\n",
953a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(MHI_PM_M3_EXIT),
954a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
955a0f5a630SManivannan Sadhasivam return -EIO;
956a0f5a630SManivannan Sadhasivam }
957a0f5a630SManivannan Sadhasivam
958a0f5a630SManivannan Sadhasivam /* Set MHI to M0 and wait for completion */
959a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
960a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
961a0f5a630SManivannan Sadhasivam
962a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
963a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state == MHI_STATE_M0 ||
964a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state == MHI_STATE_M2 ||
965a0f5a630SManivannan Sadhasivam MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
966a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
967a0f5a630SManivannan Sadhasivam
968a0f5a630SManivannan Sadhasivam if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
969a0f5a630SManivannan Sadhasivam dev_err(dev,
970a0f5a630SManivannan Sadhasivam "Did not enter M0 state, MHI state: %s, PM state: %s\n",
9713a1b8e28SManivannan Sadhasivam mhi_state_str(mhi_cntrl->dev_state),
972a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
973a0f5a630SManivannan Sadhasivam return -EIO;
974a0f5a630SManivannan Sadhasivam }
975a0f5a630SManivannan Sadhasivam
976a0f5a630SManivannan Sadhasivam return 0;
977a0f5a630SManivannan Sadhasivam }
978a0f5a630SManivannan Sadhasivam
mhi_pm_resume(struct mhi_controller * mhi_cntrl)979a0f5a630SManivannan Sadhasivam int mhi_pm_resume(struct mhi_controller *mhi_cntrl)
980a0f5a630SManivannan Sadhasivam {
981a0f5a630SManivannan Sadhasivam return __mhi_pm_resume(mhi_cntrl, false);
982a0f5a630SManivannan Sadhasivam }
983a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_pm_resume);
984a0f5a630SManivannan Sadhasivam
mhi_pm_resume_force(struct mhi_controller * mhi_cntrl)985a0f5a630SManivannan Sadhasivam int mhi_pm_resume_force(struct mhi_controller *mhi_cntrl)
986a0f5a630SManivannan Sadhasivam {
987a0f5a630SManivannan Sadhasivam return __mhi_pm_resume(mhi_cntrl, true);
988a0f5a630SManivannan Sadhasivam }
989a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_pm_resume_force);
990a0f5a630SManivannan Sadhasivam
__mhi_device_get_sync(struct mhi_controller * mhi_cntrl)991a0f5a630SManivannan Sadhasivam int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl)
992a0f5a630SManivannan Sadhasivam {
993a0f5a630SManivannan Sadhasivam int ret;
994a0f5a630SManivannan Sadhasivam
995a0f5a630SManivannan Sadhasivam /* Wake up the device */
996a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
997a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
998a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
999a0f5a630SManivannan Sadhasivam return -EIO;
1000a0f5a630SManivannan Sadhasivam }
1001a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, true);
1002a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
1003a0f5a630SManivannan Sadhasivam mhi_trigger_resume(mhi_cntrl);
1004a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
1005a0f5a630SManivannan Sadhasivam
1006a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
1007a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state == MHI_PM_M0 ||
1008a0f5a630SManivannan Sadhasivam MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
1009a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
1010a0f5a630SManivannan Sadhasivam
1011a0f5a630SManivannan Sadhasivam if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
1012a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
1013a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, false);
1014a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
1015a0f5a630SManivannan Sadhasivam return -EIO;
1016a0f5a630SManivannan Sadhasivam }
1017a0f5a630SManivannan Sadhasivam
1018a0f5a630SManivannan Sadhasivam return 0;
1019a0f5a630SManivannan Sadhasivam }
1020a0f5a630SManivannan Sadhasivam
1021a0f5a630SManivannan Sadhasivam /* Assert device wake db */
mhi_assert_dev_wake(struct mhi_controller * mhi_cntrl,bool force)1022a0f5a630SManivannan Sadhasivam static void mhi_assert_dev_wake(struct mhi_controller *mhi_cntrl, bool force)
1023a0f5a630SManivannan Sadhasivam {
1024a0f5a630SManivannan Sadhasivam unsigned long flags;
1025a0f5a630SManivannan Sadhasivam
1026a0f5a630SManivannan Sadhasivam /*
1027a0f5a630SManivannan Sadhasivam * If force flag is set, then increment the wake count value and
1028a0f5a630SManivannan Sadhasivam * ring wake db
1029a0f5a630SManivannan Sadhasivam */
1030a0f5a630SManivannan Sadhasivam if (unlikely(force)) {
1031a0f5a630SManivannan Sadhasivam spin_lock_irqsave(&mhi_cntrl->wlock, flags);
1032a0f5a630SManivannan Sadhasivam atomic_inc(&mhi_cntrl->dev_wake);
1033a0f5a630SManivannan Sadhasivam if (MHI_WAKE_DB_FORCE_SET_VALID(mhi_cntrl->pm_state) &&
1034a0f5a630SManivannan Sadhasivam !mhi_cntrl->wake_set) {
1035a0f5a630SManivannan Sadhasivam mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1);
1036a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_set = true;
1037a0f5a630SManivannan Sadhasivam }
1038a0f5a630SManivannan Sadhasivam spin_unlock_irqrestore(&mhi_cntrl->wlock, flags);
1039a0f5a630SManivannan Sadhasivam } else {
1040a0f5a630SManivannan Sadhasivam /*
1041a0f5a630SManivannan Sadhasivam * If resources are already requested, then just increment
1042a0f5a630SManivannan Sadhasivam * the wake count value and return
1043a0f5a630SManivannan Sadhasivam */
1044a0f5a630SManivannan Sadhasivam if (likely(atomic_add_unless(&mhi_cntrl->dev_wake, 1, 0)))
1045a0f5a630SManivannan Sadhasivam return;
1046a0f5a630SManivannan Sadhasivam
1047a0f5a630SManivannan Sadhasivam spin_lock_irqsave(&mhi_cntrl->wlock, flags);
1048a0f5a630SManivannan Sadhasivam if ((atomic_inc_return(&mhi_cntrl->dev_wake) == 1) &&
1049a0f5a630SManivannan Sadhasivam MHI_WAKE_DB_SET_VALID(mhi_cntrl->pm_state) &&
1050a0f5a630SManivannan Sadhasivam !mhi_cntrl->wake_set) {
1051a0f5a630SManivannan Sadhasivam mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1);
1052a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_set = true;
1053a0f5a630SManivannan Sadhasivam }
1054a0f5a630SManivannan Sadhasivam spin_unlock_irqrestore(&mhi_cntrl->wlock, flags);
1055a0f5a630SManivannan Sadhasivam }
1056a0f5a630SManivannan Sadhasivam }
1057a0f5a630SManivannan Sadhasivam
1058a0f5a630SManivannan Sadhasivam /* De-assert device wake db */
mhi_deassert_dev_wake(struct mhi_controller * mhi_cntrl,bool override)1059a0f5a630SManivannan Sadhasivam static void mhi_deassert_dev_wake(struct mhi_controller *mhi_cntrl,
1060a0f5a630SManivannan Sadhasivam bool override)
1061a0f5a630SManivannan Sadhasivam {
1062a0f5a630SManivannan Sadhasivam unsigned long flags;
1063a0f5a630SManivannan Sadhasivam
1064a0f5a630SManivannan Sadhasivam /*
1065a0f5a630SManivannan Sadhasivam * Only continue if there is a single resource, else just decrement
1066a0f5a630SManivannan Sadhasivam * and return
1067a0f5a630SManivannan Sadhasivam */
1068a0f5a630SManivannan Sadhasivam if (likely(atomic_add_unless(&mhi_cntrl->dev_wake, -1, 1)))
1069a0f5a630SManivannan Sadhasivam return;
1070a0f5a630SManivannan Sadhasivam
1071a0f5a630SManivannan Sadhasivam spin_lock_irqsave(&mhi_cntrl->wlock, flags);
1072a0f5a630SManivannan Sadhasivam if ((atomic_dec_return(&mhi_cntrl->dev_wake) == 0) &&
1073a0f5a630SManivannan Sadhasivam MHI_WAKE_DB_CLEAR_VALID(mhi_cntrl->pm_state) && !override &&
1074a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_set) {
1075a0f5a630SManivannan Sadhasivam mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 0);
1076a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_set = false;
1077a0f5a630SManivannan Sadhasivam }
1078a0f5a630SManivannan Sadhasivam spin_unlock_irqrestore(&mhi_cntrl->wlock, flags);
1079a0f5a630SManivannan Sadhasivam }
1080a0f5a630SManivannan Sadhasivam
mhi_async_power_up(struct mhi_controller * mhi_cntrl)1081a0f5a630SManivannan Sadhasivam int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
1082a0f5a630SManivannan Sadhasivam {
10831227d2a2SQiang Yu struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
1084a0f5a630SManivannan Sadhasivam enum mhi_state state;
1085a0f5a630SManivannan Sadhasivam enum mhi_ee_type current_ee;
1086a0f5a630SManivannan Sadhasivam enum dev_st_transition next_state;
1087a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
1088a0f5a630SManivannan Sadhasivam u32 interval_us = 25000; /* poll register field every 25 milliseconds */
10891227d2a2SQiang Yu int ret, i;
1090a0f5a630SManivannan Sadhasivam
1091a0f5a630SManivannan Sadhasivam dev_info(dev, "Requested to power ON\n");
1092a0f5a630SManivannan Sadhasivam
1093a0f5a630SManivannan Sadhasivam /* Supply default wake routines if not provided by controller driver */
1094a0f5a630SManivannan Sadhasivam if (!mhi_cntrl->wake_get || !mhi_cntrl->wake_put ||
1095a0f5a630SManivannan Sadhasivam !mhi_cntrl->wake_toggle) {
1096a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get = mhi_assert_dev_wake;
1097a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put = mhi_deassert_dev_wake;
1098a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_toggle = (mhi_cntrl->db_access & MHI_PM_M2) ?
1099a0f5a630SManivannan Sadhasivam mhi_toggle_dev_wake_nop : mhi_toggle_dev_wake;
1100a0f5a630SManivannan Sadhasivam }
1101a0f5a630SManivannan Sadhasivam
1102a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
1103a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = MHI_PM_DISABLE;
1104a0f5a630SManivannan Sadhasivam
1105a0f5a630SManivannan Sadhasivam /* Setup BHI INTVEC */
1106a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
1107a0f5a630SManivannan Sadhasivam mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
1108a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = MHI_PM_POR;
1109a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = MHI_EE_MAX;
1110a0f5a630SManivannan Sadhasivam current_ee = mhi_get_exec_env(mhi_cntrl);
1111a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
1112a0f5a630SManivannan Sadhasivam
1113a0f5a630SManivannan Sadhasivam /* Confirm that the device is in valid exec env */
1114a0f5a630SManivannan Sadhasivam if (!MHI_POWER_UP_CAPABLE(current_ee)) {
1115a0f5a630SManivannan Sadhasivam dev_err(dev, "%s is not a valid EE for power on\n",
1116a0f5a630SManivannan Sadhasivam TO_MHI_EXEC_STR(current_ee));
1117a0f5a630SManivannan Sadhasivam ret = -EIO;
1118a0f5a630SManivannan Sadhasivam goto error_exit;
1119a0f5a630SManivannan Sadhasivam }
1120a0f5a630SManivannan Sadhasivam
1121a0f5a630SManivannan Sadhasivam state = mhi_get_mhi_state(mhi_cntrl);
1122a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Attempting power on with EE: %s, state: %s\n",
11233a1b8e28SManivannan Sadhasivam TO_MHI_EXEC_STR(current_ee), mhi_state_str(state));
1124a0f5a630SManivannan Sadhasivam
1125a0f5a630SManivannan Sadhasivam if (state == MHI_STATE_SYS_ERR) {
1126a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
1127a0f5a630SManivannan Sadhasivam ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
1128d28cab4dSManivannan Sadhasivam MHICTRL_RESET_MASK, 0, interval_us);
1129a0f5a630SManivannan Sadhasivam if (ret) {
1130a0f5a630SManivannan Sadhasivam dev_info(dev, "Failed to reset MHI due to syserr state\n");
1131a0f5a630SManivannan Sadhasivam goto error_exit;
1132a0f5a630SManivannan Sadhasivam }
1133a0f5a630SManivannan Sadhasivam
1134a0f5a630SManivannan Sadhasivam /*
1135a0f5a630SManivannan Sadhasivam * device cleares INTVEC as part of RESET processing,
1136a0f5a630SManivannan Sadhasivam * re-program it
1137a0f5a630SManivannan Sadhasivam */
1138a0f5a630SManivannan Sadhasivam mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
1139a0f5a630SManivannan Sadhasivam }
1140a0f5a630SManivannan Sadhasivam
11411227d2a2SQiang Yu /* IRQs have been requested during probe, so we just need to enable them. */
11421227d2a2SQiang Yu enable_irq(mhi_cntrl->irq[0]);
11431227d2a2SQiang Yu
11441227d2a2SQiang Yu for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) {
11451227d2a2SQiang Yu if (mhi_event->offload_ev)
11461227d2a2SQiang Yu continue;
11471227d2a2SQiang Yu
11481227d2a2SQiang Yu enable_irq(mhi_cntrl->irq[mhi_event->irq]);
11491227d2a2SQiang Yu }
1150a0f5a630SManivannan Sadhasivam
1151a0f5a630SManivannan Sadhasivam /* Transition to next state */
1152a0f5a630SManivannan Sadhasivam next_state = MHI_IN_PBL(current_ee) ?
1153a0f5a630SManivannan Sadhasivam DEV_ST_TRANSITION_PBL : DEV_ST_TRANSITION_READY;
1154a0f5a630SManivannan Sadhasivam
1155a0f5a630SManivannan Sadhasivam mhi_queue_state_transition(mhi_cntrl, next_state);
1156a0f5a630SManivannan Sadhasivam
1157a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
1158a0f5a630SManivannan Sadhasivam
1159a0f5a630SManivannan Sadhasivam dev_info(dev, "Power on setup success\n");
1160a0f5a630SManivannan Sadhasivam
1161a0f5a630SManivannan Sadhasivam return 0;
1162a0f5a630SManivannan Sadhasivam
1163a0f5a630SManivannan Sadhasivam error_exit:
1164a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = MHI_PM_DISABLE;
1165a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
1166a0f5a630SManivannan Sadhasivam
1167a0f5a630SManivannan Sadhasivam return ret;
1168a0f5a630SManivannan Sadhasivam }
1169a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_async_power_up);
1170a0f5a630SManivannan Sadhasivam
mhi_power_down(struct mhi_controller * mhi_cntrl,bool graceful)1171a0f5a630SManivannan Sadhasivam void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
1172a0f5a630SManivannan Sadhasivam {
1173a0f5a630SManivannan Sadhasivam enum mhi_pm_state cur_state, transition_state;
1174a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
1175a0f5a630SManivannan Sadhasivam
1176a0f5a630SManivannan Sadhasivam mutex_lock(&mhi_cntrl->pm_mutex);
1177a0f5a630SManivannan Sadhasivam write_lock_irq(&mhi_cntrl->pm_lock);
1178a0f5a630SManivannan Sadhasivam cur_state = mhi_cntrl->pm_state;
1179a0f5a630SManivannan Sadhasivam if (cur_state == MHI_PM_DISABLE) {
1180a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
1181a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
1182a0f5a630SManivannan Sadhasivam return; /* Already powered down */
1183a0f5a630SManivannan Sadhasivam }
1184a0f5a630SManivannan Sadhasivam
1185a0f5a630SManivannan Sadhasivam /* If it's not a graceful shutdown, force MHI to linkdown state */
1186a0f5a630SManivannan Sadhasivam transition_state = (graceful) ? MHI_PM_SHUTDOWN_PROCESS :
1187a0f5a630SManivannan Sadhasivam MHI_PM_LD_ERR_FATAL_DETECT;
1188a0f5a630SManivannan Sadhasivam
1189a0f5a630SManivannan Sadhasivam cur_state = mhi_tryset_pm_state(mhi_cntrl, transition_state);
1190a0f5a630SManivannan Sadhasivam if (cur_state != transition_state) {
1191a0f5a630SManivannan Sadhasivam dev_err(dev, "Failed to move to state: %s from: %s\n",
1192a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(transition_state),
1193a0f5a630SManivannan Sadhasivam to_mhi_pm_state_str(mhi_cntrl->pm_state));
1194a0f5a630SManivannan Sadhasivam /* Force link down or error fatal detected state */
1195a0f5a630SManivannan Sadhasivam mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
1196a0f5a630SManivannan Sadhasivam }
1197a0f5a630SManivannan Sadhasivam
1198a0f5a630SManivannan Sadhasivam /* mark device inactive to avoid any further host processing */
1199a0f5a630SManivannan Sadhasivam mhi_cntrl->ee = MHI_EE_DISABLE_TRANSITION;
1200a0f5a630SManivannan Sadhasivam mhi_cntrl->dev_state = MHI_STATE_RESET;
1201a0f5a630SManivannan Sadhasivam
1202a0f5a630SManivannan Sadhasivam wake_up_all(&mhi_cntrl->state_event);
1203a0f5a630SManivannan Sadhasivam
1204a0f5a630SManivannan Sadhasivam write_unlock_irq(&mhi_cntrl->pm_lock);
1205a0f5a630SManivannan Sadhasivam mutex_unlock(&mhi_cntrl->pm_mutex);
1206a0f5a630SManivannan Sadhasivam
1207a0f5a630SManivannan Sadhasivam mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
1208a0f5a630SManivannan Sadhasivam
1209a0f5a630SManivannan Sadhasivam /* Wait for shutdown to complete */
1210a0f5a630SManivannan Sadhasivam flush_work(&mhi_cntrl->st_worker);
1211a0f5a630SManivannan Sadhasivam
12121227d2a2SQiang Yu disable_irq(mhi_cntrl->irq[0]);
1213a0f5a630SManivannan Sadhasivam }
1214a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_power_down);
1215a0f5a630SManivannan Sadhasivam
mhi_sync_power_up(struct mhi_controller * mhi_cntrl)1216a0f5a630SManivannan Sadhasivam int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
1217a0f5a630SManivannan Sadhasivam {
1218a0f5a630SManivannan Sadhasivam int ret = mhi_async_power_up(mhi_cntrl);
1219a0f5a630SManivannan Sadhasivam
1220a0f5a630SManivannan Sadhasivam if (ret)
1221a0f5a630SManivannan Sadhasivam return ret;
1222a0f5a630SManivannan Sadhasivam
1223a0f5a630SManivannan Sadhasivam wait_event_timeout(mhi_cntrl->state_event,
1224a0f5a630SManivannan Sadhasivam MHI_IN_MISSION_MODE(mhi_cntrl->ee) ||
1225a0f5a630SManivannan Sadhasivam MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
1226a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
1227a0f5a630SManivannan Sadhasivam
1228a0f5a630SManivannan Sadhasivam ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT;
1229a0f5a630SManivannan Sadhasivam if (ret)
1230a0f5a630SManivannan Sadhasivam mhi_power_down(mhi_cntrl, false);
1231a0f5a630SManivannan Sadhasivam
1232a0f5a630SManivannan Sadhasivam return ret;
1233a0f5a630SManivannan Sadhasivam }
1234a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL(mhi_sync_power_up);
1235a0f5a630SManivannan Sadhasivam
mhi_force_rddm_mode(struct mhi_controller * mhi_cntrl)1236a0f5a630SManivannan Sadhasivam int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
1237a0f5a630SManivannan Sadhasivam {
1238a0f5a630SManivannan Sadhasivam struct device *dev = &mhi_cntrl->mhi_dev->dev;
1239a0f5a630SManivannan Sadhasivam int ret;
1240a0f5a630SManivannan Sadhasivam
1241a0f5a630SManivannan Sadhasivam /* Check if device is already in RDDM */
1242a0f5a630SManivannan Sadhasivam if (mhi_cntrl->ee == MHI_EE_RDDM)
1243a0f5a630SManivannan Sadhasivam return 0;
1244a0f5a630SManivannan Sadhasivam
1245a0f5a630SManivannan Sadhasivam dev_dbg(dev, "Triggering SYS_ERR to force RDDM state\n");
1246a0f5a630SManivannan Sadhasivam mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
1247a0f5a630SManivannan Sadhasivam
1248a0f5a630SManivannan Sadhasivam /* Wait for RDDM event */
1249a0f5a630SManivannan Sadhasivam ret = wait_event_timeout(mhi_cntrl->state_event,
1250a0f5a630SManivannan Sadhasivam mhi_cntrl->ee == MHI_EE_RDDM,
1251a0f5a630SManivannan Sadhasivam msecs_to_jiffies(mhi_cntrl->timeout_ms));
1252a0f5a630SManivannan Sadhasivam ret = ret ? 0 : -EIO;
1253a0f5a630SManivannan Sadhasivam
1254a0f5a630SManivannan Sadhasivam return ret;
1255a0f5a630SManivannan Sadhasivam }
1256a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_force_rddm_mode);
1257a0f5a630SManivannan Sadhasivam
mhi_device_get(struct mhi_device * mhi_dev)1258a0f5a630SManivannan Sadhasivam void mhi_device_get(struct mhi_device *mhi_dev)
1259a0f5a630SManivannan Sadhasivam {
1260a0f5a630SManivannan Sadhasivam struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
1261a0f5a630SManivannan Sadhasivam
1262a0f5a630SManivannan Sadhasivam mhi_dev->dev_wake++;
1263a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
1264a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
1265a0f5a630SManivannan Sadhasivam mhi_trigger_resume(mhi_cntrl);
1266a0f5a630SManivannan Sadhasivam
1267a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_get(mhi_cntrl, true);
1268a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
1269a0f5a630SManivannan Sadhasivam }
1270a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_device_get);
1271a0f5a630SManivannan Sadhasivam
mhi_device_get_sync(struct mhi_device * mhi_dev)1272a0f5a630SManivannan Sadhasivam int mhi_device_get_sync(struct mhi_device *mhi_dev)
1273a0f5a630SManivannan Sadhasivam {
1274a0f5a630SManivannan Sadhasivam struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
1275a0f5a630SManivannan Sadhasivam int ret;
1276a0f5a630SManivannan Sadhasivam
1277a0f5a630SManivannan Sadhasivam ret = __mhi_device_get_sync(mhi_cntrl);
1278a0f5a630SManivannan Sadhasivam if (!ret)
1279a0f5a630SManivannan Sadhasivam mhi_dev->dev_wake++;
1280a0f5a630SManivannan Sadhasivam
1281a0f5a630SManivannan Sadhasivam return ret;
1282a0f5a630SManivannan Sadhasivam }
1283a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_device_get_sync);
1284a0f5a630SManivannan Sadhasivam
mhi_device_put(struct mhi_device * mhi_dev)1285a0f5a630SManivannan Sadhasivam void mhi_device_put(struct mhi_device *mhi_dev)
1286a0f5a630SManivannan Sadhasivam {
1287a0f5a630SManivannan Sadhasivam struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
1288a0f5a630SManivannan Sadhasivam
1289a0f5a630SManivannan Sadhasivam mhi_dev->dev_wake--;
1290a0f5a630SManivannan Sadhasivam read_lock_bh(&mhi_cntrl->pm_lock);
1291a0f5a630SManivannan Sadhasivam if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
1292a0f5a630SManivannan Sadhasivam mhi_trigger_resume(mhi_cntrl);
1293a0f5a630SManivannan Sadhasivam
1294a0f5a630SManivannan Sadhasivam mhi_cntrl->wake_put(mhi_cntrl, false);
1295a0f5a630SManivannan Sadhasivam read_unlock_bh(&mhi_cntrl->pm_lock);
1296a0f5a630SManivannan Sadhasivam }
1297a0f5a630SManivannan Sadhasivam EXPORT_SYMBOL_GPL(mhi_device_put);
1298