diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-28 17:08:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-28 17:08:46 -0400 |
commit | 7bf97e1d5a94b6a71815771bb9452fc2c022c966 (patch) | |
tree | f89082dbd33a51a79c9a9c0dddb29e2b11b8207e /drivers/gpio/gpio-sodaville.c | |
parent | 30304e5a79d424eb2c8707b3ff0e9b8bf6ab3e8f (diff) | |
parent | c77c8a6fd3d57b586ff5ecb5ab5b32ca4f54fe75 (diff) |
Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6
Pull GPIO changes for v3.4 from Grant Likely:
"Primarily gpio device driver changes with some minor side effects
under arch/arm and arch/x86. Also includes a few core changes such as
explicitly supporting (electrical) open source and open drain outputs
and some help for parsing gpio devicetree properties."
Fix up context conflict due to Laxman Dewangan adding sleep control for
the tps65910 driver separately for gpio's and regulators.
* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
gpio/ep93xx: Remove unused inline function and useless pr_err message
gpio/sodaville: Mark broken due to core irqdomain migration
gpio/omap: fix redundant decoding of gpio offset
gpio/omap: fix incorrect update to context.irqenable1
gpio/omap: fix incorrect context restore logic in omap_gpio_runtime_*
gpio/omap: fix missing dataout context save in _set_gpio_dataout_reg
gpio/omap: fix _set_gpio_irqenable implementation
gpio/omap: fix trigger type to unsigned
gpio/omap: fix wakeup_en register update in _set_gpio_wakeup()
gpio: tegra: tegra_gpio_config shouldn't be __init
gpio/davinci: fix enabling unbanked GPIO IRQs
gpio/davinci: fix oops on unbanked gpio irq request
gpio/omap: Fix section warning for omap_mpuio_alloc_gc()
ARM: tegra: export tegra_gpio_{en,dis}able
gpio/gpio-stmpe: Fix the value returned by _get_value routine
Documentation/gpio.txt: Explain expected pinctrl interaction
GPIO: LPC32xx: Add output reading to GPO P3
GPIO: LPC32xx: Fix missing bit selection mask
gpio/omap: fix wakeups on level-triggered GPIOs
gpio/omap: Fix IRQ handling for SPARSE_IRQ
...
Diffstat (limited to 'drivers/gpio/gpio-sodaville.c')
-rw-r--r-- | drivers/gpio/gpio-sodaville.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c new file mode 100644 index 000000000000..9ba15d31d242 --- /dev/null +++ b/drivers/gpio/gpio-sodaville.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * GPIO interface for Intel Sodaville SoCs. | ||
3 | * | ||
4 | * Copyright (c) 2010, 2011 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License 2 as published | ||
8 | * by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/gpio.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/of_irq.h> | ||
23 | #include <linux/basic_mmio_gpio.h> | ||
24 | |||
25 | #define DRV_NAME "sdv_gpio" | ||
26 | #define SDV_NUM_PUB_GPIOS 12 | ||
27 | #define PCI_DEVICE_ID_SDV_GPIO 0x2e67 | ||
28 | #define GPIO_BAR 0 | ||
29 | |||
30 | #define GPOUTR 0x00 | ||
31 | #define GPOER 0x04 | ||
32 | #define GPINR 0x08 | ||
33 | |||
34 | #define GPSTR 0x0c | ||
35 | #define GPIT1R0 0x10 | ||
36 | #define GPIO_INT 0x14 | ||
37 | #define GPIT1R1 0x18 | ||
38 | |||
39 | #define GPMUXCTL 0x1c | ||
40 | |||
41 | struct sdv_gpio_chip_data { | ||
42 | int irq_base; | ||
43 | void __iomem *gpio_pub_base; | ||
44 | struct irq_domain id; | ||
45 | struct irq_chip_generic *gc; | ||
46 | struct bgpio_chip bgpio; | ||
47 | }; | ||
48 | |||
49 | static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type) | ||
50 | { | ||
51 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
52 | struct sdv_gpio_chip_data *sd = gc->private; | ||
53 | void __iomem *type_reg; | ||
54 | u32 irq_offs = d->irq - sd->irq_base; | ||
55 | u32 reg; | ||
56 | |||
57 | if (irq_offs < 8) | ||
58 | type_reg = sd->gpio_pub_base + GPIT1R0; | ||
59 | else | ||
60 | type_reg = sd->gpio_pub_base + GPIT1R1; | ||
61 | |||
62 | reg = readl(type_reg); | ||
63 | |||
64 | switch (type) { | ||
65 | case IRQ_TYPE_LEVEL_HIGH: | ||
66 | reg &= ~BIT(4 * (irq_offs % 8)); | ||
67 | break; | ||
68 | |||
69 | case IRQ_TYPE_LEVEL_LOW: | ||
70 | reg |= BIT(4 * (irq_offs % 8)); | ||
71 | break; | ||
72 | |||
73 | default: | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | |||
77 | writel(reg, type_reg); | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data) | ||
82 | { | ||
83 | struct sdv_gpio_chip_data *sd = data; | ||
84 | u32 irq_stat = readl(sd->gpio_pub_base + GPSTR); | ||
85 | |||
86 | irq_stat &= readl(sd->gpio_pub_base + GPIO_INT); | ||
87 | if (!irq_stat) | ||
88 | return IRQ_NONE; | ||
89 | |||
90 | while (irq_stat) { | ||
91 | u32 irq_bit = __fls(irq_stat); | ||
92 | |||
93 | irq_stat &= ~BIT(irq_bit); | ||
94 | generic_handle_irq(sd->irq_base + irq_bit); | ||
95 | } | ||
96 | |||
97 | return IRQ_HANDLED; | ||
98 | } | ||
99 | |||
100 | static int sdv_xlate(struct irq_domain *h, struct device_node *node, | ||
101 | const u32 *intspec, u32 intsize, irq_hw_number_t *out_hwirq, | ||
102 | u32 *out_type) | ||
103 | { | ||
104 | u32 line, type; | ||
105 | |||
106 | if (node != h->of_node) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (intsize < 2) | ||
110 | return -EINVAL; | ||
111 | |||
112 | line = *intspec; | ||
113 | *out_hwirq = line; | ||
114 | |||
115 | intspec++; | ||
116 | type = *intspec; | ||
117 | |||
118 | switch (type) { | ||
119 | case IRQ_TYPE_LEVEL_LOW: | ||
120 | case IRQ_TYPE_LEVEL_HIGH: | ||
121 | *out_type = type; | ||
122 | break; | ||
123 | default: | ||
124 | return -EINVAL; | ||
125 | } | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static struct irq_domain_ops irq_domain_sdv_ops = { | ||
130 | .dt_translate = sdv_xlate, | ||
131 | }; | ||
132 | |||
133 | static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, | ||
134 | struct pci_dev *pdev) | ||
135 | { | ||
136 | struct irq_chip_type *ct; | ||
137 | int ret; | ||
138 | |||
139 | sd->irq_base = irq_alloc_descs(-1, 0, SDV_NUM_PUB_GPIOS, -1); | ||
140 | if (sd->irq_base < 0) | ||
141 | return sd->irq_base; | ||
142 | |||
143 | /* mask + ACK all interrupt sources */ | ||
144 | writel(0, sd->gpio_pub_base + GPIO_INT); | ||
145 | writel((1 << 11) - 1, sd->gpio_pub_base + GPSTR); | ||
146 | |||
147 | ret = request_irq(pdev->irq, sdv_gpio_pub_irq_handler, IRQF_SHARED, | ||
148 | "sdv_gpio", sd); | ||
149 | if (ret) | ||
150 | goto out_free_desc; | ||
151 | |||
152 | sd->id.irq_base = sd->irq_base; | ||
153 | sd->id.of_node = of_node_get(pdev->dev.of_node); | ||
154 | sd->id.ops = &irq_domain_sdv_ops; | ||
155 | |||
156 | /* | ||
157 | * This gpio irq controller latches level irqs. Testing shows that if | ||
158 | * we unmask & ACK the IRQ before the source of the interrupt is gone | ||
159 | * then the interrupt is active again. | ||
160 | */ | ||
161 | sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base, | ||
162 | sd->gpio_pub_base, handle_fasteoi_irq); | ||
163 | if (!sd->gc) { | ||
164 | ret = -ENOMEM; | ||
165 | goto out_free_irq; | ||
166 | } | ||
167 | |||
168 | sd->gc->private = sd; | ||
169 | ct = sd->gc->chip_types; | ||
170 | ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; | ||
171 | ct->regs.eoi = GPSTR; | ||
172 | ct->regs.mask = GPIO_INT; | ||
173 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | ||
174 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | ||
175 | ct->chip.irq_eoi = irq_gc_eoi; | ||
176 | ct->chip.irq_set_type = sdv_gpio_pub_set_type; | ||
177 | |||
178 | irq_setup_generic_chip(sd->gc, IRQ_MSK(SDV_NUM_PUB_GPIOS), | ||
179 | IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, | ||
180 | IRQ_LEVEL | IRQ_NOPROBE); | ||
181 | |||
182 | irq_domain_add(&sd->id); | ||
183 | return 0; | ||
184 | out_free_irq: | ||
185 | free_irq(pdev->irq, sd); | ||
186 | out_free_desc: | ||
187 | irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static int __devinit sdv_gpio_probe(struct pci_dev *pdev, | ||
192 | const struct pci_device_id *pci_id) | ||
193 | { | ||
194 | struct sdv_gpio_chip_data *sd; | ||
195 | unsigned long addr; | ||
196 | const void *prop; | ||
197 | int len; | ||
198 | int ret; | ||
199 | u32 mux_val; | ||
200 | |||
201 | sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL); | ||
202 | if (!sd) | ||
203 | return -ENOMEM; | ||
204 | ret = pci_enable_device(pdev); | ||
205 | if (ret) { | ||
206 | dev_err(&pdev->dev, "can't enable device.\n"); | ||
207 | goto done; | ||
208 | } | ||
209 | |||
210 | ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME); | ||
211 | if (ret) { | ||
212 | dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR); | ||
213 | goto disable_pci; | ||
214 | } | ||
215 | |||
216 | addr = pci_resource_start(pdev, GPIO_BAR); | ||
217 | if (!addr) | ||
218 | goto release_reg; | ||
219 | sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR)); | ||
220 | |||
221 | prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len); | ||
222 | if (prop && len == 4) { | ||
223 | mux_val = of_read_number(prop, 1); | ||
224 | writel(mux_val, sd->gpio_pub_base + GPMUXCTL); | ||
225 | } | ||
226 | |||
227 | ret = bgpio_init(&sd->bgpio, &pdev->dev, 4, | ||
228 | sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR, | ||
229 | NULL, sd->gpio_pub_base + GPOER, NULL, false); | ||
230 | if (ret) | ||
231 | goto unmap; | ||
232 | sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS; | ||
233 | |||
234 | ret = gpiochip_add(&sd->bgpio.gc); | ||
235 | if (ret < 0) { | ||
236 | dev_err(&pdev->dev, "gpiochip_add() failed.\n"); | ||
237 | goto unmap; | ||
238 | } | ||
239 | |||
240 | ret = sdv_register_irqsupport(sd, pdev); | ||
241 | if (ret) | ||
242 | goto unmap; | ||
243 | |||
244 | pci_set_drvdata(pdev, sd); | ||
245 | dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n"); | ||
246 | return 0; | ||
247 | |||
248 | unmap: | ||
249 | iounmap(sd->gpio_pub_base); | ||
250 | release_reg: | ||
251 | pci_release_region(pdev, GPIO_BAR); | ||
252 | disable_pci: | ||
253 | pci_disable_device(pdev); | ||
254 | done: | ||
255 | kfree(sd); | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | static void sdv_gpio_remove(struct pci_dev *pdev) | ||
260 | { | ||
261 | struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev); | ||
262 | |||
263 | irq_domain_del(&sd->id); | ||
264 | free_irq(pdev->irq, sd); | ||
265 | irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); | ||
266 | |||
267 | if (gpiochip_remove(&sd->bgpio.gc)) | ||
268 | dev_err(&pdev->dev, "gpiochip_remove() failed.\n"); | ||
269 | |||
270 | pci_release_region(pdev, GPIO_BAR); | ||
271 | iounmap(sd->gpio_pub_base); | ||
272 | pci_disable_device(pdev); | ||
273 | kfree(sd); | ||
274 | } | ||
275 | |||
276 | static struct pci_device_id sdv_gpio_pci_ids[] __devinitdata = { | ||
277 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) }, | ||
278 | { 0, }, | ||
279 | }; | ||
280 | |||
281 | static struct pci_driver sdv_gpio_driver = { | ||
282 | .name = DRV_NAME, | ||
283 | .id_table = sdv_gpio_pci_ids, | ||
284 | .probe = sdv_gpio_probe, | ||
285 | .remove = sdv_gpio_remove, | ||
286 | }; | ||
287 | |||
288 | static int __init sdv_gpio_init(void) | ||
289 | { | ||
290 | return pci_register_driver(&sdv_gpio_driver); | ||
291 | } | ||
292 | module_init(sdv_gpio_init); | ||
293 | |||
294 | static void __exit sdv_gpio_exit(void) | ||
295 | { | ||
296 | pci_unregister_driver(&sdv_gpio_driver); | ||
297 | } | ||
298 | module_exit(sdv_gpio_exit); | ||
299 | |||
300 | MODULE_AUTHOR("Hans J. Koch <hjk@linutronix.de>"); | ||
301 | MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs"); | ||
302 | MODULE_LICENSE("GPL v2"); | ||