1 /*
2  * Copyright (c) 2024, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 /**
9  * \file    gpio_pl061_drv.h
10  * \brief   Driver for ARM Primecell GPIO Element
11  *          As described in DDI0190
12  *          https://developer.arm.com/documentation/ddi0190/latest/
13  */
14 
15 #ifndef GPIO_PL061_DRV_H_
16 #define GPIO_PL061_DRV_H_
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 
22 #include <stdint.h>
23 
24 #include "tfm_hal_device_header.h"
25 
26 typedef __PACKED_STRUCT {
27     __IOM   uint32_t gpiodata[256];     /*!< Offset: 0x000 (R/W) Data Register Array */
28     __IOM   uint32_t gpiodir;           /*!< Offset: 0x400 (R/W) Direction Register */
29     __IOM   uint32_t gpiois;            /*!< Offset: 0x404 (R/W) Interrupt Sense Register */
30     __IOM   uint32_t gpioibe;           /*!< Offset: 0x408 (R/W) Interrupt Both Edges Register */
31     __IOM   uint32_t gpioiev;           /*!< Offset: 0x40C (R/W) Interrupt Event Register */
32     __IOM   uint32_t gpioie;            /*!< Offset: 0x410 (R/W) Interrupt Event Register */
33     __IM    uint32_t gpioris;           /*!< Offset: 0x414 (R/O) Raw Interrupt Status Register */
34     __IM    uint32_t gpiomis;           /*!< Offset: 0x418 (R/O) Masked Interrupt Status Register */
35     __OM    uint32_t gpioic;            /*!< Offset: 0x41C (R/W) Interrupt Clear Register */
36     __IOM   uint32_t gpioafsel;         /*!< Offset: 0x420 (R/W) Mode Control Select Register */
37     const   uint32_t reserved[751];
38     __IM    uint32_t gpioperiphid[4];   /*!< Offset: 0xFE0 (R/O) Peripheral Identification */
39     __IM    uint32_t gpiocellid[4];     /*!< Offset: 0xFF0 (R/O) Primecell Identification */
40 } pl061_regblk_t;
41 
42 /**
43  * \brief           Set all GPIO to given state for every HIGH bit in mask
44  *
45  * \param[in] pdev  Pointer to the PL061 register block
46  * \param[in] mask  Bitmask for writes to be masked
47  * \param[in] pins  Bitmask for state of pins
48  */
49 __STATIC_FORCEINLINE
pl061_set_gpio(pl061_regblk_t * pdev,uint8_t mask,uint8_t pins)50 void pl061_set_gpio(pl061_regblk_t * pdev, uint8_t mask, uint8_t pins)
51 {
52     /* DDI0190: 3.3.1: Masked by address; Never write HIGH to INPUT */
53     pdev->gpiodata[mask] = pins & pdev->gpiodir;
54 }
55 
56 /**
57  * \brief           Set GPIO to HIGH state for every HIGH bit in mask
58  *
59  * \param[in] pdev  Pointer to the PL061 register block
60  * \param[in] mask  Bitmask for writes to be masked
61  */
62 __STATIC_FORCEINLINE
pl061_set_high(pl061_regblk_t * pdev,uint8_t mask)63 void pl061_set_high(pl061_regblk_t * pdev, uint8_t mask)
64 {
65     /* DDI0190: 3.3.1: Masked by address; Never write HIGH to INPUT */
66     pdev->gpiodata[mask] = UINT8_MAX & pdev->gpiodir;
67 }
68 
69 /**
70  * \brief           Set the GPIO to LOW for every HIGH bit in mask
71  *
72  * \param[in] pdev  Pointer to the PL061 register block
73  * \param[in] mask  Bitmask for writes to be masked
74  */
75 __STATIC_FORCEINLINE
gpio_set_low(pl061_regblk_t * pdev,uint8_t mask)76 void gpio_set_low(pl061_regblk_t * pdev, uint8_t mask)
77 {
78     /* DDI0190: 3.3.1: Masked by address */
79     pdev->gpiodata[mask] = 0u;
80 }
81 
82 /**
83  * \brief           Get the GPIO state for every HIGH bit in mask
84  *
85  * \param[in] pdev  Pointer to the PL061 register block
86  * \param[in] mask  Bitmask for reads to be masked
87  * \return uint8_t  Masked state of GPIO
88  */
89 __STATIC_FORCEINLINE
pl061_get_gpio(pl061_regblk_t * pdev,uint8_t mask)90 uint8_t pl061_get_gpio(pl061_regblk_t * pdev, uint8_t mask)
91 {
92     /* DDI0190: 3.3.1: Masked by address */
93     return pdev->gpiodata[mask] & UINT8_MAX; /* only 8 bit wide */
94 }
95 
96 /**
97  * \brief           Set the GPIO direction to INPUT for every HIGH bit in mask
98  *
99  * \param[in] pdev  Pointer to the PL061 register block
100  * \param[in] mask  Bitmask for GPIO to be configured as INPUT
101  */
102 __STATIC_FORCEINLINE
pl061_set_input(pl061_regblk_t * pdev,uint8_t mask)103 void pl061_set_input(pl061_regblk_t * pdev, uint8_t mask)
104 {
105     pdev->gpiodir &= ~mask;
106 }
107 
108 /**
109  * \brief           Set GPIO direction to OUTPUT for every HIGH bit in mask
110  *
111  * \param[in] pdev  Pointer to the PL061 register block
112  * \param[in] mask  Bitmask for GPIO to be configured as OUTPUT
113  */
114 __STATIC_FORCEINLINE
pl061_set_output(pl061_regblk_t * pdev,uint8_t mask)115 void pl061_set_output(pl061_regblk_t * pdev, uint8_t mask)
116 {
117     pdev->gpiodir |= mask;
118 }
119 
120 /**
121  * \brief       Get peripheral ID from hardware
122  *
123  * \param pdev  Pointer to the PL061 register block
124  * \return      uint32_t
125  */
126 uint32_t pl061_get_perifid(pl061_regblk_t * pdev);
127 
128 /**
129  * \brief           Get primecell ID from hardware
130  *
131  * \param[in] pdev  Pointer to the PL061 register block
132  * \return          uint32_t - 0xB105_F00D
133  */
134 uint32_t pl061_get_cellid(pl061_regblk_t * pdev);
135 
136 #ifdef __cplusplus
137 }
138 #endif
139 
140 #endif /* GPIO_PL061_DRV_H_ */
141