Commit | Line | Data |
---|---|---|
55059114 PM |
1 | /* |
2 | * arch/sh/boards/mach-x3proto/gpio.c | |
3 | * | |
4 | * Renesas SH-X3 Prototype Baseboard GPIO Support. | |
5 | * | |
6 | * Copyright (C) 2010 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/io.h> | |
21 | #include <mach/ilsel.h> | |
22 | #include <mach/hardware.h> | |
23 | ||
24 | #define KEYCTLR 0xb81c0000 | |
25 | #define KEYOUTR 0xb81c0002 | |
26 | #define KEYDETR 0xb81c0004 | |
27 | ||
28 | static DEFINE_SPINLOCK(x3proto_gpio_lock); | |
29 | static unsigned int x3proto_gpio_irq_map[NR_BASEBOARD_GPIOS] = { 0, }; | |
30 | ||
31 | static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | |
32 | { | |
33 | unsigned long flags; | |
34 | unsigned int data; | |
35 | ||
36 | spin_lock_irqsave(&x3proto_gpio_lock, flags); | |
37 | data = __raw_readw(KEYCTLR); | |
38 | data |= (1 << gpio); | |
39 | __raw_writew(data, KEYCTLR); | |
40 | spin_unlock_irqrestore(&x3proto_gpio_lock, flags); | |
41 | ||
42 | return 0; | |
43 | } | |
44 | ||
45 | static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) | |
46 | { | |
47 | return !!(__raw_readw(KEYDETR) & (1 << gpio)); | |
48 | } | |
49 | ||
50 | static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) | |
51 | { | |
52 | return x3proto_gpio_irq_map[gpio]; | |
53 | } | |
54 | ||
55 | static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) | |
56 | { | |
79c98128 PM |
57 | struct irq_data *data = irq_get_irq_data(irq); |
58 | struct irq_chip *chip = irq_data_get_irq_chip(data); | |
55059114 PM |
59 | unsigned long mask; |
60 | int pin; | |
61 | ||
79c98128 | 62 | chip->irq_mask_ack(data); |
55059114 PM |
63 | |
64 | mask = __raw_readw(KEYDETR); | |
65 | ||
66 | for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) | |
67 | generic_handle_irq(x3proto_gpio_to_irq(NULL, pin)); | |
68 | ||
79c98128 | 69 | chip->irq_unmask(data); |
55059114 PM |
70 | } |
71 | ||
72 | struct gpio_chip x3proto_gpio_chip = { | |
73 | .label = "x3proto-gpio", | |
74 | .direction_input = x3proto_gpio_direction_input, | |
75 | .get = x3proto_gpio_get, | |
76 | .to_irq = x3proto_gpio_to_irq, | |
77 | .base = -1, | |
78 | .ngpio = NR_BASEBOARD_GPIOS, | |
79 | }; | |
80 | ||
81 | int __init x3proto_gpio_setup(void) | |
82 | { | |
5a30d7bf | 83 | int ilsel; |
55059114 PM |
84 | int ret, i; |
85 | ||
86 | ilsel = ilsel_enable(ILSEL_KEY); | |
87 | if (unlikely(ilsel < 0)) | |
88 | return ilsel; | |
89 | ||
90 | ret = gpiochip_add(&x3proto_gpio_chip); | |
91 | if (unlikely(ret)) | |
92 | goto err_gpio; | |
93 | ||
94 | for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { | |
95 | unsigned long flags; | |
5a30d7bf | 96 | int irq = create_irq(); |
55059114 PM |
97 | |
98 | if (unlikely(irq < 0)) { | |
99 | ret = -EINVAL; | |
100 | goto err_irq; | |
101 | } | |
102 | ||
103 | spin_lock_irqsave(&x3proto_gpio_lock, flags); | |
104 | x3proto_gpio_irq_map[i] = irq; | |
fcb8918f TG |
105 | irq_set_chip_and_handler_name(irq, &dummy_irq_chip, |
106 | handle_simple_irq, "gpio"); | |
55059114 PM |
107 | spin_unlock_irqrestore(&x3proto_gpio_lock, flags); |
108 | } | |
109 | ||
110 | pr_info("registering '%s' support, handling GPIOs %u -> %u, " | |
111 | "bound to IRQ %u\n", | |
112 | x3proto_gpio_chip.label, x3proto_gpio_chip.base, | |
113 | x3proto_gpio_chip.base + x3proto_gpio_chip.ngpio, | |
114 | ilsel); | |
115 | ||
fcb8918f TG |
116 | irq_set_chained_handler(ilsel, x3proto_gpio_irq_handler); |
117 | irq_set_irq_wake(ilsel, 1); | |
55059114 PM |
118 | |
119 | return 0; | |
120 | ||
121 | err_irq: | |
122 | for (; i >= 0; --i) | |
123 | if (x3proto_gpio_irq_map[i]) | |
124 | destroy_irq(x3proto_gpio_irq_map[i]); | |
125 | ||
126 | ret = gpiochip_remove(&x3proto_gpio_chip); | |
127 | if (unlikely(ret)) | |
128 | pr_err("Failed deregistering GPIO\n"); | |
129 | ||
130 | err_gpio: | |
131 | synchronize_irq(ilsel); | |
132 | ||
133 | ilsel_disable(ILSEL_KEY); | |
134 | ||
135 | return ret; | |
136 | } |