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 ®->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(®->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(®->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(®->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(®->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(®->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(®->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