/** @file gpio.h
 * Module handling General Purpose Input/Output (GPIO) pins
 *
 * The module provides functions to configure the gpio pins and read from/
 * write to them
 */

#ifndef _GPIO_H_
#define _GPIO_H_

//--includes--------------------------------------------------------------------

#include "stdint.h"
#include "stdbool.h"


//--type definitions------------------------------------------------------------

/**
 * Available GPIO ports. Note that not all ports may be available for a
 * particular chip
 */
enum GpioPort {
	GPIO_PORT_A = 0,
	GPIO_PORT_B,
	GPIO_PORT_C,
	GPIO_PORT_D,
	GPIO_PORT_E,
	GPIO_PORT_F,
	GPIO_PORT_G,
};

/**
 * Availables pin for a particular port. Note that all ports available may not
 * have all the pins listed here and that some pins may be used by defaut (jtag,
 * ...)
 */
enum GpioPin {
	GPIO_PIN_0  = (0x1 <<  0),
	GPIO_PIN_1  = (0x1 <<  1),
	GPIO_PIN_2  = (0x1 <<  2),
	GPIO_PIN_3  = (0x1 <<  3),
	GPIO_PIN_4  = (0x1 <<  4),
	GPIO_PIN_5  = (0x1 <<  5),
	GPIO_PIN_6  = (0x1 <<  6),
	GPIO_PIN_7  = (0x1 <<  7),
	GPIO_PIN_8  = (0x1 <<  8),
	GPIO_PIN_9  = (0x1 <<  9),
	GPIO_PIN_10 = (0x1 << 10),
	GPIO_PIN_11 = (0x1 << 11),
	GPIO_PIN_12 = (0x1 << 12),
	GPIO_PIN_13 = (0x1 << 13),
	GPIO_PIN_14 = (0x1 << 14),
	GPIO_PIN_15 = (0x1 << 15),
};

/**
 * Available modes for a pin. To have a pin in both modes simultaneously, use
 * push-pull configuration in input mode but note that the power may be
 * limited
 */
enum GpioMode {
	GPIO_MODE_INPUT = 0,
	GPIO_MODE_OUTPUT,      //10MHz max
	GPIO_MODE_OUTPUT_SLOW, //2MHz max
	GPIO_MODE_OUTPUT_FAST, //50MHz max
};

/**
 * Available configurations for a pin. Some configurations only apply in input
 * mode (denoted "IN") while others only apply in output mode (denoted "OUT")
 */
enum GpioConfig {
	GPIO_CONFIG_IN_ANALOG          = 0,
	GPIO_CONFIG_IN_FLOATING,
	GPIO_CONFIG_IN_PUSH_PULL,      //use gpio_write() to configure pull_up/down
	GPIO_CONFIG_OUT_PUSH_PULL      = 0,
	GPIO_CONFIG_OUT_OPEN_DRAIN,
	GPIO_CONFIG_OUT_ALT_PUSH_PULL,
	GPIO_CONFIG_OUT_ALT_OPEN_DRAIN,
};


//--functions-------------------------------------------------------------------

/**
 * Configures gpio pins on a single port. The GpioPin enum can be used as mask
 * to configure multiple pins at the same time. Before configuring a pin, make
 * sure it isn't used somewhere else (peripherals, jtag, ...). After
 * configuration, the selected output pins are set to output 0V
 */
void gpio_configure(enum GpioPort port, enum GpioPin pin_mask,
		enum GpioMode mode, enum GpioConfig config);

/**
 * Resets gpio pins on a single port. The GpioPin enum can be used as mask
 * to reset multiple pins at the same time. This is a simple wrapper over
 * gpio_configure(), setting the selected ports as floating input
 */
void gpio_reset(enum GpioPort port, enum GpioPin pin_mask);

/**
 * Write a value to output gpios on a single port. The GpioPin enum can be used
 * as mask to reset multiple pins at the same time. In push-pull input mode,
 * configure the pull up/down. Has no effect otherwise. Writes are atomic
 * operations
 */
void gpio_write(enum GpioPort port, enum GpioPin pin_mask, bool value);

/**
 * Reads the value of gpios on a single port. The GpioPin enum can be used
 * as mask to read multiple pins at the same time. For output pins, reads the
 * pin state. Returns true if all selected ports are high
 */
bool gpio_read(enum GpioPort port, enum GpioPin pin_mask);

/**
 * Resets all gpios and disable all ports. The gpio peripherals are returned to
 * their reset configuration and their respective clocks disabled
 */
void gpio_reset_peripheral(void);


#endif //_GPIO_H_

