diff options
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.29.1/linux-2.6.29-timberdale.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.29.1/linux-2.6.29-timberdale.patch | 6095 |
1 files changed, 6095 insertions, 0 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.29.1/linux-2.6.29-timberdale.patch b/meta-moblin/packages/linux/linux-moblin-2.6.29.1/linux-2.6.29-timberdale.patch new file mode 100644 index 0000000000..c36e5ba4ad --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.29.1/linux-2.6.29-timberdale.patch | |||
@@ -0,0 +1,6095 @@ | |||
1 | Patch provided by Mocean in order to enable the timberdale subsystem of the Russelville board. | ||
2 | |||
3 | Signed-off-by: Joel Clark <joel.clark@intel.com> | ||
4 | Acked-by: Arjan van de Ven <arjan@infradead.org> | ||
5 | Signed-off-by: Todd Brandt todd.e.brandt@intel.com | ||
6 | |||
7 | |||
8 | diff -uNr linux-2.6.29-clean/drivers/gpio/Kconfig linux-2.6.29/drivers/gpio/Kconfig | ||
9 | --- linux-2.6.29-clean/drivers/gpio/Kconfig 2009-04-01 09:20:23.000000000 -0700 | ||
10 | +++ linux-2.6.29/drivers/gpio/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
11 | @@ -161,6 +161,12 @@ | ||
12 | |||
13 | If unsure, say N. | ||
14 | |||
15 | +config GPIO_TIMBERDALE | ||
16 | + tristate "Support for timberdale GPIO" | ||
17 | + depends on MFD_TIMBERDALE && GPIOLIB | ||
18 | + ---help--- | ||
19 | + Add support for GPIO usage of some pins of the timberdale FPGA. | ||
20 | + | ||
21 | comment "SPI GPIO expanders:" | ||
22 | |||
23 | config GPIO_MAX7301 | ||
24 | diff -uNr linux-2.6.29-clean/drivers/gpio/Makefile linux-2.6.29/drivers/gpio/Makefile | ||
25 | --- linux-2.6.29-clean/drivers/gpio/Makefile 2009-04-01 09:20:23.000000000 -0700 | ||
26 | +++ linux-2.6.29/drivers/gpio/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
27 | @@ -12,3 +12,4 @@ | ||
28 | obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o | ||
29 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | ||
30 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o | ||
31 | +obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o | ||
32 | diff -uNr linux-2.6.29-clean/drivers/gpio/timbgpio.c linux-2.6.29/drivers/gpio/timbgpio.c | ||
33 | --- linux-2.6.29-clean/drivers/gpio/timbgpio.c 1969-12-31 16:00:00.000000000 -0800 | ||
34 | +++ linux-2.6.29/drivers/gpio/timbgpio.c 2009-04-06 13:51:47.000000000 -0700 | ||
35 | @@ -0,0 +1,275 @@ | ||
36 | +/* | ||
37 | + * timbgpio.c timberdale FPGA GPIO driver | ||
38 | + * Copyright (c) 2009 Intel Corporation | ||
39 | + * | ||
40 | + * This program is free software; you can redistribute it and/or modify | ||
41 | + * it under the terms of the GNU General Public License version 2 as | ||
42 | + * published by the Free Software Foundation. | ||
43 | + * | ||
44 | + * This program is distributed in the hope that it will be useful, | ||
45 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
46 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
47 | + * GNU General Public License for more details. | ||
48 | + * | ||
49 | + * You should have received a copy of the GNU General Public License | ||
50 | + * along with this program; if not, write to the Free Software | ||
51 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
52 | + */ | ||
53 | + | ||
54 | +/* Supports: | ||
55 | + * Timberdale FPGA GPIO | ||
56 | + */ | ||
57 | + | ||
58 | +#include <linux/module.h> | ||
59 | +#include <linux/gpio.h> | ||
60 | +#include <linux/pci.h> | ||
61 | +#include <linux/platform_device.h> | ||
62 | +#include <linux/interrupt.h> | ||
63 | + | ||
64 | +#include "timbgpio.h" | ||
65 | + | ||
66 | +static u32 timbgpio_configure(struct gpio_chip *gpio, unsigned nr, | ||
67 | + unsigned off, unsigned val) | ||
68 | +{ | ||
69 | + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
70 | + | ||
71 | + u32 config, oldconfig, wconfig; | ||
72 | + | ||
73 | + mutex_lock(&tgpio->lock); | ||
74 | + config = ioread32(tgpio->membase + off); | ||
75 | + oldconfig = config; | ||
76 | + | ||
77 | + if (val) | ||
78 | + config |= (1 << nr); | ||
79 | + else | ||
80 | + config &= ~(1 << nr); | ||
81 | + | ||
82 | + iowrite32(config, tgpio->membase + off); | ||
83 | + wconfig = ioread32(tgpio->membase + off); | ||
84 | + mutex_unlock(&tgpio->lock); | ||
85 | + | ||
86 | + return oldconfig; | ||
87 | +} | ||
88 | + | ||
89 | +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | ||
90 | +{ | ||
91 | + timbgpio_configure(gpio, nr, TGPIODIR, 1); | ||
92 | + return 0; | ||
93 | +} | ||
94 | + | ||
95 | +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | ||
96 | +{ | ||
97 | + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
98 | + u32 value; | ||
99 | + | ||
100 | + value = ioread32(tgpio->membase + TGPIOVAL); | ||
101 | + return (value & (1 << nr)) ? 1 : 0; | ||
102 | +} | ||
103 | + | ||
104 | +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, | ||
105 | + unsigned nr, int val) | ||
106 | +{ | ||
107 | + timbgpio_configure(gpio, nr, TGPIODIR, 0); | ||
108 | + return 0; | ||
109 | +} | ||
110 | + | ||
111 | + | ||
112 | + | ||
113 | +static void timbgpio_gpio_set(struct gpio_chip *gpio, | ||
114 | + unsigned nr, int val) | ||
115 | +{ | ||
116 | + timbgpio_configure(gpio, nr, TGPIOVAL, val); | ||
117 | +} | ||
118 | + | ||
119 | +/* | ||
120 | + * Function to control flank or level triggered GPIO pin | ||
121 | + * @nr - pin | ||
122 | + * @ val - 1: flank, 0: level | ||
123 | + * | ||
124 | + */ | ||
125 | +static void timbgpio_gpio_flnk_lvl_ctrl(struct gpio_chip *gpio, | ||
126 | + unsigned nr, int val) | ||
127 | +{ | ||
128 | + timbgpio_configure(gpio, nr, TGPIOFLK, val); | ||
129 | +} | ||
130 | +EXPORT_SYMBOL(timbgpio_gpio_flnk_lvl_ctrl); | ||
131 | + | ||
132 | +/* | ||
133 | + * Enable or disable interrupt | ||
134 | + * | ||
135 | + */ | ||
136 | +static void timbgpio_gpio_int_ctrl(struct gpio_chip *gpio, | ||
137 | + unsigned nr, int val) | ||
138 | +{ | ||
139 | + timbgpio_configure(gpio, nr, TGPIOINT, val); | ||
140 | +} | ||
141 | +EXPORT_SYMBOL(timbgpio_gpio_int_ctrl); | ||
142 | + | ||
143 | +/* | ||
144 | + * @val - 1: Asserted high or on positive flank, 0: Asserted low or on negative flank | ||
145 | + * | ||
146 | + */ | ||
147 | +static void timbgpio_gpio_lvl_ctrl(struct gpio_chip *gpio, | ||
148 | + unsigned nr, int val) | ||
149 | +{ | ||
150 | + timbgpio_configure(gpio, nr, TGPIOLVL, val); | ||
151 | +} | ||
152 | +EXPORT_SYMBOL(timbgpio_gpio_lvl_ctrl); | ||
153 | + | ||
154 | +static void timbgpio_gpio_int_clr(struct gpio_chip *gpio, | ||
155 | + unsigned nr, int val) | ||
156 | +{ | ||
157 | + timbgpio_configure(gpio, nr, TGPIOINT_CLR, val); | ||
158 | +} | ||
159 | +EXPORT_SYMBOL(timbgpio_gpio_int_clr); | ||
160 | + | ||
161 | + | ||
162 | +static irqreturn_t timbgpio_handleinterrupt(int irq, void *devid) | ||
163 | +{ | ||
164 | + struct timbgpio *tgpio = (struct timbgpio *)devid; | ||
165 | + | ||
166 | + iowrite32(0xffffffff, tgpio->membase + TGPIOINT_CLR); | ||
167 | + | ||
168 | + return IRQ_HANDLED; | ||
169 | +} | ||
170 | + | ||
171 | +static int timbgpio_probe(struct platform_device *dev) | ||
172 | +{ | ||
173 | + int err, irq; | ||
174 | + struct gpio_chip *gc; | ||
175 | + struct timbgpio *tgpio; | ||
176 | + struct resource *iomem, *rscr; | ||
177 | + | ||
178 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
179 | + if (!iomem) { | ||
180 | + err = -EINVAL; | ||
181 | + goto err_mem; | ||
182 | + } | ||
183 | + | ||
184 | + tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); | ||
185 | + if (!tgpio) { | ||
186 | + err = -EINVAL; | ||
187 | + goto err_mem; | ||
188 | + } | ||
189 | + | ||
190 | + mutex_init(&tgpio->lock); | ||
191 | + | ||
192 | + rscr = &tgpio->rscr; | ||
193 | + rscr->name = "timb-gpio"; | ||
194 | + rscr->start = iomem->start; | ||
195 | + rscr->end = iomem->end; | ||
196 | + rscr->flags = IORESOURCE_MEM; | ||
197 | + | ||
198 | + err = request_resource(iomem, rscr); | ||
199 | + if (err) | ||
200 | + goto err_request; | ||
201 | + | ||
202 | + tgpio->membase = ioremap(rscr->start, resource_size(rscr)); | ||
203 | + if (!tgpio->membase) { | ||
204 | + err = -ENOMEM; | ||
205 | + goto err_ioremap; | ||
206 | + } | ||
207 | + | ||
208 | + gc = &tgpio->gpio; | ||
209 | + | ||
210 | + gc->label = "timbgpio"; | ||
211 | + gc->owner = THIS_MODULE; | ||
212 | + gc->direction_input = timbgpio_gpio_direction_input; | ||
213 | + gc->get = timbgpio_gpio_get; | ||
214 | + gc->direction_output = timbgpio_gpio_direction_output; | ||
215 | + gc->set = timbgpio_gpio_set; | ||
216 | + gc->dbg_show = NULL; | ||
217 | + gc->base = 0; | ||
218 | + gc->ngpio = TIMB_NR_GPIOS; | ||
219 | + gc->can_sleep = 0; | ||
220 | + | ||
221 | + err = gpiochip_add(gc); | ||
222 | + if (err) | ||
223 | + goto err_chipadd; | ||
224 | + | ||
225 | + platform_set_drvdata(dev, tgpio); | ||
226 | + | ||
227 | + /* register interrupt */ | ||
228 | + irq = platform_get_irq(dev, 0); | ||
229 | + if (irq < 0) | ||
230 | + goto err_get_irq; | ||
231 | + | ||
232 | + /* clear pending interrupts */ | ||
233 | + iowrite32(0xffffffff, tgpio->membase + TGPIOINT_CLR); | ||
234 | + iowrite32(0x0, tgpio->membase + TGPIOINT); | ||
235 | + | ||
236 | + /* request IRQ */ | ||
237 | + err = request_irq(irq, timbgpio_handleinterrupt, IRQF_SHARED, | ||
238 | + "timb-gpio", tgpio); | ||
239 | + if (err) { | ||
240 | + printk(KERN_ERR "timbgpio: Failed to request IRQ\n"); | ||
241 | + goto err_get_irq; | ||
242 | + } | ||
243 | + | ||
244 | + return err; | ||
245 | + | ||
246 | +err_get_irq: | ||
247 | + err = gpiochip_remove(&tgpio->gpio); | ||
248 | + if (err) | ||
249 | + printk(KERN_ERR "timbgpio: failed to remove gpio_chip\n"); | ||
250 | +err_chipadd: | ||
251 | + iounmap(tgpio->membase); | ||
252 | +err_ioremap: | ||
253 | + release_resource(&tgpio->rscr); | ||
254 | +err_request: | ||
255 | + kfree(tgpio); | ||
256 | +err_mem: | ||
257 | + printk(KERN_ERR "timberdale: Failed to register GPIOs: %d\n", err); | ||
258 | + | ||
259 | + return err; | ||
260 | +} | ||
261 | + | ||
262 | +static int timbgpio_remove(struct platform_device *dev) | ||
263 | +{ | ||
264 | + int err; | ||
265 | + struct timbgpio *tgpio = platform_get_drvdata(dev); | ||
266 | + | ||
267 | + /* disable interrupts */ | ||
268 | + iowrite32(0x0, tgpio->membase + TGPIOINT); | ||
269 | + | ||
270 | + free_irq(platform_get_irq(dev, 0), tgpio); | ||
271 | + err = gpiochip_remove(&tgpio->gpio); | ||
272 | + if (err) | ||
273 | + printk(KERN_ERR "timbgpio: failed to remove gpio_chip\n"); | ||
274 | + | ||
275 | + iounmap(tgpio->membase); | ||
276 | + release_resource(&tgpio->rscr); | ||
277 | + kfree(tgpio); | ||
278 | + | ||
279 | + return 0; | ||
280 | +} | ||
281 | + | ||
282 | +static struct platform_driver timbgpio_platform_driver = { | ||
283 | + .driver = { | ||
284 | + .name = "timb-gpio", | ||
285 | + .owner = THIS_MODULE, | ||
286 | + }, | ||
287 | + .probe = timbgpio_probe, | ||
288 | + .remove = timbgpio_remove, | ||
289 | +}; | ||
290 | + | ||
291 | +/*--------------------------------------------------------------------------*/ | ||
292 | + | ||
293 | +static int __init timbgpio_init(void) | ||
294 | +{ | ||
295 | + return platform_driver_register(&timbgpio_platform_driver); | ||
296 | +} | ||
297 | + | ||
298 | +static void __exit timbgpio_exit(void) | ||
299 | +{ | ||
300 | + platform_driver_unregister(&timbgpio_platform_driver); | ||
301 | +} | ||
302 | + | ||
303 | +module_init(timbgpio_init); | ||
304 | +module_exit(timbgpio_exit); | ||
305 | + | ||
306 | +MODULE_DESCRIPTION("Timberdale GPIO driver"); | ||
307 | +MODULE_LICENSE("GPL v2"); | ||
308 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
309 | +MODULE_ALIAS("platform:timb-gpio"); | ||
310 | + | ||
311 | diff -uNr linux-2.6.29-clean/drivers/gpio/timbgpio.h linux-2.6.29/drivers/gpio/timbgpio.h | ||
312 | --- linux-2.6.29-clean/drivers/gpio/timbgpio.h 1969-12-31 16:00:00.000000000 -0800 | ||
313 | +++ linux-2.6.29/drivers/gpio/timbgpio.h 2009-04-06 13:51:47.000000000 -0700 | ||
314 | @@ -0,0 +1,48 @@ | ||
315 | +/* | ||
316 | + * timbgpio.h timberdale FPGA GPIO driver defines | ||
317 | + * Copyright (c) 2009 Intel Corporation | ||
318 | + * | ||
319 | + * This program is free software; you can redistribute it and/or modify | ||
320 | + * it under the terms of the GNU General Public License version 2 as | ||
321 | + * published by the Free Software Foundation. | ||
322 | + * | ||
323 | + * This program is distributed in the hope that it will be useful, | ||
324 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
325 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
326 | + * GNU General Public License for more details. | ||
327 | + * | ||
328 | + * You should have received a copy of the GNU General Public License | ||
329 | + * along with this program; if not, write to the Free Software | ||
330 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
331 | + */ | ||
332 | + | ||
333 | +/* Supports: | ||
334 | + * Timberdale FPGA GPIO | ||
335 | + */ | ||
336 | + | ||
337 | +#ifndef _TIMBGPIO_H_ | ||
338 | +#define _TIMBGPIO_H_ | ||
339 | + | ||
340 | +#include <linux/mutex.h> | ||
341 | +#include <linux/gpio.h> | ||
342 | + | ||
343 | +#define TIMB_NR_GPIOS 16 | ||
344 | + | ||
345 | +#define TGPIOVAL 0 | ||
346 | +#define TGPIODIR 0x04 | ||
347 | +#define TGPIOINT 0x08 | ||
348 | +#define TGPIOINT_STATUS 0x0c | ||
349 | +#define TGPIOINT_PENDING 0x10 | ||
350 | +#define TGPIOINT_CLR 0x14 | ||
351 | +#define TGPIOFLK 0x18 | ||
352 | +#define TGPIOLVL 0x1c | ||
353 | + | ||
354 | +struct timbgpio { | ||
355 | + void __iomem *membase; | ||
356 | + struct resource rscr; | ||
357 | + struct mutex lock; /* mutual exclusion */ | ||
358 | + struct pci_dev *pdev; | ||
359 | + struct gpio_chip gpio; | ||
360 | +}; | ||
361 | + | ||
362 | +#endif | ||
363 | diff -uNr linux-2.6.29-clean/drivers/i2c/busses/i2c-ocores.c linux-2.6.29/drivers/i2c/busses/i2c-ocores.c | ||
364 | --- linux-2.6.29-clean/drivers/i2c/busses/i2c-ocores.c 2009-04-01 09:20:24.000000000 -0700 | ||
365 | +++ linux-2.6.29/drivers/i2c/busses/i2c-ocores.c 2009-04-06 13:51:47.000000000 -0700 | ||
366 | @@ -216,6 +216,7 @@ | ||
367 | struct ocores_i2c_platform_data *pdata; | ||
368 | struct resource *res, *res2; | ||
369 | int ret; | ||
370 | + u8 i; | ||
371 | |||
372 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
373 | if (!res) | ||
374 | @@ -271,6 +272,10 @@ | ||
375 | goto add_adapter_failed; | ||
376 | } | ||
377 | |||
378 | + /* add in known devices to the bus */ | ||
379 | + for (i = 0; i < pdata->num_devices; i++) | ||
380 | + i2c_new_device(&i2c->adap, pdata->devices + i); | ||
381 | + | ||
382 | return 0; | ||
383 | |||
384 | add_adapter_failed: | ||
385 | diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/Kconfig linux-2.6.29/drivers/input/touchscreen/Kconfig | ||
386 | --- linux-2.6.29-clean/drivers/input/touchscreen/Kconfig 2009-04-01 09:20:23.000000000 -0700 | ||
387 | +++ linux-2.6.29/drivers/input/touchscreen/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
388 | @@ -397,6 +397,17 @@ | ||
389 | To compile this driver as a module, choose M here: the | ||
390 | module will be called touchit213. | ||
391 | |||
392 | +config TOUCHSCREEN_TSC2003 | ||
393 | + tristate "TSC2003 based touchscreens" | ||
394 | + depends on I2C | ||
395 | + help | ||
396 | + Say Y here if you have a TSC2003 based touchscreen. | ||
397 | + | ||
398 | + If unsure, say N. | ||
399 | + | ||
400 | + To compile this driver as a module, choose M here: the | ||
401 | + module will be called tsc2003. | ||
402 | + | ||
403 | config TOUCHSCREEN_TSC2007 | ||
404 | tristate "TSC2007 based touchscreens" | ||
405 | depends on I2C | ||
406 | diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/Makefile linux-2.6.29/drivers/input/touchscreen/Makefile | ||
407 | --- linux-2.6.29-clean/drivers/input/touchscreen/Makefile 2009-04-01 09:20:23.000000000 -0700 | ||
408 | +++ linux-2.6.29/drivers/input/touchscreen/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
409 | @@ -25,6 +25,7 @@ | ||
410 | obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o | ||
411 | obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o | ||
412 | obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o | ||
413 | +obj-$(CONFIG_TOUCHSCREEN_TSC2003) += tsc2003.o | ||
414 | obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o | ||
415 | obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o | ||
416 | obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o | ||
417 | diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/tsc2003.c linux-2.6.29/drivers/input/touchscreen/tsc2003.c | ||
418 | --- linux-2.6.29-clean/drivers/input/touchscreen/tsc2003.c 1969-12-31 16:00:00.000000000 -0800 | ||
419 | +++ linux-2.6.29/drivers/input/touchscreen/tsc2003.c 2009-04-06 13:51:47.000000000 -0700 | ||
420 | @@ -0,0 +1,387 @@ | ||
421 | +/* | ||
422 | + * tsc2003.c Driver for TI TSC2003 touch screen controller | ||
423 | + * Copyright (c) 2009 Intel Corporation | ||
424 | + * | ||
425 | + * This program is free software; you can redistribute it and/or modify | ||
426 | + * it under the terms of the GNU General Public License version 2 as | ||
427 | + * published by the Free Software Foundation. | ||
428 | + * | ||
429 | + * This program is distributed in the hope that it will be useful, | ||
430 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
431 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
432 | + * GNU General Public License for more details. | ||
433 | + * | ||
434 | + * You should have received a copy of the GNU General Public License | ||
435 | + * along with this program; if not, write to the Free Software | ||
436 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
437 | + */ | ||
438 | + | ||
439 | +/* Supports: | ||
440 | + * TI TSC2003 | ||
441 | + * | ||
442 | + * Inspired by tsc2007, Copyright (c) 2008 MtekVision Co., Ltd. | ||
443 | + */ | ||
444 | +#include <linux/module.h> | ||
445 | +#include <linux/input.h> | ||
446 | +#include <linux/interrupt.h> | ||
447 | +#include <linux/i2c.h> | ||
448 | +#include <linux/i2c/tsc2007.h> | ||
449 | +#include <linux/kthread.h> | ||
450 | +#include <linux/semaphore.h> | ||
451 | + | ||
452 | +#define TSC2003_DRIVER_NAME "tsc2003" | ||
453 | + | ||
454 | +#define TS_POLL_PERIOD 20 /* ms delay between samples */ | ||
455 | + | ||
456 | +#define TSC2003_MEASURE_TEMP0 (0x0 << 4) | ||
457 | +#define TSC2003_MEASURE_AUX (0x2 << 4) | ||
458 | +#define TSC2003_MEASURE_TEMP1 (0x4 << 4) | ||
459 | +#define TSC2003_ACTIVATE_XN (0x8 << 4) | ||
460 | +#define TSC2003_ACTIVATE_YN (0x9 << 4) | ||
461 | +#define TSC2003_ACTIVATE_YP_XN (0xa << 4) | ||
462 | +#define TSC2003_SETUP (0xb << 4) | ||
463 | +#define TSC2003_MEASURE_X (0xc << 4) | ||
464 | +#define TSC2003_MEASURE_Y (0xd << 4) | ||
465 | +#define TSC2003_MEASURE_Z1 (0xe << 4) | ||
466 | +#define TSC2003_MEASURE_Z2 (0xf << 4) | ||
467 | + | ||
468 | +#define TSC2003_POWER_OFF_IRQ_EN (0x0 << 2) | ||
469 | +#define TSC2003_ADC_ON_IRQ_DIS0 (0x1 << 2) | ||
470 | +#define TSC2003_ADC_OFF_IRQ_EN (0x2 << 2) | ||
471 | +#define TSC2003_ADC_ON_IRQ_DIS1 (0x3 << 2) | ||
472 | + | ||
473 | +#define TSC2003_12BIT (0x0 << 1) | ||
474 | +#define TSC2003_8BIT (0x1 << 1) | ||
475 | + | ||
476 | +#define MAX_12BIT ((1 << 12) - 1) | ||
477 | + | ||
478 | +#define ADC_ON_12BIT (TSC2003_12BIT | TSC2003_ADC_ON_IRQ_DIS0) | ||
479 | + | ||
480 | +#define READ_Y (ADC_ON_12BIT | TSC2003_MEASURE_Y) | ||
481 | +#define READ_Z1 (ADC_ON_12BIT | TSC2003_MEASURE_Z1) | ||
482 | +#define READ_Z2 (ADC_ON_12BIT | TSC2003_MEASURE_Z2) | ||
483 | +#define READ_X (ADC_ON_12BIT | TSC2003_MEASURE_X) | ||
484 | +#define PWRDOWN (TSC2003_12BIT | TSC2003_POWER_OFF_IRQ_EN) | ||
485 | + | ||
486 | +struct ts_event { | ||
487 | + int x; | ||
488 | + int y; | ||
489 | + int z1, z2; | ||
490 | +}; | ||
491 | + | ||
492 | +struct tsc2003 { | ||
493 | + struct input_dev *input; | ||
494 | + char phys[32]; | ||
495 | + struct task_struct *task; | ||
496 | + struct ts_event tc; | ||
497 | + struct completion penirq_completion; | ||
498 | + | ||
499 | + struct i2c_client *client; | ||
500 | + | ||
501 | + u16 model; | ||
502 | + u16 x_plate_ohms; | ||
503 | + | ||
504 | + unsigned pendown; | ||
505 | +}; | ||
506 | + | ||
507 | +static inline int tsc2003_xfer(struct tsc2003 *tsc, u8 cmd) | ||
508 | +{ | ||
509 | + s32 data; | ||
510 | + u16 val; | ||
511 | + | ||
512 | + data = i2c_smbus_read_word_data(tsc->client, cmd); | ||
513 | + if (data < 0) { | ||
514 | + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); | ||
515 | + return data; | ||
516 | + } | ||
517 | + | ||
518 | + /* The protocol and raw data format from i2c interface: | ||
519 | + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P | ||
520 | + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. | ||
521 | + */ | ||
522 | + val = swab16(data) >> 4; | ||
523 | + | ||
524 | + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); | ||
525 | + | ||
526 | + return val; | ||
527 | +} | ||
528 | + | ||
529 | +static void tsc2003_send_event(void *tsc) | ||
530 | +{ | ||
531 | + struct tsc2003 *ts = tsc; | ||
532 | + struct input_dev *input = ts->input; | ||
533 | + u32 rt = 0; | ||
534 | + u16 x, y, z1, z2; | ||
535 | + | ||
536 | + x = ts->tc.x; | ||
537 | + y = ts->tc.y; | ||
538 | + z1 = ts->tc.z1; | ||
539 | + z2 = ts->tc.z2; | ||
540 | + | ||
541 | + /* range filtering */ | ||
542 | + if (x == MAX_12BIT) | ||
543 | + x = 0; | ||
544 | + | ||
545 | + if (likely(x && z1)) { | ||
546 | + /* compute touch pressure resistance using equation #1 */ | ||
547 | + rt = z2; | ||
548 | + rt -= z1; | ||
549 | + rt *= x; | ||
550 | + rt *= ts->x_plate_ohms; | ||
551 | + rt /= z1; | ||
552 | + rt = (rt + 2047) >> 12; | ||
553 | + } | ||
554 | + | ||
555 | + /* Sample found inconsistent by debouncing or pressure is beyond | ||
556 | + * the maximum. Don't report it to user space, repeat at least | ||
557 | + * once more the measurement | ||
558 | + */ | ||
559 | + if (rt > MAX_12BIT) | ||
560 | + return; | ||
561 | + | ||
562 | + /* NOTE: We can't rely on the pressure to determine the pen down | ||
563 | + * state, even this controller has a pressure sensor. The pressure | ||
564 | + * value can fluctuate for quite a while after lifting the pen and | ||
565 | + * in some cases may not even settle at the expected value. | ||
566 | + * | ||
567 | + * The only safe way to check for the pen up condition is in the | ||
568 | + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). | ||
569 | + */ | ||
570 | + if (rt) { | ||
571 | + if (!ts->pendown) { | ||
572 | + dev_dbg(&ts->client->dev, "DOWN\n"); | ||
573 | + | ||
574 | + input_report_key(input, BTN_TOUCH, 1); | ||
575 | + ts->pendown = 1; | ||
576 | + } | ||
577 | + | ||
578 | + input_report_abs(input, ABS_X, x); | ||
579 | + input_report_abs(input, ABS_Y, y); | ||
580 | + input_report_abs(input, ABS_PRESSURE, rt); | ||
581 | + | ||
582 | + input_sync(input); | ||
583 | + | ||
584 | + dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", | ||
585 | + x, y, rt); | ||
586 | + } else if (ts->pendown) { | ||
587 | + /* pen up */ | ||
588 | + dev_dbg(&ts->client->dev, "UP\n"); | ||
589 | + input_report_key(input, BTN_TOUCH, 0); | ||
590 | + input_report_abs(input, ABS_PRESSURE, 0); | ||
591 | + input_sync(input); | ||
592 | + | ||
593 | + ts->pendown = 0; | ||
594 | + } | ||
595 | +} | ||
596 | + | ||
597 | +static int tsc2003_power_off_irq_en(struct tsc2003 *tsc) | ||
598 | +{ | ||
599 | + /* power down */ | ||
600 | + return tsc2003_xfer(tsc, PWRDOWN); | ||
601 | +} | ||
602 | + | ||
603 | +static int tsc2003_read_values(struct tsc2003 *tsc) | ||
604 | +{ | ||
605 | + /* y- still on; turn on only y+ (and ADC) */ | ||
606 | + tsc->tc.y = tsc2003_xfer(tsc, READ_Y); | ||
607 | + if (tsc->tc.y < 0) | ||
608 | + return tsc->tc.y; | ||
609 | + | ||
610 | + /* turn y- off, x+ on, then leave in lowpower */ | ||
611 | + tsc->tc.x = tsc2003_xfer(tsc, READ_X); | ||
612 | + if (tsc->tc.x < 0) | ||
613 | + return tsc->tc.x; | ||
614 | + | ||
615 | + /* turn y+ off, x- on; we'll use formula #1 */ | ||
616 | + tsc->tc.z1 = tsc2003_xfer(tsc, READ_Z1); | ||
617 | + if (tsc->tc.z1 < 0) | ||
618 | + return tsc->tc.z1; | ||
619 | + | ||
620 | + tsc->tc.z2 = tsc2003_xfer(tsc, READ_Z2); | ||
621 | + if (tsc->tc.z2 < 0) | ||
622 | + return tsc->tc.z2; | ||
623 | + | ||
624 | + return 0; | ||
625 | +} | ||
626 | + | ||
627 | + | ||
628 | +static irqreturn_t tsc2003_irq(int irq, void *handle) | ||
629 | +{ | ||
630 | + struct tsc2003 *ts = handle; | ||
631 | + | ||
632 | + /* do not call the synced version -> deadlock */ | ||
633 | + disable_irq_nosync(irq); | ||
634 | + /* signal the thread to continue */ | ||
635 | + complete(&ts->penirq_completion); | ||
636 | + | ||
637 | + return IRQ_HANDLED; | ||
638 | +} | ||
639 | + | ||
640 | +static int tsc2003_thread(void *d) | ||
641 | +{ | ||
642 | + struct tsc2003 *ts = (struct tsc2003 *)d; | ||
643 | + int ret; | ||
644 | + | ||
645 | + allow_signal(SIGKILL); | ||
646 | + | ||
647 | + while (!signal_pending(current)) { | ||
648 | + /* power down and wait for interrupt */ | ||
649 | + do { | ||
650 | + /* loop because the I2C bus might be busy */ | ||
651 | + ret = msleep_interruptible(TS_POLL_PERIOD); | ||
652 | + if (!ret) | ||
653 | + ret = tsc2003_power_off_irq_en(ts); | ||
654 | + } while (ret == -EAGAIN && !signal_pending(current)); | ||
655 | + | ||
656 | + if (signal_pending(current)) | ||
657 | + break; | ||
658 | + | ||
659 | + ret = wait_for_completion_interruptible(&ts->penirq_completion); | ||
660 | + if (!ret) { | ||
661 | + int first = 1; | ||
662 | + /* got IRQ, start poll, until pen is up */ | ||
663 | + while (!ret && !signal_pending(current) | ||
664 | + && (first || ts->pendown)) { | ||
665 | + ret = tsc2003_read_values(ts); | ||
666 | + if (!ret) | ||
667 | + tsc2003_send_event(ts); | ||
668 | + ret = msleep_interruptible(TS_POLL_PERIOD); | ||
669 | + first = 0; | ||
670 | + } | ||
671 | + | ||
672 | + /* we re enable the interrupt */ | ||
673 | + if (!signal_pending(current)) | ||
674 | + enable_irq(ts->client->irq); | ||
675 | + } | ||
676 | + } | ||
677 | + | ||
678 | + return 0; | ||
679 | +} | ||
680 | + | ||
681 | +static int tsc2003_probe(struct i2c_client *client, | ||
682 | + const struct i2c_device_id *id) | ||
683 | +{ | ||
684 | + struct tsc2003 *ts; | ||
685 | + struct tsc2007_platform_data *pdata = client->dev.platform_data; | ||
686 | + struct input_dev *input_dev; | ||
687 | + int err; | ||
688 | + | ||
689 | + if (!pdata) { | ||
690 | + dev_err(&client->dev, "platform data is required!\n"); | ||
691 | + return -EINVAL; | ||
692 | + } | ||
693 | + | ||
694 | + if (!i2c_check_functionality(client->adapter, | ||
695 | + I2C_FUNC_SMBUS_READ_WORD_DATA)) | ||
696 | + return -EIO; | ||
697 | + | ||
698 | + ts = kzalloc(sizeof(struct tsc2003), GFP_KERNEL); | ||
699 | + input_dev = input_allocate_device(); | ||
700 | + if (!ts || !input_dev) { | ||
701 | + err = -ENOMEM; | ||
702 | + goto err_free_mem; | ||
703 | + } | ||
704 | + | ||
705 | + ts->client = client; | ||
706 | + i2c_set_clientdata(client, ts); | ||
707 | + | ||
708 | + ts->input = input_dev; | ||
709 | + | ||
710 | + ts->model = pdata->model; | ||
711 | + ts->x_plate_ohms = pdata->x_plate_ohms; | ||
712 | + | ||
713 | + snprintf(ts->phys, sizeof(ts->phys), | ||
714 | + "%s/input0", dev_name(&client->dev)); | ||
715 | + | ||
716 | + input_dev->name = TSC2003_DRIVER_NAME" Touchscreen"; | ||
717 | + input_dev->phys = ts->phys; | ||
718 | + input_dev->id.bustype = BUS_I2C; | ||
719 | + | ||
720 | + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | ||
721 | + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
722 | + | ||
723 | + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); | ||
724 | + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | ||
725 | + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | ||
726 | + | ||
727 | + init_completion(&ts->penirq_completion); | ||
728 | + | ||
729 | + ts->task = kthread_run(tsc2003_thread, ts, TSC2003_DRIVER_NAME); | ||
730 | + if (IS_ERR(ts->task)) { | ||
731 | + err = PTR_ERR(ts->task); | ||
732 | + goto err_free_mem; | ||
733 | + } | ||
734 | + | ||
735 | + err = request_irq(client->irq, tsc2003_irq, 0, | ||
736 | + client->dev.driver->name, ts); | ||
737 | + if (err < 0) { | ||
738 | + dev_err(&client->dev, "irq %d busy?\n", client->irq); | ||
739 | + goto err_free_thread; | ||
740 | + } | ||
741 | + | ||
742 | + err = input_register_device(input_dev); | ||
743 | + if (err) | ||
744 | + goto err_free_irq; | ||
745 | + | ||
746 | + dev_info(&client->dev, "registered with irq (%d)\n", client->irq); | ||
747 | + | ||
748 | + return 0; | ||
749 | + | ||
750 | + err_free_irq: | ||
751 | + free_irq(client->irq, ts); | ||
752 | + err_free_thread: | ||
753 | + kthread_stop(ts->task); | ||
754 | + err_free_mem: | ||
755 | + input_free_device(input_dev); | ||
756 | + kfree(ts); | ||
757 | + return err; | ||
758 | +} | ||
759 | + | ||
760 | +static int tsc2003_remove(struct i2c_client *client) | ||
761 | +{ | ||
762 | + struct tsc2003 *ts = i2c_get_clientdata(client); | ||
763 | + | ||
764 | + free_irq(client->irq, ts); | ||
765 | + send_sig(SIGKILL, ts->task, 1); | ||
766 | + kthread_stop(ts->task); | ||
767 | + input_unregister_device(ts->input); | ||
768 | + kfree(ts); | ||
769 | + | ||
770 | + return 0; | ||
771 | +} | ||
772 | + | ||
773 | +static struct i2c_device_id tsc2003_idtable[] = { | ||
774 | + { TSC2003_DRIVER_NAME, 0 }, | ||
775 | + { } | ||
776 | +}; | ||
777 | + | ||
778 | +MODULE_DEVICE_TABLE(i2c, tsc2003_idtable); | ||
779 | + | ||
780 | +static struct i2c_driver tsc2003_driver = { | ||
781 | + .driver = { | ||
782 | + .owner = THIS_MODULE, | ||
783 | + .name = TSC2003_DRIVER_NAME, | ||
784 | + .bus = &i2c_bus_type, | ||
785 | + }, | ||
786 | + .id_table = tsc2003_idtable, | ||
787 | + .probe = tsc2003_probe, | ||
788 | + .remove = tsc2003_remove, | ||
789 | +}; | ||
790 | + | ||
791 | +static int __init tsc2003_init(void) | ||
792 | +{ | ||
793 | + return i2c_add_driver(&tsc2003_driver); | ||
794 | +} | ||
795 | + | ||
796 | +static void __exit tsc2003_exit(void) | ||
797 | +{ | ||
798 | + i2c_del_driver(&tsc2003_driver); | ||
799 | +} | ||
800 | + | ||
801 | +module_init(tsc2003_init); | ||
802 | +module_exit(tsc2003_exit); | ||
803 | + | ||
804 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
805 | +MODULE_DESCRIPTION("TSC2003 TouchScreen Driver"); | ||
806 | +MODULE_LICENSE("GPL v2"); | ||
807 | + | ||
808 | diff -uNr linux-2.6.29-clean/drivers/media/video/adv7180.c linux-2.6.29/drivers/media/video/adv7180.c | ||
809 | --- linux-2.6.29-clean/drivers/media/video/adv7180.c 1969-12-31 16:00:00.000000000 -0800 | ||
810 | +++ linux-2.6.29/drivers/media/video/adv7180.c 2009-04-06 13:51:47.000000000 -0700 | ||
811 | @@ -0,0 +1,361 @@ | ||
812 | +/* | ||
813 | + * adv7180.c Analog Devices ADV7180 video decoder driver | ||
814 | + * Copyright (c) 2009 Intel Corporation | ||
815 | + * | ||
816 | + * This program is free software; you can redistribute it and/or modify | ||
817 | + * it under the terms of the GNU General Public License version 2 as | ||
818 | + * published by the Free Software Foundation. | ||
819 | + * | ||
820 | + * This program is distributed in the hope that it will be useful, | ||
821 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
822 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
823 | + * GNU General Public License for more details. | ||
824 | + * | ||
825 | + * You should have received a copy of the GNU General Public License | ||
826 | + * along with this program; if not, write to the Free Software | ||
827 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
828 | + */ | ||
829 | + | ||
830 | +#include <linux/module.h> | ||
831 | +#include <linux/init.h> | ||
832 | +#include <linux/interrupt.h> | ||
833 | +#include <linux/delay.h> | ||
834 | +#include <linux/errno.h> | ||
835 | +#include <linux/fs.h> | ||
836 | +#include <linux/kernel.h> | ||
837 | +#include <linux/major.h> | ||
838 | +#include <linux/slab.h> | ||
839 | +#include <linux/mm.h> | ||
840 | +#include <linux/signal.h> | ||
841 | +#include <linux/types.h> | ||
842 | +#include <linux/io.h> | ||
843 | +#include <asm/pgtable.h> | ||
844 | +#include <asm/page.h> | ||
845 | +#include <linux/uaccess.h> | ||
846 | + | ||
847 | +#include <linux/i2c-ocores.h> | ||
848 | +#include <linux/platform_device.h> | ||
849 | +#include <linux/i2c.h> | ||
850 | +#include <linux/i2c-id.h> | ||
851 | +#include <linux/videodev.h> | ||
852 | +#include <linux/video_decoder.h> | ||
853 | +#include <media/v4l2-ioctl.h> | ||
854 | +#include <media/adv7180.h> | ||
855 | + | ||
856 | + | ||
857 | +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); | ||
858 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
859 | +MODULE_LICENSE("GPL v2"); | ||
860 | + | ||
861 | +static inline int adv7180_write(struct i2c_client *client, | ||
862 | + u8 reg, u8 value) | ||
863 | +{ | ||
864 | + struct adv7180 *decoder = i2c_get_clientdata(client); | ||
865 | + | ||
866 | + decoder->reg[reg] = value; | ||
867 | + return i2c_smbus_write_byte_data(client, reg, value); | ||
868 | +} | ||
869 | + | ||
870 | +static inline int adv7180_read(struct i2c_client *client, u8 reg) | ||
871 | +{ | ||
872 | + return i2c_smbus_read_byte_data(client, reg); | ||
873 | +} | ||
874 | + | ||
875 | +static int adv7180_write_block(struct i2c_client *client, | ||
876 | + const u8 *data, unsigned int len) | ||
877 | +{ | ||
878 | + int ret = -1; | ||
879 | + u8 reg; | ||
880 | + | ||
881 | + /* the adv7180 has an autoincrement function, use it if | ||
882 | + * the adapter understands raw I2C */ | ||
883 | + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
884 | + /* do raw I2C, not smbus compatible */ | ||
885 | + struct adv7180 *decoder = i2c_get_clientdata(client); | ||
886 | + u8 block_data[32]; | ||
887 | + int block_len; | ||
888 | + | ||
889 | + while (len >= 2) { | ||
890 | + block_len = 0; | ||
891 | + reg = data[0]; | ||
892 | + block_data[block_len++] = reg; | ||
893 | + do { | ||
894 | + block_data[block_len++] = | ||
895 | + decoder->reg[reg++] = data[1]; | ||
896 | + len -= 2; | ||
897 | + data += 2; | ||
898 | + } while (len >= 2 && data[0] == reg && | ||
899 | + block_len < 32); | ||
900 | + | ||
901 | + ret = i2c_master_send(client, block_data, block_len); | ||
902 | + if (ret < 0) | ||
903 | + break; | ||
904 | + } | ||
905 | + } else { | ||
906 | + /* do some slow I2C emulation kind of thing */ | ||
907 | + while (len >= 2) { | ||
908 | + reg = *data++; | ||
909 | + ret = adv7180_write(client, reg, *data++); | ||
910 | + if (ret < 0) | ||
911 | + break; | ||
912 | + | ||
913 | + len -= 2; | ||
914 | + } | ||
915 | + } | ||
916 | + | ||
917 | + return ret; | ||
918 | +} | ||
919 | +#ifdef CONFIG_MFD_TIMBERDALE | ||
920 | +static irqreturn_t adv7180_irq(int irq, void *dev_id) | ||
921 | +{ | ||
922 | + struct adv7180 *decoder = (struct adv7180 *) dev_id; | ||
923 | + | ||
924 | + /* Activate access to sub-regs */ | ||
925 | + adv7180_write(decoder->client, ADV7180_ADI_CTRL, ADI_ENABLE); | ||
926 | + | ||
927 | + /* TODO: implement a real interrupt handler | ||
928 | + * for now just | ||
929 | + * clear all four regs | ||
930 | + */ | ||
931 | + adv7180_write_block(decoder->client, reset_icr, sizeof(reset_icr)); | ||
932 | + | ||
933 | + return IRQ_HANDLED; | ||
934 | +} | ||
935 | +#endif | ||
936 | +static int adv7180_command(struct i2c_client *client, | ||
937 | + unsigned int cmd, void *arg) | ||
938 | +{ | ||
939 | + struct adv7180 *decoder = i2c_get_clientdata(client); | ||
940 | + int *iarg = (int *)arg; | ||
941 | + int status; | ||
942 | + | ||
943 | + switch (cmd) { | ||
944 | + | ||
945 | + case DECODER_INIT: | ||
946 | + adv7180_write(client, 0x0f, 0x80); /* Reset */ | ||
947 | + break; | ||
948 | + | ||
949 | + case DECODER_GET_CAPABILITIES: | ||
950 | + { | ||
951 | + struct video_decoder_capability *cap = arg; | ||
952 | + cap->flags = VIDEO_DECODER_PAL | | ||
953 | + VIDEO_DECODER_NTSC | | ||
954 | + VIDEO_DECODER_SECAM | | ||
955 | + VIDEO_DECODER_AUTO; | ||
956 | + cap->inputs = 3; | ||
957 | + cap->outputs = 1; | ||
958 | + } | ||
959 | + break; | ||
960 | + | ||
961 | + case DECODER_GET_STATUS: | ||
962 | + { | ||
963 | + *iarg = 0; | ||
964 | + status = adv7180_read(client, ADV7180_SR); | ||
965 | + if ((status & ADV7180_STATUS_PAL)) | ||
966 | + *iarg = (*iarg | DECODER_STATUS_PAL); | ||
967 | + | ||
968 | + if ((status & ADV7180_STATUS_NTSC)) | ||
969 | + *iarg = (*iarg | DECODER_STATUS_NTSC); | ||
970 | + | ||
971 | + if ((status & ADV7180_STATUS_SECAM)) | ||
972 | + *iarg = (*iarg | DECODER_STATUS_SECAM); | ||
973 | + } | ||
974 | + break; | ||
975 | + | ||
976 | + case DECODER_SET_NORM: | ||
977 | + { | ||
978 | + int v = *(int *) arg; | ||
979 | + if (decoder->norm != v) { | ||
980 | + decoder->norm = v; | ||
981 | + switch (v) { | ||
982 | + case VIDEO_MODE_NTSC: | ||
983 | + adv7180_write(client, ADV7180_IN_CTRL, 0x40); | ||
984 | + break; | ||
985 | + case VIDEO_MODE_PAL: | ||
986 | + adv7180_write(client, ADV7180_IN_CTRL, 0x70); | ||
987 | + break; | ||
988 | + case VIDEO_MODE_SECAM: | ||
989 | + adv7180_write(client, ADV7180_IN_CTRL, 0x90); | ||
990 | + break; | ||
991 | + case VIDEO_MODE_AUTO: | ||
992 | + adv7180_write(client, ADV7180_IN_CTRL, 0x00); | ||
993 | + break; | ||
994 | + default: | ||
995 | + return -EPERM; | ||
996 | + } | ||
997 | + } | ||
998 | + } | ||
999 | + break; | ||
1000 | + | ||
1001 | + case DECODER_SET_INPUT: | ||
1002 | + { | ||
1003 | + int v = *(int *) arg; | ||
1004 | + if (decoder->input != v) { | ||
1005 | + decoder->input = v; | ||
1006 | + | ||
1007 | + switch (v) { | ||
1008 | + case CVBS: | ||
1009 | + adv7180_write_block(client, init_cvbs_64, | ||
1010 | + sizeof(init_cvbs_64)); | ||
1011 | + break; | ||
1012 | + case SVIDEO: | ||
1013 | + adv7180_write_block(client, init_svideo_64, | ||
1014 | + sizeof(init_svideo_64)); | ||
1015 | + break; | ||
1016 | + case YPbPr: | ||
1017 | + adv7180_write_block(client, init_ypbpr_64, | ||
1018 | + sizeof(init_ypbpr_64)); | ||
1019 | + break; | ||
1020 | + default: | ||
1021 | + return -EINVAL; | ||
1022 | + } | ||
1023 | + } | ||
1024 | + } | ||
1025 | + break; | ||
1026 | + | ||
1027 | + case DECODER_SET_OUTPUT: | ||
1028 | + { | ||
1029 | + } | ||
1030 | + break; | ||
1031 | + | ||
1032 | + case DECODER_ENABLE_OUTPUT: | ||
1033 | + { | ||
1034 | + } | ||
1035 | + break; | ||
1036 | + | ||
1037 | + case DECODER_SET_PICTURE: | ||
1038 | + { | ||
1039 | + } | ||
1040 | + break; | ||
1041 | + | ||
1042 | + case DECODER_DUMP: | ||
1043 | + { | ||
1044 | + adv7180_write(client, 1, 0x88); | ||
1045 | + } | ||
1046 | + break; | ||
1047 | + | ||
1048 | + default: | ||
1049 | + return -EINVAL; | ||
1050 | + } | ||
1051 | + return 0; | ||
1052 | +} | ||
1053 | + | ||
1054 | +/* ----------------------------------------------------------------------- */ | ||
1055 | + | ||
1056 | +/* | ||
1057 | + * Generic i2c probe | ||
1058 | + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | ||
1059 | + */ | ||
1060 | +static unsigned short normal_i2c[] = { | ||
1061 | + 0x40 >> 1, 0x41 >> 1, | ||
1062 | + I2C_ADV7180 >> 1, 0x43 >> 1, | ||
1063 | + I2C_CLIENT_END | ||
1064 | +}; | ||
1065 | + | ||
1066 | +I2C_CLIENT_INSMOD; | ||
1067 | + | ||
1068 | +static int adv7180_detect(struct i2c_client *client, int kind, | ||
1069 | + struct i2c_board_info *info) | ||
1070 | +{ | ||
1071 | + struct i2c_adapter *adapter = client->adapter; | ||
1072 | + | ||
1073 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | ||
1074 | + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | ||
1075 | + return -ENODEV; | ||
1076 | + | ||
1077 | + /* Is chip alive ? */ | ||
1078 | + if (adv7180_read(client, 0x11) != 0x1b) | ||
1079 | + return -ENODEV; | ||
1080 | + | ||
1081 | + strlcpy(info->type, DRIVER_NAME, I2C_NAME_SIZE); | ||
1082 | + | ||
1083 | + return 0; | ||
1084 | +} | ||
1085 | + | ||
1086 | +static int adv7180_probe(struct i2c_client *client, | ||
1087 | + const struct i2c_device_id *id) | ||
1088 | +{ | ||
1089 | + int err = 0; | ||
1090 | + struct adv7180 *decoder; | ||
1091 | + | ||
1092 | + printk(KERN_INFO DRIVER_NAME" chip found @ 0x%x (%s)\n", | ||
1093 | + client->addr << 1, client->adapter->name); | ||
1094 | + | ||
1095 | + decoder = kzalloc(sizeof(struct adv7180), GFP_KERNEL); | ||
1096 | + if (decoder == NULL) | ||
1097 | + return -ENOMEM; | ||
1098 | + | ||
1099 | + decoder->norm = VIDEO_MODE_PAL | VIDEO_MODE_NTSC | | ||
1100 | + VIDEO_MODE_SECAM | | ||
1101 | + VIDEO_MODE_AUTO; | ||
1102 | + decoder->input = CVBS; | ||
1103 | + decoder->enable = 1; | ||
1104 | + decoder->client = client; | ||
1105 | + i2c_set_clientdata(client, decoder); | ||
1106 | +#ifdef CONFIG_MFD_TIMBERDALE | ||
1107 | + err = request_irq(client->irq, adv7180_irq, 0, | ||
1108 | + client->dev.driver->name, decoder); | ||
1109 | + if (err < 0) { | ||
1110 | + dev_err(&client->dev, "irq %d busy?\n", client->irq); | ||
1111 | + goto err_free_dec; | ||
1112 | + } | ||
1113 | + dev_info(&client->dev, "registered with irq (%d)\n", client->irq); | ||
1114 | +#endif | ||
1115 | + adv7180_command(client, DECODER_INIT, NULL); /* Reset */ | ||
1116 | + | ||
1117 | + return 0; | ||
1118 | +#ifdef CONFIG_MFD_TIMBERDALE | ||
1119 | +err_free_dec: | ||
1120 | + kfree(decoder); | ||
1121 | + | ||
1122 | + return err; | ||
1123 | +#endif | ||
1124 | +} | ||
1125 | + | ||
1126 | +static int adv7180_remove(struct i2c_client *client) | ||
1127 | +{ | ||
1128 | + struct adv7180 *decoder = i2c_get_clientdata(client); | ||
1129 | +#ifdef CONFIG_MFD_TIMBERDALE | ||
1130 | + free_irq(client->irq, decoder); | ||
1131 | +#endif | ||
1132 | + kfree(decoder); | ||
1133 | + return 0; | ||
1134 | +} | ||
1135 | + | ||
1136 | +/* ----------------------------------------------------------------------- */ | ||
1137 | +static const struct i2c_device_id adv7180_id[] = { | ||
1138 | + { DRIVER_NAME, 0 }, | ||
1139 | + { } | ||
1140 | +}; | ||
1141 | +MODULE_DEVICE_TABLE(i2c, adv7180_id); | ||
1142 | + | ||
1143 | +static struct i2c_driver i2c_driver_adv7180 = { | ||
1144 | + .driver = { | ||
1145 | + .owner = THIS_MODULE, | ||
1146 | + .name = DRIVER_NAME, | ||
1147 | + .bus = &i2c_bus_type, | ||
1148 | + }, | ||
1149 | + | ||
1150 | + .id_table = adv7180_id, | ||
1151 | + .probe = adv7180_probe, | ||
1152 | + .remove = adv7180_remove, | ||
1153 | + | ||
1154 | + .class = 0xffffffff, | ||
1155 | + .detect = adv7180_detect, | ||
1156 | + .address_data = &addr_data, | ||
1157 | + | ||
1158 | + .command = adv7180_command, | ||
1159 | +}; | ||
1160 | + | ||
1161 | +static int __init adv7180_init(void) | ||
1162 | +{ | ||
1163 | + return i2c_add_driver(&i2c_driver_adv7180); | ||
1164 | +} | ||
1165 | + | ||
1166 | +static void __exit adv7180_exit(void) | ||
1167 | +{ | ||
1168 | + i2c_del_driver(&i2c_driver_adv7180); | ||
1169 | +} | ||
1170 | + | ||
1171 | +module_init(adv7180_init); | ||
1172 | +module_exit(adv7180_exit); | ||
1173 | diff -uNr linux-2.6.29-clean/drivers/media/video/Kconfig linux-2.6.29/drivers/media/video/Kconfig | ||
1174 | --- linux-2.6.29-clean/drivers/media/video/Kconfig 2009-04-01 09:20:24.000000000 -0700 | ||
1175 | +++ linux-2.6.29/drivers/media/video/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
1176 | @@ -251,6 +251,15 @@ | ||
1177 | |||
1178 | comment "Video decoders" | ||
1179 | |||
1180 | +config VIDEO_ADV7180 | ||
1181 | + tristate "Analog Devices ADV7180 decoder" | ||
1182 | + depends on VIDEO_V4L1 && I2C | ||
1183 | + ---help--- | ||
1184 | + Support for the Analog Devices ADV7180 video decoder. | ||
1185 | + | ||
1186 | + To compile this driver as a module, choose M here: the | ||
1187 | + module will be called adv7180. | ||
1188 | + | ||
1189 | config VIDEO_BT819 | ||
1190 | tristate "BT819A VideoStream decoder" | ||
1191 | depends on VIDEO_V4L1 && I2C | ||
1192 | @@ -800,6 +809,12 @@ | ||
1193 | ---help--- | ||
1194 | This is a v4l2 driver for the TI OMAP2 camera capture interface | ||
1195 | |||
1196 | +config VIDEO_TIMBERDALE | ||
1197 | + tristate "Support for timberdale Video In/LogiWIN" | ||
1198 | + depends on VIDEO_V4L2 && MFD_TIMBERDALE_DMA | ||
1199 | + ---help--- | ||
1200 | + Add support for the Video In peripherial of the timberdale FPGA. | ||
1201 | + | ||
1202 | # | ||
1203 | # USB Multimedia device configuration | ||
1204 | # | ||
1205 | diff -uNr linux-2.6.29-clean/drivers/media/video/Makefile linux-2.6.29/drivers/media/video/Makefile | ||
1206 | --- linux-2.6.29-clean/drivers/media/video/Makefile 2009-04-01 09:20:24.000000000 -0700 | ||
1207 | +++ linux-2.6.29/drivers/media/video/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
1208 | @@ -52,6 +52,7 @@ | ||
1209 | obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o | ||
1210 | obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o | ||
1211 | obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o | ||
1212 | +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o | ||
1213 | obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o | ||
1214 | obj-$(CONFIG_VIDEO_BT819) += bt819.o | ||
1215 | obj-$(CONFIG_VIDEO_BT856) += bt856.o | ||
1216 | @@ -148,6 +149,8 @@ | ||
1217 | |||
1218 | obj-$(CONFIG_VIDEO_AU0828) += au0828/ | ||
1219 | |||
1220 | +obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o | ||
1221 | + | ||
1222 | obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ | ||
1223 | |||
1224 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core | ||
1225 | diff -uNr linux-2.6.29-clean/drivers/media/video/timblogiw.c linux-2.6.29/drivers/media/video/timblogiw.c | ||
1226 | --- linux-2.6.29-clean/drivers/media/video/timblogiw.c 1969-12-31 16:00:00.000000000 -0800 | ||
1227 | +++ linux-2.6.29/drivers/media/video/timblogiw.c 2009-04-06 13:51:47.000000000 -0700 | ||
1228 | @@ -0,0 +1,930 @@ | ||
1229 | +/* | ||
1230 | + * timblogiw.c timberdale FPGA LogiWin Video In driver | ||
1231 | + * Copyright (c) 2009 Intel Corporation | ||
1232 | + * | ||
1233 | + * This program is free software; you can redistribute it and/or modify | ||
1234 | + * it under the terms of the GNU General Public License version 2 as | ||
1235 | + * published by the Free Software Foundation. | ||
1236 | + * | ||
1237 | + * This program is distributed in the hope that it will be useful, | ||
1238 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1239 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1240 | + * GNU General Public License for more details. | ||
1241 | + * | ||
1242 | + * You should have received a copy of the GNU General Public License | ||
1243 | + * along with this program; if not, write to the Free Software | ||
1244 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
1245 | + */ | ||
1246 | + | ||
1247 | +/* Supports: | ||
1248 | + * Timberdale FPGA LogiWin Video In | ||
1249 | + */ | ||
1250 | + | ||
1251 | +#include <linux/list.h> | ||
1252 | +#include <linux/version.h> | ||
1253 | +#include <linux/module.h> | ||
1254 | +#include <linux/pci.h> | ||
1255 | +#include <linux/dma-mapping.h> | ||
1256 | +#include <media/v4l2-common.h> | ||
1257 | +#include <media/v4l2-ioctl.h> | ||
1258 | +#include <linux/platform_device.h> | ||
1259 | +#include <linux/interrupt.h> | ||
1260 | +#include "timblogiw.h" | ||
1261 | +#include <linux/mfd/timbdma.h> | ||
1262 | + | ||
1263 | + | ||
1264 | +#define TIMBLOGIW_CTRL 0x40 | ||
1265 | + | ||
1266 | +#define TIMBLOGIW_H_SCALE 0x20 | ||
1267 | +#define TIMBLOGIW_V_SCALE 0x28 | ||
1268 | + | ||
1269 | +#define TIMBLOGIW_X_CROP 0x58 | ||
1270 | +#define TIMBLOGIW_Y_CROP 0x60 | ||
1271 | + | ||
1272 | +#define TIMBLOGIW_W_CROP 0x00 | ||
1273 | +#define TIMBLOGIW_H_CROP 0x08 | ||
1274 | + | ||
1275 | +#define TIMBLOGIW_VERSION_CODE 0x02 | ||
1276 | + | ||
1277 | +#define TIMBLOGIW_FRAME 0x10 | ||
1278 | +#define TIMBLOGIW_DROP 0x20 | ||
1279 | + | ||
1280 | +#define TIMBLOGIW_BUF 0x04 | ||
1281 | +#define TIMBLOGIW_TBI 0x2c | ||
1282 | +#define TIMBLOGIW_BPL 0x30 | ||
1283 | + | ||
1284 | +#define dbg(...) | ||
1285 | + | ||
1286 | +const struct timblogiw_tvnorm timblogiw_tvnorms[] = { | ||
1287 | + { | ||
1288 | + .v4l2_id = V4L2_STD_PAL, | ||
1289 | + .name = "PAL", | ||
1290 | + .swidth = 720, | ||
1291 | + .sheight = 576 | ||
1292 | + }, | ||
1293 | + { | ||
1294 | + .v4l2_id = V4L2_STD_NTSC_M, | ||
1295 | + .name = "NTSC", | ||
1296 | + .swidth = 720, | ||
1297 | + .sheight = 480 | ||
1298 | + } | ||
1299 | +}; | ||
1300 | + | ||
1301 | +static void timblogiw_handleframe(unsigned long arg) | ||
1302 | +{ | ||
1303 | + struct timblogiw_frame *f; | ||
1304 | + struct timblogiw *lw = (struct timblogiw *)arg; | ||
1305 | + | ||
1306 | + spin_lock_bh(&lw->queue_lock); | ||
1307 | + if (!list_empty(&lw->inqueue)) { | ||
1308 | + /* put the entry in the outqueue */ | ||
1309 | + f = list_entry(lw->inqueue.next, struct timblogiw_frame, frame); | ||
1310 | + | ||
1311 | + /* copy data from the DMA buffer */ | ||
1312 | + memcpy(f->bufmem, lw->dma.filled->buf, f->buf.length); | ||
1313 | + /* buffer consumed */ | ||
1314 | + lw->dma.filled = NULL; | ||
1315 | + | ||
1316 | + do_gettimeofday(&f->buf.timestamp); | ||
1317 | + f->buf.sequence = ++lw->frame_count; | ||
1318 | + f->buf.field = V4L2_FIELD_NONE; | ||
1319 | + f->state = F_DONE; | ||
1320 | + f->buf.bytesused = lw->frame_size; | ||
1321 | + list_move_tail(&f->frame, &lw->outqueue); | ||
1322 | + /* wake up any waiter */ | ||
1323 | + wake_up(&lw->wait_frame); | ||
1324 | + } | ||
1325 | + spin_unlock_bh(&lw->queue_lock); | ||
1326 | +} | ||
1327 | + | ||
1328 | +static int timblogiw_isr(u32 flag, void *pdev) | ||
1329 | +{ | ||
1330 | + struct timblogiw *lw = (struct timblogiw *)pdev; | ||
1331 | + | ||
1332 | + if (!lw->dma.filled) { | ||
1333 | + /* no stored transfer so far, store this, and flip to next */ | ||
1334 | + lw->dma.filled = lw->dma.transfer + lw->dma.curr; | ||
1335 | + lw->dma.curr = !lw->dma.curr; | ||
1336 | + } | ||
1337 | + | ||
1338 | + if (lw->stream == STREAM_ON) | ||
1339 | + timb_start_dma(DMA_IRQ_VIDEO_RX, | ||
1340 | + lw->dma.transfer[lw->dma.curr].handle, lw->frame_size, | ||
1341 | + lw->bytesperline); | ||
1342 | + | ||
1343 | + if (flag & DMA_IRQ_VIDEO_DROP) | ||
1344 | + dbg("%s: frame dropped\n", __func__); | ||
1345 | + if (flag & DMA_IRQ_VIDEO_RX) { | ||
1346 | + dbg("%s: frame RX\n", __func__); | ||
1347 | + tasklet_schedule(&lw->tasklet); | ||
1348 | + } | ||
1349 | + return 0; | ||
1350 | +} | ||
1351 | + | ||
1352 | +static void timblogiw_empty_framequeues(struct timblogiw *lw) | ||
1353 | +{ | ||
1354 | + u32 i; | ||
1355 | + | ||
1356 | + dbg("%s\n", __func__); | ||
1357 | + | ||
1358 | + INIT_LIST_HEAD(&lw->inqueue); | ||
1359 | + INIT_LIST_HEAD(&lw->outqueue); | ||
1360 | + | ||
1361 | + for (i = 0; i < lw->num_frames; i++) { | ||
1362 | + lw->frame[i].state = F_UNUSED; | ||
1363 | + lw->frame[i].buf.bytesused = 0; | ||
1364 | + } | ||
1365 | +} | ||
1366 | + | ||
1367 | +u32 timblogiw_request_buffers(struct timblogiw *lw, u32 count) | ||
1368 | +{ | ||
1369 | + /* needs to be page aligned cause the */ | ||
1370 | + /* buffers can be mapped individually! */ | ||
1371 | + const size_t imagesize = PAGE_ALIGN(lw->frame_size); | ||
1372 | + void *buff = NULL; | ||
1373 | + u32 i; | ||
1374 | + | ||
1375 | + dbg("%s - request of %i buffers of size %zi\n", | ||
1376 | + __func__, count, lw->frame_size); | ||
1377 | + | ||
1378 | + lw->dma.transfer[0].buf = pci_alloc_consistent(lw->dev, imagesize, | ||
1379 | + &lw->dma.transfer[0].handle); | ||
1380 | + lw->dma.transfer[1].buf = pci_alloc_consistent(lw->dev, imagesize, | ||
1381 | + &lw->dma.transfer[1].handle); | ||
1382 | + if ((lw->dma.transfer[0].buf == NULL) || | ||
1383 | + (lw->dma.transfer[1].buf == NULL)) { | ||
1384 | + printk(KERN_ALERT "alloc failed\n"); | ||
1385 | + if (lw->dma.transfer[0].buf != NULL) | ||
1386 | + pci_free_consistent(lw->dev, imagesize, | ||
1387 | + lw->dma.transfer[0].buf, | ||
1388 | + lw->dma.transfer[0].handle); | ||
1389 | + if (lw->dma.transfer[1].buf != NULL) | ||
1390 | + pci_free_consistent(lw->dev, imagesize, | ||
1391 | + lw->dma.transfer[1].buf, | ||
1392 | + lw->dma.transfer[1].handle); | ||
1393 | + return 0; | ||
1394 | + } | ||
1395 | + | ||
1396 | + if (count > TIMBLOGIW_NUM_FRAMES) | ||
1397 | + count = TIMBLOGIW_NUM_FRAMES; | ||
1398 | + | ||
1399 | + lw->num_frames = count; | ||
1400 | + while (lw->num_frames > 0) { | ||
1401 | + buff = vmalloc_32(lw->num_frames * imagesize); | ||
1402 | + if (buff) { | ||
1403 | + memset(buff, 0, lw->num_frames * imagesize); | ||
1404 | + break; | ||
1405 | + } | ||
1406 | + lw->num_frames--; | ||
1407 | + } | ||
1408 | + | ||
1409 | + for (i = 0; i < lw->num_frames; i++) { | ||
1410 | + lw->frame[i].bufmem = buff + i * imagesize; | ||
1411 | + lw->frame[i].buf.index = i; | ||
1412 | + lw->frame[i].buf.m.offset = i * imagesize; | ||
1413 | + lw->frame[i].buf.length = lw->frame_size; | ||
1414 | + lw->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1415 | + lw->frame[i].buf.sequence = 0; | ||
1416 | + lw->frame[i].buf.field = V4L2_FIELD_NONE; | ||
1417 | + lw->frame[i].buf.memory = V4L2_MEMORY_MMAP; | ||
1418 | + lw->frame[i].buf.flags = 0; | ||
1419 | + } | ||
1420 | + | ||
1421 | + lw->dma.curr = 0; | ||
1422 | + lw->dma.filled = NULL; | ||
1423 | + return lw->num_frames; | ||
1424 | +} | ||
1425 | + | ||
1426 | +void timblogiw_release_buffers(struct timblogiw *lw) | ||
1427 | +{ | ||
1428 | + dbg("%s\n", __func__); | ||
1429 | + | ||
1430 | + if (lw->frame[0].bufmem != NULL) { | ||
1431 | + vfree(lw->frame[0].bufmem); | ||
1432 | + lw->frame[0].bufmem = NULL; | ||
1433 | + lw->num_frames = TIMBLOGIW_NUM_FRAMES; | ||
1434 | + pci_free_consistent(lw->dev, lw->frame_size, | ||
1435 | + lw->dma.transfer[0].buf, lw->dma.transfer[0].handle); | ||
1436 | + pci_free_consistent(lw->dev, lw->frame_size, | ||
1437 | + lw->dma.transfer[1].buf, lw->dma.transfer[1].handle); | ||
1438 | + } | ||
1439 | +} | ||
1440 | + | ||
1441 | +/* IOCTL functions */ | ||
1442 | + | ||
1443 | +static int timblogiw_g_fmt(struct timblogiw *lw, struct v4l2_format *format) | ||
1444 | +{ | ||
1445 | + dbg("%s -\n", __func__); | ||
1446 | + | ||
1447 | + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1448 | + return -EINVAL; | ||
1449 | + | ||
1450 | + format->fmt.pix.width = lw->width; | ||
1451 | + format->fmt.pix.height = lw->height; | ||
1452 | + format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; | ||
1453 | + format->fmt.pix.bytesperline = lw->bytesperline; | ||
1454 | + format->fmt.pix.sizeimage = lw->frame_size; | ||
1455 | + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; | ||
1456 | + format->fmt.pix.field = V4L2_FIELD_NONE; | ||
1457 | + return 0; | ||
1458 | +} | ||
1459 | + | ||
1460 | +static int timblogiw_s_fmt(struct timblogiw *lw, struct v4l2_format *format) | ||
1461 | +{ | ||
1462 | + struct v4l2_pix_format *pix = &format->fmt.pix; | ||
1463 | + dbg("%s - type: %d\n", __func__, format->type); | ||
1464 | + | ||
1465 | + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1466 | + return -EINVAL; | ||
1467 | + | ||
1468 | + if ((lw->height != pix->height) || (lw->width != lw->width)) | ||
1469 | + return -EINVAL; | ||
1470 | + | ||
1471 | + if (format->fmt.pix.field != V4L2_FIELD_NONE) | ||
1472 | + return -EINVAL; | ||
1473 | + | ||
1474 | + dbg("%s - width=%d, height=%d, pixelformat=%d, field=%d\n" | ||
1475 | + "bytes per line %d, size image: %d, colorspace: %d\n", | ||
1476 | + __func__, | ||
1477 | + pix->width, pix->height, pix->pixelformat, pix->field, | ||
1478 | + pix->bytesperline, pix->sizeimage, pix->colorspace); | ||
1479 | + | ||
1480 | + return 0; | ||
1481 | +} | ||
1482 | + | ||
1483 | +static int timblogiw_querycap(struct timblogiw *lw, | ||
1484 | + struct v4l2_capability *cap) | ||
1485 | +{ | ||
1486 | + memset(cap, 0, sizeof(*cap)); | ||
1487 | + strncpy(cap->card, "Timberdale Video", sizeof(cap->card)-1); | ||
1488 | + strncpy(cap->driver, "Timblogiw", sizeof(cap->card)-1); | ||
1489 | + cap->version = TIMBLOGIW_VERSION_CODE; | ||
1490 | + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | | ||
1491 | + V4L2_CAP_STREAMING; | ||
1492 | + | ||
1493 | + return 0; | ||
1494 | +} | ||
1495 | + | ||
1496 | +static int timblogiw_enum_fmt(struct timblogiw *lw, struct v4l2_fmtdesc *fmt) | ||
1497 | +{ | ||
1498 | + dbg("%s - VIDIOC_ENUM_FMT\n", __func__); | ||
1499 | + | ||
1500 | + if (fmt->index != 0) | ||
1501 | + return -EINVAL; | ||
1502 | + memset(fmt, 0, sizeof(*fmt)); | ||
1503 | + fmt->index = 0; | ||
1504 | + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1505 | + strncpy(fmt->description, "4:2:2, packed, YUYV", | ||
1506 | + sizeof(fmt->description)-1); | ||
1507 | + fmt->pixelformat = V4L2_PIX_FMT_YUYV; | ||
1508 | + memset(fmt->reserved, 0, sizeof(fmt->reserved)); | ||
1509 | + | ||
1510 | + return 0; | ||
1511 | +} | ||
1512 | + | ||
1513 | +static int timblogiw_reqbufs(struct timblogiw *lw, | ||
1514 | + struct v4l2_requestbuffers *rb) | ||
1515 | +{ | ||
1516 | + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
1517 | + rb->memory != V4L2_MEMORY_MMAP) | ||
1518 | + return -EINVAL; | ||
1519 | + | ||
1520 | + timblogiw_empty_framequeues(lw); | ||
1521 | + | ||
1522 | + timblogiw_release_buffers(lw); | ||
1523 | + if (rb->count) | ||
1524 | + rb->count = timblogiw_request_buffers(lw, rb->count); | ||
1525 | + | ||
1526 | + dbg("%s - VIDIOC_REQBUFS: io method is mmap. num bufs %i\n", | ||
1527 | + __func__, rb->count); | ||
1528 | + | ||
1529 | + return 0; | ||
1530 | +} | ||
1531 | + | ||
1532 | +static int timblogiw_querybuf(struct timblogiw *lw, struct v4l2_buffer *b) | ||
1533 | +{ | ||
1534 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
1535 | + b->index >= lw->num_frames) | ||
1536 | + return -EINVAL; | ||
1537 | + | ||
1538 | + memcpy(b, &lw->frame[b->index].buf, sizeof(*b)); | ||
1539 | + | ||
1540 | + if (lw->frame[b->index].vma_use_count) | ||
1541 | + b->flags |= V4L2_BUF_FLAG_MAPPED; | ||
1542 | + | ||
1543 | + if (lw->frame[b->index].state == F_DONE) | ||
1544 | + b->flags |= V4L2_BUF_FLAG_DONE; | ||
1545 | + else if (lw->frame[b->index].state != F_UNUSED) | ||
1546 | + b->flags |= V4L2_BUF_FLAG_QUEUED; | ||
1547 | + | ||
1548 | + return 0; | ||
1549 | +} | ||
1550 | + | ||
1551 | +static int timblogiw_qbuf(struct timblogiw *lw, struct v4l2_buffer *b) | ||
1552 | +{ | ||
1553 | + unsigned long lock_flags; | ||
1554 | + | ||
1555 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
1556 | + b->index >= lw->num_frames) | ||
1557 | + return -EINVAL; | ||
1558 | + | ||
1559 | + if (lw->frame[b->index].state != F_UNUSED) | ||
1560 | + return -EAGAIN; | ||
1561 | + | ||
1562 | + if (b->memory != V4L2_MEMORY_MMAP) | ||
1563 | + return -EINVAL; | ||
1564 | + | ||
1565 | + lw->frame[b->index].state = F_QUEUED; | ||
1566 | + | ||
1567 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
1568 | + list_add_tail(&lw->frame[b->index].frame, &lw->inqueue); | ||
1569 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
1570 | + | ||
1571 | + return 0; | ||
1572 | +} | ||
1573 | + | ||
1574 | +static int timblogiw_dqbuf(struct timblogiw *lw, struct file *file, | ||
1575 | + struct v4l2_buffer *b) | ||
1576 | +{ | ||
1577 | + struct timblogiw_frame *f; | ||
1578 | + unsigned long lock_flags; | ||
1579 | + int ret = 0; | ||
1580 | + | ||
1581 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
1582 | + dbg("%s - VIDIOC_DQBUF, illegal buf type!\n", | ||
1583 | + __func__); | ||
1584 | + return -EINVAL; | ||
1585 | + } | ||
1586 | + | ||
1587 | + if (list_empty(&lw->outqueue)) { | ||
1588 | + if (file->f_flags & O_NONBLOCK) | ||
1589 | + return -EAGAIN; | ||
1590 | + | ||
1591 | + ret = wait_event_interruptible(lw->wait_frame, | ||
1592 | + !list_empty(&lw->outqueue)); | ||
1593 | + if (ret) | ||
1594 | + return ret; | ||
1595 | + } | ||
1596 | + | ||
1597 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
1598 | + f = list_entry(lw->outqueue.next, | ||
1599 | + struct timblogiw_frame, frame); | ||
1600 | + list_del(lw->outqueue.next); | ||
1601 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
1602 | + | ||
1603 | + f->state = F_UNUSED; | ||
1604 | + memcpy(b, &f->buf, sizeof(*b)); | ||
1605 | + | ||
1606 | + if (f->vma_use_count) | ||
1607 | + b->flags |= V4L2_BUF_FLAG_MAPPED; | ||
1608 | + | ||
1609 | + return 0; | ||
1610 | +} | ||
1611 | + | ||
1612 | +static int timblogiw_enumstd(struct timblogiw *lw, struct v4l2_standard *std) | ||
1613 | +{ | ||
1614 | + if (std->index != 0) | ||
1615 | + return -EINVAL; | ||
1616 | + | ||
1617 | + memset(std, 0, sizeof(*std)); | ||
1618 | + std->index = 0; | ||
1619 | + | ||
1620 | + std->id = V4L2_STD_PAL; | ||
1621 | + strncpy(std->name, "PAL", sizeof(std->name)-1); | ||
1622 | + | ||
1623 | + return 0; | ||
1624 | +} | ||
1625 | + | ||
1626 | +static int timblogiw_g_std(struct timblogiw *lw, v4l2_std_id *std) | ||
1627 | +{ | ||
1628 | + *std = V4L2_STD_PAL; | ||
1629 | + return 0; | ||
1630 | +} | ||
1631 | + | ||
1632 | +static int timblogiw_s_std(struct timblogiw *lw, v4l2_std_id *std) | ||
1633 | +{ | ||
1634 | + if (!(*std & V4L2_STD_PAL)) | ||
1635 | + return -EINVAL; | ||
1636 | + return 0; | ||
1637 | +} | ||
1638 | + | ||
1639 | +static int timblogiw_enuminput(struct timblogiw *lw, struct v4l2_input *inp) | ||
1640 | +{ | ||
1641 | + if (inp->index != 0) | ||
1642 | + return -EINVAL; | ||
1643 | + | ||
1644 | + memset(inp, 0, sizeof(*inp)); | ||
1645 | + inp->index = 0; | ||
1646 | + | ||
1647 | + strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); | ||
1648 | + inp->type = V4L2_INPUT_TYPE_CAMERA; | ||
1649 | + inp->std = V4L2_STD_ALL; | ||
1650 | + | ||
1651 | + return 0; | ||
1652 | +} | ||
1653 | + | ||
1654 | +static int timblogiw_g_input(struct timblogiw *lw, int *input) | ||
1655 | +{ | ||
1656 | + *input = 0; | ||
1657 | + | ||
1658 | + return 0; | ||
1659 | +} | ||
1660 | + | ||
1661 | +static int timblogiw_s_input(struct timblogiw *lw, int *input) | ||
1662 | +{ | ||
1663 | + if (*input != 0) | ||
1664 | + return -EINVAL; | ||
1665 | + return 0; | ||
1666 | +} | ||
1667 | + | ||
1668 | +static int timblogiw_streamon(struct timblogiw *lw, int *type) | ||
1669 | +{ | ||
1670 | + struct timblogiw_frame *f; | ||
1671 | + | ||
1672 | + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
1673 | + dbg("%s - No capture device\n", __func__); | ||
1674 | + return -EINVAL; | ||
1675 | + } | ||
1676 | + | ||
1677 | + if (list_empty(&lw->inqueue)) { | ||
1678 | + dbg("%s - inqueue is empty\n", __func__); | ||
1679 | + return -EINVAL; | ||
1680 | + } | ||
1681 | + | ||
1682 | + if (lw->stream == STREAM_ON) | ||
1683 | + return 0; | ||
1684 | + | ||
1685 | + lw->stream = STREAM_ON; | ||
1686 | + | ||
1687 | + f = list_entry(lw->inqueue.next, | ||
1688 | + struct timblogiw_frame, frame); | ||
1689 | + | ||
1690 | + dbg("%s - f size: %d, bpr: %d, dma addr: %x\n", __func__, | ||
1691 | + lw->frame_size, lw->bytesperline, | ||
1692 | + (unsigned int)lw->dma.transfer[lw->dma.curr].handle); | ||
1693 | + timb_start_dma(DMA_IRQ_VIDEO_RX, | ||
1694 | + lw->dma.transfer[lw->dma.curr].handle, | ||
1695 | + lw->frame_size, lw->bytesperline); | ||
1696 | + | ||
1697 | + return 0; | ||
1698 | +} | ||
1699 | + | ||
1700 | +static int timblogiw_streamoff(struct timblogiw *lw, int *type) | ||
1701 | +{ | ||
1702 | + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1703 | + return -EINVAL; | ||
1704 | + | ||
1705 | + if (lw->stream == STREAM_ON) { | ||
1706 | + unsigned long lock_flags; | ||
1707 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
1708 | + timb_stop_dma(DMA_IRQ_VIDEO_RX); | ||
1709 | + lw->stream = STREAM_OFF; | ||
1710 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
1711 | + } | ||
1712 | + timblogiw_empty_framequeues(lw); | ||
1713 | + | ||
1714 | + return 0; | ||
1715 | +} | ||
1716 | + | ||
1717 | +static int timblogiw_querystd(struct timblogiw *lw, v4l2_std_id *std) | ||
1718 | +{ | ||
1719 | + /* TODO: Ask encoder */ | ||
1720 | + *std = V4L2_STD_PAL; | ||
1721 | + return 0; | ||
1722 | +} | ||
1723 | + | ||
1724 | +static int timblogiw_enum_framsizes(struct timblogiw *lw, | ||
1725 | + struct v4l2_frmsizeenum *fsize) | ||
1726 | +{ | ||
1727 | + if ((fsize->index != 0) || | ||
1728 | + (fsize->pixel_format != V4L2_PIX_FMT_YUYV)) | ||
1729 | + return -EINVAL; | ||
1730 | + | ||
1731 | + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | ||
1732 | + fsize->discrete.width = lw->width; | ||
1733 | + fsize->discrete.height = lw->height; | ||
1734 | + | ||
1735 | + return 0; | ||
1736 | +} | ||
1737 | + | ||
1738 | +static int timblogiw_g_parm(struct timblogiw *lw, struct v4l2_streamparm *sp) | ||
1739 | +{ | ||
1740 | + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1741 | + return -EINVAL; | ||
1742 | + | ||
1743 | + sp->parm.capture.extendedmode = 0; | ||
1744 | + sp->parm.capture.readbuffers = lw->num_frames; | ||
1745 | + return 0; | ||
1746 | +} | ||
1747 | + | ||
1748 | +/******************************* | ||
1749 | + * Device Operations functions * | ||
1750 | + *******************************/ | ||
1751 | + | ||
1752 | +static int timblogiw_open(struct file *file) | ||
1753 | +{ | ||
1754 | + struct video_device *vdev = video_devdata(file); | ||
1755 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
1756 | + | ||
1757 | + dbg("%s -\n", __func__); | ||
1758 | + | ||
1759 | + mutex_init(&lw->fileop_lock); | ||
1760 | + spin_lock_init(&lw->queue_lock); | ||
1761 | + init_waitqueue_head(&lw->wait_frame); | ||
1762 | + | ||
1763 | + mutex_lock(&lw->lock); | ||
1764 | + | ||
1765 | + lw->width = 720; /* TODO: Should depend on tv norm */ | ||
1766 | + lw->height = 576; | ||
1767 | + lw->frame_size = lw->width * lw->height * 2; | ||
1768 | + lw->bytesperline = lw->width * 2; | ||
1769 | + | ||
1770 | + file->private_data = lw; | ||
1771 | + lw->stream = STREAM_OFF; | ||
1772 | + lw->num_frames = TIMBLOGIW_NUM_FRAMES; | ||
1773 | + | ||
1774 | + timblogiw_empty_framequeues(lw); | ||
1775 | + | ||
1776 | + timb_set_dma_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, | ||
1777 | + timblogiw_isr, (void *)lw); | ||
1778 | + | ||
1779 | + mutex_unlock(&lw->lock); | ||
1780 | + | ||
1781 | + return 0; | ||
1782 | +} | ||
1783 | + | ||
1784 | +static int timblogiw_close(struct file *file) | ||
1785 | +{ | ||
1786 | + struct timblogiw *lw = file->private_data; | ||
1787 | + | ||
1788 | + dbg("%s - entry\n", __func__); | ||
1789 | + | ||
1790 | + mutex_lock(&lw->lock); | ||
1791 | + | ||
1792 | + timb_stop_dma(DMA_IRQ_VIDEO_RX); | ||
1793 | + timb_set_dma_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, NULL, | ||
1794 | + NULL); | ||
1795 | + timblogiw_release_buffers(lw); | ||
1796 | + | ||
1797 | + mutex_unlock(&lw->lock); | ||
1798 | + return 0; | ||
1799 | +} | ||
1800 | + | ||
1801 | +static ssize_t timblogiw_read(struct file *file, char __user *data, | ||
1802 | + size_t count, loff_t *ppos) | ||
1803 | +{ | ||
1804 | + dbg("%s - read request\n", __func__); | ||
1805 | + return -EINVAL; | ||
1806 | +} | ||
1807 | + | ||
1808 | +static void timblogiw_vm_open(struct vm_area_struct *vma) | ||
1809 | +{ | ||
1810 | + struct timblogiw_frame *f = vma->vm_private_data; | ||
1811 | + f->vma_use_count++; | ||
1812 | +} | ||
1813 | + | ||
1814 | +static void timblogiw_vm_close(struct vm_area_struct *vma) | ||
1815 | +{ | ||
1816 | + struct timblogiw_frame *f = vma->vm_private_data; | ||
1817 | + f->vma_use_count--; | ||
1818 | +} | ||
1819 | + | ||
1820 | +static struct vm_operations_struct timblogiw_vm_ops = { | ||
1821 | + .open = timblogiw_vm_open, | ||
1822 | + .close = timblogiw_vm_close, | ||
1823 | +}; | ||
1824 | + | ||
1825 | +static int timblogiw_mmap(struct file *filp, struct vm_area_struct *vma) | ||
1826 | +{ | ||
1827 | + unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; | ||
1828 | + void *pos; | ||
1829 | + u32 i; | ||
1830 | + int ret = -EINVAL; | ||
1831 | + | ||
1832 | + struct timblogiw *lw = filp->private_data; | ||
1833 | + dbg("%s\n", __func__); | ||
1834 | + | ||
1835 | + if (mutex_lock_interruptible(&lw->fileop_lock)) | ||
1836 | + return -ERESTARTSYS; | ||
1837 | + | ||
1838 | + if (!(vma->vm_flags & VM_WRITE) || | ||
1839 | + size != PAGE_ALIGN(lw->frame[0].buf.length)) | ||
1840 | + goto error_unlock; | ||
1841 | + | ||
1842 | + for (i = 0; i < lw->num_frames; i++) | ||
1843 | + if ((lw->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) | ||
1844 | + break; | ||
1845 | + | ||
1846 | + if (i == lw->num_frames) { | ||
1847 | + dbg("%s - user supplied mapping address is out of range\n", | ||
1848 | + __func__); | ||
1849 | + goto error_unlock; | ||
1850 | + } | ||
1851 | + | ||
1852 | + vma->vm_flags |= VM_IO; | ||
1853 | + vma->vm_flags |= VM_RESERVED; /* Do not swap out this VMA */ | ||
1854 | + | ||
1855 | + pos = lw->frame[i].bufmem; | ||
1856 | + while (size > 0) { /* size is page-aligned */ | ||
1857 | + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { | ||
1858 | + dbg("%s - vm_insert_page failed\n", __func__); | ||
1859 | + ret = -EAGAIN; | ||
1860 | + goto error_unlock; | ||
1861 | + } | ||
1862 | + start += PAGE_SIZE; | ||
1863 | + pos += PAGE_SIZE; | ||
1864 | + size -= PAGE_SIZE; | ||
1865 | + } | ||
1866 | + | ||
1867 | + vma->vm_ops = &timblogiw_vm_ops; | ||
1868 | + vma->vm_private_data = &lw->frame[i]; | ||
1869 | + timblogiw_vm_open(vma); | ||
1870 | + ret = 0; | ||
1871 | + | ||
1872 | +error_unlock: | ||
1873 | + mutex_unlock(&lw->fileop_lock); | ||
1874 | + return ret; | ||
1875 | +} | ||
1876 | + | ||
1877 | +static long | ||
1878 | +timblogiw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
1879 | +{ | ||
1880 | + struct timblogiw *lw = file->private_data; | ||
1881 | + | ||
1882 | + switch (cmd) { | ||
1883 | + | ||
1884 | + case VIDIOC_QUERYCAP: | ||
1885 | + { | ||
1886 | + dbg("%s - VIDIOC_QUERYCAP\n", __func__); | ||
1887 | + return timblogiw_querycap(lw, (struct v4l2_capability *)arg); | ||
1888 | + } | ||
1889 | + | ||
1890 | + case VIDIOC_ENUM_FMT: | ||
1891 | + { | ||
1892 | + dbg("%s - VIDIOC_ENUM_FMT\n", __func__); | ||
1893 | + return timblogiw_enum_fmt(lw, (struct v4l2_fmtdesc *)arg); | ||
1894 | + } | ||
1895 | + | ||
1896 | + case VIDIOC_G_FMT: | ||
1897 | + { | ||
1898 | + dbg("%s - VIDIOC_G_FMT\n", __func__); | ||
1899 | + return timblogiw_g_fmt(lw, (struct v4l2_format *) arg); | ||
1900 | + } | ||
1901 | + | ||
1902 | + case VIDIOC_TRY_FMT: | ||
1903 | + case VIDIOC_S_FMT: | ||
1904 | + { | ||
1905 | + dbg("%s - VIDIOC_S_FMT\n", __func__); | ||
1906 | + return timblogiw_s_fmt(lw, (struct v4l2_format *)arg); | ||
1907 | + } | ||
1908 | + | ||
1909 | + case VIDIOC_REQBUFS: | ||
1910 | + { | ||
1911 | + dbg("%s - VIDIOC_REQBUFS\n", __func__); | ||
1912 | + return timblogiw_reqbufs(lw, (struct v4l2_requestbuffers *)arg); | ||
1913 | + } | ||
1914 | + | ||
1915 | + case VIDIOC_QUERYBUF: | ||
1916 | + { | ||
1917 | + dbg("%s - VIDIOC_QUERYBUF\n", __func__); | ||
1918 | + return timblogiw_querybuf(lw, (struct v4l2_buffer *)arg); | ||
1919 | + } | ||
1920 | + | ||
1921 | + case VIDIOC_QBUF: | ||
1922 | + { | ||
1923 | + return timblogiw_qbuf(lw, (struct v4l2_buffer *)arg); | ||
1924 | + } | ||
1925 | + | ||
1926 | + case VIDIOC_DQBUF: | ||
1927 | + { | ||
1928 | + return timblogiw_dqbuf(lw, file, (struct v4l2_buffer *)arg); | ||
1929 | + } | ||
1930 | + | ||
1931 | + case VIDIOC_ENUMSTD: | ||
1932 | + { | ||
1933 | + dbg("%s - VIDIOC_ENUMSTD\n", __func__); | ||
1934 | + return timblogiw_enumstd(lw, (struct v4l2_standard *)arg); | ||
1935 | + } | ||
1936 | + | ||
1937 | + case VIDIOC_G_STD: | ||
1938 | + { | ||
1939 | + dbg("%s - VIDIOC_G_STD\n", __func__); | ||
1940 | + return timblogiw_g_std(lw, (v4l2_std_id *)arg); | ||
1941 | + } | ||
1942 | + | ||
1943 | + case VIDIOC_S_STD: | ||
1944 | + { | ||
1945 | + dbg("%s - VIDIOC_S_STD\n", __func__); | ||
1946 | + return timblogiw_s_std(lw, (v4l2_std_id *)arg); | ||
1947 | + } | ||
1948 | + | ||
1949 | + case VIDIOC_ENUMINPUT: | ||
1950 | + { | ||
1951 | + dbg("%s - VIDIOC_ENUMINPUT\n", __func__); | ||
1952 | + return timblogiw_enuminput(lw, (struct v4l2_input *)arg); | ||
1953 | + } | ||
1954 | + | ||
1955 | + case VIDIOC_G_INPUT: | ||
1956 | + { | ||
1957 | + dbg("%s - VIDIOC_G_INPUT\n", __func__); | ||
1958 | + return timblogiw_g_input(lw, (int *)arg); | ||
1959 | + } | ||
1960 | + | ||
1961 | + case VIDIOC_S_INPUT: | ||
1962 | + { | ||
1963 | + dbg("%s - VIDIOC_S_INPUT\n", __func__); | ||
1964 | + return timblogiw_s_input(lw, (int *)arg); | ||
1965 | + } | ||
1966 | + | ||
1967 | + case VIDIOC_STREAMON: | ||
1968 | + { | ||
1969 | + dbg("%s - VIDIOC_STREAMON\n", __func__); | ||
1970 | + return timblogiw_streamon(lw, (int *)arg); | ||
1971 | + } | ||
1972 | + | ||
1973 | + case VIDIOC_STREAMOFF: | ||
1974 | + { | ||
1975 | + dbg("%s - VIDIOC_STREAMOFF\n", __func__); | ||
1976 | + return timblogiw_streamoff(lw, (int *)arg); | ||
1977 | + } | ||
1978 | + | ||
1979 | + case VIDIOC_QUERYSTD: | ||
1980 | + { | ||
1981 | + dbg("%s - VIDIOC_QUERYSTD\n", __func__); | ||
1982 | + return timblogiw_querystd(lw, (v4l2_std_id *)arg); | ||
1983 | + } | ||
1984 | + | ||
1985 | + case VIDIOC_ENUM_FRAMESIZES: | ||
1986 | + { | ||
1987 | + dbg("%s - VIDIOC_ENUM_FRAMESIZES\n", __func__); | ||
1988 | + return timblogiw_enum_framsizes(lw, | ||
1989 | + (struct v4l2_frmsizeenum *)arg); | ||
1990 | + } | ||
1991 | + | ||
1992 | + case VIDIOC_G_PARM: | ||
1993 | + { | ||
1994 | + dbg("%s - VIDIOC_G_PARM\n", __func__); | ||
1995 | + return timblogiw_g_parm(lw, (struct v4l2_streamparm *)arg); | ||
1996 | + } | ||
1997 | + | ||
1998 | + default: | ||
1999 | + { | ||
2000 | + dbg("%s Unknown command, dir: %x, type: %x, nr: %x, size: %x\n", | ||
2001 | + __func__, | ||
2002 | + _IOC_DIR(cmd), | ||
2003 | + _IOC_TYPE(cmd), | ||
2004 | + _IOC_NR(cmd), | ||
2005 | + _IOC_SIZE(cmd)); | ||
2006 | + break; | ||
2007 | + } | ||
2008 | + } | ||
2009 | + | ||
2010 | + return -EINVAL; | ||
2011 | +} | ||
2012 | + | ||
2013 | +void timblogiw_vdev_release(struct video_device *vdev) | ||
2014 | +{ | ||
2015 | + kfree(vdev); | ||
2016 | +} | ||
2017 | + | ||
2018 | +static const struct v4l2_file_operations timblogiw_fops = { | ||
2019 | + .owner = THIS_MODULE, | ||
2020 | + .open = timblogiw_open, | ||
2021 | + .release = timblogiw_close, | ||
2022 | + .ioctl = timblogiw_ioctl, | ||
2023 | + .mmap = timblogiw_mmap, | ||
2024 | + .read = timblogiw_read, | ||
2025 | +}; | ||
2026 | + | ||
2027 | +static const struct video_device timblogiw_template = { | ||
2028 | + .name = TIMBLOGIWIN_NAME, | ||
2029 | + .fops = &timblogiw_fops, | ||
2030 | + .release = &timblogiw_vdev_release, | ||
2031 | + .minor = -1 | ||
2032 | +}; | ||
2033 | + | ||
2034 | +static int timblogiw_probe(struct platform_device *dev) | ||
2035 | +{ | ||
2036 | + int err; | ||
2037 | + struct timblogiw *lw; | ||
2038 | + struct resource *iomem; | ||
2039 | + | ||
2040 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
2041 | + if (!iomem) { | ||
2042 | + err = -EINVAL; | ||
2043 | + goto err_mem; | ||
2044 | + } | ||
2045 | + | ||
2046 | + lw = kzalloc(sizeof(*lw), GFP_KERNEL); | ||
2047 | + if (!lw) { | ||
2048 | + err = -EINVAL; | ||
2049 | + goto err_mem; | ||
2050 | + } | ||
2051 | + | ||
2052 | + /* find the PCI device from the parent... */ | ||
2053 | + if (!dev->dev.parent) { | ||
2054 | + printk(KERN_ERR "timblogwi: No parent device found??\n"); | ||
2055 | + err = -ENODEV; | ||
2056 | + goto err_mem; | ||
2057 | + } | ||
2058 | + | ||
2059 | + lw->dev = container_of(dev->dev.parent, struct pci_dev, dev); | ||
2060 | + | ||
2061 | + mutex_init(&lw->lock); | ||
2062 | + | ||
2063 | + lw->video_dev = video_device_alloc(); | ||
2064 | + if (!lw->video_dev) { | ||
2065 | + err = -ENOMEM; | ||
2066 | + goto err_video_req; | ||
2067 | + } | ||
2068 | + *lw->video_dev = timblogiw_template; | ||
2069 | + | ||
2070 | + err = video_register_device(lw->video_dev, VFL_TYPE_GRABBER, 0); | ||
2071 | + if (err) { | ||
2072 | + video_device_release(lw->video_dev); | ||
2073 | + printk(KERN_ALERT "Error reg video\n"); | ||
2074 | + goto err_video_req; | ||
2075 | + } | ||
2076 | + | ||
2077 | + tasklet_init(&lw->tasklet, timblogiw_handleframe, (unsigned long)lw); | ||
2078 | + | ||
2079 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
2080 | + "timb-video")) { | ||
2081 | + err = -EBUSY; | ||
2082 | + goto err_request; | ||
2083 | + } | ||
2084 | + | ||
2085 | + lw->membase = ioremap(iomem->start, resource_size(iomem)); | ||
2086 | + if (!lw->membase) { | ||
2087 | + err = -ENOMEM; | ||
2088 | + goto err_ioremap; | ||
2089 | + } | ||
2090 | + | ||
2091 | + platform_set_drvdata(dev, lw); | ||
2092 | + video_set_drvdata(lw->video_dev, lw); | ||
2093 | + | ||
2094 | + return 0; | ||
2095 | + | ||
2096 | +err_ioremap: | ||
2097 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2098 | +err_request: | ||
2099 | + if (-1 != lw->video_dev->minor) | ||
2100 | + video_unregister_device(lw->video_dev); | ||
2101 | + else | ||
2102 | + video_device_release(lw->video_dev); | ||
2103 | +err_video_req: | ||
2104 | + kfree(lw); | ||
2105 | +err_mem: | ||
2106 | + printk(KERN_ERR | ||
2107 | + "timberdale: Failed to register Timberdale Video In: %d\n", | ||
2108 | + err); | ||
2109 | + | ||
2110 | + return err; | ||
2111 | +} | ||
2112 | + | ||
2113 | +static int timblogiw_remove(struct platform_device *dev) | ||
2114 | +{ | ||
2115 | + struct timblogiw *lw = platform_get_drvdata(dev); | ||
2116 | + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
2117 | + | ||
2118 | + if (-1 != lw->video_dev->minor) | ||
2119 | + video_unregister_device(lw->video_dev); | ||
2120 | + else | ||
2121 | + video_device_release(lw->video_dev); | ||
2122 | + | ||
2123 | + tasklet_kill(&lw->tasklet); | ||
2124 | + iounmap(lw->membase); | ||
2125 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2126 | + kfree(lw); | ||
2127 | + | ||
2128 | + return 0; | ||
2129 | +} | ||
2130 | + | ||
2131 | +static struct platform_driver timblogiw_platform_driver = { | ||
2132 | + .driver = { | ||
2133 | + .name = "timb-video", | ||
2134 | + .owner = THIS_MODULE, | ||
2135 | + }, | ||
2136 | + .probe = timblogiw_probe, | ||
2137 | + .remove = timblogiw_remove, | ||
2138 | +}; | ||
2139 | + | ||
2140 | +/*--------------------------------------------------------------------------*/ | ||
2141 | + | ||
2142 | +static int __init timblogiw_init(void) | ||
2143 | +{ | ||
2144 | + return platform_driver_register(&timblogiw_platform_driver); | ||
2145 | +} | ||
2146 | + | ||
2147 | +static void __exit timblogiw_exit(void) | ||
2148 | +{ | ||
2149 | + platform_driver_unregister(&timblogiw_platform_driver); | ||
2150 | +} | ||
2151 | + | ||
2152 | +module_init(timblogiw_init); | ||
2153 | +module_exit(timblogiw_exit); | ||
2154 | + | ||
2155 | +MODULE_DESCRIPTION("Timberdale Video In driver"); | ||
2156 | +MODULE_LICENSE("GPL v2"); | ||
2157 | +MODULE_ALIAS("platform:timb-video"); | ||
2158 | + | ||
2159 | diff -uNr linux-2.6.29-clean/drivers/media/video/timblogiw.h linux-2.6.29/drivers/media/video/timblogiw.h | ||
2160 | --- linux-2.6.29-clean/drivers/media/video/timblogiw.h 1969-12-31 16:00:00.000000000 -0800 | ||
2161 | +++ linux-2.6.29/drivers/media/video/timblogiw.h 2009-04-06 13:51:47.000000000 -0700 | ||
2162 | @@ -0,0 +1,95 @@ | ||
2163 | +/* | ||
2164 | + * timblogiw.h timberdale FPGA LogiWin Video In driver defines | ||
2165 | + * Copyright (c) 2009 Intel Corporation | ||
2166 | + * | ||
2167 | + * This program is free software; you can redistribute it and/or modify | ||
2168 | + * it under the terms of the GNU General Public License version 2 as | ||
2169 | + * published by the Free Software Foundation. | ||
2170 | + * | ||
2171 | + * This program is distributed in the hope that it will be useful, | ||
2172 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2173 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2174 | + * GNU General Public License for more details. | ||
2175 | + * | ||
2176 | + * You should have received a copy of the GNU General Public License | ||
2177 | + * along with this program; if not, write to the Free Software | ||
2178 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
2179 | + */ | ||
2180 | + | ||
2181 | +/* Supports: | ||
2182 | + * Timberdale FPGA LogiWin Video In | ||
2183 | + */ | ||
2184 | + | ||
2185 | +#ifndef _TIMBLOGIW_H | ||
2186 | +#define _TIMBLOGIW_H | ||
2187 | + | ||
2188 | +#include <linux/interrupt.h> | ||
2189 | + | ||
2190 | +#define TIMBLOGIWIN_NAME "Timberdale Video-In" | ||
2191 | + | ||
2192 | +#define TIMBLOGIW_NUM_FRAMES 10 | ||
2193 | + | ||
2194 | + | ||
2195 | +enum timblogiw_stream_state { | ||
2196 | + STREAM_OFF, | ||
2197 | + STREAM_ON, | ||
2198 | +}; | ||
2199 | + | ||
2200 | +enum timblogiw_frame_state { | ||
2201 | + F_UNUSED = 0, | ||
2202 | + F_QUEUED, | ||
2203 | + F_GRABBING, | ||
2204 | + F_DONE, | ||
2205 | + F_ERROR, | ||
2206 | +}; | ||
2207 | + | ||
2208 | +struct timblogiw_frame { | ||
2209 | + void *bufmem; | ||
2210 | + struct v4l2_buffer buf; | ||
2211 | + enum timblogiw_frame_state state; | ||
2212 | + struct list_head frame; | ||
2213 | + unsigned long vma_use_count; | ||
2214 | +}; | ||
2215 | + | ||
2216 | +struct timblogiw_tvnorm { | ||
2217 | + int v4l2_id; | ||
2218 | + char *name; | ||
2219 | + u16 swidth; | ||
2220 | + u16 sheight; | ||
2221 | +}; | ||
2222 | + | ||
2223 | + | ||
2224 | +struct timbdma_transfer { | ||
2225 | + dma_addr_t handle; | ||
2226 | + void *buf; | ||
2227 | +}; | ||
2228 | + | ||
2229 | +struct timbdma_control { | ||
2230 | + struct timbdma_transfer transfer[2]; | ||
2231 | + struct timbdma_transfer *filled; | ||
2232 | + int curr; | ||
2233 | +}; | ||
2234 | + | ||
2235 | +struct timblogiw { | ||
2236 | + struct i2c_client *decoder; | ||
2237 | + struct timblogiw_frame frame[TIMBLOGIW_NUM_FRAMES]; | ||
2238 | + int num_frames; | ||
2239 | + unsigned int frame_count; | ||
2240 | + struct list_head inqueue, outqueue; | ||
2241 | + spinlock_t queue_lock; /* mutual exclusion */ | ||
2242 | + enum timblogiw_stream_state stream; | ||
2243 | + struct video_device *video_dev; | ||
2244 | + struct mutex lock, fileop_lock; | ||
2245 | + wait_queue_head_t wait_frame; | ||
2246 | + int width; | ||
2247 | + int height; | ||
2248 | + u32 frame_size; | ||
2249 | + int bytesperline; | ||
2250 | + struct pci_dev *dev; | ||
2251 | + struct timbdma_control dma; | ||
2252 | + void __iomem *membase; | ||
2253 | + struct tasklet_struct tasklet; | ||
2254 | +}; | ||
2255 | + | ||
2256 | +#endif /* _TIMBLOGIW_H */ | ||
2257 | + | ||
2258 | diff -uNr linux-2.6.29-clean/drivers/mfd/Kconfig linux-2.6.29/drivers/mfd/Kconfig | ||
2259 | --- linux-2.6.29-clean/drivers/mfd/Kconfig 2009-04-01 09:20:24.000000000 -0700 | ||
2260 | +++ linux-2.6.29/drivers/mfd/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
2261 | @@ -240,6 +240,27 @@ | ||
2262 | Say yes here if you want to include support GPIO for pins on | ||
2263 | the PCF50633 chip. | ||
2264 | |||
2265 | +config MFD_TIMBERDALE | ||
2266 | + bool "Support for Timberdale" | ||
2267 | + select MFD_CORE | ||
2268 | + ---help--- | ||
2269 | + This is the core driver for the timberdale FPGA. This device is a | ||
2270 | + multifunctioanl device which may provide numerous interfaces. | ||
2271 | + | ||
2272 | +config MFD_TIMBERDALE_DMA | ||
2273 | + tristate "Support for timberdale DMA" | ||
2274 | + depends on MFD_TIMBERDALE | ||
2275 | + ---help--- | ||
2276 | + Add support the DMA block inside the timberdale FPGA. This to be able | ||
2277 | + to do DMA transfers directly to some of the blocks inside the FPGA | ||
2278 | + | ||
2279 | +config MFD_TIMBERDALE_I2S | ||
2280 | + tristate "Support for timberdale I2S bus" | ||
2281 | + depends on MFD_TIMBERDALE | ||
2282 | + ---help--- | ||
2283 | + Add support for the I2S bus handled by timberdale FPGA. | ||
2284 | + I2S RX and TX instances are then available for other devices to make use of. | ||
2285 | + | ||
2286 | endmenu | ||
2287 | |||
2288 | menu "Multimedia Capabilities Port drivers" | ||
2289 | diff -uNr linux-2.6.29-clean/drivers/mfd/Makefile linux-2.6.29/drivers/mfd/Makefile | ||
2290 | --- linux-2.6.29-clean/drivers/mfd/Makefile 2009-04-01 09:20:24.000000000 -0700 | ||
2291 | +++ linux-2.6.29/drivers/mfd/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
2292 | @@ -40,4 +40,8 @@ | ||
2293 | |||
2294 | obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o | ||
2295 | obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o | ||
2296 | -obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o | ||
2297 | \ No newline at end of file | ||
2298 | +obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o | ||
2299 | + | ||
2300 | +obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o | ||
2301 | +obj-$(CONFIG_MFD_TIMBERDALE_DMA) += timbdma.o | ||
2302 | +obj-$(CONFIG_MFD_TIMBERDALE_I2S) += timbi2s.o | ||
2303 | diff -uNr linux-2.6.29-clean/drivers/mfd/timbdma.c linux-2.6.29/drivers/mfd/timbdma.c | ||
2304 | --- linux-2.6.29-clean/drivers/mfd/timbdma.c 1969-12-31 16:00:00.000000000 -0800 | ||
2305 | +++ linux-2.6.29/drivers/mfd/timbdma.c 2009-04-06 13:51:47.000000000 -0700 | ||
2306 | @@ -0,0 +1,301 @@ | ||
2307 | +/* | ||
2308 | + * timbdma.c timberdale FPGA DMA driver | ||
2309 | + * Copyright (c) 2009 Intel Corporation | ||
2310 | + * | ||
2311 | + * This program is free software; you can redistribute it and/or modify | ||
2312 | + * it under the terms of the GNU General Public License version 2 as | ||
2313 | + * published by the Free Software Foundation. | ||
2314 | + * | ||
2315 | + * This program is distributed in the hope that it will be useful, | ||
2316 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2317 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2318 | + * GNU General Public License for more details. | ||
2319 | + * | ||
2320 | + * You should have received a copy of the GNU General Public License | ||
2321 | + * along with this program; if not, write to the Free Software | ||
2322 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
2323 | + */ | ||
2324 | + | ||
2325 | +/* Supports: | ||
2326 | + * Timberdale FPGA DMA engine | ||
2327 | + */ | ||
2328 | + | ||
2329 | +#include <linux/version.h> | ||
2330 | +#include <linux/module.h> | ||
2331 | +#include <linux/pci.h> | ||
2332 | +#include <linux/interrupt.h> | ||
2333 | +#include <linux/platform_device.h> | ||
2334 | + | ||
2335 | +#include <linux/mfd/timbdma.h> | ||
2336 | + | ||
2337 | +static struct timbdma_dev *self_g; | ||
2338 | + | ||
2339 | +static irqreturn_t timbdma_handleinterrupt(int irq, void *devid) | ||
2340 | +{ | ||
2341 | + struct timbdma_dev *dev = (struct timbdma_dev *)devid; | ||
2342 | + int ipr; | ||
2343 | + int i; | ||
2344 | + | ||
2345 | + ipr = ioread32(dev->membase + timbdma_ctrlmap_TIMBPEND); | ||
2346 | + | ||
2347 | + /* ack */ | ||
2348 | + iowrite32(ipr, dev->membase + timbdma_ctrlmap_TIMBSTATUS); | ||
2349 | + | ||
2350 | + /* call the callbacks */ | ||
2351 | + for (i = 0; i < DMA_IRQS; i++) { | ||
2352 | + int mask = 1 << i; | ||
2353 | + if ((ipr & mask) && dev->callbacks[i]) | ||
2354 | + dev->callbacks[i](mask, dev->callback_data[i]); | ||
2355 | + } | ||
2356 | + | ||
2357 | + if (ipr) | ||
2358 | + return IRQ_HANDLED; | ||
2359 | + else | ||
2360 | + return IRQ_NONE; | ||
2361 | +} | ||
2362 | + | ||
2363 | + | ||
2364 | +void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row) | ||
2365 | +{ | ||
2366 | + int i; | ||
2367 | + unsigned long irqflags; | ||
2368 | + struct timbdma_dev *dev = self_g; | ||
2369 | + | ||
2370 | + spin_lock_irqsave(&dev->lock, irqflags); | ||
2371 | + | ||
2372 | + /* now enable the DMA transfer */ | ||
2373 | + for (i = 0; i < DMA_IRQS; i++) | ||
2374 | + if (flag & (1 << i)) { | ||
2375 | + u32 offset = i / 2 * 0x40; | ||
2376 | + | ||
2377 | + if (!(i % 2)) { | ||
2378 | + /* RX */ | ||
2379 | + /* bytes per row */ | ||
2380 | + iowrite32(bytes_per_row, dev->membase + offset + | ||
2381 | + timbdma_dmacfg_BPERROW); | ||
2382 | + /* address high */ | ||
2383 | + iowrite32(0, dev->membase + offset + | ||
2384 | + timbdma_dmacfg_RXSTARTH); | ||
2385 | + /* address low */ | ||
2386 | + iowrite32(buf, dev->membase + offset + | ||
2387 | + timbdma_dmacfg_RXSTARTL); | ||
2388 | + /* Length */ | ||
2389 | + iowrite32(len, dev->membase + offset + | ||
2390 | + timbdma_dmacfg_RXLENGTH); | ||
2391 | + /* Clear rx sw read pointer */ | ||
2392 | + iowrite32(0, dev->membase + offset + | ||
2393 | + timbdma_dmacfg_RXSWRP); | ||
2394 | + /* enable the transfer */ | ||
2395 | + iowrite32(1, dev->membase + offset + | ||
2396 | + timbdma_dmacfg_RXENABLE); | ||
2397 | + } else { | ||
2398 | + /* TX */ | ||
2399 | + /* address high */ | ||
2400 | + iowrite32(0, dev->membase + offset + | ||
2401 | + timbdma_dmacfg_TXSTARTH); | ||
2402 | + /* address low */ | ||
2403 | + iowrite32(buf, dev->membase + offset + | ||
2404 | + timbdma_dmacfg_TXSTARTL); | ||
2405 | + /* Length */ | ||
2406 | + iowrite32(len, dev->membase + offset + | ||
2407 | + timbdma_dmacfg_TXLENGTH); | ||
2408 | + /* Set tx sw write pointer */ | ||
2409 | + iowrite32(len, dev->membase + offset + | ||
2410 | + timbdma_dmacfg_TXSWWP); | ||
2411 | + } | ||
2412 | + | ||
2413 | + /* only allow one bit in the flag field */ | ||
2414 | + break; | ||
2415 | + } | ||
2416 | + spin_unlock_irqrestore(&dev->lock, irqflags); | ||
2417 | +} | ||
2418 | +EXPORT_SYMBOL(timb_start_dma); | ||
2419 | + | ||
2420 | +void *timb_stop_dma(u32 flags) | ||
2421 | +{ | ||
2422 | + int i; | ||
2423 | + unsigned long irqflags; | ||
2424 | + struct timbdma_dev *dev = self_g; | ||
2425 | + void *result = 0; | ||
2426 | + | ||
2427 | + spin_lock_irqsave(&dev->lock, irqflags); | ||
2428 | + | ||
2429 | + /* now disable the DMA transfers */ | ||
2430 | + for (i = 0; i < DMA_IRQS; i++) | ||
2431 | + if (flags & (1 << i)) { | ||
2432 | + /* | ||
2433 | + RX enable registers are located at: | ||
2434 | + 0x14 | ||
2435 | + 0x54 | ||
2436 | + 0x94 | ||
2437 | + | ||
2438 | + TX SW pointer registers are located at: | ||
2439 | + 0x24 | ||
2440 | + 0x64 | ||
2441 | + */ | ||
2442 | + u32 offset = i / 2 * 0x40; | ||
2443 | + u32 result_offset = offset; | ||
2444 | + if (!(i % 2)) { | ||
2445 | + /* even -> RX enable */ | ||
2446 | + offset += timbdma_dmacfg_RXENABLE; | ||
2447 | + result_offset += timbdma_dmacfg_RXFPGAWP; | ||
2448 | + } else { | ||
2449 | + /* odd -> TX SW pointer reg */ | ||
2450 | + offset += timbdma_dmacfg_TXSWWP; | ||
2451 | + result_offset = timbdma_dmacfg_TXFPGARP; | ||
2452 | + } | ||
2453 | + | ||
2454 | + iowrite32(0, dev->membase + offset); | ||
2455 | + /* check how far the FPGA has written/read */ | ||
2456 | + result = (void *)ioread32(dev->membase + result_offset); | ||
2457 | + } | ||
2458 | + | ||
2459 | + /* ack any pending IRQs */ | ||
2460 | + iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS); | ||
2461 | + | ||
2462 | + spin_unlock_irqrestore(&dev->lock, irqflags); | ||
2463 | + | ||
2464 | + return result; | ||
2465 | +} | ||
2466 | +EXPORT_SYMBOL(timb_stop_dma); | ||
2467 | + | ||
2468 | +void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data) | ||
2469 | +{ | ||
2470 | + int i; | ||
2471 | + unsigned long irqflags; | ||
2472 | + struct timbdma_dev *dev = self_g; | ||
2473 | + u32 ier; | ||
2474 | + | ||
2475 | + spin_lock_irqsave(&dev->lock, irqflags); | ||
2476 | + | ||
2477 | + for (i = 0; i < DMA_IRQS; i++) | ||
2478 | + if (flags & (1 << i)) { | ||
2479 | + dev->callbacks[i] = icb; | ||
2480 | + dev->callback_data[i] = data; | ||
2481 | + } | ||
2482 | + | ||
2483 | + /* Ack any pending IRQ */ | ||
2484 | + iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS); | ||
2485 | + | ||
2486 | + /* if a null callback is given -> clear interrupt, else -> enable */ | ||
2487 | + ier = ioread32(dev->membase + timbdma_ctrlmap_TIMBENABLE); | ||
2488 | + if (icb != NULL) | ||
2489 | + ier |= flags; | ||
2490 | + else | ||
2491 | + ier &= ~flags; | ||
2492 | + iowrite32(ier, dev->membase + timbdma_ctrlmap_TIMBENABLE); | ||
2493 | + | ||
2494 | + spin_unlock_irqrestore(&dev->lock, irqflags); | ||
2495 | +} | ||
2496 | +EXPORT_SYMBOL(timb_set_dma_interruptcb); | ||
2497 | + | ||
2498 | +static int timbdma_probe(struct platform_device *dev) | ||
2499 | +{ | ||
2500 | + int err, irq; | ||
2501 | + struct timbdma_dev *self; | ||
2502 | + struct resource *iomem; | ||
2503 | + | ||
2504 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
2505 | + if (!iomem) { | ||
2506 | + err = -EINVAL; | ||
2507 | + goto err_mem; | ||
2508 | + } | ||
2509 | + | ||
2510 | + self = kzalloc(sizeof(*self), GFP_KERNEL); | ||
2511 | + if (!self) { | ||
2512 | + err = -EINVAL; | ||
2513 | + goto err_mem; | ||
2514 | + } | ||
2515 | + | ||
2516 | + spin_lock_init(&self->lock); | ||
2517 | + | ||
2518 | + if (!request_mem_region(iomem->start, | ||
2519 | + resource_size(iomem), "timb-dma")) { | ||
2520 | + err = -EBUSY; | ||
2521 | + goto err_request; | ||
2522 | + } | ||
2523 | + | ||
2524 | + self->membase = ioremap(iomem->start, resource_size(iomem)); | ||
2525 | + if (!self->membase) { | ||
2526 | + printk(KERN_ERR "timbdma: Failed to remap I/O memory\n"); | ||
2527 | + err = -ENOMEM; | ||
2528 | + goto err_ioremap; | ||
2529 | + } | ||
2530 | + | ||
2531 | + /* register interrupt */ | ||
2532 | + irq = platform_get_irq(dev, 0); | ||
2533 | + if (irq < 0) { | ||
2534 | + err = irq; | ||
2535 | + goto err_get_irq; | ||
2536 | + } | ||
2537 | + | ||
2538 | + /* request IRQ */ | ||
2539 | + err = request_irq(irq, timbdma_handleinterrupt, IRQF_SHARED, | ||
2540 | + "timb-dma", self); | ||
2541 | + if (err) { | ||
2542 | + printk(KERN_ERR "timbdma: Failed to request IRQ\n"); | ||
2543 | + goto err_get_irq; | ||
2544 | + } | ||
2545 | + | ||
2546 | + platform_set_drvdata(dev, self); | ||
2547 | + | ||
2548 | + /* assign the global pointer */ | ||
2549 | + self_g = self; | ||
2550 | + | ||
2551 | + return 0; | ||
2552 | + | ||
2553 | +err_get_irq: | ||
2554 | + iounmap(self->membase); | ||
2555 | +err_ioremap: | ||
2556 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2557 | +err_request: | ||
2558 | + kfree(self); | ||
2559 | +err_mem: | ||
2560 | + printk(KERN_ERR "timberdale: Failed to register Timberdale DMA: %d\n", | ||
2561 | + err); | ||
2562 | + | ||
2563 | + return err; | ||
2564 | +} | ||
2565 | + | ||
2566 | +static int timbdma_remove(struct platform_device *dev) | ||
2567 | +{ | ||
2568 | + struct timbdma_dev *self = platform_get_drvdata(dev); | ||
2569 | + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
2570 | + | ||
2571 | + free_irq(platform_get_irq(dev, 0), self); | ||
2572 | + iounmap(self->membase); | ||
2573 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2574 | + kfree(self); | ||
2575 | + self_g = NULL; | ||
2576 | + return 0; | ||
2577 | +} | ||
2578 | + | ||
2579 | +static struct platform_driver timbdma_platform_driver = { | ||
2580 | + .driver = { | ||
2581 | + .name = "timb-dma", | ||
2582 | + .owner = THIS_MODULE, | ||
2583 | + }, | ||
2584 | + .probe = timbdma_probe, | ||
2585 | + .remove = timbdma_remove, | ||
2586 | +}; | ||
2587 | + | ||
2588 | +/*--------------------------------------------------------------------------*/ | ||
2589 | + | ||
2590 | +static int __init timbdma_init(void) | ||
2591 | +{ | ||
2592 | + self_g = NULL; | ||
2593 | + return platform_driver_register(&timbdma_platform_driver); | ||
2594 | +} | ||
2595 | + | ||
2596 | +static void __exit timbdma_exit(void) | ||
2597 | +{ | ||
2598 | + platform_driver_unregister(&timbdma_platform_driver); | ||
2599 | +} | ||
2600 | + | ||
2601 | +module_init(timbdma_init); | ||
2602 | +module_exit(timbdma_exit); | ||
2603 | + | ||
2604 | +MODULE_DESCRIPTION("Timberdale DMA driver"); | ||
2605 | +MODULE_LICENSE("GPL v2"); | ||
2606 | +MODULE_ALIAS("platform:timb-dma"); | ||
2607 | + | ||
2608 | diff -uNr linux-2.6.29-clean/drivers/mfd/timberdale.c linux-2.6.29/drivers/mfd/timberdale.c | ||
2609 | --- linux-2.6.29-clean/drivers/mfd/timberdale.c 1969-12-31 16:00:00.000000000 -0800 | ||
2610 | +++ linux-2.6.29/drivers/mfd/timberdale.c 2009-04-06 13:51:47.000000000 -0700 | ||
2611 | @@ -0,0 +1,599 @@ | ||
2612 | +/* | ||
2613 | + * timberdale.c timberdale FPGA mfd shim driver | ||
2614 | + * Copyright (c) 2009 Intel Corporation | ||
2615 | + * | ||
2616 | + * This program is free software; you can redistribute it and/or modify | ||
2617 | + * it under the terms of the GNU General Public License version 2 as | ||
2618 | + * published by the Free Software Foundation. | ||
2619 | + * | ||
2620 | + * This program is distributed in the hope that it will be useful, | ||
2621 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2622 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2623 | + * GNU General Public License for more details. | ||
2624 | + * | ||
2625 | + * You should have received a copy of the GNU General Public License | ||
2626 | + * along with this program; if not, write to the Free Software | ||
2627 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
2628 | + */ | ||
2629 | + | ||
2630 | +/* Supports: | ||
2631 | + * Timberdale FPGA | ||
2632 | + */ | ||
2633 | + | ||
2634 | +#include <linux/kernel.h> | ||
2635 | +#include <linux/module.h> | ||
2636 | +#include <linux/pci.h> | ||
2637 | +#include <linux/msi.h> | ||
2638 | +#include <linux/init.h> | ||
2639 | +#include <linux/interrupt.h> | ||
2640 | +#include <linux/platform_device.h> | ||
2641 | +#include <linux/mfd/core.h> | ||
2642 | +#include <linux/irq.h> | ||
2643 | + | ||
2644 | +#include <linux/i2c.h> | ||
2645 | +#include <linux/i2c-ocores.h> | ||
2646 | +#include <linux/i2c/tsc2007.h> | ||
2647 | +#include <linux/spi/xilinx_spi.h> | ||
2648 | +#include "timberdale.h" | ||
2649 | + | ||
2650 | +struct timberdale_device { | ||
2651 | + resource_size_t intc_mapbase; | ||
2652 | + resource_size_t ctl_mapbase; | ||
2653 | + unsigned char __iomem *intc_membase; | ||
2654 | + unsigned char __iomem *ctl_membase; | ||
2655 | + int irq_base; | ||
2656 | + u32 irq_ack_mask; | ||
2657 | + /* locking from interrupts while modifiying registers */ | ||
2658 | + spinlock_t lock; | ||
2659 | +}; | ||
2660 | + | ||
2661 | +/*--------------------------------------------------------------------------*/ | ||
2662 | + | ||
2663 | +struct tsc2007_platform_data timberdale_tsc2007_platform_data = { | ||
2664 | + .model = 2003, | ||
2665 | + .x_plate_ohms = 100 | ||
2666 | +}; | ||
2667 | + | ||
2668 | +struct i2c_board_info timberdale_i2c_board_info[] = { | ||
2669 | + { | ||
2670 | + I2C_BOARD_INFO("tsc2003", 0x48), | ||
2671 | + .platform_data = &timberdale_tsc2007_platform_data, | ||
2672 | + .irq = IRQ_TIMBERDALE_TSC_INT | ||
2673 | + }, | ||
2674 | + { | ||
2675 | + I2C_BOARD_INFO("adv7180", 0x42 >> 1), | ||
2676 | + .irq = IRQ_TIMBERDALE_ADV7180 | ||
2677 | + } | ||
2678 | +}; | ||
2679 | + | ||
2680 | +static __devinitdata struct ocores_i2c_platform_data | ||
2681 | +timberdale_i2c_platform_data = { | ||
2682 | + .regstep = 4, | ||
2683 | + .clock_khz = 62500, | ||
2684 | + .devices = timberdale_i2c_board_info, | ||
2685 | + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) | ||
2686 | +}; | ||
2687 | + | ||
2688 | +const static __devinitconst struct resource timberdale_i2c_resources[] = { | ||
2689 | + { | ||
2690 | + .start = I2COFFSET, | ||
2691 | + .end = I2CEND, | ||
2692 | + .flags = IORESOURCE_MEM, | ||
2693 | + }, | ||
2694 | + { | ||
2695 | + .start = IRQ_TIMBERDALE_I2C, | ||
2696 | + .end = IRQ_TIMBERDALE_I2C, | ||
2697 | + .flags = IORESOURCE_IRQ, | ||
2698 | + }, | ||
2699 | +}; | ||
2700 | + | ||
2701 | +static __devinitdata struct xspi_platform_data timberdale_xspi_platorm_data = { | ||
2702 | + .bus_num = -1, | ||
2703 | + /* according to spec. we can have up to 32 slaves however, | ||
2704 | + * as of current(2009-03-06) revision of | ||
2705 | + * Timberdale we can only handle 3 right now | ||
2706 | + */ | ||
2707 | + .num_chipselect = 3, | ||
2708 | + .speed_hz = 1953125, /* hardcoded value in IP, for now */ | ||
2709 | + .cr_offset = 0x60, | ||
2710 | + .sr_offset = 0x64, | ||
2711 | + .txd_offset = 0x68, | ||
2712 | + .rxd_offset = 0x6c, | ||
2713 | + .ssr_offset = 0x70 | ||
2714 | +}; | ||
2715 | + | ||
2716 | +const static __devinitconst struct resource timberdale_spi_resources[] = { | ||
2717 | + { | ||
2718 | + .start = SPIOFFSET, | ||
2719 | + .end = SPIEND, | ||
2720 | + .flags = IORESOURCE_MEM, | ||
2721 | + }, | ||
2722 | + { | ||
2723 | + .start = IRQ_TIMBERDALE_SPI, | ||
2724 | + .end = IRQ_TIMBERDALE_SPI, | ||
2725 | + .flags = IORESOURCE_IRQ, | ||
2726 | + }, | ||
2727 | +}; | ||
2728 | + | ||
2729 | +const static __devinitconst struct resource timberdale_eth_resources[] = { | ||
2730 | + { | ||
2731 | + .start = ETHOFFSET, | ||
2732 | + .end = ETHEND, | ||
2733 | + .flags = IORESOURCE_MEM, | ||
2734 | + }, | ||
2735 | + { | ||
2736 | + .start = IRQ_TIMBERDALE_ETHSW_IF, | ||
2737 | + .end = IRQ_TIMBERDALE_ETHSW_IF, | ||
2738 | + .flags = IORESOURCE_IRQ, | ||
2739 | + }, | ||
2740 | +}; | ||
2741 | + | ||
2742 | +const static __devinitconst struct resource timberdale_gpio_resources[] = { | ||
2743 | + { | ||
2744 | + .start = GPIOOFFSET, | ||
2745 | + .end = GPIOEND, | ||
2746 | + .flags = IORESOURCE_MEM, | ||
2747 | + }, | ||
2748 | + { | ||
2749 | + .start = IRQ_TIMBERDALE_GPIO, | ||
2750 | + .end = IRQ_TIMBERDALE_GPIO, | ||
2751 | + .flags = IORESOURCE_IRQ, | ||
2752 | + }, | ||
2753 | +}; | ||
2754 | + | ||
2755 | + | ||
2756 | +const static __devinitconst struct resource timberdale_most_resources[] = { | ||
2757 | + { | ||
2758 | + .start = MOSTOFFSET, | ||
2759 | + .end = MOSTEND, | ||
2760 | + .flags = IORESOURCE_MEM, | ||
2761 | + }, | ||
2762 | + { | ||
2763 | + .start = IRQ_TIMBERDALE_MLB, | ||
2764 | + .end = IRQ_TIMBERDALE_MLB, | ||
2765 | + .flags = IORESOURCE_IRQ, | ||
2766 | + }, | ||
2767 | +}; | ||
2768 | + | ||
2769 | +const static __devinitconst struct resource timberdale_uart_resources[] = { | ||
2770 | + { | ||
2771 | + .start = UARTOFFSET, | ||
2772 | + .end = UARTEND, | ||
2773 | + .flags = IORESOURCE_MEM, | ||
2774 | + }, | ||
2775 | + { | ||
2776 | + .start = IRQ_TIMBERDALE_UART, | ||
2777 | + .end = IRQ_TIMBERDALE_UART, | ||
2778 | + .flags = IORESOURCE_IRQ, | ||
2779 | + }, | ||
2780 | +}; | ||
2781 | + | ||
2782 | +const static __devinitconst struct resource timberdale_i2s_resources[] = { | ||
2783 | + { | ||
2784 | + .start = I2SOFFSET, | ||
2785 | + .end = I2SEND, | ||
2786 | + .flags = IORESOURCE_MEM, | ||
2787 | + }, | ||
2788 | + { | ||
2789 | + .start = IRQ_TIMBERDALE_I2S, | ||
2790 | + .end = IRQ_TIMBERDALE_I2S, | ||
2791 | + .flags = IORESOURCE_IRQ, | ||
2792 | + }, | ||
2793 | +}; | ||
2794 | + | ||
2795 | +const static __devinitconst struct resource timberdale_video_resources[] = { | ||
2796 | + { | ||
2797 | + .start = LOGIWOFFSET, | ||
2798 | + .end = LOGIWEND, | ||
2799 | + .flags = IORESOURCE_MEM, | ||
2800 | + }, | ||
2801 | + /* | ||
2802 | + note that the "frame buffer" is located in DMA area | ||
2803 | + starting at 0x1200000 | ||
2804 | + */ | ||
2805 | +}; | ||
2806 | + | ||
2807 | +const static __devinitconst struct resource timberdale_dma_resources[] = { | ||
2808 | + { | ||
2809 | + .start = DMAOFFSET, | ||
2810 | + .end = DMAEND, | ||
2811 | + .flags = IORESOURCE_MEM, | ||
2812 | + }, | ||
2813 | + { | ||
2814 | + .start = IRQ_TIMBERDALE_DMA, | ||
2815 | + .end = IRQ_TIMBERDALE_DMA, | ||
2816 | + .flags = IORESOURCE_IRQ, | ||
2817 | + }, | ||
2818 | +}; | ||
2819 | + | ||
2820 | +static __devinitdata struct mfd_cell timberdale_cells_bar0[] = { | ||
2821 | + { | ||
2822 | + .name = "timb-uart", | ||
2823 | + .num_resources = ARRAY_SIZE(timberdale_uart_resources), | ||
2824 | + .resources = timberdale_uart_resources, | ||
2825 | + }, | ||
2826 | + { | ||
2827 | + .name = "ocores-i2c", | ||
2828 | + .num_resources = ARRAY_SIZE(timberdale_i2c_resources), | ||
2829 | + .resources = timberdale_i2c_resources, | ||
2830 | + .platform_data = &timberdale_i2c_platform_data, | ||
2831 | + .data_size = sizeof(timberdale_i2c_platform_data), | ||
2832 | + }, | ||
2833 | + { | ||
2834 | + .name = "timb-gpio", | ||
2835 | + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), | ||
2836 | + .resources = timberdale_gpio_resources, | ||
2837 | + }, | ||
2838 | + { | ||
2839 | + .name = "timb-i2s", | ||
2840 | + .num_resources = ARRAY_SIZE(timberdale_i2s_resources), | ||
2841 | + .resources = timberdale_i2s_resources, | ||
2842 | + }, | ||
2843 | + { | ||
2844 | + .name = "timb-most", | ||
2845 | + .num_resources = ARRAY_SIZE(timberdale_most_resources), | ||
2846 | + .resources = timberdale_most_resources, | ||
2847 | + }, | ||
2848 | + { | ||
2849 | + .name = "timb-video", | ||
2850 | + .num_resources = ARRAY_SIZE(timberdale_video_resources), | ||
2851 | + .resources = timberdale_video_resources, | ||
2852 | + }, | ||
2853 | + { | ||
2854 | + .name = "xilinx_spi", | ||
2855 | + .num_resources = ARRAY_SIZE(timberdale_spi_resources), | ||
2856 | + .resources = timberdale_spi_resources, | ||
2857 | + .platform_data = &timberdale_xspi_platorm_data, | ||
2858 | + .data_size = sizeof(timberdale_xspi_platorm_data), | ||
2859 | + }, | ||
2860 | + { | ||
2861 | + .name = "ks884x", | ||
2862 | + .num_resources = ARRAY_SIZE(timberdale_eth_resources), | ||
2863 | + .resources = timberdale_eth_resources, | ||
2864 | + }, | ||
2865 | + { | ||
2866 | + .name = "timb-dma", | ||
2867 | + .num_resources = ARRAY_SIZE(timberdale_dma_resources), | ||
2868 | + .resources = timberdale_dma_resources, | ||
2869 | + }, | ||
2870 | +}; | ||
2871 | + | ||
2872 | +static const __devinitconst struct resource timberdale_sdhc_resources_bar1[] = { | ||
2873 | + { | ||
2874 | + .start = SDHC0OFFSET, | ||
2875 | + .end = SDHC0END, | ||
2876 | + .flags = IORESOURCE_MEM, | ||
2877 | + }, | ||
2878 | + { | ||
2879 | + .start = IRQ_TIMBERDALE_SDHC, | ||
2880 | + .end = IRQ_TIMBERDALE_SDHC, | ||
2881 | + .flags = IORESOURCE_IRQ, | ||
2882 | + }, | ||
2883 | +}; | ||
2884 | + | ||
2885 | +static __devinitdata struct mfd_cell timberdale_cells_bar1[] = { | ||
2886 | + { | ||
2887 | + .name = "sdhci", | ||
2888 | + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources_bar1), | ||
2889 | + .resources = timberdale_sdhc_resources_bar1, | ||
2890 | + }, | ||
2891 | +}; | ||
2892 | + | ||
2893 | +/*--------------------------------------------------------------------------*/ | ||
2894 | + | ||
2895 | + | ||
2896 | +/* Handle the timberdale interrupt mux */ | ||
2897 | +static void timberdale_irq(unsigned int irq, struct irq_desc *desc) | ||
2898 | +{ | ||
2899 | + struct timberdale_device *priv = get_irq_data(irq); | ||
2900 | + unsigned int i, ipr; | ||
2901 | + | ||
2902 | + desc->chip->ack(irq); | ||
2903 | + | ||
2904 | + while ((ipr = ioread32(priv->intc_membase + IPR))) { | ||
2905 | + priv->irq_ack_mask = 0; | ||
2906 | + for (i = 0; i < TIMBERDALE_NR_IRQS; i++) | ||
2907 | + if (ipr & (1 << i)) | ||
2908 | + generic_handle_irq(priv->irq_base + i); | ||
2909 | + if (priv->irq_ack_mask) | ||
2910 | + iowrite32(priv->irq_ack_mask, priv->intc_membase + IAR); | ||
2911 | + } | ||
2912 | +} | ||
2913 | + | ||
2914 | +static void timberdale_irq_mask(unsigned int irq) | ||
2915 | +{ | ||
2916 | + struct timberdale_device *priv = get_irq_chip_data(irq); | ||
2917 | + unsigned long flags; | ||
2918 | + | ||
2919 | + spin_lock_irqsave(&priv->lock, flags); | ||
2920 | + iowrite32(1 << (irq - priv->irq_base), priv->intc_membase + CIE); | ||
2921 | + spin_unlock_irqrestore(&priv->lock, flags); | ||
2922 | +} | ||
2923 | + | ||
2924 | +static void timberdale_irq_unmask(unsigned int irq) | ||
2925 | +{ | ||
2926 | + struct timberdale_device *priv = get_irq_chip_data(irq); | ||
2927 | + unsigned long flags; | ||
2928 | + | ||
2929 | + spin_lock_irqsave(&priv->lock, flags); | ||
2930 | + iowrite32(1 << (irq - priv->irq_base), priv->intc_membase + SIE); | ||
2931 | + spin_unlock_irqrestore(&priv->lock, flags); | ||
2932 | +} | ||
2933 | + | ||
2934 | +static void timberdale_irq_ack(unsigned int irq) | ||
2935 | +{ | ||
2936 | + struct timberdale_device *priv = get_irq_chip_data(irq); | ||
2937 | + unsigned long flags; | ||
2938 | + u32 ack_mask = 1 << (irq - priv->irq_base); | ||
2939 | + | ||
2940 | + spin_lock_irqsave(&priv->lock, flags); | ||
2941 | + /* if edge triggered, ack directly. Otherwhise ack in the end of | ||
2942 | + * irq handler | ||
2943 | + */ | ||
2944 | + if (ack_mask & IRQ_TIMBERDALE_EDGE_MASK) | ||
2945 | + iowrite32(ack_mask, priv->intc_membase + IAR); | ||
2946 | + else | ||
2947 | + priv->irq_ack_mask |= ack_mask; | ||
2948 | + spin_unlock_irqrestore(&priv->lock, flags); | ||
2949 | +} | ||
2950 | + | ||
2951 | +static struct irq_chip timberdale_chip = { | ||
2952 | + .name = "timberdale", | ||
2953 | + .ack = timberdale_irq_ack, | ||
2954 | + .mask = timberdale_irq_mask, | ||
2955 | + .unmask = timberdale_irq_unmask, | ||
2956 | + .disable = timberdale_irq_mask, | ||
2957 | + .enable = timberdale_irq_unmask, | ||
2958 | +}; | ||
2959 | + | ||
2960 | +/*--------------------------------------------------------------------------*/ | ||
2961 | + | ||
2962 | +/* Install the IRQ handler */ | ||
2963 | +static void timberdale_attach_irq(struct pci_dev *dev) | ||
2964 | +{ | ||
2965 | + struct timberdale_device *priv = pci_get_drvdata(dev); | ||
2966 | + unsigned int irq, irq_base; | ||
2967 | + | ||
2968 | + irq_base = priv->irq_base; | ||
2969 | + for (irq = irq_base; irq < irq_base + TIMBERDALE_NR_IRQS; irq++) { | ||
2970 | + set_irq_chip_and_handler_name(irq, &timberdale_chip, | ||
2971 | + handle_edge_irq, "mux"); | ||
2972 | + | ||
2973 | + set_irq_chip_data(irq, priv); | ||
2974 | + | ||
2975 | +#ifdef CONFIG_ARM | ||
2976 | + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
2977 | +#endif | ||
2978 | + } | ||
2979 | + | ||
2980 | + set_irq_data(dev->irq, priv); | ||
2981 | + set_irq_chained_handler(dev->irq, timberdale_irq); | ||
2982 | +} | ||
2983 | + | ||
2984 | +static void timberdale_detach_irq(struct pci_dev *dev) | ||
2985 | +{ | ||
2986 | + struct timberdale_device *priv = pci_get_drvdata(dev); | ||
2987 | + unsigned int irq, irq_base; | ||
2988 | + | ||
2989 | + irq_base = priv->irq_base; | ||
2990 | + | ||
2991 | + set_irq_chained_handler(dev->irq, NULL); | ||
2992 | + set_irq_data(dev->irq, NULL); | ||
2993 | + | ||
2994 | + for (irq = irq_base; irq < irq_base + TIMBERDALE_NR_IRQS; irq++) { | ||
2995 | +#ifdef CONFIG_ARM | ||
2996 | + set_irq_flags(irq, 0); | ||
2997 | +#endif | ||
2998 | + set_irq_chip(irq, NULL); | ||
2999 | + set_irq_chip_data(irq, NULL); | ||
3000 | + } | ||
3001 | +} | ||
3002 | + | ||
3003 | +static int __devinit timb_probe(struct pci_dev *dev, | ||
3004 | + const struct pci_device_id *id) | ||
3005 | +{ | ||
3006 | + struct timberdale_device *priv; | ||
3007 | + int err, i; | ||
3008 | + u16 ver; | ||
3009 | + resource_size_t mapbase; | ||
3010 | + | ||
3011 | + priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
3012 | + if (!priv) | ||
3013 | + return -ENOMEM; | ||
3014 | + | ||
3015 | + spin_lock_init(&priv->lock); | ||
3016 | + pci_set_drvdata(dev, priv); | ||
3017 | + | ||
3018 | + err = pci_enable_device(dev); | ||
3019 | + if (err) | ||
3020 | + goto err_enable; | ||
3021 | + | ||
3022 | + mapbase = pci_resource_start(dev, 0); | ||
3023 | + if (!mapbase) { | ||
3024 | + printk(KERN_ERR "timberdale: No resource\n"); | ||
3025 | + goto err_start; | ||
3026 | + } | ||
3027 | + | ||
3028 | + /* create a resource for the Interrupt controller registers */ | ||
3029 | + priv->intc_mapbase = mapbase + INTCOFFSET; | ||
3030 | + if (!request_mem_region(priv->intc_mapbase, INTCSIZE, "timb-intc")) { | ||
3031 | + printk(KERN_ERR "timberdale: Failed to request intc mem\n"); | ||
3032 | + goto err_request; | ||
3033 | + } | ||
3034 | + | ||
3035 | + /* create a resource for the PCI master register */ | ||
3036 | + priv->ctl_mapbase = mapbase + CHIPCTLOFFSET; | ||
3037 | + if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-intc")) { | ||
3038 | + printk(KERN_ERR "timberdale: Failed to request ctl mem\n"); | ||
3039 | + goto err_request_ctl; | ||
3040 | + } | ||
3041 | + | ||
3042 | + priv->intc_membase = ioremap(priv->intc_mapbase, INTCSIZE); | ||
3043 | + if (!priv->intc_membase) { | ||
3044 | + printk(KERN_ALERT "timberdale: Map error, intc\n"); | ||
3045 | + goto err_ioremap; | ||
3046 | + } | ||
3047 | + | ||
3048 | + priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); | ||
3049 | + if (!priv->ctl_membase) { | ||
3050 | + printk(KERN_ALERT "timberdale: Map error, ctl\n"); | ||
3051 | + goto err_ioremap_ctl; | ||
3052 | + } | ||
3053 | + | ||
3054 | + err = pci_enable_msi(dev); | ||
3055 | + if (err) { | ||
3056 | + printk(KERN_WARNING "timberdale: MSI init failed: %d\n", err); | ||
3057 | + goto err_msi; | ||
3058 | + } | ||
3059 | + | ||
3060 | + /* Reset all FPGA PLB peripherals */ | ||
3061 | + iowrite32(0x1, priv->ctl_membase + MAYSVILLERST); | ||
3062 | + | ||
3063 | + /* at this stage the FPGA does not generate a | ||
3064 | + * unique interrupt per function, to emulate real interrupts | ||
3065 | + * we assign them a faked interrupt which we issue in the | ||
3066 | + * interrupt handler. For now just hard code a base number | ||
3067 | + */ | ||
3068 | + priv->irq_base = NR_IRQS - TIMBERDALE_NR_IRQS - 1; | ||
3069 | + if (priv->irq_base < dev->irq) | ||
3070 | + /* ops the device itself got the IRQ in the end... */ | ||
3071 | + priv->irq_base = 400; | ||
3072 | + | ||
3073 | + timberdale_attach_irq(dev); | ||
3074 | + | ||
3075 | + /* update IRQ offsets in I2C board info */ | ||
3076 | + for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++) | ||
3077 | + timberdale_i2c_board_info[i].irq += priv->irq_base; | ||
3078 | + | ||
3079 | + /* don't leave platform_data empty on any device */ | ||
3080 | + for (i = 0; i < ARRAY_SIZE(timberdale_cells_bar0); i++) | ||
3081 | + if (timberdale_cells_bar0[i].platform_data == NULL) { | ||
3082 | + timberdale_cells_bar0[i].platform_data = | ||
3083 | + timberdale_cells_bar0 + i; | ||
3084 | + timberdale_cells_bar0[i].data_size = | ||
3085 | + sizeof(timberdale_cells_bar0[i]); | ||
3086 | + } | ||
3087 | + | ||
3088 | + err = mfd_add_devices(&dev->dev, -1, | ||
3089 | + timberdale_cells_bar0, ARRAY_SIZE(timberdale_cells_bar0), | ||
3090 | + &dev->resource[0], priv->irq_base); | ||
3091 | + if (err) | ||
3092 | + printk(KERN_WARNING | ||
3093 | + "timberdale: mfd_add_devices failed: %d\n", err); | ||
3094 | + else { | ||
3095 | + err = mfd_add_devices(&dev->dev, -1, | ||
3096 | + timberdale_cells_bar1, | ||
3097 | + ARRAY_SIZE(timberdale_cells_bar1), | ||
3098 | + &dev->resource[1], priv->irq_base); | ||
3099 | + | ||
3100 | + if (err) | ||
3101 | + printk(KERN_WARNING | ||
3102 | + "timberdale: timb_add_sdhci failed: %d\n", err); | ||
3103 | + } | ||
3104 | + | ||
3105 | + if (err) | ||
3106 | + goto err_mfd; | ||
3107 | + | ||
3108 | + ver = ioread16(priv->ctl_membase + TIMB_REV); | ||
3109 | + | ||
3110 | + printk(KERN_INFO "Found Maysville Card. Rev: %d\n", ver); | ||
3111 | + | ||
3112 | + /* Enable interrupts and wire the hardware interrupts */ | ||
3113 | + iowrite32(0x3, priv->intc_membase + MER); | ||
3114 | + | ||
3115 | + return 0; | ||
3116 | +err_mfd: | ||
3117 | + timberdale_detach_irq(dev); | ||
3118 | + pci_disable_msi(dev); | ||
3119 | +err_msi: | ||
3120 | + iounmap(priv->ctl_membase); | ||
3121 | +err_ioremap_ctl: | ||
3122 | + iounmap(priv->intc_membase); | ||
3123 | +err_ioremap: | ||
3124 | + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); | ||
3125 | +err_request_ctl: | ||
3126 | + release_mem_region(priv->intc_mapbase, INTCSIZE); | ||
3127 | +err_request: | ||
3128 | + pci_set_drvdata(dev, NULL); | ||
3129 | +err_start: | ||
3130 | + pci_disable_device(dev); | ||
3131 | +err_enable: | ||
3132 | + kfree(priv); | ||
3133 | + pci_set_drvdata(dev, NULL); | ||
3134 | + return -ENODEV; | ||
3135 | +} | ||
3136 | + | ||
3137 | +static void __devexit timb_remove(struct pci_dev *dev) | ||
3138 | +{ | ||
3139 | + /* clean up any allocated resources and stuff here. | ||
3140 | + * like call release_region(); | ||
3141 | + */ | ||
3142 | + struct timberdale_device *priv; | ||
3143 | + | ||
3144 | + priv = pci_get_drvdata(dev); | ||
3145 | + | ||
3146 | + mfd_remove_devices(&dev->dev); | ||
3147 | + | ||
3148 | + timberdale_detach_irq(dev); | ||
3149 | + | ||
3150 | + iowrite32(0xffffffff, priv->intc_membase + IAR); | ||
3151 | + iowrite32(0, priv->intc_membase + MER); | ||
3152 | + iowrite32(0, priv->intc_membase + IER); | ||
3153 | + | ||
3154 | + iounmap(priv->ctl_membase); | ||
3155 | + iounmap(priv->intc_membase); | ||
3156 | + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); | ||
3157 | + release_mem_region(priv->intc_mapbase, INTCSIZE); | ||
3158 | + | ||
3159 | + pci_disable_msi(dev); | ||
3160 | + pci_disable_device(dev); | ||
3161 | + pci_set_drvdata(dev, NULL); | ||
3162 | + kfree(priv); | ||
3163 | +} | ||
3164 | + | ||
3165 | +static struct pci_device_id timberdale_pci_tbl[] = { | ||
3166 | + { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) }, | ||
3167 | + { 0 } | ||
3168 | +}; | ||
3169 | +MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl); | ||
3170 | + | ||
3171 | +static struct pci_driver timberdale_pci_driver = { | ||
3172 | + .name = "timberdale", | ||
3173 | + .id_table = timberdale_pci_tbl, | ||
3174 | + .probe = timb_probe, | ||
3175 | + .remove = timb_remove, | ||
3176 | +}; | ||
3177 | + | ||
3178 | +static int __init timberdale_init(void) | ||
3179 | +{ | ||
3180 | + int err; | ||
3181 | + | ||
3182 | + err = pci_register_driver(&timberdale_pci_driver); | ||
3183 | + if (err < 0) { | ||
3184 | + printk(KERN_ERR | ||
3185 | + "Failed to register PCI driver for %s device.\n", | ||
3186 | + timberdale_pci_driver.name); | ||
3187 | + return -ENODEV; | ||
3188 | + } | ||
3189 | + | ||
3190 | + printk(KERN_INFO "Driver for %s has been successfully registered.\n", | ||
3191 | + timberdale_pci_driver.name); | ||
3192 | + | ||
3193 | + return 0; | ||
3194 | +} | ||
3195 | + | ||
3196 | +static void __exit timberdale_exit(void) | ||
3197 | +{ | ||
3198 | + pci_unregister_driver(&timberdale_pci_driver); | ||
3199 | + | ||
3200 | + printk(KERN_INFO "Driver for %s has been successfully unregistered.\n", | ||
3201 | + timberdale_pci_driver.name); | ||
3202 | +} | ||
3203 | + | ||
3204 | +MODULE_LICENSE("GPL v2"); | ||
3205 | +MODULE_VERSION(DRV_VERSION); | ||
3206 | +MODULE_AUTHOR("Richard Rojfors"); | ||
3207 | + | ||
3208 | +module_init(timberdale_init); | ||
3209 | +module_exit(timberdale_exit); | ||
3210 | + | ||
3211 | diff -uNr linux-2.6.29-clean/drivers/mfd/timberdale.h linux-2.6.29/drivers/mfd/timberdale.h | ||
3212 | --- linux-2.6.29-clean/drivers/mfd/timberdale.h 1969-12-31 16:00:00.000000000 -0800 | ||
3213 | +++ linux-2.6.29/drivers/mfd/timberdale.h 2009-04-06 13:51:47.000000000 -0700 | ||
3214 | @@ -0,0 +1,114 @@ | ||
3215 | +/* | ||
3216 | + * timberdale.h timberdale FPGA mfd shim driver defines | ||
3217 | + * Copyright (c) 2009 Intel Corporation | ||
3218 | + * | ||
3219 | + * This program is free software; you can redistribute it and/or modify | ||
3220 | + * it under the terms of the GNU General Public License version 2 as | ||
3221 | + * published by the Free Software Foundation. | ||
3222 | + * | ||
3223 | + * This program is distributed in the hope that it will be useful, | ||
3224 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3225 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3226 | + * GNU General Public License for more details. | ||
3227 | + * | ||
3228 | + * You should have received a copy of the GNU General Public License | ||
3229 | + * along with this program; if not, write to the Free Software | ||
3230 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
3231 | + */ | ||
3232 | + | ||
3233 | +/* Supports: | ||
3234 | + * Timberdale FPGA | ||
3235 | + */ | ||
3236 | + | ||
3237 | +#ifndef MFD_TIMBERDALE_H | ||
3238 | +#define MFD_TIMBERDALE_H | ||
3239 | + | ||
3240 | +/* Registers of the interrupt controller */ | ||
3241 | +#define ISR 0x00 | ||
3242 | +#define IPR 0x04 | ||
3243 | +#define IER 0x08 | ||
3244 | +#define IAR 0x0c | ||
3245 | +#define SIE 0x10 | ||
3246 | +#define CIE 0x14 | ||
3247 | +#define MER 0x1c | ||
3248 | + | ||
3249 | +/* Registers of the control area */ | ||
3250 | +#define TIMB_REV 0x00 | ||
3251 | +#define MAYSVILLERST 0x40 | ||
3252 | + | ||
3253 | + | ||
3254 | +#define I2COFFSET 0x0 | ||
3255 | +#define I2CEND 0x1f | ||
3256 | + | ||
3257 | +#define SPIOFFSET 0x80 | ||
3258 | +#define SPIEND 0xff | ||
3259 | + | ||
3260 | +#define ETHOFFSET 0x300 | ||
3261 | +#define ETHEND 0x30f | ||
3262 | + | ||
3263 | +#define GPIOOFFSET 0x400 | ||
3264 | +#define GPIOEND 0x7ff | ||
3265 | + | ||
3266 | +#define CHIPCTLOFFSET 0x800 | ||
3267 | +#define CHIPCTLEND 0x8ff | ||
3268 | +#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) | ||
3269 | + | ||
3270 | +#define INTCOFFSET 0xc00 | ||
3271 | +#define INTCEND 0xfff | ||
3272 | +#define INTCSIZE (INTCEND - INTCOFFSET) | ||
3273 | + | ||
3274 | +#define MOSTOFFSET 0x1000 | ||
3275 | +#define MOSTEND 0x13ff | ||
3276 | + | ||
3277 | +#define UARTOFFSET 0x1400 | ||
3278 | +#define UARTEND 0x17ff | ||
3279 | + | ||
3280 | +#define I2SOFFSET 0x1C00 | ||
3281 | +#define I2SEND 0x1fff | ||
3282 | + | ||
3283 | +#define LOGIWOFFSET 0x30000 | ||
3284 | +#define LOGIWEND 0x37fff | ||
3285 | + | ||
3286 | +#define DMAOFFSET 0x01000000 | ||
3287 | +#define DMAEND 0x013fffff | ||
3288 | + | ||
3289 | +/* SDHC0 is placed in PCI bar 1 */ | ||
3290 | +#define SDHC0OFFSET 0x00 | ||
3291 | +#define SDHC0END 0xff | ||
3292 | + | ||
3293 | +/* SDHC1 is placed in PCI bar 2 */ | ||
3294 | +#define SDHC1OFFSET 0x00 | ||
3295 | +#define SDHC1END 0xff | ||
3296 | + | ||
3297 | +#define PCI_VENDOR_ID_TIMB 0x10ee | ||
3298 | +#define PCI_DEVICE_ID_TIMB 0xa123 | ||
3299 | +#define DRV_VERSION "0.1" | ||
3300 | + | ||
3301 | + | ||
3302 | +#define IRQ_TIMBERDALE_INIC 0 | ||
3303 | +#define IRQ_TIMBERDALE_MLB 1 | ||
3304 | +#define IRQ_TIMBERDALE_GPIO 2 | ||
3305 | +#define IRQ_TIMBERDALE_I2C 3 | ||
3306 | +#define IRQ_TIMBERDALE_UART 4 | ||
3307 | +#define IRQ_TIMBERDALE_DMA 5 | ||
3308 | +#define IRQ_TIMBERDALE_I2S 6 | ||
3309 | +#define IRQ_TIMBERDALE_TSC_INT 7 | ||
3310 | +#define IRQ_TIMBERDALE_SDHC 8 | ||
3311 | +#define IRQ_TIMBERDALE_ADV7180 9 | ||
3312 | +#define IRQ_TIMBERDALE_ETHSW_IF 10 | ||
3313 | +#define IRQ_TIMBERDALE_SPI 11 | ||
3314 | + | ||
3315 | +#define TIMBERDALE_NR_IRQS 12 | ||
3316 | + | ||
3317 | +/* Some of the interrupts are level triggered, some are edge triggered */ | ||
3318 | +#define IRQ_TIMBERDALE_EDGE_MASK ((1 << IRQ_TIMBERDALE_ADV7180) | \ | ||
3319 | + (1 << IRQ_TIMBERDALE_TSC_INT) | (1 << IRQ_TIMBERDALE_DMA) | \ | ||
3320 | + (1 << IRQ_TIMBERDALE_MLB) | (1 << IRQ_TIMBERDALE_INIC)) | ||
3321 | + | ||
3322 | +#define IRQ_TIMBERDALE_LEVEL_MASK ((1 << IRQ_TIMBERDALE_SPI) | \ | ||
3323 | + (1 << IRQ_TIMBERDALE_ETHSW_IF) | (1 << IRQ_TIMBERDALE_SDHC) | \ | ||
3324 | + (1 << IRQ_TIMBERDALE_I2S) | (1 << IRQ_TIMBERDALE_UART) | \ | ||
3325 | + (1 << IRQ_TIMBERDALE_I2C) | (1 << IRQ_TIMBERDALE_GPIO)) | ||
3326 | + | ||
3327 | +#endif | ||
3328 | + | ||
3329 | diff -uNr linux-2.6.29-clean/drivers/mfd/timbi2s.c linux-2.6.29/drivers/mfd/timbi2s.c | ||
3330 | --- linux-2.6.29-clean/drivers/mfd/timbi2s.c 1969-12-31 16:00:00.000000000 -0800 | ||
3331 | +++ linux-2.6.29/drivers/mfd/timbi2s.c 2009-04-06 13:51:47.000000000 -0700 | ||
3332 | @@ -0,0 +1,597 @@ | ||
3333 | +/* | ||
3334 | + * timbi2s.c timberdale FPGA I2S driver | ||
3335 | + * Copyright (c) 2009 Intel Corporation | ||
3336 | + * | ||
3337 | + * This program is free software; you can redistribute it and/or modify | ||
3338 | + * it under the terms of the GNU General Public License version 2 as | ||
3339 | + * published by the Free Software Foundation. | ||
3340 | + * | ||
3341 | + * This program is distributed in the hope that it will be useful, | ||
3342 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3343 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3344 | + * GNU General Public License for more details. | ||
3345 | + * | ||
3346 | + * You should have received a copy of the GNU General Public License | ||
3347 | + * along with this program; if not, write to the Free Software | ||
3348 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
3349 | + */ | ||
3350 | + | ||
3351 | +/* Supports: | ||
3352 | + * Timberdale FPGA I2S | ||
3353 | + * | ||
3354 | + * As of 2009-03-23 I2S instances | ||
3355 | + * are not configured as masters | ||
3356 | + * | ||
3357 | + * TODO: implement switching between master and slave | ||
3358 | + */ | ||
3359 | + | ||
3360 | +#include <linux/io.h> | ||
3361 | +#include <linux/fs.h> | ||
3362 | +#include <linux/module.h> | ||
3363 | +#include <linux/circ_buf.h> | ||
3364 | +#include <linux/spinlock.h> | ||
3365 | +#include <linux/workqueue.h> | ||
3366 | +#include <linux/interrupt.h> | ||
3367 | +#include <linux/platform_device.h> | ||
3368 | + | ||
3369 | +#include <linux/mfd/timbi2s.h> | ||
3370 | + | ||
3371 | +#define DRIVER_NAME "timb-i2s" | ||
3372 | + | ||
3373 | +#define I2S_CLOCK_SPEED 62500000 /* 62,5MHz */ | ||
3374 | + | ||
3375 | +#define FIFO_FILL_SIZE 127 | ||
3376 | +#define I2S_BUFFER_SIZE PAGE_SIZE | ||
3377 | + | ||
3378 | +#define ALMOST_FULL 170 | ||
3379 | +#define ALMOST_EMPTY 85 | ||
3380 | + | ||
3381 | +/* As of 2009-03-16, IP can instanciate max. 4 RX and 4 TX */ | ||
3382 | +#define MAX_TX_NR 4 | ||
3383 | +#define MAX_RX_NR 4 | ||
3384 | +/* and actually up and running only 4. | ||
3385 | + * 1 TX and 3 RX | ||
3386 | + */ | ||
3387 | +#define IP_I2S_NR 4 | ||
3388 | +#define REGSTEP 0x04 | ||
3389 | + | ||
3390 | +#define VERSION 0x00 | ||
3391 | +#define I2S_UIR 0x04 /* Unit Interrupt Register */ | ||
3392 | + | ||
3393 | +/* Registers for all possible I2S IP instances | ||
3394 | + * are the same as for first one (from 0x08 to 0x20) | ||
3395 | + */ | ||
3396 | +#define I2S_PRESCALE 0x08 /* Holds prescale value, if clock master */ | ||
3397 | +#define I2S_ICR 0x0c /* Interrupt Clear Register */ | ||
3398 | +# define ICR_F 0x01 /* Full */ | ||
3399 | +# define ICR_AF 0x02 /* Almost full */ | ||
3400 | +# define ICR_AE 0x04 /* Almost empty */ | ||
3401 | +# define ICR_RX_D 0x08 /* Data present, RX only */ | ||
3402 | +# define ICR_TX_E 0x08 /* Epmty, TX only */ | ||
3403 | + | ||
3404 | +#define I2S_IPR 0x10 /* Interrupt Pending Register */ | ||
3405 | +#define I2S_ISR 0x14 /* Interrupt Status Register */ | ||
3406 | + | ||
3407 | +#define I2S_IER 0x18 /* Interrupt Enable Register */ | ||
3408 | +# define IER_FF 0x01 /* RX/TX FIFO Full */ | ||
3409 | +# define IER_FAF 0x02 /* RX/TX FIFO Almost Full */ | ||
3410 | +# define IER_FAE 0x04 /* RX/TX FIFO Almost Empty */ | ||
3411 | +# define IER_RX_DATA 0x08 /* RX. Data Present */ | ||
3412 | +# define IER_TX_FE 0x08 /* TX. FIFO Empty */ | ||
3413 | + | ||
3414 | +#define I2S_CTRL 0x1c /* Control Register */ | ||
3415 | +# define CTRL_TX_ENABLE 0x01 /* Enable TX */ | ||
3416 | +# define CTRL_RX_ENABLE 0x02 /* Enable RX */ | ||
3417 | +# define CTRL_NONE 0x04 /* Not used */ | ||
3418 | +# define CTRL_FIFO_CLR 0x08 /* FIFO Clear */ | ||
3419 | +# define CTRL_SWR 0x10 /* Soft reset */ | ||
3420 | +# define CTRL_CLKMASTER 0x1000 /* IP I2S instance is master */ | ||
3421 | +# define CTRL_IS_TX 0x40000000 /* IP I2S is an TX-instance */ | ||
3422 | +# define CTRL_IS_RX 0x20000000 /* IP I2S is an RX-instance */ | ||
3423 | + | ||
3424 | +#define I2S_FIFO 0x20 /* read/write FIFO */ | ||
3425 | + | ||
3426 | +#define INC_HEAD(buf, size) \ | ||
3427 | + (buf->head = (buf->head + 1) & (size-1)) | ||
3428 | + | ||
3429 | +#define INC_TAIL(buf, size) \ | ||
3430 | + (buf->tail = (buf->tail + 1) & (size-1)) | ||
3431 | + | ||
3432 | + | ||
3433 | +/* circular buffer */ | ||
3434 | +static struct circ_buf *timbi2s_buf_alloc(void); | ||
3435 | +static void timbi2s_buf_free(struct circ_buf *cb); | ||
3436 | +static void timbi2s_buf_clear(struct circ_buf *cb); | ||
3437 | + | ||
3438 | +static int timbi2s_fifo_read(struct circ_buf *cb, ssize_t count, long add); | ||
3439 | +static int timbi2s_fifo_write(struct circ_buf *cb, ssize_t count, long add); | ||
3440 | + | ||
3441 | +static int timbi2s_ioctrl(struct timbi2s_dev *); | ||
3442 | + | ||
3443 | +static struct timbi2s_bus *bus_p; | ||
3444 | + | ||
3445 | +static int timbi2s_is_tx(struct timbi2s_dev *i2sdev) | ||
3446 | +{ | ||
3447 | + return (ioread32(i2sdev->membase + i2sdev->ctrl_offset) | ||
3448 | + & CTRL_IS_TX) ? 1 : 0; | ||
3449 | +} | ||
3450 | + | ||
3451 | +static int timbi2s_is_rx(struct timbi2s_dev *i2sdev) | ||
3452 | +{ | ||
3453 | + return (ioread32(i2sdev->membase + i2sdev->ctrl_offset) | ||
3454 | + & CTRL_IS_RX) ? 1 : 0; | ||
3455 | +} | ||
3456 | + | ||
3457 | +/* Return unused TX-instance */ | ||
3458 | +static struct timbi2s_dev *timbi2s_get_tx(void) | ||
3459 | +{ | ||
3460 | + struct timbi2s_dev *tdev, *tmp; | ||
3461 | + | ||
3462 | + if (bus_p == NULL) | ||
3463 | + return NULL; | ||
3464 | + | ||
3465 | + list_for_each_entry_safe(tdev, tmp, &bus_p->control->list, item) { | ||
3466 | + if (!tdev->in_use && timbi2s_is_tx(tdev)) { | ||
3467 | + tdev->in_use = 1; | ||
3468 | + return tdev; | ||
3469 | + } | ||
3470 | + | ||
3471 | + } | ||
3472 | + return NULL; | ||
3473 | +} | ||
3474 | +EXPORT_SYMBOL_GPL(timbi2s_get_tx); | ||
3475 | + | ||
3476 | +/* Return unused RX-instance */ | ||
3477 | +static struct timbi2s_dev *timbi2s_get_rx(void) | ||
3478 | +{ | ||
3479 | + struct timbi2s_dev *tdev, *tmp; | ||
3480 | + | ||
3481 | + if (bus_p == NULL) | ||
3482 | + return NULL; | ||
3483 | + | ||
3484 | + list_for_each_entry_safe(tdev, tmp, &bus_p->control->list, item) { | ||
3485 | + if (!tdev->in_use && timbi2s_is_rx(tdev)) { | ||
3486 | + tdev->in_use = 1; | ||
3487 | + return tdev; | ||
3488 | + } | ||
3489 | + | ||
3490 | + } | ||
3491 | + return NULL; | ||
3492 | +} | ||
3493 | +EXPORT_SYMBOL_GPL(timbi2s_get_rx); | ||
3494 | + | ||
3495 | +/* Flag TX/RX as unused and reset it */ | ||
3496 | +static void timbi2s_put(struct timbi2s_dev *tdev) | ||
3497 | +{ | ||
3498 | + if (tdev->in_use) { | ||
3499 | + tdev->in_use = 0; | ||
3500 | + timbi2s_ioctrl(tdev); | ||
3501 | + } | ||
3502 | +} | ||
3503 | +EXPORT_SYMBOL_GPL(timbi2s_put); | ||
3504 | + | ||
3505 | +/* | ||
3506 | + * Write data to the FIFO | ||
3507 | + */ | ||
3508 | +static void timbi2s_tx_handler(struct timbi2s_dev *i2sdev) | ||
3509 | +{ | ||
3510 | + u32 pend; | ||
3511 | + | ||
3512 | + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); | ||
3513 | + | ||
3514 | + if (pend & IER_FAE) { | ||
3515 | + timbi2s_fifo_write(i2sdev->buffer, | ||
3516 | + ALMOST_FULL - ALMOST_EMPTY, | ||
3517 | + (unsigned long)i2sdev->membase + | ||
3518 | + i2sdev->fifo); | ||
3519 | + /* clear interrupt */ | ||
3520 | + iowrite32(ICR_AE, i2sdev->membase + i2sdev->icr_offset); | ||
3521 | + } | ||
3522 | +} | ||
3523 | + | ||
3524 | +/* | ||
3525 | + * Read data from the FIFO | ||
3526 | + */ | ||
3527 | +static void timbi2s_rx_handler(struct timbi2s_dev *i2sdev) | ||
3528 | +{ | ||
3529 | + u32 pend; | ||
3530 | + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); | ||
3531 | + | ||
3532 | + if (pend & IER_FAE) { | ||
3533 | + timbi2s_fifo_read(i2sdev->buffer, | ||
3534 | + ALMOST_EMPTY, | ||
3535 | + (unsigned long)i2sdev->membase + | ||
3536 | + i2sdev->fifo); | ||
3537 | + | ||
3538 | + /* clear interrupt */ | ||
3539 | + iowrite32(ICR_AE | ICR_AF, | ||
3540 | + i2sdev->membase + i2sdev->icr_offset); | ||
3541 | + } | ||
3542 | +} | ||
3543 | + | ||
3544 | +void timbi2s_int_handler(struct work_struct *workp) | ||
3545 | +{ | ||
3546 | + u32 pend, stat, i2stype; | ||
3547 | + unsigned long flags; | ||
3548 | + struct timbi2s_dev *i2sdev = container_of(workp, | ||
3549 | + struct timbi2s_dev, | ||
3550 | + work); | ||
3551 | + | ||
3552 | + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); | ||
3553 | + stat = ioread32(i2sdev->membase + i2sdev->isr_offset); | ||
3554 | + i2stype = ioread32(i2sdev->membase + i2sdev->ctrl_offset); | ||
3555 | + | ||
3556 | + spin_lock_irqsave(&i2sdev->lock, flags); | ||
3557 | + | ||
3558 | + if (i2stype & CTRL_IS_RX) { | ||
3559 | + /* Enable Almost Empty Almost Full interrupt */ | ||
3560 | + iowrite32(IER_FAE | IER_FAF, | ||
3561 | + i2sdev->membase + i2sdev->ier_offset); | ||
3562 | + /* Enable RX */ | ||
3563 | + iowrite32(CTRL_RX_ENABLE, | ||
3564 | + i2sdev->membase + i2sdev->ctrl_offset); | ||
3565 | + timbi2s_rx_handler(i2sdev); | ||
3566 | + } else if (i2stype & CTRL_IS_TX) { | ||
3567 | + /* Enable Almost Empty interrupt */ | ||
3568 | + iowrite32(IER_FAE, i2sdev->membase + i2sdev->ier_offset); | ||
3569 | + /* Enable TX */ | ||
3570 | + iowrite32(CTRL_TX_ENABLE, | ||
3571 | + i2sdev->membase + i2sdev->ctrl_offset); | ||
3572 | + timbi2s_tx_handler(i2sdev); | ||
3573 | + } | ||
3574 | + | ||
3575 | + spin_unlock_irqrestore(&i2sdev->lock, flags); | ||
3576 | +} | ||
3577 | + | ||
3578 | +static int timbi2s_ioctrl(struct timbi2s_dev *i2sdev) | ||
3579 | +{ | ||
3580 | + u32 i2stype; | ||
3581 | + | ||
3582 | + /* Reset */ | ||
3583 | + iowrite8(CTRL_SWR, i2sdev->membase + i2sdev->ctrl_offset); | ||
3584 | + /* Clear IER */ | ||
3585 | + iowrite32(0x00000000, i2sdev->membase + i2sdev->ier_offset); | ||
3586 | + /* Clear ICR */ | ||
3587 | + iowrite32(0xffffffff, i2sdev->membase + i2sdev->icr_offset); | ||
3588 | + | ||
3589 | + i2stype = ioread32(i2sdev->membase + i2sdev->ctrl_offset); | ||
3590 | + | ||
3591 | + if (i2stype & CTRL_IS_TX) | ||
3592 | + printk(KERN_INFO DRIVER_NAME": found active I2S Transmitter\n"); | ||
3593 | + else if (i2stype & CTRL_IS_RX) | ||
3594 | + printk(KERN_INFO DRIVER_NAME": found active I2S Receiver\n"); | ||
3595 | + | ||
3596 | + return 1; | ||
3597 | +} | ||
3598 | +EXPORT_SYMBOL_GPL(timbi2s_ioctrl); | ||
3599 | + | ||
3600 | +static struct circ_buf *timbi2s_buf_alloc(void) | ||
3601 | +{ | ||
3602 | + struct circ_buf *cb; | ||
3603 | + | ||
3604 | + cb = kzalloc(sizeof(*cb), GFP_KERNEL); | ||
3605 | + if (cb == NULL) | ||
3606 | + return NULL; | ||
3607 | + | ||
3608 | + cb->buf = kzalloc(I2S_BUFFER_SIZE, GFP_KERNEL); | ||
3609 | + if (cb->buf == NULL) { | ||
3610 | + kfree(cb); | ||
3611 | + return NULL; | ||
3612 | + } | ||
3613 | + | ||
3614 | + timbi2s_buf_clear(cb); | ||
3615 | + | ||
3616 | + return cb; | ||
3617 | +} | ||
3618 | + | ||
3619 | +static void timbi2s_buf_free(struct circ_buf *cb) | ||
3620 | +{ | ||
3621 | + kfree(cb->buf); | ||
3622 | + kfree(cb); | ||
3623 | +} | ||
3624 | + | ||
3625 | +static void timbi2s_buf_clear(struct circ_buf *cb) | ||
3626 | +{ | ||
3627 | + cb->head = 0; | ||
3628 | + cb->tail = cb->head; | ||
3629 | +} | ||
3630 | + | ||
3631 | +/* | ||
3632 | + * Read data from the FIFO and write it to the given circular buffer | ||
3633 | + */ | ||
3634 | +static int timbi2s_fifo_read(struct circ_buf *cb, ssize_t count, long add) | ||
3635 | +{ | ||
3636 | + int c, ret = 0; | ||
3637 | + | ||
3638 | + unsigned char *hi = (unsigned char *)ioread32((void *)(add >> 16)); | ||
3639 | + unsigned char *lo = (unsigned char *)ioread32((void *)(add & 0xFFFF)); | ||
3640 | + | ||
3641 | + c = CIRC_SPACE_TO_END(cb->head, cb->tail, I2S_BUFFER_SIZE); | ||
3642 | + if (count < c) | ||
3643 | + c = count; | ||
3644 | + | ||
3645 | + if (c <= 0) | ||
3646 | + return 1; | ||
3647 | + | ||
3648 | + while (c >= 0) { | ||
3649 | + memcpy(cb->buf + cb->head, hi, 2); | ||
3650 | + INC_HEAD(cb, I2S_BUFFER_SIZE); | ||
3651 | + | ||
3652 | + memcpy(cb->buf + cb->head, lo, 2); | ||
3653 | + INC_HEAD(cb, I2S_BUFFER_SIZE); | ||
3654 | + count -= 4; | ||
3655 | + } | ||
3656 | + return ret; | ||
3657 | +} | ||
3658 | + | ||
3659 | +/* | ||
3660 | + * Get data from the circular buffer and write it to the given FIFO address | ||
3661 | + */ | ||
3662 | +static int timbi2s_fifo_write(struct circ_buf *cb, ssize_t count, long add) | ||
3663 | +{ | ||
3664 | + int c, ret = 0; | ||
3665 | + | ||
3666 | + c = CIRC_CNT_TO_END(cb->head, cb->tail, I2S_BUFFER_SIZE); | ||
3667 | + if (count < c) | ||
3668 | + c = count; | ||
3669 | + | ||
3670 | + if (c <= 0) | ||
3671 | + return 1; | ||
3672 | + | ||
3673 | + while (c >= 0) { | ||
3674 | + iowrite32(*(s16 *)(cb->buf + cb->tail), (void *)(add >> 16)); | ||
3675 | + INC_TAIL(cb, I2S_BUFFER_SIZE); | ||
3676 | + | ||
3677 | + iowrite32(*(s16 *)(cb->buf + cb->tail), (void *)(add & 0xFFFF)); | ||
3678 | + INC_TAIL(cb, I2S_BUFFER_SIZE); | ||
3679 | + count -= 4; | ||
3680 | + } | ||
3681 | + | ||
3682 | + return ret; | ||
3683 | +} | ||
3684 | + | ||
3685 | +static void timbi2s_control_destroy(struct timbi2s_bus_control *control) | ||
3686 | +{ | ||
3687 | + kfree(control); | ||
3688 | + control = NULL; | ||
3689 | +} | ||
3690 | + | ||
3691 | +static void timbi2s_control_add_dev(struct timbi2s_dev *i2sdev) | ||
3692 | +{ | ||
3693 | + list_add(&i2sdev->item, &i2sdev->bus->control->list); | ||
3694 | +} | ||
3695 | + | ||
3696 | +static void timbi2s_control_del_dev(struct timbi2s_dev *i2sdev) | ||
3697 | +{ | ||
3698 | + list_del(&i2sdev->item); | ||
3699 | + if (list_empty(&i2sdev->bus->control->list)) | ||
3700 | + timbi2s_control_destroy(i2sdev->bus->control); | ||
3701 | +} | ||
3702 | + | ||
3703 | +static irqreturn_t timbi2s_irq(int irq, void *dev_id) | ||
3704 | +{ | ||
3705 | + u8 pend; | ||
3706 | + u32 iunit; | ||
3707 | + int i; | ||
3708 | + | ||
3709 | + struct timbi2s_bus *tbus = dev_id; | ||
3710 | + queue_work(tbus->workqueue, &tbus->work); | ||
3711 | + | ||
3712 | + iunit = ioread32(tbus->membase + I2S_UIR); | ||
3713 | + /* Find out which I2S instance is interrupting */ | ||
3714 | + for (i = 0; i < 32; i++) { | ||
3715 | + if ((1 << i) & iunit) { | ||
3716 | + pend = ioread8(tbus->membase + | ||
3717 | + (I2S_IPR + (i * REGSTEP * 7))); | ||
3718 | + iowrite8(pend, tbus->membase + | ||
3719 | + (I2S_ICR + (i * REGSTEP * 7))); | ||
3720 | + } | ||
3721 | + } | ||
3722 | + | ||
3723 | + return IRQ_HANDLED; | ||
3724 | +} | ||
3725 | + | ||
3726 | +static int __init timbi2s_probe(struct platform_device *dev) | ||
3727 | +{ | ||
3728 | + int err = 0; | ||
3729 | + struct timbi2s_dev *tdev, *tmp; | ||
3730 | + struct timbi2s_bus *tbus; | ||
3731 | + struct resource *iomem; | ||
3732 | + int i; | ||
3733 | + | ||
3734 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
3735 | + if (!iomem) { | ||
3736 | + err = -EINVAL; | ||
3737 | + goto err_mem; | ||
3738 | + } | ||
3739 | + | ||
3740 | + tbus = kzalloc(sizeof(*tbus), GFP_KERNEL); | ||
3741 | + if (!tbus) { | ||
3742 | + err = -EINVAL; | ||
3743 | + goto err_mem; | ||
3744 | + } | ||
3745 | + | ||
3746 | + /* Init bus_control */ | ||
3747 | + tbus->control = kzalloc(sizeof(struct timbi2s_bus_control), GFP_KERNEL); | ||
3748 | + if (!tbus->control) { | ||
3749 | + printk(KERN_ERR DRIVER_NAME | ||
3750 | + ": Failed to allocate timbi2s_bus_control.\n"); | ||
3751 | + err = -ENOMEM; | ||
3752 | + goto err_free; | ||
3753 | + } | ||
3754 | + INIT_LIST_HEAD(&tbus->control->list); | ||
3755 | + | ||
3756 | + /* Init workqueue */ | ||
3757 | + tbus->workqueue = create_singlethread_workqueue("timbi2s"); | ||
3758 | + if (tbus->workqueue == NULL) { | ||
3759 | + printk(KERN_ERR DRIVER_NAME | ||
3760 | + ": unable to create workqueue\n"); | ||
3761 | + err = -ENOMEM; | ||
3762 | + goto err_control; | ||
3763 | + } | ||
3764 | + INIT_WORK(&tbus->work, timbi2s_int_handler); | ||
3765 | + | ||
3766 | + if (!request_mem_region(iomem->start, | ||
3767 | + resource_size(iomem), DRIVER_NAME)) { | ||
3768 | + printk(KERN_EMERG DRIVER_NAME | ||
3769 | + ": Mem region is already in use\n"); | ||
3770 | + err = -ENXIO; | ||
3771 | + goto err_control; | ||
3772 | + } | ||
3773 | + | ||
3774 | + tbus->membase = ioremap(iomem->start, resource_size(iomem)); | ||
3775 | + if (tbus->membase == NULL) { | ||
3776 | + err = -ENOMEM; | ||
3777 | + goto err_request; | ||
3778 | + } | ||
3779 | + | ||
3780 | + bus_p = tbus; | ||
3781 | + | ||
3782 | + | ||
3783 | + | ||
3784 | + /* For now we have only 4 I2S instances in IP : 3 RX and 1 TX */ | ||
3785 | + /* Note: TX'es are always on top */ | ||
3786 | + /* TODO: auto-check how many are alive and bring them into control */ | ||
3787 | + for (i = 0; i < IP_I2S_NR; i++) { | ||
3788 | + tdev = kzalloc(sizeof(*tdev), GFP_KERNEL); | ||
3789 | + if (!tdev) { | ||
3790 | + err = -EINVAL; | ||
3791 | + goto clean_list; | ||
3792 | + } | ||
3793 | + | ||
3794 | + /* Allocate circ_buf */ | ||
3795 | + tdev->buffer = timbi2s_buf_alloc(); | ||
3796 | + if (tdev->buffer == NULL) { | ||
3797 | + printk(KERN_ERR "timbi2s: unable to allocate buffer\n"); | ||
3798 | + goto clean_list; | ||
3799 | + } | ||
3800 | + | ||
3801 | + INIT_LIST_HEAD(&tdev->item); | ||
3802 | + spin_lock_init(&tdev->lock); | ||
3803 | + | ||
3804 | + /* set up offsets for each instance of I2S */ | ||
3805 | + tdev->bus = tbus; /* ptr to our bus */ | ||
3806 | + tdev->membase = tbus->membase; | ||
3807 | + tdev->in_use = 0; | ||
3808 | + tdev->pscale_offset = I2S_PRESCALE + (i * REGSTEP * 7); | ||
3809 | + tdev->icr_offset = I2S_ICR + (i * REGSTEP * 7); | ||
3810 | + tdev->isr_offset = I2S_ISR + (i * REGSTEP * 7); | ||
3811 | + tdev->ipr_offset = I2S_IPR + (i * REGSTEP * 7); | ||
3812 | + tdev->ier_offset = I2S_IER + (i * REGSTEP * 7); | ||
3813 | + tdev->ctrl_offset = I2S_CTRL + (i * REGSTEP * 7); | ||
3814 | + tdev->fifo = I2S_FIFO + (i * REGSTEP * 7); | ||
3815 | + | ||
3816 | + /* Try to check and reset hardware */ | ||
3817 | + if (timbi2s_ioctrl(tdev)) | ||
3818 | + timbi2s_control_add_dev(tdev); | ||
3819 | + | ||
3820 | + tdev = NULL; | ||
3821 | + } | ||
3822 | + | ||
3823 | + tbus->irq = platform_get_irq(dev, 0); | ||
3824 | + if (tbus->irq < 0) { | ||
3825 | + err = -EINVAL; | ||
3826 | + goto clean_list; | ||
3827 | + } | ||
3828 | + | ||
3829 | + err = request_irq(tbus->irq, timbi2s_irq, 0, DRIVER_NAME, tbus); | ||
3830 | + if (err != 0) | ||
3831 | + goto clean_list; | ||
3832 | + | ||
3833 | + platform_set_drvdata(dev, tbus); | ||
3834 | + | ||
3835 | + dev_info(&dev->dev, "Driver for Timberdale I2S (ver: %d)" | ||
3836 | + " has been successfully registered.\n", | ||
3837 | + ioread32(tbus->membase + 0x00)); | ||
3838 | + return 0; | ||
3839 | + | ||
3840 | +clean_list: | ||
3841 | + list_for_each_entry_safe(tdev, tmp, &tbus->control->list, item) { | ||
3842 | + if (tdev->workqueue != NULL) { | ||
3843 | + flush_workqueue(tdev->workqueue); | ||
3844 | + destroy_workqueue(tdev->workqueue); | ||
3845 | + } | ||
3846 | + | ||
3847 | + if (tdev->buffer != NULL) | ||
3848 | + timbi2s_buf_free(tdev->buffer); | ||
3849 | + | ||
3850 | + timbi2s_control_del_dev(tdev); | ||
3851 | + kfree(tdev); | ||
3852 | + } | ||
3853 | + free_irq(tbus->irq, tbus); | ||
3854 | + iounmap(tbus->membase); | ||
3855 | +err_request: | ||
3856 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
3857 | +err_control: | ||
3858 | + if (tbus->control != NULL) | ||
3859 | + timbi2s_control_destroy(tbus->control); | ||
3860 | +err_free: | ||
3861 | + kfree(tbus); | ||
3862 | +err_mem: | ||
3863 | + printk(KERN_ERR | ||
3864 | + DRIVER_NAME": Failed to register Timberdale I2S: %d\n", err); | ||
3865 | + | ||
3866 | + return err; | ||
3867 | +} | ||
3868 | + | ||
3869 | +static int __devexit timbi2s_remove(struct platform_device *dev) | ||
3870 | +{ | ||
3871 | + struct timbi2s_bus *tbus; | ||
3872 | + struct timbi2s_dev *tdev, *tmp; | ||
3873 | + struct resource *r; | ||
3874 | + | ||
3875 | + tbus = platform_get_drvdata(dev); | ||
3876 | + free_irq(tbus->irq, tbus); | ||
3877 | + | ||
3878 | + r = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
3879 | + | ||
3880 | + list_for_each_entry_safe(tdev, tmp, &tbus->control->list, item) { | ||
3881 | + if (tdev->workqueue != NULL) { | ||
3882 | + flush_workqueue(tdev->workqueue); | ||
3883 | + destroy_workqueue(tdev->workqueue); | ||
3884 | + } | ||
3885 | + | ||
3886 | + if (tdev->buffer != NULL) | ||
3887 | + timbi2s_buf_free(tdev->buffer); | ||
3888 | + | ||
3889 | + kfree(tdev); | ||
3890 | + } | ||
3891 | + | ||
3892 | + iounmap(tdev->membase); | ||
3893 | + if (r) | ||
3894 | + release_mem_region(r->start, resource_size(r)); | ||
3895 | + | ||
3896 | + dev_info(&dev->dev, "Driver for Timberdale I2S has been" | ||
3897 | + " successfully unregistered.\n"); | ||
3898 | + | ||
3899 | + platform_set_drvdata(dev, 0); | ||
3900 | + return 0; | ||
3901 | +} | ||
3902 | + | ||
3903 | +static struct platform_driver timbi2s_platform_driver = { | ||
3904 | + .driver = { | ||
3905 | + .name = DRIVER_NAME, | ||
3906 | + .owner = THIS_MODULE, | ||
3907 | + }, | ||
3908 | + .probe = timbi2s_probe, | ||
3909 | + .remove = __devexit_p(timbi2s_remove), | ||
3910 | +}; | ||
3911 | + | ||
3912 | +/*--------------------------------------------------------------------------*/ | ||
3913 | + | ||
3914 | +static int __init timbi2s_init(void) | ||
3915 | +{ | ||
3916 | + return platform_driver_register(&timbi2s_platform_driver); | ||
3917 | +} | ||
3918 | + | ||
3919 | +static void __exit timbi2s_exit(void) | ||
3920 | +{ | ||
3921 | + platform_driver_unregister(&timbi2s_platform_driver); | ||
3922 | +} | ||
3923 | + | ||
3924 | +module_init(timbi2s_init); | ||
3925 | +module_exit(timbi2s_exit); | ||
3926 | + | ||
3927 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
3928 | +MODULE_DESCRIPTION("Timberdale I2S bus driver"); | ||
3929 | +MODULE_LICENSE("GPL v2"); | ||
3930 | diff -uNr linux-2.6.29-clean/drivers/mmc/host/Kconfig linux-2.6.29/drivers/mmc/host/Kconfig | ||
3931 | --- linux-2.6.29-clean/drivers/mmc/host/Kconfig 2009-04-01 09:20:24.000000000 -0700 | ||
3932 | +++ linux-2.6.29/drivers/mmc/host/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
3933 | @@ -65,6 +65,16 @@ | ||
3934 | |||
3935 | If unsure, say Y. | ||
3936 | |||
3937 | +config MMC_SDHCI_PLTFM | ||
3938 | + tristate "SDHCI support on platform devices" | ||
3939 | + depends on MMC_SDHCI | ||
3940 | + help | ||
3941 | + This selects the Secure Digital Host Controller Interface. | ||
3942 | + | ||
3943 | + If you have a controller with this interface, say Y or M here. | ||
3944 | + | ||
3945 | + If unsure, say N. | ||
3946 | + | ||
3947 | config MMC_OMAP | ||
3948 | tristate "TI OMAP Multimedia Card Interface support" | ||
3949 | depends on ARCH_OMAP | ||
3950 | diff -uNr linux-2.6.29-clean/drivers/mmc/host/Makefile linux-2.6.29/drivers/mmc/host/Makefile | ||
3951 | --- linux-2.6.29-clean/drivers/mmc/host/Makefile 2009-04-01 09:20:24.000000000 -0700 | ||
3952 | +++ linux-2.6.29/drivers/mmc/host/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
3953 | @@ -13,6 +13,7 @@ | ||
3954 | obj-$(CONFIG_MMC_SDHCI) += sdhci.o | ||
3955 | obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o | ||
3956 | obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o | ||
3957 | +obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o | ||
3958 | obj-$(CONFIG_MMC_WBSD) += wbsd.o | ||
3959 | obj-$(CONFIG_MMC_AU1X) += au1xmmc.o | ||
3960 | obj-$(CONFIG_MMC_OMAP) += omap.o | ||
3961 | diff -uNr linux-2.6.29-clean/drivers/mmc/host/sdhci-pltfm.c linux-2.6.29/drivers/mmc/host/sdhci-pltfm.c | ||
3962 | --- linux-2.6.29-clean/drivers/mmc/host/sdhci-pltfm.c 1969-12-31 16:00:00.000000000 -0800 | ||
3963 | +++ linux-2.6.29/drivers/mmc/host/sdhci-pltfm.c 2009-04-06 13:51:47.000000000 -0700 | ||
3964 | @@ -0,0 +1,262 @@ | ||
3965 | +/* | ||
3966 | + * sdhci-pltfm.c Support for SDHCI platform devices | ||
3967 | + * Copyright (c) 2009 Intel Corporation | ||
3968 | + * | ||
3969 | + * This program is free software; you can redistribute it and/or modify | ||
3970 | + * it under the terms of the GNU General Public License version 2 as | ||
3971 | + * published by the Free Software Foundation. | ||
3972 | + * | ||
3973 | + * This program is distributed in the hope that it will be useful, | ||
3974 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3975 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3976 | + * GNU General Public License for more details. | ||
3977 | + * | ||
3978 | + * You should have received a copy of the GNU General Public License | ||
3979 | + * along with this program; if not, write to the Free Software | ||
3980 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
3981 | + */ | ||
3982 | + | ||
3983 | +/* Supports: | ||
3984 | + * SDHCI platform devices | ||
3985 | + * | ||
3986 | + * Inspired by sdhci-pci.c, by Pierre Ossman | ||
3987 | + */ | ||
3988 | + | ||
3989 | +#include <linux/delay.h> | ||
3990 | +#include <linux/highmem.h> | ||
3991 | +#include <linux/platform_device.h> | ||
3992 | + | ||
3993 | +#include <linux/mmc/host.h> | ||
3994 | + | ||
3995 | +#include <linux/io.h> | ||
3996 | + | ||
3997 | +#include "sdhci.h" | ||
3998 | + | ||
3999 | + | ||
4000 | +#define MAX_SLOTS 8 | ||
4001 | + | ||
4002 | +struct sdhci_pltfm_chip; | ||
4003 | + | ||
4004 | +struct sdhci_pltfm_slot { | ||
4005 | + struct sdhci_pltfm_chip *chip; | ||
4006 | + struct sdhci_host *host; | ||
4007 | + | ||
4008 | + int pltfm_resource; | ||
4009 | +}; | ||
4010 | + | ||
4011 | +struct sdhci_pltfm_chip { | ||
4012 | + struct platform_device *pdev; | ||
4013 | + | ||
4014 | + unsigned int quirks; | ||
4015 | + | ||
4016 | + int num_slots; /* Slots on controller */ | ||
4017 | + struct sdhci_pltfm_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ | ||
4018 | +}; | ||
4019 | + | ||
4020 | + | ||
4021 | +/*****************************************************************************\ | ||
4022 | + * * | ||
4023 | + * SDHCI core callbacks * | ||
4024 | + * * | ||
4025 | +\*****************************************************************************/ | ||
4026 | + | ||
4027 | +static struct sdhci_ops sdhci_pltfm_ops = { | ||
4028 | +}; | ||
4029 | + | ||
4030 | +/*****************************************************************************\ | ||
4031 | + * * | ||
4032 | + * Device probing/removal * | ||
4033 | + * * | ||
4034 | +\*****************************************************************************/ | ||
4035 | + | ||
4036 | + | ||
4037 | +static struct sdhci_pltfm_slot * __devinit sdhci_pltfm_probe_slot( | ||
4038 | + struct platform_device *pdev, struct sdhci_pltfm_chip *chip, | ||
4039 | + int resource) | ||
4040 | +{ | ||
4041 | + struct sdhci_pltfm_slot *slot; | ||
4042 | + struct sdhci_host *host; | ||
4043 | + struct resource *iomem; | ||
4044 | + int ret; | ||
4045 | + | ||
4046 | + iomem = platform_get_resource(pdev, IORESOURCE_MEM, resource); | ||
4047 | + if (!iomem) | ||
4048 | + return ERR_PTR(-ENODEV); | ||
4049 | + | ||
4050 | + if (resource_size(iomem) != 0x100) { | ||
4051 | + dev_err(&pdev->dev, "Invalid iomem size. You may " | ||
4052 | + "experience problems.\n"); | ||
4053 | + } | ||
4054 | + | ||
4055 | + if (!pdev->dev.parent) { | ||
4056 | + dev_err(&pdev->dev, "The parent device be a PCI device\n"); | ||
4057 | + return ERR_PTR(-ENODEV); | ||
4058 | + } | ||
4059 | + | ||
4060 | + host = sdhci_alloc_host(pdev->dev.parent, | ||
4061 | + sizeof(struct sdhci_pltfm_slot)); | ||
4062 | + if (IS_ERR(host)) | ||
4063 | + return ERR_PTR(PTR_ERR(host)); | ||
4064 | + | ||
4065 | + slot = sdhci_priv(host); | ||
4066 | + | ||
4067 | + slot->chip = chip; | ||
4068 | + slot->host = host; | ||
4069 | + slot->pltfm_resource = resource; | ||
4070 | + | ||
4071 | + host->hw_name = "PLTFM"; | ||
4072 | + host->ops = &sdhci_pltfm_ops; | ||
4073 | + host->quirks = chip->quirks; | ||
4074 | + | ||
4075 | + host->irq = platform_get_irq(pdev, 0); | ||
4076 | + | ||
4077 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
4078 | + mmc_hostname(host->mmc))) { | ||
4079 | + dev_err(&pdev->dev, "cannot request region\n"); | ||
4080 | + ret = -EBUSY; | ||
4081 | + goto free; | ||
4082 | + } | ||
4083 | + | ||
4084 | + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); | ||
4085 | + if (!host->ioaddr) { | ||
4086 | + dev_err(&pdev->dev, "failed to remap registers\n"); | ||
4087 | + goto release; | ||
4088 | + } | ||
4089 | + | ||
4090 | + ret = sdhci_add_host(host); | ||
4091 | + if (ret) | ||
4092 | + goto unmap; | ||
4093 | + | ||
4094 | + return slot; | ||
4095 | + | ||
4096 | +unmap: | ||
4097 | + iounmap(host->ioaddr); | ||
4098 | +release: | ||
4099 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
4100 | +free: | ||
4101 | + sdhci_free_host(host); | ||
4102 | + | ||
4103 | + return ERR_PTR(ret); | ||
4104 | +} | ||
4105 | + | ||
4106 | +static void sdhci_pltfm_remove_slot(struct sdhci_pltfm_slot *slot) | ||
4107 | +{ | ||
4108 | + int dead; | ||
4109 | + u32 scratch; | ||
4110 | + struct resource *iomem; | ||
4111 | + | ||
4112 | + dead = 0; | ||
4113 | + scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); | ||
4114 | + if (scratch == (u32)-1) | ||
4115 | + dead = 1; | ||
4116 | + | ||
4117 | + sdhci_remove_host(slot->host, dead); | ||
4118 | + | ||
4119 | + iounmap(slot->host->ioaddr); | ||
4120 | + | ||
4121 | + iomem = platform_get_resource(slot->chip->pdev, IORESOURCE_MEM, | ||
4122 | + slot->pltfm_resource); | ||
4123 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
4124 | + | ||
4125 | + sdhci_free_host(slot->host); | ||
4126 | +} | ||
4127 | + | ||
4128 | +static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) | ||
4129 | +{ | ||
4130 | + struct sdhci_pltfm_chip *chip; | ||
4131 | + struct sdhci_pltfm_slot *slot; | ||
4132 | + u8 slots; | ||
4133 | + int ret, i; | ||
4134 | + | ||
4135 | + BUG_ON(pdev == NULL); | ||
4136 | + | ||
4137 | + for (slots = 0; slots <= MAX_SLOTS; slots++) | ||
4138 | + if (!platform_get_resource(pdev, IORESOURCE_MEM, slots)) | ||
4139 | + break; | ||
4140 | + | ||
4141 | + BUG_ON(slots > MAX_SLOTS || slots == 0); | ||
4142 | + | ||
4143 | + chip = kzalloc(sizeof(struct sdhci_pltfm_chip), GFP_KERNEL); | ||
4144 | + if (!chip) { | ||
4145 | + ret = -ENOMEM; | ||
4146 | + goto err; | ||
4147 | + } | ||
4148 | + | ||
4149 | + chip->pdev = pdev; | ||
4150 | + chip->num_slots = slots; | ||
4151 | + platform_set_drvdata(pdev, chip); | ||
4152 | + | ||
4153 | + for (i = 0; i < slots; i++) { | ||
4154 | + slot = sdhci_pltfm_probe_slot(pdev, chip, i); | ||
4155 | + if (IS_ERR(slot)) { | ||
4156 | + for (i--; i >= 0; i--) | ||
4157 | + sdhci_pltfm_remove_slot(chip->slots[i]); | ||
4158 | + ret = PTR_ERR(slot); | ||
4159 | + goto free; | ||
4160 | + } | ||
4161 | + | ||
4162 | + chip->slots[i] = slot; | ||
4163 | + } | ||
4164 | + | ||
4165 | + return 0; | ||
4166 | + | ||
4167 | +free: | ||
4168 | + platform_set_drvdata(pdev, NULL); | ||
4169 | + kfree(chip); | ||
4170 | + | ||
4171 | +err: | ||
4172 | + printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret); | ||
4173 | + return ret; | ||
4174 | +} | ||
4175 | + | ||
4176 | +static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) | ||
4177 | +{ | ||
4178 | + int i; | ||
4179 | + struct sdhci_pltfm_chip *chip; | ||
4180 | + | ||
4181 | + chip = platform_get_drvdata(pdev); | ||
4182 | + | ||
4183 | + if (chip) { | ||
4184 | + for (i = 0; i < chip->num_slots; i++) | ||
4185 | + sdhci_pltfm_remove_slot(chip->slots[i]); | ||
4186 | + | ||
4187 | + platform_set_drvdata(pdev, NULL); | ||
4188 | + kfree(chip); | ||
4189 | + } | ||
4190 | + | ||
4191 | + return 0; | ||
4192 | +} | ||
4193 | + | ||
4194 | +static struct platform_driver sdhci_pltfm_driver = { | ||
4195 | + .driver = { | ||
4196 | + .name = "sdhci", | ||
4197 | + .owner = THIS_MODULE, | ||
4198 | + }, | ||
4199 | + .probe = sdhci_pltfm_probe, | ||
4200 | + .remove = __devexit_p(sdhci_pltfm_remove), | ||
4201 | +}; | ||
4202 | + | ||
4203 | +/*****************************************************************************\ | ||
4204 | + * * | ||
4205 | + * Driver init/exit * | ||
4206 | + * * | ||
4207 | +\*****************************************************************************/ | ||
4208 | + | ||
4209 | +static int __init sdhci_drv_init(void) | ||
4210 | +{ | ||
4211 | + return platform_driver_register(&sdhci_pltfm_driver); | ||
4212 | +} | ||
4213 | + | ||
4214 | +static void __exit sdhci_drv_exit(void) | ||
4215 | +{ | ||
4216 | + platform_driver_unregister(&sdhci_pltfm_driver); | ||
4217 | +} | ||
4218 | + | ||
4219 | +module_init(sdhci_drv_init); | ||
4220 | +module_exit(sdhci_drv_exit); | ||
4221 | + | ||
4222 | +MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); | ||
4223 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
4224 | +MODULE_LICENSE("GPL v2"); | ||
4225 | +MODULE_ALIAS("platform:sdhci"); | ||
4226 | + | ||
4227 | diff -uNr linux-2.6.29-clean/drivers/serial/Kconfig linux-2.6.29/drivers/serial/Kconfig | ||
4228 | --- linux-2.6.29-clean/drivers/serial/Kconfig 2009-04-01 09:20:24.000000000 -0700 | ||
4229 | +++ linux-2.6.29/drivers/serial/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
4230 | @@ -1412,4 +1412,11 @@ | ||
4231 | default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) | ||
4232 | default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) | ||
4233 | |||
4234 | +config SERIAL_TIMBERDALE | ||
4235 | + tristate "Support for timberdale UART" | ||
4236 | + depends on MFD_TIMBERDALE | ||
4237 | + select SERIAL_CORE | ||
4238 | + ---help--- | ||
4239 | + Add support for UART controller on timberdale. | ||
4240 | + | ||
4241 | endmenu | ||
4242 | diff -uNr linux-2.6.29-clean/drivers/serial/Makefile linux-2.6.29/drivers/serial/Makefile | ||
4243 | --- linux-2.6.29-clean/drivers/serial/Makefile 2009-04-01 09:20:24.000000000 -0700 | ||
4244 | +++ linux-2.6.29/drivers/serial/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
4245 | @@ -76,3 +76,4 @@ | ||
4246 | obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o | ||
4247 | obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o | ||
4248 | obj-$(CONFIG_SERIAL_QE) += ucc_uart.o | ||
4249 | +obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o | ||
4250 | diff -uNr linux-2.6.29-clean/drivers/serial/timbuart.c linux-2.6.29/drivers/serial/timbuart.c | ||
4251 | --- linux-2.6.29-clean/drivers/serial/timbuart.c 1969-12-31 16:00:00.000000000 -0800 | ||
4252 | +++ linux-2.6.29/drivers/serial/timbuart.c 2009-04-06 13:51:47.000000000 -0700 | ||
4253 | @@ -0,0 +1,519 @@ | ||
4254 | +/* | ||
4255 | + * timbuart.c timberdale FPGA UART driver | ||
4256 | + * Copyright (c) 2009 Intel Corporation | ||
4257 | + * | ||
4258 | + * This program is free software; you can redistribute it and/or modify | ||
4259 | + * it under the terms of the GNU General Public License version 2 as | ||
4260 | + * published by the Free Software Foundation. | ||
4261 | + * | ||
4262 | + * This program is distributed in the hope that it will be useful, | ||
4263 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4264 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4265 | + * GNU General Public License for more details. | ||
4266 | + * | ||
4267 | + * You should have received a copy of the GNU General Public License | ||
4268 | + * along with this program; if not, write to the Free Software | ||
4269 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
4270 | + */ | ||
4271 | + | ||
4272 | +/* Supports: | ||
4273 | + * Timberdale FPGA UART | ||
4274 | + */ | ||
4275 | + | ||
4276 | +#include <linux/pci.h> | ||
4277 | +#include <linux/interrupt.h> | ||
4278 | +#include <linux/serial_core.h> | ||
4279 | +#include <linux/kernel.h> | ||
4280 | +#include <linux/platform_device.h> | ||
4281 | +#include <linux/ioport.h> | ||
4282 | + | ||
4283 | +#include "timbuart.h" | ||
4284 | + | ||
4285 | +struct timbuart_port { | ||
4286 | + struct uart_port port; | ||
4287 | + struct tasklet_struct tasklet; | ||
4288 | + int usedma; | ||
4289 | + u8 last_ier; | ||
4290 | + struct platform_device *dev; | ||
4291 | +}; | ||
4292 | + | ||
4293 | +static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, | ||
4294 | + 921600, 1843200, 3250000}; | ||
4295 | + | ||
4296 | +static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier); | ||
4297 | + | ||
4298 | +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); | ||
4299 | + | ||
4300 | +static void timbuart_stop_rx(struct uart_port *port) | ||
4301 | +{ | ||
4302 | + /* spin lock held by upper layer, disable all RX interrupts */ | ||
4303 | + u8 ier = ioread8(port->membase + TIMBUART_IER) & ~RXFLAGS; | ||
4304 | + iowrite8(ier, port->membase + TIMBUART_IER); | ||
4305 | +} | ||
4306 | + | ||
4307 | +static void timbuart_stop_tx(struct uart_port *port) | ||
4308 | +{ | ||
4309 | + /* spinlock held by upper layer, disable TX interrupt */ | ||
4310 | + u8 ier = ioread8(port->membase + TIMBUART_IER) & ~TXBAE; | ||
4311 | + iowrite8(ier, port->membase + TIMBUART_IER); | ||
4312 | +} | ||
4313 | + | ||
4314 | +static void timbuart_start_tx(struct uart_port *port) | ||
4315 | +{ | ||
4316 | + struct timbuart_port *uart = | ||
4317 | + container_of(port, struct timbuart_port, port); | ||
4318 | + | ||
4319 | + /* do not transfer anything here -> fire off the tasklet */ | ||
4320 | + tasklet_schedule(&uart->tasklet); | ||
4321 | +} | ||
4322 | + | ||
4323 | +static void timbuart_flush_buffer(struct uart_port *port) | ||
4324 | +{ | ||
4325 | + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; | ||
4326 | + | ||
4327 | + iowrite8(ctl, port->membase + TIMBUART_CTRL); | ||
4328 | + iowrite8(TXBF, port->membase + TIMBUART_ISR); | ||
4329 | +} | ||
4330 | + | ||
4331 | +static void timbuart_rx_chars(struct uart_port *port) | ||
4332 | +{ | ||
4333 | + struct tty_struct *tty = port->info->port.tty; | ||
4334 | + | ||
4335 | + while (ioread8(port->membase + TIMBUART_ISR) & RXDP) { | ||
4336 | + u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); | ||
4337 | + /* ack */ | ||
4338 | + iowrite8(RXDP, port->membase + TIMBUART_ISR); | ||
4339 | + port->icount.rx++; | ||
4340 | + tty_insert_flip_char(tty, ch, TTY_NORMAL); | ||
4341 | + } | ||
4342 | + | ||
4343 | + spin_unlock(&port->lock); | ||
4344 | + tty_flip_buffer_push(port->info->port.tty); | ||
4345 | + spin_lock(&port->lock); | ||
4346 | + | ||
4347 | + dev_dbg(port->dev, "%s - total read %d bytes\n", | ||
4348 | + __func__, port->icount.rx); | ||
4349 | +} | ||
4350 | + | ||
4351 | +static void timbuart_tx_chars(struct uart_port *port) | ||
4352 | +{ | ||
4353 | + struct circ_buf *xmit = &port->info->xmit; | ||
4354 | + | ||
4355 | + while (!(ioread8(port->membase + TIMBUART_ISR) & TXBF) && | ||
4356 | + !uart_circ_empty(xmit)) { | ||
4357 | + iowrite8(xmit->buf[xmit->tail], | ||
4358 | + port->membase + TIMBUART_TXFIFO); | ||
4359 | + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
4360 | + port->icount.tx++; | ||
4361 | + } | ||
4362 | + | ||
4363 | + dev_dbg(port->dev, | ||
4364 | + "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", | ||
4365 | + __func__, | ||
4366 | + port->icount.tx, | ||
4367 | + ioread8(port->membase + TIMBUART_CTRL), | ||
4368 | + port->mctrl & TIOCM_RTS, | ||
4369 | + ioread8(port->membase + TIMBUART_BAUDRATE)); | ||
4370 | +} | ||
4371 | + | ||
4372 | +static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier) | ||
4373 | +{ | ||
4374 | + struct timbuart_port *uart = | ||
4375 | + container_of(port, struct timbuart_port, port); | ||
4376 | + struct circ_buf *xmit = &port->info->xmit; | ||
4377 | + | ||
4378 | + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) | ||
4379 | + return; | ||
4380 | + | ||
4381 | + if (port->x_char) | ||
4382 | + return; | ||
4383 | + | ||
4384 | + if (isr & TXFLAGS) { | ||
4385 | + timbuart_tx_chars(port); | ||
4386 | + /* clear all TX interrupts */ | ||
4387 | + iowrite8(TXFLAGS, port->membase + TIMBUART_ISR); | ||
4388 | + | ||
4389 | + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
4390 | + uart_write_wakeup(port); | ||
4391 | + } else | ||
4392 | + /* Re-enable any tx interrupt */ | ||
4393 | + *ier |= uart->last_ier & TXFLAGS; | ||
4394 | + | ||
4395 | + /* enable interrupts if there are chars in the transmit buffer, | ||
4396 | + * Or if we delivered some bytes and want the almost empty interrupt | ||
4397 | + * we wake up the upper layer later when we got the interrupt | ||
4398 | + * to give it some time to go out... | ||
4399 | + */ | ||
4400 | + if (!uart_circ_empty(xmit)) | ||
4401 | + *ier |= TXBAE; | ||
4402 | + | ||
4403 | + dev_dbg(port->dev, "%s - leaving\n", __func__); | ||
4404 | +} | ||
4405 | + | ||
4406 | +void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier) | ||
4407 | +{ | ||
4408 | + if (isr & RXFLAGS) { | ||
4409 | + /* Some RX status is set */ | ||
4410 | + if (isr & RXBF) { | ||
4411 | + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | | ||
4412 | + TIMBUART_CTRL_FLSHRX; | ||
4413 | + iowrite8(ctl, port->membase + TIMBUART_CTRL); | ||
4414 | + port->icount.overrun++; | ||
4415 | + } else if (isr & (RXDP)) | ||
4416 | + timbuart_rx_chars(port); | ||
4417 | + | ||
4418 | + /* ack all RX interrupts */ | ||
4419 | + iowrite8(RXFLAGS, port->membase + TIMBUART_ISR); | ||
4420 | + } | ||
4421 | + | ||
4422 | + /* always have the RX interrupts enabled */ | ||
4423 | + *ier |= RXBAF | RXBF | RXTT; | ||
4424 | + | ||
4425 | + dev_dbg(port->dev, "%s - leaving\n", __func__); | ||
4426 | +} | ||
4427 | + | ||
4428 | +void timbuart_tasklet(unsigned long arg) | ||
4429 | +{ | ||
4430 | + struct timbuart_port *uart = (struct timbuart_port *)arg; | ||
4431 | + u8 isr, ier = 0; | ||
4432 | + | ||
4433 | + spin_lock(&uart->port.lock); | ||
4434 | + | ||
4435 | + isr = ioread8(uart->port.membase + TIMBUART_ISR); | ||
4436 | + dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); | ||
4437 | + | ||
4438 | + if (!uart->usedma) | ||
4439 | + timbuart_handle_tx_port(&uart->port, isr, &ier); | ||
4440 | + | ||
4441 | + timbuart_mctrl_check(&uart->port, isr, &ier); | ||
4442 | + | ||
4443 | + if (!uart->usedma) | ||
4444 | + timbuart_handle_rx_port(&uart->port, isr, &ier); | ||
4445 | + | ||
4446 | + iowrite8(ier, uart->port.membase + TIMBUART_IER); | ||
4447 | + | ||
4448 | + spin_unlock(&uart->port.lock); | ||
4449 | + dev_dbg(uart->port.dev, "%s leaving\n", __func__); | ||
4450 | +} | ||
4451 | + | ||
4452 | +static unsigned int timbuart_tx_empty(struct uart_port *port) | ||
4453 | +{ | ||
4454 | + u8 isr = ioread8(port->membase + TIMBUART_ISR); | ||
4455 | + | ||
4456 | + return (isr & TXBAE) ? TIOCSER_TEMT : 0; | ||
4457 | +} | ||
4458 | + | ||
4459 | +static unsigned int timbuart_get_mctrl(struct uart_port *port) | ||
4460 | +{ | ||
4461 | + u8 cts = ioread8(port->membase + TIMBUART_CTRL); | ||
4462 | + dev_dbg(port->dev, "%s - cts %x\n", __func__, cts); | ||
4463 | + | ||
4464 | + if (cts & TIMBUART_CTRL_CTS) | ||
4465 | + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; | ||
4466 | + else | ||
4467 | + return TIOCM_DSR | TIOCM_CAR; | ||
4468 | +} | ||
4469 | + | ||
4470 | +static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||
4471 | +{ | ||
4472 | + dev_dbg(port->dev, "%s - %x\n", __func__, mctrl); | ||
4473 | + | ||
4474 | + if (mctrl & TIOCM_RTS) | ||
4475 | + iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); | ||
4476 | + else | ||
4477 | + iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); | ||
4478 | +} | ||
4479 | + | ||
4480 | +static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier) | ||
4481 | +{ | ||
4482 | + unsigned int cts; | ||
4483 | + | ||
4484 | + if (isr & CTS_DELTA) { | ||
4485 | + /* ack */ | ||
4486 | + iowrite8(CTS_DELTA, port->membase + TIMBUART_ISR); | ||
4487 | + cts = timbuart_get_mctrl(port); | ||
4488 | + uart_handle_cts_change(port, cts & TIOCM_CTS); | ||
4489 | + wake_up_interruptible(&port->info->delta_msr_wait); | ||
4490 | + } | ||
4491 | + | ||
4492 | + *ier |= CTS_DELTA; | ||
4493 | +} | ||
4494 | + | ||
4495 | +static void timbuart_enable_ms(struct uart_port *port) | ||
4496 | +{ | ||
4497 | + /* N/A */ | ||
4498 | +} | ||
4499 | + | ||
4500 | +static void timbuart_break_ctl(struct uart_port *port, int ctl) | ||
4501 | +{ | ||
4502 | + /* N/A */ | ||
4503 | +} | ||
4504 | + | ||
4505 | +static int timbuart_startup(struct uart_port *port) | ||
4506 | +{ | ||
4507 | + struct timbuart_port *uart = | ||
4508 | + container_of(port, struct timbuart_port, port); | ||
4509 | + | ||
4510 | + dev_dbg(port->dev, "%s\n", __func__); | ||
4511 | + | ||
4512 | + iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); | ||
4513 | + iowrite8(0xff, port->membase + TIMBUART_ISR); | ||
4514 | + /* Enable all but TX interrupts */ | ||
4515 | + iowrite8(RXBAF | RXBF | RXTT | CTS_DELTA, | ||
4516 | + port->membase + TIMBUART_IER); | ||
4517 | + | ||
4518 | + return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED, | ||
4519 | + "timb-uart", uart); | ||
4520 | +} | ||
4521 | + | ||
4522 | +static void timbuart_shutdown(struct uart_port *port) | ||
4523 | +{ | ||
4524 | + struct timbuart_port *uart = | ||
4525 | + container_of(port, struct timbuart_port, port); | ||
4526 | + dev_dbg(port->dev, "%s\n", __func__); | ||
4527 | + free_irq(port->irq, uart); | ||
4528 | + iowrite8(0, port->membase + TIMBUART_IER); | ||
4529 | +} | ||
4530 | + | ||
4531 | +static int get_bindex(int baud) | ||
4532 | +{ | ||
4533 | + int i; | ||
4534 | + | ||
4535 | + for (i = 0; i < ARRAY_SIZE(baudrates); i++) | ||
4536 | + if (baud == baudrates[i]) | ||
4537 | + return i; | ||
4538 | + | ||
4539 | + return -1; | ||
4540 | +} | ||
4541 | + | ||
4542 | +static void timbuart_set_termios(struct uart_port *port, | ||
4543 | + struct ktermios *termios, | ||
4544 | + struct ktermios *old) | ||
4545 | +{ | ||
4546 | + unsigned int baud; | ||
4547 | + short bindex; | ||
4548 | + unsigned long flags; | ||
4549 | + | ||
4550 | + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); | ||
4551 | + bindex = get_bindex(baud); | ||
4552 | + dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex); | ||
4553 | + | ||
4554 | + if (bindex < 0) { | ||
4555 | + printk(KERN_ALERT "timbuart: Unsupported baud rate\n"); | ||
4556 | + } else { | ||
4557 | + spin_lock_irqsave(&port->lock, flags); | ||
4558 | + iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); | ||
4559 | + uart_update_timeout(port, termios->c_cflag, baud); | ||
4560 | + spin_unlock_irqrestore(&port->lock, flags); | ||
4561 | + } | ||
4562 | +} | ||
4563 | + | ||
4564 | +static const char *timbuart_type(struct uart_port *port) | ||
4565 | +{ | ||
4566 | + return port->type == PORT_UNKNOWN ? "timbuart" : NULL; | ||
4567 | +} | ||
4568 | + | ||
4569 | +/* We do not request/release mappings of the registers here, | ||
4570 | + * currently it's done in the proble function. | ||
4571 | + */ | ||
4572 | +static void timbuart_release_port(struct uart_port *port) | ||
4573 | +{ | ||
4574 | + struct platform_device *pdev = to_platform_device(port->dev); | ||
4575 | + int size = | ||
4576 | + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
4577 | + | ||
4578 | + if (port->flags & UPF_IOREMAP) { | ||
4579 | + iounmap(port->membase); | ||
4580 | + port->membase = NULL; | ||
4581 | + } | ||
4582 | + | ||
4583 | + release_mem_region(port->mapbase, size); | ||
4584 | +} | ||
4585 | + | ||
4586 | +static int timbuart_request_port(struct uart_port *port) | ||
4587 | +{ | ||
4588 | + struct platform_device *pdev = to_platform_device(port->dev); | ||
4589 | + int size = | ||
4590 | + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
4591 | + | ||
4592 | + if (!request_mem_region(port->mapbase, size, "timb-uart")) | ||
4593 | + return -EBUSY; | ||
4594 | + | ||
4595 | + if (port->flags & UPF_IOREMAP) { | ||
4596 | + port->membase = ioremap(port->mapbase, size); | ||
4597 | + if (port->membase == NULL) { | ||
4598 | + release_mem_region(port->mapbase, size); | ||
4599 | + return -ENOMEM; | ||
4600 | + } | ||
4601 | + } | ||
4602 | + | ||
4603 | + return 0; | ||
4604 | +} | ||
4605 | + | ||
4606 | +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) | ||
4607 | +{ | ||
4608 | + struct timbuart_port *uart = (struct timbuart_port *)devid; | ||
4609 | + | ||
4610 | + uart->last_ier = ioread8(uart->port.membase + TIMBUART_IER); | ||
4611 | + | ||
4612 | + /* disable interrupts, let the tasklet enable them again if needed */ | ||
4613 | + iowrite8(0, uart->port.membase + TIMBUART_IER); | ||
4614 | + | ||
4615 | + /* fire off bottom half */ | ||
4616 | + tasklet_schedule(&uart->tasklet); | ||
4617 | + | ||
4618 | + return IRQ_HANDLED; | ||
4619 | +} | ||
4620 | + | ||
4621 | +/* | ||
4622 | + * Configure/autoconfigure the port. | ||
4623 | + */ | ||
4624 | +static void timbuart_config_port(struct uart_port *port, int flags) | ||
4625 | +{ | ||
4626 | + if (flags & UART_CONFIG_TYPE) { | ||
4627 | + port->type = PORT_TIMBUART; | ||
4628 | + timbuart_request_port(port); | ||
4629 | + } | ||
4630 | +} | ||
4631 | + | ||
4632 | +static int timbuart_verify_port(struct uart_port *port, | ||
4633 | + struct serial_struct *ser) | ||
4634 | +{ | ||
4635 | + /* we don't want the core code to modify any port params */ | ||
4636 | + return -EINVAL; | ||
4637 | +} | ||
4638 | + | ||
4639 | +static struct uart_ops timbuart_ops = { | ||
4640 | + .tx_empty = timbuart_tx_empty, | ||
4641 | + .set_mctrl = timbuart_set_mctrl, | ||
4642 | + .get_mctrl = timbuart_get_mctrl, | ||
4643 | + .stop_tx = timbuart_stop_tx, | ||
4644 | + .start_tx = timbuart_start_tx, | ||
4645 | + .flush_buffer = timbuart_flush_buffer, | ||
4646 | + .stop_rx = timbuart_stop_rx, | ||
4647 | + .enable_ms = timbuart_enable_ms, | ||
4648 | + .break_ctl = timbuart_break_ctl, | ||
4649 | + .startup = timbuart_startup, | ||
4650 | + .shutdown = timbuart_shutdown, | ||
4651 | + .set_termios = timbuart_set_termios, | ||
4652 | + .type = timbuart_type, | ||
4653 | + .release_port = timbuart_release_port, | ||
4654 | + .request_port = timbuart_request_port, | ||
4655 | + .config_port = timbuart_config_port, | ||
4656 | + .verify_port = timbuart_verify_port | ||
4657 | +}; | ||
4658 | + | ||
4659 | +static struct uart_driver timbuart_driver = { | ||
4660 | + .owner = THIS_MODULE, | ||
4661 | + .driver_name = "timberdale_uart", | ||
4662 | + .dev_name = "ttyTU", | ||
4663 | + .major = TIMBUART_MAJOR, | ||
4664 | + .minor = TIMBUART_MINOR, | ||
4665 | + .nr = 1 | ||
4666 | +}; | ||
4667 | + | ||
4668 | +static int timbuart_probe(struct platform_device *dev) | ||
4669 | +{ | ||
4670 | + int err; | ||
4671 | + struct timbuart_port *uart; | ||
4672 | + struct resource *iomem; | ||
4673 | + | ||
4674 | + dev_dbg(&dev->dev, "%s\n", __func__); | ||
4675 | + | ||
4676 | + uart = kzalloc(sizeof(*uart), GFP_KERNEL); | ||
4677 | + if (!uart) { | ||
4678 | + err = -EINVAL; | ||
4679 | + goto err_mem; | ||
4680 | + } | ||
4681 | + | ||
4682 | + uart->usedma = 0; | ||
4683 | + | ||
4684 | + uart->port.uartclk = 3250000 * 16; | ||
4685 | + uart->port.fifosize = TIMBUART_FIFO_SIZE; | ||
4686 | + uart->port.regshift = 2; | ||
4687 | + uart->port.iotype = UPIO_MEM; | ||
4688 | + uart->port.ops = &timbuart_ops; | ||
4689 | + uart->port.irq = 0; | ||
4690 | + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; | ||
4691 | + uart->port.line = 0; | ||
4692 | + uart->port.dev = &dev->dev; | ||
4693 | + | ||
4694 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
4695 | + if (!iomem) { | ||
4696 | + err = -ENOMEM; | ||
4697 | + goto err_register; | ||
4698 | + } | ||
4699 | + uart->port.mapbase = iomem->start; | ||
4700 | + uart->port.membase = NULL; | ||
4701 | + | ||
4702 | + uart->port.irq = platform_get_irq(dev, 0); | ||
4703 | + if (uart->port.irq < 0) { | ||
4704 | + err = -EINVAL; | ||
4705 | + goto err_register; | ||
4706 | + } | ||
4707 | + | ||
4708 | + tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); | ||
4709 | + | ||
4710 | + err = uart_register_driver(&timbuart_driver); | ||
4711 | + if (err) | ||
4712 | + goto err_register; | ||
4713 | + | ||
4714 | + err = uart_add_one_port(&timbuart_driver, &uart->port); | ||
4715 | + if (err) | ||
4716 | + goto err_add_port; | ||
4717 | + | ||
4718 | + platform_set_drvdata(dev, uart); | ||
4719 | + | ||
4720 | + return 0; | ||
4721 | + | ||
4722 | +err_add_port: | ||
4723 | + uart_unregister_driver(&timbuart_driver); | ||
4724 | +err_register: | ||
4725 | + kfree(uart); | ||
4726 | +err_mem: | ||
4727 | + printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n", | ||
4728 | + err); | ||
4729 | + | ||
4730 | + return err; | ||
4731 | +} | ||
4732 | + | ||
4733 | +static int timbuart_remove(struct platform_device *dev) | ||
4734 | +{ | ||
4735 | + struct timbuart_port *uart = platform_get_drvdata(dev); | ||
4736 | + | ||
4737 | + tasklet_kill(&uart->tasklet); | ||
4738 | + uart_remove_one_port(&timbuart_driver, &uart->port); | ||
4739 | + uart_unregister_driver(&timbuart_driver); | ||
4740 | + kfree(uart); | ||
4741 | + | ||
4742 | + return 0; | ||
4743 | +} | ||
4744 | + | ||
4745 | +static struct platform_driver timbuart_platform_driver = { | ||
4746 | + .driver = { | ||
4747 | + .name = "timb-uart", | ||
4748 | + .owner = THIS_MODULE, | ||
4749 | + }, | ||
4750 | + .probe = timbuart_probe, | ||
4751 | + .remove = timbuart_remove, | ||
4752 | +}; | ||
4753 | + | ||
4754 | +/*--------------------------------------------------------------------------*/ | ||
4755 | + | ||
4756 | +static int __init timbuart_init(void) | ||
4757 | +{ | ||
4758 | + return platform_driver_register(&timbuart_platform_driver); | ||
4759 | +} | ||
4760 | + | ||
4761 | +static void __exit timbuart_exit(void) | ||
4762 | +{ | ||
4763 | + platform_driver_unregister(&timbuart_platform_driver); | ||
4764 | +} | ||
4765 | + | ||
4766 | +module_init(timbuart_init); | ||
4767 | +module_exit(timbuart_exit); | ||
4768 | + | ||
4769 | +MODULE_DESCRIPTION("Timberdale UART driver"); | ||
4770 | +MODULE_LICENSE("GPL v2"); | ||
4771 | +MODULE_ALIAS("platform:timb-uart"); | ||
4772 | + | ||
4773 | diff -uNr linux-2.6.29-clean/drivers/serial/timbuart.h linux-2.6.29/drivers/serial/timbuart.h | ||
4774 | --- linux-2.6.29-clean/drivers/serial/timbuart.h 1969-12-31 16:00:00.000000000 -0800 | ||
4775 | +++ linux-2.6.29/drivers/serial/timbuart.h 2009-04-06 13:51:47.000000000 -0700 | ||
4776 | @@ -0,0 +1,57 @@ | ||
4777 | +/* | ||
4778 | + * timbuart.c timberdale FPGA GPIO driver | ||
4779 | + * Copyright (c) 2009 Intel Corporation | ||
4780 | + * | ||
4781 | + * This program is free software; you can redistribute it and/or modify | ||
4782 | + * it under the terms of the GNU General Public License version 2 as | ||
4783 | + * published by the Free Software Foundation. | ||
4784 | + * | ||
4785 | + * This program is distributed in the hope that it will be useful, | ||
4786 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4787 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4788 | + * GNU General Public License for more details. | ||
4789 | + * | ||
4790 | + * You should have received a copy of the GNU General Public License | ||
4791 | + * along with this program; if not, write to the Free Software | ||
4792 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
4793 | + */ | ||
4794 | + | ||
4795 | +/* Supports: | ||
4796 | + * Timberdale FPGA UART | ||
4797 | + */ | ||
4798 | + | ||
4799 | +#ifndef _TIMBUART_H | ||
4800 | +#define _TIMBUART_H | ||
4801 | + | ||
4802 | +#define TIMBUART_FIFO_SIZE 2048 | ||
4803 | + | ||
4804 | +#define TIMBUART_RXFIFO 0x08 | ||
4805 | +#define TIMBUART_TXFIFO 0x0c | ||
4806 | +#define TIMBUART_IER 0x10 | ||
4807 | +#define TIMBUART_IPR 0x14 | ||
4808 | +#define TIMBUART_ISR 0x18 | ||
4809 | +#define TIMBUART_CTRL 0x1c | ||
4810 | +#define TIMBUART_BAUDRATE 0x20 | ||
4811 | + | ||
4812 | +#define TIMBUART_CTRL_RTS 0x01 | ||
4813 | +#define TIMBUART_CTRL_CTS 0x02 | ||
4814 | +#define TIMBUART_CTRL_FLSHTX 0x40 | ||
4815 | +#define TIMBUART_CTRL_FLSHRX 0x80 | ||
4816 | + | ||
4817 | +#define TXBF 0x01 | ||
4818 | +#define TXBAE 0x02 | ||
4819 | +#define CTS_DELTA 0x04 | ||
4820 | +#define RXDP 0x08 | ||
4821 | +#define RXBAF 0x10 | ||
4822 | +#define RXBF 0x20 | ||
4823 | +#define RXTT 0x40 | ||
4824 | +#define RXBNAE 0x80 | ||
4825 | + | ||
4826 | +#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE) | ||
4827 | +#define TXFLAGS (TXBF | TXBAE) | ||
4828 | + | ||
4829 | +#define TIMBUART_MAJOR 204 | ||
4830 | +#define TIMBUART_MINOR 192 | ||
4831 | + | ||
4832 | +#endif /* _TIMBUART_H */ | ||
4833 | + | ||
4834 | diff -uNr linux-2.6.29-clean/drivers/spi/Kconfig linux-2.6.29/drivers/spi/Kconfig | ||
4835 | --- linux-2.6.29-clean/drivers/spi/Kconfig 2009-04-01 09:20:25.000000000 -0700 | ||
4836 | +++ linux-2.6.29/drivers/spi/Kconfig 2009-04-06 13:51:47.000000000 -0700 | ||
4837 | @@ -211,8 +211,8 @@ | ||
4838 | SPI driver for Toshiba TXx9 MIPS SoCs | ||
4839 | |||
4840 | config SPI_XILINX | ||
4841 | - tristate "Xilinx SPI controller" | ||
4842 | - depends on XILINX_VIRTEX && EXPERIMENTAL | ||
4843 | + tristate "Xilinx SPI controller common module" | ||
4844 | + depends on EXPERIMENTAL | ||
4845 | select SPI_BITBANG | ||
4846 | help | ||
4847 | This exposes the SPI controller IP from the Xilinx EDK. | ||
4848 | @@ -220,6 +220,25 @@ | ||
4849 | See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
4850 | Product Specification document (DS464) for hardware details. | ||
4851 | |||
4852 | +config SPI_XILINX_OF | ||
4853 | + tristate "Xilinx SPI controller OF device" | ||
4854 | + depends on SPI_XILINX && XILINX_VIRTEX | ||
4855 | + help | ||
4856 | + This exposes the SPI controller IP from the Xilinx EDK. | ||
4857 | + | ||
4858 | + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
4859 | + Product Specification document (DS464) for hardware details. | ||
4860 | + | ||
4861 | +config SPI_XILINX_PLTFM | ||
4862 | + tristate "Xilinx SPI controller platform device" | ||
4863 | + depends on SPI_XILINX | ||
4864 | + help | ||
4865 | + This exposes the SPI controller IP from the Xilinx EDK. | ||
4866 | + | ||
4867 | + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
4868 | + Product Specification document (DS464) for hardware details. | ||
4869 | + | ||
4870 | + | ||
4871 | # | ||
4872 | # Add new SPI master controllers in alphabetical order above this line | ||
4873 | # | ||
4874 | diff -uNr linux-2.6.29-clean/drivers/spi/Makefile linux-2.6.29/drivers/spi/Makefile | ||
4875 | --- linux-2.6.29-clean/drivers/spi/Makefile 2009-04-01 09:20:25.000000000 -0700 | ||
4876 | +++ linux-2.6.29/drivers/spi/Makefile 2009-04-06 13:51:47.000000000 -0700 | ||
4877 | @@ -29,6 +29,8 @@ | ||
4878 | obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o | ||
4879 | obj-$(CONFIG_SPI_TXX9) += spi_txx9.o | ||
4880 | obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o | ||
4881 | +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o | ||
4882 | +obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o | ||
4883 | obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o | ||
4884 | # ... add above this line ... | ||
4885 | |||
4886 | diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi.c linux-2.6.29/drivers/spi/xilinx_spi.c | ||
4887 | --- linux-2.6.29-clean/drivers/spi/xilinx_spi.c 2009-04-01 09:20:25.000000000 -0700 | ||
4888 | +++ linux-2.6.29/drivers/spi/xilinx_spi.c 2009-04-06 13:51:47.000000000 -0700 | ||
4889 | @@ -14,22 +14,28 @@ | ||
4890 | #include <linux/module.h> | ||
4891 | #include <linux/init.h> | ||
4892 | #include <linux/interrupt.h> | ||
4893 | -#include <linux/platform_device.h> | ||
4894 | - | ||
4895 | -#include <linux/of_platform.h> | ||
4896 | -#include <linux/of_device.h> | ||
4897 | -#include <linux/of_spi.h> | ||
4898 | |||
4899 | #include <linux/spi/spi.h> | ||
4900 | #include <linux/spi/spi_bitbang.h> | ||
4901 | #include <linux/io.h> | ||
4902 | |||
4903 | -#define XILINX_SPI_NAME "xilinx_spi" | ||
4904 | +#include "xilinx_spi.h" | ||
4905 | + | ||
4906 | +#ifndef CONFIG_PPC | ||
4907 | +#define in_8(addr) ioread8(addr) | ||
4908 | +#define in_be16(addr) ioread16(addr) | ||
4909 | +#define in_be32(addr) ioread32(addr) | ||
4910 | + | ||
4911 | +#define out_8(addr, b) iowrite8(b, addr) | ||
4912 | +#define out_be16(addr, w) iowrite16(w, addr) | ||
4913 | +#define out_be32(addr, l) iowrite32(l, addr) | ||
4914 | +#endif | ||
4915 | + | ||
4916 | |||
4917 | /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) | ||
4918 | * Product Specification", DS464 | ||
4919 | */ | ||
4920 | -#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ | ||
4921 | +#define XSPI_CR_OFFSET_DEF 0x62 /* 16-bit Control Register */ | ||
4922 | |||
4923 | #define XSPI_CR_ENABLE 0x02 | ||
4924 | #define XSPI_CR_MASTER_MODE 0x04 | ||
4925 | @@ -41,7 +47,7 @@ | ||
4926 | #define XSPI_CR_MANUAL_SSELECT 0x80 | ||
4927 | #define XSPI_CR_TRANS_INHIBIT 0x100 | ||
4928 | |||
4929 | -#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ | ||
4930 | +#define XSPI_SR_OFFSET_DEF 0x67 /* 8-bit Status Register */ | ||
4931 | |||
4932 | #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ | ||
4933 | #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ | ||
4934 | @@ -49,10 +55,10 @@ | ||
4935 | #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ | ||
4936 | #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ | ||
4937 | |||
4938 | -#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ | ||
4939 | -#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ | ||
4940 | +#define XSPI_TXD_OFFSET_DEF 0x6b /* 8-bit Data Transmit Register */ | ||
4941 | +#define XSPI_RXD_OFFSET_DEF 0x6f /* 8-bit Data Receive Register */ | ||
4942 | |||
4943 | -#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ | ||
4944 | +#define XSPI_SSR_OFFSET_DEF 0x70 /* 32-bit Slave Select Register */ | ||
4945 | |||
4946 | /* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414 | ||
4947 | * IPIF registers are 32 bit | ||
4948 | @@ -74,24 +80,10 @@ | ||
4949 | #define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ | ||
4950 | #define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ | ||
4951 | |||
4952 | -struct xilinx_spi { | ||
4953 | - /* bitbang has to be first */ | ||
4954 | - struct spi_bitbang bitbang; | ||
4955 | - struct completion done; | ||
4956 | - | ||
4957 | - void __iomem *regs; /* virt. address of the control registers */ | ||
4958 | - | ||
4959 | - u32 irq; | ||
4960 | - | ||
4961 | - u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ | ||
4962 | - | ||
4963 | - u8 *rx_ptr; /* pointer in the Tx buffer */ | ||
4964 | - const u8 *tx_ptr; /* pointer in the Rx buffer */ | ||
4965 | - int remaining_bytes; /* the number of bytes left to transfer */ | ||
4966 | -}; | ||
4967 | |||
4968 | -static void xspi_init_hw(void __iomem *regs_base) | ||
4969 | +void xspi_init_hw(struct xilinx_spi *xspi) | ||
4970 | { | ||
4971 | + void __iomem *regs_base = xspi->regs; | ||
4972 | /* Reset the SPI device */ | ||
4973 | out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, | ||
4974 | XIPIF_V123B_RESET_MASK); | ||
4975 | @@ -101,30 +93,31 @@ | ||
4976 | out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, | ||
4977 | XIPIF_V123B_GINTR_ENABLE); | ||
4978 | /* Deselect the slave on the SPI bus */ | ||
4979 | - out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); | ||
4980 | + out_be32(regs_base + xspi->ssr_offset, 0xffff); | ||
4981 | /* Disable the transmitter, enable Manual Slave Select Assertion, | ||
4982 | * put SPI controller into master mode, and enable it */ | ||
4983 | - out_be16(regs_base + XSPI_CR_OFFSET, | ||
4984 | + out_be16(regs_base + xspi->cr_offset, | ||
4985 | XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | ||
4986 | | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); | ||
4987 | } | ||
4988 | +EXPORT_SYMBOL(xspi_init_hw); | ||
4989 | |||
4990 | -static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) | ||
4991 | +void xilinx_spi_chipselect(struct spi_device *spi, int is_on) | ||
4992 | { | ||
4993 | struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); | ||
4994 | |||
4995 | if (is_on == BITBANG_CS_INACTIVE) { | ||
4996 | /* Deselect the slave on the SPI bus */ | ||
4997 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); | ||
4998 | + out_be32(xspi->regs + xspi->ssr_offset, 0xffff); | ||
4999 | } else if (is_on == BITBANG_CS_ACTIVE) { | ||
5000 | /* Set the SPI clock phase and polarity */ | ||
5001 | - u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) | ||
5002 | + u16 cr = in_be16(xspi->regs + xspi->cr_offset) | ||
5003 | & ~XSPI_CR_MODE_MASK; | ||
5004 | if (spi->mode & SPI_CPHA) | ||
5005 | cr |= XSPI_CR_CPHA; | ||
5006 | if (spi->mode & SPI_CPOL) | ||
5007 | cr |= XSPI_CR_CPOL; | ||
5008 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
5009 | + out_be16(xspi->regs + xspi->cr_offset, cr); | ||
5010 | |||
5011 | /* We do not check spi->max_speed_hz here as the SPI clock | ||
5012 | * frequency is not software programmable (the IP block design | ||
5013 | @@ -132,10 +125,11 @@ | ||
5014 | */ | ||
5015 | |||
5016 | /* Activate the chip select */ | ||
5017 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, | ||
5018 | + out_be32(xspi->regs + xspi->ssr_offset, | ||
5019 | ~(0x0001 << spi->chip_select)); | ||
5020 | } | ||
5021 | } | ||
5022 | +EXPORT_SYMBOL(xilinx_spi_chipselect); | ||
5023 | |||
5024 | /* spi_bitbang requires custom setup_transfer() to be defined if there is a | ||
5025 | * custom txrx_bufs(). We have nothing to setup here as the SPI IP block | ||
5026 | @@ -143,8 +137,7 @@ | ||
5027 | * Check for 8 bits per word. Chip select delay calculations could be | ||
5028 | * added here as soon as bitbang_work() can be made aware of the delay value. | ||
5029 | */ | ||
5030 | -static int xilinx_spi_setup_transfer(struct spi_device *spi, | ||
5031 | - struct spi_transfer *t) | ||
5032 | +int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) | ||
5033 | { | ||
5034 | u8 bits_per_word; | ||
5035 | |||
5036 | @@ -157,11 +150,12 @@ | ||
5037 | |||
5038 | return 0; | ||
5039 | } | ||
5040 | +EXPORT_SYMBOL(xilinx_spi_setup_transfer); | ||
5041 | |||
5042 | /* the spi->mode bits understood by this driver: */ | ||
5043 | #define MODEBITS (SPI_CPOL | SPI_CPHA) | ||
5044 | |||
5045 | -static int xilinx_spi_setup(struct spi_device *spi) | ||
5046 | +int xilinx_spi_setup(struct spi_device *spi) | ||
5047 | { | ||
5048 | struct spi_bitbang *bitbang; | ||
5049 | struct xilinx_spi *xspi; | ||
5050 | @@ -188,25 +182,25 @@ | ||
5051 | |||
5052 | return 0; | ||
5053 | } | ||
5054 | +EXPORT_SYMBOL(xilinx_spi_setup); | ||
5055 | |||
5056 | static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) | ||
5057 | { | ||
5058 | u8 sr; | ||
5059 | |||
5060 | /* Fill the Tx FIFO with as many bytes as possible */ | ||
5061 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
5062 | + sr = in_8(xspi->regs + xspi->sr_offset); | ||
5063 | while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { | ||
5064 | - if (xspi->tx_ptr) { | ||
5065 | - out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); | ||
5066 | - } else { | ||
5067 | - out_8(xspi->regs + XSPI_TXD_OFFSET, 0); | ||
5068 | - } | ||
5069 | + if (xspi->tx_ptr) | ||
5070 | + out_8(xspi->regs + xspi->txd_offset, *xspi->tx_ptr++); | ||
5071 | + else | ||
5072 | + out_8(xspi->regs + xspi->txd_offset, 0); | ||
5073 | xspi->remaining_bytes--; | ||
5074 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
5075 | + sr = in_8(xspi->regs + xspi->sr_offset); | ||
5076 | } | ||
5077 | } | ||
5078 | |||
5079 | -static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | ||
5080 | +int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | ||
5081 | { | ||
5082 | struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); | ||
5083 | u32 ipif_ier; | ||
5084 | @@ -229,8 +223,8 @@ | ||
5085 | ipif_ier | XSPI_INTR_TX_EMPTY); | ||
5086 | |||
5087 | /* Start the transfer by not inhibiting the transmitter any longer */ | ||
5088 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; | ||
5089 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
5090 | + cr = in_be16(xspi->regs + xspi->cr_offset) & ~XSPI_CR_TRANS_INHIBIT; | ||
5091 | + out_be16(xspi->regs + xspi->cr_offset, cr); | ||
5092 | |||
5093 | wait_for_completion(&xspi->done); | ||
5094 | |||
5095 | @@ -239,14 +233,14 @@ | ||
5096 | |||
5097 | return t->len - xspi->remaining_bytes; | ||
5098 | } | ||
5099 | - | ||
5100 | +EXPORT_SYMBOL(xilinx_spi_txrx_bufs); | ||
5101 | |||
5102 | /* This driver supports single master mode only. Hence Tx FIFO Empty | ||
5103 | * is the only interrupt we care about. | ||
5104 | * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode | ||
5105 | * Fault are not to happen. | ||
5106 | */ | ||
5107 | -static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) | ||
5108 | +irqreturn_t xilinx_spi_irq(int irq, void *dev_id) | ||
5109 | { | ||
5110 | struct xilinx_spi *xspi = dev_id; | ||
5111 | u32 ipif_isr; | ||
5112 | @@ -264,20 +258,19 @@ | ||
5113 | * transmitter while the Isr refills the transmit register/FIFO, | ||
5114 | * or make sure it is stopped if we're done. | ||
5115 | */ | ||
5116 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET); | ||
5117 | - out_be16(xspi->regs + XSPI_CR_OFFSET, | ||
5118 | + cr = in_be16(xspi->regs + xspi->cr_offset); | ||
5119 | + out_be16(xspi->regs + xspi->cr_offset, | ||
5120 | cr | XSPI_CR_TRANS_INHIBIT); | ||
5121 | |||
5122 | /* Read out all the data from the Rx FIFO */ | ||
5123 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
5124 | + sr = in_8(xspi->regs + xspi->sr_offset); | ||
5125 | while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { | ||
5126 | u8 data; | ||
5127 | |||
5128 | - data = in_8(xspi->regs + XSPI_RXD_OFFSET); | ||
5129 | - if (xspi->rx_ptr) { | ||
5130 | + data = in_8(xspi->regs + xspi->rxd_offset); | ||
5131 | + if (xspi->rx_ptr) | ||
5132 | *xspi->rx_ptr++ = data; | ||
5133 | - } | ||
5134 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
5135 | + sr = in_8(xspi->regs + xspi->sr_offset); | ||
5136 | } | ||
5137 | |||
5138 | /* See if there is more data to send */ | ||
5139 | @@ -286,7 +279,7 @@ | ||
5140 | /* Start the transfer by not inhibiting the | ||
5141 | * transmitter any longer | ||
5142 | */ | ||
5143 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
5144 | + out_be16(xspi->regs + xspi->cr_offset, cr); | ||
5145 | } else { | ||
5146 | /* No more data to send. | ||
5147 | * Indicate the transfer is completed. | ||
5148 | @@ -297,167 +290,18 @@ | ||
5149 | |||
5150 | return IRQ_HANDLED; | ||
5151 | } | ||
5152 | +EXPORT_SYMBOL(xilinx_spi_irq); | ||
5153 | |||
5154 | -static int __init xilinx_spi_of_probe(struct of_device *ofdev, | ||
5155 | - const struct of_device_id *match) | ||
5156 | -{ | ||
5157 | - struct spi_master *master; | ||
5158 | - struct xilinx_spi *xspi; | ||
5159 | - struct resource r_irq_struct; | ||
5160 | - struct resource r_mem_struct; | ||
5161 | - | ||
5162 | - struct resource *r_irq = &r_irq_struct; | ||
5163 | - struct resource *r_mem = &r_mem_struct; | ||
5164 | - int rc = 0; | ||
5165 | - const u32 *prop; | ||
5166 | - int len; | ||
5167 | - | ||
5168 | - /* Get resources(memory, IRQ) associated with the device */ | ||
5169 | - master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); | ||
5170 | - | ||
5171 | - if (master == NULL) { | ||
5172 | - return -ENOMEM; | ||
5173 | - } | ||
5174 | - | ||
5175 | - dev_set_drvdata(&ofdev->dev, master); | ||
5176 | - | ||
5177 | - rc = of_address_to_resource(ofdev->node, 0, r_mem); | ||
5178 | - if (rc) { | ||
5179 | - dev_warn(&ofdev->dev, "invalid address\n"); | ||
5180 | - goto put_master; | ||
5181 | - } | ||
5182 | - | ||
5183 | - rc = of_irq_to_resource(ofdev->node, 0, r_irq); | ||
5184 | - if (rc == NO_IRQ) { | ||
5185 | - dev_warn(&ofdev->dev, "no IRQ found\n"); | ||
5186 | - goto put_master; | ||
5187 | - } | ||
5188 | - | ||
5189 | - xspi = spi_master_get_devdata(master); | ||
5190 | - xspi->bitbang.master = spi_master_get(master); | ||
5191 | - xspi->bitbang.chipselect = xilinx_spi_chipselect; | ||
5192 | - xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; | ||
5193 | - xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; | ||
5194 | - xspi->bitbang.master->setup = xilinx_spi_setup; | ||
5195 | - init_completion(&xspi->done); | ||
5196 | - | ||
5197 | - xspi->irq = r_irq->start; | ||
5198 | - | ||
5199 | - if (!request_mem_region(r_mem->start, | ||
5200 | - r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { | ||
5201 | - rc = -ENXIO; | ||
5202 | - dev_warn(&ofdev->dev, "memory request failure\n"); | ||
5203 | - goto put_master; | ||
5204 | - } | ||
5205 | - | ||
5206 | - xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); | ||
5207 | - if (xspi->regs == NULL) { | ||
5208 | - rc = -ENOMEM; | ||
5209 | - dev_warn(&ofdev->dev, "ioremap failure\n"); | ||
5210 | - goto put_master; | ||
5211 | - } | ||
5212 | - xspi->irq = r_irq->start; | ||
5213 | - | ||
5214 | - /* dynamic bus assignment */ | ||
5215 | - master->bus_num = -1; | ||
5216 | - | ||
5217 | - /* number of slave select bits is required */ | ||
5218 | - prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | ||
5219 | - if (!prop || len < sizeof(*prop)) { | ||
5220 | - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | ||
5221 | - goto put_master; | ||
5222 | - } | ||
5223 | - master->num_chipselect = *prop; | ||
5224 | - | ||
5225 | - /* SPI controller initializations */ | ||
5226 | - xspi_init_hw(xspi->regs); | ||
5227 | - | ||
5228 | - /* Register for SPI Interrupt */ | ||
5229 | - rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | ||
5230 | - if (rc != 0) { | ||
5231 | - dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); | ||
5232 | - goto unmap_io; | ||
5233 | - } | ||
5234 | - | ||
5235 | - rc = spi_bitbang_start(&xspi->bitbang); | ||
5236 | - if (rc != 0) { | ||
5237 | - dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); | ||
5238 | - goto free_irq; | ||
5239 | - } | ||
5240 | - | ||
5241 | - dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | ||
5242 | - (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); | ||
5243 | - | ||
5244 | - /* Add any subnodes on the SPI bus */ | ||
5245 | - of_register_spi_devices(master, ofdev->node); | ||
5246 | - | ||
5247 | - return rc; | ||
5248 | - | ||
5249 | -free_irq: | ||
5250 | - free_irq(xspi->irq, xspi); | ||
5251 | -unmap_io: | ||
5252 | - iounmap(xspi->regs); | ||
5253 | -put_master: | ||
5254 | - spi_master_put(master); | ||
5255 | - return rc; | ||
5256 | -} | ||
5257 | - | ||
5258 | -static int __devexit xilinx_spi_remove(struct of_device *ofdev) | ||
5259 | +void xilinx_spi_set_default_reg_offsets(struct xilinx_spi *xspi) | ||
5260 | { | ||
5261 | - struct xilinx_spi *xspi; | ||
5262 | - struct spi_master *master; | ||
5263 | - | ||
5264 | - master = platform_get_drvdata(ofdev); | ||
5265 | - xspi = spi_master_get_devdata(master); | ||
5266 | - | ||
5267 | - spi_bitbang_stop(&xspi->bitbang); | ||
5268 | - free_irq(xspi->irq, xspi); | ||
5269 | - iounmap(xspi->regs); | ||
5270 | - dev_set_drvdata(&ofdev->dev, 0); | ||
5271 | - spi_master_put(xspi->bitbang.master); | ||
5272 | - | ||
5273 | - return 0; | ||
5274 | -} | ||
5275 | - | ||
5276 | -/* work with hotplug and coldplug */ | ||
5277 | -MODULE_ALIAS("platform:" XILINX_SPI_NAME); | ||
5278 | - | ||
5279 | -static int __exit xilinx_spi_of_remove(struct of_device *op) | ||
5280 | -{ | ||
5281 | - return xilinx_spi_remove(op); | ||
5282 | + xspi->cr_offset = XSPI_CR_OFFSET_DEF; | ||
5283 | + xspi->sr_offset = XSPI_SR_OFFSET_DEF; | ||
5284 | + xspi->txd_offset = XSPI_TXD_OFFSET_DEF; | ||
5285 | + xspi->rxd_offset = XSPI_RXD_OFFSET_DEF; | ||
5286 | + xspi->ssr_offset = XSPI_SSR_OFFSET_DEF; | ||
5287 | } | ||
5288 | +EXPORT_SYMBOL(xilinx_spi_set_default_reg_offsets); | ||
5289 | |||
5290 | -static struct of_device_id xilinx_spi_of_match[] = { | ||
5291 | - { .compatible = "xlnx,xps-spi-2.00.a", }, | ||
5292 | - { .compatible = "xlnx,xps-spi-2.00.b", }, | ||
5293 | - {} | ||
5294 | -}; | ||
5295 | - | ||
5296 | -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | ||
5297 | - | ||
5298 | -static struct of_platform_driver xilinx_spi_of_driver = { | ||
5299 | - .owner = THIS_MODULE, | ||
5300 | - .name = "xilinx-xps-spi", | ||
5301 | - .match_table = xilinx_spi_of_match, | ||
5302 | - .probe = xilinx_spi_of_probe, | ||
5303 | - .remove = __exit_p(xilinx_spi_of_remove), | ||
5304 | - .driver = { | ||
5305 | - .name = "xilinx-xps-spi", | ||
5306 | - .owner = THIS_MODULE, | ||
5307 | - }, | ||
5308 | -}; | ||
5309 | - | ||
5310 | -static int __init xilinx_spi_init(void) | ||
5311 | -{ | ||
5312 | - return of_register_platform_driver(&xilinx_spi_of_driver); | ||
5313 | -} | ||
5314 | -module_init(xilinx_spi_init); | ||
5315 | - | ||
5316 | -static void __exit xilinx_spi_exit(void) | ||
5317 | -{ | ||
5318 | - of_unregister_platform_driver(&xilinx_spi_of_driver); | ||
5319 | -} | ||
5320 | -module_exit(xilinx_spi_exit); | ||
5321 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | ||
5322 | MODULE_DESCRIPTION("Xilinx SPI driver"); | ||
5323 | MODULE_LICENSE("GPL"); | ||
5324 | diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi.h linux-2.6.29/drivers/spi/xilinx_spi.h | ||
5325 | --- linux-2.6.29-clean/drivers/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 | ||
5326 | +++ linux-2.6.29/drivers/spi/xilinx_spi.h 2009-04-06 13:51:47.000000000 -0700 | ||
5327 | @@ -0,0 +1,52 @@ | ||
5328 | +/* | ||
5329 | + * xilinx_spi.c | ||
5330 | + * | ||
5331 | + * Xilinx SPI controller driver (master mode only) | ||
5332 | + * | ||
5333 | + * Author: MontaVista Software, Inc. | ||
5334 | + * source@mvista.com | ||
5335 | + * | ||
5336 | + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the | ||
5337 | + * terms of the GNU General Public License version 2. This program is licensed | ||
5338 | + * "as is" without any warranty of any kind, whether express or implied. | ||
5339 | + */ | ||
5340 | + | ||
5341 | +#ifndef _XILINX_SPI_H_ | ||
5342 | +#define _XILINX_SPI_H_ 1 | ||
5343 | + | ||
5344 | +#include <linux/spi/spi.h> | ||
5345 | +#include <linux/spi/spi_bitbang.h> | ||
5346 | + | ||
5347 | +#define XILINX_SPI_NAME "xilinx_spi" | ||
5348 | + | ||
5349 | + | ||
5350 | +struct xilinx_spi { | ||
5351 | + /* bitbang has to be first */ | ||
5352 | + struct spi_bitbang bitbang; | ||
5353 | + struct completion done; | ||
5354 | + | ||
5355 | + void __iomem *regs; /* virt. address of the control registers */ | ||
5356 | + | ||
5357 | + u32 irq; | ||
5358 | + | ||
5359 | + u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ | ||
5360 | + | ||
5361 | + u8 *rx_ptr; /* pointer in the Tx buffer */ | ||
5362 | + const u8 *tx_ptr; /* pointer in the Rx buffer */ | ||
5363 | + int remaining_bytes; /* the number of bytes left to transfer */ | ||
5364 | + /* offset to the XSPI regs, these might vary... */ | ||
5365 | + u8 cr_offset; | ||
5366 | + u8 sr_offset; | ||
5367 | + u8 txd_offset; | ||
5368 | + u8 rxd_offset; | ||
5369 | + u8 ssr_offset; | ||
5370 | +}; | ||
5371 | + | ||
5372 | +void xspi_init_hw(struct xilinx_spi *xspi); | ||
5373 | +void xilinx_spi_set_default_reg_offsets(struct xilinx_spi *xspi); | ||
5374 | +void xilinx_spi_chipselect(struct spi_device *spi, int is_on); | ||
5375 | +int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t); | ||
5376 | +int xilinx_spi_setup(struct spi_device *spi); | ||
5377 | +int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t); | ||
5378 | +irqreturn_t xilinx_spi_irq(int irq, void *dev_id); | ||
5379 | +#endif | ||
5380 | diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi_of.c linux-2.6.29/drivers/spi/xilinx_spi_of.c | ||
5381 | --- linux-2.6.29-clean/drivers/spi/xilinx_spi_of.c 1969-12-31 16:00:00.000000000 -0800 | ||
5382 | +++ linux-2.6.29/drivers/spi/xilinx_spi_of.c 2009-04-06 13:51:47.000000000 -0700 | ||
5383 | @@ -0,0 +1,193 @@ | ||
5384 | +/* | ||
5385 | + * xilinx_spi.c | ||
5386 | + * | ||
5387 | + * Xilinx SPI controller driver (master mode only) | ||
5388 | + * | ||
5389 | + * Author: MontaVista Software, Inc. | ||
5390 | + * source@mvista.com | ||
5391 | + * | ||
5392 | + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the | ||
5393 | + * terms of the GNU General Public License version 2. This program is licensed | ||
5394 | + * "as is" without any warranty of any kind, whether express or implied. | ||
5395 | + */ | ||
5396 | + | ||
5397 | +#include <linux/module.h> | ||
5398 | +#include <linux/init.h> | ||
5399 | +#include <linux/interrupt.h> | ||
5400 | +#include <linux/io.h> | ||
5401 | +#include <linux/platform_device.h> | ||
5402 | + | ||
5403 | +#include <linux/of_platform.h> | ||
5404 | +#include <linux/of_device.h> | ||
5405 | +#include <linux/of_spi.h> | ||
5406 | + | ||
5407 | +#include <linux/spi/spi.h> | ||
5408 | +#include <linux/spi/spi_bitbang.h> | ||
5409 | + | ||
5410 | +#include "xilinx_spi.h" | ||
5411 | + | ||
5412 | + | ||
5413 | +static int __init xilinx_spi_of_probe(struct of_device *ofdev, | ||
5414 | + const struct of_device_id *match) | ||
5415 | +{ | ||
5416 | + struct spi_master *master; | ||
5417 | + struct xilinx_spi *xspi; | ||
5418 | + struct resource r_irq_struct; | ||
5419 | + struct resource r_mem_struct; | ||
5420 | + | ||
5421 | + struct resource *r_irq = &r_irq_struct; | ||
5422 | + struct resource *r_mem = &r_mem_struct; | ||
5423 | + int rc = 0; | ||
5424 | + const u32 *prop; | ||
5425 | + int len; | ||
5426 | + | ||
5427 | + /* Get resources(memory, IRQ) associated with the device */ | ||
5428 | + master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); | ||
5429 | + | ||
5430 | + if (master == NULL) | ||
5431 | + return -ENOMEM; | ||
5432 | + | ||
5433 | + dev_set_drvdata(&ofdev->dev, master); | ||
5434 | + | ||
5435 | + rc = of_address_to_resource(ofdev->node, 0, r_mem); | ||
5436 | + if (rc) { | ||
5437 | + dev_warn(&ofdev->dev, "invalid address\n"); | ||
5438 | + goto put_master; | ||
5439 | + } | ||
5440 | + | ||
5441 | + rc = of_irq_to_resource(ofdev->node, 0, r_irq); | ||
5442 | + if (rc == NO_IRQ) { | ||
5443 | + dev_warn(&ofdev->dev, "no IRQ found\n"); | ||
5444 | + goto put_master; | ||
5445 | + } | ||
5446 | + | ||
5447 | + xspi = spi_master_get_devdata(master); | ||
5448 | + xspi->bitbang.master = spi_master_get(master); | ||
5449 | + xspi->bitbang.chipselect = xilinx_spi_chipselect; | ||
5450 | + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; | ||
5451 | + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; | ||
5452 | + xspi->bitbang.master->setup = xilinx_spi_setup; | ||
5453 | + init_completion(&xspi->done); | ||
5454 | + | ||
5455 | + xspi->irq = r_irq->start; | ||
5456 | + | ||
5457 | + if (!request_mem_region(r_mem->start, | ||
5458 | + r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { | ||
5459 | + rc = -ENXIO; | ||
5460 | + dev_warn(&ofdev->dev, "memory request failure\n"); | ||
5461 | + goto put_master; | ||
5462 | + } | ||
5463 | + | ||
5464 | + xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); | ||
5465 | + if (xspi->regs == NULL) { | ||
5466 | + rc = -ENOMEM; | ||
5467 | + dev_warn(&ofdev->dev, "ioremap failure\n"); | ||
5468 | + goto put_master; | ||
5469 | + } | ||
5470 | + xspi->irq = r_irq->start; | ||
5471 | + | ||
5472 | + /* dynamic bus assignment */ | ||
5473 | + master->bus_num = -1; | ||
5474 | + | ||
5475 | + /* number of slave select bits is required */ | ||
5476 | + prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | ||
5477 | + if (!prop || len < sizeof(*prop)) { | ||
5478 | + dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | ||
5479 | + goto put_master; | ||
5480 | + } | ||
5481 | + master->num_chipselect = *prop; | ||
5482 | + | ||
5483 | + xilinx_spi_set_default_reg_offsets(xspi); | ||
5484 | + | ||
5485 | + /* SPI controller initializations */ | ||
5486 | + xspi_init_hw(xspi->regs); | ||
5487 | + | ||
5488 | + /* Register for SPI Interrupt */ | ||
5489 | + rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | ||
5490 | + if (rc != 0) { | ||
5491 | + dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); | ||
5492 | + goto unmap_io; | ||
5493 | + } | ||
5494 | + | ||
5495 | + rc = spi_bitbang_start(&xspi->bitbang); | ||
5496 | + if (rc != 0) { | ||
5497 | + dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); | ||
5498 | + goto free_irq; | ||
5499 | + } | ||
5500 | + | ||
5501 | + dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | ||
5502 | + (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); | ||
5503 | + | ||
5504 | + /* Add any subnodes on the SPI bus */ | ||
5505 | + of_register_spi_devices(master, ofdev->node); | ||
5506 | + | ||
5507 | + return rc; | ||
5508 | + | ||
5509 | +free_irq: | ||
5510 | + free_irq(xspi->irq, xspi); | ||
5511 | +unmap_io: | ||
5512 | + iounmap(xspi->regs); | ||
5513 | +put_master: | ||
5514 | + spi_master_put(master); | ||
5515 | + return rc; | ||
5516 | +} | ||
5517 | + | ||
5518 | +static int __devexit xilinx_spi_remove(struct of_device *ofdev) | ||
5519 | +{ | ||
5520 | + struct xilinx_spi *xspi; | ||
5521 | + struct spi_master *master; | ||
5522 | + | ||
5523 | + master = platform_get_drvdata(ofdev); | ||
5524 | + xspi = spi_master_get_devdata(master); | ||
5525 | + | ||
5526 | + spi_bitbang_stop(&xspi->bitbang); | ||
5527 | + free_irq(xspi->irq, xspi); | ||
5528 | + iounmap(xspi->regs); | ||
5529 | + dev_set_drvdata(&ofdev->dev, 0); | ||
5530 | + spi_master_put(xspi->bitbang.master); | ||
5531 | + | ||
5532 | + return 0; | ||
5533 | +} | ||
5534 | + | ||
5535 | +/* work with hotplug and coldplug */ | ||
5536 | +MODULE_ALIAS("platform:" XILINX_SPI_NAME); | ||
5537 | + | ||
5538 | +static int __exit xilinx_spi_of_remove(struct of_device *op) | ||
5539 | +{ | ||
5540 | + return xilinx_spi_remove(op); | ||
5541 | +} | ||
5542 | + | ||
5543 | +static struct of_device_id xilinx_spi_of_match[] = { | ||
5544 | + { .compatible = "xlnx,xps-spi-2.00.a", }, | ||
5545 | + { .compatible = "xlnx,xps-spi-2.00.b", }, | ||
5546 | + {} | ||
5547 | +}; | ||
5548 | + | ||
5549 | +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | ||
5550 | + | ||
5551 | +static struct of_platform_driver xilinx_spi_of_driver = { | ||
5552 | + .owner = THIS_MODULE, | ||
5553 | + .name = "xilinx-xps-spi", | ||
5554 | + .match_table = xilinx_spi_of_match, | ||
5555 | + .probe = xilinx_spi_of_probe, | ||
5556 | + .remove = __exit_p(xilinx_spi_of_remove), | ||
5557 | + .driver = { | ||
5558 | + .name = "xilinx-xps-spi", | ||
5559 | + .owner = THIS_MODULE, | ||
5560 | + }, | ||
5561 | +}; | ||
5562 | + | ||
5563 | +static int __init xilinx_spi_init(void) | ||
5564 | +{ | ||
5565 | + return of_register_platform_driver(&xilinx_spi_of_driver); | ||
5566 | +} | ||
5567 | +module_init(xilinx_spi_init); | ||
5568 | + | ||
5569 | +static void __exit xilinx_spi_exit(void) | ||
5570 | +{ | ||
5571 | + of_unregister_platform_driver(&xilinx_spi_of_driver); | ||
5572 | +} | ||
5573 | +module_exit(xilinx_spi_exit); | ||
5574 | +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | ||
5575 | +MODULE_DESCRIPTION("Xilinx SPI driver"); | ||
5576 | +MODULE_LICENSE("GPL"); | ||
5577 | diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi_pltfm.c linux-2.6.29/drivers/spi/xilinx_spi_pltfm.c | ||
5578 | --- linux-2.6.29-clean/drivers/spi/xilinx_spi_pltfm.c 1969-12-31 16:00:00.000000000 -0800 | ||
5579 | +++ linux-2.6.29/drivers/spi/xilinx_spi_pltfm.c 2009-04-06 13:51:47.000000000 -0700 | ||
5580 | @@ -0,0 +1,184 @@ | ||
5581 | +/* | ||
5582 | + * xilinx_spi_pltfm.c Support for Xilinx SPI platform devices | ||
5583 | + * Copyright (c) 2009 Intel Corporation | ||
5584 | + * | ||
5585 | + * This program is free software; you can redistribute it and/or modify | ||
5586 | + * it under the terms of the GNU General Public License version 2 as | ||
5587 | + * published by the Free Software Foundation. | ||
5588 | + * | ||
5589 | + * This program is distributed in the hope that it will be useful, | ||
5590 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5591 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5592 | + * GNU General Public License for more details. | ||
5593 | + * | ||
5594 | + * You should have received a copy of the GNU General Public License | ||
5595 | + * along with this program; if not, write to the Free Software | ||
5596 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5597 | + */ | ||
5598 | + | ||
5599 | +/* Supports: | ||
5600 | + * Xilinx SPI devices as platform devices | ||
5601 | + * | ||
5602 | + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. | ||
5603 | + */ | ||
5604 | + | ||
5605 | +#include <linux/module.h> | ||
5606 | +#include <linux/init.h> | ||
5607 | +#include <linux/interrupt.h> | ||
5608 | +#include <linux/io.h> | ||
5609 | +#include <linux/platform_device.h> | ||
5610 | + | ||
5611 | +#include <linux/spi/spi.h> | ||
5612 | +#include <linux/spi/spi_bitbang.h> | ||
5613 | +#include <linux/spi/xilinx_spi.h> | ||
5614 | + | ||
5615 | +#include "xilinx_spi.h" | ||
5616 | + | ||
5617 | +static int __init xilinx_spi_probe(struct platform_device *dev) | ||
5618 | +{ | ||
5619 | + int ret = 0; | ||
5620 | + struct spi_master *master; | ||
5621 | + struct xilinx_spi *xspi; | ||
5622 | + struct xspi_platform_data *pdata; | ||
5623 | + struct resource *r; | ||
5624 | + | ||
5625 | + master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi)); | ||
5626 | + | ||
5627 | + if (master == NULL) | ||
5628 | + return -ENOMEM; | ||
5629 | + | ||
5630 | + | ||
5631 | + platform_set_drvdata(dev, master); | ||
5632 | + pdata = dev->dev.platform_data; | ||
5633 | + if (pdata == NULL) { | ||
5634 | + ret = -ENODEV; | ||
5635 | + goto put_master; | ||
5636 | + } | ||
5637 | + | ||
5638 | + r = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
5639 | + if (r == NULL) { | ||
5640 | + ret = -ENODEV; | ||
5641 | + goto put_master; | ||
5642 | + } | ||
5643 | + | ||
5644 | + xspi = spi_master_get_devdata(master); | ||
5645 | + xspi->bitbang.master = spi_master_get(master); | ||
5646 | + xspi->bitbang.chipselect = xilinx_spi_chipselect; | ||
5647 | + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; | ||
5648 | + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; | ||
5649 | + xspi->bitbang.master->setup = xilinx_spi_setup; | ||
5650 | + init_completion(&xspi->done); | ||
5651 | + | ||
5652 | + if (!request_mem_region(r->start, resource_size(r), XILINX_SPI_NAME)) { | ||
5653 | + ret = -ENXIO; | ||
5654 | + goto put_master; | ||
5655 | + } | ||
5656 | + | ||
5657 | + xspi->regs = ioremap(r->start, resource_size(r)); | ||
5658 | + if (xspi->regs == NULL) { | ||
5659 | + ret = -ENOMEM; | ||
5660 | + goto map_failed; | ||
5661 | + } | ||
5662 | + | ||
5663 | + ret = platform_get_irq(dev, 0); | ||
5664 | + if (ret < 0) { | ||
5665 | + ret = -ENXIO; | ||
5666 | + goto unmap_io; | ||
5667 | + } | ||
5668 | + xspi->irq = ret; | ||
5669 | + | ||
5670 | + master->bus_num = pdata->bus_num; | ||
5671 | + master->num_chipselect = pdata->num_chipselect; | ||
5672 | + xspi->speed_hz = pdata->speed_hz; | ||
5673 | + xilinx_spi_set_default_reg_offsets(xspi); | ||
5674 | + if (pdata->cr_offset) | ||
5675 | + xspi->cr_offset = pdata->cr_offset; | ||
5676 | + if (pdata->sr_offset) | ||
5677 | + xspi->sr_offset = pdata->sr_offset; | ||
5678 | + if (pdata->txd_offset) | ||
5679 | + xspi->txd_offset = pdata->txd_offset; | ||
5680 | + if (pdata->rxd_offset) | ||
5681 | + xspi->rxd_offset = pdata->rxd_offset; | ||
5682 | + if (pdata->ssr_offset) | ||
5683 | + xspi->ssr_offset = pdata->ssr_offset; | ||
5684 | + | ||
5685 | + /* SPI controller initializations */ | ||
5686 | + xspi_init_hw(xspi); | ||
5687 | + | ||
5688 | + /* Register for SPI Interrupt */ | ||
5689 | + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | ||
5690 | + if (ret != 0) | ||
5691 | + goto unmap_io; | ||
5692 | + | ||
5693 | + ret = spi_bitbang_start(&xspi->bitbang); | ||
5694 | + if (ret != 0) { | ||
5695 | + dev_err(&dev->dev, "spi_bitbang_start FAILED\n"); | ||
5696 | + goto free_irq; | ||
5697 | + } | ||
5698 | + | ||
5699 | + dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | ||
5700 | + (u32)r->start, (u32)xspi->regs, xspi->irq); | ||
5701 | + return ret; | ||
5702 | + | ||
5703 | +free_irq: | ||
5704 | + free_irq(xspi->irq, xspi); | ||
5705 | +unmap_io: | ||
5706 | + iounmap(xspi->regs); | ||
5707 | +map_failed: | ||
5708 | + release_mem_region(r->start, resource_size(r)); | ||
5709 | +put_master: | ||
5710 | + spi_master_put(master); | ||
5711 | + return ret; | ||
5712 | +} | ||
5713 | + | ||
5714 | +static int __devexit xilinx_spi_remove(struct platform_device *dev) | ||
5715 | +{ | ||
5716 | + struct xilinx_spi *xspi; | ||
5717 | + struct spi_master *master; | ||
5718 | + struct resource *r; | ||
5719 | + | ||
5720 | + master = platform_get_drvdata(dev); | ||
5721 | + xspi = spi_master_get_devdata(master); | ||
5722 | + r = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
5723 | + | ||
5724 | + spi_bitbang_stop(&xspi->bitbang); | ||
5725 | + free_irq(xspi->irq, xspi); | ||
5726 | + iounmap(xspi->regs); | ||
5727 | + | ||
5728 | + if (r) | ||
5729 | + release_mem_region(r->start, resource_size(r)); | ||
5730 | + | ||
5731 | + platform_set_drvdata(dev, 0); | ||
5732 | + spi_master_put(xspi->bitbang.master); | ||
5733 | + | ||
5734 | + return 0; | ||
5735 | +} | ||
5736 | + | ||
5737 | +/* work with hotplug and coldplug */ | ||
5738 | +MODULE_ALIAS("platform:" XILINX_SPI_NAME); | ||
5739 | + | ||
5740 | +static struct platform_driver xilinx_spi_driver = { | ||
5741 | + .probe = xilinx_spi_probe, | ||
5742 | + .remove = __devexit_p(xilinx_spi_remove), | ||
5743 | + .driver = { | ||
5744 | + .name = XILINX_SPI_NAME, | ||
5745 | + .owner = THIS_MODULE, | ||
5746 | + }, | ||
5747 | +}; | ||
5748 | + | ||
5749 | +static int __init xilinx_spi_init(void) | ||
5750 | +{ | ||
5751 | + return platform_driver_register(&xilinx_spi_driver); | ||
5752 | +} | ||
5753 | +module_init(xilinx_spi_init); | ||
5754 | + | ||
5755 | +static void __exit xilinx_spi_exit(void) | ||
5756 | +{ | ||
5757 | + platform_driver_unregister(&xilinx_spi_driver); | ||
5758 | +} | ||
5759 | +module_exit(xilinx_spi_exit); | ||
5760 | + | ||
5761 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
5762 | +MODULE_DESCRIPTION("Xilinx SPI platform driver"); | ||
5763 | +MODULE_LICENSE("GPL v2"); | ||
5764 | + | ||
5765 | diff -uNr linux-2.6.29-clean/include/linux/i2c-ocores.h linux-2.6.29/include/linux/i2c-ocores.h | ||
5766 | --- linux-2.6.29-clean/include/linux/i2c-ocores.h 2009-04-01 09:20:20.000000000 -0700 | ||
5767 | +++ linux-2.6.29/include/linux/i2c-ocores.h 2009-04-06 13:51:47.000000000 -0700 | ||
5768 | @@ -14,6 +14,8 @@ | ||
5769 | struct ocores_i2c_platform_data { | ||
5770 | u32 regstep; /* distance between registers */ | ||
5771 | u32 clock_khz; /* input clock in kHz */ | ||
5772 | + u8 num_devices; /* number of devices in the devices list */ | ||
5773 | + struct i2c_board_info const *devices; /* devices connected to the bus */ | ||
5774 | }; | ||
5775 | |||
5776 | #endif /* _LINUX_I2C_OCORES_H */ | ||
5777 | diff -uNr linux-2.6.29-clean/include/linux/mfd/timbdma.h linux-2.6.29/include/linux/mfd/timbdma.h | ||
5778 | --- linux-2.6.29-clean/include/linux/mfd/timbdma.h 1969-12-31 16:00:00.000000000 -0800 | ||
5779 | +++ linux-2.6.29/include/linux/mfd/timbdma.h 2009-04-06 13:51:47.000000000 -0700 | ||
5780 | @@ -0,0 +1,80 @@ | ||
5781 | +/* | ||
5782 | + * timbdma.h timberdale FPGA DMA driver defines | ||
5783 | + * Copyright (c) 2009 Intel Corporation | ||
5784 | + * | ||
5785 | + * This program is free software; you can redistribute it and/or modify | ||
5786 | + * it under the terms of the GNU General Public License version 2 as | ||
5787 | + * published by the Free Software Foundation. | ||
5788 | + * | ||
5789 | + * This program is distributed in the hope that it will be useful, | ||
5790 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5791 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5792 | + * GNU General Public License for more details. | ||
5793 | + * | ||
5794 | + * You should have received a copy of the GNU General Public License | ||
5795 | + * along with this program; if not, write to the Free Software | ||
5796 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5797 | + */ | ||
5798 | + | ||
5799 | +/* Supports: | ||
5800 | + * Timberdale FPGA DMA engine | ||
5801 | + */ | ||
5802 | + | ||
5803 | +#ifndef _TIMBDMA_H | ||
5804 | +#define _TIMBDMA_H | ||
5805 | + | ||
5806 | +#include <linux/spinlock.h> | ||
5807 | + | ||
5808 | + | ||
5809 | +#define DMA_IRQ_UART_RX 0x01 | ||
5810 | +#define DMA_IRQ_UART_TX 0x02 | ||
5811 | +#define DMA_IRQ_MLB_RX 0x04 | ||
5812 | +#define DMA_IRQ_MLB_TX 0x08 | ||
5813 | +#define DMA_IRQ_VIDEO_RX 0x10 | ||
5814 | +#define DMA_IRQ_VIDEO_DROP 0x20 | ||
5815 | +#define DMA_IRQS 6 | ||
5816 | + | ||
5817 | + | ||
5818 | +typedef int (*timbdma_interruptcb)(u32 flag, void *data); | ||
5819 | + | ||
5820 | +enum timbdma_ctrlmap { | ||
5821 | + timbdma_ctrlmap_DMACFGBTUART = 0x000000, | ||
5822 | + timbdma_ctrlmap_DMACFGMLBSY = 0x000040, | ||
5823 | + timbdma_ctrlmap_DMACFGVIDEO = 0x000080, | ||
5824 | + timbdma_ctrlmap_TIMBSTATUS = 0x080000, | ||
5825 | + timbdma_ctrlmap_TIMBPEND = 0x080004, | ||
5826 | + timbdma_ctrlmap_TIMBENABLE = 0x080008, | ||
5827 | + timbdma_ctrlmap_VIDEOBUFFER = 0x200000 | ||
5828 | +}; | ||
5829 | + | ||
5830 | +enum timbdma_dmacfg { | ||
5831 | + timbdma_dmacfg_RXSTARTH = 0x00, | ||
5832 | + timbdma_dmacfg_RXSTARTL = 0x04, | ||
5833 | + timbdma_dmacfg_RXLENGTH = 0x08, | ||
5834 | + timbdma_dmacfg_RXFPGAWP = 0x0C, | ||
5835 | + timbdma_dmacfg_RXSWRP = 0x10, | ||
5836 | + timbdma_dmacfg_RXENABLE = 0x14, | ||
5837 | + timbdma_dmacfg_TXSTARTH = 0x18, | ||
5838 | + timbdma_dmacfg_TXSTARTL = 0x1C, | ||
5839 | + timbdma_dmacfg_TXLENGTH = 0x20, | ||
5840 | + timbdma_dmacfg_TXSWWP = 0x24, | ||
5841 | + timbdma_dmacfg_TXFPGARP = 0x28, | ||
5842 | + timbdma_dmacfg_TXBEFINT = 0x2C, | ||
5843 | + timbdma_dmacfg_BPERROW = 0x30 | ||
5844 | +}; | ||
5845 | + | ||
5846 | +struct timbdma_dev { | ||
5847 | + void __iomem *membase; | ||
5848 | + timbdma_interruptcb callbacks[DMA_IRQS]; | ||
5849 | + void *callback_data[DMA_IRQS]; | ||
5850 | + spinlock_t lock; /* mutual exclusion */ | ||
5851 | +}; | ||
5852 | + | ||
5853 | +void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row); | ||
5854 | + | ||
5855 | +void *timb_stop_dma(u32 flags); | ||
5856 | + | ||
5857 | +void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data); | ||
5858 | + | ||
5859 | +#endif /* _TIMBDMA_H */ | ||
5860 | + | ||
5861 | diff -uNr linux-2.6.29-clean/include/linux/mfd/timbi2s.h linux-2.6.29/include/linux/mfd/timbi2s.h | ||
5862 | --- linux-2.6.29-clean/include/linux/mfd/timbi2s.h 1969-12-31 16:00:00.000000000 -0800 | ||
5863 | +++ linux-2.6.29/include/linux/mfd/timbi2s.h 2009-04-06 13:51:47.000000000 -0700 | ||
5864 | @@ -0,0 +1,66 @@ | ||
5865 | +/* | ||
5866 | + * timbi2s.h timberdale FPGA I2S driver | ||
5867 | + * Copyright (c) 2009 Intel Corporation | ||
5868 | + * | ||
5869 | + * This program is free software; you can redistribute it and/or modify | ||
5870 | + * it under the terms of the GNU General Public License version 2 as | ||
5871 | + * published by the Free Software Foundation. | ||
5872 | + * | ||
5873 | + * This program is distributed in the hope that it will be useful, | ||
5874 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5875 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5876 | + * GNU General Public License for more details. | ||
5877 | + * | ||
5878 | + * You should have received a copy of the GNU General Public License | ||
5879 | + * along with this program; if not, write to the Free Software | ||
5880 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5881 | + */ | ||
5882 | + | ||
5883 | +/* Supports: | ||
5884 | + * Timberdale FPGA I2S | ||
5885 | + */ | ||
5886 | + | ||
5887 | +struct timbi2s_bus_control { | ||
5888 | + struct list_head list; | ||
5889 | +}; | ||
5890 | + | ||
5891 | +struct timbi2s_bus { | ||
5892 | + void __iomem *membase; | ||
5893 | + u32 irq; | ||
5894 | + struct timbi2s_bus_control *control; | ||
5895 | + struct workqueue_struct *workqueue; | ||
5896 | + struct work_struct work; | ||
5897 | +}; | ||
5898 | + | ||
5899 | +struct timbi2s_dev { | ||
5900 | + void __iomem *membase; | ||
5901 | + u32 irq; | ||
5902 | + struct timbi2s_bus *bus; | ||
5903 | + struct workqueue_struct *workqueue; | ||
5904 | + struct work_struct work; | ||
5905 | + u32 ioctrl; | ||
5906 | + u32 devid; | ||
5907 | + u8 timbi2s_rx; | ||
5908 | + u8 timbi2s_tx; | ||
5909 | + struct circ_buf *buffer; | ||
5910 | + /* Register access */ | ||
5911 | + spinlock_t lock; | ||
5912 | + | ||
5913 | + int in_use; | ||
5914 | + u8 pscale_offset; /* Prescale */ | ||
5915 | + u8 icr_offset; /* Clear register */ | ||
5916 | + u8 isr_offset; /* Status */ | ||
5917 | + u8 ipr_offset; /* Pending register */ | ||
5918 | + u8 ier_offset; /* Interrupt Enable register */ | ||
5919 | + u8 ctrl_offset; | ||
5920 | + u8 fifo; | ||
5921 | + | ||
5922 | + struct list_head item; | ||
5923 | +}; | ||
5924 | + | ||
5925 | +static struct timbi2s_dev *timbi2s_get_tx(void); | ||
5926 | +static struct timbi2s_dev *timbi2s_get_rx(void); | ||
5927 | +static void timbi2s_put(struct timbi2s_dev *tdev); | ||
5928 | + | ||
5929 | +static int timbi2s_ioctrl(struct timbi2s_dev *i2sdev); | ||
5930 | + | ||
5931 | diff -uNr linux-2.6.29-clean/include/linux/serial_core.h linux-2.6.29/include/linux/serial_core.h | ||
5932 | --- linux-2.6.29-clean/include/linux/serial_core.h 2009-04-01 09:20:20.000000000 -0700 | ||
5933 | +++ linux-2.6.29/include/linux/serial_core.h 2009-04-06 13:51:47.000000000 -0700 | ||
5934 | @@ -164,6 +164,9 @@ | ||
5935 | /* NWPSERIAL */ | ||
5936 | #define PORT_NWPSERIAL 85 | ||
5937 | |||
5938 | +/* Timberdale UART */ | ||
5939 | +#define PORT_TIMBUART 86 | ||
5940 | + | ||
5941 | #ifdef __KERNEL__ | ||
5942 | |||
5943 | #include <linux/compiler.h> | ||
5944 | diff -uNr linux-2.6.29-clean/include/linux/spi/xilinx_spi.h linux-2.6.29/include/linux/spi/xilinx_spi.h | ||
5945 | --- linux-2.6.29-clean/include/linux/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 | ||
5946 | +++ linux-2.6.29/include/linux/spi/xilinx_spi.h 2009-04-06 13:51:47.000000000 -0700 | ||
5947 | @@ -0,0 +1,17 @@ | ||
5948 | +#ifndef __LINUX_SPI_XILINX_SPI_H | ||
5949 | +#define __LINUX_SPI_XILINX_SPI_H | ||
5950 | + | ||
5951 | +/* SPI Controller IP */ | ||
5952 | +struct xspi_platform_data { | ||
5953 | + s16 bus_num; | ||
5954 | + u16 num_chipselect; | ||
5955 | + u32 speed_hz; | ||
5956 | + u8 cr_offset; | ||
5957 | + u8 sr_offset; | ||
5958 | + u8 txd_offset; | ||
5959 | + u8 rxd_offset; | ||
5960 | + u8 ssr_offset; | ||
5961 | +}; | ||
5962 | + | ||
5963 | +#endif /* __LINUX_SPI_XILINX_SPI_H */ | ||
5964 | + | ||
5965 | diff -uNr linux-2.6.29-clean/include/media/adv7180.h linux-2.6.29/include/media/adv7180.h | ||
5966 | --- linux-2.6.29-clean/include/media/adv7180.h 1969-12-31 16:00:00.000000000 -0800 | ||
5967 | +++ linux-2.6.29/include/media/adv7180.h 2009-04-06 13:51:47.000000000 -0700 | ||
5968 | @@ -0,0 +1,127 @@ | ||
5969 | +/* | ||
5970 | + * adv7180.h Analog Devices ADV7180 video decoder driver defines | ||
5971 | + * Copyright (c) 2009 Intel Corporation | ||
5972 | + * | ||
5973 | + * This program is free software; you can redistribute it and/or modify | ||
5974 | + * it under the terms of the GNU General Public License version 2 as | ||
5975 | + * published by the Free Software Foundation. | ||
5976 | + * | ||
5977 | + * This program is distributed in the hope that it will be useful, | ||
5978 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5979 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5980 | + * GNU General Public License for more details. | ||
5981 | + * | ||
5982 | + * You should have received a copy of the GNU General Public License | ||
5983 | + * along with this program; if not, write to the Free Software | ||
5984 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5985 | + */ | ||
5986 | + | ||
5987 | +#define DRIVER_NAME "adv7180" | ||
5988 | + | ||
5989 | +#define I2C_ADV7180 0x42 | ||
5990 | +#define ADV7180_NR_REG 0xfc | ||
5991 | + | ||
5992 | +#define ADV7180_IN_CTRL 0x00 /* Input CR */ | ||
5993 | +#define ADV7180_OUT_CTRL 0x03 /* Output CR */ | ||
5994 | +#define ADV7180_EXT_OUT_CTRL 0x04 /* Extended Output CR */ | ||
5995 | + | ||
5996 | +#define ADV7180_ADI_CTRL 0x0e /* ADI CR */ | ||
5997 | +# define ADI_ENABLE 0x20 /* Enable access to sub-regs */ | ||
5998 | + | ||
5999 | +#define ADV7180_SR_1 0x10 /* Status Register 1 */ | ||
6000 | +#define ADV7180_SR_2 0x12 | ||
6001 | +#define ADV7180_SR_3 0x13 | ||
6002 | + | ||
6003 | +/* Interrupt and VDP sub-registers */ | ||
6004 | +#define ADV7180_ISR_1 0x42 /* Interrupt Status Register 1 */ | ||
6005 | +#define ADV7180_ICR_1 0x43 /* Interrupt Clear Register 1 */ | ||
6006 | + | ||
6007 | +#define ADV7180_ISR_2 0x46 | ||
6008 | +#define ADV7180_ICR_2 0x47 | ||
6009 | + | ||
6010 | +#define ADV7180_ISR_3 0x4a | ||
6011 | +#define ADV7180_ICR_3 0x4b | ||
6012 | + | ||
6013 | +#define ADV7180_ISR_4 0x4e | ||
6014 | +#define ADV7180_ICR_4 0x4f | ||
6015 | +/* */ | ||
6016 | + | ||
6017 | +#define ADV7180_SR 0x10 | ||
6018 | +#define ADV7180_STATUS_NTSM 0x00 /* NTSM M/J */ | ||
6019 | +#define ADV7180_STATUS_NTSC 0x10 /* NTSC 4.43 */ | ||
6020 | +#define ADV7180_STATUS_PAL_M 0x20 /* PAL M */ | ||
6021 | +#define ADV7180_STATUS_PAL_60 0x30 /* PAL 60 */ | ||
6022 | +#define ADV7180_STATUS_PAL 0x40 /* PAL B/G/H/I/D */ | ||
6023 | +#define ADV7180_STATUS_SECAM 0x50 /* SECAM */ | ||
6024 | +#define ADV7180_STATUS_PAL_N 0x60 /* PAL Combination N */ | ||
6025 | +#define ADV7180_STATUS_SECAM_525 0x70 /* SECAM 525 */ | ||
6026 | + | ||
6027 | +enum input_mode { | ||
6028 | + CVBS, /* Composite */ | ||
6029 | + SVIDEO, /* S-video */ | ||
6030 | + YPbPr, /* Component */ | ||
6031 | +}; | ||
6032 | + | ||
6033 | +struct adv7180 { | ||
6034 | + unsigned char reg[ADV7180_NR_REG]; | ||
6035 | + int norm; | ||
6036 | + enum input_mode input; | ||
6037 | + int enable; | ||
6038 | + struct i2c_client *client; | ||
6039 | +}; | ||
6040 | + | ||
6041 | +static const unsigned char reset_icr[] = { | ||
6042 | + ADV7180_ICR_1, 0x00, | ||
6043 | + ADV7180_ICR_2, 0x00, | ||
6044 | + ADV7180_ICR_3, 0x00, | ||
6045 | + ADV7180_ICR_4, 0x00, | ||
6046 | +}; | ||
6047 | + | ||
6048 | +/* ADV7180 LQFP-64. ADV7180.pdf, page 104 */ | ||
6049 | +static const unsigned char init_cvbs_64[] = { | ||
6050 | + 0x00, 0x01, /* INSEL = CVBS in on Ain2 */ | ||
6051 | + 0x04, 0x57, /* Enable SFL */ | ||
6052 | + 0x17, 0x41, /* Select SH1 */ | ||
6053 | + | ||
6054 | + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to | ||
6055 | + * suit ADV video encoders | ||
6056 | + */ | ||
6057 | + 0x3d, 0xa2, /* MWE enable manual window, | ||
6058 | + * color kill threshold to 2 | ||
6059 | + */ | ||
6060 | + 0x3e, 0x6a, /* BLM optimization */ | ||
6061 | + 0x3f, 0xa0, /* BGB optimization */ | ||
6062 | + 0x0e, 0x80, /* Hidden space */ | ||
6063 | + 0x55, 0x81, /* ADC configuration */ | ||
6064 | + 0x0e, 0x00, /* User space */ | ||
6065 | +}; | ||
6066 | + | ||
6067 | +static const unsigned char init_svideo_64[] = { | ||
6068 | + 0x00, 0x08, /* Insel = Y/C, Y = AIN3, C = AIN6 */ | ||
6069 | + 0x04, 0x57, /* Enable SFL */ | ||
6070 | + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to | ||
6071 | + * suit ADV video encoders | ||
6072 | + */ | ||
6073 | + 0x3d, 0xa2, /* MWE enable manual window, | ||
6074 | + * color kill threshold to 2 | ||
6075 | + */ | ||
6076 | + 0x3e, 0x6a, /* BLM optimization */ | ||
6077 | + 0x3f, 0xa0, /* BGB optimization */ | ||
6078 | + 0x58, 0x04, /* Mandatory write. This must be | ||
6079 | + * performed for correct operation. | ||
6080 | + */ | ||
6081 | + 0x0e, 0x80, /* Hidden space */ | ||
6082 | + 0x55, 0x81, /* ADC configuration */ | ||
6083 | + 0x0e, 0x00, /* User space */ | ||
6084 | +}; | ||
6085 | + | ||
6086 | +static const unsigned char init_ypbpr_64[] = { | ||
6087 | + 0x00, 0x09, /* INSEL = YPrPb, Y = AIN1, Pr = AIN4, Pb = AIN5 */ | ||
6088 | + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to suit ADV video encoders */ | ||
6089 | + 0x3d, 0xa2, /* MWE enable manual window */ | ||
6090 | + 0x3e, 0x6a, /* BLM optimization */ | ||
6091 | + 0x3f, 0xa0, /* ADI recommended */ | ||
6092 | + 0x0e, 0x80, /* Hidden space */ | ||
6093 | + 0x55, 0x81, /* ADC configuration */ | ||
6094 | + 0x0e, 0x00, /* User space */ | ||
6095 | +}; | ||