1 /* 2 * Export the keypad GPIOs on the Letux 400 as a data space accessible via the 3 * "keypad" capability. 4 * 5 * (c) 2018 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/devices/gpio-jz4730.h> 24 #include <l4/devices/dataspace.h> 25 #include <l4/devices/memory.h> 26 #include "keypad-server.h" 27 28 #include <l4/sys/thread.h> 29 #include <pthread.h> 30 #include <pthread-l4.h> 31 32 #include <l4/cxx/ipc_server> 33 #include <l4/re/dataspace> 34 #include <l4/re/env> 35 #include <l4/re/rm> 36 #include <l4/re/util/cap_alloc> 37 #include <l4/re/util/object_registry> 38 39 #include <l4/sys/cache.h> 40 #include <l4/util/util.h> 41 42 #include <stdint.h> 43 44 enum Jz4730_keypad_gpio 45 { 46 Jz4730_keypad_gpio_inputs_count = 8, 47 Jz4730_keypad_gpio_outputs_count = 17, 48 }; 49 50 /* Port A input pins. */ 51 52 const uint8_t Jz4730_keypad_inputs[Jz4730_keypad_gpio_inputs_count] = { 53 0, 1, 2, 3, 4, 5, 6, 7 54 }; 55 56 const Pin_slice Jz4730_keypad_inputs_mask = {0x000000ff, 0}; 57 58 /* Port D output pins. */ 59 60 const uint8_t Jz4730_keypad_outputs[Jz4730_keypad_gpio_outputs_count] = { 61 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 62 }; 63 64 const Pin_slice Jz4730_keypad_outputs_mask = {0x2000ffff, 0}; 65 66 /* Peripheral memory regions. */ 67 68 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 69 70 /* GPIO abstractions. */ 71 72 static void *gpio_port_a, *gpio_port_d; 73 74 /* Keypad status: an array of keypad column values. */ 75 76 uint32_t *keypad = 0; 77 78 /* Imported keypad memory referenced by the array. */ 79 80 void *keymem = 0; 81 82 /* Thread details. */ 83 84 static pthread_t _pthread; 85 86 87 88 /* Initialise the pins for scanning the keypad. */ 89 90 static void init_keyscan(void) 91 { 92 jz4730_gpio_multi_setup(gpio_port_a, &Jz4730_keypad_inputs_mask, Hw::Gpio_chip::Input, 0); 93 jz4730_gpio_multi_config_pull(gpio_port_a, &Jz4730_keypad_inputs_mask, Hw::Gpio_chip::Pull_up); 94 jz4730_gpio_multi_setup(gpio_port_d, &Jz4730_keypad_outputs_mask, Hw::Gpio_chip::Input, 0); 95 } 96 97 /* 98 Scan the keypad by enabling each output column and inspecting each input row. 99 Store each column bitmap in the keypad array. 100 */ 101 102 static void scan_keypad(void) 103 { 104 uint8_t column, row, value; 105 106 for (column = 0; column < Jz4730_keypad_gpio_outputs_count; column++) 107 { 108 jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Hw::Gpio_chip::Output, 0); 109 l4_sleep(1); 110 111 value = 0; 112 113 for (row = 0; row < Jz4730_keypad_gpio_inputs_count; row++) 114 value = (value << 1) | (jz4730_gpio_get(gpio_port_a, Jz4730_keypad_inputs[row]) ? 0 : 1); 115 116 keypad[column] = value; 117 118 jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Hw::Gpio_chip::Input, 0); 119 } 120 121 l4_cache_clean_data((unsigned long) keypad, 122 (unsigned long) keypad + Jz4730_keypad_gpio_outputs_count); 123 } 124 125 /* Set up access to memory. */ 126 127 static int setup_memory(void) 128 { 129 if (get_memory("jz4730-gpio", &gpio_virt_base, &gpio_virt_base_end) < 0) 130 return 1; 131 132 gpio_port_a = jz4730_gpio_init(gpio_virt_base, gpio_virt_base + 0x30, 32); 133 gpio_port_d = jz4730_gpio_init(gpio_virt_base + 0x90, gpio_virt_base + 0xc0, 32); 134 135 return 0; 136 } 137 138 139 140 /* Worker thread for scanning the keypad. */ 141 142 static void *scan_thread(void *data) 143 { 144 (void) data; 145 146 while (1) 147 { 148 scan_keypad(); 149 l4_sleep(20); /* 20ms -> 50Hz */ 150 } 151 152 return 0; 153 } 154 155 /* Thread initialisation. */ 156 157 static int init_thread(void) 158 { 159 pthread_attr_t thread_attr; 160 struct sched_param sp; 161 162 if (pthread_attr_init(&thread_attr)) 163 return 1; 164 165 sp.sched_priority = 0x20; 166 pthread_attr_setschedpolicy(&thread_attr, SCHED_L4); 167 pthread_attr_setschedparam(&thread_attr, &sp); 168 pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); 169 170 return pthread_create(&_pthread, &thread_attr, scan_thread, 0); 171 } 172 173 174 175 static L4Re::Util::Registry_server<> server; 176 177 178 179 /* Main program. */ 180 181 int main(void) 182 { 183 /* Memory allocation capability for the keypad data. */ 184 185 L4::Cap<L4Re::Dataspace> mem; 186 l4_size_t mem_size = Jz4730_keypad_gpio_outputs_count * sizeof(uint32_t); 187 188 if (setup_memory()) return 1; 189 190 mem = allocate_data(mem_size, &keymem); 191 192 if (!mem.is_valid()) return 1; 193 194 keypad = (uint32_t *) keymem; 195 196 /* Set up keypad access and start scanning. */ 197 198 init_keyscan(); 199 200 /* Set up a thread to scan the keypad concurrently with the server loop. */ 201 202 init_thread(); 203 204 /* Initialise and register a server object. */ 205 206 Keypad_server server_obj(mem); 207 server.registry()->register_obj(&server_obj, "keypad"); 208 209 /* Enter the IPC server loop. */ 210 211 server.loop(); 212 return 0; 213 }