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