#pragma once

#ifndef _GPIO_H
#define _GPIO_H

#include "stm32g0xx.h"

/* x = A to F */

/* GPIO port mode register (GPIOx_MODER) */
#define GPIO_MODE_IN    0x0
#define GPIO_MODE_OUT   0x1
#define GPIO_MODE_AFF   0x2
#define GPIO_MODE_ANL   0x3

/* GPIO port output type register (GPIOx_OTYPER) */
#define GPIO_OTYPE_PP   0x0
#define GPIO_OTYPE_OD   0x1

/* GPIO port output speed register (GPIOx_OSPEEDR) */
#define GPIO_OSPEED_VL  0x0
#define GPIO_OSPEED_LW  0x1
#define GPIO_OSPEED_HI  0x2
#define GPIO_OSPEED_VH  0x3

/* GPIO port pull-up/pull-down register (GPIOx_PUPDR) */
#define GPIO_PUPDR_NO   0x0
#define GPIO_PUPDR_UP   0x1
#define GPIO_PUPDR_DW   0x2
#define GPIO_PUPDR_NN   0x3

/* GPIO port input data register (GPIOx_IDR) */

/* GPIO port output data register (GPIOx_ODR) */

/* GPIO port bit set/reset register (GPIOx_BSRR) */

/* GPIO port bit reset register (GPIOx_BRR) (x = A to F) */

/* GPIO port configuration lock register (GPIOx_LCKR) */

/* GPIO alternate function high/low register (GPIOx_AFRL/GPIOx_AFRH) for pin 0..7/8..15 */
#define GPIO_AF_0   	0x0
#define GPIO_AF_1   	0x1
#define GPIO_AF_2   	0x2
#define GPIO_AF_3   	0x3
#define GPIO_AF_4   	0x4
#define GPIO_AF_5   	0x5
#define GPIO_AF_6   	0x6
#define GPIO_AF_7   	0x7

/* PORT = GPIOA..GPIOF, PIN = (1<<0..15) */
#define GPIO_PIN_SET(PORT, PIN)    ((PORT->BSRR) = (PIN))
#define GPIO_PIN_RES(PORT, PIN)    ((PORT->BRR) = (PIN))

/* Bits definition for GPIO PINs */
#define GPIO_PIN_0    0x0001
#define GPIO_PIN_1    0x0002
#define GPIO_PIN_2    0x0004
#define GPIO_PIN_3    0x0008
#define GPIO_PIN_4    0x0010
#define GPIO_PIN_5    0x0020
#define GPIO_PIN_6    0x0040
#define GPIO_PIN_7    0x0080
#define GPIO_PIN_8    0x0100
#define GPIO_PIN_9    0x0200
#define GPIO_PIN_10   0x0400
#define GPIO_PIN_11   0x0800
#define GPIO_PIN_12   0x1000
#define GPIO_PIN_13   0x2000
#define GPIO_PIN_14   0x4000
#define GPIO_PIN_15   0x8000

/* functions from stm32g0xx_ll_gpio.h */
static inline void GPIO_SetPinMode(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Mode) {
  MODIFY_REG(GPIOx->MODER, ((Pin * Pin) * GPIO_MODER_MODE0), ((Pin * Pin) * Mode));
}

static inline void GPIO_SetPinOutputType(GPIO_TypeDef *GPIOx, uint32_t PinMask, uint32_t OutputType) {
  MODIFY_REG(GPIOx->OTYPER, PinMask, (PinMask * OutputType));
}

static inline void GPIO_SetPinSpeed(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t  Speed) {
  MODIFY_REG(GPIOx->OSPEEDR, ((Pin * Pin) * GPIO_OSPEEDR_OSPEED0), ((Pin * Pin) * Speed));
}

static inline void GPIO_SetPinPull(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Pull) {
  MODIFY_REG(GPIOx->PUPDR, ((Pin * Pin) * GPIO_PUPDR_PUPD0), ((Pin * Pin) * Pull));
}

static inline void GPIO_SetAFPin_0_7(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Alternate) {
  MODIFY_REG(GPIOx->AFR[0], ((((Pin * Pin) * Pin) * Pin) * GPIO_AFRL_AFSEL0),
             ((((Pin * Pin) * Pin) * Pin) * Alternate));
}

static inline void GPIO_SetAFPin_8_15(GPIO_TypeDef *GPIOx, uint32_t Pin, uint32_t Alternate) {
  MODIFY_REG(GPIOx->AFR[1], (((((Pin >> 8U) * (Pin >> 8U)) * (Pin >> 8U)) * (Pin >> 8U)) * GPIO_AFRH_AFSEL8),
             (((((Pin >> 8U) * (Pin >> 8U)) * (Pin >> 8U)) * (Pin >> 8U)) * Alternate));
}

#endif
