blob: b5ed4e2b99f24ab2fef725b67809a1aeb6709772 [file] [log] [blame]
/*
* arch/arm/plat-ambarella/generic/gpio.c
*
* History:
* 2007/11/21 - [Grady Chen] created file
* 2008/01/08 - [Anthony Ginger] Add GENERIC_GPIO support.
*
* Copyright (C) 2004-2009, Ambarella, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/gpio.h>
/* ==========================================================================*/
struct ambarella_gpio_chip {
struct gpio_chip chip;
u32 base_reg;
spinlock_t lock;
struct {
u32 data_reg;
u32 dir_reg;
u32 is_reg;
u32 ibe_reg;
u32 iev_reg;
u32 ie_reg;
u32 afsel_reg;
u32 mask_reg;
} pm_info;
};
#define to_ambarella_gpio_chip(c) \
container_of(c, struct ambarella_gpio_chip, chip)
static inline int ambarella_gpio_inline_config(
struct ambarella_gpio_chip *agchip, u32 offset, int func)
{
int retval = 0;
unsigned long flags;
spin_lock_irqsave(&agchip->lock, flags);
if (func == GPIO_FUNC_HW) {
amba_setbitsl(agchip->base_reg + GPIO_AFSEL_OFFSET,
(0x1 << offset));
amba_clrbitsl(agchip->base_reg + GPIO_MASK_OFFSET,
(0x1 << offset));
} else if (func == GPIO_FUNC_SW_INPUT) {
amba_clrbitsl(agchip->base_reg + GPIO_AFSEL_OFFSET,
(0x1 << offset));
amba_clrbitsl(agchip->base_reg + GPIO_DIR_OFFSET,
(0x1 << offset));
} else if (func == GPIO_FUNC_SW_OUTPUT) {
amba_clrbitsl(agchip->base_reg + GPIO_AFSEL_OFFSET,
(0x1 << offset));
amba_setbitsl(agchip->base_reg + GPIO_DIR_OFFSET,
(0x1 << offset));
} else {
pr_err("%s: invalid GPIO func %d for 0x%x:0x%x.\n",
__func__, func, agchip->base_reg, offset);
retval = -EINVAL;
}
spin_unlock_irqrestore(&agchip->lock, flags);
return retval;
}
static inline void ambarella_gpio_inline_set(
struct ambarella_gpio_chip *agchip, u32 offset, int value)
{
unsigned long flags;
u32 mask;
mask = (0x1 << offset);
spin_lock_irqsave(&agchip->lock, flags);
amba_writel(agchip->base_reg + GPIO_MASK_OFFSET, mask);
if (value == GPIO_LOW) {
amba_writel(agchip->base_reg + GPIO_DATA_OFFSET, 0);
} else {
amba_writel(agchip->base_reg + GPIO_DATA_OFFSET, mask);
}
spin_unlock_irqrestore(&agchip->lock, flags);
}
static inline int ambarella_gpio_inline_get(
struct ambarella_gpio_chip *agchip, u32 offset)
{
u32 val = 0;
unsigned long flags;
spin_lock_irqsave(&agchip->lock, flags);
amba_writel(agchip->base_reg + GPIO_MASK_OFFSET, (0x1 << offset));
val = amba_readl(agchip->base_reg + GPIO_DATA_OFFSET);
spin_unlock_irqrestore(&agchip->lock, flags);
val = (val >> offset) & 0x1;
return (val ? GPIO_HIGH : GPIO_LOW);
}
/* ==========================================================================*/
static DEFINE_MUTEX(ambarella_gpio_mtx);
static unsigned long ambarella_gpio_valid[BITS_TO_LONGS(AMBGPIO_SIZE)];
static unsigned long ambarella_gpio_freeflag[BITS_TO_LONGS(AMBGPIO_SIZE)];
static int ambarella_gpio_request(struct gpio_chip *chip, unsigned offset)
{
int retval = 0;
mutex_lock(&ambarella_gpio_mtx);
if (test_bit((chip->base + offset), ambarella_gpio_valid)) {
if (test_bit((chip->base + offset), ambarella_gpio_freeflag)) {
__clear_bit((chip->base + offset),
ambarella_gpio_freeflag);
} else {
retval = -EACCES;
}
} else {
retval = -EPERM;
}
mutex_unlock(&ambarella_gpio_mtx);
return retval;
}
static void ambarella_gpio_free(struct gpio_chip *chip, unsigned offset)
{
mutex_lock(&ambarella_gpio_mtx);
__set_bit((chip->base + offset), ambarella_gpio_freeflag);
mutex_unlock(&ambarella_gpio_mtx);
}
static int ambarella_gpio_chip_direction_input(struct gpio_chip *chip,
unsigned offset)
{
int retval = 0;
struct ambarella_gpio_chip *agchip;
agchip = to_ambarella_gpio_chip(chip);
retval = ambarella_gpio_inline_config(agchip,
offset, GPIO_FUNC_SW_INPUT);
return retval;
}
static int ambarella_gpio_chip_get(struct gpio_chip *chip,
unsigned offset)
{
int retval = 0;
struct ambarella_gpio_chip *agchip;
agchip = to_ambarella_gpio_chip(chip);
retval = ambarella_gpio_inline_get(agchip,
offset);
return retval;
}
static int ambarella_gpio_chip_direction_output(struct gpio_chip *chip,
unsigned offset, int val)
{
int retval = 0;
struct ambarella_gpio_chip *agchip;
agchip = to_ambarella_gpio_chip(chip);
retval = ambarella_gpio_inline_config(agchip,
offset, GPIO_FUNC_SW_OUTPUT);
ambarella_gpio_inline_set(agchip, offset, val);
return retval;
}
static void ambarella_gpio_chip_set(struct gpio_chip *chip,
unsigned offset, int val)
{
struct ambarella_gpio_chip *agchip;
agchip = to_ambarella_gpio_chip(chip);
ambarella_gpio_inline_set(agchip, offset, val);
}
static int ambarella_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
if ((chip->base + offset) < GPIO_MAX_LINES)
return GPIO_INT_VEC(chip->base + offset);
return -EINVAL;
}
static void ambarella_gpio_chip_dbg_show(struct seq_file *s,
struct gpio_chip *chip)
{
int i;
struct ambarella_gpio_chip *agchip;
u32 afsel;
u32 mask;
u32 data;
u32 dir;
unsigned long flags;
agchip = to_ambarella_gpio_chip(chip);
spin_lock_irqsave(&agchip->lock, flags);
afsel = amba_readl(agchip->base_reg + GPIO_AFSEL_OFFSET);
dir = amba_readl(agchip->base_reg + GPIO_DIR_OFFSET);
mask = amba_readl(agchip->base_reg + GPIO_MASK_OFFSET);
amba_writel(agchip->base_reg + GPIO_MASK_OFFSET, ~afsel);
data = amba_readl(agchip->base_reg + GPIO_DATA_OFFSET);
amba_writel(agchip->base_reg + GPIO_MASK_OFFSET, mask);
spin_unlock_irqrestore(&agchip->lock, flags);
seq_printf(s, "GPIO_BASE:\t0x%08X\n", agchip->base_reg);
seq_printf(s, "GPIO_AFSEL:\t0x%08X\n", afsel);
seq_printf(s, "GPIO_DIR:\t0x%08X\n", dir);
seq_printf(s, "GPIO_MASK:\t0x%08X:0x%08X\n", mask, ~afsel);
seq_printf(s, "GPIO_DATA:\t0x%08X\n", data);
for (i = 0; i < chip->ngpio; i++) {
seq_printf(s, "GPIO %d: ", (chip->base + i));
if (afsel & (1 << i)) {
seq_printf(s, "HW\n");
} else {
seq_printf(s, "%s\t%s\n",
(dir & (1 << i)) ? "out" : "in",
(data & (1 << i)) ? "set" : "clear");
}
}
}
#define AMBARELLA_GPIO_BANK(name, reg_base, base_gpio) \
{ \
.chip = { \
.label = name, \
.owner = THIS_MODULE, \
.request = ambarella_gpio_request, \
.free = ambarella_gpio_free, \
.direction_input = ambarella_gpio_chip_direction_input, \
.get = ambarella_gpio_chip_get, \
.direction_output = ambarella_gpio_chip_direction_output, \
.set = ambarella_gpio_chip_set, \
.to_irq = ambarella_gpio_to_irq, \
.dbg_show = ambarella_gpio_chip_dbg_show, \
.base = base_gpio, \
.ngpio = GPIO_BANK_SIZE, \
.can_sleep = 0, \
.exported = 0, \
}, \
.base_reg = reg_base, \
}
static struct ambarella_gpio_chip ambarella_gpio_banks[] = {
AMBARELLA_GPIO_BANK("ambarella-gpio0",
GPIO0_BASE, GPIO(0 * GPIO_BANK_SIZE)),
AMBARELLA_GPIO_BANK("ambarella-gpio1",
GPIO1_BASE, GPIO(1 * GPIO_BANK_SIZE)),
#if (GPIO_INSTANCES >= 3)
AMBARELLA_GPIO_BANK("ambarella-gpio2",
GPIO2_BASE, GPIO(2 * GPIO_BANK_SIZE)),
#endif
#if (GPIO_INSTANCES >= 4)
AMBARELLA_GPIO_BANK("ambarella-gpio3",
GPIO3_BASE, GPIO(3 * GPIO_BANK_SIZE)),
#endif
#if (GPIO_INSTANCES >= 5)
AMBARELLA_GPIO_BANK("ambarella-gpio4",
GPIO4_BASE, GPIO(4 * GPIO_BANK_SIZE)),
#endif
#if (GPIO_INSTANCES >= 6)
AMBARELLA_GPIO_BANK("ambarella-gpio5",
GPIO5_BASE, GPIO(5 * GPIO_BANK_SIZE)),
#endif
};
/* ==========================================================================*/
static struct ambarella_gpio_chip *ambarella_gpio_id_to_chip(int id)
{
struct ambarella_gpio_chip *chip = NULL;
if (id < 0) {
chip = NULL;
} else if (id < (1 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[0];
} else if (id < (2 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[1];
#if (GPIO_INSTANCES >= 3)
} else if (id < (3 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[2];
#endif
#if (GPIO_INSTANCES >= 4)
} else if (id < (4 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[3];
#endif
#if (GPIO_INSTANCES >= 5)
} else if (id < (5 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[4];
#endif
#if (GPIO_INSTANCES >= 6)
} else if (id < (6 * GPIO_BANK_SIZE)) {
chip = &ambarella_gpio_banks[5];
#endif
}
return chip;
}
void ambarella_gpio_config(int id, int func)
{
struct ambarella_gpio_chip *chip;
u32 offset;
chip = ambarella_gpio_id_to_chip(id);
if (chip == NULL) {
pr_err("%s: invalid GPIO %d for func %d.\n",
__func__, id, func);
return;
}
offset = id & 0x1f;
ambarella_gpio_inline_config(chip, offset, func);
}
EXPORT_SYMBOL(ambarella_gpio_config);
void ambarella_gpio_set(int id, int value)
{
struct ambarella_gpio_chip *chip;
u32 offset;
chip = ambarella_gpio_id_to_chip(id);
if (chip == NULL) {
pr_err("%s: invalid GPIO %d.\n", __func__, id);
return;
}
offset = id & 0x1f;
ambarella_gpio_inline_set(chip, offset, value);
}
EXPORT_SYMBOL(ambarella_gpio_set);
int ambarella_gpio_get(int id)
{
struct ambarella_gpio_chip *chip;
u32 offset;
chip = ambarella_gpio_id_to_chip(id);
if (chip == NULL) {
pr_err("%s: invalid GPIO %d.\n", __func__, id);
return 0;
}
offset = id & 0x1f;
return ambarella_gpio_inline_get(chip, offset);
}
EXPORT_SYMBOL(ambarella_gpio_get);
/* ==========================================================================*/
void ambarella_gpio_raw_lock(u32 id, unsigned long *pflags)
{
spin_lock_irqsave(&ambarella_gpio_banks[id].lock, *pflags);
}
void ambarella_gpio_raw_unlock(u32 id, unsigned long *pflags)
{
spin_unlock_irqrestore(&ambarella_gpio_banks[id].lock, *pflags);
}
/* ==========================================================================*/
void __init ambarella_gpio_set_valid(unsigned pin, int valid)
{
if (likely(pin >=0 && pin < AMBGPIO_SIZE)) {
if (valid)
__set_bit(pin, ambarella_gpio_valid);
else
__clear_bit(pin, ambarella_gpio_valid);
}
}
int __init ambarella_init_gpio(void)
{
int retval = 0;
int i;
mutex_lock(&ambarella_gpio_mtx);
memset(ambarella_gpio_valid, 0xff, sizeof(ambarella_gpio_valid));
memset(ambarella_gpio_freeflag, 0xff, sizeof(ambarella_gpio_freeflag));
for (i = GPIO_MAX_LINES + 1; i < AMBGPIO_SIZE; i++) {
ambarella_gpio_set_valid(i, 0);
__clear_bit(i, ambarella_gpio_freeflag);
}
mutex_unlock(&ambarella_gpio_mtx);
for (i = 0; i < ARRAY_SIZE(ambarella_gpio_banks); i++) {
spin_lock_init(&ambarella_gpio_banks[i].lock);
retval = gpiochip_add(&ambarella_gpio_banks[i].chip);
if (retval) {
pr_err("%s: gpiochip_add %s fail %d.\n", __func__,
ambarella_gpio_banks[i].chip.label, retval);
break;
}
}
return retval;
}
/* ==========================================================================*/
u32 ambarella_gpio_suspend(u32 level)
{
u32 i;
unsigned long flags;
for (i = 0; i < GPIO_INSTANCES; i++) {
spin_lock_irqsave(&ambarella_gpio_banks[i].lock, flags);
ambarella_gpio_banks[i].pm_info.afsel_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_AFSEL_OFFSET);
ambarella_gpio_banks[i].pm_info.dir_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_DIR_OFFSET);
ambarella_gpio_banks[i].pm_info.is_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_IS_OFFSET);
ambarella_gpio_banks[i].pm_info.ibe_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_IBE_OFFSET);
ambarella_gpio_banks[i].pm_info.iev_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_IEV_OFFSET);
ambarella_gpio_banks[i].pm_info.ie_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_IE_OFFSET);
ambarella_gpio_banks[i].pm_info.mask_reg =
~ambarella_gpio_banks[i].pm_info.afsel_reg;
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_MASK_OFFSET,
ambarella_gpio_banks[i].pm_info.mask_reg);
ambarella_gpio_banks[i].pm_info.data_reg = amba_readl(
ambarella_gpio_banks[i].base_reg + GPIO_DATA_OFFSET);
spin_unlock_irqrestore(&ambarella_gpio_banks[i].lock, flags);
}
return 0;
}
u32 ambarella_gpio_resume(u32 level)
{
u32 i;
unsigned long flags;
for (i = 0; i < GPIO_INSTANCES; i++) {
spin_lock_irqsave(&ambarella_gpio_banks[i].lock, flags);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_AFSEL_OFFSET,
ambarella_gpio_banks[i].pm_info.afsel_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_DIR_OFFSET,
ambarella_gpio_banks[i].pm_info.dir_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_MASK_OFFSET,
ambarella_gpio_banks[i].pm_info.mask_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_DATA_OFFSET,
ambarella_gpio_banks[i].pm_info.data_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_IS_OFFSET,
ambarella_gpio_banks[i].pm_info.is_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_IBE_OFFSET,
ambarella_gpio_banks[i].pm_info.ibe_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_IEV_OFFSET,
ambarella_gpio_banks[i].pm_info.iev_reg);
amba_writel(
ambarella_gpio_banks[i].base_reg + GPIO_IE_OFFSET,
ambarella_gpio_banks[i].pm_info.ie_reg);
spin_unlock_irqrestore(&ambarella_gpio_banks[i].lock, flags);
}
return 0;
}
/* ==========================================================================*/
int ambarella_set_gpio_output_can_sleep(
struct ambarella_gpio_io_info *pinfo, u32 on, int can_sleep)
{
int retval = 0;
if (pinfo == NULL) {
pr_err("%s: pinfo is NULL.\n", __func__);
retval = -1;
goto ambarella_set_gpio_output_can_sleep_exit;
}
if (pinfo->gpio_id < 0 ) {
pr_debug("%s: wrong gpio id %d.\n", __func__, pinfo->gpio_id);
retval = -1;
goto ambarella_set_gpio_output_can_sleep_exit;
}
pr_debug("%s: Gpio[%d] %s, level[%s], delay[%dms].\n", __func__,
pinfo->gpio_id, on ? "ON" : "OFF",
pinfo->active_level ? "HIGH" : "LOW",
pinfo->active_delay);
if (pinfo->gpio_id >= EXT_GPIO(0)) {
pr_debug("%s: try expander gpio id %d.\n",
__func__, pinfo->gpio_id);
if (on) {
gpio_direction_output(pinfo->gpio_id,
pinfo->active_level);
} else {
gpio_direction_output(pinfo->gpio_id,
!pinfo->active_level);
}
} else {
ambarella_gpio_config(pinfo->gpio_id, GPIO_FUNC_SW_OUTPUT);
if (on) {
ambarella_gpio_set(pinfo->gpio_id,
pinfo->active_level);
} else {
ambarella_gpio_set(pinfo->gpio_id,
!pinfo->active_level);
}
}
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
ambarella_set_gpio_output_can_sleep_exit:
return retval;
}
EXPORT_SYMBOL(ambarella_set_gpio_output_can_sleep);
u32 ambarella_get_gpio_input_can_sleep(
struct ambarella_gpio_io_info *pinfo, int can_sleep)
{
u32 gpio_value = 0;
if (pinfo == NULL) {
pr_err("%s: pinfo is NULL.\n", __func__);
goto ambarella_get_gpio_input_can_sleep_exit;
}
if (pinfo->gpio_id < 0 ) {
pr_debug("%s: wrong gpio id %d.\n", __func__, pinfo->gpio_id);
goto ambarella_get_gpio_input_can_sleep_exit;
}
if (pinfo->gpio_id >= EXT_GPIO(0)) {
pr_debug("%s: try expander gpio id %d.\n",
__func__, pinfo->gpio_id);
gpio_direction_input(pinfo->gpio_id);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
gpio_value = gpio_get_value(pinfo->gpio_id);
} else {
ambarella_gpio_config(pinfo->gpio_id, GPIO_FUNC_SW_INPUT);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
gpio_value = ambarella_gpio_get(pinfo->gpio_id);
}
pr_debug("%s: {gpio[%d], level[%s], delay[%dms]} get[%d].\n",
__func__, pinfo->gpio_id,
pinfo->active_level ? "HIGH" : "LOW",
pinfo->active_delay, gpio_value);
ambarella_get_gpio_input_can_sleep_exit:
return (gpio_value == pinfo->active_level) ? 1 : 0;
}
EXPORT_SYMBOL(ambarella_get_gpio_input_can_sleep);
int ambarella_set_gpio_reset_can_sleep(
struct ambarella_gpio_io_info *pinfo, int can_sleep)
{
int retval = 0;
if (pinfo == NULL) {
pr_err("%s: pinfo is NULL.\n", __func__);
retval = -1;
goto ambarella_set_gpio_reset_can_sleep_exit;
}
if (pinfo->gpio_id < 0 ) {
pr_debug("%s: wrong gpio id %d.\n", __func__, pinfo->gpio_id);
retval = -1;
goto ambarella_set_gpio_reset_can_sleep_exit;
}
pr_debug("%s: Reset gpio[%d], level[%s], delay[%dms].\n",
__func__, pinfo->gpio_id,
pinfo->active_level ? "HIGH" : "LOW",
pinfo->active_delay);
if (pinfo->gpio_id >= EXT_GPIO(0)) {
pr_debug("%s: try expander gpio id %d.\n",
__func__, pinfo->gpio_id);
gpio_direction_output(pinfo->gpio_id, pinfo->active_level);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
gpio_direction_output(pinfo->gpio_id, !pinfo->active_level);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
} else {
ambarella_gpio_config(pinfo->gpio_id, GPIO_FUNC_SW_OUTPUT);
ambarella_gpio_set(pinfo->gpio_id, pinfo->active_level);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
ambarella_gpio_set(pinfo->gpio_id, !pinfo->active_level);
if (can_sleep) {
msleep(pinfo->active_delay);
} else {
mdelay(pinfo->active_delay);
}
}
ambarella_set_gpio_reset_can_sleep_exit:
return retval;
}
EXPORT_SYMBOL(ambarella_set_gpio_reset_can_sleep);
/* ==========================================================================*/
int ambarella_set_gpio_output(
struct ambarella_gpio_io_info *pinfo, u32 on)
{
return ambarella_set_gpio_output_can_sleep(pinfo, on, 0);
}
EXPORT_SYMBOL(ambarella_set_gpio_output);
u32 ambarella_get_gpio_input(
struct ambarella_gpio_io_info *pinfo)
{
return ambarella_get_gpio_input_can_sleep(pinfo, 0);
}
EXPORT_SYMBOL(ambarella_get_gpio_input);
int ambarella_set_gpio_reset(
struct ambarella_gpio_io_info *pinfo)
{
return ambarella_set_gpio_reset_can_sleep(pinfo, 0);
}
EXPORT_SYMBOL(ambarella_set_gpio_reset);
int ambarella_is_valid_gpio_irq(struct ambarella_gpio_irq_info *pinfo)
{
int bvalid = 0;
if (pinfo == NULL) {
pr_err("%s: pinfo is NULL.\n", __func__);
goto ambarella_is_valid_gpio_irq_exit;
}
if ((pinfo->irq_gpio < 0 ) || (pinfo->irq_gpio >= ARCH_NR_GPIOS))
goto ambarella_is_valid_gpio_irq_exit;
if ((pinfo->irq_type != IRQ_TYPE_EDGE_RISING) &&
(pinfo->irq_type != IRQ_TYPE_EDGE_FALLING) &&
(pinfo->irq_type != IRQ_TYPE_EDGE_BOTH) &&
(pinfo->irq_type != IRQ_TYPE_LEVEL_HIGH) &&
(pinfo->irq_type != IRQ_TYPE_LEVEL_LOW))
goto ambarella_is_valid_gpio_irq_exit;
if ((pinfo->irq_gpio_val != GPIO_HIGH) &&
(pinfo->irq_gpio_val != GPIO_LOW))
goto ambarella_is_valid_gpio_irq_exit;
if ((pinfo->irq_gpio_mode != GPIO_FUNC_SW_INPUT) &&
(pinfo->irq_gpio_mode != GPIO_FUNC_SW_OUTPUT) &&
(pinfo->irq_gpio_mode != GPIO_FUNC_HW))
goto ambarella_is_valid_gpio_irq_exit;
if ((pinfo->irq_gpio_mode != GPIO_FUNC_HW) &&
((pinfo->irq_line < GPIO_INT_VEC(0)) ||
(pinfo->irq_line >= NR_IRQS)))
goto ambarella_is_valid_gpio_irq_exit;
bvalid = 1;
ambarella_is_valid_gpio_irq_exit:
return bvalid;
}
EXPORT_SYMBOL(ambarella_is_valid_gpio_irq);