162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Amstrad E3 FIQ handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2009 Janusz Krzysztofik
662306a36Sopenharmony_ci *  Copyright (c) 2006 Matt Callow
762306a36Sopenharmony_ci *  Copyright (c) 2004 Amstrad Plc
862306a36Sopenharmony_ci *  Copyright (C) 2001 RidgeRun, Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Parts of this code are taken from linux/arch/arm/mach-omap/irq.c
1162306a36Sopenharmony_ci * in the MontaVista 2.4 kernel (and the Amstrad changes therein)
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1462306a36Sopenharmony_ci#include <linux/gpio/machine.h>
1562306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/platform_data/ams-delta-fiq.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/fiq.h>
2462306a36Sopenharmony_ci#include <linux/soc/ti/omap1-io.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "hardware.h"
2762306a36Sopenharmony_ci#include "ams-delta-fiq.h"
2862306a36Sopenharmony_ci#include "board-ams-delta.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic struct fiq_handler fh = {
3162306a36Sopenharmony_ci	.name	= "ams-delta-fiq"
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * This buffer is shared between FIQ and IRQ contexts.
3662306a36Sopenharmony_ci * The FIQ and IRQ isrs can both read and write it.
3762306a36Sopenharmony_ci * It is structured as a header section several 32bit slots,
3862306a36Sopenharmony_ci * followed by the circular buffer where the FIQ isr stores
3962306a36Sopenharmony_ci * keystrokes received from the qwerty keyboard.  See
4062306a36Sopenharmony_ci * <linux/platform_data/ams-delta-fiq.h> for details of offsets.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistatic unsigned int fiq_buffer[1024];
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct irq_chip *irq_chip;
4562306a36Sopenharmony_cistatic struct irq_data *irq_data[16];
4662306a36Sopenharmony_cistatic unsigned int irq_counter[16];
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const char *pin_name[16] __initconst = {
4962306a36Sopenharmony_ci	[AMS_DELTA_GPIO_PIN_KEYBRD_DATA]	= "keybrd_data",
5062306a36Sopenharmony_ci	[AMS_DELTA_GPIO_PIN_KEYBRD_CLK]		= "keybrd_clk",
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic irqreturn_t deferred_fiq(int irq, void *dev_id)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct irq_data *d;
5662306a36Sopenharmony_ci	int gpio, irq_num, fiq_count;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/*
5962306a36Sopenharmony_ci	 * For each handled GPIO interrupt, keep calling its interrupt handler
6062306a36Sopenharmony_ci	 * until the IRQ counter catches the FIQ incremented interrupt counter.
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	for (gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK;
6362306a36Sopenharmony_ci			gpio <= AMS_DELTA_GPIO_PIN_HOOK_SWITCH; gpio++) {
6462306a36Sopenharmony_ci		d = irq_data[gpio];
6562306a36Sopenharmony_ci		irq_num = d->irq;
6662306a36Sopenharmony_ci		fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio];
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		if (irq_counter[gpio] < fiq_count &&
6962306a36Sopenharmony_ci				gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) {
7062306a36Sopenharmony_ci			/*
7162306a36Sopenharmony_ci			 * handle_simple_irq() that OMAP GPIO edge
7262306a36Sopenharmony_ci			 * interrupts default to since commit 80ac93c27441
7362306a36Sopenharmony_ci			 * requires interrupt already acked and unmasked.
7462306a36Sopenharmony_ci			 */
7562306a36Sopenharmony_ci			if (!WARN_ON_ONCE(!irq_chip->irq_unmask))
7662306a36Sopenharmony_ci				irq_chip->irq_unmask(d);
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci		for (; irq_counter[gpio] < fiq_count; irq_counter[gpio]++)
7962306a36Sopenharmony_ci			generic_handle_irq(irq_num);
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	return IRQ_HANDLED;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_civoid __init ams_delta_init_fiq(struct gpio_chip *chip,
8562306a36Sopenharmony_ci			       struct platform_device *serio)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct gpio_desc *gpiod, *data = NULL, *clk = NULL;
8862306a36Sopenharmony_ci	void *fiqhandler_start;
8962306a36Sopenharmony_ci	unsigned int fiqhandler_length;
9062306a36Sopenharmony_ci	struct pt_regs FIQ_regs;
9162306a36Sopenharmony_ci	unsigned long val, offset;
9262306a36Sopenharmony_ci	int i, retval;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Store irq_chip location for IRQ handler use */
9562306a36Sopenharmony_ci	irq_chip = chip->irq.chip;
9662306a36Sopenharmony_ci	if (!irq_chip) {
9762306a36Sopenharmony_ci		pr_err("%s: GPIO chip %s is missing IRQ function\n", __func__,
9862306a36Sopenharmony_ci		       chip->label);
9962306a36Sopenharmony_ci		return;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(irq_data); i++) {
10362306a36Sopenharmony_ci		gpiod = gpiochip_request_own_desc(chip, i, pin_name[i],
10462306a36Sopenharmony_ci						  GPIO_ACTIVE_HIGH, GPIOD_IN);
10562306a36Sopenharmony_ci		if (IS_ERR(gpiod)) {
10662306a36Sopenharmony_ci			pr_err("%s: failed to get GPIO pin %d (%ld)\n",
10762306a36Sopenharmony_ci			       __func__, i, PTR_ERR(gpiod));
10862306a36Sopenharmony_ci			return;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci		/* Store irq_data location for IRQ handler use */
11162306a36Sopenharmony_ci		irq_data[i] = irq_get_irq_data(gpiod_to_irq(gpiod));
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		/*
11462306a36Sopenharmony_ci		 * FIQ handler takes full control over serio data and clk GPIO
11562306a36Sopenharmony_ci		 * pins.  Initialize them and keep requested so nobody can
11662306a36Sopenharmony_ci		 * interfere.  Fail if any of those two couldn't be requested.
11762306a36Sopenharmony_ci		 */
11862306a36Sopenharmony_ci		switch (i) {
11962306a36Sopenharmony_ci		case AMS_DELTA_GPIO_PIN_KEYBRD_DATA:
12062306a36Sopenharmony_ci			data = gpiod;
12162306a36Sopenharmony_ci			gpiod_direction_input(data);
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		case AMS_DELTA_GPIO_PIN_KEYBRD_CLK:
12462306a36Sopenharmony_ci			clk = gpiod;
12562306a36Sopenharmony_ci			gpiod_direction_input(clk);
12662306a36Sopenharmony_ci			break;
12762306a36Sopenharmony_ci		default:
12862306a36Sopenharmony_ci			gpiochip_free_own_desc(gpiod);
12962306a36Sopenharmony_ci			break;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	if (!data || !clk)
13362306a36Sopenharmony_ci		goto out_gpio;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	fiqhandler_start = &qwerty_fiqin_start;
13662306a36Sopenharmony_ci	fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start;
13762306a36Sopenharmony_ci	pr_info("Installing fiq handler from %p, length 0x%x\n",
13862306a36Sopenharmony_ci			fiqhandler_start, fiqhandler_length);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	retval = claim_fiq(&fh);
14162306a36Sopenharmony_ci	if (retval) {
14262306a36Sopenharmony_ci		pr_err("ams_delta_init_fiq(): couldn't claim FIQ, ret=%d\n",
14362306a36Sopenharmony_ci				retval);
14462306a36Sopenharmony_ci		goto out_gpio;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	retval = request_irq(INT_DEFERRED_FIQ, deferred_fiq,
14862306a36Sopenharmony_ci			IRQ_TYPE_EDGE_RISING, "deferred_fiq", NULL);
14962306a36Sopenharmony_ci	if (retval < 0) {
15062306a36Sopenharmony_ci		pr_err("Failed to get deferred_fiq IRQ, ret=%d\n", retval);
15162306a36Sopenharmony_ci		release_fiq(&fh);
15262306a36Sopenharmony_ci		goto out_gpio;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci	/*
15562306a36Sopenharmony_ci	 * Since no set_type() method is provided by OMAP irq chip,
15662306a36Sopenharmony_ci	 * switch to edge triggered interrupt type manually.
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	offset = IRQ_ILR0_REG_OFFSET +
15962306a36Sopenharmony_ci			((INT_DEFERRED_FIQ - NR_IRQS_LEGACY) & 0x1f) * 0x4;
16062306a36Sopenharmony_ci	val = omap_readl(DEFERRED_FIQ_IH_BASE + offset) & ~(1 << 1);
16162306a36Sopenharmony_ci	omap_writel(val, DEFERRED_FIQ_IH_BASE + offset);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	set_fiq_handler(fiqhandler_start, fiqhandler_length);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/*
16662306a36Sopenharmony_ci	 * Initialise the buffer which is shared
16762306a36Sopenharmony_ci	 * between FIQ mode and IRQ mode
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	fiq_buffer[FIQ_GPIO_INT_MASK]	= 0;
17062306a36Sopenharmony_ci	fiq_buffer[FIQ_MASK]		= 0;
17162306a36Sopenharmony_ci	fiq_buffer[FIQ_STATE]		= 0;
17262306a36Sopenharmony_ci	fiq_buffer[FIQ_KEY]		= 0;
17362306a36Sopenharmony_ci	fiq_buffer[FIQ_KEYS_CNT]	= 0;
17462306a36Sopenharmony_ci	fiq_buffer[FIQ_KEYS_HICNT]	= 0;
17562306a36Sopenharmony_ci	fiq_buffer[FIQ_TAIL_OFFSET]	= 0;
17662306a36Sopenharmony_ci	fiq_buffer[FIQ_HEAD_OFFSET]	= 0;
17762306a36Sopenharmony_ci	fiq_buffer[FIQ_BUF_LEN]		= 256;
17862306a36Sopenharmony_ci	fiq_buffer[FIQ_MISSED_KEYS]	= 0;
17962306a36Sopenharmony_ci	fiq_buffer[FIQ_BUFFER_START]	=
18062306a36Sopenharmony_ci			(unsigned int) &fiq_buffer[FIQ_CIRC_BUFF];
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = FIQ_CNT_INT_00; i <= FIQ_CNT_INT_15; i++)
18362306a36Sopenharmony_ci		fiq_buffer[i] = 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * FIQ mode r9 always points to the fiq_buffer, because the FIQ isr
18762306a36Sopenharmony_ci	 * will run in an unpredictable context. The fiq_buffer is the FIQ isr's
18862306a36Sopenharmony_ci	 * only means of communication with the IRQ level and other kernel
18962306a36Sopenharmony_ci	 * context code.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	FIQ_regs.ARM_r9 = (unsigned int)fiq_buffer;
19262306a36Sopenharmony_ci	set_fiq_regs(&FIQ_regs);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	pr_info("request_fiq(): fiq_buffer = %p\n", fiq_buffer);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/*
19762306a36Sopenharmony_ci	 * Redirect GPIO interrupts to FIQ
19862306a36Sopenharmony_ci	 */
19962306a36Sopenharmony_ci	offset = IRQ_ILR0_REG_OFFSET + (INT_GPIO_BANK1 - NR_IRQS_LEGACY) * 0x4;
20062306a36Sopenharmony_ci	val = omap_readl(OMAP_IH1_BASE + offset) | 1;
20162306a36Sopenharmony_ci	omap_writel(val, OMAP_IH1_BASE + offset);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Initialize serio device IRQ resource and platform_data */
20462306a36Sopenharmony_ci	serio->resource[0].start = gpiod_to_irq(clk);
20562306a36Sopenharmony_ci	serio->resource[0].end = serio->resource[0].start;
20662306a36Sopenharmony_ci	serio->dev.platform_data = fiq_buffer;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * Since FIQ handler performs handling of GPIO registers for
21062306a36Sopenharmony_ci	 * "keybrd_clk" IRQ pin, ams_delta_serio driver used to set
21162306a36Sopenharmony_ci	 * handle_simple_irq() as active IRQ handler for that pin to avoid
21262306a36Sopenharmony_ci	 * bad interaction with gpio-omap driver.  This is no longer needed
21362306a36Sopenharmony_ci	 * as handle_simple_irq() is now the default handler for OMAP GPIO
21462306a36Sopenharmony_ci	 * edge interrupts.
21562306a36Sopenharmony_ci	 * This comment replaces the obsolete code which has been removed
21662306a36Sopenharmony_ci	 * from the ams_delta_serio driver and stands here only as a reminder
21762306a36Sopenharmony_ci	 * of that dependency on gpio-omap driver behavior.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ciout_gpio:
22362306a36Sopenharmony_ci	if (data)
22462306a36Sopenharmony_ci		gpiochip_free_own_desc(data);
22562306a36Sopenharmony_ci	if (clk)
22662306a36Sopenharmony_ci		gpiochip_free_own_desc(clk);
22762306a36Sopenharmony_ci}
228