// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2016-2018 Nuvoton Technology corporation.
// Copyright (c) 2016, Dell Inc
// Copyright (c) 2021-2022 Jonathan Neuschäfer
//
// This driver uses the following registers:
// - Pin mux registers, in the GCR (general control registers) block
// - GPIO registers, specific to each GPIO bank
// - GPIO event (interrupt) registers, located centrally in the GPIO register
// block, shared between all GPIO banks
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include "../core.h"
/* GCR registers */
#define WPCM450_GCR_MFSEL1 0x0c
#define WPCM450_GCR_MFSEL2 0x10
#define WPCM450_GCR_NONE 0
/* GPIO event (interrupt) registers */
#define WPCM450_GPEVTYPE 0x00
#define WPCM450_GPEVPOL 0x04
#define WPCM450_GPEVDBNC 0x08
#define WPCM450_GPEVEN 0x0c
#define WPCM450_GPEVST 0x10
#define WPCM450_NUM_BANKS 8
#define WPCM450_NUM_GPIOS 128
#define WPCM450_NUM_GPIO_IRQS 4
struct wpcm450_pinctrl;
struct wpcm450_bank;
struct wpcm450_gpio {
struct gpio_chip gc;
struct wpcm450_pinctrl *pctrl;
const struct wpcm450_bank *bank;
};
struct wpcm450_pinctrl {
struct pinctrl_dev *pctldev;
struct device *dev;
struct irq_domain *domain;
struct regmap *gcr_regmap;
void __iomem *gpio_base;
struct wpcm450_gpio gpio_bank[WPCM450_NUM_BANKS];
unsigned long both_edges;
/*
* This spin lock protects registers and struct wpcm450_pinctrl fields
* against concurrent access.
*/
raw_spinlock_t lock;
};
struct wpcm450_bank {
/* Range of GPIOs in this port */
u8 base;
u8 length;
/* Register offsets (0 = register doesn't exist in this port) */
u8 cfg0, cfg1, cfg2;
u8 blink;
u8 dataout, datain;
/* Interrupt bit mapping */
u8 first_irq_bit; /* First bit in GPEVST that belongs to this bank */
u8 num_irqs; /* Number of IRQ-capable GPIOs in this bank */
u8 first_irq_gpio; /* First IRQ-capable GPIO in this bank */
};
static const struct wpcm450_bank wpcm450_banks[WPCM450_NUM_BANKS] = {
/* range cfg0 cfg1 cfg2 blink out in IRQ map */
{ 0, 16, 0x14, 0x18, 0, 0, 0x1c, 0x20, 0, 16, 0 },
{ 16, 16, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 16, 2, 8 },
{ 32, 16, 0x3c, 0x40, 0x44, 0, 0x48, 0x4c, 0, 0, 0 },
{ 48, 16, 0x50, 0x54, 0x58, 0, 0x5c, 0x60, 0, 0, 0 },
{ 64, 16, 0x64, 0x68, 0x6c, 0, 0x70, 0x74, 0, 0, 0 },
{ 80, 16, 0x78, 0x7c, 0x80, 0, 0x84, 0x88, 0, 0, 0 },
{ 96, 18, 0, 0, 0, 0, 0, 0x8c, 0, 0, 0 },
{ 114, 14, 0x90, 0x94, 0x98, 0, 0x9c, 0xa0, 0, 0, 0 },
};
static int wpcm450_gpio_irq_bitnum(struct wpcm450_gpio *gpio, struct irq_data *d)
{
const struct wpcm450_bank *bank = gpio->bank;
int hwirq = irqd_to_hwirq(d);
if (hwirq < bank->first_irq_gpio)
return -EINVAL;
if (hwirq - bank->first_irq_gpio >= bank->num_irqs)
return -EINVAL;
return hwirq - bank->first_irq_gpio + bank->first_irq_bit;
}
static int wpcm450_irq_bitnum_to_gpio(struct wpcm450_gpio *gpio, int bitnum)
{
const struct wpcm450_bank *bank = gpio->bank;
if (bitnum < bank->first_irq_bit)
return -EINVAL;
if (bitnum - bank->first_irq_bit > bank->num_irqs)
return -EINVAL;
return bitnum - bank->first_irq_bit + bank->first_irq_gpio;
}
static void wpcm450_gpio_irq_ack(struct irq_data *d)
{
struct wpcm450_gpio *gpio = gpiochip_get_data(irq_data_get_irq_chip_data(d));
struct wpcm450_pinctrl *pctrl = gpio->pctrl;
unsigned