1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "bootctl_reg.h"
9 #include "hsspi_reg.h"
10 #include "mod_hsspi.h"
11 #include "qspi_api.h"
12 
13 #include <fwk_id.h>
14 #include <fwk_mm.h>
15 #include <fwk_module.h>
16 #include <fwk_status.h>
17 
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <string.h>
21 
22 struct hsspi_dev_ctx {
23     bool is_start;
24     const struct mod_hsspi_dev_config *config;
25     struct hsspi_reg *reg;
26     void *memory;
27     uint8_t slave_num;
28 };
29 
30 struct hsspi_ctx {
31     struct bootctl_reg *bootctl;
32     struct hsspi_dev_ctx *dev_ctx;
33 };
34 static struct hsspi_ctx hsspi_ctx;
35 
36 /*
37  * Local macro function
38  */
39 #define REG_MASK_WRITE(addr, mask, val) \
40     { \
41         uint32_t tmp_val = (*addr) & ~(mask); \
42         (*addr) = (tmp_val | (val & mask)); \
43     }
44 
45 /*
46  * Local functions for BOOT_CTL
47  */
set_hsel_mem(int dev_idx,struct bootctl_reg * reg)48 static void set_hsel_mem(int dev_idx, struct bootctl_reg *reg)
49 {
50     uint32_t bs;
51 
52     if (dev_idx == 0) {
53         bs = ADRDEC_HSSPI1_BIT;
54     } else {
55         bs = ADRDEC_HSSPI2_BIT;
56     }
57 
58     REG_MASK_WRITE(
59         &reg->BOOT_HSSPI, ADRDEC_HSSPIx_MASK(bs), ADRDEC_HSSPIx(HSEL_MEM, bs));
60 }
61 
62 /*
63  * Local functions
64  */
enable_module(struct hsspi_reg * reg,bool enable)65 static void enable_module(struct hsspi_reg *reg, bool enable)
66 {
67     uint32_t mes = (enable) ? MCTRL_MES(ENABLE) : 0;
68 
69     if (enable) {
70         reg->MCTRL |= MCTRL_MEN(ENABLE);
71     } else {
72         reg->MCTRL &= ~(MCTRL_MEN(ENABLE));
73     }
74 
75     while ((reg->MCTRL & MCTRL_MES_MASK) != mes) {
76     };
77 }
78 
set_pcc(struct hsspi_reg * reg,uint8_t clock_div,enum hsspi_deselect_time dsel_time)79 static void set_pcc(
80     struct hsspi_reg *reg,
81     uint8_t clock_div,
82     enum hsspi_deselect_time dsel_time)
83 {
84     uint32_t mask, val;
85 
86     mask = PCC_RD_DESEL_TIME_MASK | PCC_WR_DESEL_TIME_MASK | PCC_CLOCK_DIV_MASK;
87     val = PCC_RD_DESEL_TIME(dsel_time) | PCC_WR_DESEL_TIME(dsel_time) |
88         PCC_CLOCK_DIV(clock_div >> 1);
89 
90     /*
91      * In the command sequencer mode,
92      * all slave devices are controlled by HSSPIn_PCC0 register.
93      */
94     REG_MASK_WRITE(&reg->PCC[0], mask, val);
95 }
96 
set_clock_source(struct hsspi_reg * reg,enum hsspi_clock_select clock_sel)97 static void set_clock_source(
98     struct hsspi_reg *reg,
99     enum hsspi_clock_select clock_sel)
100 {
101     REG_MASK_WRITE(&reg->MCTRL, MCTRL_CDSS_MASK, MCTRL_CDSS(clock_sel));
102 }
103 
set_mode(struct hsspi_reg * reg,enum hsspi_mode mode)104 static void set_mode(struct hsspi_reg *reg, enum hsspi_mode mode)
105 {
106     REG_MASK_WRITE(&reg->MCTRL, MCTRL_CSEN_MASK, MCTRL_CSEN(mode));
107 }
108 
set_cscfg(struct hsspi_reg * reg,uint8_t slave_num,enum hsspi_memory_bank_size bank_size)109 static void set_cscfg(
110     struct hsspi_reg *reg,
111     uint8_t slave_num,
112     enum hsspi_memory_bank_size bank_size)
113 {
114     uint32_t mask, val;
115     uint8_t ssel0en, ssel1en, ssel2en, ssel3en;
116 
117     if (bank_size == HSSPI_MEMORY_BANK_SIZE_256M) {
118         ssel0en = ENABLE;
119         ssel1en = DISABLE;
120         ssel2en = DISABLE;
121         ssel3en = DISABLE;
122     } else if (bank_size == HSSPI_MEMORY_BANK_SIZE_128M) {
123         ssel0en = ENABLE;
124         ssel1en = (slave_num > 1) ? ENABLE : DISABLE;
125         ssel2en = DISABLE;
126         ssel3en = DISABLE;
127     } else {
128         ssel0en = ENABLE;
129         ssel1en = (slave_num > 1) ? ENABLE : DISABLE;
130         ssel2en = (slave_num > 2) ? ENABLE : DISABLE;
131         ssel3en = (slave_num > 3) ? ENABLE : DISABLE;
132     }
133 
134     mask = CSCFG_MSEL_MASK | CSCFG_SSELEN_MASK | CSCFG_BOOTEN_MASK |
135         CSCFG_SPICHG_MASK;
136     val = CSCFG_MSEL(bank_size) |
137         CSCFG_SSELEN(ssel0en, ssel1en, ssel2en, ssel3en);
138     /* BOOTEN and SPICHG are disable only */
139 
140     REG_MASK_WRITE(&reg->CSCFG, mask, val);
141 }
142 
enable_cs_access_mode(struct hsspi_reg * reg,int io_bit,bool enable)143 static void enable_cs_access_mode(
144     struct hsspi_reg *reg,
145     int io_bit,
146     bool enable)
147 {
148     uint32_t mask, val;
149 
150     mask = CSCFG_SRAM_MASK;
151 
152     if (io_bit == 1) {
153         val = CSCFG_MBM(IO_SINGLE);
154         mask |= CSCFG_MBM_MASK;
155     } else if (io_bit == 2) {
156         val = CSCFG_MBM(IO_DUAL);
157         mask |= CSCFG_MBM_MASK;
158     } else if (io_bit == 4) {
159         val = CSCFG_MBM(IO_QUAD);
160         mask |= CSCFG_MBM_MASK;
161     } else
162         val = 0;
163 
164     if (enable) {
165         val |= CSCFG_SRAM(WRITABLE);
166     } else {
167         val |= CSCFG_SRAM(READ_ONLY);
168     }
169 
170     REG_MASK_WRITE(&reg->CSCFG, mask, val);
171 }
172 
set_csitime(struct hsspi_reg * reg,uint16_t timeout)173 static void set_csitime(struct hsspi_reg *reg, uint16_t timeout)
174 {
175     REG_MASK_WRITE(&reg->CSITIME, CSITIME_MASK, timeout);
176 }
177 
get_cs_io_bit(struct hsspi_reg * reg)178 static uint8_t get_cs_io_bit(struct hsspi_reg *reg)
179 {
180     if ((reg->CSCFG & CSCFG_MBM_MASK) == CSCFG_MBM(IO_DUAL)) {
181         return 2;
182     } else if ((reg->CSCFG & CSCFG_MBM_MASK) == CSCFG_MBM(IO_QUAD)) {
183         return 4;
184     } else {
185         return 1;
186     }
187 }
188 
get_trp_io(int io_bit)189 static int get_trp_io(int io_bit)
190 {
191     if (io_bit == 1) {
192         return TRP_SINGLE;
193     } else if (io_bit == 2) {
194         return TRP_DUAL;
195     } else if (io_bit == 4) {
196         return TRP_QUAD;
197     } else {
198         return TRP_DEFAULT;
199     }
200 }
201 
get_dummy_command_len(struct hsspi_reg * reg,struct qspi_command * command)202 static uint8_t get_dummy_command_len(
203     struct hsspi_reg *reg,
204     struct qspi_command *command)
205 {
206     uint8_t dummy_cycle = command->len.dummy_cycle;
207     uint8_t dummy_cycle_per_command;
208 
209     if (dummy_cycle == 0) {
210         return 0;
211     }
212 
213     /* dummy clcycle should be even */
214     if ((dummy_cycle & 1) == 1) {
215         dummy_cycle += 1;
216     }
217 
218     /*
219      * number of dummy cycle is affected by the address phase I/O bit
220      * because hsspi has 1byte wait command only.
221      */
222     if (command->io.addr_bit != 0) {
223         dummy_cycle_per_command = 8 / command->io.addr_bit;
224     } else {
225         dummy_cycle_per_command = 8 / get_cs_io_bit(reg);
226     }
227 
228     return (dummy_cycle / dummy_cycle_per_command);
229 }
230 
get_command_len(struct hsspi_reg * reg,struct qspi_command * command)231 static uint8_t get_command_len(
232     struct hsspi_reg *reg,
233     struct qspi_command *command)
234 {
235     uint8_t len = 1; /* count of instruction phase */
236 
237     len += command->len.addr_byte;
238     len += command->len.alt_byte;
239     len += get_dummy_command_len(reg, command);
240 
241     return len;
242 }
243 
set_command_sequence(struct hsspi_reg * reg,struct qspi_command * command,uint16_t * val)244 static int set_command_sequence(
245     struct hsspi_reg *reg,
246     struct qspi_command *command,
247     uint16_t *val)
248 {
249     uint16_t dec_addr[4];
250     uint8_t i, cnt = 0;
251 
252     /* max commnad length is 8 because number of register is 8 */
253     if (8 < get_command_len(reg, command)) {
254         return FWK_E_PARAM;
255     }
256 
257     /* instruction phase */
258     val[cnt++] = SET_RAW_DATA(command->code, get_trp_io(command->io.code_bit));
259 
260     /* address phase */
261     dec_addr[0] = SET_ADDR_1BYTE(get_trp_io(command->io.addr_bit));
262     dec_addr[1] = SET_ADDR_2BYTE(get_trp_io(command->io.addr_bit));
263     dec_addr[2] = SET_ADDR_3BYTE(get_trp_io(command->io.addr_bit));
264     dec_addr[3] = SET_ADDR_4BYTE(get_trp_io(command->io.addr_bit));
265     for (i = command->len.addr_byte; i > 0; i--) {
266         val[cnt++] = dec_addr[i - 1];
267     }
268 
269     /* alternative phase */
270     for (i = command->len.alt_byte; i > 0; i--) {
271         if (i == 1 && command->alt.is_nibble) {
272             val[cnt++] = SET_ALT_NIBBLE(
273                 command->alt.data[0], get_trp_io(command->io.addr_bit));
274         } else {
275             val[cnt++] = SET_RAW_DATA(
276                 command->alt.data[i - 1], get_trp_io(command->io.addr_bit));
277         }
278     }
279 
280     /* dummy phase */
281     for (i = get_dummy_command_len(reg, command); i > 0; i--) {
282         val[cnt++] = SET_DUMMY_1BYTE(get_trp_io(command->io.addr_bit));
283     }
284 
285     /*
286      * hsspi cannot skip data phase in command sequence mode.
287      * thus, if data byte is 0, the last one except dummy should be sent by data
288      * phase
289      */
290     if (command->len.data_byte == 0 && command->len.dummy_cycle == 0) {
291         val[--cnt] = END_OF_COMMAND(TRP_DEFAULT);
292     }
293 
294     return FWK_SUCCESS;
295 }
296 
set_rdcsdc(struct hsspi_reg * reg,struct qspi_command * command)297 static int set_rdcsdc(struct hsspi_reg *reg, struct qspi_command *command)
298 {
299     uint16_t i, cs_val[8];
300     int status;
301 
302     /* initialize command sequence value */
303     for (i = 0; i < 8; i++) {
304         cs_val[i] = END_OF_COMMAND(TRP_DEFAULT);
305     }
306 
307     status = set_command_sequence(reg, command, cs_val);
308     if (status != FWK_SUCCESS) {
309         return status;
310     }
311 
312     for (i = 0; i < 4; i++) {
313         reg->RDCSDC[i] = ((uint32_t)cs_val[i * 2 + 1] << 16) | cs_val[i * 2];
314     }
315 
316     return FWK_SUCCESS;
317 }
318 
set_wrcsdc(struct hsspi_reg * reg,struct qspi_command * command)319 static int set_wrcsdc(struct hsspi_reg *reg, struct qspi_command *command)
320 {
321     uint16_t i, cs_val[8];
322     int status;
323 
324     /* initialize command sequence value */
325     for (i = 0; i < 8; i++) {
326         cs_val[i] = END_OF_COMMAND(TRP_DEFAULT);
327     }
328 
329     status = set_command_sequence(reg, command, cs_val);
330     if (status != FWK_SUCCESS) {
331         return status;
332     }
333 
334     for (i = 0; i < 4; i++) {
335         reg->WRCSDC[i] = ((uint32_t)cs_val[i * 2 + 1] << 16) | cs_val[i * 2];
336     }
337 
338     return FWK_SUCCESS;
339 }
340 
memory_access(struct hsspi_dev_ctx * ctx,uint8_t slave,uint32_t offset,void * data,uint32_t len,bool is_write)341 static void memory_access(
342     struct hsspi_dev_ctx *ctx,
343     uint8_t slave,
344     uint32_t offset,
345     void *data,
346     uint32_t len,
347     bool is_write)
348 {
349     void *slave_addr, *access_addr;
350     uint32_t bank_size, bank_mask, bank_len;
351     uint32_t access_offset, access_len, total_len;
352 
353     bank_size = MEMORY_BANK_SIZE(ctx->config->memory_bank_size);
354     bank_mask = MEMORY_BANK_MASK(ctx->config->memory_bank_size);
355     bank_len = bank_size - (offset & bank_mask);
356 
357     access_offset = offset;
358     total_len = 0;
359 
360     slave_addr = (char *)ctx->memory + bank_size * slave;
361 
362     do {
363         /* check whether beyond the boundary of current bank */
364         if ((len - total_len) < bank_len) {
365             access_len = len - total_len; /* within the bank */
366         } else {
367             access_len = bank_len; /* to boundary of bank */
368         }
369 
370         /* set extension register */
371         ctx->reg->CSAEXT = (access_offset & ~bank_mask);
372 
373         access_addr = (char *)slave_addr + (access_offset & bank_mask);
374         if (is_write) {
375             memcpy(access_addr, data, access_len);
376         } else {
377             memcpy(data, access_addr, access_len);
378         }
379 
380         data = (char *)data + access_len;
381         access_offset += access_len;
382         total_len += access_len;
383 
384         /* next bank_len is bank_size because reach to boundary of bank */
385         bank_len = bank_size;
386     } while (total_len < len);
387 }
388 
memory_read(struct hsspi_dev_ctx * ctx,uint8_t slave,uint32_t offset,void * data,uint32_t len)389 static void memory_read(
390     struct hsspi_dev_ctx *ctx,
391     uint8_t slave,
392     uint32_t offset,
393     void *data,
394     uint32_t len)
395 {
396     memory_access(ctx, slave, offset, data, len, false);
397 }
398 
memory_write(struct hsspi_dev_ctx * ctx,uint8_t slave,uint32_t offset,void * data,uint32_t len)399 static void memory_write(
400     struct hsspi_dev_ctx *ctx,
401     uint8_t slave,
402     uint32_t offset,
403     void *data,
404     uint32_t len)
405 {
406     memory_access(ctx, slave, offset, data, len, true);
407 }
408 
check_command(struct qspi_command * command)409 static int check_command(struct qspi_command *command)
410 {
411     if (command == NULL)
412         return FWK_E_PARAM;
413 
414     /* length check */
415     if (command->len.code_byte != 1) {
416         return FWK_E_PARAM;
417     }
418 
419     if (command->len.addr_byte > 4) {
420         return FWK_E_PARAM;
421     }
422 
423     if (command->len.alt_byte > 4) {
424         return FWK_E_PARAM;
425     }
426 
427     /* I/O bit check */
428     if (command->io.code_bit != 0 && command->io.code_bit != 1 &&
429         command->io.code_bit != 2 && command->io.code_bit != 4) {
430         return FWK_E_PARAM;
431     }
432 
433     if (command->io.addr_bit != 0 && command->io.addr_bit != 1 &&
434         command->io.addr_bit != 2 && command->io.addr_bit != 4) {
435         return FWK_E_PARAM;
436     }
437 
438     if (command->io.data_bit != 0 && command->io.data_bit != 1 &&
439         command->io.data_bit != 2 && command->io.data_bit != 4) {
440         return FWK_E_PARAM;
441     }
442 
443     return FWK_SUCCESS;
444 }
445 
446 /*
447  * Module APIs
448  */
set_read_command(fwk_id_t id,struct qspi_command * command)449 static int set_read_command(fwk_id_t id, struct qspi_command *command)
450 {
451     struct hsspi_dev_ctx *ctx;
452     uint8_t slave;
453 
454     ctx = hsspi_ctx.dev_ctx + fwk_id_get_element_idx(id);
455     if (!ctx->is_start) {
456         return FWK_E_STATE;
457     }
458 
459     slave = fwk_id_get_sub_element_idx(id);
460     if (slave >= ctx->slave_num || (FWK_SUCCESS != check_command(command))) {
461         return FWK_E_PARAM;
462     }
463 
464     enable_cs_access_mode(ctx->reg, command->io.data_bit, false);
465     return set_rdcsdc(ctx->reg, command);
466 }
467 
set_write_command(fwk_id_t id,struct qspi_command * command)468 static int set_write_command(fwk_id_t id, struct qspi_command *command)
469 {
470     struct hsspi_dev_ctx *ctx;
471     uint8_t slave;
472     int status;
473 
474     ctx = hsspi_ctx.dev_ctx + fwk_id_get_element_idx(id);
475     if (!ctx->is_start) {
476         return FWK_E_STATE;
477     }
478 
479     slave = fwk_id_get_sub_element_idx(id);
480     if (slave >= ctx->slave_num || (FWK_SUCCESS != check_command(command))) {
481         return FWK_E_PARAM;
482     }
483 
484     enable_cs_access_mode(ctx->reg, command->io.data_bit, true);
485     status = set_wrcsdc(ctx->reg, command);
486     if (status == FWK_SUCCESS) {
487         /*
488          * if any data except code is nothing, code is sent by data phase
489          * because hsspi cannot skip data phase.
490          */
491         if (command->len.code_byte != 0 && command->len.addr_byte == 0 &&
492             command->len.alt_byte == 0 && command->len.dummy_cycle == 0 &&
493             command->len.data_byte == 0) {
494             memory_write(ctx, slave, 0, &command->code, 1);
495         }
496     }
497     return status;
498 }
499 
read(fwk_id_t id,uint32_t offset,void * buf,uint32_t len)500 static int read(fwk_id_t id, uint32_t offset, void *buf, uint32_t len)
501 {
502     struct hsspi_dev_ctx *ctx;
503     uint8_t slave;
504 
505     ctx = hsspi_ctx.dev_ctx + fwk_id_get_element_idx(id);
506     if (!ctx->is_start) {
507         return FWK_E_STATE;
508     }
509 
510     slave = fwk_id_get_sub_element_idx(id);
511     if (slave >= ctx->slave_num || buf == NULL || len == 0) {
512         return FWK_E_PARAM;
513     }
514 
515     memory_read(ctx, slave, offset, buf, len);
516     return FWK_SUCCESS;
517 }
518 
write(fwk_id_t id,uint32_t offset,void * buf,uint32_t len)519 static int write(fwk_id_t id, uint32_t offset, void *buf, uint32_t len)
520 {
521     struct hsspi_dev_ctx *ctx;
522     uint8_t slave;
523 
524     ctx = hsspi_ctx.dev_ctx + fwk_id_get_element_idx(id);
525     if (!ctx->is_start) {
526         return FWK_E_STATE;
527     }
528 
529     slave = fwk_id_get_sub_element_idx(id);
530     if (slave >= ctx->slave_num) {
531         return FWK_E_PARAM;
532     }
533 
534     if (buf == NULL || len == 0) {
535         /* set lower 1byte of offset as data, if data size is 0 */
536         uint8_t data = (uint8_t)offset;
537         memory_write(ctx, slave, offset, &data, sizeof(data));
538     } else {
539         memory_write(ctx, slave, offset, buf, len);
540     }
541     return FWK_SUCCESS;
542 }
543 
erase(fwk_id_t id,uint32_t offset)544 static int erase(fwk_id_t id, uint32_t offset)
545 {
546     return write(id, offset, NULL, 0);
547 }
548 
csmode_init(struct hsspi_dev_ctx * ctx)549 static void csmode_init(struct hsspi_dev_ctx *ctx)
550 {
551     /* at first, stop module */
552     enable_module(ctx->reg, false);
553 
554     /* then, start setup */
555     set_pcc(ctx->reg, ctx->config->clock_div, ctx->config->deselect_time);
556     set_clock_source(ctx->reg, ctx->config->clock_sel);
557 
558     /* set memory bank and disable booten */
559     set_cscfg(ctx->reg, ctx->slave_num, ctx->config->memory_bank_size);
560 
561     /* set single I/O mode, read-only */
562     enable_cs_access_mode(ctx->reg, 1, false);
563 
564     /* set idle timeout : 256cycle (same as synquacer) */
565     set_csitime(ctx->reg, CSITIME_256);
566 
567     set_mode(ctx->reg, HSSPI_MODE_COMMAND_SEQUENCE);
568     enable_module(ctx->reg, true);
569 }
570 
initialize_csmode(fwk_id_t id)571 static int initialize_csmode(fwk_id_t id)
572 {
573     struct hsspi_dev_ctx *ctx;
574     unsigned int dev_idx;
575 
576     dev_idx = fwk_id_get_element_idx(id);
577     ctx = hsspi_ctx.dev_ctx + dev_idx;
578 
579     csmode_init(ctx);
580 
581     return FWK_SUCCESS;
582 }
583 
584 static struct qspi_api mod_hsspi_api = {
585     .set_read_command = set_read_command,
586     .set_write_command = set_write_command,
587     .read = read,
588     .write = write,
589     .erase = erase,
590     .init_csmode = initialize_csmode,
591 };
592 
593 /*
594  * Framework handlers
595  */
596 
hsspi_init(fwk_id_t module_id,unsigned int element_count,const void * data)597 static int hsspi_init(
598     fwk_id_t module_id,
599     unsigned int element_count,
600     const void *data)
601 {
602     const struct mod_hsspi_config *config;
603 
604     if (element_count == 0 || data == NULL) {
605         return FWK_E_PANIC;
606     }
607 
608     config = (const struct mod_hsspi_config *)data;
609     hsspi_ctx.bootctl = (struct bootctl_reg *)config->bootctl_base;
610     hsspi_ctx.dev_ctx =
611         fwk_mm_alloc(element_count, sizeof(hsspi_ctx.dev_ctx[0]));
612 
613     return FWK_SUCCESS;
614 }
615 
hsspi_element_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)616 static int hsspi_element_init(
617     fwk_id_t element_id,
618     unsigned int sub_element_count,
619     const void *data)
620 {
621     struct hsspi_dev_ctx *ctx;
622 
623     if (data == NULL || sub_element_count == 0 ||
624         sub_element_count > MAX_SLAVE_NUM) {
625         return FWK_E_PANIC;
626     }
627 
628     ctx = hsspi_ctx.dev_ctx + fwk_id_get_element_idx(element_id);
629 
630     ctx->is_start = false;
631 
632     ctx->config = (const struct mod_hsspi_dev_config *)data;
633     ctx->reg = (struct hsspi_reg *)ctx->config->reg_base;
634     ctx->memory = (void *)ctx->config->memory_base;
635     ctx->slave_num = (uint8_t)sub_element_count;
636 
637     return FWK_SUCCESS;
638 }
639 
hsspi_start(fwk_id_t id)640 static int hsspi_start(fwk_id_t id)
641 {
642     struct hsspi_dev_ctx *ctx;
643     unsigned int dev_idx;
644 
645     if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
646         return FWK_SUCCESS;
647     }
648 
649     dev_idx = fwk_id_get_element_idx(id);
650     ctx = hsspi_ctx.dev_ctx + dev_idx;
651 
652     /* set to BOOT_CTL register */
653     set_hsel_mem(dev_idx, hsspi_ctx.bootctl);
654 
655     csmode_init(ctx);
656 
657     ctx->is_start = true;
658 
659     return FWK_SUCCESS;
660 }
661 
hsspi_process_bind_request(fwk_id_t requester_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)662 static int hsspi_process_bind_request(
663     fwk_id_t requester_id,
664     fwk_id_t target_id,
665     fwk_id_t api_id,
666     const void **api)
667 {
668     switch (fwk_id_get_api_idx(api_id)) {
669     case QSPI_API_TYPE_DEFAULT:
670         *api = &mod_hsspi_api;
671         break;
672     default:
673         return FWK_E_PARAM;
674     }
675 
676     return FWK_SUCCESS;
677 }
678 
679 const struct fwk_module module_hsspi = {
680     .type = FWK_MODULE_TYPE_DRIVER,
681     .api_count = QSPI_API_TYPE_COUNT,
682     .init = hsspi_init,
683     .element_init = hsspi_element_init,
684     .start = hsspi_start,
685     .process_bind_request = hsspi_process_bind_request,
686 };
687