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