218 lines
6.0 KiB
C
218 lines
6.0 KiB
C
/********************************************************************
|
|
* Copyright (c) 2020 Kneron, Inc. All Rights Reserved.
|
|
*
|
|
* The information contained herein is property of Kneron, Inc.
|
|
* Terms and conditions of usage are described in detail in Kneron
|
|
* STANDARD SOFTWARE LICENSE AGREEMENT.
|
|
*
|
|
* Licensees are granted free, non-transferable use of the information.
|
|
* NO WARRANTY of ANY KIND is provided. This heading must NOT be removed
|
|
* from the file.
|
|
********************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include "kdrv_cmsis_core.h"
|
|
#include "regbase.h"
|
|
#include "system_config.h"
|
|
#include "kdrv_gpio.h"
|
|
|
|
/* GPIO registers offset */
|
|
|
|
typedef volatile union {
|
|
struct
|
|
{
|
|
uint32_t DOUT_OFFSET; // 0x0
|
|
uint32_t DIN_OFFSET; // 0x4
|
|
uint32_t PINOUT_OFFSET; // 0x8
|
|
uint32_t PIN_BYPASS; // 0xC
|
|
uint32_t DATASET; // 0x10
|
|
uint32_t DATACLR; // 0x14
|
|
uint32_t PULLENABLE; // 0x18
|
|
uint32_t PULLTYPE; // 0x1C
|
|
uint32_t INT_ENABLE; // 0x20
|
|
uint32_t INT_RAWSTATE; // 0x24
|
|
uint32_t INT_MASKSTATE; // 0x28
|
|
uint32_t INT_MASK; // 0x2C
|
|
uint32_t INT_CLEAR; // 0x30
|
|
uint32_t INT_TRIGGER; // 0x34
|
|
uint32_t INT_BOTH; // 0x38
|
|
uint32_t INT_RISENEG; // 0x3C
|
|
uint32_t INT_BOUNCEENABLE; // 0x40
|
|
uint32_t INT_PRESCALE; // 0x44
|
|
} dw; //double word
|
|
} U_regGPIO;
|
|
|
|
// GPIO register base pointer
|
|
#define regGPIO ((U_regGPIO *)GPIO_FTGPIO010_PA_BASE)
|
|
|
|
// to record user callback and argument
|
|
static gpio_interrupt_callback_t _usr_isr_cb = 0;
|
|
static void *_usr_arg = 0;
|
|
|
|
// GPIO controller to NVIC ISR
|
|
static void gpio_isr(void)
|
|
{
|
|
// read GPIO interrupt pins with masked status
|
|
uint32_t intr_status = regGPIO->dw.INT_MASKSTATE;
|
|
|
|
// clear the interrupt source coming from GPIO peripheral
|
|
regGPIO->dw.INT_CLEAR = intr_status;
|
|
|
|
if (!_usr_isr_cb)
|
|
return;
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (intr_status & (0x1 << i))
|
|
{
|
|
_usr_isr_cb((kdrv_gpio_pin_t)i, _usr_arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_initialize()
|
|
{
|
|
// FIXME: enable gpio pclk (gpio_pclk[20])
|
|
uint32_t apb_clock_reg = (*(volatile unsigned int *)(SCU_FTSCU100_PA_BASE + 0x60));
|
|
apb_clock_reg |= (0x1 << 20);
|
|
(*(volatile unsigned int *)(SCU_FTSCU100_PA_BASE + 0x60)) = apb_clock_reg;
|
|
|
|
NVIC_SetVector(GPIO_FTGPIO010_IRQ, (uint32_t)gpio_isr);
|
|
|
|
// Clear and Enable SAI IRQ
|
|
NVIC_ClearPendingIRQ(GPIO_FTGPIO010_IRQ);
|
|
NVIC_EnableIRQ(GPIO_FTGPIO010_IRQ);
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_uninitialize()
|
|
{
|
|
NVIC_DisableIRQ(GPIO_FTGPIO010_IRQ);
|
|
|
|
// FIXME: disable gpio pclk (gpio_pclk[20])
|
|
uint32_t apb_clock_reg = (*(volatile unsigned int *)(SCU_FTSCU100_PA_BASE + 0x60));
|
|
apb_clock_reg &= ~(0x1 << 20);
|
|
(*(volatile unsigned int *)(SCU_FTSCU100_PA_BASE + 0x60)) = apb_clock_reg;
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_set_attribute(kdrv_gpio_pin_t pin, uint32_t attributes)
|
|
{
|
|
uint32_t pin_val = (1 << pin);
|
|
|
|
// disable interrup in case of wrong conditions
|
|
regGPIO->dw.INT_ENABLE &= ~pin_val;
|
|
regGPIO->dw.INT_MASK |= pin_val;
|
|
|
|
// set pin direction
|
|
if (attributes & GPIO_DIR_INPUT)
|
|
regGPIO->dw.PINOUT_OFFSET &= ~pin_val;
|
|
else // GPIO_DIR_OUTPUT
|
|
regGPIO->dw.PINOUT_OFFSET |= pin_val;
|
|
|
|
uint32_t edge_attributes =
|
|
(GPIO_INT_EDGE_RISING | GPIO_INT_EDGE_FALLING | GPIO_INT_EDGE_BOTH);
|
|
|
|
if (attributes & edge_attributes)
|
|
{
|
|
// edge trigger
|
|
regGPIO->dw.INT_TRIGGER &= ~pin_val;
|
|
|
|
// set both or single edge
|
|
if (attributes & GPIO_INT_EDGE_BOTH)
|
|
regGPIO->dw.INT_BOTH |= pin_val;
|
|
else
|
|
regGPIO->dw.INT_BOTH &= ~pin_val;
|
|
|
|
// set rising or falling edge
|
|
if (attributes & GPIO_INT_EDGE_RISING)
|
|
regGPIO->dw.INT_RISENEG &= ~pin_val;
|
|
else
|
|
regGPIO->dw.INT_RISENEG |= pin_val;
|
|
}
|
|
|
|
uint32_t level_attributes =
|
|
(GPIO_INT_LEVEL_HIGH | GPIO_INT_LEVEL_LOW);
|
|
|
|
if (attributes & level_attributes)
|
|
{
|
|
// level trigger
|
|
regGPIO->dw.INT_TRIGGER |= pin_val;
|
|
|
|
// set high or low level
|
|
if (attributes & GPIO_INT_LEVEL_HIGH)
|
|
regGPIO->dw.INT_RISENEG &= ~pin_val;
|
|
else
|
|
regGPIO->dw.INT_RISENEG |= pin_val;
|
|
}
|
|
|
|
// clear pull-enable here, should use IO control register to set pull-up or pull-down
|
|
regGPIO->dw.PULLENABLE &= ~pin_val;
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_register_callback(gpio_interrupt_callback_t gpio_isr_cb, void *usr_arg)
|
|
{
|
|
_usr_isr_cb = gpio_isr_cb;
|
|
_usr_arg = usr_arg;
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_set_interrupt(kdrv_gpio_pin_t pin, bool isEnable)
|
|
{
|
|
uint32_t pin_val = (1 << pin);
|
|
|
|
if (isEnable)
|
|
{
|
|
regGPIO->dw.INT_CLEAR |= pin_val;
|
|
regGPIO->dw.INT_ENABLE |= pin_val;
|
|
regGPIO->dw.INT_MASK &= ~pin_val;
|
|
}
|
|
else
|
|
{
|
|
regGPIO->dw.INT_ENABLE &= ~pin_val;
|
|
regGPIO->dw.INT_MASK |= pin_val;
|
|
}
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_set_debounce(kdrv_gpio_pin_t pin, bool isEnable, uint32_t debounce_clock)
|
|
{
|
|
uint32_t pin_val = (1 << pin);
|
|
|
|
if (isEnable)
|
|
{
|
|
regGPIO->dw.INT_BOUNCEENABLE |= pin_val;
|
|
regGPIO->dw.INT_PRESCALE = (APB_CLOCK / debounce_clock);
|
|
}
|
|
else
|
|
regGPIO->dw.INT_BOUNCEENABLE &= ~pin_val;
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_write_pin(kdrv_gpio_pin_t pin, bool value)
|
|
{
|
|
uint32_t pin_val = (1 << pin);
|
|
|
|
if (value == true)
|
|
regGPIO->dw.DATASET = pin_val;
|
|
else
|
|
regGPIO->dw.DATACLR = pin_val;
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|
|
|
|
kdrv_status_t kdrv_gpio_read_pin(kdrv_gpio_pin_t pin, bool *pValue)
|
|
{
|
|
uint32_t all_bits = regGPIO->dw.DIN_OFFSET;
|
|
|
|
*pValue = (all_bits & (0x1 << pin)) ? true : false;
|
|
|
|
return KDRV_STATUS_OK;
|
|
}
|