/*
 * Control GPIO pins on the fly
 *
 * Copyright (c) 2008-2010 Analog Devices Inc.
 *
 * Licensed under the GPL-2 or later.
 */

#include <common.h>
#include <command.h>
#include <linux/ctype.h>

#include <asm/blackfin.h>
#include <asm/gpio.h>

enum {
	GPIO_INPUT,
	GPIO_SET,
	GPIO_CLEAR,
	GPIO_TOGGLE,
};

int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	if (argc == 2 && !strcmp(argv[1], "status")) {
		bfin_gpio_labels();
		return 0;
	}

	if (argc != 3)
 show_usage:
		return cmd_usage(cmdtp);

	/* parse the behavior */
	ulong sub_cmd;
	switch (argv[1][0]) {
		case 'i': sub_cmd = GPIO_INPUT;  break;
		case 's': sub_cmd = GPIO_SET;    break;
		case 'c': sub_cmd = GPIO_CLEAR;  break;
		case 't': sub_cmd = GPIO_TOGGLE; break;
		default:  goto show_usage;
	}

	/* parse the pin with format: [p][port]<#> */
	const char *str_pin = argv[2];

	/* grab the [p]<port> portion */
	ulong port_base;
	if (tolower(*str_pin) == 'p') ++str_pin;
	switch (tolower(*str_pin)) {
#ifdef GPIO_PA0
		case 'a': port_base = GPIO_PA0; break;
#endif
#ifdef GPIO_PB0
		case 'b': port_base = GPIO_PB0; break;
#endif
#ifdef GPIO_PC0
		case 'c': port_base = GPIO_PC0; break;
#endif
#ifdef GPIO_PD0
		case 'd': port_base = GPIO_PD0; break;
#endif
#ifdef GPIO_PE0
		case 'e': port_base = GPIO_PE0; break;
#endif
#ifdef GPIO_PF0
		case 'f': port_base = GPIO_PF0; break;
#endif
#ifdef GPIO_PG0
		case 'g': port_base = GPIO_PG0; break;
#endif
#ifdef GPIO_PH0
		case 'h': port_base = GPIO_PH0; break;
#endif
#ifdef GPIO_PI0
		case 'i': port_base = GPIO_PI0; break;
#endif
#ifdef GPIO_PJ
		case 'j': port_base = GPIO_PJ0; break;
#endif
		default:  goto show_usage;
	}

	/* grab the <#> portion */
	ulong pin = simple_strtoul(str_pin + 1, NULL, 10);
	if (pin > 15)
		goto show_usage;

	/* grab the pin before we tweak it */
	ulong gpio = port_base + pin;
	gpio_request(gpio, "cmd_gpio");

	/* finally, let's do it: set direction and exec command */
	ulong value;
	if (sub_cmd == GPIO_INPUT) {
		gpio_direction_input(gpio);
		value = gpio_get_value(gpio);
	} else {
		switch (sub_cmd) {
			case GPIO_SET:    value = 1; break;
			case GPIO_CLEAR:  value = 0; break;
			case GPIO_TOGGLE: value = !gpio_get_value(gpio); break;
			default:          goto show_usage;
		}
		gpio_direction_output(gpio, value);
	}
	printf("gpio: pin %lu on port %c (gpio %lu) value is %lu\n",
		pin, *str_pin, gpio, value);

	gpio_free(gpio);

	return value;
}

U_BOOT_CMD(gpio, 3, 0, do_gpio,
	"input/set/clear/toggle gpio output pins",
	"<input|set|clear|toggle> <port><pin>\n"
	"    - input/set/clear/toggle the specified pin (e.g. PF10)");
