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