Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9af4d80b FV |
2 | /* |
3 | * GPIO controller in LSI ZEVIO SoCs. | |
4 | * | |
5 | * Author: Fabian Vogt <fabian@ritter-vogt.de> | |
9af4d80b FV |
6 | */ |
7 | ||
8 | #include <linux/spinlock.h> | |
9 | #include <linux/errno.h> | |
a90295b4 | 10 | #include <linux/init.h> |
9af4d80b FV |
11 | #include <linux/bitops.h> |
12 | #include <linux/io.h> | |
13 | #include <linux/of_device.h> | |
14 | #include <linux/of_gpio.h> | |
15 | #include <linux/slab.h> | |
f0916167 | 16 | #include <linux/gpio/driver.h> |
9af4d80b FV |
17 | |
18 | /* | |
19 | * Memory layout: | |
20 | * This chip has four gpio sections, each controls 8 GPIOs. | |
21 | * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10. | |
22 | * Disclaimer: Reverse engineered! | |
23 | * For more information refer to: | |
24 | * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29 | |
25 | * | |
26 | * 0x00-0x3F: Section 0 | |
27 | * +0x00: Masked interrupt status (read-only) | |
28 | * +0x04: R: Interrupt status W: Reset interrupt status | |
29 | * +0x08: R: Interrupt mask W: Mask interrupt | |
30 | * +0x0C: W: Unmask interrupt (write-only) | |
31 | * +0x10: Direction: I/O=1/0 | |
32 | * +0x14: Output | |
33 | * +0x18: Input (read-only) | |
34 | * +0x20: R: Level interrupt W: Set as level interrupt | |
35 | * 0x40-0x7F: Section 1 | |
36 | * 0x80-0xBF: Section 2 | |
37 | * 0xC0-0xFF: Section 3 | |
38 | */ | |
39 | ||
40 | #define ZEVIO_GPIO_SECTION_SIZE 0x40 | |
41 | ||
42 | /* Offsets to various registers */ | |
43 | #define ZEVIO_GPIO_INT_MASKED_STATUS 0x00 | |
44 | #define ZEVIO_GPIO_INT_STATUS 0x04 | |
45 | #define ZEVIO_GPIO_INT_UNMASK 0x08 | |
46 | #define ZEVIO_GPIO_INT_MASK 0x0C | |
47 | #define ZEVIO_GPIO_DIRECTION 0x10 | |
48 | #define ZEVIO_GPIO_OUTPUT 0x14 | |
49 | #define ZEVIO_GPIO_INPUT 0x18 | |
50 | #define ZEVIO_GPIO_INT_STICKY 0x20 | |
51 | ||
9af4d80b FV |
52 | /* Bit number of GPIO in its section */ |
53 | #define ZEVIO_GPIO_BIT(gpio) (gpio&7) | |
54 | ||
55 | struct zevio_gpio { | |
56 | spinlock_t lock; | |
57 | struct of_mm_gpio_chip chip; | |
58 | }; | |
59 | ||
60 | static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, | |
61 | unsigned port_offset) | |
62 | { | |
63 | unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; | |
64 | return readl(IOMEM(c->chip.regs + section_offset + port_offset)); | |
65 | } | |
66 | ||
67 | static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, | |
68 | unsigned port_offset, u32 val) | |
69 | { | |
70 | unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; | |
71 | writel(val, IOMEM(c->chip.regs + section_offset + port_offset)); | |
72 | } | |
73 | ||
74 | /* Functions for struct gpio_chip */ | |
75 | static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) | |
76 | { | |
9a3ad668 | 77 | struct zevio_gpio *controller = gpiochip_get_data(chip); |
ef7de262 | 78 | u32 val, dir; |
9af4d80b | 79 | |
ef7de262 AL |
80 | spin_lock(&controller->lock); |
81 | dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
82 | if (dir & BIT(ZEVIO_GPIO_BIT(pin))) | |
83 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT); | |
84 | else | |
85 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
86 | spin_unlock(&controller->lock); | |
9af4d80b FV |
87 | |
88 | return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; | |
89 | } | |
90 | ||
91 | static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) | |
92 | { | |
9a3ad668 | 93 | struct zevio_gpio *controller = gpiochip_get_data(chip); |
9af4d80b FV |
94 | u32 val; |
95 | ||
96 | spin_lock(&controller->lock); | |
97 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
98 | if (value) | |
99 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
100 | else | |
101 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
102 | ||
103 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); | |
104 | spin_unlock(&controller->lock); | |
105 | } | |
106 | ||
107 | static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) | |
108 | { | |
9a3ad668 | 109 | struct zevio_gpio *controller = gpiochip_get_data(chip); |
9af4d80b FV |
110 | u32 val; |
111 | ||
112 | spin_lock(&controller->lock); | |
113 | ||
114 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
115 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
116 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); | |
117 | ||
118 | spin_unlock(&controller->lock); | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
123 | static int zevio_gpio_direction_output(struct gpio_chip *chip, | |
124 | unsigned pin, int value) | |
125 | { | |
9a3ad668 | 126 | struct zevio_gpio *controller = gpiochip_get_data(chip); |
9af4d80b FV |
127 | u32 val; |
128 | ||
129 | spin_lock(&controller->lock); | |
130 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
131 | if (value) | |
132 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
133 | else | |
134 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
135 | ||
136 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); | |
137 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
138 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
139 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); | |
140 | ||
141 | spin_unlock(&controller->lock); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) | |
147 | { | |
148 | /* | |
149 | * TODO: Implement IRQs. | |
150 | * Not implemented yet due to weird lockups | |
151 | */ | |
152 | ||
153 | return -ENXIO; | |
154 | } | |
155 | ||
4a14af45 | 156 | static const struct gpio_chip zevio_gpio_chip = { |
9af4d80b FV |
157 | .direction_input = zevio_gpio_direction_input, |
158 | .direction_output = zevio_gpio_direction_output, | |
159 | .set = zevio_gpio_set, | |
160 | .get = zevio_gpio_get, | |
161 | .to_irq = zevio_gpio_to_irq, | |
162 | .base = 0, | |
163 | .owner = THIS_MODULE, | |
164 | .ngpio = 32, | |
165 | .of_gpio_n_cells = 2, | |
166 | }; | |
167 | ||
168 | /* Initialization */ | |
169 | static int zevio_gpio_probe(struct platform_device *pdev) | |
170 | { | |
171 | struct zevio_gpio *controller; | |
172 | int status, i; | |
173 | ||
174 | controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); | |
50908d61 | 175 | if (!controller) |
9af4d80b | 176 | return -ENOMEM; |
9af4d80b | 177 | |
ff00be69 RRD |
178 | platform_set_drvdata(pdev, controller); |
179 | ||
9af4d80b FV |
180 | /* Copy our reference */ |
181 | controller->chip.gc = zevio_gpio_chip; | |
58383c78 | 182 | controller->chip.gc.parent = &pdev->dev; |
9af4d80b | 183 | |
9a3ad668 LW |
184 | status = of_mm_gpiochip_add_data(pdev->dev.of_node, |
185 | &(controller->chip), | |
186 | controller); | |
9af4d80b FV |
187 | if (status) { |
188 | dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status); | |
189 | return status; | |
190 | } | |
191 | ||
192 | spin_lock_init(&controller->lock); | |
193 | ||
194 | /* Disable interrupts, they only cause errors */ | |
195 | for (i = 0; i < controller->chip.gc.ngpio; i += 8) | |
196 | zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF); | |
197 | ||
58383c78 | 198 | dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!\n"); |
9af4d80b FV |
199 | |
200 | return 0; | |
201 | } | |
202 | ||
a49f2e74 | 203 | static const struct of_device_id zevio_gpio_of_match[] = { |
9af4d80b FV |
204 | { .compatible = "lsi,zevio-gpio", }, |
205 | { }, | |
206 | }; | |
207 | ||
9af4d80b FV |
208 | static struct platform_driver zevio_gpio_driver = { |
209 | .driver = { | |
210 | .name = "gpio-zevio", | |
9ea8d810 | 211 | .of_match_table = zevio_gpio_of_match, |
a90295b4 | 212 | .suppress_bind_attrs = true, |
9af4d80b FV |
213 | }, |
214 | .probe = zevio_gpio_probe, | |
215 | }; | |
a90295b4 | 216 | builtin_platform_driver(zevio_gpio_driver); |