1 /*
2 * Copyright 2018 Jared Boone
3 *
4 * This file is part of HackRF.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "portapack.h"
23
24 #include "hackrf_core.h"
25 #include "gpio_lpc.h"
26
27 #include <libopencm3/lpc43xx/scu.h>
28
portapack_sleep_milliseconds(const uint32_t milliseconds)29 static void portapack_sleep_milliseconds(const uint32_t milliseconds) {
30 /* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */
31 delay(milliseconds * 40800);
32 }
33
34 static struct gpio_t gpio_io_stbx = GPIO(5, 0); /* P2_0 */
35 static struct gpio_t gpio_addr = GPIO(5, 1); /* P2_1 */
36 __attribute__((unused)) static struct gpio_t gpio_lcd_te = GPIO(5, 3); /* P2_3 */
37 __attribute__((unused)) static struct gpio_t gpio_unused = GPIO(5, 7); /* P2_8 */
38 static struct gpio_t gpio_lcd_rdx = GPIO(5, 4); /* P2_4 */
39 static struct gpio_t gpio_lcd_wrx = GPIO(1, 10); /* P2_9 */
40 static struct gpio_t gpio_dir = GPIO(1, 13); /* P2_13 */
41
42 typedef struct portapack_if_t {
43 gpio_t gpio_dir;
44 gpio_t gpio_lcd_rdx;
45 gpio_t gpio_lcd_wrx;
46 gpio_t gpio_io_stbx;
47 gpio_t gpio_addr;
48 gpio_port_t* const gpio_port_data;
49 uint8_t io_reg;
50 } portapack_if_t;
51
52 static portapack_if_t portapack_if = {
53 .gpio_dir = &gpio_dir,
54 .gpio_lcd_rdx = &gpio_lcd_rdx,
55 .gpio_lcd_wrx = &gpio_lcd_wrx,
56 .gpio_io_stbx = &gpio_io_stbx,
57 .gpio_addr = &gpio_addr,
58 .gpio_port_data = GPIO_LPC_PORT(3),
59 .io_reg = 0x03,
60 };
61
62 /* NOTE: Code below assumes the shift value is "8". */
63 #define GPIO_DATA_SHIFT (8)
64 static const uint32_t gpio_data_mask = 0xFFU << GPIO_DATA_SHIFT;
65
portapack_data_mask_set()66 static void portapack_data_mask_set() {
67 portapack_if.gpio_port_data->mask = ~gpio_data_mask;
68 }
69
portapack_data_write_low(const uint32_t value)70 static void portapack_data_write_low(const uint32_t value) {
71 portapack_if.gpio_port_data->mpin = (value << GPIO_DATA_SHIFT);
72 }
73
portapack_data_write_high(const uint32_t value)74 static void portapack_data_write_high(const uint32_t value) {
75 /* NOTE: Assumes no other bits in the port are masked. */
76 /* NOTE: Assumes that bits 15 through 8 are masked. */
77 portapack_if.gpio_port_data->mpin = value;
78 }
79
portapack_dir_read()80 static void portapack_dir_read() {
81 portapack_if.gpio_port_data->dir &= ~gpio_data_mask;
82 gpio_set(portapack_if.gpio_dir);
83 }
84
portapack_dir_write()85 static void portapack_dir_write() {
86 gpio_clear(portapack_if.gpio_dir);
87 portapack_if.gpio_port_data->dir |= gpio_data_mask;
88 /* TODO: Manipulating DIR[3] makes me queasy. The RFFC5072 DATA pin
89 * is also on port 3, and switches direction periodically...
90 * Time to resort to bit-banding to enforce atomicity? But then, how
91 * to change direction on eight bits efficiently? Or do I care, since
92 * the PortaPack data bus shouldn't change direction too frequently?
93 */
94 }
95
portapack_lcd_rd_assert()96 __attribute__((unused)) static void portapack_lcd_rd_assert() {
97 gpio_clear(portapack_if.gpio_lcd_rdx);
98 }
99
portapack_lcd_rd_deassert()100 static void portapack_lcd_rd_deassert() {
101 gpio_set(portapack_if.gpio_lcd_rdx);
102 }
103
portapack_lcd_wr_assert()104 static void portapack_lcd_wr_assert() {
105 gpio_clear(portapack_if.gpio_lcd_wrx);
106 }
107
portapack_lcd_wr_deassert()108 static void portapack_lcd_wr_deassert() {
109 gpio_set(portapack_if.gpio_lcd_wrx);
110 }
111
portapack_io_stb_assert()112 static void portapack_io_stb_assert() {
113 gpio_clear(portapack_if.gpio_io_stbx);
114 }
115
portapack_io_stb_deassert()116 static void portapack_io_stb_deassert() {
117 gpio_set(portapack_if.gpio_io_stbx);
118 }
119
portapack_addr(const bool value)120 static void portapack_addr(const bool value) {
121 gpio_write(portapack_if.gpio_addr, value);
122 }
123
portapack_lcd_command(const uint32_t value)124 static void portapack_lcd_command(const uint32_t value) {
125 portapack_data_write_high(0); /* Drive high byte (with zero -- don't care) */
126 portapack_dir_write(); /* Turn around data bus, MCU->CPLD */
127 portapack_addr(0); /* Indicate command */
128 __asm__("nop");
129 __asm__("nop");
130 __asm__("nop");
131 portapack_lcd_wr_assert(); /* Latch high byte */
132
133 portapack_data_write_low(value); /* Drive low byte (pass-through) */
134 __asm__("nop");
135 __asm__("nop");
136 __asm__("nop");
137 portapack_lcd_wr_deassert(); /* Complete write operation */
138
139 portapack_addr(1); /* Set up for data phase (most likely after a command) */
140 }
141
portapack_lcd_write_data(const uint32_t value)142 static void portapack_lcd_write_data(const uint32_t value) {
143 // NOTE: Assumes and DIR=0 and ADDR=1 from command phase.
144 portapack_data_write_high(value); /* Drive high byte */
145 __asm__("nop");
146 portapack_lcd_wr_assert(); /* Latch high byte */
147
148 portapack_data_write_low(value); /* Drive low byte (pass-through) */
149 __asm__("nop");
150 __asm__("nop");
151 __asm__("nop");
152 portapack_lcd_wr_deassert(); /* Complete write operation */
153 }
154
portapack_io_write(const bool address,const uint_fast16_t value)155 static void portapack_io_write(const bool address, const uint_fast16_t value) {
156 portapack_data_write_low(value);
157 portapack_dir_write();
158 portapack_addr(address);
159 __asm__("nop");
160 __asm__("nop");
161 __asm__("nop");
162 portapack_io_stb_assert();
163 __asm__("nop");
164 __asm__("nop");
165 __asm__("nop");
166 portapack_io_stb_deassert();
167 }
168
portapack_if_init()169 static void portapack_if_init() {
170 portapack_data_mask_set();
171 portapack_data_write_high(0);
172
173 portapack_dir_read();
174 portapack_lcd_rd_deassert();
175 portapack_lcd_wr_deassert();
176 portapack_io_stb_deassert();
177 portapack_addr(0);
178
179 gpio_output(portapack_if.gpio_dir);
180 gpio_output(portapack_if.gpio_lcd_rdx);
181 gpio_output(portapack_if.gpio_lcd_wrx);
182 gpio_output(portapack_if.gpio_io_stbx);
183 gpio_output(portapack_if.gpio_addr);
184 /* gpio_input(portapack_if.gpio_rot_a); */
185 /* gpio_input(portapack_if.gpio_rot_b); */
186
187 scu_pinmux(SCU_PINMUX_PP_D0, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
188 scu_pinmux(SCU_PINMUX_PP_D1, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
189 scu_pinmux(SCU_PINMUX_PP_D2, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
190 scu_pinmux(SCU_PINMUX_PP_D3, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
191 scu_pinmux(SCU_PINMUX_PP_D4, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
192 scu_pinmux(SCU_PINMUX_PP_D5, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
193 scu_pinmux(SCU_PINMUX_PP_D6, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
194 scu_pinmux(SCU_PINMUX_PP_D7, SCU_CONF_FUNCTION0 | SCU_GPIO_PDN);
195
196 scu_pinmux(SCU_PINMUX_PP_DIR, SCU_CONF_FUNCTION0 | SCU_GPIO_NOPULL);
197 scu_pinmux(SCU_PINMUX_PP_LCD_RDX, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
198 scu_pinmux(SCU_PINMUX_PP_LCD_WRX, SCU_CONF_FUNCTION0 | SCU_GPIO_NOPULL);
199 scu_pinmux(SCU_PINMUX_PP_IO_STBX, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
200 scu_pinmux(SCU_PINMUX_PP_ADDR, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL);
201 /* scu_pinmux(SCU_PINMUX_PP_LCD_TE, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL); */
202 /* scu_pinmux(SCU_PINMUX_PP_UNUSED, SCU_CONF_FUNCTION4 | SCU_GPIO_NOPULL); */
203 }
204
portapack_lcd_reset_state(const bool active)205 static void portapack_lcd_reset_state(const bool active) {
206 portapack_if.io_reg = (portapack_if.io_reg & 0xfe) | (active ? (1 << 0) : 0);
207 portapack_io_write(1, portapack_if.io_reg);
208 }
209
portapack_lcd_data_write_command_and_data(const uint_fast8_t command,const uint8_t * data,const size_t data_count)210 static void portapack_lcd_data_write_command_and_data(
211 const uint_fast8_t command,
212 const uint8_t* data,
213 const size_t data_count
214 ) {
215 portapack_lcd_command(command);
216 for(size_t i=0; i<data_count; i++) {
217 portapack_lcd_write_data(data[i]);
218 }
219 }
220
portapack_lcd_sleep_out()221 static void portapack_lcd_sleep_out() {
222 const uint8_t cmd_11[] = {};
223 portapack_lcd_data_write_command_and_data(0x11, cmd_11, ARRAY_SIZEOF(cmd_11));
224 // "It will be necessary to wait 120msec after sending Sleep Out
225 // command (when in Sleep In Mode) before Sleep In command can be
226 // sent."
227 portapack_sleep_milliseconds(120);
228 }
229
portapack_lcd_display_on()230 static void portapack_lcd_display_on() {
231 const uint8_t cmd_29[] = {};
232 portapack_lcd_data_write_command_and_data(0x29, cmd_29, ARRAY_SIZEOF(cmd_29));
233 }
234
portapack_lcd_ramwr_start()235 static void portapack_lcd_ramwr_start() {
236 const uint8_t cmd_2c[] = {};
237 portapack_lcd_data_write_command_and_data(0x2c, cmd_2c, ARRAY_SIZEOF(cmd_2c));
238 }
239
portapack_lcd_set(const uint_fast8_t command,const uint_fast16_t start,const uint_fast16_t end)240 static void portapack_lcd_set(const uint_fast8_t command, const uint_fast16_t start, const uint_fast16_t end) {
241 const uint8_t data[] = {
242 (start >> 8), (start & 0xff),
243 (end >> 8), (end & 0xff)
244 };
245 portapack_lcd_data_write_command_and_data(command, data, ARRAY_SIZEOF(data));
246 }
247
portapack_lcd_caset(const uint_fast16_t start_column,const uint_fast16_t end_column)248 static void portapack_lcd_caset(const uint_fast16_t start_column, const uint_fast16_t end_column) {
249 portapack_lcd_set(0x2a, start_column, end_column);
250 }
251
portapack_lcd_paset(const uint_fast16_t start_page,const uint_fast16_t end_page)252 static void portapack_lcd_paset(const uint_fast16_t start_page, const uint_fast16_t end_page) {
253 portapack_lcd_set(0x2b, start_page, end_page);
254 }
255
portapack_lcd_start_ram_write(const ui_rect_t rect)256 static void portapack_lcd_start_ram_write(
257 const ui_rect_t rect
258 ) {
259 portapack_lcd_caset(rect.point.x, rect.point.x + rect.size.width - 1);
260 portapack_lcd_paset(rect.point.y, rect.point.y + rect.size.height - 1);
261 portapack_lcd_ramwr_start();
262 }
263
portapack_lcd_write_pixel(const ui_color_t pixel)264 static void portapack_lcd_write_pixel(const ui_color_t pixel) {
265 portapack_lcd_write_data(pixel.v);
266 }
267
portapack_lcd_write_pixels_color(const ui_color_t c,size_t n)268 static void portapack_lcd_write_pixels_color(const ui_color_t c, size_t n) {
269 while(n--) {
270 portapack_lcd_write_data(c.v);
271 }
272 }
273
portapack_lcd_wake()274 static void portapack_lcd_wake() {
275 portapack_lcd_sleep_out();
276 portapack_lcd_display_on();
277 }
278
portapack_lcd_reset()279 static void portapack_lcd_reset() {
280 portapack_lcd_reset_state(false);
281 portapack_sleep_milliseconds(1);
282 portapack_lcd_reset_state(true);
283 portapack_sleep_milliseconds(10);
284 portapack_lcd_reset_state(false);
285 portapack_sleep_milliseconds(120);
286 }
287
portapack_lcd_init()288 static void portapack_lcd_init() {
289 // LCDs are configured for IM[2:0] = 001
290 // 8080-I system, 16-bit parallel bus
291
292 //
293 // 0x3a: DBI[2:0] = 101
294 // MDT[1:0] = XX (if not in 18-bit mode, right?)
295
296 // Power control B
297 // 0
298 // PCEQ=1, DRV_ena=0, Power control=3
299 const uint8_t cmd_cf[] = { 0x00, 0xD9, 0x30 };
300 portapack_lcd_data_write_command_and_data(0xCF, cmd_cf, ARRAY_SIZEOF(cmd_cf));
301
302 // Power on sequence control
303 const uint8_t cmd_ed[] = { 0x64, 0x03, 0x12, 0x81 };
304 portapack_lcd_data_write_command_and_data(0xED, cmd_ed, ARRAY_SIZEOF(cmd_ed));
305
306 // Driver timing control A
307 const uint8_t cmd_e8[] = { 0x85, 0x10, 0x78 };
308 portapack_lcd_data_write_command_and_data(0xE8, cmd_e8, ARRAY_SIZEOF(cmd_e8));
309
310 // Power control A
311 const uint8_t cmd_cb[] = { 0x39, 0x2C, 0x00, 0x34, 0x02 };
312 portapack_lcd_data_write_command_and_data(0xCB, cmd_cb, ARRAY_SIZEOF(cmd_cb));
313
314 // Pump ratio control
315 const uint8_t cmd_f7[] = { 0x20 };
316 portapack_lcd_data_write_command_and_data(0xF7, cmd_f7, ARRAY_SIZEOF(cmd_f7));
317
318 // Driver timing control B
319 const uint8_t cmd_ea[] = { 0x00, 0x00 };
320 portapack_lcd_data_write_command_and_data(0xEA, cmd_ea, ARRAY_SIZEOF(cmd_ea));
321
322 const uint8_t cmd_b1[] = { 0x00, 0x1B };
323 portapack_lcd_data_write_command_and_data(0xB1, cmd_b1, ARRAY_SIZEOF(cmd_b1));
324
325 // Blanking Porch Control
326 // VFP = 0b0000010 = 2 (number of HSYNC of vertical front porch)
327 // VBP = 0b0000010 = 2 (number of HSYNC of vertical back porch)
328 // HFP = 0b0001010 = 10 (number of DOTCLOCK of horizontal front porch)
329 // HBP = 0b0010100 = 20 (number of DOTCLOCK of horizontal back porch)
330 const uint8_t cmd_b5[] = { 0x02, 0x02, 0x0a, 0x14 };
331 portapack_lcd_data_write_command_and_data(0xB5, cmd_b5, ARRAY_SIZEOF(cmd_b5));
332
333 // Display Function Control
334 // PT[1:0] = 0b10
335 // PTG[1:0] = 0b10
336 // ISC[3:0] = 0b0010 (scan cycle interval of gate driver: 5 frames)
337 // SM = 0 (gate driver pin arrangement in combination with GS)
338 // SS = 1 (source output scan direction S720 -> S1)
339 // GS = 0 (gate output scan direction G1 -> G320)
340 // REV = 1 (normally white)
341 // NL = 0b100111 (default)
342 // PCDIV = 0b000000 (default?)
343 const uint8_t cmd_b6[] = { 0x0A, 0xA2, 0x27, 0x00 };
344 portapack_lcd_data_write_command_and_data(0xB6, cmd_b6, ARRAY_SIZEOF(cmd_b6));
345
346 // Power Control 1
347 //VRH[5:0]
348 const uint8_t cmd_c0[] = { 0x1B };
349 portapack_lcd_data_write_command_and_data(0xC0, cmd_c0, ARRAY_SIZEOF(cmd_c0));
350
351 // Power Control 2
352 //SAP[2:0];BT[3:0]
353 const uint8_t cmd_c1[] = { 0x12 };
354 portapack_lcd_data_write_command_and_data(0xC1, cmd_c1, ARRAY_SIZEOF(cmd_c1));
355
356 // VCOM Control 1
357 const uint8_t cmd_c5[] = { 0x32, 0x3C };
358 portapack_lcd_data_write_command_and_data(0xC5, cmd_c5, ARRAY_SIZEOF(cmd_c5));
359
360 // VCOM Control 2
361 const uint8_t cmd_c7[] = { 0x9B };
362 portapack_lcd_data_write_command_and_data(0xC7, cmd_c7, ARRAY_SIZEOF(cmd_c7));
363
364 // Memory Access Control
365 // Invert X and Y memory access order, so upper-left of
366 // screen is (0,0) when writing to display.
367 const uint8_t cmd_36[] = {
368 (1 << 7) | // MY=1
369 (1 << 6) | // MX=1
370 (0 << 5) | // MV=0
371 (1 << 4) | // ML=1: reverse vertical refresh to simplify scrolling logic
372 (1 << 3) // BGR=1: For Kingtech LCD, BGR filter.
373 };
374 portapack_lcd_data_write_command_and_data(0x36, cmd_36, ARRAY_SIZEOF(cmd_36));
375
376 // COLMOD: Pixel Format Set
377 // DPI=101 (16 bits/pixel), DBI=101 (16 bits/pixel)
378 const uint8_t cmd_3a[] = { 0x55 };
379 portapack_lcd_data_write_command_and_data(0x3A, cmd_3a, ARRAY_SIZEOF(cmd_3a));
380
381 //portapack_lcd_data_write_command_and_data(0xF6, { 0x01, 0x30 });
382 // WEMODE=1 (reset column and page number on overflow)
383 // MDT[1:0]
384 // EPF[1:0]=00 (use channel MSB for LSB)
385 // RIM=0 (If COLMOD[6:4]=101 (65k color), 16-bit RGB interface (1 transfer/pixel))
386 // RM=0 (system interface/VSYNC interface)
387 // DM[1:0]=00 (internal clock operation)
388 // ENDIAN=0 (doesn't matter with 16-bit interface)
389 const uint8_t cmd_f6[] = { 0x01, 0x30, 0x00 };
390 portapack_lcd_data_write_command_and_data(0xF6, cmd_f6, ARRAY_SIZEOF(cmd_f6));
391
392 // 3Gamma Function Disable
393 const uint8_t cmd_f2[] = { 0x00 };
394 portapack_lcd_data_write_command_and_data(0xF2, cmd_f2, ARRAY_SIZEOF(cmd_f2));
395
396 // Gamma curve selected
397 const uint8_t cmd_26[] = { 0x01 };
398 portapack_lcd_data_write_command_and_data(0x26, cmd_26, ARRAY_SIZEOF(cmd_26));
399
400 // Set Gamma
401 const uint8_t cmd_e0[] = {
402 0x0F, 0x1D, 0x19, 0x0E, 0x10, 0x07, 0x4C, 0x63,
403 0x3F, 0x03, 0x0D, 0x00, 0x26, 0x24, 0x04
404 };
405 portapack_lcd_data_write_command_and_data(0xE0, cmd_e0, ARRAY_SIZEOF(cmd_e0));
406
407 // Set Gamma
408 const uint8_t cmd_e1[] = {
409 0x00, 0x1C, 0x1F, 0x02, 0x0F, 0x03, 0x35, 0x25,
410 0x47, 0x04, 0x0C, 0x0B, 0x29, 0x2F, 0x05
411 };
412 portapack_lcd_data_write_command_and_data(0xE1, cmd_e1, ARRAY_SIZEOF(cmd_e1));
413
414 portapack_lcd_wake();
415
416 // Turn on Tearing Effect Line (TE) output signal.
417 const uint8_t cmd_35[] = { 0b00000000 };
418 portapack_lcd_data_write_command_and_data(0x35, cmd_35, ARRAY_SIZEOF(cmd_35));
419 }
420
portapack_backlight(const bool on)421 void portapack_backlight(const bool on) {
422 portapack_if.io_reg = (portapack_if.io_reg & 0x7f) | (on ? (1 << 7) : 0);
423 portapack_io_write(1, portapack_if.io_reg);
424 }
425
portapack_reference_oscillator(const bool on)426 void portapack_reference_oscillator(const bool on) {
427 const uint8_t mask = 1 << 6;
428 portapack_if.io_reg = (portapack_if.io_reg & ~mask) | (on ? mask : 0);
429 portapack_io_write(1, portapack_if.io_reg);
430 }
431
portapack_fill_rectangle(const ui_rect_t rect,const ui_color_t color)432 void portapack_fill_rectangle(
433 const ui_rect_t rect,
434 const ui_color_t color
435 ) {
436 portapack_lcd_start_ram_write(rect);
437 portapack_lcd_write_pixels_color(color, rect.size.width * rect.size.height);
438 }
439
portapack_clear_display(const ui_color_t color)440 void portapack_clear_display(const ui_color_t color) {
441 const ui_rect_t rect_screen = { { 0, 0 }, { 240, 320 } };
442 portapack_fill_rectangle(rect_screen, color);
443 }
444
portapack_draw_bitmap(const ui_point_t point,const ui_bitmap_t bitmap,const ui_color_t foreground,const ui_color_t background)445 void portapack_draw_bitmap(
446 const ui_point_t point,
447 const ui_bitmap_t bitmap,
448 const ui_color_t foreground,
449 const ui_color_t background
450 ) {
451 const ui_rect_t rect = {
452 .point = point,
453 .size = bitmap.size
454 };
455
456 portapack_lcd_start_ram_write(rect);
457
458 const size_t count = bitmap.size.width * bitmap.size.height;
459 for(size_t i=0; i<count; i++) {
460 const uint8_t pixel = bitmap.data[i >> 3] & (1U << (i & 0x7));
461 portapack_lcd_write_pixel(pixel ? foreground : background);
462 }
463 }
464
portapack_font_glyph(const ui_font_t * const font,const char c)465 ui_bitmap_t portapack_font_glyph(
466 const ui_font_t* const font,
467 const char c
468 ) {
469 if( c >= font->c_start ) {
470 const uint_fast8_t index = c - font->c_start;
471 if( index < font->c_count ) {
472 const ui_bitmap_t bitmap = {
473 .size = font->glyph_size,
474 .data = &font->data[index * font->data_stride]
475 };
476 return bitmap;
477 }
478 }
479
480 const ui_bitmap_t bitmap = {
481 .size = font->glyph_size,
482 .data = font->data,
483 };
484 return bitmap;
485 }
486
jtag_pp_tck(const bool tms_value)487 static bool jtag_pp_tck(const bool tms_value) {
488 gpio_write(jtag_cpld.gpio->gpio_pp_tms, tms_value);
489
490 // 8 ns TMS/TDI to TCK setup
491 __asm__("nop");
492 __asm__("nop");
493 __asm__("nop");
494
495 gpio_set(jtag_cpld.gpio->gpio_tck);
496
497 // 15 ns TCK to TMS/TDI hold time
498 // 20 ns TCK high time
499 __asm__("nop");
500 __asm__("nop");
501 __asm__("nop");
502 __asm__("nop");
503 __asm__("nop");
504
505 gpio_clear(jtag_cpld.gpio->gpio_tck);
506
507 // 20 ns TCK low time
508 // 25 ns TCK falling edge to TDO valid
509 __asm__("nop");
510 __asm__("nop");
511 __asm__("nop");
512 __asm__("nop");
513 __asm__("nop");
514 __asm__("nop");
515 __asm__("nop");
516
517 return gpio_read(jtag_cpld.gpio->gpio_pp_tdo);
518 }
519
jtag_pp_shift(const uint32_t tms_bits,const size_t count)520 static uint32_t jtag_pp_shift(const uint32_t tms_bits, const size_t count) {
521 uint32_t result = 0;
522 size_t bit_in_index = count - 1;
523 size_t bit_out_index = 0;
524 while(bit_out_index < count) {
525 const uint32_t tdo = jtag_pp_tck((tms_bits >> bit_in_index) & 1) & 1;
526 result |= (tdo << bit_out_index);
527
528 bit_in_index--;
529 bit_out_index++;
530 }
531
532 return result;
533 }
534
jtag_pp_idcode(void)535 static uint32_t jtag_pp_idcode(void) {
536 cpld_jtag_take(&jtag_cpld);
537
538 /* TODO: Check if PortaPack TMS is floating or driven by an external device. */
539 gpio_output(jtag_cpld.gpio->gpio_pp_tms);
540
541 /* Test-Logic/Reset -> Run-Test/Idle -> Select-DR/Scan -> Capture-DR */
542 jtag_pp_shift(0b11111010, 8);
543
544 /* Shift-DR */
545 const uint32_t idcode = jtag_pp_shift(0, 32);
546
547 /* Exit1-DR -> Update-DR -> Run-Test/Idle -> ... -> Test-Logic/Reset */
548 jtag_pp_shift(0b11011111, 8);
549
550 cpld_jtag_release(&jtag_cpld);
551
552 return idcode;
553 }
554
portapack_detect(void)555 static bool portapack_detect(void) {
556 return jtag_pp_idcode() == 0x020A50DD;
557 }
558
559 static const portapack_t portapack_instance = {
560 };
561
562 static const portapack_t* portapack_pointer = NULL;
563
portapack(void)564 const portapack_t* portapack(void) {
565 return portapack_pointer;
566 }
567
portapack_init(void)568 void portapack_init(void) {
569 if( portapack_detect() ) {
570 portapack_if_init();
571 portapack_lcd_reset();
572 portapack_lcd_init();
573 portapack_pointer = &portapack_instance;
574 } else {
575 portapack_pointer = NULL;
576 }
577 }