1 /* 2 * Export the keypad GPIOs on the Ben NanoNote as a data space accessible via 3 * the "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-jz4740.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/re/dataspace> 33 #include <l4/re/env> 34 #include <l4/re/util/object_registry> 35 36 #include <l4/sys/cache.h> 37 #include <l4/util/util.h> 38 39 #include <stdint.h> 40 41 enum Jz4740_keypad_gpio 42 { 43 Jz4740_keypad_gpio_inputs_count = 8, 44 Jz4740_keypad_gpio_outputs_count = 8, 45 }; 46 47 /* Port D input pins. */ 48 49 const uint8_t Jz4740_keypad_inputs[Jz4740_keypad_gpio_inputs_count] = { 50 18, 19, 20, 21, 22, 23, 24, 26 51 }; 52 53 const Pin_slice Jz4740_keypad_inputs_mask = {0x05fc0000, 0}; 54 55 /* Port C output pins. */ 56 57 const uint8_t Jz4740_keypad_outputs[Jz4740_keypad_gpio_outputs_count] = { 58 10, 11, 12, 13, 14, 15, 16, 17 59 }; 60 61 const Pin_slice Jz4740_keypad_outputs_mask = {0x0003fc00, 0}; 62 63 /* Peripheral memory regions. */ 64 65 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 66 67 /* GPIO abstractions. */ 68 69 static void *gpio_port_c, *gpio_port_d; 70 71 /* Keypad status: an array of keypad column values. */ 72 73 uint32_t *keypad = 0; 74 75 /* Imported keypad memory referenced by the array. */ 76 77 void *keymem = 0; 78 79 /* Thread details. */ 80 81 static pthread_t _pthread; 82 83 84 85 /* Initialise the pins for scanning the keypad. */ 86 87 static void init_keyscan(void) 88 { 89 jz4740_gpio_multi_setup(gpio_port_d, &Jz4740_keypad_inputs_mask, Hw::Gpio_chip::Input, 0); 90 jz4740_gpio_multi_config_pull(gpio_port_d, &Jz4740_keypad_inputs_mask, Hw::Gpio_chip::Pull_up); 91 jz4740_gpio_multi_setup(gpio_port_c, &Jz4740_keypad_outputs_mask, Hw::Gpio_chip::Input, 0); 92 } 93 94 /* 95 Scan the keypad by enabling each output column and inspecting each input row. 96 Store each column bitmap in the keypad array. 97 */ 98 99 static void scan_keypad(void) 100 { 101 uint8_t column, row, value; 102 103 for (column = 0; column < Jz4740_keypad_gpio_outputs_count; column++) 104 { 105 jz4740_gpio_setup(gpio_port_c, Jz4740_keypad_outputs[column], Hw::Gpio_chip::Output, 0); 106 l4_sleep(1); 107 108 value = 0; 109 110 for (row = 0; row < Jz4740_keypad_gpio_inputs_count; row++) 111 value = (value << 1) | (jz4740_gpio_get(gpio_port_d, Jz4740_keypad_inputs[row]) ? 0 : 1); 112 113 keypad[column] = value; 114 115 jz4740_gpio_setup(gpio_port_c, Jz4740_keypad_outputs[column], Hw::Gpio_chip::Input, 0); 116 } 117 118 l4_cache_clean_data((unsigned long) keypad, 119 (unsigned long) keypad + Jz4740_keypad_gpio_outputs_count); 120 } 121 122 /* Set up access to memory. */ 123 124 static int setup_memory(void) 125 { 126 if (get_memory("jz4740-gpio", &gpio_virt_base, &gpio_virt_base_end) < 0) 127 return 1; 128 129 gpio_port_c = jz4740_gpio_init(gpio_virt_base + 0x200, gpio_virt_base + 0x300, 32); 130 gpio_port_d = jz4740_gpio_init(gpio_virt_base + 0x300, gpio_virt_base + 0x400, 32); 131 132 return 0; 133 } 134 135 136 137 /* Worker thread for scanning the keypad. */ 138 139 static void *scan_thread(void *data) 140 { 141 (void) data; 142 143 while (1) 144 { 145 scan_keypad(); 146 l4_sleep(20); /* 20ms -> 50Hz */ 147 } 148 149 return 0; 150 } 151 152 /* Thread initialisation. */ 153 154 static int init_thread(void) 155 { 156 pthread_attr_t thread_attr; 157 struct sched_param sp; 158 159 if (pthread_attr_init(&thread_attr)) 160 return 1; 161 162 sp.sched_priority = 0x20; 163 pthread_attr_setschedpolicy(&thread_attr, SCHED_L4); 164 pthread_attr_setschedparam(&thread_attr, &sp); 165 pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); 166 167 return pthread_create(&_pthread, &thread_attr, scan_thread, 0); 168 } 169 170 171 172 static L4Re::Util::Registry_server<> server; 173 174 175 176 /* Main program. */ 177 178 int main(void) 179 { 180 /* Memory allocation capability for the keypad data. */ 181 182 L4::Cap<L4Re::Dataspace> mem; 183 l4_size_t mem_size = Jz4740_keypad_gpio_outputs_count * sizeof(uint32_t); 184 185 if (setup_memory()) return 1; 186 187 mem = allocate_data(mem_size, &keymem); 188 189 if (!mem.is_valid()) return 1; 190 191 keypad = (uint32_t *) keymem; 192 193 /* Set up keypad access and start scanning. */ 194 195 init_keyscan(); 196 197 /* Set up a thread to scan the keypad concurrently with the server loop. */ 198 199 init_thread(); 200 201 /* Initialise and register a server object. */ 202 203 Keypad_server server_obj(mem); 204 server.registry()->register_obj(&server_obj, "keypad"); 205 206 /* Enter the IPC server loop. */ 207 208 server.loop(); 209 return 0; 210 }