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