1 /*
2  * (C) Copyright 2011 Henrik Nordstrom <henrik@henriknordstrom.net>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307 USA
18  */
19 
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #ifndef NO_MMAP
28   #include <sys/mman.h>
29 #endif
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 
34 #include "common.h"
35 #include "portable_endian.h"
36 
37 #define PIO_REG_SIZE 0x228 /*0x300*/
38 #define PIO_PORT_SIZE 0x24
39 
40 struct pio_status {
41 	int mul_sel;
42 	int pull;
43 	int drv_level;
44 	int data;
45 };
46 
47 #define PIO_REG_CFG(B, N, I)	((B) + (N)*0x24 + ((I)<<2) + 0x00)
48 #define PIO_REG_DLEVEL(B, N, I)	((B) + (N)*0x24 + ((I)<<2) + 0x14)
49 #define PIO_REG_PULL(B, N, I)	((B) + (N)*0x24 + ((I)<<2) + 0x1C)
50 #define PIO_REG_DATA(B, N)	((B) + (N)*0x24 + 0x10)
51 #define PIO_NR_PORTS		9 /* A-I */
52 
53 #define LE32TOH(X)		le32toh(*((uint32_t*)(X)))
54 
pio_get(const char * buf,uint32_t port,uint32_t port_num,struct pio_status * pio)55 static int pio_get(const char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio)
56 {
57 	uint32_t val;
58 	uint32_t port_num_func, port_num_pull;
59 	uint32_t offset_func, offset_pull;
60 
61 	port_num_func = port_num >> 3;
62 	offset_func = ((port_num & 0x07) << 2);
63 
64 	port_num_pull = port_num >> 4;
65 	offset_pull = ((port_num & 0x0f) << 1);
66 
67 	/* func */
68 	val = LE32TOH(PIO_REG_CFG(buf, port, port_num_func));
69 	pio->mul_sel = (val>>offset_func) & 0x07;
70 
71 	/* pull */
72 	val = LE32TOH(PIO_REG_PULL(buf, port, port_num_pull));
73 	pio->pull = (val>>offset_pull) & 0x03;
74 
75 	/* dlevel */
76 	val = LE32TOH(PIO_REG_DLEVEL(buf, port, port_num_pull));
77 	pio->drv_level = (val>>offset_pull) & 0x03;
78 
79 	/* i/o data */
80 	if (pio->mul_sel > 1)
81 		pio->data = -1;
82 	else {
83 		val = LE32TOH(PIO_REG_DATA(buf, port));
84 		pio->data = (val >> port_num) & 0x01;
85 	}
86 	return 1;
87 }
88 
pio_set(char * buf,uint32_t port,uint32_t port_num,struct pio_status * pio)89 static int pio_set(char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio)
90 {
91 	uint32_t *addr, val;
92 	uint32_t port_num_func, port_num_pull;
93 	uint32_t offset_func, offset_pull;
94 
95 	port_num_func = port_num >> 3;
96 	offset_func = ((port_num & 0x07) << 2);
97 
98 	port_num_pull = port_num >> 4;
99 	offset_pull = ((port_num & 0x0f) << 1);
100 
101 	/* func */
102 	if (pio->mul_sel >= 0) {
103 		addr = (uint32_t*)PIO_REG_CFG(buf, port, port_num_func);
104 		val = le32toh(*addr);
105 		val &= ~(0x07 << offset_func);
106 		val |=  (pio->mul_sel & 0x07) << offset_func;
107 		*addr = htole32(val);
108 	}
109 
110 	/* pull */
111 	if (pio->pull >= 0) {
112 		addr = (uint32_t*)PIO_REG_PULL(buf, port, port_num_pull);
113 		val = le32toh(*addr);
114 		val &= ~(0x03 << offset_pull);
115 		val |=  (pio->pull & 0x03) << offset_pull;
116 		*addr = htole32(val);
117 	}
118 
119 	/* dlevel */
120 	if (pio->drv_level >= 0) {
121 		addr = (uint32_t*)PIO_REG_DLEVEL(buf, port, port_num_pull);
122 		val = le32toh(*addr);
123 		val &= ~(0x03 << offset_pull);
124 		val |=  (pio->drv_level & 0x03) << offset_pull;
125 		*addr = htole32(val);
126 	}
127 
128 	/* data */
129 	if (pio->data >= 0) {
130 		addr = (uint32_t*)PIO_REG_DATA(buf, port);
131 		val = le32toh(*addr);
132 		if (pio->data)
133 			val |= (0x01 << port_num);
134 		else
135 			val &= ~(0x01 << port_num);
136 		*addr = htole32(val);
137 	}
138 
139 	return 1;
140 }
141 
pio_print(int port,int port_nr,struct pio_status * pio)142 static void pio_print(int port, int port_nr, struct pio_status *pio)
143 {
144 	printf("P%c%d", 'A'+port, port_nr);
145 	printf("<%x>", pio->mul_sel);
146 	printf("<%x>", pio->pull);
147 	printf("<%x>", pio->drv_level);
148 	if (pio->data >= 0)
149 		printf("<%x>", pio->data);
150 	fputc('\n', stdout);
151 }
152 
print(const char * buf)153 static void print(const char *buf)
154 {
155 	int port, i;
156 	struct pio_status pio;
157 	for (port=0; port < PIO_NR_PORTS; port++) {
158 		for (i=0; i<32; i++) {
159 			if (pio_get(buf, port, i, &pio)) {
160 				pio_print(port, i, &pio);
161 			}
162 		}
163 	}
164 }
165 
166 static const char *argv0;
167 
usage(int rc)168 static void usage(int rc )
169 {
170 	fputs("sunxi-pio " VERSION "\n\n", stderr);
171 	fprintf(stderr, "usage: %s -m|-i input [-o output] pin..\n", argv0);
172 	fprintf(stderr," -m				mmap - read pin state from system\n");
173 	fprintf(stderr," -i				read pin state from file\n");
174 	fprintf(stderr," -o				save pin state data to file\n");
175 	fprintf(stderr," print				Show all pins\n");
176 	fprintf(stderr," Pxx				Show pin\n");
177 	fprintf(stderr," Pxx<mode><pull><drive><data>	Configure pin\n");
178 	fprintf(stderr," Pxx=data,drive			Configure GPIO output\n");
179 	fprintf(stderr," Pxx*count			Oscillate GPIO output (mmap mode only)\n");
180 	fprintf(stderr," Pxx?pull			Configure GPIO input\n");
181 	fprintf(stderr," clean				Clean input pins\n");
182 	fprintf(stderr, "\n	mode 0-7, 0=input, 1=ouput, 2-7 I/O function\n");
183 	fprintf(stderr, "	pull 0=none, 1=up, 2=down\n");
184 	fprintf(stderr, "	drive 0-3, I/O drive level\n");
185 
186 	exit(rc);
187 }
188 
parse_pin(int * port,int * pin,const char * name)189 static void parse_pin(int *port, int *pin, const char *name)
190 {
191 	if (*name == 'P') name++;
192 	*port = *name++ - 'A';
193 	*pin = atoi(name);
194 }
195 
cmd_show_pin(char * buf,const char * pin)196 static void cmd_show_pin(char *buf, const char *pin)
197 {
198 	int port, port_nr;
199 	struct pio_status pio;
200 	parse_pin(&port, &port_nr, pin);
201 	if (!pio_get(buf, port, port_nr, &pio))
202 		usage(1);
203     	pio_print(port, port_nr, &pio);
204 }
205 
parse_int(int * dst,const char * in)206 static int parse_int(int *dst, const char *in)
207 {
208 	int value;
209 	char *next;
210 	errno = 0;
211 	value = strtol(in, &next, 0);
212 	if (!errno && next != in) {
213 		*dst = value;
214 		return 0;
215 	}
216 	return -1;
217 }
218 
cmd_set_pin(char * buf,const char * pin)219 static void cmd_set_pin(char *buf, const char *pin)
220 {
221 	int port, port_nr;
222 	const char *t = pin;
223 	struct pio_status pio;
224 	parse_pin(&port, &port_nr, pin);
225 	if (!pio_get(buf, port, port_nr, &pio))
226 		usage(1);
227 	if ((t = strchr(pin, '='))) {
228 		pio.mul_sel = 1;
229 		if (t) {
230 			t++;
231 			parse_int(&pio.data, t);
232 		}
233 		if (t)
234 			t = strchr(t, ',');
235 		if (t) {
236 			t++;
237 			parse_int(&pio.drv_level, t);
238 		}
239 	} else if ((t = strchr(pin, '?'))) {
240 		pio.mul_sel = 0;
241 		pio.data = 0;
242 		pio.drv_level = 0;
243 		if (t) {
244 			t++;
245 			parse_int(&pio.pull, t);
246 		}
247 	} else if ((t = strchr(pin, '<'))) {
248 		if (t) {
249 			t++;
250 			parse_int(&pio.mul_sel, t);
251 		}
252 		if (t)
253 			t = strchr(t, '<');
254 		if (t) {
255 			t++;
256 			parse_int(&pio.pull, t);
257 		}
258 		if (t)
259 			t = strchr(t, '<');
260 		if (t) {
261 			t++;
262 			parse_int(&pio.drv_level, t);
263 		}
264 		if (t)
265 			t = strchr(t, '<');
266 		if (t) {
267 			t++;
268 			parse_int(&pio.data, t);
269 		}
270 	}
271 	pio_set(buf, port, port_nr, &pio);
272 }
273 
cmd_oscillate(char * buf,const char * pin)274 static void cmd_oscillate(char *buf, const char *pin)
275 {
276 	int port, port_nr;
277 	const char *t = pin;
278 	int i, n = 0;
279 	uint32_t *addr, val;
280 
281 	parse_pin(&port, &port_nr, pin);
282 	{
283 		struct pio_status pio;
284 		if (!pio_get(buf, port, port_nr, &pio))
285 			usage(1);
286 		pio.mul_sel = 1;
287 		pio_set(buf, port, port_nr, &pio);
288 	}
289 
290 	addr = (uint32_t*)PIO_REG_DATA(buf, port);
291 	t = strchr(pin, '*');
292 	parse_int(&n, t+1);
293 	val = le32toh(*addr);
294 	for (i = 0; i < n; i++) {
295 		val ^= 1 << port_nr;
296 		*addr = htole32(val);
297 	}
298 }
299 
cmd_clean(char * buf)300 static void cmd_clean(char *buf)
301 {
302 	int port, i;
303 	struct pio_status pio;
304 	for (port=0; port < PIO_NR_PORTS; port++) {
305 		for (i=0; i<32; i++) {
306 			if (pio_get(buf, port, i, &pio)) {
307 				if (pio.mul_sel == 0) {
308 					pio.data = 0;
309 					pio_set(buf, port, i, &pio);
310 				}
311 			}
312 		}
313 	}
314 }
315 
do_command(char * buf,const char ** args,int UNUSED (argc))316 static int do_command(char *buf, const char **args, int UNUSED(argc))
317 {
318 	const char *command = args[0];
319 	if (*command == 'P') {
320 		if (strchr(command, '<'))
321 			cmd_set_pin(buf, command);
322 		else if (strchr(command, '='))
323 			cmd_set_pin(buf, command);
324 		else if (strchr(command, '?'))
325 			cmd_set_pin(buf, command);
326 		else if (strchr(command, '*'))
327 			cmd_oscillate(buf, command);
328 		else
329 			cmd_show_pin(buf, command);
330 	}
331 	else if (strcmp(command, "print") == 0)
332 		print(buf);
333 	else if (strcmp(command, "clean") == 0)
334 		cmd_clean(buf);
335 	else	usage(1);
336 	return 1;
337 }
338 
main(int argc,char ** argv)339 int main(int argc, char **argv)
340 {
341 	int opt;
342 	FILE *in = NULL;
343 	FILE *out = NULL;
344 	const char *in_name = NULL;
345 	const char *out_name = NULL;
346 	char buf_[PIO_REG_SIZE];
347 	char *buf = buf_;
348 	int do_mmap = 0;
349 
350 	argv0 = argv[0];
351 
352 	while ((opt = getopt(argc, argv, "i:o:m")) != -1) {
353 		switch(opt) {
354 		case '?':
355 			usage(0);
356 		case 'm':
357 			do_mmap = 1;
358 			break;
359 		case 'i':
360 			in_name = optarg;
361 			break;
362 		case 'o':
363 			out_name = optarg;
364 			break;
365 		}
366 	}
367 	if (!in_name && !do_mmap)
368 		usage(1);
369 	if (do_mmap) {
370 #ifdef NO_MMAP
371 		errno = ENOSYS; /* Function not implemented */
372 		perror("mmap PIO");
373 #else
374 		int pagesize = sysconf(_SC_PAGESIZE);
375 		int fd = open("/dev/mem",O_RDWR);
376 		int addr = 0x01c20800 & ~(pagesize-1);
377 		int offset = 0x01c20800 & (pagesize-1);
378 		if (fd == -1) {
379 			perror("open /dev/mem");
380 			exit(1);
381 		}
382 		buf = mmap(NULL, (0x800 + pagesize - 1) & ~(pagesize-1), PROT_WRITE|PROT_READ, MAP_SHARED, fd, addr);
383 		if (!buf) {
384 			perror("mmap PIO");
385 			exit(1);
386 		}
387 		close(fd);
388 		buf += offset;
389 #endif
390 	}
391 	if (in_name) {
392 		if (strcmp(in_name, "-") == 0) {
393 			in = stdin;
394 		} else {
395 			in = fopen(in_name, "rb");
396 			if (!in) {
397 				perror("open input");
398 				exit(1);
399 			}
400 		}
401 	}
402 	if (in) {
403 		if (fread(buf, PIO_REG_SIZE, 1, in) != 1) {
404 			perror("read input");
405 			exit(1);
406 		}
407 		if (in != stdin)
408 			fclose(in);
409 	}
410 
411 	while(optind < argc) {
412 		optind += do_command(buf, (const char **)(argv + optind), argc - optind);
413 	}
414 
415 	if (out_name) {
416 		if (strcmp(out_name, "-") == 0) {
417 			out = stdout;
418 		} else {
419 			out = fopen(out_name, "wb");
420 			if (!out) {
421 				perror("open output");
422 				exit(1);
423 			}
424 		}
425 		if (fwrite(buf, PIO_REG_SIZE, 1, out) != 1) {
426 			perror("write output");
427 			exit(1);
428 		}
429 	}
430 }
431