1/* 2 * arch/sh/boards/mach-x3proto/gpio.c 3 * 4 * Renesas SH-X3 Prototype Baseboard GPIO Support. 5 * 6 * Copyright (C) 2010 - 2012 Paul Mundt 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file "COPYING" in the main directory of this archive 10 * for more details. 11 */ 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14#include <linux/init.h> 15#include <linux/interrupt.h> 16#include <linux/gpio.h> 17#include <linux/irq.h> 18#include <linux/kernel.h> 19#include <linux/spinlock.h> 20#include <linux/irqdomain.h> 21#include <linux/io.h> 22#include <mach/ilsel.h> 23#include <mach/hardware.h> 24 25#define KEYCTLR 0xb81c0000 26#define KEYOUTR 0xb81c0002 27#define KEYDETR 0xb81c0004 28 29static DEFINE_SPINLOCK(x3proto_gpio_lock); 30static struct irq_domain *x3proto_irq_domain; 31 32static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 33{ 34 unsigned long flags; 35 unsigned int data; 36 37 spin_lock_irqsave(&x3proto_gpio_lock, flags); 38 data = __raw_readw(KEYCTLR); 39 data |= (1 << gpio); 40 __raw_writew(data, KEYCTLR); 41 spin_unlock_irqrestore(&x3proto_gpio_lock, flags); 42 43 return 0; 44} 45 46static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) 47{ 48 return !!(__raw_readw(KEYDETR) & (1 << gpio)); 49} 50 51static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) 52{ 53 int virq; 54 55 if (gpio < chip->ngpio) 56 virq = irq_create_mapping(x3proto_irq_domain, gpio); 57 else 58 virq = -ENXIO; 59 60 return virq; 61} 62 63static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) 64{ 65 struct irq_data *data = irq_get_irq_data(irq); 66 struct irq_chip *chip = irq_data_get_irq_chip(data); 67 unsigned long mask; 68 int pin; 69 70 chip->irq_mask_ack(data); 71 72 mask = __raw_readw(KEYDETR); 73 for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) 74 generic_handle_irq(irq_linear_revmap(x3proto_irq_domain, pin)); 75 76 chip->irq_unmask(data); 77} 78 79struct gpio_chip x3proto_gpio_chip = { 80 .label = "x3proto-gpio", 81 .direction_input = x3proto_gpio_direction_input, 82 .get = x3proto_gpio_get, 83 .to_irq = x3proto_gpio_to_irq, 84 .base = -1, 85 .ngpio = NR_BASEBOARD_GPIOS, 86}; 87 88static int x3proto_gpio_irq_map(struct irq_domain *domain, unsigned int virq, 89 irq_hw_number_t hwirq) 90{ 91 irq_set_chip_and_handler_name(virq, &dummy_irq_chip, handle_simple_irq, 92 "gpio"); 93 94 return 0; 95} 96 97static struct irq_domain_ops x3proto_gpio_irq_ops = { 98 .map = x3proto_gpio_irq_map, 99 .xlate = irq_domain_xlate_twocell, 100}; 101 102int __init x3proto_gpio_setup(void) 103{ 104 int ilsel, ret; 105 106 ilsel = ilsel_enable(ILSEL_KEY); 107 if (unlikely(ilsel < 0)) 108 return ilsel; 109 110 ret = gpiochip_add(&x3proto_gpio_chip); 111 if (unlikely(ret)) 112 goto err_gpio; 113 114 x3proto_irq_domain = irq_domain_add_linear(NULL, NR_BASEBOARD_GPIOS, 115 &x3proto_gpio_irq_ops, NULL); 116 if (unlikely(!x3proto_irq_domain)) 117 goto err_irq; 118 119 pr_info("registering '%s' support, handling GPIOs %u -> %u, " 120 "bound to IRQ %u\n", 121 x3proto_gpio_chip.label, x3proto_gpio_chip.base, 122 x3proto_gpio_chip.base + x3proto_gpio_chip.ngpio, 123 ilsel); 124 125 irq_set_chained_handler(ilsel, x3proto_gpio_irq_handler); 126 irq_set_irq_wake(ilsel, 1); 127 128 return 0; 129 130err_irq: 131 gpiochip_remove(&x3proto_gpio_chip); 132 ret = 0; 133err_gpio: 134 synchronize_irq(ilsel); 135 136 ilsel_disable(ILSEL_KEY); 137 138 return ret; 139} 140