]> git.sesse.net Git - pistorm/blob - gpio/ps_protocol.c
[WIP] Pile of stuff
[pistorm] / gpio / ps_protocol.c
1 /*
2   Original Copyright 2020 Claude Schwarz
3   Code reorganized and rewritten by 
4   Niklas Ekström 2021 (https://github.com/niklasekstrom)
5 */
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include "ps_protocol.h"
17
18 volatile unsigned int *gpio;
19 volatile unsigned int *gpclk;
20
21 unsigned int gpfsel0;
22 unsigned int gpfsel1;
23 unsigned int gpfsel2;
24
25 unsigned int gpfsel0_o;
26 unsigned int gpfsel1_o;
27 unsigned int gpfsel2_o;
28
29 static void setup_io() {
30   int fd = open("/dev/mem", O_RDWR | O_SYNC);
31   if (fd < 0) {
32     printf("Unable to open /dev/mem. Run as root using sudo?\n");
33     exit(-1);
34   }
35
36   void *gpio_map = mmap(
37       NULL,                    // Any adddress in our space will do
38       BCM2708_PERI_SIZE,       // Map length
39       PROT_READ | PROT_WRITE,  // Enable reading & writting to mapped memory
40       MAP_SHARED,              // Shared with other processes
41       fd,                      // File to map
42       BCM2708_PERI_BASE        // Offset to GPIO peripheral
43   );
44
45   close(fd);
46
47   if (gpio_map == MAP_FAILED) {
48     printf("mmap failed, errno = %d\n", errno);
49     exit(-1);
50   }
51
52   gpio = ((volatile unsigned *)gpio_map) + GPIO_ADDR / 4;
53   gpclk = ((volatile unsigned *)gpio_map) + GPCLK_ADDR / 4;
54 }
55
56 static void setup_gpclk() {
57   // Enable 200MHz CLK output on GPIO4, adjust divider and pll source depending
58   // on pi model
59   *(gpclk + (CLK_GP0_CTL / 4)) = CLK_PASSWD | (1 << 5);
60   usleep(10);
61   while ((*(gpclk + (CLK_GP0_CTL / 4))) & (1 << 7))
62     ;
63   usleep(100);
64   *(gpclk + (CLK_GP0_DIV / 4)) =
65       CLK_PASSWD | (6 << 12);  // divider , 6=200MHz on pi3
66   usleep(10);
67   *(gpclk + (CLK_GP0_CTL / 4)) =
68       CLK_PASSWD | 5 | (1 << 4);  // pll? 6=plld, 5=pllc
69   usleep(10);
70   while (((*(gpclk + (CLK_GP0_CTL / 4))) & (1 << 7)) == 0)
71     ;
72   usleep(100);
73
74   SET_GPIO_ALT(PIN_CLK, 0);  // gpclk0
75 }
76
77 void ps_setup_protocol() {
78   setup_io();
79   setup_gpclk();
80
81   *(gpio + 10) = 0xffffec;
82
83   *(gpio + 0) = GPFSEL0_INPUT;
84   *(gpio + 1) = GPFSEL1_INPUT;
85   *(gpio + 2) = GPFSEL2_INPUT;
86 }
87
88 void ps_write_16(unsigned int address, unsigned int data) {
89   *(gpio + 0) = GPFSEL0_OUTPUT;
90   *(gpio + 1) = GPFSEL1_OUTPUT;
91   *(gpio + 2) = GPFSEL2_OUTPUT;
92
93   *(gpio + 7) = ((data & 0xffff) << 8) | (REG_DATA << PIN_A0);
94   *(gpio + 7) = 1 << PIN_WR;
95   *(gpio + 10) = 1 << PIN_WR;
96   *(gpio + 10) = 0xffffec;
97
98   *(gpio + 7) = ((address & 0xffff) << 8) | (REG_ADDR_LO << PIN_A0);
99   *(gpio + 7) = 1 << PIN_WR;
100   *(gpio + 10) = 1 << PIN_WR;
101   *(gpio + 10) = 0xffffec;
102
103   *(gpio + 7) = ((0x0000 | (address >> 16)) << 8) | (REG_ADDR_HI << PIN_A0);
104   *(gpio + 7) = 1 << PIN_WR;
105   *(gpio + 10) = 1 << PIN_WR;
106   *(gpio + 10) = 0xffffec;
107
108   *(gpio + 0) = GPFSEL0_INPUT;
109   *(gpio + 1) = GPFSEL1_INPUT;
110   *(gpio + 2) = GPFSEL2_INPUT;
111
112   while (*(gpio + 13) & (1 << PIN_TXN_IN_PROGRESS))
113     ;
114 }
115
116 void ps_write_8(unsigned int address, unsigned int data) {
117   if ((address & 1) == 0)
118     data = data + (data << 8);  // EVEN, A0=0,UDS
119   else
120     data = data & 0xff;  // ODD , A0=1,LDS
121
122   *(gpio + 0) = GPFSEL0_OUTPUT;
123   *(gpio + 1) = GPFSEL1_OUTPUT;
124   *(gpio + 2) = GPFSEL2_OUTPUT;
125
126   *(gpio + 7) = ((data & 0xffff) << 8) | (REG_DATA << PIN_A0);
127   *(gpio + 7) = 1 << PIN_WR;
128   *(gpio + 10) = 1 << PIN_WR;
129   *(gpio + 10) = 0xffffec;
130
131   *(gpio + 7) = ((address & 0xffff) << 8) | (REG_ADDR_LO << PIN_A0);
132   *(gpio + 7) = 1 << PIN_WR;
133   *(gpio + 10) = 1 << PIN_WR;
134   *(gpio + 10) = 0xffffec;
135
136   *(gpio + 7) = ((0x0100 | (address >> 16)) << 8) | (REG_ADDR_HI << PIN_A0);
137   *(gpio + 7) = 1 << PIN_WR;
138   *(gpio + 10) = 1 << PIN_WR;
139   *(gpio + 10) = 0xffffec;
140
141   *(gpio + 0) = GPFSEL0_INPUT;
142   *(gpio + 1) = GPFSEL1_INPUT;
143   *(gpio + 2) = GPFSEL2_INPUT;
144
145   while (*(gpio + 13) & (1 << PIN_TXN_IN_PROGRESS))
146     ;
147 }
148
149 void ps_write_32(unsigned int address, unsigned int value) {
150   ps_write_16(address, value >> 16);
151   ps_write_16(address + 2, value);
152 }
153
154 unsigned int ps_read_16(unsigned int address) {
155   *(gpio + 0) = GPFSEL0_OUTPUT;
156   *(gpio + 1) = GPFSEL1_OUTPUT;
157   *(gpio + 2) = GPFSEL2_OUTPUT;
158
159   *(gpio + 7) = ((address & 0xffff) << 8) | (REG_ADDR_LO << PIN_A0);
160   *(gpio + 7) = 1 << PIN_WR;
161   *(gpio + 10) = 1 << PIN_WR;
162   *(gpio + 10) = 0xffffec;
163
164   *(gpio + 7) = ((0x0200 | (address >> 16)) << 8) | (REG_ADDR_HI << PIN_A0);
165   *(gpio + 7) = 1 << PIN_WR;
166   *(gpio + 10) = 1 << PIN_WR;
167   *(gpio + 10) = 0xffffec;
168
169   *(gpio + 0) = GPFSEL0_INPUT;
170   *(gpio + 1) = GPFSEL1_INPUT;
171   *(gpio + 2) = GPFSEL2_INPUT;
172
173   *(gpio + 7) = (REG_DATA << PIN_A0);
174   *(gpio + 7) = 1 << PIN_RD;
175
176   while (*(gpio + 13) & (1 << PIN_TXN_IN_PROGRESS))
177     ;
178
179   unsigned int value = *(gpio + 13);
180
181   *(gpio + 10) = 0xffffec;
182
183   return (value >> 8) & 0xffff;
184 }
185
186 unsigned int ps_read_8(unsigned int address) {
187   *(gpio + 0) = GPFSEL0_OUTPUT;
188   *(gpio + 1) = GPFSEL1_OUTPUT;
189   *(gpio + 2) = GPFSEL2_OUTPUT;
190
191   *(gpio + 7) = ((address & 0xffff) << 8) | (REG_ADDR_LO << PIN_A0);
192   *(gpio + 7) = 1 << PIN_WR;
193   *(gpio + 10) = 1 << PIN_WR;
194   *(gpio + 10) = 0xffffec;
195
196   *(gpio + 7) = ((0x0300 | (address >> 16)) << 8) | (REG_ADDR_HI << PIN_A0);
197   *(gpio + 7) = 1 << PIN_WR;
198   *(gpio + 10) = 1 << PIN_WR;
199   *(gpio + 10) = 0xffffec;
200
201   *(gpio + 0) = GPFSEL0_INPUT;
202   *(gpio + 1) = GPFSEL1_INPUT;
203   *(gpio + 2) = GPFSEL2_INPUT;
204
205   *(gpio + 7) = (REG_DATA << PIN_A0);
206   *(gpio + 7) = 1 << PIN_RD;
207
208   while (*(gpio + 13) & (1 << PIN_TXN_IN_PROGRESS))
209     ;
210
211   unsigned int value = *(gpio + 13);
212
213   *(gpio + 10) = 0xffffec;
214
215   value = (value >> 8) & 0xffff;
216
217   if ((address & 1) == 0)
218     return (value >> 8) & 0xff;  // EVEN, A0=0,UDS
219   else
220     return value & 0xff;  // ODD , A0=1,LDS
221 }
222
223 unsigned int ps_read_32(unsigned int address) {
224   unsigned int a = ps_read_16(address);
225   unsigned int b = ps_read_16(address + 2);
226   return (a << 16) | b;
227 }
228
229 void ps_write_status_reg(unsigned int value) {
230   *(gpio + 0) = GPFSEL0_OUTPUT;
231   *(gpio + 1) = GPFSEL1_OUTPUT;
232   *(gpio + 2) = GPFSEL2_OUTPUT;
233
234   *(gpio + 7) = ((value & 0xffff) << 8) | (REG_STATUS << PIN_A0);
235
236   *(gpio + 7) = 1 << PIN_WR;
237   *(gpio + 7) = 1 << PIN_WR;  // delay
238   *(gpio + 10) = 1 << PIN_WR;
239   *(gpio + 10) = 0xffffec;
240
241   *(gpio + 0) = GPFSEL0_INPUT;
242   *(gpio + 1) = GPFSEL1_INPUT;
243   *(gpio + 2) = GPFSEL2_INPUT;
244 }
245
246 unsigned int ps_read_status_reg() {
247   *(gpio + 7) = (REG_STATUS << PIN_A0);
248   *(gpio + 7) = 1 << PIN_RD;
249   *(gpio + 7) = 1 << PIN_RD;
250   *(gpio + 7) = 1 << PIN_RD;
251   *(gpio + 7) = 1 << PIN_RD;
252
253   unsigned int value = *(gpio + 13);
254
255   *(gpio + 10) = 0xffffec;
256
257   return (value >> 8) & 0xffff;
258 }
259
260 void ps_reset_state_machine() {
261   ps_write_status_reg(STATUS_BIT_INIT);
262   usleep(1500);
263   ps_write_status_reg(0);
264   usleep(100);
265 }
266
267 void ps_pulse_reset() {
268   ps_write_status_reg(0);
269   usleep(100000);
270   ps_write_status_reg(STATUS_BIT_RESET);
271 }
272
273 unsigned int ps_get_ipl_zero() {
274   unsigned int value = *(gpio + 13);
275   return value & (1 << PIN_IPL_ZERO);
276 }
277
278 #define INT2_ENABLED 1
279
280 void ps_update_irq() {
281   unsigned int ipl = 0;
282
283   if (!ps_get_ipl_zero()) {
284     unsigned int status = ps_read_status_reg();
285     ipl = (status & 0xe000) >> 13;
286   }
287
288   /*if (ipl < 2 && INT2_ENABLED && emu_int2_req()) {
289     ipl = 2;
290   }*/
291
292   m68k_set_irq(ipl);
293 }