1 /*
2 * Copyright 2024 Microchip Technology Inc. and its subsidiaries.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stddef.h>
7 #include <stdint.h>
8
9 #include <device_mec5.h>
10 #include "mec_pcfg.h"
11 #include "mec_defs.h"
12 #include "mec_ecia_api.h"
13 #include "mec_pcr_api.h"
14 #include "mec_bclink_api.h"
15 #include "mec_retval.h"
16
17 #define MEC_BCL_GIRQ 18
18 #define MEC_BCL_BCLR_GIRQ_POS 6
19 #define MEC_BCL_BERR_GIRQ_POS 7
20 #define MEC_BCL_GIRQ_AGGR_NVIC 10
21 #define MEC_BCL_BCLR_NVIC 97
22 #define MEC_BCL_BERR_NVIC 96
23
24
25 #define MEC_BCL_BUSY_CLR_ECIA_INFO \
26 MEC5_ECIA_INFO(MEC_BCL_GIRQ, MEC_BCL_BUSY_CLR_GIRQ_POS, \
27 MEC_BCL_GIRQ_AGGR_NVIC, MEC_BCL_BUSY_CLR_NVIC)
28 #define MEC_BCL_ERR_ECIA_INFO \
29 MEC5_ECIA_INFO(MEC_BCL_GIRQ, MEC_BCL_ERR_GIRQ_POS, MEC_BCL_GIRQ_AGGR_NVIC, MEC_BCL_ERR_NVIC)
30
31 #define MEC_BCL_BCLR_DEVI_IDX 0
32 #define MEC_BCL_BERR_DEVI_IDX 1
33
34 /* BC-Link EC-only registers
35 *
36 * EC Data (WO)
37 * EC KB status (RW except OBF, IBF, and CD)
38 * KB Control RW
39 * EC AUX Data (WO). Writes clear CD bit and set IBF in KB Status
40 * PCOBF bit[0] is PCOBF (RW) cleared when Host reads Data or AUX Data.
41 */
42
43 /* ---- Public API ---- */
44
45 /* Initialize BC-Link controller
46 * BC-Link is a two pin interface (clock and data).
47 * BCL clock is derived from the AHB 48MHz clock using an 8-bit divider: Fbcl = 48MHz / (div + 1)
48 * Programming frequency divider requires putting BC-Link controller into soft reset.
49 * BC-Link controller requires 48 BC-Link clocks after soft-reset is released before it is ready
50 * and clears the BUSY read-only status. We do not wait for BUSY to clear. Also, when HW clears
51 * BUSY due to soft-reset it does NOT generate the BUSY clear interrupt.
52 */
mec_hal_bcl_init(struct mec_bcl_regs * base,uint32_t flags)53 int mec_hal_bcl_init(struct mec_bcl_regs *base, uint32_t flags)
54 {
55 uint32_t girq_bitmap = MEC_BIT(MEC_BCL_BCLR_GIRQ_POS) | MEC_BIT(MEC_BCL_BERR_GIRQ_POS);
56
57 #ifdef MEC_BCL_BASE_CHECK
58 if ((uintptr_t)base != (uintptr_t)MEC_BCL0_BASE) {
59 return MEC_RET_ERR_INVAL;
60 }
61 #endif
62
63 mec_hal_pcr_clr_blk_slp_en(MEC_PCR_BCL);
64 /* put BCL controller and bus into reset before programing and clear error status */
65 base->STATUS = MEC_BIT(MEC_BCL_STATUS_SRST_Pos) | MEC_BIT(MEC_BCL_STATUS_BCERR_Pos);
66
67 /* frequency divider can only be programmed while reset is active or not busy */
68 base->CLKSEL = (flags & MEC_BCL_CFG_FREQ_DIV_MSK) >> MEC_BCL_CFG_FREQ_DIV_POS;
69
70 mec_hal_girq_bm_clr_src(MEC_BCL_GIRQ, girq_bitmap);
71
72 /* take BC-Link controller out of reset */
73 base->STATUS &= (uint32_t)~MEC_BIT(MEC_BCL_STATUS_SRST_Pos);
74
75 return MEC_RET_OK;
76 }
77
mec_hal_bcl_soft_reset(struct mec_bcl_regs * regs,uint8_t enable)78 int mec_hal_bcl_soft_reset(struct mec_bcl_regs *regs, uint8_t enable)
79 {
80 #ifdef MEC_BCL_BASE_CHECK
81 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
82 return MEC_RET_ERR_INVAL;
83 }
84 #endif
85
86 if (enable) {
87 regs->STATUS |= MEC_BIT(MEC_BCL_STATUS_SRST_Pos);
88 } else {
89 regs->STATUS = (uint32_t)~MEC_BIT(MEC_BCL_STATUS_SRST_Pos);
90 }
91
92 return MEC_RET_OK;
93 }
94
mec_hal_bcl_get_freq(struct mec_bcl_regs * regs,uint32_t * freq_hz)95 int mec_hal_bcl_get_freq(struct mec_bcl_regs *regs, uint32_t *freq_hz)
96 {
97 #ifdef MEC_BCL_BASE_CHECK
98 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
99 return false;
100 }
101 #endif
102 if (!freq_hz) {
103 return MEC_RET_ERR_INVAL;
104 }
105
106 uint32_t fdiv = (regs->CLKSEL & 0xffu);
107
108 *freq_hz = (uint32_t)(MEC_BCL_SOURCE_CLOCK_FREQ) / (fdiv + 1u);
109
110 return MEC_RET_OK;
111 }
112
mec_hal_bcl_is_busy(struct mec_bcl_regs * regs)113 bool mec_hal_bcl_is_busy(struct mec_bcl_regs *regs)
114 {
115 #ifdef MEC_BCL_BASE_CHECK
116 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
117 return true;
118 }
119 #endif
120
121 if (regs->STATUS & MEC_BIT(MEC_BCL_STATUS_BUSY_Pos)) {
122 return true;
123 }
124
125 return false;
126 }
127
128 /* BC-Link clock frequency = Source clock frequency / (clock divider + 1) */
mec_hal_bcl_set_freq(struct mec_bcl_regs * regs,uint32_t freq_hz)129 int mec_hal_bcl_set_freq(struct mec_bcl_regs *regs, uint32_t freq_hz)
130 {
131 uint32_t clkdiv = 0; /* maximum frequency */
132
133 #ifdef MEC_BCL_BASE_CHECK
134 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
135 return false;
136 }
137 #endif
138
139 if (mec_hal_bcl_is_busy(regs)) {
140 return MEC_RET_ERR_BUSY;
141 }
142
143 if ((freq_hz < MEC_BCL_MIN_CLK_FREQ) || (freq_hz > MEC_BCL_MAX_CLK_FREQ)) {
144 return MEC_RET_ERR_INVAL;
145 }
146
147 clkdiv = MEC_BCL_SOURCE_CLOCK_FREQ / freq_hz;
148 if (clkdiv) {
149 clkdiv--;
150 }
151
152 regs->CLKSEL = (regs->CLKSEL & 0xffffff00u) | (clkdiv & 0xffu);
153
154 return MEC_RET_OK;
155 }
156
mec_hal_bcl_is_error(struct mec_bcl_regs * regs)157 bool mec_hal_bcl_is_error(struct mec_bcl_regs *regs)
158 {
159 #ifdef MEC_BCL_BASE_CHECK
160 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
161 return false;
162 }
163 #endif
164
165 if (regs->STATUS & MEC_BIT(MEC_BCL_STATUS_BCERR_Pos)) {
166 return true;
167 }
168
169 return false;
170 }
171
mec_hal_bcl_clear_error(struct mec_bcl_regs * regs)172 int mec_hal_bcl_clear_error(struct mec_bcl_regs *regs)
173 {
174 #ifdef MEC_BCL_BASE_CHECK
175 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
176 return MEC_RET_ERR_INVAL;
177 }
178 #endif
179
180 regs->STATUS |= MEC_BIT(MEC_BCL_STATUS_BCERR_Pos);
181 mec_hal_girq_bm_clr_src(MEC_BCL_GIRQ, MEC_BIT(MEC_BCL_BERR_GIRQ_POS));
182
183 return MEC_RET_OK;
184 }
185
186 /* Busy-Clear status is read-only in the BC-Link status register.
187 * We can clear the latched R/W1C GIRQ bit busy clear is connected to
188 * but it will be latched again while the controller is not busy.
189 */
mec_hal_bcl_clear_not_busy(struct mec_bcl_regs * regs)190 int mec_hal_bcl_clear_not_busy(struct mec_bcl_regs *regs)
191 {
192 #ifdef MEC_BCL_BASE_CHECK
193 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
194 return MEC_RET_ERR_INVAL;
195 }
196 #else
197 (void)regs;
198 #endif
199
200 mec_hal_girq_bm_clr_src(MEC_BCL_GIRQ, MEC_BIT(MEC_BCL_BCLR_GIRQ_POS));
201
202 return MEC_RET_OK;
203 }
204
mec_hal_bcl_intr_ctrl(struct mec_bcl_regs * regs,uint8_t msk,uint8_t enable)205 int mec_hal_bcl_intr_ctrl(struct mec_bcl_regs *regs, uint8_t msk, uint8_t enable)
206 {
207 uint32_t regmsk = 0, ien = 0;
208
209 #ifdef MEC_BCL_BASE_CHECK
210 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
211 return MEC_RET_ERR_INVAL;
212 }
213 #endif
214
215 if (msk & MEC_BIT(MEC_BCL_BCLR_IRQ_POS)) {
216 regmsk |= MEC_BIT(MEC_BCL_STATUS_BCLR_IEN_Pos);
217 if (enable) {
218 ien |= MEC_BIT(MEC_BCL_STATUS_BCLR_IEN_Pos);
219 }
220 }
221
222 if (msk & MEC_BIT(MEC_BCL_BERR_IRQ_POS)) {
223 regmsk |= MEC_BIT(MEC_BCL_STATUS_BERR_IEN_Pos);
224 if (enable) {
225 ien |= MEC_BIT(MEC_BCL_STATUS_BERR_IEN_Pos);
226 }
227 }
228
229 if (!regmsk) {
230 return MEC_RET_OK;
231 }
232
233 regs->STATUS = (regs->STATUS & (uint32_t)~regmsk) | ien;
234
235 return MEC_RET_OK;
236 }
237
mec_hal_bcl_get_target_address(struct mec_bcl_regs * regs,uint8_t * target_address)238 int mec_hal_bcl_get_target_address(struct mec_bcl_regs *regs, uint8_t *target_address)
239 {
240 #ifdef MEC_BCL_BASE_CHECK
241 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
242 return MEC_RET_ERR_INVAL;
243 }
244 #endif
245
246 if (target_address) {
247 *target_address = (uint8_t)(regs->DESTA & 0xffu);
248 }
249
250 return MEC_RET_OK;
251 }
252
mec_hal_bcl_set_target_address(struct mec_bcl_regs * regs,uint8_t target_address)253 int mec_hal_bcl_set_target_address(struct mec_bcl_regs *regs, uint8_t target_address)
254 {
255 #ifdef MEC_BCL_BASE_CHECK
256 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
257 return MEC_RET_ERR_INVAL;
258 }
259 #endif
260
261 regs->DESTA = target_address;
262
263 return MEC_RET_OK;
264 }
265
266
mec_hal_bcl_get_data(struct mec_bcl_regs * regs,uint8_t * data)267 int mec_hal_bcl_get_data(struct mec_bcl_regs *regs, uint8_t *data)
268 {
269 #ifdef MEC_BCL_BASE_CHECK
270 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
271 return MEC_RET_ERR_INVAL;
272 }
273 #endif
274
275 if (data) {
276 *data = (uint8_t)regs->DATA;
277 }
278
279 return MEC_RET_OK;
280 }
281
mec_hal_bcl_set_data(struct mec_bcl_regs * regs,uint8_t data)282 int mec_hal_bcl_set_data(struct mec_bcl_regs *regs, uint8_t data)
283 {
284 #ifdef MEC_BCL_BASE_CHECK
285 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
286 return MEC_RET_ERR_INVAL;
287 }
288 #endif
289
290 regs->DATA = data;
291
292 return MEC_RET_OK;
293 }
294
mec_hal_bcl_start(struct mec_bcl_regs * regs,uint8_t target_reg,uint8_t wrdata,uint32_t flags)295 int mec_hal_bcl_start(struct mec_bcl_regs *regs, uint8_t target_reg, uint8_t wrdata, uint32_t flags)
296 {
297 uint32_t girq_bitmap = MEC_BIT(MEC_BCL_BCLR_GIRQ_POS) | MEC_BIT(MEC_BCL_BERR_GIRQ_POS);
298
299 #ifdef MEC_BCL_BASE_CHECK
300 if ((uintptr_t)regs != (uintptr_t)MEC_BCL0_BASE) {
301 return MEC_RET_ERR_INVAL;
302 }
303 #endif
304
305 if (regs->STATUS & MEC_BIT(MEC_BCL_STATUS_BUSY_Pos)) {
306 return MEC_RET_ERR_BUSY;
307 }
308
309 regs->STATUS |= MEC_BIT(MEC_BCL_STATUS_BCERR_Pos);
310 mec_hal_girq_bm_clr_src(MEC_BCL_GIRQ, girq_bitmap);
311
312 if (flags & MEC_BCL_START_FLAG_BERR_IEN) {
313 regs->STATUS |= MEC_BIT(MEC_BCL_STATUS_BERR_IEN_Pos);
314 }
315
316 /* write target register address to BC-Link address register */
317 regs->DESTA = target_reg;
318
319 if (flags & MEC_BCL_START_FLAG_READ) {
320 regs->DATA; /* first read of data register triggers transmit of read packet */
321 } else {
322 regs->DATA = wrdata; /* write data register triggers transmit of write packet */
323 }
324
325 /* HW is busy, try clearing the GIRQ latched busy-clear status */
326 mec_hal_girq_bm_clr_src(MEC_BCL_GIRQ, MEC_BIT(MEC_BCL_BCLR_GIRQ_POS));
327
328 /* Request busy-clear(done) interrupt? */
329 if (flags & MEC_BCL_START_FLAG_BCLR_IEN) {
330 regs->STATUS |= MEC_BIT(MEC_BCL_STATUS_BCLR_IEN_Pos);
331 }
332
333 return MEC_RET_OK;
334 }
335
336 /* end mec_bclink.c */
337