Source
x
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2017 Socionext Inc.
// Author: Masahiro Yamada <yamada.masahiro@socionext.com>
/* data */
/* direction (1:in, 0:out) */
/* irq enable */
/* irq mode (1: both edge) */
/* noise filter enable */
/* noise filter clock cycle */
struct uniphier_gpio_priv {
struct gpio_chip chip;
struct irq_chip irq_chip;
struct irq_domain *domain;
void __iomem *regs;
spinlock_t lock;
u32 saved_vals[0];
};
static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
{
unsigned int reg;
reg = (bank + 1) * 8;
/*
* Unfortunately, the GPIO port registers are not contiguous because
* offset 0x90-0x9f is used for IRQ. Add 0x10 when crossing the region.
*/
if (reg >= UNIPHIER_GPIO_IRQ_EN)
reg += 0x10;
return reg;
}
static void uniphier_gpio_get_bank_and_mask(unsigned int offset,
unsigned int *bank, u32 *mask)
{
*bank = offset / UNIPHIER_GPIO_LINES_PER_BANK;
*mask = BIT(offset % UNIPHIER_GPIO_LINES_PER_BANK);
}
static void uniphier_gpio_reg_update(struct uniphier_gpio_priv *priv,
unsigned int reg, u32 mask, u32 val)
{
unsigned long flags;
u32 tmp;
spin_lock_irqsave(&priv->lock, flags);
tmp = readl(priv->regs + reg);
tmp &= ~mask;
tmp |= mask & val;
writel(tmp, priv->regs + reg);
spin_unlock_irqrestore(&priv->lock, flags);
}
static void uniphier_gpio_bank_write(struct gpio_chip *chip, unsigned int bank,
unsigned int reg, u32 mask, u32 val)
{
struct uniphier_gpio_priv *priv = gpiochip_get_data(chip);
if (!mask)
return;
uniphier_gpio_reg_update(priv, uniphier_gpio_bank_to_reg(bank) + reg,
mask, val);
}
static void uniphier_gpio_offset_write(struct gpio_chip *chip,
unsigned int offset, unsigned int reg,
int val)
{
unsigned int bank;