﻿/*
    Part of the Raspberry-Pi Bare Metal Tutorials
    Copyright (c) 2013-2015, Brian Sidebotham
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
        this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef RPI_GPIO_H
#define RPI_GPIO_H

#include "rpi-base.h"
#include "startup.h"


/** The base address of the GPIO peripheral (ARM Physical Address) */
#define RPI_GPIO_BASE       (_get_peripheral_base() + 0x200000UL)

// Raspberry Pi3 has a differentway of controlling the LED

//#if defined(RPIZERO) || defined(RPIBPLUS) || defined(RPI2) || defined(RPI3)|| defined(RPI4)

    #define LED_GPFSEL      GPFSEL[4]
    #define LED_GPFBIT      21
    #define LED_GPSET       GPSET1
    #define LED_GPCLR       GPCLR1
    #define LED_GPIO_BIT    15
    #define LED_ON()        do { RPI_GpioBase->LED_GPCLR = (1 << LED_GPIO_BIT); } while(0)
    #define LED_OFF()       do { RPI_GpioBase->LED_GPSET = (1 << LED_GPIO_BIT); } while(0)

//#else
//    #define LED_GPFSEL      GPFSEL[1]
//    #define LED_GPFBIT      18
//    #define LED_GPSET       GPSET0
//    #define LED_GPCLR       GPCLR0
//    #define LED_GPIO_BIT    16
//    #define LED_ON()        do { RPI_GpioBase->LED_GPSET = (1 << LED_GPIO_BIT); } while(0)
//    #define LED_OFF()       do { RPI_GpioBase->LED_GPCLR = (1 << LED_GPIO_BIT); } while(0)
//#endif

typedef enum
{
    FS_INPUT = 0,
    FS_OUTPUT,
    FS_ALT5,
    FS_ALT4,
    FS_ALT0,
    FS_ALT1,
    FS_ALT2,
    FS_ALT3,
} rpi_gpio_alt_function_t;

/* A mask to be able to clear the bits in the register before setting the value we require */
#define FS_MASK     (7)

typedef enum
{
    RPI_GPIO0 = 0,
    RPI_GPIO1,
    RPI_GPIO2,
    RPI_GPIO3,
    RPI_GPIO4,
    RPI_GPIO5,
    RPI_GPIO6,
    RPI_GPIO7,
    RPI_GPIO8,
    RPI_GPIO9,
    RPI_GPIO10 = 10,
    RPI_GPIO11,
    RPI_GPIO12,
    RPI_GPIO13,
    RPI_GPIO14,
    RPI_GPIO15,
    RPI_GPIO16,
    RPI_GPIO17,
    RPI_GPIO18,
    RPI_GPIO19,
    RPI_GPIO20 = 20,
    RPI_GPIO21,
    RPI_GPIO22,
    RPI_GPIO23,
    RPI_GPIO24,
    RPI_GPIO25,
    RPI_GPIO26,
    RPI_GPIO27,
    RPI_GPIO28,
    RPI_GPIO29,
    RPI_GPIO30 = 30,
    RPI_GPIO31,
    RPI_GPIO32,
    RPI_GPIO33,
    RPI_GPIO34,
    RPI_GPIO35,
    RPI_GPIO36,
    RPI_GPIO37,
    RPI_GPIO38,
    RPI_GPIO39,
    RPI_GPIO40 = 40,
    RPI_GPIO41,
    RPI_GPIO42,
    RPI_GPIO43,
    RPI_GPIO44,
    RPI_GPIO45,
    RPI_GPIO46,
    RPI_GPIO47,
    RPI_GPIO48,
    RPI_GPIO49,
    RPI_GPIO50 = 50,
    RPI_GPIO51,
    RPI_GPIO52,
    RPI_GPIO53,
} rpi_gpio_pin_t;


/** The GPIO Peripheral is described in section 6 of the BCM2835 Peripherals
    documentation.

    There are 54 general-purpose I/O (GPIO) lines split into two banks. All
    GPIO pins have at least two alternative functions within BCM. The
    alternate functions are usually peripheral IO and a single peripheral
    may appear in each bank to allow flexibility on the choice of IO voltage.
    Details of alternative functions are given in section 6.2. Alternative
    Function Assignments.

    The GPIO peripheral has three dedicated interrupt lines. These lines are
    triggered by the setting of bits in the event detect status register. Each
    bank has its’ own interrupt line with the third line shared between all
    bits.

    The Alternate function table also has the pull state (pull-up/pull-down)
    which is applied after a power down. */
typedef struct
{
    rpi_reg_rw_t    GPFSEL[6];
    rpi_reg_ro_t    Reserved0;
    rpi_reg_wo_t    GPSET0;
    rpi_reg_wo_t    GPSET1;
    rpi_reg_ro_t    Reserved1;
    rpi_reg_wo_t    GPCLR0;
    rpi_reg_wo_t    GPCLR1;
    rpi_reg_ro_t    Reserved2;
    rpi_reg_ro_t    GPLEV0;
    rpi_reg_ro_t    GPLEV1;
    rpi_reg_ro_t    Reserved3;
    rpi_reg_rw_t    GPEDS0;
    rpi_reg_rw_t    GPEDS1;
    rpi_reg_ro_t    Reserved4;
    rpi_reg_rw_t    GPREN0;
    rpi_reg_rw_t    GPREN1;
    rpi_reg_ro_t    Reserved5;
    rpi_reg_rw_t    GPFEN0;
    rpi_reg_rw_t    GPFEN1;
    rpi_reg_ro_t    Reserved6;
    rpi_reg_rw_t    GPHEN0;
    rpi_reg_rw_t    GPHEN1;
    rpi_reg_ro_t    Reserved7;
    rpi_reg_rw_t    GPLEN0;
    rpi_reg_rw_t    GPLEN1;
    rpi_reg_ro_t    Reserved8;
    rpi_reg_rw_t    GPAREN0;
    rpi_reg_rw_t    GPAREN1;
    rpi_reg_ro_t    Reserved9;
    rpi_reg_rw_t    GPAFEN0;
    rpi_reg_rw_t    GPAFEN1;    //0x8C
    rpi_reg_ro_t    Reserved10; //0x90
    rpi_reg_rw_t    GPPUD;      // 0x94
    rpi_reg_rw_t    GPPUDCLK0;  // 0x98
    rpi_reg_rw_t    GPPUDCLK1;  // 0x9c
    rpi_reg_ro_t    Reserved11[0x11];
    rpi_reg_rw_t    GPPULL[4]; // 0xe4 Pi4 only
} rpi_gpio_t;
typedef enum
{
    RPI_IO_LO = 0,
    RPI_IO_HI,
    RPI_IO_ON,
    RPI_IO_OFF,
    RPI_IO_UNKNOWN,
} rpi_gpio_value_t;

#define GPIO_PULLNONE 0x00
#define GPIO_PULLDOWN 0x01
#define GPIO_PULLUP   0x02

extern rpi_gpio_t* RPI_GpioBase;
extern void RPI_SetGpioPinFunction(rpi_gpio_pin_t gpio, rpi_gpio_alt_function_t func);
extern void RPI_SetGpioOutput(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioInput(rpi_gpio_pin_t gpio);
extern rpi_gpio_value_t RPI_GetGpioValue(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioHi(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioLo(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioValue(rpi_gpio_pin_t gpio, rpi_gpio_value_t value);
extern void RPI_ToggleGpio(rpi_gpio_pin_t gpio);
extern void RPI_SetGpioPullUpDown(uint32_t gpio_pins, uint32_t pullup_type);
#endif
