Commit | Line | Data |
---|---|---|
cb0e9a7b | 1 | // SPDX-License-Identifier: GPL-2.0 |
be9b06b2 | 2 | /* |
c103de24 | 3 | * GPIO interface for Intel Poulsbo SCH |
be9b06b2 DT |
4 | * |
5 | * Copyright (c) 2010 CompuLab Ltd | |
6 | * Author: Denis Turischev <denis@compulab.co.il> | |
be9b06b2 DT |
7 | */ |
8 | ||
47091b05 | 9 | #include <linux/acpi.h> |
fdc1f5df | 10 | #include <linux/bitops.h> |
47091b05 AS |
11 | #include <linux/errno.h> |
12 | #include <linux/gpio/driver.h> | |
13 | #include <linux/io.h> | |
7a816384 | 14 | #include <linux/irq.h> |
be9b06b2 DT |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> | |
f04ddfcd | 17 | #include <linux/pci_ids.h> |
47091b05 | 18 | #include <linux/platform_device.h> |
7a816384 | 19 | #include <linux/types.h> |
be9b06b2 | 20 | |
c479ff09 MW |
21 | #define GEN 0x00 |
22 | #define GIO 0x04 | |
23 | #define GLV 0x08 | |
7a816384 JK |
24 | #define GTPE 0x0c |
25 | #define GTNE 0x10 | |
26 | #define GGPE 0x14 | |
27 | #define GSMI 0x18 | |
28 | #define GTS 0x1c | |
29 | ||
30 | #define CORE_BANK_OFFSET 0x00 | |
31 | #define RESUME_BANK_OFFSET 0x20 | |
c479ff09 | 32 | |
fdc1f5df AS |
33 | /* |
34 | * iLB datasheet describes GPE0BLK registers, in particular GPE0E.GPIO bit. | |
35 | * Document Number: 328195-001 | |
36 | */ | |
37 | #define GPE0E_GPIO 14 | |
38 | ||
c479ff09 MW |
39 | struct sch_gpio { |
40 | struct gpio_chip chip; | |
abaed898 | 41 | void __iomem *regs; |
c479ff09 | 42 | spinlock_t lock; |
c479ff09 | 43 | unsigned short resume_base; |
fdc1f5df AS |
44 | |
45 | /* GPE handling */ | |
46 | u32 gpe; | |
47 | acpi_gpe_handler gpe_handler; | |
c479ff09 | 48 | }; |
be9b06b2 | 49 | |
2c58e44a AS |
50 | static unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio, |
51 | unsigned int reg) | |
be9b06b2 | 52 | { |
7a816384 | 53 | unsigned int base = CORE_BANK_OFFSET; |
be9b06b2 | 54 | |
c479ff09 MW |
55 | if (gpio >= sch->resume_base) { |
56 | gpio -= sch->resume_base; | |
7a816384 | 57 | base = RESUME_BANK_OFFSET; |
c479ff09 | 58 | } |
be9b06b2 | 59 | |
c479ff09 | 60 | return base + reg + gpio / 8; |
be9b06b2 DT |
61 | } |
62 | ||
2c58e44a | 63 | static unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio) |
be9b06b2 | 64 | { |
c479ff09 MW |
65 | if (gpio >= sch->resume_base) |
66 | gpio -= sch->resume_base; | |
67 | return gpio % 8; | |
be9b06b2 DT |
68 | } |
69 | ||
2c58e44a | 70 | static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) |
be9b06b2 | 71 | { |
be9b06b2 | 72 | unsigned short offset, bit; |
920dfd82 | 73 | u8 reg_val; |
be9b06b2 | 74 | |
920dfd82 | 75 | offset = sch_gpio_offset(sch, gpio, reg); |
c479ff09 | 76 | bit = sch_gpio_bit(sch, gpio); |
be9b06b2 | 77 | |
abaed898 | 78 | reg_val = !!(ioread8(sch->regs + offset) & BIT(bit)); |
1e0d9823 | 79 | |
920dfd82 | 80 | return reg_val; |
be9b06b2 DT |
81 | } |
82 | ||
2c58e44a | 83 | static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg, |
920dfd82 | 84 | int val) |
be9b06b2 | 85 | { |
3cbf1822 | 86 | unsigned short offset, bit; |
920dfd82 | 87 | u8 reg_val; |
be9b06b2 | 88 | |
920dfd82 CRSF |
89 | offset = sch_gpio_offset(sch, gpio, reg); |
90 | bit = sch_gpio_bit(sch, gpio); | |
be9b06b2 | 91 | |
abaed898 | 92 | reg_val = ioread8(sch->regs + offset); |
3cbf1822 | 93 | |
920dfd82 | 94 | if (val) |
abaed898 | 95 | reg_val |= BIT(bit); |
920dfd82 | 96 | else |
abaed898 AS |
97 | reg_val &= ~BIT(bit); |
98 | ||
99 | iowrite8(reg_val, sch->regs + offset); | |
920dfd82 | 100 | } |
be9b06b2 | 101 | |
2c58e44a | 102 | static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) |
920dfd82 | 103 | { |
737c8fcc | 104 | struct sch_gpio *sch = gpiochip_get_data(gc); |
7a816384 | 105 | unsigned long flags; |
be9b06b2 | 106 | |
7a816384 | 107 | spin_lock_irqsave(&sch->lock, flags); |
87041a58 | 108 | sch_gpio_reg_set(sch, gpio_num, GIO, 1); |
7a816384 | 109 | spin_unlock_irqrestore(&sch->lock, flags); |
be9b06b2 DT |
110 | return 0; |
111 | } | |
112 | ||
2c58e44a | 113 | static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) |
be9b06b2 | 114 | { |
87041a58 | 115 | struct sch_gpio *sch = gpiochip_get_data(gc); |
4941b8de | 116 | |
87041a58 | 117 | return sch_gpio_reg_get(sch, gpio_num, GLV); |
be9b06b2 DT |
118 | } |
119 | ||
2c58e44a | 120 | static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) |
be9b06b2 | 121 | { |
737c8fcc | 122 | struct sch_gpio *sch = gpiochip_get_data(gc); |
7a816384 | 123 | unsigned long flags; |
be9b06b2 | 124 | |
7a816384 | 125 | spin_lock_irqsave(&sch->lock, flags); |
87041a58 | 126 | sch_gpio_reg_set(sch, gpio_num, GLV, val); |
7a816384 | 127 | spin_unlock_irqrestore(&sch->lock, flags); |
be9b06b2 DT |
128 | } |
129 | ||
2c58e44a | 130 | static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, |
c479ff09 | 131 | int val) |
be9b06b2 | 132 | { |
737c8fcc | 133 | struct sch_gpio *sch = gpiochip_get_data(gc); |
7a816384 | 134 | unsigned long flags; |
be9b06b2 | 135 | |
7a816384 | 136 | spin_lock_irqsave(&sch->lock, flags); |
87041a58 | 137 | sch_gpio_reg_set(sch, gpio_num, GIO, 0); |
7a816384 | 138 | spin_unlock_irqrestore(&sch->lock, flags); |
1e0d9823 DK |
139 | |
140 | /* | |
c479ff09 MW |
141 | * according to the datasheet, writing to the level register has no |
142 | * effect when GPIO is programmed as input. | |
9d5f0f66 | 143 | * Actually the level register is read-only when configured as input. |
c479ff09 MW |
144 | * Thus presetting the output level before switching to output is _NOT_ possible. |
145 | * Hence we set the level after configuring the GPIO as output. | |
146 | * But we cannot prevent a short low pulse if direction is set to high | |
147 | * and an external pull-up is connected. | |
148 | */ | |
149 | sch_gpio_set(gc, gpio_num, val); | |
be9b06b2 DT |
150 | return 0; |
151 | } | |
152 | ||
2c58e44a | 153 | static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) |
d8e764c2 LW |
154 | { |
155 | struct sch_gpio *sch = gpiochip_get_data(gc); | |
156 | ||
e42615ec MV |
157 | if (sch_gpio_reg_get(sch, gpio_num, GIO)) |
158 | return GPIO_LINE_DIRECTION_IN; | |
159 | ||
160 | return GPIO_LINE_DIRECTION_OUT; | |
d8e764c2 LW |
161 | } |
162 | ||
e35b5ab0 | 163 | static const struct gpio_chip sch_gpio_chip = { |
c479ff09 | 164 | .label = "sch_gpio", |
be9b06b2 | 165 | .owner = THIS_MODULE, |
c479ff09 MW |
166 | .direction_input = sch_gpio_direction_in, |
167 | .get = sch_gpio_get, | |
168 | .direction_output = sch_gpio_direction_out, | |
169 | .set = sch_gpio_set, | |
d8e764c2 | 170 | .get_direction = sch_gpio_get_direction, |
be9b06b2 DT |
171 | }; |
172 | ||
7a816384 JK |
173 | static int sch_irq_type(struct irq_data *d, unsigned int type) |
174 | { | |
175 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
176 | struct sch_gpio *sch = gpiochip_get_data(gc); | |
177 | irq_hw_number_t gpio_num = irqd_to_hwirq(d); | |
178 | unsigned long flags; | |
179 | int rising, falling; | |
180 | ||
181 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
182 | case IRQ_TYPE_EDGE_RISING: | |
183 | rising = 1; | |
184 | falling = 0; | |
185 | break; | |
186 | case IRQ_TYPE_EDGE_FALLING: | |
187 | rising = 0; | |
188 | falling = 1; | |
189 | break; | |
190 | case IRQ_TYPE_EDGE_BOTH: | |
191 | rising = 1; | |
192 | falling = 1; | |
193 | break; | |
194 | default: | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | spin_lock_irqsave(&sch->lock, flags); | |
199 | ||
200 | sch_gpio_reg_set(sch, gpio_num, GTPE, rising); | |
201 | sch_gpio_reg_set(sch, gpio_num, GTNE, falling); | |
202 | ||
203 | irq_set_handler_locked(d, handle_edge_irq); | |
204 | ||
205 | spin_unlock_irqrestore(&sch->lock, flags); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static void sch_irq_ack(struct irq_data *d) | |
211 | { | |
212 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); | |
213 | struct sch_gpio *sch = gpiochip_get_data(gc); | |
214 | irq_hw_number_t gpio_num = irqd_to_hwirq(d); | |
215 | unsigned long flags; | |
216 | ||
217 | spin_lock_irqsave(&sch->lock, flags); | |
218 | sch_gpio_reg_set(sch, gpio_num, GTS, 1); | |
219 | spin_unlock_irqrestore(&sch->lock, flags); | |
220 | } | |
221 | ||
f1138dac | 222 | static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) |
7a816384 | 223 | { |
7a816384 | 224 | struct sch_gpio *sch = gpiochip_get_data(gc); |
7a816384 JK |
225 | unsigned long flags; |
226 | ||
227 | spin_lock_irqsave(&sch->lock, flags); | |
228 | sch_gpio_reg_set(sch, gpio_num, GGPE, val); | |
229 | spin_unlock_irqrestore(&sch->lock, flags); | |
230 | } | |
231 | ||
232 | static void sch_irq_mask(struct irq_data *d) | |
233 | { | |
f1138dac AS |
234 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
235 | irq_hw_number_t gpio_num = irqd_to_hwirq(d); | |
236 | ||
237 | sch_irq_mask_unmask(gc, gpio_num, 0); | |
238 | gpiochip_disable_irq(gc, gpio_num); | |
7a816384 JK |
239 | } |
240 | ||
241 | static void sch_irq_unmask(struct irq_data *d) | |
242 | { | |
f1138dac AS |
243 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
244 | irq_hw_number_t gpio_num = irqd_to_hwirq(d); | |
245 | ||
246 | gpiochip_enable_irq(gc, gpio_num); | |
247 | sch_irq_mask_unmask(gc, gpio_num, 1); | |
7a816384 JK |
248 | } |
249 | ||
f1138dac AS |
250 | static const struct irq_chip sch_irqchip = { |
251 | .name = "sch_gpio", | |
252 | .irq_ack = sch_irq_ack, | |
253 | .irq_mask = sch_irq_mask, | |
254 | .irq_unmask = sch_irq_unmask, | |
255 | .irq_set_type = sch_irq_type, | |
256 | .flags = IRQCHIP_IMMUTABLE, | |
257 | GPIOCHIP_IRQ_RESOURCE_HELPERS, | |
258 | }; | |
259 | ||
fdc1f5df AS |
260 | static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) |
261 | { | |
262 | struct sch_gpio *sch = context; | |
263 | struct gpio_chip *gc = &sch->chip; | |
264 | unsigned long core_status, resume_status; | |
265 | unsigned long pending; | |
266 | unsigned long flags; | |
267 | int offset; | |
268 | u32 ret; | |
269 | ||
270 | spin_lock_irqsave(&sch->lock, flags); | |
271 | ||
abaed898 AS |
272 | core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); |
273 | resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); | |
fdc1f5df AS |
274 | |
275 | spin_unlock_irqrestore(&sch->lock, flags); | |
276 | ||
277 | pending = (resume_status << sch->resume_base) | core_status; | |
278 | for_each_set_bit(offset, &pending, sch->chip.ngpio) | |
dbd1c54f | 279 | generic_handle_domain_irq(gc->irq.domain, offset); |
fdc1f5df AS |
280 | |
281 | /* Set returning value depending on whether we handled an interrupt */ | |
282 | ret = pending ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; | |
283 | ||
284 | /* Acknowledge GPE to ACPICA */ | |
285 | ret |= ACPI_REENABLE_GPE; | |
286 | ||
287 | return ret; | |
288 | } | |
289 | ||
290 | static void sch_gpio_remove_gpe_handler(void *data) | |
291 | { | |
292 | struct sch_gpio *sch = data; | |
293 | ||
294 | acpi_disable_gpe(NULL, sch->gpe); | |
295 | acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler); | |
296 | } | |
297 | ||
298 | static int sch_gpio_install_gpe_handler(struct sch_gpio *sch) | |
299 | { | |
300 | struct device *dev = sch->chip.parent; | |
301 | acpi_status status; | |
302 | ||
303 | status = acpi_install_gpe_handler(NULL, sch->gpe, ACPI_GPE_LEVEL_TRIGGERED, | |
304 | sch->gpe_handler, sch); | |
305 | if (ACPI_FAILURE(status)) { | |
306 | dev_err(dev, "Failed to install GPE handler for %u: %s\n", | |
307 | sch->gpe, acpi_format_exception(status)); | |
308 | return -ENODEV; | |
309 | } | |
310 | ||
311 | status = acpi_enable_gpe(NULL, sch->gpe); | |
312 | if (ACPI_FAILURE(status)) { | |
313 | dev_err(dev, "Failed to enable GPE handler for %u: %s\n", | |
314 | sch->gpe, acpi_format_exception(status)); | |
315 | acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler); | |
316 | return -ENODEV; | |
317 | } | |
318 | ||
319 | return devm_add_action_or_reset(dev, sch_gpio_remove_gpe_handler, sch); | |
320 | } | |
321 | ||
3836309d | 322 | static int sch_gpio_probe(struct platform_device *pdev) |
be9b06b2 | 323 | { |
abaed898 | 324 | struct device *dev = &pdev->dev; |
7a816384 | 325 | struct gpio_irq_chip *girq; |
c479ff09 | 326 | struct sch_gpio *sch; |
be9b06b2 | 327 | struct resource *res; |
abaed898 | 328 | void __iomem *regs; |
fdc1f5df | 329 | int ret; |
f04ddfcd | 330 | |
2d485d47 | 331 | sch = devm_kzalloc(dev, sizeof(*sch), GFP_KERNEL); |
c479ff09 MW |
332 | if (!sch) |
333 | return -ENOMEM; | |
be9b06b2 DT |
334 | |
335 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | |
336 | if (!res) | |
337 | return -EBUSY; | |
338 | ||
abaed898 AS |
339 | regs = devm_ioport_map(dev, res->start, resource_size(res)); |
340 | if (!regs) | |
be9b06b2 DT |
341 | return -EBUSY; |
342 | ||
abaed898 AS |
343 | sch->regs = regs; |
344 | ||
c479ff09 | 345 | spin_lock_init(&sch->lock); |
c479ff09 | 346 | sch->chip = sch_gpio_chip; |
2d485d47 AS |
347 | sch->chip.label = dev_name(dev); |
348 | sch->chip.parent = dev; | |
be9b06b2 | 349 | |
c479ff09 | 350 | switch (pdev->id) { |
be41cf58 | 351 | case PCI_DEVICE_ID_INTEL_SCH_LPC: |
c479ff09 MW |
352 | sch->resume_base = 10; |
353 | sch->chip.ngpio = 14; | |
354 | ||
be41cf58 LN |
355 | /* |
356 | * GPIO[6:0] enabled by default | |
357 | * GPIO7 is configured by the CMC as SLPIOVR | |
358 | * Enable GPIO[9:8] core powered gpios explicitly | |
359 | */ | |
87041a58 CP |
360 | sch_gpio_reg_set(sch, 8, GEN, 1); |
361 | sch_gpio_reg_set(sch, 9, GEN, 1); | |
be41cf58 LN |
362 | /* |
363 | * SUS_GPIO[2:0] enabled by default | |
364 | * Enable SUS_GPIO3 resume powered gpio explicitly | |
365 | */ | |
87041a58 | 366 | sch_gpio_reg_set(sch, 13, GEN, 1); |
be41cf58 LN |
367 | break; |
368 | ||
369 | case PCI_DEVICE_ID_INTEL_ITC_LPC: | |
c479ff09 MW |
370 | sch->resume_base = 5; |
371 | sch->chip.ngpio = 14; | |
be41cf58 LN |
372 | break; |
373 | ||
374 | case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: | |
c479ff09 MW |
375 | sch->resume_base = 21; |
376 | sch->chip.ngpio = 30; | |
be41cf58 LN |
377 | break; |
378 | ||
92021490 | 379 | case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB: |
92021490 CRSF |
380 | sch->resume_base = 2; |
381 | sch->chip.ngpio = 8; | |
382 | break; | |
383 | ||
be41cf58 | 384 | default: |
c479ff09 | 385 | return -ENODEV; |
f04ddfcd | 386 | } |
be9b06b2 | 387 | |
7a816384 | 388 | girq = &sch->chip.irq; |
f1138dac | 389 | gpio_irq_chip_set_chip(girq, &sch_irqchip); |
7a816384 JK |
390 | girq->num_parents = 0; |
391 | girq->parents = NULL; | |
392 | girq->parent_handler = NULL; | |
393 | girq->default_type = IRQ_TYPE_NONE; | |
394 | girq->handler = handle_bad_irq; | |
395 | ||
fdc1f5df AS |
396 | /* GPE setup is optional */ |
397 | sch->gpe = GPE0E_GPIO; | |
398 | sch->gpe_handler = sch_gpio_gpe_handler; | |
399 | ||
400 | ret = sch_gpio_install_gpe_handler(sch); | |
401 | if (ret) | |
2d485d47 | 402 | dev_warn(dev, "Can't setup GPE, no IRQ support\n"); |
fdc1f5df | 403 | |
2d485d47 | 404 | return devm_gpiochip_add_data(dev, &sch->chip, sch); |
be9b06b2 DT |
405 | } |
406 | ||
407 | static struct platform_driver sch_gpio_driver = { | |
408 | .driver = { | |
409 | .name = "sch_gpio", | |
be9b06b2 DT |
410 | }, |
411 | .probe = sch_gpio_probe, | |
be9b06b2 DT |
412 | }; |
413 | ||
6f61415e | 414 | module_platform_driver(sch_gpio_driver); |
be9b06b2 DT |
415 | |
416 | MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); | |
417 | MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); | |
cb0e9a7b | 418 | MODULE_LICENSE("GPL v2"); |
be9b06b2 | 419 | MODULE_ALIAS("platform:sch_gpio"); |