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