361 lines
11 KiB
C
361 lines
11 KiB
C
/***************************************************************************//**
|
|
* @file xilinx/xilinx_gpio_irq.c
|
|
* @brief Implementation of Xilinx GPIO IRQ Generic Driver.
|
|
* @author Porumb Andrei ([email protected])
|
|
********************************************************************************
|
|
* Copyright 2021(c) Analog Devices, Inc.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* - Neither the name of Analog Devices, Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* - The use of this software may or may not infringe the patent rights
|
|
* of one or more patent holders. This license does not release you
|
|
* from the requirement that you obtain separate licenses from these
|
|
* patent holders to use this software.
|
|
* - Use of the software either in source or binary form, must be run
|
|
* on or directly connected to an Analog Devices Inc. component.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
/***************************** Include Files **********************************/
|
|
/******************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include "xparameters.h"
|
|
#include "error.h"
|
|
#include "xilinx_gpio_irq.h"
|
|
#include "util.h"
|
|
#include "list.h"
|
|
#include "irq.h"
|
|
|
|
/******************************************************************************/
|
|
/************************ Functions Definitions *******************************/
|
|
/******************************************************************************/
|
|
|
|
/**
|
|
* @brief Comparing function used by list.
|
|
* @param desc_a - Compared item
|
|
* @param desc_b - Reference item
|
|
* @return - 0 if items match
|
|
*/
|
|
static int32_t call_cmp(struct xil_callback_desc *desc_a,
|
|
struct xil_callback_desc *desc_b)
|
|
{
|
|
return desc_a->pin_nb - desc_b->pin_nb;
|
|
}
|
|
|
|
/**
|
|
* @brief Function called when GPIO IRQ occurs.
|
|
* @param param - GPIO IRQ desc's extra field
|
|
* @return - None.
|
|
*/
|
|
static void xil_gpio_irq_handler(struct xil_gpio_irq_desc *param)
|
|
{
|
|
int32_t status = 0;
|
|
int32_t pin_nb;
|
|
struct xil_callback_desc *callback_desc;
|
|
|
|
status = iterator_move_to_idx(param->it, 0);
|
|
while(!status) {
|
|
iterator_read(param->it, &callback_desc);
|
|
status = iterator_move(param->it, 1);
|
|
if(XGpioPs_IntrGetStatusPin(¶m->my_Gpio, callback_desc->pin_nb))
|
|
callback_desc->triggered = true;
|
|
}
|
|
|
|
status = iterator_move_to_idx(param->it, 0);
|
|
while(!status) {
|
|
iterator_read(param->it, &callback_desc);
|
|
status = iterator_move(param->it, 1);
|
|
if(callback_desc->triggered == true) {
|
|
callback_desc->triggered = false;
|
|
XGpioPs_IntrDisablePin(¶m->my_Gpio, callback_desc->pin_nb);
|
|
XGpioPs_IntrClearPin(¶m->my_Gpio, callback_desc->pin_nb);
|
|
callback_desc->callback.legacy_callback(callback_desc->callback.ctx, 0U, NULL);
|
|
if(callback_desc->enabled)
|
|
XGpioPs_IntrEnablePin(¶m->my_Gpio, callback_desc->pin_nb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Disable specific GPIO IRQ pin.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param irq_id - Pin number.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_disable(struct irq_ctrl_desc *desc, uint32_t irq_id)
|
|
{
|
|
int32_t status;
|
|
struct xil_gpio_irq_desc *extra;
|
|
struct xil_callback_desc *callback_desc;
|
|
|
|
extra = desc->extra;
|
|
|
|
status = iterator_move_to_idx(extra->it, 0);
|
|
while(!status) {
|
|
iterator_read(extra->it, &callback_desc);
|
|
status = iterator_move(extra->it, 1);
|
|
if(callback_desc->pin_nb == irq_id) {
|
|
callback_desc->enabled = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
XGpioPs_IntrDisablePin(&extra->my_Gpio, irq_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the GPIO IRQ controller.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param param - The GPIO IRQ controller initial parameters.
|
|
* irq_ctrl_id from irq_init_param is the interrupt id for the parent controller
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_ctrl_init(struct irq_ctrl_desc **desc,
|
|
const struct irq_init_param *param)
|
|
{
|
|
int32_t i;
|
|
int32_t status;
|
|
struct irq_ctrl_desc *ldesc;
|
|
static XGpioPs_Config *GPIO_Config;
|
|
struct xil_gpio_irq_desc *xil_desc;
|
|
struct xil_gpio_irq_init_param *xil_ip;
|
|
struct callback_desc callback;
|
|
|
|
xil_desc = (struct xil_gpio_irq_desc *)calloc(1, sizeof(*xil_desc));
|
|
if(!xil_desc)
|
|
return -ENOMEM;
|
|
|
|
ldesc = (struct irq_ctrl_desc *)calloc(1, sizeof(*ldesc));
|
|
if(!ldesc) {
|
|
free(xil_desc);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
xil_ip = param->extra;
|
|
GPIO_Config = XGpioPs_LookupConfig(xil_ip->gpio_device_id);
|
|
status = XGpioPs_CfgInitialize(&xil_desc->my_Gpio, GPIO_Config,
|
|
GPIO_Config->BaseAddr);
|
|
if(status)
|
|
goto error_desc;
|
|
|
|
ldesc->extra = xil_desc;
|
|
xil_desc->parent_desc = xil_ip->parent_desc;
|
|
|
|
status = list_init(&xil_desc->callback_list, LIST_DEFAULT,
|
|
call_cmp);
|
|
if(status)
|
|
goto error_list;
|
|
|
|
status = iterator_init(&xil_desc->it, xil_desc->callback_list, 0);
|
|
if(status) {
|
|
iterator_remove(xil_desc->it);
|
|
goto error_list;
|
|
}
|
|
|
|
ldesc->irq_ctrl_id = param->irq_ctrl_id;
|
|
status = irq_trigger_level_set(xil_desc->parent_desc, ldesc->irq_ctrl_id,
|
|
IRQ_EDGE_RISING);
|
|
if(status)
|
|
goto error_op;
|
|
|
|
status = irq_enable(xil_desc->parent_desc, ldesc->irq_ctrl_id);
|
|
if(status)
|
|
goto error_op;
|
|
|
|
callback.legacy_callback = &xil_gpio_irq_handler;
|
|
callback.ctx = ldesc->extra;
|
|
status = irq_register_callback(xil_desc->parent_desc, ldesc->irq_ctrl_id,
|
|
&callback);
|
|
if(status)
|
|
goto error_op;
|
|
|
|
*desc = ldesc;
|
|
|
|
return 0;
|
|
|
|
error_op:
|
|
iterator_remove(xil_desc->it);
|
|
error_list:
|
|
list_remove(xil_desc->callback_list);
|
|
error_desc:
|
|
free(xil_desc);
|
|
free(ldesc);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO interrupt trigger level.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param irq_id - Pin number.
|
|
* @param trig - New trigger level for the GPIO interrupt.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_trigger_level_set(struct irq_ctrl_desc *desc,
|
|
uint32_t irq_id,
|
|
enum irq_trig_level trig)
|
|
{
|
|
static const int32_t trig_level[5] = {XGPIOPS_IRQ_TYPE_LEVEL_LOW,
|
|
XGPIOPS_IRQ_TYPE_LEVEL_HIGH,
|
|
XGPIOPS_IRQ_TYPE_EDGE_FALLING,
|
|
XGPIOPS_IRQ_TYPE_EDGE_RISING,
|
|
XGPIOPS_IRQ_TYPE_EDGE_BOTH
|
|
};
|
|
struct xil_gpio_irq_desc *extra;
|
|
|
|
extra = desc->extra;
|
|
XGpioPs_SetIntrTypePin(&extra->my_Gpio, irq_id, trig_level[trig]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Register pin specific GPIO interrupt.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param irq_id - Pin number.
|
|
* @param callback_desc - Callback.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_register_callback(struct irq_ctrl_desc *desc,
|
|
uint32_t irq_id,
|
|
struct callback_desc *callback_desc)
|
|
{
|
|
struct xil_callback_desc *dev_callback;
|
|
struct xil_gpio_irq_desc *extra;
|
|
|
|
dev_callback = (struct xil_callback_desc *)calloc(1,
|
|
sizeof(*dev_callback));
|
|
if(!dev_callback)
|
|
return -1;
|
|
|
|
dev_callback->pin_nb = irq_id;
|
|
dev_callback->callback.legacy_callback = callback_desc->legacy_callback;
|
|
dev_callback->callback.ctx = callback_desc->ctx;
|
|
dev_callback->triggered = false;
|
|
|
|
extra = desc->extra;
|
|
XGpioPs_SetDirectionPin(&extra->my_Gpio, irq_id, 0);
|
|
list_add_last(extra->callback_list, dev_callback);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unregister pin specific GPIO interrupt.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param irq_id - Pin number.
|
|
* @param cb - Callback descriptor.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_unregister_callback(struct irq_ctrl_desc *desc,
|
|
uint32_t irq_id, struct callback_desc *cb)
|
|
{
|
|
int32_t status;
|
|
struct xil_gpio_irq_desc *extra;
|
|
struct xil_callback_desc *dev_callback;
|
|
struct xil_callback_desc search_callback;
|
|
|
|
extra = desc->extra;
|
|
search_callback.pin_nb = irq_id;
|
|
status = list_get_find(extra->callback_list, &dev_callback,
|
|
&search_callback);
|
|
if(status)
|
|
return -ENXIO;
|
|
|
|
free(dev_callback);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable specific GPIO IRQ pin.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @param irq_id - Pin number.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_enable(struct irq_ctrl_desc *desc, uint32_t irq_id)
|
|
{
|
|
int32_t status;
|
|
struct xil_gpio_irq_desc *extra;
|
|
struct xil_callback_desc *callback_desc;
|
|
|
|
extra = desc->extra;
|
|
|
|
status = iterator_move_to_idx(extra->it, 0);
|
|
while(!status) {
|
|
iterator_read(extra->it, &callback_desc);
|
|
status = iterator_move(extra->it, 1);
|
|
if(callback_desc->pin_nb == irq_id) {
|
|
callback_desc->enabled = true;
|
|
XGpioPs_IntrClearPin(&extra->my_Gpio, irq_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XGpioPs_IntrEnablePin(&extra->my_Gpio, irq_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove the GPIO IRQ controller.
|
|
* @param desc - The GPIO IRQ controller descriptor.
|
|
* @return 0 in case of success, -1 otherwise.
|
|
*/
|
|
int32_t xil_gpio_irq_ctrl_remove(struct irq_ctrl_desc *desc)
|
|
{
|
|
struct xil_gpio_irq_desc *extra;
|
|
struct xil_callback_desc *callback_desc;
|
|
|
|
if (!desc)
|
|
return -EINVAL;
|
|
|
|
extra = desc->extra;
|
|
while (0 == list_get_first(extra->callback_list, &callback_desc))
|
|
free(callback_desc);
|
|
|
|
iterator_remove(extra->it);
|
|
list_remove(extra->callback_list);
|
|
free(extra);
|
|
free(desc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Xilinx platform specific GPIO IRQ platform ops structure
|
|
*/
|
|
const struct irq_platform_ops xil_gpio_irq_ops = {
|
|
.init = &xil_gpio_irq_ctrl_init,
|
|
.enable = &xil_gpio_irq_enable,
|
|
.disable = &xil_gpio_irq_disable,
|
|
.trigger_level_set = &xil_gpio_irq_trigger_level_set,
|
|
.register_callback = &xil_gpio_irq_register_callback,
|
|
.unregister_callback = xil_gpio_irq_unregister_callback,
|
|
.remove = &xil_gpio_irq_ctrl_remove
|
|
};
|