1/*
2 * Copyright 2019, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#pragma once
14
15struct gpio_sys;
16typedef struct gpio_sys gpio_sys_t;
17typedef int gpio_id_t;
18
19#include <stdbool.h>
20#include <utils/util.h>
21#include <platsupport/io.h>
22
23#define GPIOID(port, pin)             ((port) * 32 + (pin))
24#define GPIOID_PORT(gpio)             ((gpio) / 32)
25#define GPIOID_PIN(gpio)              ((gpio) % 32)
26
27typedef struct gpio gpio_t;
28struct gpio {
29/// GPIO port identifier
30    gpio_id_t id;
31/// GPIO subsystem handle
32    gpio_sys_t *gpio_sys;
33};
34
35typedef enum gpio_dir {
36/// Input direction
37    GPIO_DIR_IN,
38    /* Output direction:
39     * DEFAULT_LOW will ensure that the pin stays low even while the driver is
40     * initializing the pin.
41     * DEFAULT_HIGH will ensure that the pin stays high even while the driver is
42     * initializing the pin.
43     */
44    GPIO_DIR_OUT_DEFAULT_LOW,
45    GPIO_DIR_OUT = GPIO_DIR_OUT_DEFAULT_LOW,
46    GPIO_DIR_OUT_DEFAULT_HIGH,
47
48/// Input direction with IRQ on low logic level
49    GPIO_DIR_IRQ_LOW,
50/// Input direction with IRQ on high logic level
51    GPIO_DIR_IRQ_HIGH,
52/// Input direction with IRQ on falling edge
53    GPIO_DIR_IRQ_FALL,
54/// Input direction with IRQ on rising edge
55    GPIO_DIR_IRQ_RISE,
56/// Input direction with IRQ on both rising and falling edges
57    GPIO_DIR_IRQ_EDGE
58} gpio_dir_t;
59
60typedef enum gpio_level {
61    /* GPIO input/output levels */
62    GPIO_LEVEL_LOW,
63    GPIO_LEVEL_HIGH
64} gpio_level_t;
65
66struct gpio_sys {
67    /** Initialize a GPIO pin.
68     * @param   gpio_sys        Initialized gpio driver instance.
69     * @param   id              ID of the pin to initialize a handle to.
70     * @param   gpio_dir        Configure the pin for input/output/IRQ.
71     *                          Use GPIO_DIR_OUT_DEFAULT_HIGH and
72     *                          GPIO_DIR_OUT_DEFAULT_LOW to set the pin's
73     *                          default logic level. Can be useful for things
74     *                          like GPIO pins used as SPI chipselects where
75     *                          you want to ensure that a pin stays in a certain
76     *                          logic level even while this initialization
77     *                          function is running.
78     * @param   gpio[out]       Pointer to a gpio_t structure to be initialised.
79     * @return 0 on success. Non-zero on error.
80     */
81    int (*init)(gpio_sys_t *gpio_sys, gpio_id_t id, enum gpio_dir dir, gpio_t *gpio);
82
83    /**
84     * Set a GPIO's output level. The pin must be configured for output
85     * for this to work.
86     * @param   gpio    Initialised GPIO pin instance.
87     * @param   level   The output level to set for the pin.
88     * @return 0 on success. Non-zero on error.
89     */
90    int (*set_level)(gpio_t *gpio, enum gpio_level level);
91
92    /**
93     * Read a pin's input level. The pin must be configured for input for
94     * this to work.
95     * @param   gpio    Initialised GPIO pin instance.
96     * @return GPIO_LEVEL_LOW or GPIO_LEVEL_HIGH depending on the input level.
97     *         Negative integer on error.
98     */
99    int (*read_level)(gpio_t *gpio);
100
101    /**
102     * Read and manipulate the status of a pending IRQ.
103     * @param   gpio    Initialised GPIO pin instance.
104     * @param   clear   Flag indicating whether or not the pending IRQ
105     *                  should be cleared.
106     * @return 0 (none) or 1 (pending) depending on the status of the IRQ.
107     *         Negative integer on error.
108     */
109    int (*pending_status)(gpio_t *gpio, bool clear);
110
111    /**
112     * Enable or disable the IRQ signal from the pin.
113     * @param   gpio    Initialised GPIO pin instance.
114     * @param   enable  Flag indicating whether or not the IRQ signal
115     *                  should be enabled.
116     * @return 0 on success. Non-zero on error.
117     */
118    int (*irq_enable_disable)(gpio_t *gpio, bool enable);
119
120/// platform specific private data
121    void *priv;
122};
123
124static inline bool gpio_sys_valid(const gpio_sys_t *gpio_sys)
125{
126    return gpio_sys != NULL && gpio_sys->priv != NULL;
127}
128
129static inline bool gpio_instance_valid(const gpio_t *gpio)
130{
131    if (!gpio) {
132        ZF_LOGE("Handle to GPIO not supplied!");
133        return false;
134    }
135    if (!gpio->gpio_sys) {
136        ZF_LOGE("GPIO pin's parent controller handle invalid!");
137        return false;
138    }
139    return true;
140}
141
142/**
143 * Initialise the GPIO subsystem and provide a handle for access
144 * @param[in]  io_ops   io operations for device initialisation
145 * @param[out] gpio_sys A gpio handle structure to initialise
146 * @return              0 on success, errno value otherwise
147 */
148int gpio_sys_init(ps_io_ops_t *io_ops, gpio_sys_t *gpio_sys);
149
150/**
151 * Clear a GPIO pin
152 * @param[in] a handle to a GPIO
153 * @return    0 on success, otherwise errno value
154 */
155static inline int gpio_clr(gpio_t *gpio)
156{
157    if (!gpio_instance_valid(gpio)) {
158        return -EINVAL;
159    }
160    if (!gpio->gpio_sys->set_level) {
161        ZF_LOGE("Unimplemented");
162        return -ENOSYS;
163    }
164    return gpio->gpio_sys->set_level(gpio, GPIO_LEVEL_LOW);
165}
166
167/**
168 * Return the state of a GPIO pin
169 * @param[in] a handle to a GPIO
170 * @return    the value of the pin, otherwise errno value
171 */
172static inline int gpio_get(gpio_t *gpio)
173{
174    if (!gpio_instance_valid(gpio)) {
175        return -EINVAL;
176    }
177    if (!gpio->gpio_sys->read_level) {
178        ZF_LOGE("Unimplemented");
179        return -ENOSYS;
180    }
181    return gpio->gpio_sys->read_level(gpio);
182}
183
184/**
185 * Set a GPIO pin
186 * @param[in] a handle to a GPIO
187 * @return    0 on success, otherwise errno value
188 */
189static inline int gpio_set(gpio_t *gpio)
190{
191    if (!gpio_instance_valid(gpio)) {
192        return -EINVAL;
193    }
194    if (!gpio->gpio_sys->set_level) {
195        ZF_LOGE("Unimplemented");
196        return -ENOSYS;
197    }
198    return gpio->gpio_sys->set_level(gpio, GPIO_LEVEL_HIGH);
199}
200
201/**
202 * Check if an IRQ is pending for this GPIO
203 * @param[in]  a handle to a GPIO
204 * @return     errno value on error
205 *             0 if an IRQ is not pending
206 *             1 if an IRQ is pending
207 */
208static inline int gpio_is_pending(gpio_t *gpio)
209{
210    if (!gpio_instance_valid(gpio)) {
211        return -EINVAL;
212    }
213    if (!gpio->gpio_sys->pending_status) {
214        ZF_LOGE("Unimplemented");
215        return -ENOSYS;
216    }
217    return gpio->gpio_sys->pending_status(gpio, false);
218}
219
220/**
221 * Clear pending IRQs for this GPIO
222 * @param[in] a handle to a GPIO
223 * @return    0 on success, errno value on error
224 */
225static inline int gpio_pending_clear(gpio_t *gpio)
226{
227    if (!gpio_instance_valid(gpio)) {
228        return -EINVAL;
229    }
230    if (!gpio->gpio_sys->pending_status) {
231        ZF_LOGE("Unimplemented");
232        return -ENOSYS;
233    }
234    int ret = gpio->gpio_sys->pending_status(gpio, true);
235    if (ret < 0) {
236        return ret;
237    }
238    return 0;
239}
240
241/**
242 * Enable the IRQ signal from the pin.
243 * @param[in] gpio Handle to the pin to manipulate
244 * @return 0 for success, errno value on error.
245 */
246static inline int gpio_irq_enable(gpio_t *gpio)
247{
248    if (!gpio_instance_valid(gpio)) {
249        return -EINVAL;
250    }
251    if (!gpio->gpio_sys->irq_enable_disable) {
252        ZF_LOGE("Unimplemented");
253        return -ENOSYS;
254    }
255    return gpio->gpio_sys->irq_enable_disable(gpio, true);
256}
257
258/**
259 * Disable the IRQ signal from the pin.
260 * @param[in] gpio Handle to the pin to manipulate
261 * @return 0 for success, errno value on error.
262 */
263static inline int gpio_irq_disable(gpio_t *gpio)
264{
265    if (!gpio_instance_valid(gpio)) {
266        return -EINVAL;
267    }
268    if (!gpio->gpio_sys->irq_enable_disable) {
269        ZF_LOGE("Unimplemented");
270        return -ENOSYS;
271    }
272    return gpio->gpio_sys->irq_enable_disable(gpio, false);
273}
274
275/**
276 * Acquire a handle to a GPIO pin
277 * @param[in]  gpio_sys  a handle to an initialised GPIO subsystem\
278 * @param[in]  id        A pin identifier obtained from the macro
279 *                       GPIOID(port, pin)
280 * @param[in]  dir       The direction of the pin
281 * @param[out] gpio      a GPIO handle to initialise
282 * @return               0 on success
283 */
284static inline int gpio_new(gpio_sys_t *gpio_sys, gpio_id_t id, enum gpio_dir dir, gpio_t *gpio)
285{
286    if (!gpio_sys) {
287        ZF_LOGE("Handle to GPIO controller not supplied!");
288        return -EINVAL;
289    }
290
291    if (!gpio_sys->init) {
292        ZF_LOGE("Unimplemented");
293        return -ENOSYS;
294    }
295
296    if (!gpio) {
297        ZF_LOGE("Handle to output pin structure not supplied!");
298        return -EINVAL;
299    }
300
301    return gpio_sys->init(gpio_sys, id, dir, gpio);
302}
303