blob: 6115033a434674f2f2e31f71560e35a88531085e [file] [log] [blame] [edit]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <camkes/io.h>
#include <camkes/irq.h>
#include <platsupport/io.h>
#include <platsupport/irq.h>
#include <utils/util.h>
#include <platsupport/gpio.h>
#include <gpiomuxserver_plat.h>
#include "gpio.h"
/* State management for the pins */
typedef struct gpio_entry {
bool initialised;
seL4_Word owner;
gpio_t gpio;
} gpio_entry_t;
/*
* Gotchas:
* - we assume that all boards that we support has an mux controller, if some
* pins are turned off as a result of conflicting pins via the mux, we don't
* actually alert the user about this, it's up to the user to not shoot
* themselves in the foot
* - on implementations that have a working 'set_next' function, we will
* write/read partially if there are any errors in the middle of the operation
* (e.g. severed link). i.e. this operation is not transactional
* - the pins are first come first served, and there is no way to disable the pins
*/
/* GPIO control structure for initialising pins */
static gpio_sys_t gpio_sys;
/* table for keeping track of ownership of GPIO pins */
static size_t gpio_table_size;
static gpio_entry_t *gpio_table;
/* Prototypes for these functions are not generated by the camkes templates yet */
seL4_Word the_gpio_get_sender_id();
static inline bool check_valid_gpio_id(gpio_id_t pin_id)
{
return (pin_id < 0 || pin_id >= gpio_table_size) ? false : true;
}
static inline bool check_pin_initialised(gpio_id_t pin_id)
{
return gpio_table[pin_id].initialised ? true : false;
}
static inline bool check_client_owns_pin(gpio_id_t pin_id, seL4_Word client_id)
{
return gpio_table[pin_id].owner == client_id;
}
static inline seL4_Word get_client_id(void)
{
return the_gpio_get_sender_id();
}
int the_gpio_init_pin(gpio_id_t pin_id, gpio_dir_t dir)
{
int error = 0;
if (!check_valid_gpio_id(pin_id)) {
error = -EINVAL;
goto out;
}
if (GPIO_DIR_IRQ_LOW <= dir && dir <= GPIO_DIR_IRQ_EDGE) {
ZF_LOGE("Setting GPIO pins as interrupt sources is not currently supported");
error = -EINVAL;
goto out;
}
/* check if the caller owns the pin */
seL4_Word client_id = get_client_id();
if (check_client_owns_pin(pin_id, client_id)) {
error = 0;
goto out;
}
/* check if anyone has reserved the pin */
if (the_gpio_get_pin_assignee(client_id) != 0) {
error = -EBUSY;
goto out;
}
/* check if anyone else has the pin */
if (check_pin_initialised(pin_id)) {
error = -EBUSY;
goto out;
}
gpio_entry_t *gpio_entry = &gpio_table[pin_id];
error = gpio_new(&gpio_sys, pin_id, dir, &gpio_entry->gpio);
if (error) {
goto out;
}
gpio_entry->initialised = true;
gpio_entry->owner = client_id;
out:
return error;
}
int the_gpio_set_level(gpio_id_t pin_id, gpio_level_t level)
{
seL4_Word client_id = get_client_id();
int error = 0;
if (!check_valid_gpio_id(pin_id)) {
error = -EINVAL;
goto out;
}
if (!check_client_owns_pin(pin_id, client_id)) {
error = -EINVAL;
goto out;
}
if (level == GPIO_LEVEL_HIGH) {
error = gpio_set(&gpio_table[pin_id].gpio);
} else if (level == GPIO_LEVEL_LOW) {
error = gpio_clr(&gpio_table[pin_id].gpio);
} else {
/* level < 0 is not valid */
error = -EINVAL;
}
out:
return error;
}
int the_gpio_read_level(gpio_id_t pin_id)
{
seL4_Word client_id = get_client_id();
int ret = 0;
if (!check_valid_gpio_id(pin_id)) {
ret = -EINVAL;
goto out;
}
if (!check_client_owns_pin(pin_id, client_id)) {
ret = -EINVAL;
goto out;
}
ret = gpio_get(&gpio_table[pin_id].gpio);
out:
return ret;
}
int gpio_component_init(ps_io_ops_t *io_ops)
{
int error = gpio_sys_init(io_ops, &gpio_sys);
if (error) {
ZF_LOGE("Failed to initialise GPIO subsystem");
return error;
}
gpio_table_size = MAX_GPIO_ID + 1;
error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(*gpio_table) * gpio_table_size, (void **) &gpio_table);
if (error) {
ZF_LOGE("Failed to allocate memory for the table of GPIO pins");
return error;
}
for (int i = 0; i < gpio_table_size; i++) {
gpio_table[i].owner = -1;
}
return 0;
}