154359Sroberto/* 2182007Sroberto * Copyright 2019, Data61 354359Sroberto * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4182007Sroberto * ABN 41 687 119 230. 5182007Sroberto * 654359Sroberto * This software may be distributed and modified according to the terms of 754359Sroberto * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8182007Sroberto * See "LICENSE_BSD2.txt" for details. 9182007Sroberto * 10182007Sroberto * @TAG(DATA61_BSD) 11182007Sroberto */ 12182007Sroberto 13182007Sroberto#include <stdbool.h> 14182007Sroberto#include <stddef.h> 15182007Sroberto#include <utils/util.h> 16182007Sroberto#include <platsupport/gpio_utils.h> 17182007Sroberto 18182007Sroberto/* This is forward declared in the header file. */ 19182007Srobertostruct gpio_chain { 20182007Sroberto list_t pin_list; 21182007Sroberto}; 22182007Sroberto 23182007Srobertostruct gpio_callback_token { 24182007Sroberto bool is_read; 25182007Sroberto char *buffer; 26182007Sroberto size_t bits_processed; 27182007Sroberto size_t nbits_to_process; 28182007Sroberto} 29182007Sroberto 30182007Srobertostatic int gpio_chain_comparator(void *a, void *b) 31182007Sroberto{ 32182007Sroberto gpio_t *gpio_a = a; 33182007Sroberto gpio_t *gpio_b = b; 34182007Sroberto 35182007Sroberto if (gpio_a->id == gpio_b->id) { 36182007Sroberto return 0; 3754359Sroberto } 3854359Sroberto 3954359Sroberto return -1; 4054359Sroberto} 4154359Sroberto 42290000Sglebiusint gpio_chain_init(ps_malloc_ops_t *malloc_ops, gpio_chain_t **ret_chain) 43290000Sglebius{ 44290000Sglebius if (!malloc_ops || !ret_chain) { 45290000Sglebius ZF_LOGE("Arguments are NULL!"); 46290000Sglebius return -EINVAL; 47290000Sglebius } 48290000Sglebius 49290000Sglebius int error = ps_calloc(malloc_ops, 1, sizeof(**ret_chain), (void **) ret_chain); 50290000Sglebius if (error) { 51290000Sglebius return -ENOMEM; 52290000Sglebius } 53290000Sglebius 54290000Sglebius error = list_init(&(*ret_chain)->pin_list); 55290000Sglebius if (error) { 56290000Sglebius ps_free(malloc_ops, sizeof(**ret_chain), *ret_chain); 57290000Sglebius return error; 58290000Sglebius } 59290000Sglebius 60290000Sglebius return 0; 61290000Sglebius} 62290000Sglebius 63290000Sglebiusint gpio_chain_destroy(ps_malloc_ops_t *malloc_ops, gpio_chain_t *chain) 64290000Sglebius{ 65290000Sglebius if (!malloc_ops || !chain) { 66290000Sglebius ZF_LOGE("Arguments are NULL!"); 67290000Sglebius return -EINVAL; 68290000Sglebius } 69290000Sglebius 70290000Sglebius /* We're expected to have removed all elements from 71290000Sglebius * the list before destroying it */ 72290000Sglebius int error = list_remove_all(&chain->pin_list); 7354359Sroberto if (error) { 74290000Sglebius return error; 75290000Sglebius } 76290000Sglebius 77290000Sglebius error = list_destroy(&chain->pin_list); 78290000Sglebius if (error) { 79290000Sglebius return error; 80290000Sglebius } 81290000Sglebius 82290000Sglebius ps_free(malloc_ops, sizeof(*chain), chain); 83290000Sglebius 84290000Sglebius return 0; 8554359Sroberto} 8654359Sroberto 87290000Sglebiusint gpio_chain_add(gpio_chain_t *chain, gpio_t *gpio) 88290000Sglebius{ 89290000Sglebius if (!chain || !gpio) { 90290000Sglebius ZF_LOGE("Arguments are NULL!"); 91290000Sglebius return -EINVAL; 92290000Sglebius } 93290000Sglebius 9454359Sroberto int error = list_append(&chain->pin_list, (void *) gpio); 95290000Sglebius if (error) { 96290000Sglebius return error; 97290000Sglebius } 98290000Sglebius 99290000Sglebius return 0; 100290000Sglebius} 101290000Sglebius 10254359Srobertoint gpio_chain_remove(gpio_chain_t *chain, gpio_t *gpio) 103290000Sglebius{ 104290000Sglebius if (!chain || !gpio) { 105290000Sglebius ZF_LOGE("Arguments are NULL!"); 106290000Sglebius return -EINVAL; 107290000Sglebius } 108290000Sglebius 10954359Sroberto int not_found = list_remove(&chain->pin_list, gpio, gpio_chain_comparator); 110290000Sglebius if (not_found) { 111290000Sglebius return -ENOENT; 112290000Sglebius } 113290000Sglebius return 0; 114290000Sglebius} 115290000Sglebius 116290000Sglebiusint gpio_chain_io_callback(void *data, void *token) 117290000Sglebius{ 11854359Sroberto gpio_t *gpio = data; 119290000Sglebius struct gpio_callback_token *cb_token = token; 120290000Sglebius 121290000Sglebius /* Do nothing if we've already processed enough bits */ 122290000Sglebius if (cb_token->nbits_to_process == cb_token->bits_processed) { 123290000Sglebius return 0; 124290000Sglebius } 125290000Sglebius 126290000Sglebius size_t curr_byte = cb_token->bits_processed / sizeof(*(cb_token->buffer)); 127290000Sglebius size_t curr_bit = cb_token->bits_processed % sizeof(*(cb_token->buffer)); 128290000Sglebius 129290000Sglebius if (cb_token->is_read) { 130290000Sglebius int val = gpio_get(gpio); 131290000Sglebius 132290000Sglebius val <<= curr_bit; 133290000Sglebius cb_token->buffer[curr_byte] &= ~BIT(curr_bit); 134290000Sglebius cb_token->buffer[curr_byte] |= val; 135290000Sglebius } else { 136290000Sglebius if (cb_token->buffer[curr_byte] & BIT(curr_bit)) { 137290000Sglebius gpio_set(gpio); 138290000Sglebius } else { 139290000Sglebius gpio_clr(gpio); 140290000Sglebius } 141290000Sglebius } 14254359Sroberto 14354359Sroberto cb_token->bits_processed++; 144290000Sglebius 145290000Sglebius return 0; 146290000Sglebius} 147290000Sglebius 148290000Sglebiusint gpio_chain_read(gpio_chain_t *chain, char *data, int len) 149290000Sglebius{ 15054359Sroberto if (!chain || !data) { 151290000Sglebius ZF_LOGE("Arguments are NULL!"); 15254359Sroberto return -EINVAL; 153290000Sglebius } 154290000Sglebius 155290000Sglebius struct gpio_callback_token cb_token = { .is_read = true, .buffer = data, .bits_processed = 0, 156290000Sglebius .nbits_to_process = len 157290000Sglebius }; 158290000Sglebius 159290000Sglebius int error = list_foreach(&chain->pin_list, gpio_chain_io_callback, &cb_token); 160290000Sglebius if (error) { 161290000Sglebius return error; 162290000Sglebius } 163290000Sglebius 164290000Sglebius return cb_token->bits_processed; 165290000Sglebius} 166290000Sglebius 167290000Sglebiusint gpio_chain_write(gpio_chain_t *chain, const char *data, int len) 168290000Sglebius{ 169290000Sglebius if (!chain || !data) { 170290000Sglebius ZF_LOGE("Arguments are NULL!"); 171290000Sglebius return -EINVAL; 172290000Sglebius } 173290000Sglebius 174290000Sglebius struct gpio_callback_token cb_token = { .is_read = false, .buffer = data, .bits_processed = 0, 175290000Sglebius .nbits_to_process = len 176290000Sglebius }; 177290000Sglebius 178290000Sglebius int error = list_foreach(&chain->pin_list, gpio_chain_io_callback, &cb_token); 179290000Sglebius if (error) { 180290000Sglebius return error; 181290000Sglebius } 182290000Sglebius 183290000Sglebius return cb_token->bits_processed; 184290000Sglebius} 185290000Sglebius