rt-thread/bsp/thead-smart/drivers/dw_gpio.c

636 lines
16 KiB
C

/*
* Copyright (C) 2017-2019 Alibaba Group Holding Limited
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-08-20 zx.chen gpio driver
*/
/******************************************************************************
* @file dw_gpio.c
* @brief CSI Source File for GPIO Driver
* @version V1.0
* @date 02. June 2017
******************************************************************************/
#include <csi_config.h>
#include <stdbool.h>
#include <stdio.h>
#include <drv_irq.h>
#include <drv_gpio.h>
#include <dw_gpio.h>
#include <csi_core.h>
#include <pin_name.h>
extern int32_t drv_pin_config_mode(port_name_e port, uint8_t offset, gpio_mode_e pin_mode);
#define ERR_GPIO(errno) (CSI_DRV_ERRNO_GPIO_BASE | errno)
#define GPIO_NULL_PARAM_CHK(para) HANDLE_PARAM_CHK(para, ERR_GPIO(DRV_ERROR_PARAMETER))
typedef void *gpio_port_handle_t;
typedef struct
{
#ifdef CONFIG_LPM
uint8_t gpio_power_status;
uint32_t gpio_regs_saved[7];
#endif
uint32_t base; ///< handle register base
uint32_t irq; ///< irq of this handle
uint32_t pin_num; ///< pin number of this handle
gpio_mode_e mode; ///< gpio mode
gpio_direction_e dir; ///< gpio direction
uint32_t mask; ///< gpio mask bit
uint32_t value; ///< gpio value
} dw_gpio_priv_t;
typedef struct
{
uint8_t portidx;
uint8_t idx;
uint8_t offset;
gpio_event_cb_t cb;
} dw_gpio_pin_priv_t;
extern int32_t target_gpio_port_init(port_name_e port, uint32_t *base, uint32_t *irq, void **handler, uint32_t *pin_num);
extern int32_t target_gpio_pin_init(int32_t gpio_pin, uint32_t *port_idx);
static dw_gpio_priv_t gpio_handle[CONFIG_GPIO_NUM];
static dw_gpio_pin_priv_t gpio_pin_handle[CONFIG_GPIO_PIN_NUM];
//
// Functions
//
static int32_t gpio_set_direction(
void *port,
gpio_direction_e direction
)
{
dw_gpio_priv_t *gpio_priv = port;
dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base);
if (direction == GPIO_DIRECTION_INPUT)
{
gpio_reg->SWPORT_DDR &= (~gpio_priv->mask);
} else if (direction == GPIO_DIRECTION_OUTPUT)
{
gpio_reg->SWPORT_DDR |= gpio_priv->mask;
} else
{
return ERR_GPIO(GPIO_ERROR_DIRECTION);
}
return 0;
}
/*
* Read the statu of the Port choosed.
* Parameters:
* port: use to choose a I/O port among Port A, B, or C.
* return: the value of the corresponding Port.
*/
static int32_t gpio_read(void *port, uint32_t *value)
{
dw_gpio_priv_t *gpio_priv = port;
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_priv->base + 0x30);
*value = gpio_control_reg->EXT_PORTA;
return 0;
}
/*
* Write an output value to corresponding Port.
* Parameters:
* port: use to choose a I/O port among Port A, B, or C.
* output: value that will be written to the corresponding Port.
* return: SUCCESS
*/
static int32_t gpio_write(void *port, uint32_t mask)
{
dw_gpio_priv_t *gpio_priv = port;
dw_gpio_reg_t *gpio_reg = (dw_gpio_reg_t *)(gpio_priv->base);
uint32_t value = gpio_reg->SWPORT_DR;
value &= ~(mask);
value |= gpio_priv->value;
gpio_reg->SWPORT_DR = value;
return 0;
}
/**
* Configure a GPIO gpio_set_irq_mode.
* @param[in] pin the addr store the pin num.
* @param[in] _irqmode the irqmode of gpio
* @return zero on success. -1 on falure.
*/
static int32_t gpio_set_irq_mode(gpio_pin_handle_t pin, gpio_irq_mode_e irq_mode)
{
dw_gpio_pin_priv_t *gpio_pin_priv = pin;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
uint32_t offset = gpio_pin_priv->idx;
uint32_t mask = 1 << offset;
switch (irq_mode)
{
/* rising edge interrupt mode */
case GPIO_IRQ_MODE_RISING_EDGE:
gpio_control_reg->INTTYPE_LEVEL |= mask;
gpio_control_reg->INT_POLARITY |= mask;
break;
/* falling edge interrupt mode */
case GPIO_IRQ_MODE_FALLING_EDGE:
gpio_control_reg->INTTYPE_LEVEL |= mask;
gpio_control_reg->INT_POLARITY &= (~mask);
break;
/* low level interrupt mode */
case GPIO_IRQ_MODE_LOW_LEVEL:
gpio_control_reg->INTTYPE_LEVEL &= (~mask);
gpio_control_reg->INT_POLARITY &= (~mask);
break;
/* high level interrupt mode */
case GPIO_IRQ_MODE_HIGH_LEVEL:
gpio_control_reg->INTTYPE_LEVEL &= (~mask);
gpio_control_reg->INT_POLARITY |= mask;
break;
/* double edge interrupt mode */
case GPIO_IRQ_MODE_DOUBLE_EDGE:
return ERR_GPIO(DRV_ERROR_UNSUPPORTED);
default:
return ERR_GPIO(GPIO_ERROR_IRQ_MODE);
}
return 0;
}
/*
* Clear one or more interrupts of PortA.
* Parameters:
* pinno:
* return: SUCCESS.
*/
static void gpio_irq_clear(gpio_pin_handle_t pin, uint32_t idx)
{
dw_gpio_pin_priv_t *gpio_pin_priv = pin;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
gpio_control_reg->PORTA_EOI = idx;
}
/*
* Enable one or more interrupts of PortA.
* Parameters:
* pinno:
* return: SUCCESS.
*/
static void gpio_irq_enable(gpio_pin_handle_t pin)
{
dw_gpio_pin_priv_t *gpio_pin_priv = pin;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
uint32_t offset = gpio_pin_priv->idx;
uint32_t val = gpio_control_reg->INTEN;
val |= (1 << offset);
gpio_control_reg->INTEN = val;
}
/*
* Disable one or more interrupts of PortA.
* Parameters:
* pinno:
* return: SUCCESS.
*/
static void gpio_irq_disable(gpio_pin_handle_t pin)
{
dw_gpio_pin_priv_t *gpio_pin_priv = pin;
uint32_t offset = gpio_pin_priv->idx;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(port_handle->base + 0x30);
uint32_t val = gpio_control_reg->INTEN;
val &= ~(1 << offset);
gpio_control_reg->INTEN = val;
}
void dw_gpio_irqhandler(int idx)
{
if (idx >= CONFIG_GPIO_NUM)
{
return;
}
dw_gpio_control_reg_t *gpio_control_reg = (dw_gpio_control_reg_t *)(gpio_handle[idx].base + 0x30);
uint32_t value = gpio_control_reg->INTSTATUS;
uint8_t i;
/* find the interrput pin */
for (i = 0; i < 32; i++)
{
if (value & (1U << i))
{
uint32_t pin_idx = i;
#ifndef CONFIG_CHIP_DANICA
uint8_t j;
if (idx > 0)
{
for (j = 0; j < idx; j++)
{
pin_idx += gpio_handle[j].pin_num;
}
}
if (pin_idx >= CONFIG_GPIO_PIN_NUM)
{
return;
}
#endif
dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)&gpio_pin_handle[pin_idx];
gpio_irq_clear(gpio_pin_priv, (1 << i)); //clear the gpio interrupt
/* execute the callback function */
if ((gpio_event_cb_t)(gpio_pin_priv->cb))
{
((gpio_event_cb_t)(gpio_pin_priv->cb))(gpio_pin_priv->offset);
}
}
}
}
/**
\brief Initialize GPIO module. 1. Initializes the resources needed for the GPIO handle 2.registers event callback function
3.get gpio_port_handle
\param[in] port port_name.
\return gpio_port_handle
*/
gpio_port_handle_t csi_gpio_port_initialize(int32_t port)
{
dw_gpio_priv_t *gpio_priv = NULL;
/* obtain the gpio port information */
uint32_t base = 0u;
uint32_t pin_num;
uint32_t irq;
void *handler;
int32_t idx = target_gpio_port_init(port, &base, &irq, &handler, &pin_num);
if (idx < 0 || idx >= CONFIG_GPIO_NUM)
{
return NULL;
}
gpio_priv = &gpio_handle[idx];
gpio_priv->base = base;
gpio_priv->irq = irq;
gpio_priv->pin_num = pin_num;
#ifdef CONFIG_LPM
csi_gpio_power_control(gpio_priv, DRV_POWER_FULL);
#endif
drv_irq_register(gpio_priv->irq, handler);
drv_irq_enable(gpio_priv->irq);
return (gpio_port_handle_t)gpio_priv;
}
/**
\brief De-initialize GPIO handle. stops operation and releases the software resources used by the handle
\param[in] handle gpio port handle to operate.
\return error code
*/
int32_t csi_gpio_port_uninitialize(gpio_port_handle_t handle)
{
GPIO_NULL_PARAM_CHK(handle);
dw_gpio_priv_t *gpio_priv = handle;
drv_irq_disable(gpio_priv->irq);
drv_irq_unregister(gpio_priv->irq);
#ifdef CONFIG_LPM
csi_gpio_power_control(gpio_priv, DRV_POWER_OFF);
#endif
return 0;
}
#ifdef CONFIG_LPM
static void manage_clock(gpio_pin_handle_t handle, uint8_t enable)
{
dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
uint8_t device[] = {CLOCK_MANAGER_GPIO0, CLOCK_MANAGER_GPIO1};
drv_clock_manager_config(device[gpio_pin_priv->portidx], enable);
}
static void do_prepare_sleep_action(void *handle)
{
dw_gpio_priv_t *gpio_handle = handle;
uint32_t *gbase = (uint32_t *)(gpio_handle->base);
uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30);
registers_save(gpio_handle->gpio_regs_saved, gbase, 3);
registers_save(&gpio_handle->gpio_regs_saved[3], control_base, 4);
}
static void do_wakeup_sleep_action(void *handle)
{
dw_gpio_priv_t *gpio_handle = handle;
uint32_t *gbase = (uint32_t *)(gpio_handle->base);
uint32_t *control_base = (uint32_t *)(gpio_handle->base + 0x30);
registers_restore(gbase, gpio_handle->gpio_regs_saved, 3);
registers_restore(control_base, &gpio_handle->gpio_regs_saved[3], 4);
}
#endif
/**
\brief Initialize GPIO handle.
\param[in] gpio_pin Pointer to the int32_t.
\param[in] cb_event Pointer to \ref gpio_event_cb_t.
\param[in] arg Pointer to \ref arg used for the callback.
\return gpio_pin_handle
*/
gpio_pin_handle_t csi_gpio_pin_initialize(int32_t gpio_pin, gpio_event_cb_t cb_event)
{
if (gpio_pin < 0 || gpio_pin >= CONFIG_GPIO_PIN_NUM)
{
return NULL;
}
uint32_t i;
for (i = 0; i < CONFIG_GPIO_NUM; i++)
{
csi_gpio_port_initialize(i);
}
/* obtain the gpio pin information */
uint32_t port_idx;
int32_t pin_idx = target_gpio_pin_init(gpio_pin, &port_idx);
if (pin_idx < 0)
{
return NULL;
}
int32_t idx = pin_idx;
for (i = 0; i < port_idx; i++)
{
idx += (gpio_handle[i].pin_num);
}
dw_gpio_pin_priv_t *gpio_pin_priv = &(gpio_pin_handle[idx]);
gpio_pin_priv->portidx = port_idx;
gpio_pin_priv->idx = pin_idx;
gpio_pin_priv->cb = cb_event;
gpio_pin_priv->offset = gpio_pin;
return (gpio_pin_handle_t)gpio_pin_priv;
}
/**
\brief De-initialize GPIO pin handle. stops operation and releases the software resources used by the handle
\param[in] handle gpio pin handle to operate.
\return error code
*/
int32_t csi_gpio_pin_uninitialize(gpio_pin_handle_t handle)
{
if (handle == NULL)
{
return ERR_GPIO(DRV_ERROR_PARAMETER);
}
dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
gpio_pin_priv->cb = NULL;
gpio_irq_disable(handle);
return 0;
}
/**
\brief control gpio power.
\param[in] idx gpio index.
\param[in] state power state.\ref csi_power_stat_e.
\return error code
*/
int32_t csi_gpio_power_control(gpio_pin_handle_t handle, csi_power_stat_e state)
{
GPIO_NULL_PARAM_CHK(handle);
#ifdef CONFIG_LPM
dw_gpio_pin_priv_t *gpio_pin_priv = (dw_gpio_pin_priv_t *)handle;
power_cb_t callback =
{
.wakeup = do_wakeup_sleep_action,
.sleep = do_prepare_sleep_action,
.manage_clock = manage_clock
};
return drv_soc_power_control(&gpio_handle[gpio_pin_priv->portidx], state, &callback);
#else
return ERR_GPIO(DRV_ERROR_UNSUPPORTED);
#endif
}
/**
\brief config pin mode
\param[in] pin gpio pin handle to operate.
\param[in] mode \ref gpio_mode_e
\return error code
*/
int32_t csi_gpio_pin_config_mode(gpio_pin_handle_t handle,
gpio_mode_e mode)
{
GPIO_NULL_PARAM_CHK(handle);
/* config the gpio pin mode direction mask bits */
dw_gpio_pin_priv_t *gpio_pin_priv = handle;
uint8_t offset = gpio_pin_priv->idx;
int32_t ret = drv_pin_config_mode(gpio_pin_priv->portidx, offset, mode);
return ret;
}
/**
\brief config pin direction
\param[in] pin gpio pin handle to operate.
\param[in] dir \ref gpio_direction_e
\return error code
*/
int32_t csi_gpio_pin_config_direction(gpio_pin_handle_t handle,
gpio_direction_e dir)
{
GPIO_NULL_PARAM_CHK(handle);
/* config the gpio pin mode direction mask bits */
dw_gpio_pin_priv_t *gpio_pin_priv = handle;
/* convert portidx to port handle */
dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx];
gpio_priv->dir = dir;
gpio_priv->mask = 1 << gpio_pin_priv->idx;
uint32_t ret = gpio_set_direction(gpio_priv, dir);
if (ret)
{
return ret;
}
return 0;
}
/**
\brief config pin
\param[in] handle gpio pin handle to operate.
\param[in] mode \ref gpio_mode_e
\param[in] dir \ref gpio_direction_e
\return error code
*/
int32_t csi_gpio_pin_config(gpio_pin_handle_t handle,
gpio_mode_e mode,
gpio_direction_e dir)
{
GPIO_NULL_PARAM_CHK(handle);
/* config the gpio pin mode direction mask bits */
dw_gpio_pin_priv_t *gpio_pin_priv = handle;
/* convert portidx to port handle */
dw_gpio_priv_t *gpio_priv = &gpio_handle[gpio_pin_priv->portidx];
gpio_priv->mode = mode;
gpio_priv->dir = dir;
gpio_priv->mask = 1 << gpio_pin_priv->idx;
uint32_t ret = gpio_set_direction(gpio_priv, dir);
if (ret)
{
return ret;
}
return 0;
}
/**
\brief Set one or zero to the selected GPIO pin.
\param[in] handle gpio pin handle to operate.
\param[in] value the value to be set
\return error code
*/
int32_t csi_gpio_pin_write(gpio_pin_handle_t handle, bool value)
{
GPIO_NULL_PARAM_CHK(handle);
dw_gpio_pin_priv_t *gpio_pin_priv = handle;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
uint8_t offset = gpio_pin_priv->idx;
uint32_t port_value = value << offset;
port_handle->value = port_value;
gpio_write(port_handle, (1 << offset));
return 0;
}
/**
\brief Get the value of selected GPIO pin.
\param[in] handle gpio pin handle to operate.
\param[out] value buf to store the pin value
\return error code
*/
int32_t csi_gpio_pin_read(gpio_pin_handle_t handle, bool *value)
{
GPIO_NULL_PARAM_CHK(handle);
GPIO_NULL_PARAM_CHK(value);
dw_gpio_pin_priv_t *gpio_pin_priv = handle;
uint32_t port_value;
uint8_t offset = gpio_pin_priv->idx;
/* convert portidx to port handle */
dw_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
gpio_read(port_handle, &port_value);
*value = (port_value & (1 << offset)) >> offset;
return 0;
}
/**
\brief set GPIO interrupt mode.
\param[in] handle gpio pin handle to operate.
\param[in] mode the irq mode to be set
\param[in] enable the enable flag
\return error code
*/
int32_t csi_gpio_pin_set_irq(gpio_pin_handle_t handle, gpio_irq_mode_e mode, bool enable)
{
GPIO_NULL_PARAM_CHK(handle);
uint32_t ret = 0;
if (enable)
{
ret = gpio_set_irq_mode(handle, mode);
if (ret)
{
return ret;
}
gpio_irq_enable(handle);
} else
{
gpio_irq_disable(handle);
}
return ret;
}