// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 MundoReader S.L.
* Author: Heiko Stuebner <heiko@sntech.de>
*
* Copyright (c) 2021 Rockchip Electronics Co. Ltd.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "../pinctrl/core.h"
#include "../pinctrl/pinctrl-rockchip.h"
/*
* Version ID Register
* Bits [31:24] - Major Version
* Bits [23:16] - Minor Version
* Bits [15:0] - Revision Number
*/
#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */
#define GPIO_TYPE_V2 (0x01000C2B)
#define GPIO_TYPE_V2_1 (0x0101157C)
#define GPIO_TYPE_V2_2 (0x010219C8)
static const struct rockchip_gpio_regs gpio_regs_v1 = {
.port_dr = 0x00,
.port_ddr = 0x04,
.int_en = 0x30,
.int_mask = 0x34,
.int_type = 0x38,
.int_polarity = 0x3c,
.int_status = 0x40,
.int_rawstatus = 0x44,
.debounce = 0x48,
.port_eoi = 0x4c,
.ext_port = 0x50,
};
static const struct rockchip_gpio_regs gpio_regs_v2 = {
.port_dr = 0x00,
.port_ddr = 0x08,
.int_en = 0x10,
.int_mask = 0x18,
.int_type = 0x20,
.int_polarity = 0x28,
.int_bothedge = 0x30,
.int_status = 0x50,
.int_rawstatus = 0x58,
.debounce = 0x38,
.dbclk_div_en = 0x40,
.dbclk_div_con = 0x48,
.port_eoi = 0x60,
.ext_port = 0x70,
.version_id = 0x78,
};
static inline void gpio_writel_v2(u32 val, void __iomem *reg)
{
writel((val & 0xffff) | 0xffff0000, reg);
writel((val >> 16) | 0xffff0000, reg + 0x4);
}
static inline u32 gpio_readl_v2(void __iomem *reg)
{
return readl(reg + 0x4) << 16 | readl(reg);
}
static inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank,
u32 value, unsigned int offset)
{
void __iomem *reg = bank->reg_base + offset;
if (bank->gpio_type == GPIO_TYPE_V2)
gpio_writel_v2(value, reg);
else
writel(value, reg);
}
static inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank,
unsigned int offset)
{
void __iomem *reg = bank->reg_base + offset;
u32 value;
if (bank->gpio_type == GPIO_TYPE_V2)
value = gpio_readl_v2(reg);
else
value = readl(reg);
return value;
}
static inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank,
u32 bit, u32 value,
unsigned int offset)
{
void __iomem *reg = bank->reg_base + offset;
u32 data;
if (bank->gpio_type == GPIO_TYPE_V2) {
if (value)
data = BIT(bit % 16) | BIT(bit % 16 + 16);
else
data = BIT(bit % 16 + 16);
writel(data, bit >= 16 ? reg + 0x4 : reg);
} else {
data = readl(reg);
data &= ~BIT(bit);
if (value)
data |= BIT(bit);
writel(data, reg);
}
}
static inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank,
u32 bit, unsigned int offset)
{
void __iomem *reg = bank->reg_base + offset;
u32 data;
if (bank->gpio_type == GPIO_TYPE_V2) {
data = readl(bit >= 16 ? reg + 0x4 : reg);
data >>= bit % 16;
} else {
data = readl(reg);
data >>= bit;
}
return data & (0x1);
}
static int rockchip_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
u32 data;
data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
if (data)
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
static int rockchip_gpio_set_direction(struct gpio_chip *chip,
unsigned int offset, bool input)
{
struct rockchip_pin_bank *