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