GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / bus / mhi / ep / mmio.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/bitfield.h>
8 #include <linux/io.h>
9 #include <linux/mhi_ep.h>
10
11 #include "internal.h"
12
13 u32 mhi_ep_mmio_read(struct mhi_ep_cntrl *mhi_cntrl, u32 offset)
14 {
15         return readl(mhi_cntrl->mmio + offset);
16 }
17
18 void mhi_ep_mmio_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 val)
19 {
20         writel(val, mhi_cntrl->mmio + offset);
21 }
22
23 void mhi_ep_mmio_masked_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 mask, u32 val)
24 {
25         u32 regval;
26
27         regval = mhi_ep_mmio_read(mhi_cntrl, offset);
28         regval &= ~mask;
29         regval |= (val << __ffs(mask)) & mask;
30         mhi_ep_mmio_write(mhi_cntrl, offset, regval);
31 }
32
33 u32 mhi_ep_mmio_masked_read(struct mhi_ep_cntrl *dev, u32 offset, u32 mask)
34 {
35         u32 regval;
36
37         regval = mhi_ep_mmio_read(dev, offset);
38         regval &= mask;
39         regval >>= __ffs(mask);
40
41         return regval;
42 }
43
44 void mhi_ep_mmio_get_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state *state,
45                                 bool *mhi_reset)
46 {
47         u32 regval;
48
49         regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICTRL);
50         *state = FIELD_GET(MHICTRL_MHISTATE_MASK, regval);
51         *mhi_reset = !!FIELD_GET(MHICTRL_RESET_MASK, regval);
52 }
53
54 static void mhi_ep_mmio_set_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id, bool enable)
55 {
56         u32 chid_mask, chid_shift, chdb_idx, val;
57
58         chid_shift = ch_id % 32;
59         chid_mask = BIT(chid_shift);
60         chdb_idx = ch_id / 32;
61
62         val = enable ? 1 : 0;
63
64         mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CHDB_INT_MASK_n(chdb_idx), chid_mask, val);
65
66         /* Update the local copy of the channel mask */
67         mhi_cntrl->chdb[chdb_idx].mask &= ~chid_mask;
68         mhi_cntrl->chdb[chdb_idx].mask |= val << chid_shift;
69 }
70
71 void mhi_ep_mmio_enable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
72 {
73         mhi_ep_mmio_set_chdb(mhi_cntrl, ch_id, true);
74 }
75
76 void mhi_ep_mmio_disable_chdb(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id)
77 {
78         mhi_ep_mmio_set_chdb(mhi_cntrl, ch_id, false);
79 }
80
81 static void mhi_ep_mmio_set_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl, bool enable)
82 {
83         u32 val, i;
84
85         val = enable ? MHI_CHDB_INT_MASK_n_EN_ALL : 0;
86
87         for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
88                 mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_MASK_n(i), val);
89                 mhi_cntrl->chdb[i].mask = val;
90         }
91 }
92
93 void mhi_ep_mmio_enable_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
94 {
95         mhi_ep_mmio_set_chdb_interrupts(mhi_cntrl, true);
96 }
97
98 static void mhi_ep_mmio_mask_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
99 {
100         mhi_ep_mmio_set_chdb_interrupts(mhi_cntrl, false);
101 }
102
103 bool mhi_ep_mmio_read_chdb_status_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
104 {
105         bool chdb = false;
106         u32 i;
107
108         for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++) {
109                 mhi_cntrl->chdb[i].status = mhi_ep_mmio_read(mhi_cntrl, MHI_CHDB_INT_STATUS_n(i));
110                 if (mhi_cntrl->chdb[i].status)
111                         chdb = true;
112         }
113
114         /* Return whether a channel doorbell interrupt occurred or not */
115         return chdb;
116 }
117
118 static void mhi_ep_mmio_set_erdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl, bool enable)
119 {
120         u32 val, i;
121
122         val = enable ? MHI_ERDB_INT_MASK_n_EN_ALL : 0;
123
124         for (i = 0; i < MHI_MASK_ROWS_EV_DB; i++)
125                 mhi_ep_mmio_write(mhi_cntrl, MHI_ERDB_INT_MASK_n(i), val);
126 }
127
128 static void mhi_ep_mmio_mask_erdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
129 {
130         mhi_ep_mmio_set_erdb_interrupts(mhi_cntrl, false);
131 }
132
133 void mhi_ep_mmio_enable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
134 {
135         mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
136                                   MHI_CTRL_MHICTRL_MASK, 1);
137 }
138
139 void mhi_ep_mmio_disable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
140 {
141         mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
142                                   MHI_CTRL_MHICTRL_MASK, 0);
143 }
144
145 void mhi_ep_mmio_enable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
146 {
147         mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
148                                   MHI_CTRL_CRDB_MASK, 1);
149 }
150
151 void mhi_ep_mmio_disable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl)
152 {
153         mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CTRL_INT_MASK,
154                                   MHI_CTRL_CRDB_MASK, 0);
155 }
156
157 void mhi_ep_mmio_mask_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
158 {
159         mhi_ep_mmio_disable_ctrl_interrupt(mhi_cntrl);
160         mhi_ep_mmio_disable_cmdb_interrupt(mhi_cntrl);
161         mhi_ep_mmio_mask_chdb_interrupts(mhi_cntrl);
162         mhi_ep_mmio_mask_erdb_interrupts(mhi_cntrl);
163 }
164
165 static void mhi_ep_mmio_clear_interrupts(struct mhi_ep_cntrl *mhi_cntrl)
166 {
167         u32 i;
168
169         for (i = 0; i < MHI_MASK_ROWS_CH_DB; i++)
170                 mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_CLEAR_n(i),
171                                    MHI_CHDB_INT_CLEAR_n_CLEAR_ALL);
172
173         for (i = 0; i < MHI_MASK_ROWS_EV_DB; i++)
174                 mhi_ep_mmio_write(mhi_cntrl, MHI_ERDB_INT_CLEAR_n(i),
175                                    MHI_ERDB_INT_CLEAR_n_CLEAR_ALL);
176
177         mhi_ep_mmio_write(mhi_cntrl, MHI_CTRL_INT_CLEAR,
178                            MHI_CTRL_INT_MMIO_WR_CLEAR |
179                            MHI_CTRL_INT_CRDB_CLEAR |
180                            MHI_CTRL_INT_CRDB_MHICTRL_CLEAR);
181 }
182
183 void mhi_ep_mmio_get_chc_base(struct mhi_ep_cntrl *mhi_cntrl)
184 {
185         u32 regval;
186
187         regval = mhi_ep_mmio_read(mhi_cntrl, EP_CCABAP_HIGHER);
188         mhi_cntrl->ch_ctx_host_pa = regval;
189         mhi_cntrl->ch_ctx_host_pa <<= 32;
190
191         regval = mhi_ep_mmio_read(mhi_cntrl, EP_CCABAP_LOWER);
192         mhi_cntrl->ch_ctx_host_pa |= regval;
193 }
194
195 void mhi_ep_mmio_get_erc_base(struct mhi_ep_cntrl *mhi_cntrl)
196 {
197         u32 regval;
198
199         regval = mhi_ep_mmio_read(mhi_cntrl, EP_ECABAP_HIGHER);
200         mhi_cntrl->ev_ctx_host_pa = regval;
201         mhi_cntrl->ev_ctx_host_pa <<= 32;
202
203         regval = mhi_ep_mmio_read(mhi_cntrl, EP_ECABAP_LOWER);
204         mhi_cntrl->ev_ctx_host_pa |= regval;
205 }
206
207 void mhi_ep_mmio_get_crc_base(struct mhi_ep_cntrl *mhi_cntrl)
208 {
209         u32 regval;
210
211         regval = mhi_ep_mmio_read(mhi_cntrl, EP_CRCBAP_HIGHER);
212         mhi_cntrl->cmd_ctx_host_pa = regval;
213         mhi_cntrl->cmd_ctx_host_pa <<= 32;
214
215         regval = mhi_ep_mmio_read(mhi_cntrl, EP_CRCBAP_LOWER);
216         mhi_cntrl->cmd_ctx_host_pa |= regval;
217 }
218
219 u64 mhi_ep_mmio_get_db(struct mhi_ep_ring *ring)
220 {
221         struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
222         u64 db_offset;
223         u32 regval;
224
225         regval = mhi_ep_mmio_read(mhi_cntrl, ring->db_offset_h);
226         db_offset = regval;
227         db_offset <<= 32;
228
229         regval = mhi_ep_mmio_read(mhi_cntrl, ring->db_offset_l);
230         db_offset |= regval;
231
232         return db_offset;
233 }
234
235 void mhi_ep_mmio_set_env(struct mhi_ep_cntrl *mhi_cntrl, u32 value)
236 {
237         mhi_ep_mmio_write(mhi_cntrl, EP_BHI_EXECENV, value);
238 }
239
240 void mhi_ep_mmio_clear_reset(struct mhi_ep_cntrl *mhi_cntrl)
241 {
242         mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHICTRL, MHICTRL_RESET_MASK, 0);
243 }
244
245 void mhi_ep_mmio_reset(struct mhi_ep_cntrl *mhi_cntrl)
246 {
247         mhi_ep_mmio_write(mhi_cntrl, EP_MHICTRL, 0);
248         mhi_ep_mmio_write(mhi_cntrl, EP_MHISTATUS, 0);
249         mhi_ep_mmio_clear_interrupts(mhi_cntrl);
250 }
251
252 void mhi_ep_mmio_init(struct mhi_ep_cntrl *mhi_cntrl)
253 {
254         u32 regval;
255
256         mhi_cntrl->chdb_offset = mhi_ep_mmio_read(mhi_cntrl, EP_CHDBOFF);
257         mhi_cntrl->erdb_offset = mhi_ep_mmio_read(mhi_cntrl, EP_ERDBOFF);
258
259         regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICFG);
260         mhi_cntrl->event_rings = FIELD_GET(MHICFG_NER_MASK, regval);
261         mhi_cntrl->hw_event_rings = FIELD_GET(MHICFG_NHWER_MASK, regval);
262
263         mhi_ep_mmio_reset(mhi_cntrl);
264 }
265
266 void mhi_ep_mmio_update_ner(struct mhi_ep_cntrl *mhi_cntrl)
267 {
268         u32 regval;
269
270         regval = mhi_ep_mmio_read(mhi_cntrl, EP_MHICFG);
271         mhi_cntrl->event_rings = FIELD_GET(MHICFG_NER_MASK, regval);
272         mhi_cntrl->hw_event_rings = FIELD_GET(MHICFG_NHWER_MASK, regval);
273 }