You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
20 KiB
648 lines
20 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd.. 2010-2020. All rights reserved.
|
|
* Description:drv of gpio
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/string.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <asm/irq.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/io.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/list.h>
|
|
#include <linux/semaphore.h>
|
|
#include <linux/version.h>
|
|
|
|
#include "soc_errno.h"
|
|
#include "soc_module.h"
|
|
#include "drv_sys_ext.h"
|
|
#include "drv_gpio_ext.h"
|
|
|
|
#include "drv_gpio.h"
|
|
|
|
#define EXT_GPIO_GROUP_MAX 31 /* reserved9_c group is 31 */
|
|
#define GPIO_MAX_BUF 256
|
|
#define GPIO_BUF_HEAD g_gpio_attr.gpio_irq_list[g_gpio_attr.head]
|
|
#define GPIO_BUF_TAIL g_gpio_attr.gpio_irq_list[g_gpio_attr.tail]
|
|
|
|
typedef struct {
|
|
td_bool gpio_interrupt_enable;
|
|
ext_gpio_interrupt_type gpio_interrupt_type;
|
|
td_void (*gpio_server)(td_u32 gpio_num);
|
|
} gpio_interrupt_attr;
|
|
|
|
typedef struct {
|
|
td_u32 head; /* gpio interrupt list */
|
|
td_u32 tail;
|
|
td_u32 gpio_irq_list[GPIO_MAX_BUF];
|
|
td_u32 gpio_irq_list_size;
|
|
td_u32 gpio_wait_timeout;
|
|
osal_wait gpio_interrupt_wait_queue; /* gpio wait queue */
|
|
td_size_t gpio_request[GPIO_MAX_BUF];
|
|
gpio_interrupt_attr gpio_irq_attr[GPIO_MAX_BUF];
|
|
osal_semaphore mutex;
|
|
} gpio_attr;
|
|
|
|
static gpio_get_gpio_num g_gpio_num;
|
|
static osal_atomic g_gpio_init_counter = { TD_NULL };
|
|
static gpio_attr g_gpio_attr;
|
|
|
|
td_s32 ext_drv_gpio_pm_suspend(td_void *private_data)
|
|
{
|
|
soc_print("GPIO suspend OK\n");
|
|
return 0;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_pm_resume(td_void *private_data)
|
|
{
|
|
soc_print("GPIO resume OK\n");
|
|
return 0;
|
|
}
|
|
|
|
static gpio_ext_func g_gpio_export_funcs = {
|
|
.pfn_gpio_direction_get_bit = ext_drv_gpio_get_direction_bit,
|
|
.pfn_gpio_direction_set_bit = ext_drv_gpio_set_direction_bit,
|
|
.pfn_gpio_read_bit = ext_drv_gpio_read_bit,
|
|
.pfn_gpio_write_bit = ext_drv_gpio_write_bit,
|
|
.pfn_gpio_get_num = ext_drv_gpio_get_gpio_num,
|
|
.pfn_gpio_register_server_func = ext_drv_gpio_register_server_func,
|
|
.pfn_gpio_unregister_server_func = ext_drv_gpio_unregister_server_func,
|
|
.pfn_gpio_set_interrupt_type = ext_drv_gpio_set_interrupt_type,
|
|
.pfn_gpio_set_interrupt_enable = ext_drv_gpio_set_bit_interrupt_enable,
|
|
.pfn_gpio_clear_group_interrupt = ext_drv_gpio_clear_group_interrupt,
|
|
.pfn_gpio_clear_bit_interrupt = ext_drv_gpio_clear_bit_interrupt,
|
|
.pfn_gpio_suspend = ext_drv_gpio_pm_suspend,
|
|
.pfn_gpio_resume = ext_drv_gpio_pm_resume
|
|
};
|
|
|
|
td_s32 ext_drv_gpio_set_direction_bit(td_u32 gpio_num, td_u32 dir_bit)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = osal_gpio_set_direction(gpio_num, dir_bit);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("osal_gpio_set_direction failed! \n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_get_direction_bit(td_u32 gpio_num, td_u32 *pdir_bit)
|
|
{
|
|
td_s32 status;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (pdir_bit == NULL) {
|
|
soc_log_err("pdir_bit is NULL \n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
status = osal_gpio_get_direction(gpio_num);
|
|
if (status == TD_FAILURE) {
|
|
soc_log_err("osal_gpio_get_direction failed! \n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (status == GPIOF_DIR_IN) {
|
|
*pdir_bit = TD_TRUE;
|
|
} else if (status == GPIOF_DIR_OUT) {
|
|
*pdir_bit = TD_FALSE;
|
|
} else {
|
|
soc_log_err("Get gpio%u direction failed:%d\n", gpio_num, status);
|
|
return SOC_ERR_GPIO_FAILED_GETDIRECT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_write_bit(td_u32 gpio_num, td_u32 bit_value)
|
|
{
|
|
td_s32 status;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if ((bit_value != TD_TRUE) && (bit_value != TD_FALSE)) {
|
|
soc_log_err("Invalid bit_value:%u\n", bit_value);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
status = osal_gpio_get_direction(gpio_num);
|
|
if (status != GPIOF_DIR_OUT) {
|
|
soc_log_err("Input direction, write denied\n");
|
|
return SOC_ERR_GPIO_INVALID_OPT;
|
|
}
|
|
|
|
osal_gpio_set_value(gpio_num, bit_value);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_read_bit(td_u32 gpio_num, td_u32 *pbit_value)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (pbit_value == NULL) {
|
|
soc_log_err("pbit_value is null\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = osal_gpio_get_value(gpio_num);
|
|
if (ret == TD_FAILURE) {
|
|
*pbit_value = TD_FALSE;
|
|
soc_log_err("osal_gpio_get_value failed\n");
|
|
return TD_FAILURE;
|
|
}
|
|
*pbit_value = (td_u32)ret;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_set_interrupt_type(td_u32 gpio_num, ext_gpio_interrupt_type interrupt_trigger_mode)
|
|
{
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
if (interrupt_trigger_mode >= EXT_GPIO_INTTYPE_MAX) {
|
|
soc_log_err("Invalid parameter, interrupt_trigger_mode:%d\n", interrupt_trigger_mode);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_type = interrupt_trigger_mode;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static osal_irqreturn_t gpio_isr(td_s32 irq, td_void *gpio_number)
|
|
{
|
|
td_size_t gpio_num;
|
|
|
|
if (gpio_number == NULL) {
|
|
soc_log_err("gpio number is null\n");
|
|
return (osal_irqreturn_t)OSAL_IRQ_HANDLED;
|
|
}
|
|
gpio_num = (td_size_t)(uintptr_t)gpio_number;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return (osal_irqreturn_t)OSAL_IRQ_HANDLED;
|
|
}
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_type == EXT_GPIO_INTTYPE_HIGH ||
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_type == EXT_GPIO_INTTYPE_LOW) {
|
|
disable_irq_nosync(gpio_to_irq(gpio_num));
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable == TD_FALSE) {
|
|
soc_log_err("error irq! \n");
|
|
}
|
|
}
|
|
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable == TD_TRUE) {
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_server != TD_NULL) {
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_server(gpio_num);
|
|
soc_log_info("gpio[%u].gpio_server executed\n", gpio_num);
|
|
}
|
|
|
|
soc_log_info("Recive gpio interrupt: GPIO%u. \n", gpio_num);
|
|
|
|
GPIO_BUF_HEAD = gpio_num;
|
|
g_gpio_attr.head = (g_gpio_attr.head + 1) % (g_gpio_attr.gpio_irq_list_size);
|
|
osal_wait_wakeup(&(g_gpio_attr.gpio_interrupt_wait_queue));
|
|
}
|
|
|
|
return (osal_irqreturn_t)OSAL_IRQ_HANDLED;
|
|
}
|
|
|
|
static td_s32 ext_drv_gpio_set_interrupt_config(td_u32 gpio_num)
|
|
{
|
|
td_u32 irq_type;
|
|
td_s32 irq;
|
|
td_u32 flags = 0;
|
|
td_s32 ret;
|
|
|
|
ret = osal_gpio_set_direction(gpio_num, 1); /* set direction input */
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("set gpio %u input direction failed ! \n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
irq_type = g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_type;
|
|
|
|
switch (irq_type) {
|
|
case EXT_GPIO_INTTYPE_UP:
|
|
flags |= IRQF_TRIGGER_RISING;
|
|
break;
|
|
case EXT_GPIO_INTTYPE_DOWN:
|
|
flags |= IRQF_TRIGGER_FALLING;
|
|
break;
|
|
case EXT_GPIO_INTTYPE_UPDOWN:
|
|
flags |= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
|
|
break;
|
|
/* IRQF_TRIGGER_HIGH or IRQF_TRIGGER_LOW will trigger
|
|
* continuously interrupts, must avoid it
|
|
*/
|
|
case EXT_GPIO_INTTYPE_HIGH:
|
|
flags |= (IRQF_TRIGGER_HIGH | IRQF_ONESHOT);
|
|
break;
|
|
case EXT_GPIO_INTTYPE_LOW:
|
|
flags |= (IRQF_TRIGGER_LOW | IRQF_ONESHOT);
|
|
break;
|
|
default:
|
|
flags = IRQF_TRIGGER_RISING;
|
|
break;
|
|
}
|
|
|
|
if ((flags == IRQF_TRIGGER_HIGH || flags == IRQF_TRIGGER_LOW) &&
|
|
(g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable == TD_TRUE)) {
|
|
osal_gpio_irq_free(gpio_num, (td_void *)((uintptr_t)gpio_num));
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable = TD_FALSE;
|
|
}
|
|
|
|
irq = osal_gpio_irq_request(gpio_num, (osal_irq_handler)gpio_isr, flags, "gpio", (td_void *)((uintptr_t)gpio_num));
|
|
if (irq == TD_FAILURE) {
|
|
soc_log_err("Request_irq irq%d on gpio%u failed: %d\n", irq, gpio_num, ret);
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable = TD_FALSE;
|
|
return SOC_ERR_GPIO_FAILED_SETINT;
|
|
}
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable = TD_TRUE;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_set_bit_interrupt_enable(td_u32 gpio_num, td_bool enable)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (enable) { /* not mask */
|
|
ret = ext_drv_gpio_set_interrupt_config(gpio_num);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("set gpio interrupt config failed!\n");
|
|
return ret;
|
|
}
|
|
} else { /* mask */
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable == TD_TRUE) {
|
|
osal_gpio_irq_free(gpio_num, (td_void *)((uintptr_t)gpio_num));
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_interrupt_enable = TD_FALSE;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_register_server_func(td_u32 gpio_num, td_void (*func)(td_u32))
|
|
{
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (func == TD_NULL) {
|
|
soc_log_err("Register func para is null, gpio_num:%u \n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (g_gpio_attr.gpio_irq_attr[gpio_num].gpio_server != TD_NULL) {
|
|
soc_log_err("GPIO %u had registered gpio server pragram \n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_server = func;
|
|
soc_log_info("Gpio %d finished register gpio server function \n", gpio_num);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_unregister_server_func(td_u32 gpio_num)
|
|
{
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
g_gpio_attr.gpio_irq_attr[gpio_num].gpio_server = TD_NULL;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_clear_bit_interrupt(td_u32 gpio_num)
|
|
{
|
|
if (gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", gpio_num);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_clear_group_interrupt(td_u32 gpio_group)
|
|
{
|
|
if (gpio_group >= g_gpio_num.gpio_group_num) {
|
|
soc_log_err("Invalid parameter, gpio_group:%u\n", gpio_group);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_get_gpio_num(gpio_get_gpio_num *gpio_num)
|
|
{
|
|
if (gpio_num == NULL) {
|
|
soc_log_err("gpio_num is null\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
gpio_num->gpio_group_num = g_gpio_num.gpio_group_num;
|
|
gpio_num->gpio_max_num = g_gpio_num.gpio_max_num;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_gpio_open(td_void *private_data)
|
|
{
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_gpio_close(td_void *private_data)
|
|
{
|
|
td_s32 ret, i;
|
|
|
|
if (osal_atomic_read(&g_gpio_init_counter) <= 0) {
|
|
soc_log_err("gpio not initialized\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = osal_sem_down_interruptible(&g_gpio_attr.mutex);
|
|
if (ret) {
|
|
soc_log_err("osal_sem_down_interruptible:%d\n", ret);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
for (i = 0; i < GPIO_MAX_BUF; i++) {
|
|
if (g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_enable == TD_TRUE) {
|
|
osal_gpio_irq_free(i, (td_void *)((uintptr_t)i));
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_enable = TD_FALSE;
|
|
}
|
|
}
|
|
|
|
osal_sem_up(&g_gpio_attr.mutex);
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_gpio_query_interrupt_wait_condition(const td_void *para)
|
|
{
|
|
return (g_gpio_attr.head != g_gpio_attr.tail);
|
|
}
|
|
|
|
td_s32 drv_gpio_query_interrupt(gpio_interrupt *pgpio_interrupt_value)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 gpio_interrupt_num;
|
|
|
|
if (pgpio_interrupt_value == NULL) {
|
|
soc_log_err("pgpio_interrupt_value is null\n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (pgpio_interrupt_value->timeout_ms > 5000U) {
|
|
soc_log_err("pgpio_interrupt_value wait time[%d] too long, the range is 1~5000ms\n",
|
|
pgpio_interrupt_value->timeout_ms);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
if (pgpio_interrupt_value->gpio_num >= g_gpio_num.gpio_max_num) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u\n", pgpio_interrupt_value->gpio_num);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
|
|
g_gpio_attr.gpio_wait_timeout = pgpio_interrupt_value->timeout_ms;
|
|
|
|
while (g_gpio_attr.head == g_gpio_attr.tail) {
|
|
if (g_gpio_attr.gpio_wait_timeout == 0xffffffff) {
|
|
ret = osal_wait_interruptible(&g_gpio_attr.gpio_interrupt_wait_queue,
|
|
drv_gpio_query_interrupt_wait_condition, NULL);
|
|
if (ret < 0) {
|
|
soc_log_err("osal_wait_interruptible failed:%d\n", ret);
|
|
return -ERESTARTSYS;
|
|
}
|
|
} else {
|
|
ret = osal_wait_timeout_interruptible(&g_gpio_attr.gpio_interrupt_wait_queue,
|
|
drv_gpio_query_interrupt_wait_condition,
|
|
/* 1000 "ms" converted to "s" */
|
|
NULL, (td_slong)(g_gpio_attr.gpio_wait_timeout));
|
|
if (ret < 0) {
|
|
soc_log_err("osal_wait_timeout_interruptible failed:%d\n", ret);
|
|
return -ERESTARTSYS;
|
|
} else if (ret == 0) {
|
|
soc_log_err("osal_wait_timeout_interruptible timrout\n");
|
|
return SOC_ERR_GPIO_GETINT_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_gpio_attr.head != g_gpio_attr.tail) {
|
|
gpio_interrupt_num = GPIO_BUF_TAIL;
|
|
g_gpio_attr.tail = (g_gpio_attr.tail + 1) % (g_gpio_attr.gpio_irq_list_size);
|
|
pgpio_interrupt_value->gpio_num = gpio_interrupt_num;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static ext_gpio_outputtype g_gpio_outputtype = EXT_GPIO_OUTPUTTYPE_CMOS;
|
|
td_s32 drv_gpio_set_output_type(td_u32 gpio_num, ext_gpio_outputtype output_type)
|
|
{
|
|
if (output_type >= EXT_GPIO_OUTPUTTYPE_MAX) {
|
|
soc_log_err("Invalid parameter, gpio_num:%u, output_type:%u\n", gpio_num, output_type);
|
|
return SOC_ERR_GPIO_INVALID_PARA;
|
|
}
|
|
g_gpio_outputtype = output_type;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_gpio_get_output_type(td_u32 gpio_num, ext_gpio_outputtype *poutput_type)
|
|
{
|
|
if (poutput_type == TD_NULL) {
|
|
soc_log_err("gpio_num:%u, poutput_type is null\n", gpio_num);
|
|
return SOC_ERR_GPIO_NULL_PTR;
|
|
}
|
|
*poutput_type = g_gpio_outputtype;
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 drv_gpio_osal_resource_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = osal_sem_init(&g_gpio_attr.mutex, 1);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("osal_sem_init failed! \n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
ret = osal_atomic_init(&g_gpio_init_counter);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("osal_atomic_init failed! \n");
|
|
goto err0;
|
|
}
|
|
|
|
ret = osal_wait_init(&g_gpio_attr.gpio_interrupt_wait_queue);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("osal_wait_init failed! \n");
|
|
goto err1;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
|
|
err1:
|
|
osal_atomic_destroy(&g_gpio_init_counter);
|
|
err0:
|
|
osal_sem_destroy(&g_gpio_attr.mutex);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
td_s32 ext_drv_gpio_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i, tmp_gpio_group_num;
|
|
|
|
ret = drv_gpio_osal_resource_init();
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err("drv_gpio_osal_resource_init failed! \n");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
if (osal_atomic_inc_return(&g_gpio_init_counter) != 1) {
|
|
soc_log_warn(" EXT_DRV_GPIO already registered:%d\n", osal_atomic_read(&g_gpio_init_counter));
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
ret = osal_exportfunc_register(SOC_ID_GPIO, "SOC_GPIO", (td_void *)&g_gpio_export_funcs);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err(" GPIO Module register failed 0x%x.\n", ret);
|
|
goto err0;
|
|
}
|
|
|
|
g_gpio_attr.head = 0;
|
|
g_gpio_attr.tail = 0;
|
|
g_gpio_attr.gpio_irq_list_size = GPIO_MAX_BUF;
|
|
g_gpio_attr.gpio_wait_timeout = 0xffffffff;
|
|
|
|
ret = osal_dts_get_u32_byname("huanglong,gpioinfo", "groupnum", &tmp_gpio_group_num);
|
|
if (ret != 0) {
|
|
soc_log_err("get groupnum from dts failed.\n");
|
|
goto err1;
|
|
}
|
|
g_gpio_num.gpio_group_num = (td_u8)tmp_gpio_group_num;
|
|
|
|
if (g_gpio_num.gpio_group_num > EXT_GPIO_GROUP_MAX) {
|
|
soc_log_err("para gpio_num is invalid.\n");
|
|
goto err1;
|
|
}
|
|
|
|
g_gpio_num.gpio_max_num = g_gpio_num.gpio_group_num * EXT_GPIO_BIT_NUM;
|
|
|
|
for (i = 0; i < GPIO_MAX_BUF; i++) {
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_enable = TD_FALSE;
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_type = EXT_GPIO_INTTYPE_DOWN;
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_server = TD_NULL;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
|
|
err1:
|
|
ret = osal_exportfunc_unregister(SOC_ID_GPIO);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err(" GPIO Module unregister failed 0x%x.\n", ret);
|
|
}
|
|
err0:
|
|
osal_atomic_destroy(&g_gpio_init_counter);
|
|
osal_sem_destroy(&g_gpio_attr.mutex);
|
|
osal_wait_destroy(&g_gpio_attr.gpio_interrupt_wait_queue);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
td_void ext_drv_gpio_deinit(td_void)
|
|
{
|
|
td_u32 i;
|
|
td_s32 ret;
|
|
|
|
if (osal_atomic_dec_return(&g_gpio_init_counter) != 0) {
|
|
soc_log_warn("ext_drv_gpio_deinit counter:%d\n", osal_atomic_read(&g_gpio_init_counter));
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < GPIO_MAX_BUF; i++) {
|
|
if (g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_enable == TD_TRUE) {
|
|
osal_gpio_irq_free(i, (td_void *)((uintptr_t)i));
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_enable = TD_FALSE;
|
|
}
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_interrupt_type = EXT_GPIO_INTTYPE_DOWN;
|
|
g_gpio_attr.gpio_irq_attr[i].gpio_server = TD_NULL;
|
|
}
|
|
|
|
ret = osal_exportfunc_unregister(SOC_ID_GPIO);
|
|
if (ret != TD_SUCCESS) {
|
|
soc_log_err(" GPIO Module unregister failed 0x%x.\n", ret);
|
|
}
|
|
|
|
osal_atomic_destroy(&g_gpio_init_counter);
|
|
osal_sem_destroy(&g_gpio_attr.mutex);
|
|
osal_wait_destroy(&g_gpio_attr.gpio_interrupt_wait_queue);
|
|
return;
|
|
}
|
|
|
|
#ifdef MODULE
|
|
EXPORT_SYMBOL(ext_drv_gpio_init);
|
|
EXPORT_SYMBOL(ext_drv_gpio_deinit);
|
|
#endif
|
|
|
|
EXPORT_SYMBOL(ext_drv_gpio_get_direction_bit);
|
|
EXPORT_SYMBOL(ext_drv_gpio_set_direction_bit);
|
|
EXPORT_SYMBOL(ext_drv_gpio_write_bit);
|
|
EXPORT_SYMBOL(ext_drv_gpio_read_bit);
|
|
EXPORT_SYMBOL(ext_drv_gpio_get_gpio_num);
|
|
EXPORT_SYMBOL(ext_drv_gpio_register_server_func);
|
|
EXPORT_SYMBOL(ext_drv_gpio_unregister_server_func);
|
|
EXPORT_SYMBOL(ext_drv_gpio_set_interrupt_type);
|
|
EXPORT_SYMBOL(ext_drv_gpio_set_bit_interrupt_enable);
|
|
EXPORT_SYMBOL(ext_drv_gpio_clear_group_interrupt);
|
|
EXPORT_SYMBOL(ext_drv_gpio_clear_bit_interrupt);
|