1/*
2 * Copyright 2017, 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#pragma once
14
15#include <platsupport/io.h>
16
17struct ps_clk;
18typedef struct ps_chardevice ps_chardevice_t;
19
20#include <platsupport/plat/serial.h>
21
22enum chardev_status {
23/// Transfer completed successfully
24    CHARDEV_STAT_COMPLETE,
25/// Transfer was truncated or cancelled by the remote
26    CHARDEV_STAT_INCOMPLETE,
27/// A transfer error occurred
28    CHARDEV_STAT_ERROR,
29/// The transfer was aborted by the user
30    CHARDEV_STAT_CANCELLED,
31};
32
33typedef void (*chardev_callback_t)(ps_chardevice_t* device, enum chardev_status stat, size_t bytes_transfered, void* token);
34
35struct chardev_xmit_descriptor {
36    /// A function to call when the transfer is complete
37    chardev_callback_t callback;
38    /// A token to pass unmodified to callback
39    void* token;
40    /// The number of bytes transfered thus far
41    size_t bytes_transfered;
42    /// The total number of bytes to transfer
43    volatile size_t bytes_requested;
44    /// The source or destination for the data
45    void* data;
46};
47
48struct ps_chardevice {
49    /* identifier for the device */
50    enum chardev_id id;
51    void* vaddr;
52    /* Character operations for this device */
53    ssize_t (*read)(ps_chardevice_t* device, void* data, size_t bytes, chardev_callback_t cb, void* token);
54    ssize_t (*write)(ps_chardevice_t* device, const void* data, size_t bytes, chardev_callback_t cb, void* token);
55    void (*handle_irq)(ps_chardevice_t* device);
56    /* array of irqs associated with this device */
57    const int *irqs;
58    /// Transmit transfer data for use with IRQs
59    struct chardev_xmit_descriptor read_descriptor;
60    /// Receive transfer data for use with IRQs
61    struct chardev_xmit_descriptor write_descriptor;
62    /* Input clock for this device */
63    struct ps_clk* clk;
64    /* OS specific memory operations */
65    ps_io_ops_t ioops;
66    /* Device specific flags */
67    int flags;
68};
69
70/*
71 * Initialiase a device
72 * @param  id: the id of the character device
73 * @param ops: a structure containing OS specific operations for memory access
74 * @param dev: a character device structure to populate
75 * @return   : NULL on error, otherwise returns the device structure pointer
76 */
77ps_chardevice_t* ps_cdev_init(enum chardev_id id,
78                              const ps_io_ops_t* ops,
79                              ps_chardevice_t* dev);
80
81/*
82 * Statically initialise a device
83 * @param ops: a structure containing OS specific operations for memory access
84 * @param dev: a character device structure to populate
85 * @param params: a pointer generally used to pass machine or platform
86 *  specific parameters
87 */
88ps_chardevice_t* ps_cdev_static_init(const ps_io_ops_t *ops,
89                                     ps_chardevice_t* dev,
90                                     void *params);
91
92/*
93 * Create a pseudo device: initialise with nop function pointers
94 * @param o: a structure containing OS specific operations for memory access
95 * @param d: a character device structure to populate
96 * @return   : NULL on error, otherwise returns the device structure pointer
97 */
98ps_chardevice_t* ps_cdev_new(const ps_io_ops_t* o,
99                             ps_chardevice_t* d);
100
101/**
102 * Send a character to the device. New lines will be automatically be chased
103 * by a character return
104 * @param[in] d    The character device to send a character to
105 * @param[in] c    The character to send
106 */
107static inline void ps_cdev_putchar(ps_chardevice_t* d, int c)
108{
109    int ret;
110    do {
111        char data = c;
112        ret = d->write(d, &data, 1, NULL, NULL);
113    } while (ret < 1);
114}
115
116/**
117 * Receive a character from the device
118 * @param[in] d  The device to receive a character from
119 * @return       The chracter received; negative values signal that an error occurred.
120 */
121static inline int ps_cdev_getchar(ps_chardevice_t* d)
122{
123    int ret;
124    char data;
125    ret = d->read(d, &data, 1, NULL, NULL);
126    return (ret == 1) ? data : EOF;
127}
128
129/**
130 * Read data from a device
131 * @param[in]  d        The device to read data from
132 * @param[out] data     The location to store the read data to
133 * @param[in]  size     The number of bytes to read
134 * @param[in]  callback Optional: A function to call when the requested number of
135 *                      bytes have been read. The caller must periodically call
136 *                      the IRQ handler to satisfy the request.
137 * @param[in]  token    An anonymous pointer to pass, unmodified, to the provided
138 *                      callback function.
139 * @return              Returns the number of bytes read on succes, negative
140 *                      values reprents an error. If a callback function is
141 *                      provided, a return value of 0 will represent success
142 *                      If no callback funtion is provided, this function will
143 *                      read data from any internal fifos to meet the the request
144 *                      and then return. It will not block until the requested
145 *                      number of bytes are available.
146 */
147static inline ssize_t ps_cdev_read(ps_chardevice_t* d, void* data, size_t size,
148                                   chardev_callback_t callback, void* token)
149{
150    return d->read(d, data, size, callback, token);
151}
152
153/**
154 * Write data to a device
155 * @param[in]  d        The device to write data to
156 * @param[out] data     The location of the data to be written
157 * @param[in]  size     The number of bytes to write
158 * @param[in]  callback Optional: A function to call when the requested number of
159 *                      bytes have been written. The caller must periodically call
160 *                      the IRQ handler to satisfy the request.
161 * @param[in]  token    An anonymous pointer to pass, unmodified, to the provided
162 *                      callback function.
163 * @return              Returns the number of bytes written on succes, negative
164 *                      values reprents an error. If a callback function is
165 *                      provided, a return value of 0 will represent success
166 *                      If no callback funtion is provided, this function will
167 *                      write data to any internal fifos to meet the the request
168 *                      and then return. It will not block until the requested
169 *                      number of bytes have been written.
170 */
171static inline ssize_t ps_cdev_write(ps_chardevice_t* d, void* data, size_t size,
172                                    chardev_callback_t callback, void* token)
173{
174    return d->write(d, data, size, callback, token);
175}
176
177/**
178 * Pass control to the devices IRQ handler
179 * @param[in] The device to pass control to
180 * @param[in] The physical IRQ number that triggered the event
181 */
182static inline void ps_cdev_handle_irq(ps_chardevice_t* d, int irq UNUSED)
183{
184    d->handle_irq(d);
185}
186
187/**
188 * Check if the given device emits the given IRQ
189 * @param[in] d   The device to query
190 * @param[in] irq An irq number
191 * @return        non-zero if the device will produce the given IRQ
192 */
193static inline int ps_cdev_produces_irq(const ps_chardevice_t* d, int irq)
194{
195    int i;
196    for (i = 0; d->irqs[i] != -1; i++) {
197        if (d->irqs[i] == irq) {
198            return 1;
199        }
200    }
201    return 0;
202}
203
204/**
205 * Set the device specific flags.
206 * @param[in] d     The character device to set the flags to
207 * @param[in] flags The flags to set
208 */
209static inline void ps_cdev_set_flags(ps_chardevice_t* d, int flags)
210{
211    d->flags = flags;
212}
213
214