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#include <assert.h>
14#include <stdio.h>
15#include <stdbool.h>
16#include <errno.h>
17#include <camkes/io.h>
18#include <camkes/irq.h>
19#include <platsupport/io.h>
20#include <platsupport/irq.h>
21#include <utils/util.h>
22
23#include <platsupport/gpio.h>
24#include <gpiomuxserver_plat.h>
25
26#include "gpio.h"
27
28/* State management for the pins */
29typedef struct gpio_entry {
30    bool initialised;
31    seL4_Word owner;
32    gpio_t gpio;
33} gpio_entry_t;
34
35/*
36 * Gotchas:
37 *  - we assume that all boards that we support has an mux controller, if some
38 *  pins are turned off as a result of conflicting pins via the mux, we don't
39 *  actually alert the user about this, it's up to the user to not shoot
40 *  themselves in the foot
41 *  - on implementations that have a working 'set_next' function, we will
42 *  write/read partially if there are any errors in the middle of the operation
43 *  (e.g. severed link). i.e. this operation is not transactional
44 *  - the pins are first come first served, and there is no way to disable the pins
45 */
46
47/* GPIO control structure for initialising pins */
48static gpio_sys_t gpio_sys;
49/* table for keeping track of ownership of GPIO pins */
50static size_t gpio_table_size;
51static gpio_entry_t *gpio_table;
52
53/* Prototypes for these functions are not generated by the camkes templates yet */
54seL4_Word the_gpio_get_sender_id();
55
56static inline bool check_valid_gpio_id(gpio_id_t pin_id)
57{
58    return (pin_id < 0 || pin_id >= gpio_table_size) ? false : true;
59}
60
61static inline bool check_pin_initialised(gpio_id_t pin_id)
62{
63    return gpio_table[pin_id].initialised ? true : false;
64}
65
66static inline bool check_client_owns_pin(gpio_id_t pin_id, seL4_Word client_id)
67{
68    return gpio_table[pin_id].owner == client_id;
69}
70
71static inline seL4_Word get_client_id(void)
72{
73    return the_gpio_get_sender_id();
74}
75
76int the_gpio_init_pin(gpio_id_t pin_id, gpio_dir_t dir)
77{
78
79    int error = 0;
80
81    if (!check_valid_gpio_id(pin_id)) {
82        error = -EINVAL;
83        goto out;
84    }
85
86    if (GPIO_DIR_IRQ_LOW <= dir && dir <= GPIO_DIR_IRQ_EDGE) {
87        ZF_LOGE("Setting GPIO pins as interrupt sources is not currently supported");
88        error = -EINVAL;
89        goto out;
90    }
91
92    /* check if the caller owns the pin */
93    seL4_Word client_id = get_client_id();
94    if (check_client_owns_pin(pin_id, client_id)) {
95        error = 0;
96        goto out;
97    }
98
99    /* check if anyone has reserved the pin */
100    if (the_gpio_get_pin_assignee(client_id) != 0) {
101        error = -EBUSY;
102        goto out;
103    }
104
105    /* check if anyone else has the pin */
106    if (check_pin_initialised(pin_id)) {
107        error = -EBUSY;
108        goto out;
109    }
110
111    gpio_entry_t *gpio_entry = &gpio_table[pin_id];
112
113    error = gpio_new(&gpio_sys, pin_id, dir, &gpio_entry->gpio);
114    if (error) {
115        goto out;
116    }
117
118    gpio_entry->initialised = true;
119    gpio_entry->owner = client_id;
120
121out:
122    return error;
123}
124
125int the_gpio_set_level(gpio_id_t pin_id, gpio_level_t level)
126{
127
128    seL4_Word client_id = get_client_id();
129    int error = 0;
130
131    if (!check_valid_gpio_id(pin_id)) {
132        error = -EINVAL;
133        goto out;
134    }
135
136    if (!check_client_owns_pin(pin_id, client_id)) {
137        error = -EINVAL;
138        goto out;
139    }
140
141    if (level == GPIO_LEVEL_HIGH) {
142        error = gpio_set(&gpio_table[pin_id].gpio);
143    } else if (level == GPIO_LEVEL_LOW) {
144        error = gpio_clr(&gpio_table[pin_id].gpio);
145    } else {
146        /* level < 0 is not valid */
147        error = -EINVAL;
148    }
149
150out:
151    return error;
152}
153
154int the_gpio_read_level(gpio_id_t pin_id)
155{
156
157    seL4_Word client_id = get_client_id();
158    int ret = 0;
159
160    if (!check_valid_gpio_id(pin_id)) {
161        ret = -EINVAL;
162        goto out;
163    }
164
165    if (!check_client_owns_pin(pin_id, client_id)) {
166        ret = -EINVAL;
167        goto out;
168    }
169
170    ret = gpio_get(&gpio_table[pin_id].gpio);
171
172out:
173    return ret;
174}
175
176int gpio_component_init(ps_io_ops_t *io_ops)
177{
178    int error = gpio_sys_init(io_ops, &gpio_sys);
179    if (error) {
180        ZF_LOGE("Failed to initialise GPIO subsystem");
181        return error;
182    }
183
184    gpio_table_size = MAX_GPIO_ID + 1;
185    error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(*gpio_table) * gpio_table_size, (void **) &gpio_table);
186    if (error) {
187        ZF_LOGE("Failed to allocate memory for the table of GPIO pins");
188        return error;
189    }
190
191    for (int i = 0; i < gpio_table_size; i++) {
192        gpio_table[i].owner = -1;
193    }
194
195    return 0;
196}
197