Commit | Line | Data |
---|---|---|
55059114 PM |
1 | /* |
2 | * arch/sh/boards/mach-x3proto/gpio.c | |
3 | * | |
4 | * Renesas SH-X3 Prototype Baseboard GPIO Support. | |
5 | * | |
b98b3581 | 6 | * Copyright (C) 2010 - 2012 Paul Mundt |
55059114 PM |
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> | |
b98b3581 | 20 | #include <linux/irqdomain.h> |
55059114 PM |
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 | ||
29 | static DEFINE_SPINLOCK(x3proto_gpio_lock); | |
b98b3581 | 30 | static struct irq_domain *x3proto_irq_domain; |
55059114 PM |
31 | |
32 | static 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 | ||
46 | static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) | |
47 | { | |
48 | return !!(__raw_readw(KEYDETR) & (1 << gpio)); | |
49 | } | |
50 | ||
51 | static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) | |
52 | { | |
b98b3581 PM |
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; | |
55059114 PM |
61 | } |
62 | ||
63 | static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | |
64 | { | |
79c98128 PM |
65 | struct irq_data *data = irq_get_irq_data(irq); |
66 | struct irq_chip *chip = irq_data_get_irq_chip(data); | |
55059114 PM |
67 | unsigned long mask; |
68 | int pin; | |
69 | ||
79c98128 | 70 | chip->irq_mask_ack(data); |
55059114 PM |
71 | |
72 | mask = __raw_readw(KEYDETR); | |
55059114 | 73 | for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) |
b98b3581 | 74 | generic_handle_irq(irq_linear_revmap(x3proto_irq_domain, pin)); |
55059114 | 75 | |
79c98128 | 76 | chip->irq_unmask(data); |
55059114 PM |
77 | } |
78 | ||
79 | struct 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 | ||
b98b3581 PM |
88 | static 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 | ||
97 | static struct irq_domain_ops x3proto_gpio_irq_ops = { | |
98 | .map = x3proto_gpio_irq_map, | |
99 | .xlate = irq_domain_xlate_twocell, | |
100 | }; | |
101 | ||
55059114 PM |
102 | int __init x3proto_gpio_setup(void) |
103 | { | |
b98b3581 | 104 | int ilsel, ret; |
55059114 PM |
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 | ||
b98b3581 PM |
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; | |
55059114 PM |
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 | ||
fcb8918f TG |
125 | irq_set_chained_handler(ilsel, x3proto_gpio_irq_handler); |
126 | irq_set_irq_wake(ilsel, 1); | |
55059114 PM |
127 | |
128 | return 0; | |
129 | ||
130 | err_irq: | |
55059114 PM |
131 | ret = gpiochip_remove(&x3proto_gpio_chip); |
132 | if (unlikely(ret)) | |
133 | pr_err("Failed deregistering GPIO\n"); | |
134 | ||
135 | err_gpio: | |
136 | synchronize_irq(ilsel); | |
137 | ||
138 | ilsel_disable(ILSEL_KEY); | |
139 | ||
140 | return ret; | |
141 | } |