GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / bus / mhi / ep / sm.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Linaro Ltd.
4  * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
5  */
6
7 #include <linux/errno.h>
8 #include <linux/mhi_ep.h>
9 #include "internal.h"
10
11 bool __must_check mhi_ep_check_mhi_state(struct mhi_ep_cntrl *mhi_cntrl,
12                                          enum mhi_state cur_mhi_state,
13                                          enum mhi_state mhi_state)
14 {
15         if (mhi_state == MHI_STATE_SYS_ERR)
16                 return true;    /* Allowed in any state */
17
18         if (mhi_state == MHI_STATE_READY)
19                 return cur_mhi_state == MHI_STATE_RESET;
20
21         if (mhi_state == MHI_STATE_M0)
22                 return cur_mhi_state == MHI_STATE_M3 || cur_mhi_state == MHI_STATE_READY;
23
24         if (mhi_state == MHI_STATE_M3)
25                 return cur_mhi_state == MHI_STATE_M0;
26
27         return false;
28 }
29
30 int mhi_ep_set_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state mhi_state)
31 {
32         struct device *dev = &mhi_cntrl->mhi_dev->dev;
33
34         if (!mhi_ep_check_mhi_state(mhi_cntrl, mhi_cntrl->mhi_state, mhi_state)) {
35                 dev_err(dev, "MHI state change to %s from %s is not allowed!\n",
36                         mhi_state_str(mhi_state),
37                         mhi_state_str(mhi_cntrl->mhi_state));
38                 return -EACCES;
39         }
40
41         /* TODO: Add support for M1 and M2 states */
42         if (mhi_state == MHI_STATE_M1 || mhi_state == MHI_STATE_M2) {
43                 dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state));
44                 return -EOPNOTSUPP;
45         }
46
47         mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK, mhi_state);
48         mhi_cntrl->mhi_state = mhi_state;
49
50         if (mhi_state == MHI_STATE_READY)
51                 mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK, 1);
52
53         if (mhi_state == MHI_STATE_SYS_ERR)
54                 mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_SYSERR_MASK, 1);
55
56         return 0;
57 }
58
59 int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
60 {
61         struct device *dev = &mhi_cntrl->mhi_dev->dev;
62         enum mhi_state old_state;
63         int ret;
64
65         /* If MHI is in M3, resume suspended channels */
66         mutex_lock(&mhi_cntrl->state_lock);
67
68         old_state = mhi_cntrl->mhi_state;
69         if (old_state == MHI_STATE_M3)
70                 mhi_ep_resume_channels(mhi_cntrl);
71
72         ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
73         if (ret) {
74                 mhi_ep_handle_syserr(mhi_cntrl);
75                 goto err_unlock;
76         }
77
78         /* Signal host that the device moved to M0 */
79         ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0);
80         if (ret) {
81                 dev_err(dev, "Failed sending M0 state change event\n");
82                 goto err_unlock;
83         }
84
85         if (old_state == MHI_STATE_READY) {
86                 /* Send AMSS EE event to host */
87                 ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS);
88                 if (ret) {
89                         dev_err(dev, "Failed sending AMSS EE event\n");
90                         goto err_unlock;
91                 }
92         }
93
94 err_unlock:
95         mutex_unlock(&mhi_cntrl->state_lock);
96
97         return ret;
98 }
99
100 int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
101 {
102         struct device *dev = &mhi_cntrl->mhi_dev->dev;
103         int ret;
104
105         mutex_lock(&mhi_cntrl->state_lock);
106
107         ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
108         if (ret) {
109                 mhi_ep_handle_syserr(mhi_cntrl);
110                 goto err_unlock;
111         }
112
113         mhi_ep_suspend_channels(mhi_cntrl);
114
115         /* Signal host that the device moved to M3 */
116         ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3);
117         if (ret) {
118                 dev_err(dev, "Failed sending M3 state change event\n");
119                 goto err_unlock;
120         }
121
122 err_unlock:
123         mutex_unlock(&mhi_cntrl->state_lock);
124
125         return ret;
126 }
127
128 int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl)
129 {
130         struct device *dev = &mhi_cntrl->mhi_dev->dev;
131         enum mhi_state mhi_state;
132         int ret, is_ready;
133
134         mutex_lock(&mhi_cntrl->state_lock);
135
136         /* Ensure that the MHISTATUS is set to RESET by host */
137         mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK);
138         is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK);
139
140         if (mhi_state != MHI_STATE_RESET || is_ready) {
141                 dev_err(dev, "READY state transition failed. MHI host not in RESET state\n");
142                 ret = -EIO;
143                 goto err_unlock;
144         }
145
146         ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
147         if (ret)
148                 mhi_ep_handle_syserr(mhi_cntrl);
149
150 err_unlock:
151         mutex_unlock(&mhi_cntrl->state_lock);
152
153         return ret;
154 }