/* * Copyright (c) Hisilicon Technologies Co., Ltd.. 2010-2020. All rights reserved. * Description:drv of gpio */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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);