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