18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/sh/boards/mach-x3proto/gpio.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Renesas SH-X3 Prototype Baseboard GPIO Support. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2012 Paul Mundt 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <mach/ilsel.h> 208c2ecf20Sopenharmony_ci#include <mach/hardware.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define KEYCTLR 0xb81c0000 238c2ecf20Sopenharmony_ci#define KEYOUTR 0xb81c0002 248c2ecf20Sopenharmony_ci#define KEYDETR 0xb81c0004 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(x3proto_gpio_lock); 278c2ecf20Sopenharmony_cistatic struct irq_domain *x3proto_irq_domain; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci unsigned long flags; 328c2ecf20Sopenharmony_ci unsigned int data; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci spin_lock_irqsave(&x3proto_gpio_lock, flags); 358c2ecf20Sopenharmony_ci data = __raw_readw(KEYCTLR); 368c2ecf20Sopenharmony_ci data |= (1 << gpio); 378c2ecf20Sopenharmony_ci __raw_writew(data, KEYCTLR); 388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&x3proto_gpio_lock, flags); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return !!(__raw_readw(KEYDETR) & (1 << gpio)); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int virq; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (gpio < chip->ngpio) 538c2ecf20Sopenharmony_ci virq = irq_create_mapping(x3proto_irq_domain, gpio); 548c2ecf20Sopenharmony_ci else 558c2ecf20Sopenharmony_ci virq = -ENXIO; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return virq; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void x3proto_gpio_irq_handler(struct irq_desc *desc) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct irq_data *data = irq_desc_get_irq_data(desc); 638c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_data_get_irq_chip(data); 648c2ecf20Sopenharmony_ci unsigned long mask; 658c2ecf20Sopenharmony_ci int pin; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci chip->irq_mask_ack(data); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci mask = __raw_readw(KEYDETR); 708c2ecf20Sopenharmony_ci for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) 718c2ecf20Sopenharmony_ci generic_handle_irq(irq_linear_revmap(x3proto_irq_domain, pin)); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci chip->irq_unmask(data); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct gpio_chip x3proto_gpio_chip = { 778c2ecf20Sopenharmony_ci .label = "x3proto-gpio", 788c2ecf20Sopenharmony_ci .direction_input = x3proto_gpio_direction_input, 798c2ecf20Sopenharmony_ci .get = x3proto_gpio_get, 808c2ecf20Sopenharmony_ci .to_irq = x3proto_gpio_to_irq, 818c2ecf20Sopenharmony_ci .base = -1, 828c2ecf20Sopenharmony_ci .ngpio = NR_BASEBOARD_GPIOS, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int x3proto_gpio_irq_map(struct irq_domain *domain, unsigned int virq, 868c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci irq_set_chip_and_handler_name(virq, &dummy_irq_chip, handle_simple_irq, 898c2ecf20Sopenharmony_ci "gpio"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct irq_domain_ops x3proto_gpio_irq_ops = { 958c2ecf20Sopenharmony_ci .map = x3proto_gpio_irq_map, 968c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciint __init x3proto_gpio_setup(void) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int ilsel, ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ilsel = ilsel_enable(ILSEL_KEY); 1048c2ecf20Sopenharmony_ci if (unlikely(ilsel < 0)) 1058c2ecf20Sopenharmony_ci return ilsel; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&x3proto_gpio_chip, NULL); 1088c2ecf20Sopenharmony_ci if (unlikely(ret)) 1098c2ecf20Sopenharmony_ci goto err_gpio; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci x3proto_irq_domain = irq_domain_add_linear(NULL, NR_BASEBOARD_GPIOS, 1128c2ecf20Sopenharmony_ci &x3proto_gpio_irq_ops, NULL); 1138c2ecf20Sopenharmony_ci if (unlikely(!x3proto_irq_domain)) 1148c2ecf20Sopenharmony_ci goto err_irq; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pr_info("registering '%s' support, handling GPIOs %u -> %u, " 1178c2ecf20Sopenharmony_ci "bound to IRQ %u\n", 1188c2ecf20Sopenharmony_ci x3proto_gpio_chip.label, x3proto_gpio_chip.base, 1198c2ecf20Sopenharmony_ci x3proto_gpio_chip.base + x3proto_gpio_chip.ngpio, 1208c2ecf20Sopenharmony_ci ilsel); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci irq_set_chained_handler(ilsel, x3proto_gpio_irq_handler); 1238c2ecf20Sopenharmony_ci irq_set_irq_wake(ilsel, 1); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cierr_irq: 1288c2ecf20Sopenharmony_ci gpiochip_remove(&x3proto_gpio_chip); 1298c2ecf20Sopenharmony_ci ret = 0; 1308c2ecf20Sopenharmony_cierr_gpio: 1318c2ecf20Sopenharmony_ci synchronize_irq(ilsel); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ilsel_disable(ILSEL_KEY); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci} 137