summaryrefslogtreecommitdiff
path: root/FreeRTOS/Demo/RISC-V_RV64_PolarFire_SoftConsole/polarfire_hal/platform/drivers/mss/mss_gpio/mss_gpio.h
blob: 7475d051f87c173b4066f5708662e3895ce633f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
/*******************************************************************************
 * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * 
 * PolarFire SoC Microprocessor Subsystem GPIO bare metal software driver
 * public API.
 *
 * This driver is based on SmartFusion2 MSS GPIO driver v2.1.102
 *
 */

/*=========================================================================*//**
  @mainpage PolarFire SoC MSS GPIO Bare Metal Driver

  ==============================================================================
  Introduction
  ==============================================================================
  The PolarFire SoC Microprocessor Subsystem (MSS) includes three blocks of
  general  purpose input/outputs (GPIO). The GPIO0, GPIO1 and GPIO2 blocks have
  14, 24 and 32 GPIO ports respectively. This software driver provides a set of
  functions for controlling the MSS GPIO blocks as part of a bare metal system
  where no operating system is available. This driver can be adapted for use as
  part of an operating system but the implementation of the adaptation layer
  between this driver and the operating system's driver model is outside the
  scope of this driver.
  
  ==============================================================================
  Hardware Flow Dependencies
  ==============================================================================
  The configuration of all features of the MSS GPIO peripherals is covered by
  this driver with the exception of the PolarFire SoC IOMUX configuration.
  PolarFire SoC allows multiple non-concurrent uses of some external pins
  through IOMUX configuration. This feature allows optimization of external pin
  usage by assigning external pins for use by either the microprocessor
  subsystem or the FPGA fabric. The MSS GPIO signals are routed through
  IOMUXs to the PolarFire SoC device external pins. The MSS GPIO serial
  signals may also be routed through IOMUXs to the PolarFire SoC FPGA fabric.
  For more information on IOMUX, refer to the IOMUX section of the PolarFire SoC
  Microprocessor Subsystem (MSS) User's Guide.

  The IOMUXs are configured using the PolarFire SoC MSS configurator tool. You
  must ensure that the MSS GPIO peripherals are enabled and configured in the
  PolarFire SoC MSS configurator if you wish to use them. For more information
  on IOMUXs, refer to the IOMUX section of the PolarFire SoC microprocessor
  Subsystem (MSS) User's Guide.

  On PolarFire SoC an AXI switch forms a bus matrix interconnect among multiple
  masters and multiple slaves. Five RISC-V CPUs connect to the Master ports
  M10 to M14 of the AXI switch. By default, all the APB peripherals are
  accessible on AXI-Slave 5 of the AXI switch via the AXI to AHB and AHB to APB
  bridges (referred as main APB bus). However, to support logical separation in
  the Asymmetric Multi-Processing (AMP) mode of operation, the APB peripherals
  can alternatively be accessed on the AXI-Slave 6 via the AXI to AHB and AHB to
  APB bridges (referred as the AMP APB bus).
  
  Application must make sure that the desired GPIO instance is appropriately
  configured on one of the APB bus described above by configuring the PolarFire
  SoC system registers (SYSREG) as per the application need and that the
  appropriate data structures are provided to this driver as parameter to the
  functions provided by this driver.

  The base address and register addresses are defined in this driver as
  constants. The interrupt number assignment for the MSS GPIO peripherals are
  defined as constants in the MPFS HAL. You must ensure that the latest MPFS HAL
  is included in the project settings of the SoftConsole tool chain and that it
  is generated into your project.

  ==============================================================================
  Theory of Operation
  ==============================================================================
  The MSS GPIO driver functions are grouped into the following categories:
    - Initialization
    - Configuration
    - Reading and setting GPIO state
    - Interrupt control
    
  --------------------------------
  Initialization
  --------------------------------
  The MSS GPIO driver is initialized through a call to the MSS_GPIO_init()
  function. The MSS_GPIO_init() function must be called before any other MSS
  GPIO driver functions can be called.
  
  --------------------------------
  Configuration
  --------------------------------
  Each GPIO port is individually configured through a call to the
  MSS_GPIO_config() function. Configuration includes deciding if a GPIO port
  will be used as an input, an output or both. GPIO ports configured as inputs
  can be further configured to generate interrupts based on the input's state.
  Interrupts can be level or edge sensitive. The MSS_GPIO_config_byte() function
  can be used to configure eight consecutive GPIO ports identically. The
  MSS_GPIO_config_byte() function can be used to configure all available GPIO
  ports identically.
  
  --------------------------------
  Reading and Setting GPIO State
  --------------------------------
  The state of the GPIO ports can be read and set using the following functions:
    - MSS_GPIO_get_inputs()
    - MSS_GPIO_get_outputs()
    - MSS_GPIO_set_outputs()
    - MSS_GPIO_set_output()
    - MSS_GPIO_drive_inout()
  
  --------------------------------
  Interrupt Control
  --------------------------------
  Interrupts generated by GPIO ports configured as inputs are controlled using
  the following functions:
    - MSS_GPIO_enable_irq()
    - MSS_GPIO_disable_irq()
    - MSS_GPIO_clear_irq()
    - MSS_GPIO_get_irq()
    - MSS_GPIO_enable_nondirect_irq()
    - MSS_GPIO_disable_nondirect_irq()

  The GPIO interrupts are multiplexed. Total GPIO interrupt inputs on PLIC are
  41.

  41 = (14 from GPIO0 + 24 from GPIO1 + 3 non direct interrupts)
  GPIO2 interrupts are not available by default. Setting the corresponding bit
  in GPIO_INTERRUPT_FAB_CR(31:0) system register will enable GPIO2(31:0)
  corresponding interrupt on PLIC. e.g. If GPIO_INTERRUPT_FAB_CR bit0 is set
  then GPIO2 bit0 interrupt is available on the direct input pin on the PLIC.
  In this case GPIO0 bit 0 interrupt will not be available on the direct input
  pin on the PLIC however, the GPIO0 non-direct input will be asserted as OR of
  all the GPIO0 interrupts which don't have a direct interrupt input on PLIC are
  connected to corresponding non-direct input pin. The table below explains all
  the GPIO direct and non-direct interrupt connectivity options.
  
  | PLIC | GPIO_INTERRUPT_FAB_CR = 0 | GPIO_INTERRUPT_FAB_CR = 1 |
  |------|---------------------------|---------------------------|
  |   0  |      GPIO0 bit 0          |        GPIO2 bit 0        |
  |   1  |      GPIO0 bit 1          |        GPIO2 bit 1        |
  |  ... |         ...               |        ...                |
  |  12  |      GPIO0 bit 12         |        GPIO2 bit 12       |
  |  13  |      GPIO0 bit 13         |        GPIO2 bit 13       |
  |  14  |      GPIO1 bit 0          |        GPIO2 bit 14       |
  |  15  |      GPIO1 bit 1          |        GPIO2 bit 15       |
  |  ... |          ...              |             ...           |
  |  30  |      GPIO1  bit 16        |        GPIO2 bit 30       |
  |  31  |      GPIO1  bit 17        |        GPIO2 bit 31       |
  
  
  | PLIC |                           Interrupt source                           |
  |------|----------------------------------------------------------------------|
  |  32  |                             GPIO1 bit 18                             |
  |  33  |                             GPIO1 bit 19                             |
  |  34  |                             GPIO1 bit 20                             |
  |  35  |                             GPIO1 bit 21                             |
  |  36  |                             GPIO1 bit 22                             |
  |  37  |                             GPIO1 bit 23                             |
  |  38  | OR of all GPIO0 interrupts who don't have a direct connection enabled|
  |  39  | OR of all GPIO1 interrupts who don't have a direct connection enabled|
  |  40  | OR of all GPIO2 interrupts who don't have a direct connection enabled|


    NOTE: GPIO_INTERRUPT_FAB_CR controls the multiplexing in above table. It is
    your responsibility to set up the GPIO_INTERRUPT_FAB_CR bits in application
    code. you must make sure that you are using the valid combination of
    GPIO0/1/2 interrupt per above table.

 *//*=========================================================================*/
#ifndef MSS_GPIO_H_
#define MSS_GPIO_H_

#ifdef __cplusplus
extern "C" {
#endif 

#include <stdint.h>

/*-------------------------------------------------------------------------*//**
  The mss_gpio_id_t enumeration is used to identify individual GPIO ports as an
  argument to functions:
    - MSS_GPIO_config()
    - MSS_GPIO_set_output() and MSS_GPIO_drive_inout()
    - MSS_GPIO_enable_irq(), MSS_GPIO_disable_irq() and MSS_GPIO_clear_irq()
    
  Note that the GPIO0, GPIO1 and GPIO2 blocks have 14, 24 and 32 GPIO ports
  respectively.
 */
typedef enum mss_gpio_id
{
    MSS_GPIO_0 = 0,
    MSS_GPIO_1 = 1,
    MSS_GPIO_2 = 2,
    MSS_GPIO_3 = 3,
    MSS_GPIO_4 = 4,
    MSS_GPIO_5 = 5,
    MSS_GPIO_6 = 6,
    MSS_GPIO_7 = 7,
    MSS_GPIO_8 = 8,
    MSS_GPIO_9 = 9,
    MSS_GPIO_10 = 10,
    MSS_GPIO_11 = 11,
    MSS_GPIO_12 = 12,
    MSS_GPIO_13 = 13,
    MSS_GPIO_14 = 14,
    MSS_GPIO_15 = 15,
    MSS_GPIO_16 = 16,
    MSS_GPIO_17 = 17,
    MSS_GPIO_18 = 18,
    MSS_GPIO_19 = 19,
    MSS_GPIO_20 = 20,
    MSS_GPIO_21 = 21,
    MSS_GPIO_22 = 22,
    MSS_GPIO_23 = 23,
    MSS_GPIO_24 = 24,
    MSS_GPIO_25 = 25,
    MSS_GPIO_26 = 26,
    MSS_GPIO_27 = 27,
    MSS_GPIO_28 = 28,
    MSS_GPIO_29 = 29,
    MSS_GPIO_30 = 30,
    MSS_GPIO_31 = 31
} mss_gpio_id_t;

/*-------------------------------------------------------------------------*//**
  The mss_gpio_inout_state_t enumeration is used to specify the output state of
  an INOUT GPIO port as an argument to the MSS_GPIO_drive_inout() function.
 */
typedef enum mss_gpio_inout_state
{
    MSS_GPIO_DRIVE_LOW = 0,
    MSS_GPIO_DRIVE_HIGH,
    MSS_GPIO_HIGH_Z
} mss_gpio_inout_state_t;

/*-------------------------------------------------------------------------*//**
  The mss_gpio_byte_num_t enumeration is used to specify the set of the 8
  consecutive GPIO ports that are to be configured as an argument to the
  MSS_GPIO_config_byte() function.
 */
typedef enum mss_gpio_byte_num
{
    MSS_GPIO_BYTE_0 = 0,
    MSS_GPIO_BYTE_1,
    MSS_GPIO_BYTE_2,
    MSS_GPIO_BYTE_3,
    MSS_GPIO_BYTE_INVALID,
} mss_gpio_byte_num_t;

/*-------------------------------------------------------------------------*//**
  GPIO Instance Identification
  ============================
  These constants are provided for the application use. These constants must be
  passed as a first parameter of all the APIs provided by this driver. The
  GPIO0_LO, GPIO1_LO, GPIO2_LO represent the GPIO0, GPIO1 and GPIO2 hardware
  blocks when they are connected on the main APB bus. The GPIO0_HI, GPIO1_HI,
  GPIO2_HI represent the GPIO0, GPIO1 and GPIO2 hardware blocks when they are
  connected on the AMP APB bus.
  
  | Constant | Description                           |
  |----------|---------------------------------------|
  | GPIO0_LO | GPIO0 block connected on main APB bus |
  | GPIO1_LO | GPIO1 block connected on main APB bus |
  | GPIO2_LO | GPIO2 block connected on main APB bus |
  | GPIO0_HI | GPIO0 block connected on AMP APB bus  |
  | GPIO1_HI | GPIO1 block connected on AMP APB bus  |
  | GPIO2_HI | GPIO2 block connected on AMP APB bus  |
  
 */
#define GPIO0_LO                        ((GPIO_TypeDef*)0x20120000UL)
#define GPIO1_LO                        ((GPIO_TypeDef*)0x20121000UL)
#define GPIO2_LO                        ((GPIO_TypeDef*)0x20122000UL)
#define GPIO0_HI                        ((GPIO_TypeDef*)0x28120000UL)
#define GPIO1_HI                        ((GPIO_TypeDef*)0x28121000UL)
#define GPIO2_HI                        ((GPIO_TypeDef*)0x28122000UL)

/*-------------------------------------------------------------------------*//**
  GPIO Port Masks
  ===============
  These constant definitions are used as an argument to the
  MSS_GPIO_set_outputs() function to identify GPIO ports. A logical OR of these
  constants can be used to specify multiple GPIO ports.
  These definitions can also be used to identify GPIO ports through logical
  operations on the return value of the MSS_GPIO_get_inputs() function.
  
  | Constant         | Description           |
  |------------------|-----------------------|
  | MSS_GPIO_0_MASK  | GPIO port 0-bit mask  |
  | MSS_GPIO_1_MASK  | GPIO port 1-bit mask  |
  | MSS_GPIO_2_MASK  | GPIO port 2-bit mask  |
  | MSS_GPIO_3_MASK  | GPIO port 3-bit mask  |
  | MSS_GPIO_4_MASK  | GPIO port 4-bit mask  |
  | MSS_GPIO_5_MASK  | GPIO port 5-bit mask  |
  | MSS_GPIO_6_MASK  | GPIO port 6-bit mask  |
  | MSS_GPIO_7_MASK  | GPIO port 7-bit mask  |
  | MSS_GPIO_8_MASK  | GPIO port 8-bit mask  |
  | MSS_GPIO_9_MASK  | GPIO port 9-bit mask  |
  | MSS_GPIO_10_MASK | GPIO port 10-bit mask |
  | MSS_GPIO_11_MASK | GPIO port 11-bit mask |
  | MSS_GPIO_12_MASK | GPIO port 12-bit mask |
  | MSS_GPIO_13_MASK | GPIO port 13-bit mask |
  | MSS_GPIO_14_MASK | GPIO port 14-bit mask |
  | MSS_GPIO_15_MASK | GPIO port 15-bit mask |
  | MSS_GPIO_16_MASK | GPIO port 16-bit mask |
  | MSS_GPIO_17_MASK | GPIO port 17-bit mask |
  | MSS_GPIO_18_MASK | GPIO port 18-bit mask |
  | MSS_GPIO_19_MASK | GPIO port 19-bit mask |
  | MSS_GPIO_20_MASK | GPIO port 20-bit mask |
  | MSS_GPIO_21_MASK | GPIO port 21-bit mask |
  | MSS_GPIO_22_MASK | GPIO port 22-bit mask |
  | MSS_GPIO_23_MASK | GPIO port 23-bit mask |
  | MSS_GPIO_24_MASK | GPIO port 24-bit mask |
  | MSS_GPIO_25_MASK | GPIO port 25-bit mask |
  | MSS_GPIO_26_MASK | GPIO port 26-bit mask |
  | MSS_GPIO_27_MASK | GPIO port 27-bit mask |
  | MSS_GPIO_28_MASK | GPIO port 28-bit mask |
  | MSS_GPIO_29_MASK | GPIO port 29-bit mask |
  | MSS_GPIO_30_MASK | GPIO port 30-bit mask |
  | MSS_GPIO_31_MASK | GPIO port 31-bit mask |
  
 */
#define MSS_GPIO_0_MASK         0x00000001UL
#define MSS_GPIO_1_MASK         0x00000002UL
#define MSS_GPIO_2_MASK         0x00000004UL
#define MSS_GPIO_3_MASK         0x00000008UL
#define MSS_GPIO_4_MASK         0x00000010UL
#define MSS_GPIO_5_MASK         0x00000020UL
#define MSS_GPIO_6_MASK         0x00000040UL
#define MSS_GPIO_7_MASK         0x00000080UL
#define MSS_GPIO_8_MASK         0x00000100UL
#define MSS_GPIO_9_MASK         0x00000200UL
#define MSS_GPIO_10_MASK        0x00000400UL
#define MSS_GPIO_11_MASK        0x00000800UL
#define MSS_GPIO_12_MASK        0x00001000UL
#define MSS_GPIO_13_MASK        0x00002000UL
#define MSS_GPIO_14_MASK        0x00004000UL
#define MSS_GPIO_15_MASK        0x00008000UL
#define MSS_GPIO_16_MASK        0x00010000UL
#define MSS_GPIO_17_MASK        0x00020000UL
#define MSS_GPIO_18_MASK        0x00040000UL
#define MSS_GPIO_19_MASK        0x00080000UL
#define MSS_GPIO_20_MASK        0x00100000UL
#define MSS_GPIO_21_MASK        0x00200000UL
#define MSS_GPIO_22_MASK        0x00400000UL
#define MSS_GPIO_23_MASK        0x00800000UL
#define MSS_GPIO_24_MASK        0x01000000UL
#define MSS_GPIO_25_MASK        0x02000000UL
#define MSS_GPIO_26_MASK        0x04000000UL
#define MSS_GPIO_27_MASK        0x08000000UL
#define MSS_GPIO_28_MASK        0x10000000UL
#define MSS_GPIO_29_MASK        0x20000000UL
#define MSS_GPIO_30_MASK        0x40000000UL
#define MSS_GPIO_31_MASK        0x80000000UL

/*-------------------------------------------------------------------------*//**
  GPIO Port I/O Mode
  ==================
  These constant definitions are used as an argument to the MSS_GPIO_config()
  function to specify the I/O mode of each GPIO port.
  
  | Constant             | Description                |
  |----------------------|----------------------------|
  | MSS_GPIO_INPUT_MODE  | Input port only            |
  | MSS_GPIO_OUTPUT_MODE | Output port only           |
  | MSS_GPIO_INOUT_MODE  | Both input and output port |
  
 */
#define MSS_GPIO_INPUT_MODE              0x0000000002UL
#define MSS_GPIO_OUTPUT_MODE             0x0000000005UL
#define MSS_GPIO_INOUT_MODE              0x0000000003UL

/*-------------------------------------------------------------------------*//**
  GPIO Interrupt Mode
  ===================
  These constant definitions are used as an argument to the MSS_GPIO_config()
  function to specify the interrupt mode of each GPIO port.
  
  | Constant                   | Description                                         |
  |----------------------------|-----------------------------------------------------|
  | MSS_GPIO_IRQ_LEVEL_HIGH    | Interrupt on GPIO input level High                  |
  | MSS_GPIO_IRQ_LEVEL_LOW     | Interrupt on GPIO input level Low                   |
  | MSS_GPIO_IRQ_EDGE_POSITIVE | Interrupt on GPIO input positive edge               |
  | MSS_GPIO_IRQ_EDGE_NEGATIVE | Interrupt on GPIO input negative edge               |
  | MSS_GPIO_IRQ_EDGE_BOTH     | Interrupt on GPIO input positive and negative edges |
  
 */
#define MSS_GPIO_IRQ_LEVEL_HIGH           0x0000000000UL
#define MSS_GPIO_IRQ_LEVEL_LOW            0x0000000020UL
#define MSS_GPIO_IRQ_EDGE_POSITIVE        0x0000000040UL
#define MSS_GPIO_IRQ_EDGE_NEGATIVE        0x0000000060UL
#define MSS_GPIO_IRQ_EDGE_BOTH            0x0000000080UL

/*------------------------Private data structures-----------------------------*/
/*----------------------------------- GPIO -----------------------------------*/
/*----------------------------------------------------------------------------*/
typedef struct
{
    volatile uint32_t GPIO_CFG[32];
    volatile uint32_t GPIO_IRQ;
    volatile const uint32_t GPIO_IN;
    volatile uint32_t GPIO_OUT;
    volatile uint32_t GPIO_CFG_ALL;
    volatile uint32_t GPIO_CFG_BYTE[4];
    volatile uint32_t GPIO_CLR_BITS;
    volatile uint32_t GPIO_SET_BITS;

} GPIO_TypeDef;

/*--------------------------------Public APIs---------------------------------*/

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_init() function initializes the PolarFire SoC MSS GPIO block. It
  resets the MSS GPIO hardware block and it also clears any pending MSS GPIO
  interrupts in the  interrupt controller. When the function exits,
  it takes the MSS GPIO block out of reset.
  
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured
    
  @return
    This function does not return a value.

  Example:
  @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config(GPIO0_LO, MSS_GPIO_4, MSS_GPIO_INPUT_MODE |
                        MSS_GPIO_IRQ_EDGE_POSITIVE );
        return (0u);
      }
  @endcode
 */
void MSS_GPIO_init
(
    GPIO_TypeDef * gpio
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_config() function is used to configure an individual GPIO port.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured
 
  @param port_id
    The port_id parameter identifies the GPIO port to be configured. An
    enumeration item of the form MSS_GPIO_n, where n is the number of the GPIO
    port, is used to identify the GPIO port. For example, MSS_GPIO_0 identifies
    the first GPIO port and MSS_GPIO_31 is the last one.
    
  @param config
    The config parameter specifies the configuration to be applied to the GPIO
    port identified by the port_id parameter. It is a logical OR of the required
    I/O mode and the required interrupt mode. The interrupt mode is not relevant
    if the GPIO is configured as an output only.
       These I/O mode constants are allowed:
           - MSS_GPIO_INPUT_MODE
           - MSS_GPIO_OUTPUT_MODE
           - MSS_GPIO_INOUT_MODE
       These interrupt mode constants are allowed:
           - MSS_GPIO_IRQ_LEVEL_HIGH
           - MSS_GPIO_IRQ_LEVEL_LOW
           - MSS_GPIO_IRQ_EDGE_POSITIVE
           - MSS_GPIO_IRQ_EDGE_NEGATIVE
           - MSS_GPIO_IRQ_EDGE_BOTH
  
   @return
    This function does not return any value.

  Example:
  The following call will configure GPIO 4 on GPIO0 hardware block on main APB
  bus as an input generating interrupts on a Low to High transition of the input
  @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config(GPIO0_LO, MSS_GPIO_4, MSS_GPIO_INPUT_MODE |
                        MSS_GPIO_IRQ_EDGE_POSITIVE );
        return (0u);
      }
  @endcode
 */
void MSS_GPIO_config
(
    GPIO_TypeDef * gpio,
    mss_gpio_id_t port_id,
    uint32_t config
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_set_outputs() function is used to set the state of all GPIO ports
  configured as outputs.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param value
    The value parameter specifies the state of the GPIO ports configured as
    outputs. It is a bit mask of the form (MSS_GPIO_n_MASK | MSS_GPIO_m_MASK)
    where n and m are numbers identifying GPIOs. For example, (MSS_GPIO_0_MASK |
    MSS_GPIO_1_MASK | MSS_GPIO_2_MASK ) specifies that the first, second and
    third GPIO outputs must be set High and all other GPIO outputs set Low. The
    driver provides 32 mask constants, MSS_GPIO_0_MASK to MSS_GPIO_31_MASK
    inclusive, for this purpose.
  
  @return
    This function does not return any value.

  Example 1:
    Set GPIOs outputs 0 and 8 high and all other GPIO outputs low.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config(GPIO0_LO, MSS_GPIO_4, MSS_GPIO_INPUT_MODE |
                        MSS_GPIO_IRQ_EDGE_POSITIVE );
        MSS_GPIO_set_outputs(GPIO0_LO, MSS_GPIO_0_MASK | MSS_GPIO_8_MASK );
        return (0u);
      }

    @endcode

  Example 2:
    Set GPIOs outputs 2 and 4 low without affecting other GPIO outputs.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
         uint32_t gpio_outputs;
         MSS_GPIO_init(GPIO0_LO);
         MSS_GPIO_config(GPIO0_LO, MSS_GPIO_4, MSS_GPIO_INPUT_MODE |
                          MSS_GPIO_IRQ_EDGE_POSITIVE );

         gpio_outputs = MSS_GPIO_get_outputs();
         gpio_outputs &= ~( MSS_GPIO_2_MASK | MSS_GPIO_4_MASK );
         MSS_GPIO_set_outputs(GPIO0_LO, gpio_outputs );

         return (0u);
      }

    @endcode

  @see MSS_GPIO_get_outputs()
 */
static inline void
MSS_GPIO_set_outputs
(
    GPIO_TypeDef * gpio,
    uint32_t value
)
{
    gpio->GPIO_OUT = value;
}

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_config_all() function is used to configure all the ports of the
  GPIO block. This function will apply the same configuration values to all the
  GPIO ports.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param config
    The config parameter specifies the configuration to be applied to the all
    the GPIO ports. It is a logical OR of the required I/O mode and the required
    interrupt mode. The interrupt mode is not relevant if the GPIO is configured
    as an output only.
       These I/O mode constants are allowed:
           - MSS_GPIO_INPUT_MODE
           - MSS_GPIO_OUTPUT_MODE
           - MSS_GPIO_INOUT_MODE
       These interrupt mode constants are allowed:
           - MSS_GPIO_IRQ_LEVEL_HIGH
           - MSS_GPIO_IRQ_LEVEL_LOW
           - MSS_GPIO_IRQ_EDGE_POSITIVE
           - MSS_GPIO_IRQ_EDGE_NEGATIVE
           - MSS_GPIO_IRQ_EDGE_BOTH

  @return
    This function does not return any value.

  Example:
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INPUT_MODE |
                            MSS_GPIO_IRQ_EDGE_POSITIVE );
        return (0u);
      }
    @endcode
 */
void MSS_GPIO_config_all
(
    GPIO_TypeDef * gpio,
    uint32_t config
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_config_byte() function is used to byte wise (consecutive 8 ports)
  configure the gpio ports.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured.

  @param byte_num
    The byte_num parameter specifies the byte (consecutive 8 ports) which needs
    to be configured. The value 0 indicates the bunch from gpio port0 to gpio
    port7. Value of 3 indicates  the bunch from gpio port25 to gpio port31.
    When you use this function, you must make sure that the gpio ports that
    you are trying to configure do exist for that GPIO hardware block.
    GPIO0 has 14 ports.GPIO1 has 24 ports.GPIO3 has 32 ports.

  @param config
    The config parameter specifies the configuration to be applied to the GPIO
    byte identified by the byte_num parameter. It is a logical OR of the
    required I/O mode and the required interrupt mode. The interrupt mode is not
    relevant if the GPIO is configured as an output only.
       These I/O mode constants are allowed:
           - MSS_GPIO_INPUT_MODE
           - MSS_GPIO_OUTPUT_MODE
           - MSS_GPIO_INOUT_MODE
       These interrupt mode constants are allowed:
           - MSS_GPIO_IRQ_LEVEL_HIGH
           - MSS_GPIO_IRQ_LEVEL_LOW
           - MSS_GPIO_IRQ_EDGE_POSITIVE
           - MSS_GPIO_IRQ_EDGE_NEGATIVE
           - MSS_GPIO_IRQ_EDGE_BOTH

  @return
    This function does not return any value.

  Example:
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_byte(GPIO0_LO, MSS_GPIO_BYTE_1, MSS_GPIO_INPUT_MODE |
                            MSS_GPIO_IRQ_EDGE_POSITIVE );
        return (0u);
      }
    @endcode
 */
void MSS_GPIO_config_byte
(
    GPIO_TypeDef * gpio,
    mss_gpio_byte_num_t byte_num,
    uint32_t config
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_set_output() function is used to set the state of a single GPIO
  port configured as an output.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param port_id
    The port_id parameter identifies the GPIO port that is to have its output
    set. An enumeration item of the form MSS_GPIO_n, where n is the number of
    the GPIO port, is used to identify the GPIO port. For example, MSS_GPIO_0
    identifies the first GPIO port and MSS_GPIO_31 is the last one.
  
  @param value
    The value parameter specifies the desired state for the GPIO output. A value
    of 0 will set the output Low and a value of 1 will set the output High.
  
  @return
    This function does not return a value.
    
  Example:
  The following call will set GPIO output 12 High, leaving all other GPIO
  outputs unaffected:
  @code
      #include "mss_gpio.h"
      int main(void)
      {
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE);
        MSS_GPIO_set_output(GPIO0_LO, MSS_GPIO_13, 1);
        return (0u);
      }
  @endcode
 */
void MSS_GPIO_set_output
(
    GPIO_TypeDef *      gpio,
    mss_gpio_id_t       port_id,
    uint8_t             value
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_get_inputs() function is used to read the current state all GPIO
  ports configured as inputs.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @return
    This function returns a 32-bit unsigned integer where each bit represents
    the state of a GPIO input. The least significant bit represents the state of
    GPIO input 0 and the most significant bit the state of GPIO input 31.

  Example:
    Read and assign the current state of the GPIO outputs to a variable.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        uint32_t gpio_inputs;
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE);
        gpio_inputs = MSS_GPIO_get_inputs(GPIO0_LO);
        return (0u);
      }
    @endcode
 */
static inline uint32_t
MSS_GPIO_get_inputs( GPIO_TypeDef const * gpio )
{
    return gpio->GPIO_IN;
}

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_enable_nondirect_irq() function is used to enable the non-direct
  interrupt input at the PLIC.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @return
    This function does not return any value.

  Example:
    Read and assign the current state of the GPIO outputs to a variable.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        uint32_t gpio_inputs;
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE |
                                        MSS_GPIO_IRQ_EDGE_POSITIVE);
        MSS_GPIO_enable_nondirect_irq(GPIO1_LO);
        return (0u);
      }

    @endcode
 */
void
MSS_GPIO_enable_nondirect_irq
(
    GPIO_TypeDef const * gpio
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_disable_nondirect_irq() function is used to disable the
  non-direct interrupt input at the PLIC.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @return
    This function does not return any value.

  Example:
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        uint32_t gpio_inputs;
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE |
                                        MSS_GPIO_IRQ_EDGE_POSITIVE);
        MSS_GPIO_disable_nondirect_irq(GPIO1_LO);
        return (0u);
      }
    @endcode
 */
void
MSS_GPIO_disable_nondirect_irq
(
    GPIO_TypeDef const * gpio
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_get_outputs() function is used to read the current state of all
  GPIO ports configured as outputs.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @return
     This function returns a 32-bit unsigned integer where each bit represents
     the state of a GPIO output. The least significant bit represents the state
     of GPIO output 0 and the most significant bit the state of GPIO output 31.

  Example:
    Read and assign the current state of the GPIO outputs to a variable.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
         uint32_t gpio_outputs;
         MSS_GPIO_init(GPIO0_LO);
         MSS_GPIO_config(GPIO0_LO, MSS_GPIO_4, MSS_GPIO_INPUT_MODE |
                          MSS_GPIO_IRQ_EDGE_POSITIVE );

         gpio_outputs = MSS_GPIO_get_outputs();
         gpio_outputs &= ~( MSS_GPIO_2_MASK | MSS_GPIO_4_MASK );
         MSS_GPIO_set_outputs(  gpio_outputs );

         return (0u);
      }
    @endcode
 */
static inline uint32_t
MSS_GPIO_get_outputs( GPIO_TypeDef const * gpio )
{
    return gpio->GPIO_OUT;
}

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_get_irq() function is used to read the current value of the IRQ
  register. The GPIO interrupts are multiplexed. The GPIO interrupts which are
  not available on the direct GPIO interrupt line on the PLIC are ORed and
  routed to the non-direct interrupt line on the PLIC for the corresponding
  GPIO hardware block. When the non-direct interrupt is asserted, this function
  can be used to determine which exact GPIO bit(s) caused the interrupt.

  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @return
     This function returns a 32-bit unsigned integer value of the IRQ register.

  Example:
    In the non-direct interrupt ISR, read the IRQ register to know which are
    the GPIO port causing the interrupt.

    @code
    uint8_t gpio2_non_direct_plic_IRQHandler(void)
    {
        uint32_t intr_num = 0;
        intr_num = MSS_GPIO_get_irq(GPIO2_LO);

        for(int cnt=0; cnt<32; cnt++)
        {
            if (1u == (intr_num & 0x00000001U))
            {
                MSS_GPIO_clear_irq(GPIO0_LO, (mss_gpio_id_t)cnt);
            }

            intr_num >>= 1u;
        }

        return EXT_IRQ_KEEP_ENABLED;
    }
    @endcode
 */
static inline uint32_t
MSS_GPIO_get_irq( GPIO_TypeDef const * gpio )
{
    return gpio->GPIO_IRQ;
}

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_drive_inout() function is used to set the output state of a
  single GPIO port configured as an INOUT. An INOUT GPIO can be in one of three
  states:
    - High
    - Low
    - High impedance
    
  An INOUT output would typically be used where several devices can drive the
  state of a shared signal line. The High and Low states are equivalent to the
  High and Low states of a GPIO configured as an output. The High impedance
  state is used to prevent the GPIO from driving its output state onto the
  signal line, while at the same time allowing the input state of the GPIO to
  be read.
  
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param port_id
    The port_id parameter identifies the GPIO port for which you want to change
    the output state. An enumeration item of the form MSS_GPIO_n, where n is the
    number of the GPIO port, is used to identify the GPIO port. For example,
    MSS_GPIO_0 identifies the first GPIO port and MSS_GPIO_31 is the last one.
    
  @param inout_state
    The inout_state parameter specifies the state of the GPIO port identified by
    the port_id parameter. Allowed values of type mss_gpio_inout_state_t are as
    follows:
        - MSS_GPIO_DRIVE_HIGH
        - MSS_GPIO_DRIVE_LOW
        - MSS_GPIO_HIGH_Z  (High impedance)
        
  @return
    This function does not return a value.

  Example:
    The call to MSS_GPIO_drive_inout() below will set the GPIO 7 output to the
    high impedance state.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        uint32_t gpio_inputs;
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE |
                                        MSS_GPIO_IRQ_EDGE_POSITIVE);
        MSS_GPIO_drive_inout(GPIO0_LO, MSS_GPIO_7, MSS_GPIO_HIGH_Z);
        return (0u);
      }
    @endcode
 */
void MSS_GPIO_drive_inout
(
    GPIO_TypeDef *          gpio,
    mss_gpio_id_t           port_id,
    mss_gpio_inout_state_t  inout_state
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_enable_irq() function is used to enable interrupt generation for
  the specified GPIO input. Interrupts are generated based on the state of the
  GPIO input and the interrupt mode configured for it by MSS_GPIO_config(). This
  function enables the corresponding GPIO direct interrupt on the PLIC as well.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param port_id
    The port_id parameter identifies the GPIO port for which you want to enable
    interrupt generation. An enumeration item of the form MSS_GPIO_n, where n is
    the number of the GPIO port, is used to identify the GPIO port. For example,
    MSS_GPIO_0 identifies the first GPIO port and MSS_GPIO_31 is the last one in
    GPIO2 block.
    
  @return
    This function does not return a value.

  Example:
    The call to MSS_GPIO_enable_irq() below will allow GPIO 8 to generate
    interrupts.
    @code
      #include "mss_gpio.h"
      int main(void)
      {
        uint32_t gpio_inputs;
        MSS_GPIO_init(GPIO0_LO);
        MSS_GPIO_config_all(GPIO0_LO, MSS_GPIO_INOUT_MODE |
                                        MSS_GPIO_IRQ_EDGE_POSITIVE);
        MSS_GPIO_enable_irq(GPIO0_LO, MSS_GPIO_8);
        return (0u);
      }

    @endcode
 */
void MSS_GPIO_enable_irq
(
    GPIO_TypeDef * gpio,
    mss_gpio_id_t port_id
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_disable_irq() function is used to disable interrupt generation
  for the specified GPIO input. This function disables the corresponding GPIO
  direct interrupt on the PLIC as well.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param port_id
    The port_id parameter identifies the GPIO port for which you want to disable
    interrupt generation. An enumeration item of the form MSS_GPIO_n, where n is
    the number of the GPIO port, is used to identify the GPIO port. For example,
    MSS_GPIO_0 identifies the first GPIO port and MSS_GPIO_31 is the last one.
 
  @return
    This function does not return a value.

  Example:
    The call to MSS_GPIO_disable_irq() below will prevent GPIO 8 from generating
    interrupts.
    @code
    MSS_GPIO_disable_irq( MSS_GPIO_8 );
    @endcode
 */
void MSS_GPIO_disable_irq
(
    GPIO_TypeDef * gpio,
    mss_gpio_id_t port_id
);

/*-------------------------------------------------------------------------*//**
  The MSS_GPIO_clear_irq() function is used to clear a pending interrupt from
  the specified GPIO input.

  Note: The MSS_GPIO_clear_irq() function must be called as part of any GPIO
        interrupt service routine (ISR) in order to prevent the same interrupt
        event retriggering a call to the GPIO ISR.
 
  @param gpio
    The gpio parameter specifies the GPIO block that needs to be configured

  @param port_id
    The port_id parameter identifies the GPIO port for which you want to clear
    the interrupt. An enumeration item of the form MSS_GPIO_n, where n is the
    number of the GPIO port, is used to identify the GPIO port. For example,
    MSS_GPIO_0 identifies the first GPIO port and MSS_GPIO_31 is the last one.
    
  @return
    This function does not return a value.

  Example:
    The example below demonstrates the use of the MSS_GPIO_clear_irq() function
    as part of the GPIO 9 interrupt service routine.  

    @code
    uint8_t gpio2_non_direct_plic_IRQHandler(void)
    {
        uint32_t intr_num = 0;
        intr_num = MSS_GPIO_get_irq(GPIO2_LO);

        for(int cnt=0; cnt<32; cnt++)
        {
            if (1u == (intr_num & 0x00000001U))
            {
                MSS_GPIO_clear_irq(GPIO0_LO, (mss_gpio_id_t)cnt);
            }

            intr_num >>= 1u;
        }

        return EXT_IRQ_KEEP_ENABLED;
    }
    @endcode
 */
void MSS_GPIO_clear_irq
(
    GPIO_TypeDef * gpio,
    mss_gpio_id_t port_id
);

#ifdef __cplusplus
}
#endif

#endif /* MSS_GPIO_H_ */