162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2016, 2017 Cavium Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/irq.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define GPIO_RX_DAT	0x0
2062306a36Sopenharmony_ci#define GPIO_TX_SET	0x8
2162306a36Sopenharmony_ci#define GPIO_TX_CLR	0x10
2262306a36Sopenharmony_ci#define GPIO_CONST	0x90
2362306a36Sopenharmony_ci#define  GPIO_CONST_GPIOS_MASK 0xff
2462306a36Sopenharmony_ci#define GPIO_BIT_CFG	0x400
2562306a36Sopenharmony_ci#define  GPIO_BIT_CFG_TX_OE		BIT(0)
2662306a36Sopenharmony_ci#define  GPIO_BIT_CFG_PIN_XOR		BIT(1)
2762306a36Sopenharmony_ci#define  GPIO_BIT_CFG_INT_EN		BIT(2)
2862306a36Sopenharmony_ci#define  GPIO_BIT_CFG_INT_TYPE		BIT(3)
2962306a36Sopenharmony_ci#define  GPIO_BIT_CFG_FIL_MASK		GENMASK(11, 4)
3062306a36Sopenharmony_ci#define  GPIO_BIT_CFG_FIL_CNT_SHIFT	4
3162306a36Sopenharmony_ci#define  GPIO_BIT_CFG_FIL_SEL_SHIFT	8
3262306a36Sopenharmony_ci#define  GPIO_BIT_CFG_TX_OD		BIT(12)
3362306a36Sopenharmony_ci#define  GPIO_BIT_CFG_PIN_SEL_MASK	GENMASK(25, 16)
3462306a36Sopenharmony_ci#define GPIO_INTR	0x800
3562306a36Sopenharmony_ci#define  GPIO_INTR_INTR			BIT(0)
3662306a36Sopenharmony_ci#define  GPIO_INTR_INTR_W1S		BIT(1)
3762306a36Sopenharmony_ci#define  GPIO_INTR_ENA_W1C		BIT(2)
3862306a36Sopenharmony_ci#define  GPIO_INTR_ENA_W1S		BIT(3)
3962306a36Sopenharmony_ci#define GPIO_2ND_BANK	0x1400
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define GLITCH_FILTER_400NS ((4u << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \
4262306a36Sopenharmony_ci			     (9u << GPIO_BIT_CFG_FIL_CNT_SHIFT))
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct thunderx_gpio;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct thunderx_line {
4762306a36Sopenharmony_ci	struct thunderx_gpio	*txgpio;
4862306a36Sopenharmony_ci	unsigned int		line;
4962306a36Sopenharmony_ci	unsigned int		fil_bits;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct thunderx_gpio {
5362306a36Sopenharmony_ci	struct gpio_chip	chip;
5462306a36Sopenharmony_ci	u8 __iomem		*register_base;
5562306a36Sopenharmony_ci	struct msix_entry	*msix_entries;	/* per line MSI-X */
5662306a36Sopenharmony_ci	struct thunderx_line	*line_entries;	/* per line irq info */
5762306a36Sopenharmony_ci	raw_spinlock_t		lock;
5862306a36Sopenharmony_ci	unsigned long		invert_mask[2];
5962306a36Sopenharmony_ci	unsigned long		od_mask[2];
6062306a36Sopenharmony_ci	int			base_msi;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic unsigned int bit_cfg_reg(unsigned int line)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return 8 * line + GPIO_BIT_CFG;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic unsigned int intr_reg(unsigned int line)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	return 8 * line + GPIO_INTR;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic bool thunderx_gpio_is_gpio_nowarn(struct thunderx_gpio *txgpio,
7462306a36Sopenharmony_ci					 unsigned int line)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return (bit_cfg & GPIO_BIT_CFG_PIN_SEL_MASK) == 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci * Check (and WARN) that the pin is available for GPIO.  We will not
8362306a36Sopenharmony_ci * allow modification of the state of non-GPIO pins from this driver.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic bool thunderx_gpio_is_gpio(struct thunderx_gpio *txgpio,
8662306a36Sopenharmony_ci				  unsigned int line)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	bool rv = thunderx_gpio_is_gpio_nowarn(txgpio, line);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	WARN_RATELIMIT(!rv, "Pin %d not available for GPIO\n", line);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return rv;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int thunderx_gpio_request(struct gpio_chip *chip, unsigned int line)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return thunderx_gpio_is_gpio(txgpio, line) ? 0 : -EIO;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!thunderx_gpio_is_gpio(txgpio, line))
10762306a36Sopenharmony_ci		return -EIO;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	raw_spin_lock(&txgpio->lock);
11062306a36Sopenharmony_ci	clear_bit(line, txgpio->invert_mask);
11162306a36Sopenharmony_ci	clear_bit(line, txgpio->od_mask);
11262306a36Sopenharmony_ci	writeq(txgpio->line_entries[line].fil_bits,
11362306a36Sopenharmony_ci	       txgpio->register_base + bit_cfg_reg(line));
11462306a36Sopenharmony_ci	raw_spin_unlock(&txgpio->lock);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line,
11962306a36Sopenharmony_ci			      int value)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
12262306a36Sopenharmony_ci	int bank = line / 64;
12362306a36Sopenharmony_ci	int bank_bit = line % 64;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	void __iomem *reg = txgpio->register_base +
12662306a36Sopenharmony_ci		(bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	writeq(BIT_ULL(bank_bit), reg);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line,
13262306a36Sopenharmony_ci				 int value)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
13562306a36Sopenharmony_ci	u64 bit_cfg = txgpio->line_entries[line].fil_bits | GPIO_BIT_CFG_TX_OE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!thunderx_gpio_is_gpio(txgpio, line))
13862306a36Sopenharmony_ci		return -EIO;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	raw_spin_lock(&txgpio->lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	thunderx_gpio_set(chip, line, value);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (test_bit(line, txgpio->invert_mask))
14562306a36Sopenharmony_ci		bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (test_bit(line, txgpio->od_mask))
14862306a36Sopenharmony_ci		bit_cfg |= GPIO_BIT_CFG_TX_OD;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	raw_spin_unlock(&txgpio->lock);
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int thunderx_gpio_get_direction(struct gpio_chip *chip, unsigned int line)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
15962306a36Sopenharmony_ci	u64 bit_cfg;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!thunderx_gpio_is_gpio_nowarn(txgpio, line))
16262306a36Sopenharmony_ci		/*
16362306a36Sopenharmony_ci		 * Say it is input for now to avoid WARNing on
16462306a36Sopenharmony_ci		 * gpiochip_add_data().  We will WARN if someone
16562306a36Sopenharmony_ci		 * requests it or tries to use it.
16662306a36Sopenharmony_ci		 */
16762306a36Sopenharmony_ci		return 1;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (bit_cfg & GPIO_BIT_CFG_TX_OE)
17262306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int thunderx_gpio_set_config(struct gpio_chip *chip,
17862306a36Sopenharmony_ci				    unsigned int line,
17962306a36Sopenharmony_ci				    unsigned long cfg)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	bool orig_invert, orig_od, orig_dat, new_invert, new_od;
18262306a36Sopenharmony_ci	u32 arg, sel;
18362306a36Sopenharmony_ci	u64 bit_cfg;
18462306a36Sopenharmony_ci	int bank = line / 64;
18562306a36Sopenharmony_ci	int bank_bit = line % 64;
18662306a36Sopenharmony_ci	int ret = -ENOTSUPP;
18762306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
18862306a36Sopenharmony_ci	void __iomem *reg = txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!thunderx_gpio_is_gpio(txgpio, line))
19162306a36Sopenharmony_ci		return -EIO;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	raw_spin_lock(&txgpio->lock);
19462306a36Sopenharmony_ci	orig_invert = test_bit(line, txgpio->invert_mask);
19562306a36Sopenharmony_ci	new_invert  = orig_invert;
19662306a36Sopenharmony_ci	orig_od = test_bit(line, txgpio->od_mask);
19762306a36Sopenharmony_ci	new_od = orig_od;
19862306a36Sopenharmony_ci	orig_dat = ((readq(reg) >> bank_bit) & 1) ^ orig_invert;
19962306a36Sopenharmony_ci	bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
20062306a36Sopenharmony_ci	switch (pinconf_to_config_param(cfg)) {
20162306a36Sopenharmony_ci	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
20262306a36Sopenharmony_ci		/*
20362306a36Sopenharmony_ci		 * Weird, setting open-drain mode causes signal
20462306a36Sopenharmony_ci		 * inversion.  Note this so we can compensate in the
20562306a36Sopenharmony_ci		 * dir_out function.
20662306a36Sopenharmony_ci		 */
20762306a36Sopenharmony_ci		set_bit(line, txgpio->invert_mask);
20862306a36Sopenharmony_ci		new_invert  = true;
20962306a36Sopenharmony_ci		set_bit(line, txgpio->od_mask);
21062306a36Sopenharmony_ci		new_od = true;
21162306a36Sopenharmony_ci		ret = 0;
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	case PIN_CONFIG_DRIVE_PUSH_PULL:
21462306a36Sopenharmony_ci		clear_bit(line, txgpio->invert_mask);
21562306a36Sopenharmony_ci		new_invert  = false;
21662306a36Sopenharmony_ci		clear_bit(line, txgpio->od_mask);
21762306a36Sopenharmony_ci		new_od  = false;
21862306a36Sopenharmony_ci		ret = 0;
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	case PIN_CONFIG_INPUT_DEBOUNCE:
22162306a36Sopenharmony_ci		arg = pinconf_to_config_argument(cfg);
22262306a36Sopenharmony_ci		if (arg > 1228) { /* 15 * 2^15 * 2.5nS maximum */
22362306a36Sopenharmony_ci			ret = -EINVAL;
22462306a36Sopenharmony_ci			break;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci		arg *= 400; /* scale to 2.5nS clocks. */
22762306a36Sopenharmony_ci		sel = 0;
22862306a36Sopenharmony_ci		while (arg > 15) {
22962306a36Sopenharmony_ci			sel++;
23062306a36Sopenharmony_ci			arg++; /* always round up */
23162306a36Sopenharmony_ci			arg >>= 1;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci		txgpio->line_entries[line].fil_bits =
23462306a36Sopenharmony_ci			(sel << GPIO_BIT_CFG_FIL_SEL_SHIFT) |
23562306a36Sopenharmony_ci			(arg << GPIO_BIT_CFG_FIL_CNT_SHIFT);
23662306a36Sopenharmony_ci		bit_cfg &= ~GPIO_BIT_CFG_FIL_MASK;
23762306a36Sopenharmony_ci		bit_cfg |= txgpio->line_entries[line].fil_bits;
23862306a36Sopenharmony_ci		writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
23962306a36Sopenharmony_ci		ret = 0;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	default:
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	raw_spin_unlock(&txgpio->lock);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/*
24762306a36Sopenharmony_ci	 * If currently output and OPEN_DRAIN changed, install the new
24862306a36Sopenharmony_ci	 * settings
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	if ((new_invert != orig_invert || new_od != orig_od) &&
25162306a36Sopenharmony_ci	    (bit_cfg & GPIO_BIT_CFG_TX_OE))
25262306a36Sopenharmony_ci		ret = thunderx_gpio_dir_out(chip, line, orig_dat ^ new_invert);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return ret;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
26062306a36Sopenharmony_ci	int bank = line / 64;
26162306a36Sopenharmony_ci	int bank_bit = line % 64;
26262306a36Sopenharmony_ci	u64 read_bits = readq(txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_RX_DAT);
26362306a36Sopenharmony_ci	u64 masked_bits = read_bits & BIT_ULL(bank_bit);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (test_bit(line, txgpio->invert_mask))
26662306a36Sopenharmony_ci		return masked_bits == 0;
26762306a36Sopenharmony_ci	else
26862306a36Sopenharmony_ci		return masked_bits != 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void thunderx_gpio_set_multiple(struct gpio_chip *chip,
27262306a36Sopenharmony_ci				       unsigned long *mask,
27362306a36Sopenharmony_ci				       unsigned long *bits)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int bank;
27662306a36Sopenharmony_ci	u64 set_bits, clear_bits;
27762306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	for (bank = 0; bank <= chip->ngpio / 64; bank++) {
28062306a36Sopenharmony_ci		set_bits = bits[bank] & mask[bank];
28162306a36Sopenharmony_ci		clear_bits = ~bits[bank] & mask[bank];
28262306a36Sopenharmony_ci		writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET);
28362306a36Sopenharmony_ci		writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void thunderx_gpio_irq_ack(struct irq_data *d)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
29062306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	writeq(GPIO_INTR_INTR,
29362306a36Sopenharmony_ci	       txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void thunderx_gpio_irq_mask(struct irq_data *d)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
29962306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	writeq(GPIO_INTR_ENA_W1C,
30262306a36Sopenharmony_ci	       txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void thunderx_gpio_irq_mask_ack(struct irq_data *d)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
30862306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR,
31162306a36Sopenharmony_ci	       txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void thunderx_gpio_irq_unmask(struct irq_data *d)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
31762306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	writeq(GPIO_INTR_ENA_W1S,
32062306a36Sopenharmony_ci	       txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int thunderx_gpio_irq_set_type(struct irq_data *d,
32462306a36Sopenharmony_ci				      unsigned int flow_type)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
32762306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
32862306a36Sopenharmony_ci	struct thunderx_line *txline =
32962306a36Sopenharmony_ci		&txgpio->line_entries[irqd_to_hwirq(d)];
33062306a36Sopenharmony_ci	u64 bit_cfg;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	irqd_set_trigger_type(d, flow_type);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (flow_type & IRQ_TYPE_EDGE_BOTH) {
33762306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_fasteoi_ack_irq);
33862306a36Sopenharmony_ci		bit_cfg |= GPIO_BIT_CFG_INT_TYPE;
33962306a36Sopenharmony_ci	} else {
34062306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_fasteoi_mask_irq);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	raw_spin_lock(&txgpio->lock);
34462306a36Sopenharmony_ci	if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) {
34562306a36Sopenharmony_ci		bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
34662306a36Sopenharmony_ci		set_bit(txline->line, txgpio->invert_mask);
34762306a36Sopenharmony_ci	} else {
34862306a36Sopenharmony_ci		clear_bit(txline->line, txgpio->invert_mask);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	clear_bit(txline->line, txgpio->od_mask);
35162306a36Sopenharmony_ci	writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line));
35262306a36Sopenharmony_ci	raw_spin_unlock(&txgpio->lock);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return IRQ_SET_MASK_OK;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void thunderx_gpio_irq_enable(struct irq_data *d)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	gpiochip_enable_irq(gc, irqd_to_hwirq(d));
36262306a36Sopenharmony_ci	irq_chip_enable_parent(d);
36362306a36Sopenharmony_ci	thunderx_gpio_irq_unmask(d);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void thunderx_gpio_irq_disable(struct irq_data *d)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	thunderx_gpio_irq_mask(d);
37162306a36Sopenharmony_ci	irq_chip_disable_parent(d);
37262306a36Sopenharmony_ci	gpiochip_disable_irq(gc, irqd_to_hwirq(d));
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci * Interrupts are chained from underlying MSI-X vectors.  We have
37762306a36Sopenharmony_ci * these irq_chip functions to be able to handle level triggering
37862306a36Sopenharmony_ci * semantics and other acknowledgment tasks associated with the GPIO
37962306a36Sopenharmony_ci * mechanism.
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic const struct irq_chip thunderx_gpio_irq_chip = {
38262306a36Sopenharmony_ci	.name			= "GPIO",
38362306a36Sopenharmony_ci	.irq_enable		= thunderx_gpio_irq_enable,
38462306a36Sopenharmony_ci	.irq_disable		= thunderx_gpio_irq_disable,
38562306a36Sopenharmony_ci	.irq_ack		= thunderx_gpio_irq_ack,
38662306a36Sopenharmony_ci	.irq_mask		= thunderx_gpio_irq_mask,
38762306a36Sopenharmony_ci	.irq_mask_ack		= thunderx_gpio_irq_mask_ack,
38862306a36Sopenharmony_ci	.irq_unmask		= thunderx_gpio_irq_unmask,
38962306a36Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
39062306a36Sopenharmony_ci	.irq_set_affinity	= irq_chip_set_affinity_parent,
39162306a36Sopenharmony_ci	.irq_set_type		= thunderx_gpio_irq_set_type,
39262306a36Sopenharmony_ci	.flags			= IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE,
39362306a36Sopenharmony_ci	GPIOCHIP_IRQ_RESOURCE_HELPERS,
39462306a36Sopenharmony_ci};
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
39762306a36Sopenharmony_ci					       unsigned int child,
39862306a36Sopenharmony_ci					       unsigned int child_type,
39962306a36Sopenharmony_ci					       unsigned int *parent,
40062306a36Sopenharmony_ci					       unsigned int *parent_type)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
40362306a36Sopenharmony_ci	struct irq_data *irqd;
40462306a36Sopenharmony_ci	unsigned int irq;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	irq = txgpio->msix_entries[child].vector;
40762306a36Sopenharmony_ci	irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq);
40862306a36Sopenharmony_ci	if (!irqd)
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci	*parent = irqd_to_hwirq(irqd);
41162306a36Sopenharmony_ci	*parent_type = IRQ_TYPE_LEVEL_HIGH;
41262306a36Sopenharmony_ci	return 0;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
41662306a36Sopenharmony_ci						    union gpio_irq_fwspec *gfwspec,
41762306a36Sopenharmony_ci						    unsigned int parent_hwirq,
41862306a36Sopenharmony_ci						    unsigned int parent_type)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	msi_alloc_info_t *info = &gfwspec->msiinfo;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	info->hwirq = parent_hwirq;
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int thunderx_gpio_probe(struct pci_dev *pdev,
42762306a36Sopenharmony_ci			       const struct pci_device_id *id)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	void __iomem * const *tbl;
43062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
43162306a36Sopenharmony_ci	struct thunderx_gpio *txgpio;
43262306a36Sopenharmony_ci	struct gpio_chip *chip;
43362306a36Sopenharmony_ci	struct gpio_irq_chip *girq;
43462306a36Sopenharmony_ci	int ngpio, i;
43562306a36Sopenharmony_ci	int err = 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	txgpio = devm_kzalloc(dev, sizeof(*txgpio), GFP_KERNEL);
43862306a36Sopenharmony_ci	if (!txgpio)
43962306a36Sopenharmony_ci		return -ENOMEM;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	raw_spin_lock_init(&txgpio->lock);
44262306a36Sopenharmony_ci	chip = &txgpio->chip;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	pci_set_drvdata(pdev, txgpio);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	err = pcim_enable_device(pdev);
44762306a36Sopenharmony_ci	if (err) {
44862306a36Sopenharmony_ci		dev_err(dev, "Failed to enable PCI device: err %d\n", err);
44962306a36Sopenharmony_ci		goto out;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	err = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
45362306a36Sopenharmony_ci	if (err) {
45462306a36Sopenharmony_ci		dev_err(dev, "Failed to iomap PCI device: err %d\n", err);
45562306a36Sopenharmony_ci		goto out;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	tbl = pcim_iomap_table(pdev);
45962306a36Sopenharmony_ci	txgpio->register_base = tbl[0];
46062306a36Sopenharmony_ci	if (!txgpio->register_base) {
46162306a36Sopenharmony_ci		dev_err(dev, "Cannot map PCI resource\n");
46262306a36Sopenharmony_ci		err = -ENOMEM;
46362306a36Sopenharmony_ci		goto out;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (pdev->subsystem_device == 0xa10a) {
46762306a36Sopenharmony_ci		/* CN88XX has no GPIO_CONST register*/
46862306a36Sopenharmony_ci		ngpio = 50;
46962306a36Sopenharmony_ci		txgpio->base_msi = 48;
47062306a36Sopenharmony_ci	} else {
47162306a36Sopenharmony_ci		u64 c = readq(txgpio->register_base + GPIO_CONST);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		ngpio = c & GPIO_CONST_GPIOS_MASK;
47462306a36Sopenharmony_ci		txgpio->base_msi = (c >> 8) & 0xff;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	txgpio->msix_entries = devm_kcalloc(dev,
47862306a36Sopenharmony_ci					    ngpio, sizeof(struct msix_entry),
47962306a36Sopenharmony_ci					    GFP_KERNEL);
48062306a36Sopenharmony_ci	if (!txgpio->msix_entries) {
48162306a36Sopenharmony_ci		err = -ENOMEM;
48262306a36Sopenharmony_ci		goto out;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	txgpio->line_entries = devm_kcalloc(dev,
48662306a36Sopenharmony_ci					    ngpio,
48762306a36Sopenharmony_ci					    sizeof(struct thunderx_line),
48862306a36Sopenharmony_ci					    GFP_KERNEL);
48962306a36Sopenharmony_ci	if (!txgpio->line_entries) {
49062306a36Sopenharmony_ci		err = -ENOMEM;
49162306a36Sopenharmony_ci		goto out;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	for (i = 0; i < ngpio; i++) {
49562306a36Sopenharmony_ci		u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(i));
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		txgpio->msix_entries[i].entry = txgpio->base_msi + (2 * i);
49862306a36Sopenharmony_ci		txgpio->line_entries[i].line = i;
49962306a36Sopenharmony_ci		txgpio->line_entries[i].txgpio = txgpio;
50062306a36Sopenharmony_ci		/*
50162306a36Sopenharmony_ci		 * If something has already programmed the pin, use
50262306a36Sopenharmony_ci		 * the existing glitch filter settings, otherwise go
50362306a36Sopenharmony_ci		 * to 400nS.
50462306a36Sopenharmony_ci		 */
50562306a36Sopenharmony_ci		txgpio->line_entries[i].fil_bits = bit_cfg ?
50662306a36Sopenharmony_ci			(bit_cfg & GPIO_BIT_CFG_FIL_MASK) : GLITCH_FILTER_400NS;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if ((bit_cfg & GPIO_BIT_CFG_TX_OE) && (bit_cfg & GPIO_BIT_CFG_TX_OD))
50962306a36Sopenharmony_ci			set_bit(i, txgpio->od_mask);
51062306a36Sopenharmony_ci		if (bit_cfg & GPIO_BIT_CFG_PIN_XOR)
51162306a36Sopenharmony_ci			set_bit(i, txgpio->invert_mask);
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Enable all MSI-X for interrupts on all possible lines. */
51662306a36Sopenharmony_ci	err = pci_enable_msix_range(pdev, txgpio->msix_entries, ngpio, ngpio);
51762306a36Sopenharmony_ci	if (err < 0)
51862306a36Sopenharmony_ci		goto out;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	chip->label = KBUILD_MODNAME;
52162306a36Sopenharmony_ci	chip->parent = dev;
52262306a36Sopenharmony_ci	chip->owner = THIS_MODULE;
52362306a36Sopenharmony_ci	chip->request = thunderx_gpio_request;
52462306a36Sopenharmony_ci	chip->base = -1; /* System allocated */
52562306a36Sopenharmony_ci	chip->can_sleep = false;
52662306a36Sopenharmony_ci	chip->ngpio = ngpio;
52762306a36Sopenharmony_ci	chip->get_direction = thunderx_gpio_get_direction;
52862306a36Sopenharmony_ci	chip->direction_input = thunderx_gpio_dir_in;
52962306a36Sopenharmony_ci	chip->get = thunderx_gpio_get;
53062306a36Sopenharmony_ci	chip->direction_output = thunderx_gpio_dir_out;
53162306a36Sopenharmony_ci	chip->set = thunderx_gpio_set;
53262306a36Sopenharmony_ci	chip->set_multiple = thunderx_gpio_set_multiple;
53362306a36Sopenharmony_ci	chip->set_config = thunderx_gpio_set_config;
53462306a36Sopenharmony_ci	girq = &chip->irq;
53562306a36Sopenharmony_ci	gpio_irq_chip_set_chip(girq, &thunderx_gpio_irq_chip);
53662306a36Sopenharmony_ci	girq->fwnode = of_node_to_fwnode(dev->of_node);
53762306a36Sopenharmony_ci	girq->parent_domain =
53862306a36Sopenharmony_ci		irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
53962306a36Sopenharmony_ci	girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
54062306a36Sopenharmony_ci	girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info;
54162306a36Sopenharmony_ci	girq->handler = handle_bad_irq;
54262306a36Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	err = devm_gpiochip_add_data(dev, chip, txgpio);
54562306a36Sopenharmony_ci	if (err)
54662306a36Sopenharmony_ci		goto out;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Push on irq_data and the domain for each line. */
54962306a36Sopenharmony_ci	for (i = 0; i < ngpio; i++) {
55062306a36Sopenharmony_ci		struct irq_fwspec fwspec;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		fwspec.fwnode = of_node_to_fwnode(dev->of_node);
55362306a36Sopenharmony_ci		fwspec.param_count = 2;
55462306a36Sopenharmony_ci		fwspec.param[0] = i;
55562306a36Sopenharmony_ci		fwspec.param[1] = IRQ_TYPE_NONE;
55662306a36Sopenharmony_ci		err = irq_domain_push_irq(girq->domain,
55762306a36Sopenharmony_ci					  txgpio->msix_entries[i].vector,
55862306a36Sopenharmony_ci					  &fwspec);
55962306a36Sopenharmony_ci		if (err < 0)
56062306a36Sopenharmony_ci			dev_err(dev, "irq_domain_push_irq: %d\n", err);
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
56462306a36Sopenharmony_ci		 ngpio, chip->base);
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ciout:
56762306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
56862306a36Sopenharmony_ci	return err;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic void thunderx_gpio_remove(struct pci_dev *pdev)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	int i;
57462306a36Sopenharmony_ci	struct thunderx_gpio *txgpio = pci_get_drvdata(pdev);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for (i = 0; i < txgpio->chip.ngpio; i++)
57762306a36Sopenharmony_ci		irq_domain_pop_irq(txgpio->chip.irq.domain,
57862306a36Sopenharmony_ci				   txgpio->msix_entries[i].vector);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	irq_domain_remove(txgpio->chip.irq.domain);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic const struct pci_device_id thunderx_gpio_id_table[] = {
58662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA00A) },
58762306a36Sopenharmony_ci	{ 0, }	/* end of table */
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_gpio_id_table);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic struct pci_driver thunderx_gpio_driver = {
59362306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
59462306a36Sopenharmony_ci	.id_table = thunderx_gpio_id_table,
59562306a36Sopenharmony_ci	.probe = thunderx_gpio_probe,
59662306a36Sopenharmony_ci	.remove = thunderx_gpio_remove,
59762306a36Sopenharmony_ci};
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cimodule_pci_driver(thunderx_gpio_driver);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Inc. ThunderX/OCTEON-TX GPIO Driver");
60262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
603