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