piolib.c
1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2023-24 Raspberry Pi Ltd. 4 * All rights reserved. 5 */ 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <pthread.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <time.h> 15 #include <unistd.h> 16 17 #include "piolib.h" 18 #include "piolib_priv.h" 19 20 #define PIO_MAX_INSTANCES 4 21 22 extern PIO_CHIP_T *__start_piochips; 23 extern PIO_CHIP_T *__stop_piochips; 24 25 static __thread PIO __pio; 26 27 static PIO pio_instances[PIO_MAX_INSTANCES]; 28 static uint num_instances; 29 static pthread_mutex_t pio_handle_lock; 30 31 void pio_select(PIO pio) { __pio = pio; } 32 33 PIO pio_get_current(void) { 34 PIO pio = __pio; 35 check_pio_param(pio); 36 return pio; 37 } 38 39 int pio_get_index(PIO pio) { 40 int i; 41 for (i = 0; i < PIO_MAX_INSTANCES; i++) { 42 if (pio == pio_instances[i]) 43 return i; 44 } 45 return -1; 46 } 47 48 int pio_init(void) { 49 static bool initialised; 50 const PIO_CHIP_T *const *p; 51 uint i = 0; 52 int err; 53 54 if (initialised) 55 return 0; 56 num_instances = 0; 57 p = &__start_piochips; 58 while (p < &__stop_piochips && num_instances < PIO_MAX_INSTANCES) { 59 PIO_CHIP_T *chip = *p; 60 PIO pio = chip->create_instance(chip, i); 61 if (pio && !PIO_IS_ERR(pio)) { 62 pio_instances[num_instances++] = pio; 63 i++; 64 } else { 65 p++; 66 i = 0; 67 } 68 } 69 70 err = pthread_mutex_init(&pio_handle_lock, NULL); 71 if (err) 72 return err; 73 74 initialised = true; 75 return 0; 76 } 77 78 PIO pio_open(uint idx) { 79 PIO pio = NULL; 80 int err; 81 82 err = pio_init(); 83 if (err) 84 return PIO_ERR(err); 85 86 if (idx >= num_instances) 87 return PIO_ERR(-EINVAL); 88 89 pthread_mutex_lock(&pio_handle_lock); 90 91 pio = pio_instances[idx]; 92 if (pio) { 93 if (pio->in_use) 94 err = -EBUSY; 95 else 96 pio->in_use = 1; 97 } 98 99 pthread_mutex_unlock(&pio_handle_lock); 100 101 if (err) 102 return PIO_ERR(err); 103 104 err = pio->chip->open_instance(pio); 105 if (err) { 106 pio->in_use = 0; 107 return PIO_ERR(err); 108 } 109 110 pio_select(pio); 111 112 return pio; 113 } 114 115 PIO pio_open_by_name(const char *name) { 116 int err = -ENOENT; 117 uint i; 118 119 err = pio_init(); 120 if (err) 121 return PIO_ERR(err); 122 123 for (i = 0; i < num_instances; i++) { 124 PIO p = pio_instances[i]; 125 if (!strcmp(name, p->chip->name)) 126 break; 127 } 128 129 if (i == num_instances) 130 return PIO_ERR(-ENOENT); 131 132 return pio_open(i); 133 } 134 135 PIO pio_open_helper(uint idx) { 136 PIO pio = pio_instances[idx]; 137 if (!pio || !pio->in_use) { 138 pio = pio_open(idx); 139 if (PIO_IS_ERR(pio)) { 140 printf("* Failed to open PIO device %d (error %d)\n", idx, 141 PIO_ERR_VAL(pio)); 142 exit(1); 143 } 144 } 145 return pio; 146 } 147 148 void pio_close(PIO pio) { 149 pio->chip->close_instance(pio); 150 pthread_mutex_lock(&pio_handle_lock); 151 pio->in_use = 0; 152 pthread_mutex_unlock(&pio_handle_lock); 153 } 154 155 void pio_panic(const char *msg) { 156 fprintf(stderr, "PANIC: %s\n", msg); 157 exit(1); 158 } 159 160 void sleep_us(uint64_t us) { 161 const struct timespec tv = {.tv_sec = (us / 1000000), 162 .tv_nsec = 1000ull * (us % 1000000)}; 163 nanosleep(&tv, NULL); 164 }