/*
* Support functions for OMAP GPIO
*
* Copyright (C) 2003-2005 Nokia Corporation
* Written by Juha Yrjölä <juha.yrjola@nokia.com>
*
* Copyright (C) 2009 Texas Instruments
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/syscore_ops.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#define OFF_MODE 1
static LIST_HEAD(omap_gpio_list);
struct gpio_regs {
u32 irqenable1;
u32 irqenable2;
u32 wake_en;
u32 ctrl;
u32 oe;
u32 leveldetect0;
u32 leveldetect1;
u32 risingdetect;
u32 fallingdetect;
u32 dataout;
u32 debounce;
u32 debounce_en;
};
struct gpio_bank {
struct list_head node;
void __iomem *base;
u16 irq;
struct irq_domain *domain;
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
struct gpio_regs context;
u32 saved_datain;
u32 level_mask;
u32 toggle_mask;
spinlock_t lock;
struct gpio_chip chip;
struct clk *dbck;
u32 mod_usage;
u32 dbck_enable_mask;
bool dbck_enabled;
struct device *dev;
bool is_mpuio;
bool dbck_flag;
bool loses_context;
bool context_valid;
int stride;
u32 width;
int context_loss_count;
int power_mode;
bool workaround_enabled;
void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
int (*get_context_loss_count)(struct device *dev);
struct omap_gpio_reg_offs *regs;
};
#define GPIO_INDEX(bank, gpio) (gpio % bank->width)
#define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
#define GPIO_MOD_CTRL_BIT BIT(0)
static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
{
return bank->chip.base + gpio_irq;
}
static int omap_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
return irq_find_mapping(bank->domain, offset);
}
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
u32 l;
reg += bank->regs->direction;
l = __raw_readl(reg);
if (is_input)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
bank->context.oe = l;
}
/* set data out value using dedicate set/clear register */
static void _set_gpio_dataout_reg(struct gpio_bank *bank, int gpio, int enable)
{
void __iomem *reg = bank->base;
u32 l = GPIO_BIT(bank, gpio);
if (enable) {
reg += bank->regs->set_dataout;
bank->context.dataout |= l;
} else {
reg += bank->regs->clr_dataout;
bank->context.dataout &= ~l;
}
__raw_writel(l, reg);
}
/* set data out value using mask register */
static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable)
{
void __iomem *reg = bank->base + bank->regs->dataout;
u32 gpio_bit = GPIO_BIT(bank, gpio);
u32 l;
l = __raw_readl(reg);
if (enable)
l |= gpio_bit;
else
l &= ~gpio_bit;
__raw_writel(l, reg);
bank->context.dataout = l;
}
static int _get_gpio_datain(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->datain;
return (__raw_readl(reg) & (1 << offset)) != 0;
}
static int _get_gpio_dataout(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->dataout;
return (__raw_readl(reg) & (1 << offset)) != 0;
}
static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
{
int l = __raw_readl(base + reg);
if (set)
l |= mask;
else
l &= ~mask;
__raw_writel(l, base + reg);
}
static inline void _gpio_dbck_enable(struct gpio_bank *bank)
{
if (bank->dbck_enable_mask && !bank->dbck_enabled) {
clk_enable(bank->dbck);
bank->dbck_enabled = true;
__raw_writel(bank->dbck_enable_mask,
bank->base + bank->regs->debounce_en);
}
}
static inline void _gpio_dbck_disable(struct gpio_bank *bank)
{
if (bank->dbck_enable_mask && bank->dbck_enabled) {
/*
* Disable debounce before cutting it's clock. If debounce is
* enabled but the clock is not, GPIO module seems to be unable
* to detect events and generate interrupts at least on OMAP3.
*/
__raw_writel(0, bank->base + bank->regs->debounce_en);
clk_disable(bank->dbck);
bank->dbck_enabled = false;
}
}
/**
* _set_gpio_debounce - low level gpio debounce time
* @bank: the gpio bank we're acting upon
* @gpio: the gpio number on this @gpio
* @debounce: debounce time to use
*
* OMAP's debounce time is in 31us steps so we need
* to convert and round up to the closest unit.
*/
static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
|