diff options
Diffstat (limited to 'drivers/gpio/bt8xxgpio.c')
-rw-r--r-- | drivers/gpio/bt8xxgpio.c | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c new file mode 100644 index 000000000000..7a1168249dd5 --- /dev/null +++ b/drivers/gpio/bt8xxgpio.c | |||
@@ -0,0 +1,348 @@ | |||
1 | /* | ||
2 | |||
3 | bt8xx GPIO abuser | ||
4 | |||
5 | Copyright (C) 2008 Michael Buesch <mb@bu3sch.de> | ||
6 | |||
7 | Please do _only_ contact the people listed _above_ with issues related to this driver. | ||
8 | All the other people listed below are not related to this driver. Their names | ||
9 | are only here, because this driver is derived from the bt848 driver. | ||
10 | |||
11 | |||
12 | Derived from the bt848 driver: | ||
13 | |||
14 | Copyright (C) 1996,97,98 Ralph Metzler | ||
15 | & Marcus Metzler | ||
16 | (c) 1999-2002 Gerd Knorr | ||
17 | |||
18 | some v4l2 code lines are taken from Justin's bttv2 driver which is | ||
19 | (c) 2000 Justin Schoeman | ||
20 | |||
21 | V4L1 removal from: | ||
22 | (c) 2005-2006 Nickolay V. Shmyrev | ||
23 | |||
24 | Fixes to be fully V4L2 compliant by | ||
25 | (c) 2006 Mauro Carvalho Chehab | ||
26 | |||
27 | Cropping and overscan support | ||
28 | Copyright (C) 2005, 2006 Michael H. Schimek | ||
29 | Sponsored by OPQ Systems AB | ||
30 | |||
31 | This program is free software; you can redistribute it and/or modify | ||
32 | it under the terms of the GNU General Public License as published by | ||
33 | the Free Software Foundation; either version 2 of the License, or | ||
34 | (at your option) any later version. | ||
35 | |||
36 | This program is distributed in the hope that it will be useful, | ||
37 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
39 | GNU General Public License for more details. | ||
40 | |||
41 | You should have received a copy of the GNU General Public License | ||
42 | along with this program; if not, write to the Free Software | ||
43 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
44 | */ | ||
45 | |||
46 | #include <linux/module.h> | ||
47 | #include <linux/pci.h> | ||
48 | #include <linux/spinlock.h> | ||
49 | |||
50 | #include <asm/gpio.h> | ||
51 | |||
52 | /* Steal the hardware definitions from the bttv driver. */ | ||
53 | #include "../media/video/bt8xx/bt848.h" | ||
54 | |||
55 | |||
56 | #define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ | ||
57 | |||
58 | |||
59 | struct bt8xxgpio { | ||
60 | spinlock_t lock; | ||
61 | |||
62 | void __iomem *mmio; | ||
63 | struct pci_dev *pdev; | ||
64 | struct gpio_chip gpio; | ||
65 | |||
66 | #ifdef CONFIG_PM | ||
67 | u32 saved_outen; | ||
68 | u32 saved_data; | ||
69 | #endif | ||
70 | }; | ||
71 | |||
72 | #define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) | ||
73 | #define bgread(adr) readl(bg->mmio+(adr)) | ||
74 | |||
75 | |||
76 | static int modparam_gpiobase = -1/* dynamic */; | ||
77 | module_param_named(gpiobase, modparam_gpiobase, int, 0444); | ||
78 | MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); | ||
79 | |||
80 | |||
81 | static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | ||
82 | { | ||
83 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | ||
84 | unsigned long flags; | ||
85 | u32 outen, data; | ||
86 | |||
87 | spin_lock_irqsave(&bg->lock, flags); | ||
88 | |||
89 | data = bgread(BT848_GPIO_DATA); | ||
90 | data &= ~(1 << nr); | ||
91 | bgwrite(data, BT848_GPIO_DATA); | ||
92 | |||
93 | outen = bgread(BT848_GPIO_OUT_EN); | ||
94 | outen &= ~(1 << nr); | ||
95 | bgwrite(outen, BT848_GPIO_OUT_EN); | ||
96 | |||
97 | spin_unlock_irqrestore(&bg->lock, flags); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | ||
103 | { | ||
104 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | ||
105 | unsigned long flags; | ||
106 | u32 val; | ||
107 | |||
108 | spin_lock_irqsave(&bg->lock, flags); | ||
109 | val = bgread(BT848_GPIO_DATA); | ||
110 | spin_unlock_irqrestore(&bg->lock, flags); | ||
111 | |||
112 | return !!(val & (1 << nr)); | ||
113 | } | ||
114 | |||
115 | static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, | ||
116 | unsigned nr, int val) | ||
117 | { | ||
118 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | ||
119 | unsigned long flags; | ||
120 | u32 outen, data; | ||
121 | |||
122 | spin_lock_irqsave(&bg->lock, flags); | ||
123 | |||
124 | outen = bgread(BT848_GPIO_OUT_EN); | ||
125 | outen |= (1 << nr); | ||
126 | bgwrite(outen, BT848_GPIO_OUT_EN); | ||
127 | |||
128 | data = bgread(BT848_GPIO_DATA); | ||
129 | if (val) | ||
130 | data |= (1 << nr); | ||
131 | else | ||
132 | data &= ~(1 << nr); | ||
133 | bgwrite(data, BT848_GPIO_DATA); | ||
134 | |||
135 | spin_unlock_irqrestore(&bg->lock, flags); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, | ||
141 | unsigned nr, int val) | ||
142 | { | ||
143 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | ||
144 | unsigned long flags; | ||
145 | u32 data; | ||
146 | |||
147 | spin_lock_irqsave(&bg->lock, flags); | ||
148 | |||
149 | data = bgread(BT848_GPIO_DATA); | ||
150 | if (val) | ||
151 | data |= (1 << nr); | ||
152 | else | ||
153 | data &= ~(1 << nr); | ||
154 | bgwrite(data, BT848_GPIO_DATA); | ||
155 | |||
156 | spin_unlock_irqrestore(&bg->lock, flags); | ||
157 | } | ||
158 | |||
159 | static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) | ||
160 | { | ||
161 | struct gpio_chip *c = &bg->gpio; | ||
162 | |||
163 | c->label = bg->pdev->dev.bus_id; | ||
164 | c->owner = THIS_MODULE; | ||
165 | c->direction_input = bt8xxgpio_gpio_direction_input; | ||
166 | c->get = bt8xxgpio_gpio_get; | ||
167 | c->direction_output = bt8xxgpio_gpio_direction_output; | ||
168 | c->set = bt8xxgpio_gpio_set; | ||
169 | c->dbg_show = NULL; | ||
170 | c->base = modparam_gpiobase; | ||
171 | c->ngpio = BT8XXGPIO_NR_GPIOS; | ||
172 | c->can_sleep = 0; | ||
173 | } | ||
174 | |||
175 | static int bt8xxgpio_probe(struct pci_dev *dev, | ||
176 | const struct pci_device_id *pci_id) | ||
177 | { | ||
178 | struct bt8xxgpio *bg; | ||
179 | int err; | ||
180 | |||
181 | bg = kzalloc(sizeof(*bg), GFP_KERNEL); | ||
182 | if (!bg) | ||
183 | return -ENOMEM; | ||
184 | |||
185 | bg->pdev = dev; | ||
186 | spin_lock_init(&bg->lock); | ||
187 | |||
188 | err = pci_enable_device(dev); | ||
189 | if (err) { | ||
190 | printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); | ||
191 | goto err_freebg; | ||
192 | } | ||
193 | if (!request_mem_region(pci_resource_start(dev, 0), | ||
194 | pci_resource_len(dev, 0), | ||
195 | "bt8xxgpio")) { | ||
196 | printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", | ||
197 | (unsigned long long)pci_resource_start(dev, 0)); | ||
198 | err = -EBUSY; | ||
199 | goto err_disable; | ||
200 | } | ||
201 | pci_set_master(dev); | ||
202 | pci_set_drvdata(dev, bg); | ||
203 | |||
204 | bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); | ||
205 | if (!bg->mmio) { | ||
206 | printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); | ||
207 | err = -EIO; | ||
208 | goto err_release_mem; | ||
209 | } | ||
210 | |||
211 | /* Disable interrupts */ | ||
212 | bgwrite(0, BT848_INT_MASK); | ||
213 | |||
214 | /* gpio init */ | ||
215 | bgwrite(0, BT848_GPIO_DMA_CTL); | ||
216 | bgwrite(0, BT848_GPIO_REG_INP); | ||
217 | bgwrite(0, BT848_GPIO_OUT_EN); | ||
218 | |||
219 | bt8xxgpio_gpio_setup(bg); | ||
220 | err = gpiochip_add(&bg->gpio); | ||
221 | if (err) { | ||
222 | printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); | ||
223 | goto err_release_mem; | ||
224 | } | ||
225 | |||
226 | printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", | ||
227 | bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); | ||
228 | |||
229 | return 0; | ||
230 | |||
231 | err_release_mem: | ||
232 | release_mem_region(pci_resource_start(dev, 0), | ||
233 | pci_resource_len(dev, 0)); | ||
234 | pci_set_drvdata(dev, NULL); | ||
235 | err_disable: | ||
236 | pci_disable_device(dev); | ||
237 | err_freebg: | ||
238 | kfree(bg); | ||
239 | |||
240 | return err; | ||
241 | } | ||
242 | |||
243 | static void bt8xxgpio_remove(struct pci_dev *pdev) | ||
244 | { | ||
245 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | ||
246 | |||
247 | gpiochip_remove(&bg->gpio); | ||
248 | |||
249 | bgwrite(0, BT848_INT_MASK); | ||
250 | bgwrite(~0x0, BT848_INT_STAT); | ||
251 | bgwrite(0x0, BT848_GPIO_OUT_EN); | ||
252 | |||
253 | iounmap(bg->mmio); | ||
254 | release_mem_region(pci_resource_start(pdev, 0), | ||
255 | pci_resource_len(pdev, 0)); | ||
256 | pci_disable_device(pdev); | ||
257 | |||
258 | pci_set_drvdata(pdev, NULL); | ||
259 | kfree(bg); | ||
260 | } | ||
261 | |||
262 | #ifdef CONFIG_PM | ||
263 | static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) | ||
264 | { | ||
265 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | ||
266 | unsigned long flags; | ||
267 | |||
268 | spin_lock_irqsave(&bg->lock, flags); | ||
269 | |||
270 | bg->saved_outen = bgread(BT848_GPIO_OUT_EN); | ||
271 | bg->saved_data = bgread(BT848_GPIO_DATA); | ||
272 | |||
273 | bgwrite(0, BT848_INT_MASK); | ||
274 | bgwrite(~0x0, BT848_INT_STAT); | ||
275 | bgwrite(0x0, BT848_GPIO_OUT_EN); | ||
276 | |||
277 | spin_unlock_irqrestore(&bg->lock, flags); | ||
278 | |||
279 | pci_save_state(pdev); | ||
280 | pci_disable_device(pdev); | ||
281 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int bt8xxgpio_resume(struct pci_dev *pdev) | ||
287 | { | ||
288 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | ||
289 | unsigned long flags; | ||
290 | int err; | ||
291 | |||
292 | pci_set_power_state(pdev, 0); | ||
293 | err = pci_enable_device(pdev); | ||
294 | if (err) | ||
295 | return err; | ||
296 | pci_restore_state(pdev); | ||
297 | |||
298 | spin_lock_irqsave(&bg->lock, flags); | ||
299 | |||
300 | bgwrite(0, BT848_INT_MASK); | ||
301 | bgwrite(0, BT848_GPIO_DMA_CTL); | ||
302 | bgwrite(0, BT848_GPIO_REG_INP); | ||
303 | bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); | ||
304 | bgwrite(bg->saved_data & bg->saved_outen, | ||
305 | BT848_GPIO_DATA); | ||
306 | |||
307 | spin_unlock_irqrestore(&bg->lock, flags); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | #else | ||
312 | #define bt8xxgpio_suspend NULL | ||
313 | #define bt8xxgpio_resume NULL | ||
314 | #endif /* CONFIG_PM */ | ||
315 | |||
316 | static struct pci_device_id bt8xxgpio_pci_tbl[] = { | ||
317 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, | ||
318 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, | ||
319 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, | ||
320 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, | ||
321 | { 0, }, | ||
322 | }; | ||
323 | MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); | ||
324 | |||
325 | static struct pci_driver bt8xxgpio_pci_driver = { | ||
326 | .name = "bt8xxgpio", | ||
327 | .id_table = bt8xxgpio_pci_tbl, | ||
328 | .probe = bt8xxgpio_probe, | ||
329 | .remove = bt8xxgpio_remove, | ||
330 | .suspend = bt8xxgpio_suspend, | ||
331 | .resume = bt8xxgpio_resume, | ||
332 | }; | ||
333 | |||
334 | static int bt8xxgpio_init(void) | ||
335 | { | ||
336 | return pci_register_driver(&bt8xxgpio_pci_driver); | ||
337 | } | ||
338 | module_init(bt8xxgpio_init) | ||
339 | |||
340 | static void bt8xxgpio_exit(void) | ||
341 | { | ||
342 | pci_unregister_driver(&bt8xxgpio_pci_driver); | ||
343 | } | ||
344 | module_exit(bt8xxgpio_exit) | ||
345 | |||
346 | MODULE_LICENSE("GPL"); | ||
347 | MODULE_AUTHOR("Michael Buesch"); | ||
348 | MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); | ||