diff options
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-1-2-timberdale.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-1-2-timberdale.patch | 12910 |
1 files changed, 0 insertions, 12910 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-1-2-timberdale.patch b/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-1-2-timberdale.patch deleted file mode 100644 index 9db5b4ac72..0000000000 --- a/meta-moblin/packages/linux/linux-moblin-2.6.31.5/linux-2.6.31-1-2-timberdale.patch +++ /dev/null | |||
@@ -1,12910 +0,0 @@ | |||
1 | diff -uNr linux-2.6.31/drivers/gpio/Kconfig linux-2.6.31.new/drivers/gpio/Kconfig | ||
2 | --- linux-2.6.31/drivers/gpio/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
3 | +++ linux-2.6.31.new/drivers/gpio/Kconfig 2009-10-23 11:17:19.000000000 -0700 | ||
4 | @@ -173,6 +173,12 @@ | ||
5 | |||
6 | If unsure, say N. | ||
7 | |||
8 | +config GPIO_TIMBERDALE | ||
9 | + bool "Support for timberdale GPIO IP" | ||
10 | + depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM | ||
11 | + ---help--- | ||
12 | + Add support for the GPIO IP in the timberdale FPGA. | ||
13 | + | ||
14 | comment "SPI GPIO expanders:" | ||
15 | |||
16 | config GPIO_MAX7301 | ||
17 | @@ -188,4 +194,11 @@ | ||
18 | SPI driver for Microchip MCP23S08 I/O expander. This provides | ||
19 | a GPIO interface supporting inputs and outputs. | ||
20 | |||
21 | +config GPIO_MC33880 | ||
22 | + tristate "Freescale MC33880 high-side/low-side switch" | ||
23 | + depends on SPI_MASTER | ||
24 | + help | ||
25 | + SPI driver for Freescale MC33880 high-side/low-side switch. | ||
26 | + This provides GPIO interface supporting inputs and outputs. | ||
27 | + | ||
28 | endif | ||
29 | diff -uNr linux-2.6.31/drivers/gpio/Makefile linux-2.6.31.new/drivers/gpio/Makefile | ||
30 | --- linux-2.6.31/drivers/gpio/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
31 | +++ linux-2.6.31.new/drivers/gpio/Makefile 2009-10-23 11:17:19.000000000 -0700 | ||
32 | @@ -14,3 +14,6 @@ | ||
33 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | ||
34 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o | ||
35 | obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o | ||
36 | +obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o | ||
37 | +obj-$(CONFIG_GPIO_MC33880) += mc33880.o | ||
38 | + | ||
39 | diff -uNr linux-2.6.31/drivers/gpio/mc33880.c linux-2.6.31.new/drivers/gpio/mc33880.c | ||
40 | --- linux-2.6.31/drivers/gpio/mc33880.c 1969-12-31 16:00:00.000000000 -0800 | ||
41 | +++ linux-2.6.31.new/drivers/gpio/mc33880.c 2009-10-23 11:17:19.000000000 -0700 | ||
42 | @@ -0,0 +1,196 @@ | ||
43 | +/* | ||
44 | + * mc33880.c MC33880 high-side/low-side switch GPIO driver | ||
45 | + * Copyright (c) 2009 Intel Corporation | ||
46 | + * | ||
47 | + * This program is free software; you can redistribute it and/or modify | ||
48 | + * it under the terms of the GNU General Public License version 2 as | ||
49 | + * published by the Free Software Foundation. | ||
50 | + * | ||
51 | + * This program is distributed in the hope that it will be useful, | ||
52 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
53 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
54 | + * GNU General Public License for more details. | ||
55 | + * | ||
56 | + * You should have received a copy of the GNU General Public License | ||
57 | + * along with this program; if not, write to the Free Software | ||
58 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
59 | + */ | ||
60 | + | ||
61 | +/* Supports: | ||
62 | + * Freescale MC33880 high-side/low-side switch | ||
63 | + */ | ||
64 | + | ||
65 | +#include <linux/init.h> | ||
66 | +#include <linux/mutex.h> | ||
67 | +#include <linux/spi/spi.h> | ||
68 | +#include <linux/spi/mc33880.h> | ||
69 | +#include <linux/gpio.h> | ||
70 | + | ||
71 | +#define DRIVER_NAME "mc33880" | ||
72 | + | ||
73 | +/* | ||
74 | + * Pin configurations, see MAX7301 datasheet page 6 | ||
75 | + */ | ||
76 | +#define PIN_CONFIG_MASK 0x03 | ||
77 | +#define PIN_CONFIG_IN_PULLUP 0x03 | ||
78 | +#define PIN_CONFIG_IN_WO_PULLUP 0x02 | ||
79 | +#define PIN_CONFIG_OUT 0x01 | ||
80 | + | ||
81 | +#define PIN_NUMBER 8 | ||
82 | + | ||
83 | + | ||
84 | +/* | ||
85 | + * Some registers must be read back to modify. | ||
86 | + * To save time we cache them here in memory | ||
87 | + */ | ||
88 | +struct mc33880 { | ||
89 | + struct mutex lock; /* protect from simultanous accesses */ | ||
90 | + u8 port_config; | ||
91 | + struct gpio_chip chip; | ||
92 | + struct spi_device *spi; | ||
93 | +}; | ||
94 | + | ||
95 | +static int mc33880_write_config(struct mc33880 *mc) | ||
96 | +{ | ||
97 | + return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); | ||
98 | +} | ||
99 | + | ||
100 | + | ||
101 | +static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) | ||
102 | +{ | ||
103 | + if (value) | ||
104 | + mc->port_config |= 1 << offset; | ||
105 | + else | ||
106 | + mc->port_config &= ~(1 << offset); | ||
107 | + | ||
108 | + return mc33880_write_config(mc); | ||
109 | +} | ||
110 | + | ||
111 | + | ||
112 | +static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) | ||
113 | +{ | ||
114 | + struct mc33880 *mc = container_of(chip, struct mc33880, chip); | ||
115 | + | ||
116 | + mutex_lock(&mc->lock); | ||
117 | + | ||
118 | + __mc33880_set(mc, offset, value); | ||
119 | + | ||
120 | + mutex_unlock(&mc->lock); | ||
121 | +} | ||
122 | + | ||
123 | +static int __devinit mc33880_probe(struct spi_device *spi) | ||
124 | +{ | ||
125 | + struct mc33880 *mc; | ||
126 | + struct mc33880_platform_data *pdata; | ||
127 | + int ret; | ||
128 | + | ||
129 | + pdata = spi->dev.platform_data; | ||
130 | + if (!pdata || !pdata->base) { | ||
131 | + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); | ||
132 | + return -EINVAL; | ||
133 | + } | ||
134 | + | ||
135 | + /* | ||
136 | + * bits_per_word cannot be configured in platform data | ||
137 | + */ | ||
138 | + spi->bits_per_word = 8; | ||
139 | + | ||
140 | + ret = spi_setup(spi); | ||
141 | + if (ret < 0) | ||
142 | + return ret; | ||
143 | + | ||
144 | + mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); | ||
145 | + if (!mc) | ||
146 | + return -ENOMEM; | ||
147 | + | ||
148 | + mutex_init(&mc->lock); | ||
149 | + | ||
150 | + dev_set_drvdata(&spi->dev, mc); | ||
151 | + | ||
152 | + mc->spi = spi; | ||
153 | + | ||
154 | + mc->chip.label = DRIVER_NAME, | ||
155 | + mc->chip.set = mc33880_set; | ||
156 | + mc->chip.base = pdata->base; | ||
157 | + mc->chip.ngpio = PIN_NUMBER; | ||
158 | + mc->chip.can_sleep = 1; | ||
159 | + mc->chip.dev = &spi->dev; | ||
160 | + mc->chip.owner = THIS_MODULE; | ||
161 | + | ||
162 | + mc->port_config = 0x00; | ||
163 | + /* write twice, because during initialisation the first setting | ||
164 | + * is just for testing SPI communication, and the second is the | ||
165 | + * "real" configuration | ||
166 | + */ | ||
167 | + ret = mc33880_write_config(mc); | ||
168 | + mc->port_config = 0x00; | ||
169 | + if (!ret) | ||
170 | + ret = mc33880_write_config(mc); | ||
171 | + | ||
172 | + if (ret) { | ||
173 | + printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); | ||
174 | + goto exit_destroy; | ||
175 | + } | ||
176 | + | ||
177 | + ret = gpiochip_add(&mc->chip); | ||
178 | + if (ret) | ||
179 | + goto exit_destroy; | ||
180 | + | ||
181 | + return ret; | ||
182 | + | ||
183 | +exit_destroy: | ||
184 | + dev_set_drvdata(&spi->dev, NULL); | ||
185 | + mutex_destroy(&mc->lock); | ||
186 | + kfree(mc); | ||
187 | + return ret; | ||
188 | +} | ||
189 | + | ||
190 | +static int mc33880_remove(struct spi_device *spi) | ||
191 | +{ | ||
192 | + struct mc33880 *mc; | ||
193 | + int ret; | ||
194 | + | ||
195 | + mc = dev_get_drvdata(&spi->dev); | ||
196 | + if (mc == NULL) | ||
197 | + return -ENODEV; | ||
198 | + | ||
199 | + dev_set_drvdata(&spi->dev, NULL); | ||
200 | + | ||
201 | + ret = gpiochip_remove(&mc->chip); | ||
202 | + if (!ret) { | ||
203 | + mutex_destroy(&mc->lock); | ||
204 | + kfree(mc); | ||
205 | + } else | ||
206 | + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", | ||
207 | + ret); | ||
208 | + | ||
209 | + return ret; | ||
210 | +} | ||
211 | + | ||
212 | +static struct spi_driver mc33880_driver = { | ||
213 | + .driver = { | ||
214 | + .name = DRIVER_NAME, | ||
215 | + .owner = THIS_MODULE, | ||
216 | + }, | ||
217 | + .probe = mc33880_probe, | ||
218 | + .remove = __devexit_p(mc33880_remove), | ||
219 | +}; | ||
220 | + | ||
221 | +static int __init mc33880_init(void) | ||
222 | +{ | ||
223 | + return spi_register_driver(&mc33880_driver); | ||
224 | +} | ||
225 | +/* register after spi postcore initcall and before | ||
226 | + * subsys initcalls that may rely on these GPIOs | ||
227 | + */ | ||
228 | +subsys_initcall(mc33880_init); | ||
229 | + | ||
230 | +static void __exit mc33880_exit(void) | ||
231 | +{ | ||
232 | + spi_unregister_driver(&mc33880_driver); | ||
233 | +} | ||
234 | +module_exit(mc33880_exit); | ||
235 | + | ||
236 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
237 | +MODULE_LICENSE("GPL v2"); | ||
238 | + | ||
239 | diff -uNr linux-2.6.31/drivers/gpio/timbgpio.c linux-2.6.31.new/drivers/gpio/timbgpio.c | ||
240 | --- linux-2.6.31/drivers/gpio/timbgpio.c 1969-12-31 16:00:00.000000000 -0800 | ||
241 | +++ linux-2.6.31.new/drivers/gpio/timbgpio.c 2009-10-23 11:17:19.000000000 -0700 | ||
242 | @@ -0,0 +1,342 @@ | ||
243 | +/* | ||
244 | + * timbgpio.c timberdale FPGA GPIO driver | ||
245 | + * Copyright (c) 2009 Intel Corporation | ||
246 | + * | ||
247 | + * This program is free software; you can redistribute it and/or modify | ||
248 | + * it under the terms of the GNU General Public License version 2 as | ||
249 | + * published by the Free Software Foundation. | ||
250 | + * | ||
251 | + * This program is distributed in the hope that it will be useful, | ||
252 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
253 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
254 | + * GNU General Public License for more details. | ||
255 | + * | ||
256 | + * You should have received a copy of the GNU General Public License | ||
257 | + * along with this program; if not, write to the Free Software | ||
258 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
259 | + */ | ||
260 | + | ||
261 | +/* Supports: | ||
262 | + * Timberdale FPGA GPIO | ||
263 | + */ | ||
264 | + | ||
265 | +#include <linux/module.h> | ||
266 | +#include <linux/gpio.h> | ||
267 | +#include <linux/platform_device.h> | ||
268 | +#include <linux/io.h> | ||
269 | +#include <linux/timb_gpio.h> | ||
270 | +#include <linux/interrupt.h> | ||
271 | + | ||
272 | +#define DRIVER_NAME "timb-gpio" | ||
273 | + | ||
274 | +#define TGPIOVAL 0x00 | ||
275 | +#define TGPIODIR 0x04 | ||
276 | +#define TGPIO_IER 0x08 | ||
277 | +#define TGPIO_ISR 0x0c | ||
278 | +#define TGPIO_IPR 0x10 | ||
279 | +#define TGPIO_ICR 0x14 | ||
280 | +#define TGPIO_FLR 0x18 | ||
281 | +#define TGPIO_LVR 0x1c | ||
282 | + | ||
283 | +struct timbgpio { | ||
284 | + void __iomem *membase; | ||
285 | + spinlock_t lock; /* mutual exclusion */ | ||
286 | + struct gpio_chip gpio; | ||
287 | + int irq_base; | ||
288 | +}; | ||
289 | + | ||
290 | +static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, | ||
291 | + unsigned offset, bool enabled) | ||
292 | +{ | ||
293 | + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
294 | + u32 reg; | ||
295 | + | ||
296 | + spin_lock(&tgpio->lock); | ||
297 | + reg = ioread32(tgpio->membase + offset); | ||
298 | + | ||
299 | + if (enabled) | ||
300 | + reg |= (1 << index); | ||
301 | + else | ||
302 | + reg &= ~(1 << index); | ||
303 | + | ||
304 | + iowrite32(reg, tgpio->membase + offset); | ||
305 | + spin_unlock(&tgpio->lock); | ||
306 | + | ||
307 | + return 0; | ||
308 | +} | ||
309 | + | ||
310 | +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | ||
311 | +{ | ||
312 | + return timbgpio_update_bit(gpio, nr, TGPIODIR, true); | ||
313 | +} | ||
314 | + | ||
315 | +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | ||
316 | +{ | ||
317 | + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
318 | + u32 value; | ||
319 | + | ||
320 | + value = ioread32(tgpio->membase + TGPIOVAL); | ||
321 | + return (value & (1 << nr)) ? 1 : 0; | ||
322 | +} | ||
323 | + | ||
324 | +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, | ||
325 | + unsigned nr, int val) | ||
326 | +{ | ||
327 | + return timbgpio_update_bit(gpio, nr, TGPIODIR, false); | ||
328 | +} | ||
329 | + | ||
330 | +static void timbgpio_gpio_set(struct gpio_chip *gpio, | ||
331 | + unsigned nr, int val) | ||
332 | +{ | ||
333 | + timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); | ||
334 | +} | ||
335 | + | ||
336 | +static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) | ||
337 | +{ | ||
338 | + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
339 | + | ||
340 | + if (tgpio->irq_base <= 0) | ||
341 | + return -EINVAL; | ||
342 | + | ||
343 | + return tgpio->irq_base + offset; | ||
344 | +} | ||
345 | + | ||
346 | +/* | ||
347 | + * GPIO IRQ | ||
348 | + */ | ||
349 | +static void timbgpio_irq_disable(unsigned irq) | ||
350 | +{ | ||
351 | + struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
352 | + int offset = irq - tgpio->irq_base; | ||
353 | + | ||
354 | + timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); | ||
355 | +} | ||
356 | + | ||
357 | +static void timbgpio_irq_enable(unsigned irq) | ||
358 | +{ | ||
359 | + struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
360 | + int offset = irq - tgpio->irq_base; | ||
361 | + | ||
362 | + timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); | ||
363 | +} | ||
364 | + | ||
365 | +static int timbgpio_irq_type(unsigned irq, unsigned trigger) | ||
366 | +{ | ||
367 | + struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
368 | + int offset = irq - tgpio->irq_base; | ||
369 | + unsigned long flags; | ||
370 | + u32 lvr, flr; | ||
371 | + | ||
372 | + if (offset < 0 || offset > tgpio->gpio.ngpio) | ||
373 | + return -EINVAL; | ||
374 | + | ||
375 | + spin_lock_irqsave(&tgpio->lock, flags); | ||
376 | + | ||
377 | + lvr = ioread32(tgpio->membase + TGPIO_LVR); | ||
378 | + flr = ioread32(tgpio->membase + TGPIO_FLR); | ||
379 | + | ||
380 | + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { | ||
381 | + flr &= ~(1 << offset); | ||
382 | + if (trigger & IRQ_TYPE_LEVEL_HIGH) | ||
383 | + lvr |= 1 << offset; | ||
384 | + else | ||
385 | + lvr &= ~(1 << offset); | ||
386 | + } | ||
387 | + | ||
388 | + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) | ||
389 | + return -EINVAL; | ||
390 | + else { | ||
391 | + flr |= 1 << offset; | ||
392 | + /* opposite compared to the datasheet, but it mirrors the | ||
393 | + * reality | ||
394 | + */ | ||
395 | + if (trigger & IRQ_TYPE_EDGE_FALLING) | ||
396 | + lvr |= 1 << offset; | ||
397 | + else | ||
398 | + lvr &= ~(1 << offset); | ||
399 | + } | ||
400 | + | ||
401 | + iowrite32(lvr, tgpio->membase + TGPIO_LVR); | ||
402 | + iowrite32(flr, tgpio->membase + TGPIO_FLR); | ||
403 | + iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); | ||
404 | + spin_unlock_irqrestore(&tgpio->lock, flags); | ||
405 | + | ||
406 | + return 0; | ||
407 | +} | ||
408 | + | ||
409 | +static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) | ||
410 | +{ | ||
411 | + struct timbgpio *tgpio = get_irq_data(irq); | ||
412 | + unsigned long ipr; | ||
413 | + int offset; | ||
414 | + | ||
415 | + desc->chip->ack(irq); | ||
416 | + ipr = ioread32(tgpio->membase + TGPIO_IPR); | ||
417 | + iowrite32(ipr, tgpio->membase + TGPIO_ICR); | ||
418 | + | ||
419 | + for_each_bit(offset, &ipr, tgpio->gpio.ngpio) | ||
420 | + generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); | ||
421 | +} | ||
422 | + | ||
423 | +static struct irq_chip timbgpio_irqchip = { | ||
424 | + .name = "GPIO", | ||
425 | + .enable = timbgpio_irq_enable, | ||
426 | + .disable = timbgpio_irq_disable, | ||
427 | + .set_type = timbgpio_irq_type, | ||
428 | +}; | ||
429 | + | ||
430 | +static int __devinit timbgpio_probe(struct platform_device *pdev) | ||
431 | +{ | ||
432 | + int err, i; | ||
433 | + struct gpio_chip *gc; | ||
434 | + struct timbgpio *tgpio; | ||
435 | + struct resource *iomem; | ||
436 | + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; | ||
437 | + int irq = platform_get_irq(pdev, 0); | ||
438 | + | ||
439 | + if (!pdata || pdata->nr_pins > 32) { | ||
440 | + err = -EINVAL; | ||
441 | + goto err_mem; | ||
442 | + } | ||
443 | + | ||
444 | + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
445 | + if (!iomem) { | ||
446 | + err = -EINVAL; | ||
447 | + goto err_mem; | ||
448 | + } | ||
449 | + | ||
450 | + tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); | ||
451 | + if (!tgpio) { | ||
452 | + err = -EINVAL; | ||
453 | + goto err_mem; | ||
454 | + } | ||
455 | + tgpio->irq_base = pdata->irq_base; | ||
456 | + | ||
457 | + spin_lock_init(&tgpio->lock); | ||
458 | + | ||
459 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
460 | + DRIVER_NAME)) { | ||
461 | + err = -EBUSY; | ||
462 | + goto err_request; | ||
463 | + } | ||
464 | + | ||
465 | + tgpio->membase = ioremap(iomem->start, resource_size(iomem)); | ||
466 | + if (!tgpio->membase) { | ||
467 | + err = -ENOMEM; | ||
468 | + goto err_ioremap; | ||
469 | + } | ||
470 | + | ||
471 | + gc = &tgpio->gpio; | ||
472 | + | ||
473 | + gc->label = dev_name(&pdev->dev); | ||
474 | + gc->owner = THIS_MODULE; | ||
475 | + gc->dev = &pdev->dev; | ||
476 | + gc->direction_input = timbgpio_gpio_direction_input; | ||
477 | + gc->get = timbgpio_gpio_get; | ||
478 | + gc->direction_output = timbgpio_gpio_direction_output; | ||
479 | + gc->set = timbgpio_gpio_set; | ||
480 | + gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; | ||
481 | + gc->dbg_show = NULL; | ||
482 | + gc->base = pdata->gpio_base; | ||
483 | + gc->ngpio = pdata->nr_pins; | ||
484 | + gc->can_sleep = 0; | ||
485 | + | ||
486 | + err = gpiochip_add(gc); | ||
487 | + if (err) | ||
488 | + goto err_chipadd; | ||
489 | + | ||
490 | + platform_set_drvdata(pdev, tgpio); | ||
491 | + | ||
492 | + /* make sure to disable interrupts */ | ||
493 | + iowrite32(0x0, tgpio->membase + TGPIO_IER); | ||
494 | + | ||
495 | + if (irq < 0 || tgpio->irq_base <= 0) | ||
496 | + return 0; | ||
497 | + | ||
498 | + for (i = 0; i < pdata->nr_pins; i++) { | ||
499 | + set_irq_chip_and_handler_name(tgpio->irq_base + i, | ||
500 | + &timbgpio_irqchip, handle_simple_irq, "mux"); | ||
501 | + set_irq_chip_data(tgpio->irq_base + i, tgpio); | ||
502 | +#ifdef CONFIG_ARM | ||
503 | + set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); | ||
504 | +#endif | ||
505 | + } | ||
506 | + | ||
507 | + set_irq_data(irq, tgpio); | ||
508 | + set_irq_chained_handler(irq, timbgpio_irq); | ||
509 | + | ||
510 | + return 0; | ||
511 | + | ||
512 | +err_chipadd: | ||
513 | + iounmap(tgpio->membase); | ||
514 | +err_ioremap: | ||
515 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
516 | +err_request: | ||
517 | + kfree(tgpio); | ||
518 | +err_mem: | ||
519 | + printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err); | ||
520 | + | ||
521 | + return err; | ||
522 | +} | ||
523 | + | ||
524 | +static int __devexit timbgpio_remove(struct platform_device *pdev) | ||
525 | +{ | ||
526 | + int err; | ||
527 | + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; | ||
528 | + struct timbgpio *tgpio = platform_get_drvdata(pdev); | ||
529 | + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
530 | + int irq = platform_get_irq(pdev, 0); | ||
531 | + | ||
532 | + if (irq >= 0 && tgpio->irq_base > 0) { | ||
533 | + int i; | ||
534 | + for (i = 0; i < pdata->nr_pins; i++) { | ||
535 | + set_irq_chip(tgpio->irq_base + i, NULL); | ||
536 | + set_irq_chip_data(tgpio->irq_base + i, NULL); | ||
537 | + } | ||
538 | + | ||
539 | + set_irq_handler(irq, NULL); | ||
540 | + set_irq_data(irq, NULL); | ||
541 | + } | ||
542 | + | ||
543 | + err = gpiochip_remove(&tgpio->gpio); | ||
544 | + if (err) | ||
545 | + printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n"); | ||
546 | + | ||
547 | + iounmap(tgpio->membase); | ||
548 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
549 | + kfree(tgpio); | ||
550 | + | ||
551 | + platform_set_drvdata(pdev, NULL); | ||
552 | + | ||
553 | + return 0; | ||
554 | +} | ||
555 | + | ||
556 | +static struct platform_driver timbgpio_platform_driver = { | ||
557 | + .driver = { | ||
558 | + .name = DRIVER_NAME, | ||
559 | + .owner = THIS_MODULE, | ||
560 | + }, | ||
561 | + .probe = timbgpio_probe, | ||
562 | + .remove = timbgpio_remove, | ||
563 | +}; | ||
564 | + | ||
565 | +/*--------------------------------------------------------------------------*/ | ||
566 | + | ||
567 | +static int __init timbgpio_init(void) | ||
568 | +{ | ||
569 | + return platform_driver_register(&timbgpio_platform_driver); | ||
570 | +} | ||
571 | + | ||
572 | +static void __exit timbgpio_exit(void) | ||
573 | +{ | ||
574 | + platform_driver_unregister(&timbgpio_platform_driver); | ||
575 | +} | ||
576 | + | ||
577 | +module_init(timbgpio_init); | ||
578 | +module_exit(timbgpio_exit); | ||
579 | + | ||
580 | +MODULE_DESCRIPTION("Timberdale GPIO driver"); | ||
581 | +MODULE_LICENSE("GPL v2"); | ||
582 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
583 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
584 | + | ||
585 | diff -uNr linux-2.6.31/drivers/i2c/busses/i2c-xiic.c linux-2.6.31.new/drivers/i2c/busses/i2c-xiic.c | ||
586 | --- linux-2.6.31/drivers/i2c/busses/i2c-xiic.c 1969-12-31 16:00:00.000000000 -0800 | ||
587 | +++ linux-2.6.31.new/drivers/i2c/busses/i2c-xiic.c 2009-10-23 11:17:29.000000000 -0700 | ||
588 | @@ -0,0 +1,1132 @@ | ||
589 | +/* | ||
590 | + * i2c-xiic.c | ||
591 | + * Copyright (c) 2009 Intel Corporation | ||
592 | + * | ||
593 | + * This program is free software; you can redistribute it and/or modify | ||
594 | + * it under the terms of the GNU General Public License version 2 as | ||
595 | + * published by the Free Software Foundation. | ||
596 | + * | ||
597 | + * This program is distributed in the hope that it will be useful, | ||
598 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
599 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
600 | + * GNU General Public License for more details. | ||
601 | + * | ||
602 | + * You should have received a copy of the GNU General Public License | ||
603 | + * along with this program; if not, write to the Free Software | ||
604 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
605 | + */ | ||
606 | + | ||
607 | +/* Supports: | ||
608 | + * Xilinx IIC | ||
609 | + */ | ||
610 | +#include <linux/kernel.h> | ||
611 | +#include <linux/module.h> | ||
612 | +#include <linux/init.h> | ||
613 | +#include <linux/errno.h> | ||
614 | +#include <linux/platform_device.h> | ||
615 | +#include <linux/i2c.h> | ||
616 | +#include <linux/interrupt.h> | ||
617 | +#include <linux/wait.h> | ||
618 | +#include <linux/i2c-xiic.h> | ||
619 | +#include <linux/io.h> | ||
620 | + | ||
621 | +#define DRIVER_NAME "xiic-i2c" | ||
622 | + | ||
623 | +struct xiic_i2c { | ||
624 | + void __iomem *base; | ||
625 | + wait_queue_head_t wait; | ||
626 | + struct i2c_adapter adap; | ||
627 | + struct i2c_msg *tx_msg; | ||
628 | + spinlock_t lock; /* mutual exclusion */ | ||
629 | + unsigned int tx_pos; | ||
630 | + unsigned int nmsgs; | ||
631 | + int state; /* see STATE_ */ | ||
632 | + | ||
633 | + struct i2c_msg *rx_msg; /* current RX message */ | ||
634 | + int rx_pos; | ||
635 | +}; | ||
636 | + | ||
637 | +static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value); | ||
638 | + | ||
639 | +static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg); | ||
640 | + | ||
641 | +static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value); | ||
642 | + | ||
643 | +static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value); | ||
644 | + | ||
645 | +static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg); | ||
646 | + | ||
647 | +static void xiic_start_xfer(struct xiic_i2c *i2c); | ||
648 | +static void __xiic_start_xfer(struct xiic_i2c *i2c); | ||
649 | + | ||
650 | +/************************** Constant Definitions ****************************/ | ||
651 | + | ||
652 | +#define STATE_DONE 0x00 | ||
653 | +#define STATE_ERROR 0x01 | ||
654 | +#define STATE_START 0x02 | ||
655 | + | ||
656 | +#define XIIC_MSB_OFFSET 0 | ||
657 | +#define XIIC_REG_OFFSET (0x100+XIIC_MSB_OFFSET) | ||
658 | + | ||
659 | +/* | ||
660 | + * Register offsets in bytes from RegisterBase. Three is added to the | ||
661 | + * base offset to access LSB (IBM style) of the word | ||
662 | + */ | ||
663 | +#define XIIC_CR_REG_OFFSET (0x00+XIIC_REG_OFFSET) /* Control Register */ | ||
664 | +#define XIIC_SR_REG_OFFSET (0x04+XIIC_REG_OFFSET) /* Status Register */ | ||
665 | +#define XIIC_DTR_REG_OFFSET (0x08+XIIC_REG_OFFSET) /* Data Tx Register */ | ||
666 | +#define XIIC_DRR_REG_OFFSET (0x0C+XIIC_REG_OFFSET) /* Data Rx Register */ | ||
667 | +#define XIIC_ADR_REG_OFFSET (0x10+XIIC_REG_OFFSET) /* Address Register */ | ||
668 | +#define XIIC_TFO_REG_OFFSET (0x14+XIIC_REG_OFFSET) /* Tx FIFO Occupancy */ | ||
669 | +#define XIIC_RFO_REG_OFFSET (0x18+XIIC_REG_OFFSET) /* Rx FIFO Occupancy */ | ||
670 | +#define XIIC_TBA_REG_OFFSET (0x1C+XIIC_REG_OFFSET) /* 10 Bit Address reg */ | ||
671 | +#define XIIC_RFD_REG_OFFSET (0x20+XIIC_REG_OFFSET) /* Rx FIFO Depth reg */ | ||
672 | +#define XIIC_GPO_REG_OFFSET (0x24+XIIC_REG_OFFSET) /* Output Register */ | ||
673 | + | ||
674 | +/* Control Register masks */ | ||
675 | +#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */ | ||
676 | +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ | ||
677 | +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ | ||
678 | +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ | ||
679 | +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ | ||
680 | +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ | ||
681 | +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ | ||
682 | + | ||
683 | +/* Status Register masks */ | ||
684 | +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ | ||
685 | +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ | ||
686 | +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ | ||
687 | +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ | ||
688 | +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ | ||
689 | +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ | ||
690 | +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ | ||
691 | +#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */ | ||
692 | + | ||
693 | +/* Interrupt Status Register masks Interrupt occurs when... */ | ||
694 | +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ | ||
695 | +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ | ||
696 | +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ | ||
697 | +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ | ||
698 | +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ | ||
699 | +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ | ||
700 | +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ | ||
701 | +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ | ||
702 | + | ||
703 | +/* The following constants specify the depth of the FIFOs */ | ||
704 | +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ | ||
705 | +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ | ||
706 | + | ||
707 | +/* The following constants specify groups of interrupts that are typically | ||
708 | + * enabled or disables at the same time | ||
709 | + */ | ||
710 | +#define XIIC_TX_INTERRUPTS \ | ||
711 | +(XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK) | ||
712 | + | ||
713 | +#define XIIC_TX_RX_INTERRUPTS (XIIC_INTR_RX_FULL_MASK | XIIC_TX_INTERRUPTS) | ||
714 | + | ||
715 | +/* The following constants are used with the following macros to specify the | ||
716 | + * operation, a read or write operation. | ||
717 | + */ | ||
718 | +#define XIIC_READ_OPERATION 1 | ||
719 | +#define XIIC_WRITE_OPERATION 0 | ||
720 | + | ||
721 | +/* | ||
722 | + * Tx Fifo upper bit masks. | ||
723 | + */ | ||
724 | +#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */ | ||
725 | +#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */ | ||
726 | + | ||
727 | +/* | ||
728 | + * The following constants define the register offsets for the Interrupt | ||
729 | + * registers. There are some holes in the memory map for reserved addresses | ||
730 | + * to allow other registers to be added and still match the memory map of the | ||
731 | + * interrupt controller registers | ||
732 | + */ | ||
733 | +#define XIIC_DGIER_OFFSET 0x1C /* Device Global Interrupt Enable Register */ | ||
734 | +#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */ | ||
735 | +#define XIIC_IIER_OFFSET 0x28 /* Interrupt Enable Register */ | ||
736 | +#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */ | ||
737 | + | ||
738 | +#define XIIC_RESET_MASK 0xAUL | ||
739 | + | ||
740 | +/* | ||
741 | + * The following constant is used for the device global interrupt enable | ||
742 | + * register, to enable all interrupts for the device, this is the only bit | ||
743 | + * in the register | ||
744 | + */ | ||
745 | +#define XIIC_GINTR_ENABLE_MASK 0x80000000UL | ||
746 | + | ||
747 | +/***************** Macros (Inline Functions) Definitions *********************/ | ||
748 | + | ||
749 | + | ||
750 | +/****************************************************************************** | ||
751 | +* | ||
752 | +* This macro disables all interrupts for the device by writing to the Global | ||
753 | +* interrupt enable register. This register provides the ability to disable | ||
754 | +* interrupts without any modifications to the interrupt enable register such | ||
755 | +* that it is minimal effort to restore the interrupts to the previous enabled | ||
756 | +* state. The corresponding function, XIIC_GINTR_ENABLE, is provided to | ||
757 | +* restore the interrupts to the previous enabled state. This function is | ||
758 | +* designed to be used in critical sections of device drivers such that it is | ||
759 | +* not necessary to disable other device interrupts. | ||
760 | +* | ||
761 | +* @param Instance local i2c instance | ||
762 | +* | ||
763 | +* @return None. | ||
764 | +* | ||
765 | +* @note C-Style signature: | ||
766 | +* void XIIC_GINTR_DISABLE(i2c); | ||
767 | +* | ||
768 | +******************************************************************************/ | ||
769 | +#define XIIC_GINTR_DISABLE(Instance) \ | ||
770 | + xiic_setreg32(Instance, XIIC_DGIER_OFFSET, 0) | ||
771 | + | ||
772 | +/****************************************************************************** | ||
773 | +* | ||
774 | +* This macro writes to the global interrupt enable register to enable | ||
775 | +* interrupts from the device. This register provides the ability to enable | ||
776 | +* interrupts without any modifications to the interrupt enable register such | ||
777 | +* that it is minimal effort to restore the interrupts to the previous enabled | ||
778 | +* state. This function does not enable individual interrupts as the interrupt | ||
779 | +* enable register must be set appropriately. This function is designed to be | ||
780 | +* used in critical sections of device drivers such that it is not necessary to | ||
781 | +* disable other device interrupts. | ||
782 | +* | ||
783 | +* @param Instance local I2C instance | ||
784 | +* | ||
785 | +* @return None. | ||
786 | +* | ||
787 | +* @note C-Style signature: | ||
788 | +* void XIIC_GINTR_ENABLE(i2c); | ||
789 | +* | ||
790 | +******************************************************************************/ | ||
791 | +#define XIIC_GINTR_ENABLE(Instance) \ | ||
792 | + xiic_setreg32(Instance, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK) | ||
793 | + | ||
794 | +/****************************************************************************** | ||
795 | +* | ||
796 | +* | ||
797 | +* This function sets the Interrupt status register to the specified value. | ||
798 | +* This register indicates the status of interrupt sources for the device. | ||
799 | +* The status is independent of whether interrupts are enabled such that | ||
800 | +* the status register may also be polled when interrupts are not enabled. | ||
801 | +* | ||
802 | +* Each bit of the register correlates to a specific interrupt source within the | ||
803 | +* IIC device. All bits of this register are latched. Setting a bit which is 0 | ||
804 | +* within this register causes an interrupt to be generated. The device global | ||
805 | +* interrupt enable register and the device interrupt enable register must be set | ||
806 | +* appropriately to allow an interrupt to be passed out of the device. The | ||
807 | +* interrupt is cleared by writing to this register with the bits to be | ||
808 | +* cleared set to a one and all others to zero. This register implements a | ||
809 | +* toggle on write functionality meaning any bits which are set in the value | ||
810 | +* written cause the bits in the register to change to the opposite state. | ||
811 | +* | ||
812 | +* This function writes only the specified value to the register such that | ||
813 | +* some status bits may be set and others cleared. It is the caller's | ||
814 | +* responsibility to get the value of the register prior to setting the value | ||
815 | +* to prevent an destructive behavior. | ||
816 | +* | ||
817 | +* @param Instance local I2C instance | ||
818 | +* @param Status contains the value to be written to the Interrupt | ||
819 | +* status register. | ||
820 | +* | ||
821 | +* @return None. | ||
822 | +* | ||
823 | +* @note C-Style signature: | ||
824 | +* void XIIC_WRITE_IISR(i2c, u32 Status); | ||
825 | +* | ||
826 | +******************************************************************************/ | ||
827 | +#define XIIC_WRITE_IISR(Instance, Status) \ | ||
828 | + xiic_setreg32(Instance, XIIC_IISR_OFFSET, (Status)) | ||
829 | + | ||
830 | +/****************************************************************************** | ||
831 | +* | ||
832 | +* | ||
833 | +* This function gets the contents of the Interrupt Status Register. | ||
834 | +* This register indicates the status of interrupt sources for the device. | ||
835 | +* The status is independent of whether interrupts are enabled such | ||
836 | +* that the status register may also be polled when interrupts are not enabled. | ||
837 | +* | ||
838 | +* Each bit of the register correlates to a specific interrupt source within the | ||
839 | +* device. All bits of this register are latched. Writing a 1 to a bit within | ||
840 | +* this register causes an interrupt to be generated if enabled in the interrupt | ||
841 | +* enable register and the global interrupt enable is set. Since the status is | ||
842 | +* latched, each status bit must be acknowledged in order for the bit in the | ||
843 | +* status register to be updated. Each bit can be acknowledged by writing a | ||
844 | +* 0 to the bit in the status register. | ||
845 | + | ||
846 | +* @param Instance local I2C instance | ||
847 | +* | ||
848 | +* @return A status which contains the value read from the Interrupt | ||
849 | +* Status Register. | ||
850 | +* | ||
851 | +* @note C-Style signature: | ||
852 | +* u32 XIIC_READ_IISR(i2c); | ||
853 | +* | ||
854 | +******************************************************************************/ | ||
855 | +#define XIIC_READ_IISR(Instance) \ | ||
856 | + xiic_getreg32(Instance, XIIC_IISR_OFFSET) | ||
857 | + | ||
858 | +/****************************************************************************** | ||
859 | +* | ||
860 | +* This function sets the contents of the Interrupt Enable Register . This | ||
861 | +* register controls which interrupt sources of the IIC device are allowed to | ||
862 | +* generate an interrupt. The global interrupt enable register and the device | ||
863 | +* interrupt enable register must also be set appropriately for an interrupt | ||
864 | +* to be passed out of the device. | ||
865 | +* | ||
866 | +* Each bit of the register correlates to a specific interrupt source within the | ||
867 | +* device. Setting a bit in this register enables the interrupt source to gen | ||
868 | +* an interrupt. Clearing a bit in this register disables interrupt generation | ||
869 | +* for that interrupt source. | ||
870 | +* | ||
871 | +* This function writes only the specified value to the register such that | ||
872 | +* some interrupt sources may be enabled and others disabled. It is the | ||
873 | +* caller's responsibility to get the value of the interrupt enable register | ||
874 | +* prior to setting the value to prevent a destructive behavior. | ||
875 | +* | ||
876 | +* @param Instance local I2C instance | ||
877 | +* @param Enable contains the value to be written to the Interrupt Enable | ||
878 | +* Register. | ||
879 | +* | ||
880 | +* @return None | ||
881 | +* | ||
882 | +* @note C-Style signature: | ||
883 | +* void XIIC_WRITE_IIER(i2c, u32 Enable); | ||
884 | +* | ||
885 | +******************************************************************************/ | ||
886 | +#define XIIC_WRITE_IIER(Instance, Enable) \ | ||
887 | + xiic_setreg32(Instance, XIIC_IIER_OFFSET, (Enable)) | ||
888 | + | ||
889 | +/****************************************************************************** | ||
890 | +* | ||
891 | +* | ||
892 | +* This function gets the Interrupt enable register contents. This register | ||
893 | +* controls which interrupt sources of the device are allowed to generate an | ||
894 | +* interrupt. The global interrupt enable register and the device interrupt | ||
895 | +* enable register must also be set appropriately for an interrupt to be | ||
896 | +* passed out of the IIC device. | ||
897 | +* | ||
898 | +* Each bit of the register correlates to a specific interrupt source within the | ||
899 | +* IIC device. Setting a bit in this register enables the interrupt source to | ||
900 | +* generate an interrupt. Clearing a bit in this register disables interrupt | ||
901 | +* generation for that interrupt source. | ||
902 | +* | ||
903 | +* @param Instance local I2C instance | ||
904 | +* | ||
905 | +* @return The contents read from the Interrupt Enable Register. | ||
906 | +* | ||
907 | +* @note C-Style signature: | ||
908 | +* u32 XIIC_READ_IIER(i2c) | ||
909 | +* | ||
910 | +******************************************************************************/ | ||
911 | +#define XIIC_READ_IIER(Instance) \ | ||
912 | + xiic_getreg32(Instance, XIIC_IIER_OFFSET) | ||
913 | + | ||
914 | +/************************** Function Prototypes ******************************/ | ||
915 | + | ||
916 | +/****************************************************************************** | ||
917 | +* | ||
918 | +* This macro disables the specified interrupts in the Interrupt enable | ||
919 | +* register. It is non-destructive in that the register is read and only the | ||
920 | +* interrupts specified is changed. | ||
921 | +* | ||
922 | +* @param BaseAddress is the base address of the IIC device. | ||
923 | +* @param InterruptMask contains the interrupts to be disabled | ||
924 | +* | ||
925 | +* @return None. | ||
926 | +* | ||
927 | +* @note Signature: | ||
928 | +* void XIic_mDisableIntr(u32 BaseAddress, u32 InterruptMask); | ||
929 | +* | ||
930 | +******************************************************************************/ | ||
931 | +#define XIic_mDisableIntr(Instance, InterruptMask) \ | ||
932 | + XIIC_WRITE_IIER((Instance), XIIC_READ_IIER(Instance) & ~(InterruptMask)) | ||
933 | + | ||
934 | +/****************************************************************************** | ||
935 | +* | ||
936 | +* This macro enables the specified interrupts in the Interrupt enable | ||
937 | +* register. It is non-destructive in that the register is read and only the | ||
938 | +* interrupts specified is changed. | ||
939 | +* | ||
940 | +* @param BaseAddress is the base address of the IIC device. | ||
941 | +* @param InterruptMask contains the interrupts to be disabled | ||
942 | +* | ||
943 | +* @return None. | ||
944 | +* | ||
945 | +* @note Signature: | ||
946 | +* void XIic_mEnableIntr(u32 BaseAddress, u32 InterruptMask); | ||
947 | +* | ||
948 | +******************************************************************************/ | ||
949 | +#define XIic_mEnableIntr(Instance, InterruptMask) \ | ||
950 | + XIIC_WRITE_IIER((Instance), XIIC_READ_IIER(Instance) | (InterruptMask)) | ||
951 | + | ||
952 | +/****************************************************************************** | ||
953 | +* | ||
954 | +* This macro clears the specified interrupt in the Interrupt status | ||
955 | +* register. It is non-destructive in that the register is read and only the | ||
956 | +* interrupt specified is cleared. Clearing an interrupt acknowledges it. | ||
957 | +* | ||
958 | +* @param BaseAddress is the base address of the IIC device. | ||
959 | +* @param InterruptMask contains the interrupts to be disabled | ||
960 | +* | ||
961 | +* @return None. | ||
962 | +* | ||
963 | +* @note Signature: | ||
964 | +* void XIic_mClearIntr(u32 BaseAddress, u32 InterruptMask); | ||
965 | +* | ||
966 | +******************************************************************************/ | ||
967 | +#define XIic_mClearIntr(Instance, InterruptMask) \ | ||
968 | + XIIC_WRITE_IISR((Instance), XIIC_READ_IISR(Instance) & (InterruptMask)) | ||
969 | + | ||
970 | +/****************************************************************************** | ||
971 | +* | ||
972 | +* This macro clears and enables the specified interrupt in the Interrupt | ||
973 | +* status and enable registers. It is non-destructive in that the registers are | ||
974 | +* read and only the interrupt specified is modified. | ||
975 | +* Clearing an interrupt acknowledges it. | ||
976 | +* | ||
977 | +* @param BaseAddress is the base address of the IIC device. | ||
978 | +* @param InterruptMask contains the interrupts to be cleared and enabled | ||
979 | +* | ||
980 | +* @return None. | ||
981 | +* | ||
982 | +* @note Signature: | ||
983 | +* void XIic_mClearEnableIntr(u32 BaseAddress, u32 InterruptMask); | ||
984 | +* | ||
985 | +******************************************************************************/ | ||
986 | +#define XIic_mClearEnableIntr(Instance, InterruptMask) { \ | ||
987 | + XIIC_WRITE_IISR(Instance, \ | ||
988 | + (XIIC_READ_IISR(Instance) & (InterruptMask))); \ | ||
989 | + XIIC_WRITE_IIER(Instance, \ | ||
990 | + (XIIC_READ_IIER(Instance) | (InterruptMask))); \ | ||
991 | +} | ||
992 | + | ||
993 | + | ||
994 | +#define xiic_tx_space(i2c) ((i2c)->tx_msg->len - (i2c)->tx_pos) | ||
995 | +#define xiic_rx_space(i2c) ((i2c)->rx_msg->len - (i2c)->rx_pos) | ||
996 | + | ||
997 | +static void xiic_clear_rx_fifo(struct xiic_i2c *i2c) | ||
998 | +{ | ||
999 | + u8 sr; | ||
1000 | + for (sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); | ||
1001 | + !(sr & XIIC_SR_RX_FIFO_EMPTY_MASK); | ||
1002 | + sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)) | ||
1003 | + xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET); | ||
1004 | +} | ||
1005 | + | ||
1006 | +/****************************************************************************** | ||
1007 | + * | ||
1008 | + * Initialize the IIC core for Dynamic Functionality. | ||
1009 | + * | ||
1010 | + * @param i2c local I2C instance | ||
1011 | + * | ||
1012 | + * @return None. | ||
1013 | + * | ||
1014 | + * @note None. | ||
1015 | + * | ||
1016 | + ******************************************************************************/ | ||
1017 | +static void xiic_reinit(struct xiic_i2c *i2c) | ||
1018 | +{ | ||
1019 | + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); | ||
1020 | + | ||
1021 | + /* Set receive Fifo depth to maximum (zero based). */ | ||
1022 | + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); | ||
1023 | + | ||
1024 | + /* Reset Tx Fifo. */ | ||
1025 | + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); | ||
1026 | + | ||
1027 | + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ | ||
1028 | + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); | ||
1029 | + | ||
1030 | + /* make sure RX fifo is empty */ | ||
1031 | + xiic_clear_rx_fifo(i2c); | ||
1032 | + | ||
1033 | + /* Enable interrupts */ | ||
1034 | + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK); | ||
1035 | + | ||
1036 | + XIic_mClearEnableIntr(i2c, XIIC_INTR_AAS_MASK | | ||
1037 | + XIIC_INTR_ARB_LOST_MASK); | ||
1038 | +} | ||
1039 | + | ||
1040 | +/****************************************************************************** | ||
1041 | + * | ||
1042 | + * De-Initialize the IIC core. | ||
1043 | + * | ||
1044 | + * @param i2c local I2C instance | ||
1045 | + * | ||
1046 | + * @return None. | ||
1047 | + * | ||
1048 | + * @note None. | ||
1049 | + * | ||
1050 | + ******************************************************************************/ | ||
1051 | +static void xiic_deinit(struct xiic_i2c *i2c) | ||
1052 | +{ | ||
1053 | + u8 cr; | ||
1054 | + | ||
1055 | + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); | ||
1056 | + | ||
1057 | + /* Disable IIC Device. */ | ||
1058 | + cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET); | ||
1059 | + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr & ~XIIC_CR_ENABLE_DEVICE_MASK); | ||
1060 | +} | ||
1061 | + | ||
1062 | + | ||
1063 | + | ||
1064 | +/***************************************************************************** | ||
1065 | + * | ||
1066 | + * | ||
1067 | + * This function is called when the receive register is full. The number | ||
1068 | + * of bytes received to cause the interrupt is adjustable using the Receive FIFO | ||
1069 | + * Depth register. The number of bytes in the register is read in the Receive | ||
1070 | + * FIFO occupancy register. Both these registers are zero based values (0-15) | ||
1071 | + * such that a value of zero indicates 1 byte. | ||
1072 | + * | ||
1073 | + * For a Master Receiver to properly signal the end of a message, the data must | ||
1074 | + * be read in up to the message length - 1, where control register bits will be | ||
1075 | + * set for bus controls to occur on reading of the last byte. | ||
1076 | + * | ||
1077 | + * @param InstancePtr is a pointer to the XIic instance to be worked on. | ||
1078 | + * | ||
1079 | + * @return None. | ||
1080 | + * | ||
1081 | + * @note None. | ||
1082 | + * | ||
1083 | + ******************************************************************************/ | ||
1084 | +static void xiic_read_rx(struct xiic_i2c *i2c) | ||
1085 | +{ | ||
1086 | + u8 bytes_in_fifo; | ||
1087 | + int i; | ||
1088 | + | ||
1089 | + bytes_in_fifo = xiic_getreg8(i2c, XIIC_RFO_REG_OFFSET) + 1; | ||
1090 | + | ||
1091 | + dev_dbg(i2c->adap.dev.parent, "%s entry, bytes in fifo: %d, msg: %d" | ||
1092 | + ", SR: 0x%x, CR: 0x%x\n", | ||
1093 | + __func__, bytes_in_fifo, xiic_rx_space(i2c), | ||
1094 | + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET), | ||
1095 | + xiic_getreg8(i2c, XIIC_CR_REG_OFFSET)); | ||
1096 | + | ||
1097 | + if (bytes_in_fifo > xiic_rx_space(i2c)) | ||
1098 | + bytes_in_fifo = xiic_rx_space(i2c); | ||
1099 | + | ||
1100 | + for (i = 0; i < bytes_in_fifo; i++) | ||
1101 | + i2c->rx_msg->buf[i2c->rx_pos++] = | ||
1102 | + xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET); | ||
1103 | + | ||
1104 | + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, | ||
1105 | + (xiic_rx_space(i2c) > IIC_RX_FIFO_DEPTH) ? | ||
1106 | + IIC_RX_FIFO_DEPTH - 1 : xiic_rx_space(i2c) - 1); | ||
1107 | +} | ||
1108 | + | ||
1109 | +/****************************************************************************** | ||
1110 | + * | ||
1111 | + * This function fills the FIFO using the occupancy register to determine the | ||
1112 | + * available space to be filled. When the repeated start option is on, the last | ||
1113 | + * byte is withheld to allow the control register to be properly set on the last | ||
1114 | + * byte. | ||
1115 | + * | ||
1116 | + * @param InstancePtr is a pointer to the XIic instance to be worked on. | ||
1117 | + * | ||
1118 | + * @param Role indicates the role of this IIC device, a slave or a master, on | ||
1119 | + * the IIC bus (XIIC_SLAVE_ROLE or XIIC_MASTER_ROLE) | ||
1120 | + * | ||
1121 | + * @return | ||
1122 | + * | ||
1123 | + * None. | ||
1124 | + * | ||
1125 | + * @note | ||
1126 | + * | ||
1127 | + * None. | ||
1128 | + * | ||
1129 | + ******************************************************************************/ | ||
1130 | +static int xiic_tx_fifo_space(struct xiic_i2c *i2c) | ||
1131 | +{ | ||
1132 | + return IIC_TX_FIFO_DEPTH - xiic_getreg8(i2c, XIIC_TFO_REG_OFFSET) - 1; | ||
1133 | +} | ||
1134 | + | ||
1135 | +static void xiic_fill_tx_fifo(struct xiic_i2c *i2c) | ||
1136 | +{ | ||
1137 | + u8 fifo_space = xiic_tx_fifo_space(i2c); | ||
1138 | + int len = xiic_tx_space(i2c); | ||
1139 | + | ||
1140 | + len = (len > fifo_space) ? fifo_space : len; | ||
1141 | + | ||
1142 | + dev_dbg(i2c->adap.dev.parent, "%s entry, len: %d, fifo space: %d\n", | ||
1143 | + __func__, len, fifo_space); | ||
1144 | + | ||
1145 | + while (len--) { | ||
1146 | + u16 data = i2c->tx_msg->buf[i2c->tx_pos++]; | ||
1147 | + if ((xiic_tx_space(i2c) == 0) && (i2c->nmsgs == 1)) { | ||
1148 | + /* last message in transfer -> STOP */ | ||
1149 | + data |= XIIC_TX_DYN_STOP_MASK; | ||
1150 | + dev_dbg(i2c->adap.dev.parent, "%s TX STOP\n", __func__); | ||
1151 | + | ||
1152 | + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data); | ||
1153 | + } else | ||
1154 | + xiic_setreg8(i2c, XIIC_DTR_REG_OFFSET, data); | ||
1155 | + } | ||
1156 | +} | ||
1157 | + | ||
1158 | +static void xiic_wakeup(struct xiic_i2c *i2c, int code) | ||
1159 | +{ | ||
1160 | + i2c->tx_msg = NULL; | ||
1161 | + i2c->rx_msg = NULL; | ||
1162 | + i2c->nmsgs = 0; | ||
1163 | + i2c->state = code; | ||
1164 | + wake_up(&i2c->wait); | ||
1165 | +} | ||
1166 | + | ||
1167 | +static void xiic_process(struct xiic_i2c *i2c) | ||
1168 | +{ | ||
1169 | + u32 pend, isr, ier; | ||
1170 | + u32 Clear = 0; | ||
1171 | + | ||
1172 | + /* Get the interrupt Status from the IPIF. There is no clearing of | ||
1173 | + * interrupts in the IPIF. Interrupts must be cleared at the source. | ||
1174 | + * To find which interrupts are pending; AND interrupts pending with | ||
1175 | + * interrupts masked. | ||
1176 | + */ | ||
1177 | + isr = XIIC_READ_IISR(i2c); | ||
1178 | + ier = XIIC_READ_IIER(i2c); | ||
1179 | + pend = isr & ier; | ||
1180 | + | ||
1181 | + dev_dbg(i2c->adap.dev.parent, "%s entry, IER: 0x%x, ISR: 0x%x, " | ||
1182 | + "pend: 0x%x, SR: 0x%x, msg: %p, nmsgs: %d\n", | ||
1183 | + __func__, ier, isr, pend, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET), | ||
1184 | + i2c->tx_msg, i2c->nmsgs); | ||
1185 | + | ||
1186 | + /* Do not processes a devices interrupts if the device has no | ||
1187 | + * interrupts pending | ||
1188 | + */ | ||
1189 | + if (!pend) | ||
1190 | + return; | ||
1191 | + | ||
1192 | + /* Service requesting interrupt */ | ||
1193 | + if ((pend & XIIC_INTR_ARB_LOST_MASK) || | ||
1194 | + ((pend & XIIC_INTR_TX_ERROR_MASK) && | ||
1195 | + !(pend & XIIC_INTR_RX_FULL_MASK))) { | ||
1196 | + /* bus arbritration lost, or... | ||
1197 | + * Transmit error _OR_ RX completed | ||
1198 | + * if this happens when RX_FULL is not set | ||
1199 | + * this is probably a TX error | ||
1200 | + */ | ||
1201 | + | ||
1202 | + dev_dbg(i2c->adap.dev.parent, | ||
1203 | + "%s error\n", __func__); | ||
1204 | + | ||
1205 | + /* dynamic mode seem to suffer from problems if we just flushes | ||
1206 | + * fifos and the next message is a TX with len 0 (only addr) | ||
1207 | + * reset the IP instead of just flush fifos | ||
1208 | + */ | ||
1209 | + xiic_reinit(i2c); | ||
1210 | + | ||
1211 | + if (i2c->tx_msg) | ||
1212 | + xiic_wakeup(i2c, STATE_ERROR); | ||
1213 | + | ||
1214 | + } else if (pend & XIIC_INTR_RX_FULL_MASK) { | ||
1215 | + /* Receive register/FIFO is full */ | ||
1216 | + | ||
1217 | + Clear = XIIC_INTR_RX_FULL_MASK; | ||
1218 | + if (!i2c->rx_msg) { | ||
1219 | + dev_dbg(i2c->adap.dev.parent, | ||
1220 | + "%s unexpexted RX IRQ\n", __func__); | ||
1221 | + xiic_clear_rx_fifo(i2c); | ||
1222 | + goto out; | ||
1223 | + } | ||
1224 | + | ||
1225 | + xiic_read_rx(i2c); | ||
1226 | + if (xiic_rx_space(i2c) == 0) { | ||
1227 | + /* this is the last part of the message */ | ||
1228 | + i2c->rx_msg = NULL; | ||
1229 | + | ||
1230 | + /* also clear TX error if there (RX complete) */ | ||
1231 | + Clear |= (isr & XIIC_INTR_TX_ERROR_MASK); | ||
1232 | + | ||
1233 | + dev_dbg(i2c->adap.dev.parent, | ||
1234 | + "%s end of message, nmsgs: %d\n", | ||
1235 | + __func__, i2c->nmsgs); | ||
1236 | + | ||
1237 | + /* send next message if this wasn't the last, | ||
1238 | + * otherwise the transfer will be finialise when | ||
1239 | + * receiving the bus not busy interrupt | ||
1240 | + */ | ||
1241 | + if (i2c->nmsgs > 1) { | ||
1242 | + i2c->nmsgs--; | ||
1243 | + i2c->tx_msg++; | ||
1244 | + dev_dbg(i2c->adap.dev.parent, | ||
1245 | + "%s will start next...\n", __func__); | ||
1246 | + | ||
1247 | + __xiic_start_xfer(i2c); | ||
1248 | + } | ||
1249 | + } | ||
1250 | + } else if (pend & XIIC_INTR_BNB_MASK) { | ||
1251 | + /* IIC bus has transitioned to not busy */ | ||
1252 | + Clear = XIIC_INTR_BNB_MASK; | ||
1253 | + | ||
1254 | + /* The bus is not busy, disable BusNotBusy interrupt */ | ||
1255 | + XIic_mDisableIntr(i2c, XIIC_INTR_BNB_MASK); | ||
1256 | + | ||
1257 | + if (!i2c->tx_msg) | ||
1258 | + goto out; | ||
1259 | + | ||
1260 | + if ((i2c->nmsgs == 1) && !i2c->rx_msg && | ||
1261 | + xiic_tx_space(i2c) == 0) | ||
1262 | + xiic_wakeup(i2c, STATE_DONE); | ||
1263 | + else | ||
1264 | + xiic_wakeup(i2c, STATE_ERROR); | ||
1265 | + | ||
1266 | + } else if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) { | ||
1267 | + /* Transmit register/FIFO is empty or ½ empty */ | ||
1268 | + | ||
1269 | + Clear = pend & | ||
1270 | + (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK); | ||
1271 | + | ||
1272 | + if (!i2c->tx_msg) { | ||
1273 | + dev_dbg(i2c->adap.dev.parent, | ||
1274 | + "%s unexpexted TX IRQ\n", __func__); | ||
1275 | + goto out; | ||
1276 | + } | ||
1277 | + | ||
1278 | + xiic_fill_tx_fifo(i2c); | ||
1279 | + | ||
1280 | + /* current message sent and there is space in the fifo */ | ||
1281 | + if (!xiic_tx_space(i2c) && xiic_tx_fifo_space(i2c) >= 2) { | ||
1282 | + dev_dbg(i2c->adap.dev.parent, | ||
1283 | + "%s end of message sent, nmsgs: %d\n", | ||
1284 | + __func__, i2c->nmsgs); | ||
1285 | + if (i2c->nmsgs > 1) { | ||
1286 | + i2c->nmsgs--; | ||
1287 | + i2c->tx_msg++; | ||
1288 | + __xiic_start_xfer(i2c); | ||
1289 | + } else { | ||
1290 | + XIic_mDisableIntr(i2c, XIIC_INTR_TX_HALF_MASK); | ||
1291 | + | ||
1292 | + dev_err(i2c->adap.dev.parent, | ||
1293 | + "%s Got TX IRQ but no more to do...\n", | ||
1294 | + __func__); | ||
1295 | + } | ||
1296 | + } else if (!xiic_tx_space(i2c) && (i2c->nmsgs == 1)) | ||
1297 | + /* current frame is sent and is last, | ||
1298 | + * make sure to disable tx half | ||
1299 | + */ | ||
1300 | + XIic_mDisableIntr(i2c, XIIC_INTR_TX_HALF_MASK); | ||
1301 | + } else { | ||
1302 | + /* got IRQ which is not acked */ | ||
1303 | + dev_err(i2c->adap.dev.parent, "%s Got unexpected IRQ\n", | ||
1304 | + __func__); | ||
1305 | + Clear = pend; | ||
1306 | + } | ||
1307 | +out: | ||
1308 | + dev_dbg(i2c->adap.dev.parent, "%s Clear: 0x%x\n", __func__, Clear); | ||
1309 | + | ||
1310 | + XIIC_WRITE_IISR(i2c, Clear); | ||
1311 | +} | ||
1312 | + | ||
1313 | +/****************************************************************************** | ||
1314 | + * | ||
1315 | + * This function checks to see if the IIC bus is busy. If so, it will enable | ||
1316 | + * the bus not busy interrupt such that the driver is notified when the bus | ||
1317 | + * is no longer busy. | ||
1318 | + * | ||
1319 | + * @param InstancePtr points to the Iic instance to be worked on. | ||
1320 | + * | ||
1321 | + * @return FALSE if the IIC bus is not busy else TRUE. | ||
1322 | + * | ||
1323 | + * @note The BusNotBusy interrupt is enabled which will update the | ||
1324 | + * EventStatus when the bus is no longer busy. | ||
1325 | + * | ||
1326 | + ******************************************************************************/ | ||
1327 | +static int xiic_bus_busy(struct xiic_i2c *i2c) | ||
1328 | +{ | ||
1329 | + u8 sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); | ||
1330 | + | ||
1331 | + return (sr & XIIC_SR_BUS_BUSY_MASK) ? -EBUSY : 0; | ||
1332 | +} | ||
1333 | + | ||
1334 | +static int xiic_busy(struct xiic_i2c *i2c) | ||
1335 | +{ | ||
1336 | + int tries = 3; | ||
1337 | + int err; | ||
1338 | + if (i2c->tx_msg) | ||
1339 | + return -EBUSY; | ||
1340 | + | ||
1341 | + /* for instance if previous transfer was terminated due to TX error | ||
1342 | + * it might be that the bus is on it's way to become available | ||
1343 | + * give it at most 3 ms to wake | ||
1344 | + */ | ||
1345 | + err = xiic_bus_busy(i2c); | ||
1346 | + while (err && tries--) { | ||
1347 | + mdelay(1); | ||
1348 | + err = xiic_bus_busy(i2c); | ||
1349 | + } | ||
1350 | + | ||
1351 | + return err; | ||
1352 | +} | ||
1353 | + | ||
1354 | +static void xiic_dump_regs(struct xiic_i2c *i2c, const char *caller) | ||
1355 | +{ | ||
1356 | + dev_dbg(i2c->adap.dev.parent, "%s msg: %p, nmsgs: %d, " | ||
1357 | + "ISR: 0x%x, CR: 0x%x, SR: 0x%x\n", | ||
1358 | + caller, i2c->tx_msg, i2c->nmsgs, XIIC_READ_IISR(i2c), | ||
1359 | + xiic_getreg8(i2c, XIIC_CR_REG_OFFSET), | ||
1360 | + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); | ||
1361 | +} | ||
1362 | + | ||
1363 | +static void xiic_start_recv(struct xiic_i2c *i2c) | ||
1364 | +{ | ||
1365 | + u8 rx_watermark; | ||
1366 | + struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg; | ||
1367 | + | ||
1368 | + xiic_dump_regs(i2c, __func__); | ||
1369 | + | ||
1370 | + /* Clear and enable Rx full interrupt. */ | ||
1371 | + XIic_mClearEnableIntr(i2c, XIIC_INTR_RX_FULL_MASK | | ||
1372 | + XIIC_INTR_TX_ERROR_MASK); | ||
1373 | + | ||
1374 | + /* we want to get all but last byte, because the TX_ERROR IRQ is used | ||
1375 | + * to inidicate error ACK on the address, and negative ack on the last | ||
1376 | + * received byte, so to not mix them receive all but last. | ||
1377 | + * In the case where there is only one byte to receive | ||
1378 | + * we can check if ERROR and RX full is set at the same time | ||
1379 | + */ | ||
1380 | + rx_watermark = msg->len; | ||
1381 | + if (rx_watermark > IIC_RX_FIFO_DEPTH) | ||
1382 | + rx_watermark = IIC_RX_FIFO_DEPTH; | ||
1383 | + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rx_watermark - 1); | ||
1384 | + | ||
1385 | + if (!(msg->flags & I2C_M_NOSTART)) | ||
1386 | + /* write the address */ | ||
1387 | + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, | ||
1388 | + (msg->addr << 1) | XIIC_READ_OPERATION | | ||
1389 | + XIIC_TX_DYN_START_MASK); | ||
1390 | + | ||
1391 | + XIic_mClearEnableIntr(i2c, | ||
1392 | + XIIC_INTR_BNB_MASK); | ||
1393 | + | ||
1394 | + xiic_dump_regs(i2c, "after address"); | ||
1395 | + | ||
1396 | + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, | ||
1397 | + msg->len | ((i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0)); | ||
1398 | + if (i2c->nmsgs == 1) { | ||
1399 | + /* very last, enable bus busy as well */ | ||
1400 | + XIic_mClearEnableIntr(i2c, XIIC_INTR_BNB_MASK); | ||
1401 | + } | ||
1402 | + | ||
1403 | + xiic_dump_regs(i2c, "xiic_start_recv exit"); | ||
1404 | + | ||
1405 | + /* the message is tx:ed */ | ||
1406 | + i2c->tx_pos = msg->len; | ||
1407 | +} | ||
1408 | + | ||
1409 | +static void xiic_start_send(struct xiic_i2c *i2c) | ||
1410 | +{ | ||
1411 | + struct i2c_msg *msg = i2c->tx_msg; | ||
1412 | + | ||
1413 | + XIic_mClearIntr(i2c, XIIC_INTR_TX_ERROR_MASK); | ||
1414 | + | ||
1415 | + dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d, " | ||
1416 | + "ISR: 0x%x, CR: 0x%x\n", | ||
1417 | + __func__, msg, msg->len, XIIC_READ_IISR(i2c), | ||
1418 | + xiic_getreg8(i2c, XIIC_CR_REG_OFFSET)); | ||
1419 | + | ||
1420 | + if (!(msg->flags & I2C_M_NOSTART)) { | ||
1421 | + /* write the address */ | ||
1422 | + u16 data = ((msg->addr << 1) & 0xfe) | XIIC_WRITE_OPERATION | | ||
1423 | + XIIC_TX_DYN_START_MASK; | ||
1424 | + if ((i2c->nmsgs == 1) && msg->len == 0) | ||
1425 | + /* no data and last message -> add STOP */ | ||
1426 | + data |= XIIC_TX_DYN_STOP_MASK; | ||
1427 | + | ||
1428 | + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data); | ||
1429 | + } | ||
1430 | + | ||
1431 | + xiic_fill_tx_fifo(i2c); | ||
1432 | + | ||
1433 | + /* Clear any pending Tx empty, Tx Error and then enable them. */ | ||
1434 | + XIic_mClearEnableIntr(i2c, | ||
1435 | + XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK | | ||
1436 | + XIIC_INTR_BNB_MASK); | ||
1437 | +} | ||
1438 | + | ||
1439 | +static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) | ||
1440 | +{ | ||
1441 | + iowrite8(value, i2c->base + reg); | ||
1442 | +} | ||
1443 | + | ||
1444 | +static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) | ||
1445 | +{ | ||
1446 | + return ioread8(i2c->base + reg); | ||
1447 | +} | ||
1448 | + | ||
1449 | +static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) | ||
1450 | +{ | ||
1451 | + iowrite16(value, i2c->base + reg); | ||
1452 | +} | ||
1453 | + | ||
1454 | +static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) | ||
1455 | +{ | ||
1456 | + iowrite32(value, i2c->base + reg); | ||
1457 | +} | ||
1458 | + | ||
1459 | +static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) | ||
1460 | +{ | ||
1461 | + return ioread32(i2c->base + reg); | ||
1462 | +} | ||
1463 | + | ||
1464 | +static irqreturn_t xiic_isr(int irq, void *dev_id) | ||
1465 | +{ | ||
1466 | + struct xiic_i2c *i2c = dev_id; | ||
1467 | + spin_lock(&i2c->lock); | ||
1468 | + XIIC_GINTR_DISABLE(i2c); | ||
1469 | + | ||
1470 | + dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__); | ||
1471 | + | ||
1472 | + xiic_process(i2c); | ||
1473 | + | ||
1474 | + XIIC_GINTR_ENABLE(i2c); | ||
1475 | + | ||
1476 | + spin_unlock(&i2c->lock); | ||
1477 | + | ||
1478 | + return IRQ_HANDLED; | ||
1479 | +} | ||
1480 | + | ||
1481 | +static void __xiic_start_xfer(struct xiic_i2c *i2c) | ||
1482 | +{ | ||
1483 | + int first = 1; | ||
1484 | + int fifo_space = xiic_tx_fifo_space(i2c); | ||
1485 | + dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, fifos space: %d\n", | ||
1486 | + __func__, i2c->tx_msg, fifo_space); | ||
1487 | + | ||
1488 | + if (!i2c->tx_msg) | ||
1489 | + return; | ||
1490 | + | ||
1491 | + i2c->rx_pos = 0; | ||
1492 | + i2c->tx_pos = 0; | ||
1493 | + i2c->state = STATE_START; | ||
1494 | + while ((fifo_space >= 2) && (first || (i2c->nmsgs > 1))) { | ||
1495 | + if (!first) { | ||
1496 | + i2c->nmsgs--; | ||
1497 | + i2c->tx_msg++; | ||
1498 | + i2c->tx_pos = 0; | ||
1499 | + } else | ||
1500 | + first = 0; | ||
1501 | + | ||
1502 | + if (i2c->tx_msg->flags & I2C_M_RD) { | ||
1503 | + /* we dont date putting several reads in the FIFO */ | ||
1504 | + xiic_start_recv(i2c); | ||
1505 | + return; | ||
1506 | + } else { | ||
1507 | + xiic_start_send(i2c); | ||
1508 | + if (xiic_tx_space(i2c) != 0) { | ||
1509 | + /* the message could not be completely sent */ | ||
1510 | + break; | ||
1511 | + } | ||
1512 | + } | ||
1513 | + | ||
1514 | + fifo_space = xiic_tx_fifo_space(i2c); | ||
1515 | + } | ||
1516 | + | ||
1517 | + /* there are more messages or the current one could not be completely | ||
1518 | + * put into the FIFO, also enable the half empty interrupt | ||
1519 | + */ | ||
1520 | + if (i2c->nmsgs > 1 || xiic_tx_space(i2c)) | ||
1521 | + XIic_mClearEnableIntr(i2c, XIIC_INTR_TX_HALF_MASK); | ||
1522 | + | ||
1523 | +} | ||
1524 | + | ||
1525 | +static void xiic_start_xfer(struct xiic_i2c *i2c) | ||
1526 | +{ | ||
1527 | + unsigned long flags; | ||
1528 | + | ||
1529 | + spin_lock_irqsave(&i2c->lock, flags); | ||
1530 | + xiic_reinit(i2c); | ||
1531 | + XIIC_GINTR_DISABLE(i2c); | ||
1532 | + spin_unlock_irqrestore(&i2c->lock, flags); | ||
1533 | + | ||
1534 | + __xiic_start_xfer(i2c); | ||
1535 | + | ||
1536 | + XIIC_GINTR_ENABLE(i2c); | ||
1537 | +} | ||
1538 | + | ||
1539 | +static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | ||
1540 | +{ | ||
1541 | + struct xiic_i2c *i2c = i2c_get_adapdata(adap); | ||
1542 | + int err; | ||
1543 | + | ||
1544 | + dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, | ||
1545 | + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); | ||
1546 | + | ||
1547 | + err = xiic_busy(i2c); | ||
1548 | + if (err) { | ||
1549 | + xiic_dump_regs(i2c, "bus busy"); | ||
1550 | + return err; | ||
1551 | + } | ||
1552 | + | ||
1553 | + i2c->tx_msg = msgs; | ||
1554 | + i2c->nmsgs = num; | ||
1555 | + | ||
1556 | + xiic_start_xfer(i2c); | ||
1557 | + | ||
1558 | + if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || | ||
1559 | + (i2c->state == STATE_DONE), HZ)) | ||
1560 | + return (i2c->state == STATE_DONE) ? num : -EIO; | ||
1561 | + else { | ||
1562 | + xiic_dump_regs(i2c, __func__); | ||
1563 | + i2c->tx_msg = NULL; | ||
1564 | + i2c->rx_msg = NULL; | ||
1565 | + i2c->nmsgs = 0; | ||
1566 | + return -ETIMEDOUT; | ||
1567 | + } | ||
1568 | +} | ||
1569 | + | ||
1570 | +static u32 xiic_func(struct i2c_adapter *adap) | ||
1571 | +{ | ||
1572 | + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
1573 | +} | ||
1574 | + | ||
1575 | +static const struct i2c_algorithm xiic_algorithm = { | ||
1576 | + .master_xfer = xiic_xfer, | ||
1577 | + .functionality = xiic_func, | ||
1578 | +}; | ||
1579 | + | ||
1580 | +static struct i2c_adapter xiic_adapter = { | ||
1581 | + .owner = THIS_MODULE, | ||
1582 | + .name = DRIVER_NAME, | ||
1583 | + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, | ||
1584 | + .algo = &xiic_algorithm, | ||
1585 | +}; | ||
1586 | + | ||
1587 | + | ||
1588 | +static int __devinit xiic_i2c_probe(struct platform_device *pdev) | ||
1589 | +{ | ||
1590 | + struct xiic_i2c *i2c; | ||
1591 | + struct xiic_i2c_platform_data *pdata; | ||
1592 | + struct resource *res; | ||
1593 | + int ret, irq; | ||
1594 | + u8 i; | ||
1595 | + | ||
1596 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1597 | + if (!res) | ||
1598 | + return -ENODEV; | ||
1599 | + | ||
1600 | + irq = platform_get_irq(pdev, 0); | ||
1601 | + if (irq < 0) | ||
1602 | + return -ENODEV; | ||
1603 | + | ||
1604 | + pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data; | ||
1605 | + if (!pdata) | ||
1606 | + return -ENODEV; | ||
1607 | + | ||
1608 | + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); | ||
1609 | + if (!i2c) | ||
1610 | + return -ENOMEM; | ||
1611 | + | ||
1612 | + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
1613 | + dev_err(&pdev->dev, "Memory region busy\n"); | ||
1614 | + ret = -EBUSY; | ||
1615 | + goto request_mem_failed; | ||
1616 | + } | ||
1617 | + | ||
1618 | + i2c->base = ioremap(res->start, resource_size(res)); | ||
1619 | + if (!i2c->base) { | ||
1620 | + dev_err(&pdev->dev, "Unable to map registers\n"); | ||
1621 | + ret = -EIO; | ||
1622 | + goto map_failed; | ||
1623 | + } | ||
1624 | + | ||
1625 | + /* hook up driver to tree */ | ||
1626 | + platform_set_drvdata(pdev, i2c); | ||
1627 | + i2c->adap = xiic_adapter; | ||
1628 | + i2c_set_adapdata(&i2c->adap, i2c); | ||
1629 | + i2c->adap.dev.parent = &pdev->dev; | ||
1630 | + | ||
1631 | + xiic_reinit(i2c); | ||
1632 | + | ||
1633 | + spin_lock_init(&i2c->lock); | ||
1634 | + init_waitqueue_head(&i2c->wait); | ||
1635 | + ret = request_irq(irq, xiic_isr, 0, pdev->name, i2c); | ||
1636 | + if (ret) { | ||
1637 | + dev_err(&pdev->dev, "Cannot claim IRQ\n"); | ||
1638 | + goto request_irq_failed; | ||
1639 | + } | ||
1640 | + | ||
1641 | + /* add i2c adapter to i2c tree */ | ||
1642 | + ret = i2c_add_adapter(&i2c->adap); | ||
1643 | + if (ret) { | ||
1644 | + dev_err(&pdev->dev, "Failed to add adapter\n"); | ||
1645 | + goto add_adapter_failed; | ||
1646 | + } | ||
1647 | + | ||
1648 | + /* add in known devices to the bus */ | ||
1649 | + for (i = 0; i < pdata->num_devices; i++) | ||
1650 | + i2c_new_device(&i2c->adap, pdata->devices + i); | ||
1651 | + | ||
1652 | + return 0; | ||
1653 | + | ||
1654 | +add_adapter_failed: | ||
1655 | + free_irq(irq, i2c); | ||
1656 | +request_irq_failed: | ||
1657 | + xiic_deinit(i2c); | ||
1658 | + iounmap(i2c->base); | ||
1659 | +map_failed: | ||
1660 | + release_mem_region(res->start, resource_size(res)); | ||
1661 | +request_mem_failed: | ||
1662 | + kfree(i2c); | ||
1663 | + | ||
1664 | + return ret; | ||
1665 | +} | ||
1666 | + | ||
1667 | +static int __devexit xiic_i2c_remove(struct platform_device* pdev) | ||
1668 | +{ | ||
1669 | + struct xiic_i2c *i2c = platform_get_drvdata(pdev); | ||
1670 | + struct resource *res; | ||
1671 | + | ||
1672 | + /* remove adapter & data */ | ||
1673 | + i2c_del_adapter(&i2c->adap); | ||
1674 | + | ||
1675 | + xiic_deinit(i2c); | ||
1676 | + | ||
1677 | + platform_set_drvdata(pdev, NULL); | ||
1678 | + | ||
1679 | + free_irq(platform_get_irq(pdev, 0), i2c); | ||
1680 | + | ||
1681 | + iounmap(i2c->base); | ||
1682 | + | ||
1683 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1684 | + if (res) | ||
1685 | + release_mem_region(res->start, resource_size(res)); | ||
1686 | + | ||
1687 | + kfree(i2c); | ||
1688 | + | ||
1689 | + return 0; | ||
1690 | +} | ||
1691 | + | ||
1692 | + | ||
1693 | +/* work with hotplug and coldplug */ | ||
1694 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
1695 | + | ||
1696 | +static struct platform_driver xiic_i2c_driver = { | ||
1697 | + .probe = xiic_i2c_probe, | ||
1698 | + .remove = __devexit_p(xiic_i2c_remove), | ||
1699 | + .driver = { | ||
1700 | + .owner = THIS_MODULE, | ||
1701 | + .name = DRIVER_NAME, | ||
1702 | + }, | ||
1703 | +}; | ||
1704 | + | ||
1705 | +static int __init xiic_i2c_init(void) | ||
1706 | +{ | ||
1707 | + return platform_driver_register(&xiic_i2c_driver); | ||
1708 | +} | ||
1709 | + | ||
1710 | +static void __exit xiic_i2c_exit(void) | ||
1711 | +{ | ||
1712 | + platform_driver_unregister(&xiic_i2c_driver); | ||
1713 | +} | ||
1714 | + | ||
1715 | +module_init(xiic_i2c_init); | ||
1716 | +module_exit(xiic_i2c_exit); | ||
1717 | + | ||
1718 | +MODULE_AUTHOR("info@mocean-labs.com"); | ||
1719 | +MODULE_DESCRIPTION("Xilinx I2C bus driver"); | ||
1720 | +MODULE_LICENSE("GPL v2"); | ||
1721 | diff -uNr linux-2.6.31/drivers/i2c/busses/Kconfig linux-2.6.31.new/drivers/i2c/busses/Kconfig | ||
1722 | --- linux-2.6.31/drivers/i2c/busses/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
1723 | +++ linux-2.6.31.new/drivers/i2c/busses/Kconfig 2009-10-23 11:17:29.000000000 -0700 | ||
1724 | @@ -433,6 +433,16 @@ | ||
1725 | This driver can also be built as a module. If so, the module | ||
1726 | will be called i2c-ocores. | ||
1727 | |||
1728 | +config I2C_XILINX | ||
1729 | + tristate "Xilinx I2C Controller" | ||
1730 | + depends on EXPERIMENTAL && HAS_IOMEM | ||
1731 | + help | ||
1732 | + If you say yes to this option, support will be included for the | ||
1733 | + Xilinx I2C controller. | ||
1734 | + | ||
1735 | + This driver can also be built as a module. If so, the module | ||
1736 | + will be called xilinx_i2c. | ||
1737 | + | ||
1738 | config I2C_OMAP | ||
1739 | tristate "OMAP I2C adapter" | ||
1740 | depends on ARCH_OMAP | ||
1741 | diff -uNr linux-2.6.31/drivers/i2c/busses/Makefile linux-2.6.31.new/drivers/i2c/busses/Makefile | ||
1742 | --- linux-2.6.31/drivers/i2c/busses/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
1743 | +++ linux-2.6.31.new/drivers/i2c/busses/Makefile 2009-10-23 11:17:29.000000000 -0700 | ||
1744 | @@ -40,6 +40,7 @@ | ||
1745 | obj-$(CONFIG_I2C_MPC) += i2c-mpc.o | ||
1746 | obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o | ||
1747 | obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o | ||
1748 | +obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o | ||
1749 | obj-$(CONFIG_I2C_OMAP) += i2c-omap.o | ||
1750 | obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o | ||
1751 | obj-$(CONFIG_I2C_PNX) += i2c-pnx.o | ||
1752 | diff -uNr linux-2.6.31/drivers/input/touchscreen/tsc2007.c linux-2.6.31.new/drivers/input/touchscreen/tsc2007.c | ||
1753 | --- linux-2.6.31/drivers/input/touchscreen/tsc2007.c 2009-10-23 11:18:30.000000000 -0700 | ||
1754 | +++ linux-2.6.31.new/drivers/input/touchscreen/tsc2007.c 2009-10-23 11:17:19.000000000 -0700 | ||
1755 | @@ -21,15 +21,14 @@ | ||
1756 | */ | ||
1757 | |||
1758 | #include <linux/module.h> | ||
1759 | -#include <linux/hrtimer.h> | ||
1760 | #include <linux/slab.h> | ||
1761 | #include <linux/input.h> | ||
1762 | #include <linux/interrupt.h> | ||
1763 | #include <linux/i2c.h> | ||
1764 | #include <linux/i2c/tsc2007.h> | ||
1765 | |||
1766 | -#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */ | ||
1767 | -#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */ | ||
1768 | +#define TS_POLL_DELAY 1 /* ms delay between samples */ | ||
1769 | +#define TS_POLL_PERIOD 1 /* ms delay between samples */ | ||
1770 | |||
1771 | #define TSC2007_MEASURE_TEMP0 (0x0 << 4) | ||
1772 | #define TSC2007_MEASURE_AUX (0x2 << 4) | ||
1773 | @@ -70,17 +69,15 @@ | ||
1774 | struct tsc2007 { | ||
1775 | struct input_dev *input; | ||
1776 | char phys[32]; | ||
1777 | - struct hrtimer timer; | ||
1778 | - struct ts_event tc; | ||
1779 | + struct delayed_work work; | ||
1780 | |||
1781 | struct i2c_client *client; | ||
1782 | |||
1783 | - spinlock_t lock; | ||
1784 | - | ||
1785 | u16 model; | ||
1786 | u16 x_plate_ohms; | ||
1787 | |||
1788 | - unsigned pendown; | ||
1789 | + bool pendown; | ||
1790 | + bool ignore_first_irq; | ||
1791 | int irq; | ||
1792 | |||
1793 | int (*get_pendown_state)(void); | ||
1794 | @@ -109,52 +106,96 @@ | ||
1795 | return val; | ||
1796 | } | ||
1797 | |||
1798 | -static void tsc2007_send_event(void *tsc) | ||
1799 | +static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) | ||
1800 | +{ | ||
1801 | + /* y- still on; turn on only y+ (and ADC) */ | ||
1802 | + tc->y = tsc2007_xfer(tsc, READ_Y); | ||
1803 | + | ||
1804 | + /* turn y- off, x+ on, then leave in lowpower */ | ||
1805 | + tc->x = tsc2007_xfer(tsc, READ_X); | ||
1806 | + | ||
1807 | + /* turn y+ off, x- on; we'll use formula #1 */ | ||
1808 | + tc->z1 = tsc2007_xfer(tsc, READ_Z1); | ||
1809 | + tc->z2 = tsc2007_xfer(tsc, READ_Z2); | ||
1810 | + | ||
1811 | + /* Prepare for next touch reading - power down ADC, enable PENIRQ */ | ||
1812 | + tsc2007_xfer(tsc, PWRDOWN); | ||
1813 | +} | ||
1814 | + | ||
1815 | +static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) | ||
1816 | { | ||
1817 | - struct tsc2007 *ts = tsc; | ||
1818 | - u32 rt; | ||
1819 | - u16 x, y, z1, z2; | ||
1820 | - | ||
1821 | - x = ts->tc.x; | ||
1822 | - y = ts->tc.y; | ||
1823 | - z1 = ts->tc.z1; | ||
1824 | - z2 = ts->tc.z2; | ||
1825 | + u32 rt = 0; | ||
1826 | |||
1827 | /* range filtering */ | ||
1828 | - if (x == MAX_12BIT) | ||
1829 | - x = 0; | ||
1830 | + if (tc->x == MAX_12BIT) | ||
1831 | + tc->x = 0; | ||
1832 | |||
1833 | - if (likely(x && z1)) { | ||
1834 | + if (likely(tc->x && tc->z1)) { | ||
1835 | /* compute touch pressure resistance using equation #1 */ | ||
1836 | - rt = z2; | ||
1837 | - rt -= z1; | ||
1838 | - rt *= x; | ||
1839 | - rt *= ts->x_plate_ohms; | ||
1840 | - rt /= z1; | ||
1841 | + rt = tc->z2 - tc->z1; | ||
1842 | + rt *= tc->x; | ||
1843 | + rt *= tsc->x_plate_ohms; | ||
1844 | + rt /= tc->z1; | ||
1845 | rt = (rt + 2047) >> 12; | ||
1846 | - } else | ||
1847 | - rt = 0; | ||
1848 | + } | ||
1849 | + | ||
1850 | + return rt; | ||
1851 | +} | ||
1852 | + | ||
1853 | +static void tsc2007_send_up_event(struct tsc2007 *tsc) | ||
1854 | +{ | ||
1855 | + struct input_dev *input = tsc->input; | ||
1856 | + | ||
1857 | + dev_dbg(&tsc->client->dev, "UP\n"); | ||
1858 | |||
1859 | - /* Sample found inconsistent by debouncing or pressure is beyond | ||
1860 | - * the maximum. Don't report it to user space, repeat at least | ||
1861 | - * once more the measurement | ||
1862 | + input_report_key(input, BTN_TOUCH, 0); | ||
1863 | + input_report_abs(input, ABS_PRESSURE, 0); | ||
1864 | + input_sync(input); | ||
1865 | +} | ||
1866 | + | ||
1867 | +static void tsc2007_work(struct work_struct *work) | ||
1868 | +{ | ||
1869 | + struct tsc2007 *ts = | ||
1870 | + container_of(to_delayed_work(work), struct tsc2007, work); | ||
1871 | + struct ts_event tc; | ||
1872 | + u32 rt; | ||
1873 | + | ||
1874 | + /* | ||
1875 | + * NOTE: We can't rely on the pressure to determine the pen down | ||
1876 | + * state, even though this controller has a pressure sensor. | ||
1877 | + * The pressure value can fluctuate for quite a while after | ||
1878 | + * lifting the pen and in some cases may not even settle at the | ||
1879 | + * expected value. | ||
1880 | + * | ||
1881 | + * The only safe way to check for the pen up condition is in the | ||
1882 | + * work function by reading the pen signal state (it's a GPIO | ||
1883 | + * and IRQ). Unfortunately such callback is not always available, | ||
1884 | + * in that case we have rely on the pressure anyway. | ||
1885 | */ | ||
1886 | + if (ts->get_pendown_state) { | ||
1887 | + if (unlikely(!ts->get_pendown_state())) { | ||
1888 | + tsc2007_send_up_event(ts); | ||
1889 | + ts->pendown = false; | ||
1890 | + goto out; | ||
1891 | + } | ||
1892 | + | ||
1893 | + dev_dbg(&ts->client->dev, "pen is still down\n"); | ||
1894 | + } | ||
1895 | + | ||
1896 | + tsc2007_read_values(ts, &tc); | ||
1897 | + | ||
1898 | + rt = tsc2007_calculate_pressure(ts, &tc); | ||
1899 | if (rt > MAX_12BIT) { | ||
1900 | + /* | ||
1901 | + * Sample found inconsistent by debouncing or pressure is | ||
1902 | + * beyond the maximum. Don't report it to user space, | ||
1903 | + * repeat at least once more the measurement. | ||
1904 | + */ | ||
1905 | dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); | ||
1906 | + goto out; | ||
1907 | |||
1908 | - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), | ||
1909 | - HRTIMER_MODE_REL); | ||
1910 | - return; | ||
1911 | } | ||
1912 | |||
1913 | - /* NOTE: We can't rely on the pressure to determine the pen down | ||
1914 | - * state, even this controller has a pressure sensor. The pressure | ||
1915 | - * value can fluctuate for quite a while after lifting the pen and | ||
1916 | - * in some cases may not even settle at the expected value. | ||
1917 | - * | ||
1918 | - * The only safe way to check for the pen up condition is in the | ||
1919 | - * timer by reading the pen signal state (it's a GPIO _and_ IRQ). | ||
1920 | - */ | ||
1921 | if (rt) { | ||
1922 | struct input_dev *input = ts->input; | ||
1923 | |||
1924 | @@ -162,102 +203,82 @@ | ||
1925 | dev_dbg(&ts->client->dev, "DOWN\n"); | ||
1926 | |||
1927 | input_report_key(input, BTN_TOUCH, 1); | ||
1928 | - ts->pendown = 1; | ||
1929 | + ts->pendown = true; | ||
1930 | } | ||
1931 | |||
1932 | - input_report_abs(input, ABS_X, x); | ||
1933 | - input_report_abs(input, ABS_Y, y); | ||
1934 | + input_report_abs(input, ABS_X, tc.x); | ||
1935 | + input_report_abs(input, ABS_Y, tc.y); | ||
1936 | input_report_abs(input, ABS_PRESSURE, rt); | ||
1937 | |||
1938 | input_sync(input); | ||
1939 | |||
1940 | dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", | ||
1941 | - x, y, rt); | ||
1942 | - } | ||
1943 | - | ||
1944 | - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), | ||
1945 | - HRTIMER_MODE_REL); | ||
1946 | -} | ||
1947 | - | ||
1948 | -static int tsc2007_read_values(struct tsc2007 *tsc) | ||
1949 | -{ | ||
1950 | - /* y- still on; turn on only y+ (and ADC) */ | ||
1951 | - tsc->tc.y = tsc2007_xfer(tsc, READ_Y); | ||
1952 | - | ||
1953 | - /* turn y- off, x+ on, then leave in lowpower */ | ||
1954 | - tsc->tc.x = tsc2007_xfer(tsc, READ_X); | ||
1955 | - | ||
1956 | - /* turn y+ off, x- on; we'll use formula #1 */ | ||
1957 | - tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1); | ||
1958 | - tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2); | ||
1959 | - | ||
1960 | - /* power down */ | ||
1961 | - tsc2007_xfer(tsc, PWRDOWN); | ||
1962 | - | ||
1963 | - return 0; | ||
1964 | -} | ||
1965 | - | ||
1966 | -static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle) | ||
1967 | -{ | ||
1968 | - struct tsc2007 *ts = container_of(handle, struct tsc2007, timer); | ||
1969 | - unsigned long flags; | ||
1970 | - | ||
1971 | - spin_lock_irqsave(&ts->lock, flags); | ||
1972 | - | ||
1973 | - if (unlikely(!ts->get_pendown_state() && ts->pendown)) { | ||
1974 | - struct input_dev *input = ts->input; | ||
1975 | - | ||
1976 | - dev_dbg(&ts->client->dev, "UP\n"); | ||
1977 | + tc.x, tc.y, rt); | ||
1978 | |||
1979 | - input_report_key(input, BTN_TOUCH, 0); | ||
1980 | - input_report_abs(input, ABS_PRESSURE, 0); | ||
1981 | - input_sync(input); | ||
1982 | + } else if (!ts->get_pendown_state && ts->pendown) { | ||
1983 | + /* | ||
1984 | + * We don't have callback to check pendown state, so we | ||
1985 | + * have to assume that since pressure reported is 0 the | ||
1986 | + * pen was lifted up. | ||
1987 | + */ | ||
1988 | + tsc2007_send_up_event(ts); | ||
1989 | + ts->pendown = false; | ||
1990 | + } | ||
1991 | |||
1992 | - ts->pendown = 0; | ||
1993 | + out: | ||
1994 | + if (ts->pendown) | ||
1995 | + schedule_delayed_work(&ts->work, | ||
1996 | + msecs_to_jiffies(TS_POLL_PERIOD)); | ||
1997 | + else { | ||
1998 | + if (!ts->get_pendown_state) | ||
1999 | + ts->ignore_first_irq = 1; | ||
2000 | enable_irq(ts->irq); | ||
2001 | - } else { | ||
2002 | - /* pen is still down, continue with the measurement */ | ||
2003 | - dev_dbg(&ts->client->dev, "pen is still down\n"); | ||
2004 | - | ||
2005 | - tsc2007_read_values(ts); | ||
2006 | - tsc2007_send_event(ts); | ||
2007 | } | ||
2008 | - | ||
2009 | - spin_unlock_irqrestore(&ts->lock, flags); | ||
2010 | - | ||
2011 | - return HRTIMER_NORESTART; | ||
2012 | } | ||
2013 | |||
2014 | static irqreturn_t tsc2007_irq(int irq, void *handle) | ||
2015 | { | ||
2016 | struct tsc2007 *ts = handle; | ||
2017 | - unsigned long flags; | ||
2018 | |||
2019 | - spin_lock_irqsave(&ts->lock, flags); | ||
2020 | + if (ts->ignore_first_irq) { | ||
2021 | + ts->ignore_first_irq = 0; | ||
2022 | + return IRQ_HANDLED; | ||
2023 | + } | ||
2024 | |||
2025 | - if (likely(ts->get_pendown_state())) { | ||
2026 | + if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { | ||
2027 | disable_irq_nosync(ts->irq); | ||
2028 | - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), | ||
2029 | - HRTIMER_MODE_REL); | ||
2030 | + schedule_delayed_work(&ts->work, | ||
2031 | + msecs_to_jiffies(TS_POLL_DELAY)); | ||
2032 | } | ||
2033 | |||
2034 | if (ts->clear_penirq) | ||
2035 | ts->clear_penirq(); | ||
2036 | |||
2037 | - spin_unlock_irqrestore(&ts->lock, flags); | ||
2038 | - | ||
2039 | return IRQ_HANDLED; | ||
2040 | } | ||
2041 | |||
2042 | -static int tsc2007_probe(struct i2c_client *client, | ||
2043 | - const struct i2c_device_id *id) | ||
2044 | +static void tsc2007_free_irq(struct tsc2007 *ts) | ||
2045 | +{ | ||
2046 | + free_irq(ts->irq, ts); | ||
2047 | + if (cancel_delayed_work_sync(&ts->work)) { | ||
2048 | + /* | ||
2049 | + * Work was pending, therefore we need to enable | ||
2050 | + * IRQ here to balance the disable_irq() done in the | ||
2051 | + * interrupt handler. | ||
2052 | + */ | ||
2053 | + enable_irq(ts->irq); | ||
2054 | + } | ||
2055 | +} | ||
2056 | + | ||
2057 | +static int __devinit tsc2007_probe(struct i2c_client *client, | ||
2058 | + const struct i2c_device_id *id) | ||
2059 | { | ||
2060 | struct tsc2007 *ts; | ||
2061 | struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; | ||
2062 | struct input_dev *input_dev; | ||
2063 | int err; | ||
2064 | |||
2065 | - if (!pdata || !pdata->get_pendown_state) { | ||
2066 | + if (!pdata) { | ||
2067 | dev_err(&client->dev, "platform data is required!\n"); | ||
2068 | return -EINVAL; | ||
2069 | } | ||
2070 | @@ -274,22 +295,15 @@ | ||
2071 | } | ||
2072 | |||
2073 | ts->client = client; | ||
2074 | - i2c_set_clientdata(client, ts); | ||
2075 | - | ||
2076 | + ts->irq = client->irq; | ||
2077 | ts->input = input_dev; | ||
2078 | - | ||
2079 | - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
2080 | - ts->timer.function = tsc2007_timer; | ||
2081 | - | ||
2082 | - spin_lock_init(&ts->lock); | ||
2083 | + INIT_DELAYED_WORK(&ts->work, tsc2007_work); | ||
2084 | |||
2085 | ts->model = pdata->model; | ||
2086 | ts->x_plate_ohms = pdata->x_plate_ohms; | ||
2087 | ts->get_pendown_state = pdata->get_pendown_state; | ||
2088 | ts->clear_penirq = pdata->clear_penirq; | ||
2089 | |||
2090 | - pdata->init_platform_hw(); | ||
2091 | - | ||
2092 | snprintf(ts->phys, sizeof(ts->phys), | ||
2093 | "%s/input0", dev_name(&client->dev)); | ||
2094 | |||
2095 | @@ -304,9 +318,8 @@ | ||
2096 | input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | ||
2097 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | ||
2098 | |||
2099 | - tsc2007_read_values(ts); | ||
2100 | - | ||
2101 | - ts->irq = client->irq; | ||
2102 | + if (pdata->init_platform_hw) | ||
2103 | + pdata->init_platform_hw(); | ||
2104 | |||
2105 | err = request_irq(ts->irq, tsc2007_irq, 0, | ||
2106 | client->dev.driver->name, ts); | ||
2107 | @@ -319,29 +332,37 @@ | ||
2108 | if (err) | ||
2109 | goto err_free_irq; | ||
2110 | |||
2111 | - dev_info(&client->dev, "registered with irq (%d)\n", ts->irq); | ||
2112 | + i2c_set_clientdata(client, ts); | ||
2113 | + | ||
2114 | + /* Prepare for touch readings - power down ADC and enable PENIRQ */ | ||
2115 | + err = tsc2007_xfer(ts, PWRDOWN); | ||
2116 | + if (err < 0) | ||
2117 | + goto err_unreg_dev; | ||
2118 | |||
2119 | return 0; | ||
2120 | |||
2121 | + err_unreg_dev: | ||
2122 | + input_unregister_device(ts->input); | ||
2123 | err_free_irq: | ||
2124 | - free_irq(ts->irq, ts); | ||
2125 | - hrtimer_cancel(&ts->timer); | ||
2126 | + tsc2007_free_irq(ts); | ||
2127 | + if (pdata->exit_platform_hw) | ||
2128 | + pdata->exit_platform_hw(); | ||
2129 | err_free_mem: | ||
2130 | input_free_device(input_dev); | ||
2131 | kfree(ts); | ||
2132 | return err; | ||
2133 | } | ||
2134 | |||
2135 | -static int tsc2007_remove(struct i2c_client *client) | ||
2136 | +static int __devexit tsc2007_remove(struct i2c_client *client) | ||
2137 | { | ||
2138 | struct tsc2007 *ts = i2c_get_clientdata(client); | ||
2139 | - struct tsc2007_platform_data *pdata; | ||
2140 | + struct tsc2007_platform_data *pdata = client->dev.platform_data; | ||
2141 | |||
2142 | - pdata = client->dev.platform_data; | ||
2143 | - pdata->exit_platform_hw(); | ||
2144 | + tsc2007_free_irq(ts); | ||
2145 | + | ||
2146 | + if (pdata->exit_platform_hw) | ||
2147 | + pdata->exit_platform_hw(); | ||
2148 | |||
2149 | - free_irq(ts->irq, ts); | ||
2150 | - hrtimer_cancel(&ts->timer); | ||
2151 | input_unregister_device(ts->input); | ||
2152 | kfree(ts); | ||
2153 | |||
2154 | @@ -362,7 +383,7 @@ | ||
2155 | }, | ||
2156 | .id_table = tsc2007_idtable, | ||
2157 | .probe = tsc2007_probe, | ||
2158 | - .remove = tsc2007_remove, | ||
2159 | + .remove = __devexit_p(tsc2007_remove), | ||
2160 | }; | ||
2161 | |||
2162 | static int __init tsc2007_init(void) | ||
2163 | diff -uNr linux-2.6.31/drivers/media/radio/Kconfig linux-2.6.31.new/drivers/media/radio/Kconfig | ||
2164 | --- linux-2.6.31/drivers/media/radio/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
2165 | +++ linux-2.6.31.new/drivers/media/radio/Kconfig 2009-10-23 11:17:28.000000000 -0700 | ||
2166 | @@ -406,4 +406,38 @@ | ||
2167 | Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N | ||
2168 | here if TEA5764 reference frequency is connected in FREQIN. | ||
2169 | |||
2170 | +config RADIO_SAA7706H | ||
2171 | + tristate "SAA7706H Car Radio DSP" | ||
2172 | + depends on I2C && VIDEO_V4L2 | ||
2173 | + ---help--- | ||
2174 | + Say Y here if you want to use the SAA7706H Car radio Digital | ||
2175 | + Signal Processor, found for instance on the Russellville development | ||
2176 | + board. On the russellville the device is connected to internal | ||
2177 | + timberdale I2C bus. | ||
2178 | + | ||
2179 | + To compile this driver as a module, choose M here: the | ||
2180 | + module will be called SAA7706H. | ||
2181 | + | ||
2182 | +config RADIO_TEF6862 | ||
2183 | + tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" | ||
2184 | + depends on I2C && VIDEO_V4L2 | ||
2185 | + ---help--- | ||
2186 | + Say Y here if you want to use the TEF6862 Car Radio Enhanced | ||
2187 | + Selectivity Tuner, found for instance on the Russellville development | ||
2188 | + board. On the russellville the device is connected to internal | ||
2189 | + timberdale I2C bus. | ||
2190 | + | ||
2191 | + To compile this driver as a module, choose M here: the | ||
2192 | + module will be called TEF6862. | ||
2193 | + | ||
2194 | +config RADIO_TIMBERDALE | ||
2195 | + tristate "Enable the Timberdale radio driver" | ||
2196 | + depends on MFD_TIMBERDALE && VIDEO_V4L2 && HAS_IOMEM | ||
2197 | + select RADIO_TEF6862 | ||
2198 | + select RADIO_SAA7706H | ||
2199 | + ---help--- | ||
2200 | + This is a kind of umbrella driver for the Radio Tuner and DSP | ||
2201 | + found behind the Timberdale FPGA on the Russellville board. | ||
2202 | + Enable this driver will automatically select the DSP and tuner. | ||
2203 | + | ||
2204 | endif # RADIO_ADAPTERS | ||
2205 | diff -uNr linux-2.6.31/drivers/media/radio/Makefile linux-2.6.31.new/drivers/media/radio/Makefile | ||
2206 | --- linux-2.6.31/drivers/media/radio/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
2207 | +++ linux-2.6.31.new/drivers/media/radio/Makefile 2009-10-23 11:17:28.000000000 -0700 | ||
2208 | @@ -20,5 +20,8 @@ | ||
2209 | obj-$(CONFIG_USB_SI470X) += radio-si470x.o | ||
2210 | obj-$(CONFIG_USB_MR800) += radio-mr800.o | ||
2211 | obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o | ||
2212 | +obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o | ||
2213 | +obj-$(CONFIG_RADIO_TEF6862) += tef6862.o | ||
2214 | +obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o | ||
2215 | |||
2216 | EXTRA_CFLAGS += -Isound | ||
2217 | diff -uNr linux-2.6.31/drivers/media/radio/radio-timb.c linux-2.6.31.new/drivers/media/radio/radio-timb.c | ||
2218 | --- linux-2.6.31/drivers/media/radio/radio-timb.c 1969-12-31 16:00:00.000000000 -0800 | ||
2219 | +++ linux-2.6.31.new/drivers/media/radio/radio-timb.c 2009-10-23 11:17:28.000000000 -0700 | ||
2220 | @@ -0,0 +1,545 @@ | ||
2221 | +/* | ||
2222 | + * radio-timb.c Timberdale FPGA Radio driver | ||
2223 | + * Copyright (c) 2009 Intel Corporation | ||
2224 | + * | ||
2225 | + * This program is free software; you can redistribute it and/or modify | ||
2226 | + * it under the terms of the GNU General Public License version 2 as | ||
2227 | + * published by the Free Software Foundation. | ||
2228 | + * | ||
2229 | + * This program is distributed in the hope that it will be useful, | ||
2230 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2231 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2232 | + * GNU General Public License for more details. | ||
2233 | + * | ||
2234 | + * You should have received a copy of the GNU General Public License | ||
2235 | + * along with this program; if not, write to the Free Software | ||
2236 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
2237 | + */ | ||
2238 | + | ||
2239 | +#include <linux/list.h> | ||
2240 | +#include <linux/version.h> | ||
2241 | +#include <linux/module.h> | ||
2242 | +#include <linux/io.h> | ||
2243 | +#include <media/v4l2-common.h> | ||
2244 | +#include <media/v4l2-ioctl.h> | ||
2245 | +#include <media/v4l2-device.h> | ||
2246 | +#include <linux/platform_device.h> | ||
2247 | +#include <linux/interrupt.h> | ||
2248 | +#include <linux/i2c.h> | ||
2249 | +#include <media/timb_radio.h> | ||
2250 | + | ||
2251 | +#define DRIVER_NAME "timb-radio" | ||
2252 | + | ||
2253 | +#define RDS_BLOCK_SIZE 4 | ||
2254 | +#define RDS_BUFFER_SIZE (RDS_BLOCK_SIZE * 100) | ||
2255 | + | ||
2256 | +struct timbradio { | ||
2257 | + struct mutex lock; /* for mutual exclusion */ | ||
2258 | + void __iomem *membase; | ||
2259 | + struct timb_radio_platform_data pdata; | ||
2260 | + struct v4l2_subdev *sd_tuner; | ||
2261 | + struct module *tuner_owner; | ||
2262 | + struct v4l2_subdev *sd_dsp; | ||
2263 | + struct module *dsp_owner; | ||
2264 | + struct video_device *video_dev; | ||
2265 | + /* RDS related */ | ||
2266 | + int open_count; | ||
2267 | + int rds_irq; | ||
2268 | + wait_queue_head_t read_queue; | ||
2269 | + unsigned char buffer[RDS_BUFFER_SIZE]; | ||
2270 | + unsigned int rd_index; | ||
2271 | + unsigned int wr_index; | ||
2272 | +}; | ||
2273 | + | ||
2274 | + | ||
2275 | +static int timbradio_vidioc_querycap(struct file *file, void *priv, | ||
2276 | + struct v4l2_capability *v) | ||
2277 | +{ | ||
2278 | + strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver)); | ||
2279 | + strlcpy(v->card, "Timberdale Radio", sizeof(v->card)); | ||
2280 | + snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME); | ||
2281 | + v->version = KERNEL_VERSION(0, 0, 1); | ||
2282 | + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
2283 | + return 0; | ||
2284 | +} | ||
2285 | + | ||
2286 | +static int timbradio_vidioc_g_tuner(struct file *file, void *priv, | ||
2287 | + struct v4l2_tuner *v) | ||
2288 | +{ | ||
2289 | + struct timbradio *tr = video_drvdata(file); | ||
2290 | + int ret; | ||
2291 | + | ||
2292 | + mutex_lock(&tr->lock); | ||
2293 | + ret = v4l2_subdev_call(tr->sd_tuner, tuner, g_tuner, v); | ||
2294 | + mutex_unlock(&tr->lock); | ||
2295 | + | ||
2296 | + return ret; | ||
2297 | +} | ||
2298 | + | ||
2299 | +static int timbradio_vidioc_s_tuner(struct file *file, void *priv, | ||
2300 | + struct v4l2_tuner *v) | ||
2301 | +{ | ||
2302 | + struct timbradio *tr = video_drvdata(file); | ||
2303 | + int ret; | ||
2304 | + | ||
2305 | + mutex_lock(&tr->lock); | ||
2306 | + ret = v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v); | ||
2307 | + mutex_unlock(&tr->lock); | ||
2308 | + | ||
2309 | + return ret; | ||
2310 | +} | ||
2311 | + | ||
2312 | +static int timbradio_vidioc_g_input(struct file *filp, void *priv, | ||
2313 | + unsigned int *i) | ||
2314 | +{ | ||
2315 | + *i = 0; | ||
2316 | + return 0; | ||
2317 | +} | ||
2318 | + | ||
2319 | +static int timbradio_vidioc_s_input(struct file *filp, void *priv, | ||
2320 | + unsigned int i) | ||
2321 | +{ | ||
2322 | + return i ? -EINVAL : 0; | ||
2323 | +} | ||
2324 | + | ||
2325 | +static int timbradio_vidioc_g_audio(struct file *file, void *priv, | ||
2326 | + struct v4l2_audio *a) | ||
2327 | +{ | ||
2328 | + a->index = 0; | ||
2329 | + strlcpy(a->name, "Radio", sizeof(a->name)); | ||
2330 | + a->capability = V4L2_AUDCAP_STEREO; | ||
2331 | + return 0; | ||
2332 | +} | ||
2333 | + | ||
2334 | + | ||
2335 | +static int timbradio_vidioc_s_audio(struct file *file, void *priv, | ||
2336 | + struct v4l2_audio *a) | ||
2337 | +{ | ||
2338 | + return a->index ? -EINVAL : 0; | ||
2339 | +} | ||
2340 | + | ||
2341 | +static int timbradio_vidioc_s_frequency(struct file *file, void *priv, | ||
2342 | + struct v4l2_frequency *f) | ||
2343 | +{ | ||
2344 | + struct timbradio *tr = video_drvdata(file); | ||
2345 | + int ret; | ||
2346 | + | ||
2347 | + mutex_lock(&tr->lock); | ||
2348 | + ret = v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f); | ||
2349 | + mutex_unlock(&tr->lock); | ||
2350 | + | ||
2351 | + return ret; | ||
2352 | +} | ||
2353 | + | ||
2354 | +static int timbradio_vidioc_g_frequency(struct file *file, void *priv, | ||
2355 | + struct v4l2_frequency *f) | ||
2356 | +{ | ||
2357 | + struct timbradio *tr = video_drvdata(file); | ||
2358 | + int ret; | ||
2359 | + | ||
2360 | + mutex_lock(&tr->lock); | ||
2361 | + ret = v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f); | ||
2362 | + mutex_unlock(&tr->lock); | ||
2363 | + | ||
2364 | + return ret; | ||
2365 | +} | ||
2366 | + | ||
2367 | +static int timbradio_vidioc_queryctrl(struct file *file, void *priv, | ||
2368 | + struct v4l2_queryctrl *qc) | ||
2369 | +{ | ||
2370 | + struct timbradio *tr = video_drvdata(file); | ||
2371 | + int ret; | ||
2372 | + | ||
2373 | + mutex_lock(&tr->lock); | ||
2374 | + ret = v4l2_subdev_call(tr->sd_dsp, core, queryctrl, qc); | ||
2375 | + mutex_unlock(&tr->lock); | ||
2376 | + | ||
2377 | + return ret; | ||
2378 | +} | ||
2379 | + | ||
2380 | +static int timbradio_vidioc_g_ctrl(struct file *file, void *priv, | ||
2381 | + struct v4l2_control *ctrl) | ||
2382 | +{ | ||
2383 | + struct timbradio *tr = video_drvdata(file); | ||
2384 | + int ret; | ||
2385 | + | ||
2386 | + mutex_lock(&tr->lock); | ||
2387 | + ret = v4l2_subdev_call(tr->sd_dsp, core, g_ctrl, ctrl); | ||
2388 | + mutex_unlock(&tr->lock); | ||
2389 | + | ||
2390 | + return ret; | ||
2391 | +} | ||
2392 | + | ||
2393 | +static int timbradio_vidioc_s_ctrl(struct file *file, void *priv, | ||
2394 | + struct v4l2_control *ctrl) | ||
2395 | +{ | ||
2396 | + struct timbradio *tr = video_drvdata(file); | ||
2397 | + int ret; | ||
2398 | + | ||
2399 | + mutex_lock(&tr->lock); | ||
2400 | + ret = v4l2_subdev_call(tr->sd_dsp, core, s_ctrl, ctrl); | ||
2401 | + mutex_unlock(&tr->lock); | ||
2402 | + | ||
2403 | + return ret; | ||
2404 | +} | ||
2405 | + | ||
2406 | +static const struct v4l2_ioctl_ops timbradio_ioctl_ops = { | ||
2407 | + .vidioc_querycap = timbradio_vidioc_querycap, | ||
2408 | + .vidioc_g_tuner = timbradio_vidioc_g_tuner, | ||
2409 | + .vidioc_s_tuner = timbradio_vidioc_s_tuner, | ||
2410 | + .vidioc_g_frequency = timbradio_vidioc_g_frequency, | ||
2411 | + .vidioc_s_frequency = timbradio_vidioc_s_frequency, | ||
2412 | + .vidioc_g_input = timbradio_vidioc_g_input, | ||
2413 | + .vidioc_s_input = timbradio_vidioc_s_input, | ||
2414 | + .vidioc_g_audio = timbradio_vidioc_g_audio, | ||
2415 | + .vidioc_s_audio = timbradio_vidioc_s_audio, | ||
2416 | + .vidioc_queryctrl = timbradio_vidioc_queryctrl, | ||
2417 | + .vidioc_g_ctrl = timbradio_vidioc_g_ctrl, | ||
2418 | + .vidioc_s_ctrl = timbradio_vidioc_s_ctrl | ||
2419 | +}; | ||
2420 | + | ||
2421 | +static irqreturn_t timbradio_irq(int irq, void *devid) | ||
2422 | +{ | ||
2423 | + struct timbradio *tr = devid; | ||
2424 | + u32 data = ioread32(tr->membase); | ||
2425 | + | ||
2426 | + tr->buffer[tr->wr_index++] = data >> 24; | ||
2427 | + tr->buffer[tr->wr_index++] = data >> 16; | ||
2428 | + tr->buffer[tr->wr_index++] = data >> 8; | ||
2429 | + tr->buffer[tr->wr_index++] = data; | ||
2430 | + tr->wr_index %= RDS_BUFFER_SIZE; | ||
2431 | + | ||
2432 | + wake_up(&tr->read_queue); | ||
2433 | + | ||
2434 | + /* new RDS data received, read it */ | ||
2435 | + return IRQ_HANDLED; | ||
2436 | +} | ||
2437 | + | ||
2438 | +/************************************************************************** | ||
2439 | + * File Operations Interface | ||
2440 | + **************************************************************************/ | ||
2441 | + | ||
2442 | +static ssize_t timbradio_rds_fops_read(struct file *file, char __user *buf, | ||
2443 | + size_t count, loff_t *ppos) | ||
2444 | +{ | ||
2445 | + struct timbradio *tr = video_drvdata(file); | ||
2446 | + int outblocks = 0; | ||
2447 | + | ||
2448 | + /* block if no new data available */ | ||
2449 | + while (tr->wr_index == tr->rd_index) { | ||
2450 | + if (file->f_flags & O_NONBLOCK) | ||
2451 | + return -EWOULDBLOCK; | ||
2452 | + | ||
2453 | + if (wait_event_interruptible(tr->read_queue, | ||
2454 | + tr->wr_index != tr->rd_index)) | ||
2455 | + return -EINTR; | ||
2456 | + } | ||
2457 | + | ||
2458 | + count /= RDS_BLOCK_SIZE; | ||
2459 | + /* copy RDS block out of internal buffer and to user buffer */ | ||
2460 | + mutex_lock(&tr->lock); | ||
2461 | + while (outblocks < count) { | ||
2462 | + if (tr->rd_index == tr->wr_index) | ||
2463 | + break; | ||
2464 | + | ||
2465 | + if (copy_to_user(buf, tr->buffer + tr->rd_index, | ||
2466 | + RDS_BLOCK_SIZE)) | ||
2467 | + break; | ||
2468 | + tr->rd_index += RDS_BLOCK_SIZE; | ||
2469 | + tr->rd_index %= RDS_BUFFER_SIZE; | ||
2470 | + outblocks++; | ||
2471 | + } | ||
2472 | + mutex_unlock(&tr->lock); | ||
2473 | + | ||
2474 | + return outblocks *RDS_BLOCK_SIZE; | ||
2475 | +} | ||
2476 | + | ||
2477 | +static unsigned int timbradio_rds_fops_poll(struct file *file, | ||
2478 | + struct poll_table_struct *pts) | ||
2479 | +{ | ||
2480 | + struct timbradio *tr = video_drvdata(file); | ||
2481 | + | ||
2482 | + poll_wait(file, &tr->read_queue, pts); | ||
2483 | + | ||
2484 | + if (tr->rd_index != tr->wr_index) | ||
2485 | + return POLLIN | POLLRDNORM; | ||
2486 | + | ||
2487 | + return 0; | ||
2488 | +} | ||
2489 | + | ||
2490 | +struct find_addr_arg { | ||
2491 | + char const *name; | ||
2492 | + struct i2c_client *client; | ||
2493 | +}; | ||
2494 | + | ||
2495 | +static int find_name(struct device *dev, void *argp) | ||
2496 | +{ | ||
2497 | + struct find_addr_arg *arg = (struct find_addr_arg *)argp; | ||
2498 | + struct i2c_client *client = i2c_verify_client(dev); | ||
2499 | + | ||
2500 | + if (client && !strcmp(arg->name, client->name) && client->driver) | ||
2501 | + arg->client = client; | ||
2502 | + | ||
2503 | + return 0; | ||
2504 | +} | ||
2505 | + | ||
2506 | +static struct i2c_client *find_client(struct i2c_adapter *adapt, | ||
2507 | + const char *name) | ||
2508 | +{ | ||
2509 | + struct find_addr_arg find_arg; | ||
2510 | + /* now find the client */ | ||
2511 | +#ifdef MODULE | ||
2512 | + request_module(name); | ||
2513 | +#endif | ||
2514 | + /* code for finding the I2C child */ | ||
2515 | + find_arg.name = name; | ||
2516 | + find_arg.client = NULL; | ||
2517 | + device_for_each_child(&adapt->dev, &find_arg, find_name); | ||
2518 | + return find_arg.client; | ||
2519 | +} | ||
2520 | + | ||
2521 | +static int timbradio_rds_fops_open(struct file *file) | ||
2522 | +{ | ||
2523 | + struct timbradio *tr = video_drvdata(file); | ||
2524 | + int err = 0; | ||
2525 | + | ||
2526 | + mutex_lock(&tr->lock); | ||
2527 | + if (tr->open_count == 0) { | ||
2528 | + /* device currently not open, check if the DSP and tuner is not | ||
2529 | + * yet found, in that case find them | ||
2530 | + */ | ||
2531 | + if (!tr->sd_tuner) { | ||
2532 | + struct i2c_adapter *adapt; | ||
2533 | + struct i2c_client *tuner; | ||
2534 | + struct i2c_client *dsp; | ||
2535 | + | ||
2536 | + /* find the I2C bus */ | ||
2537 | + adapt = i2c_get_adapter(tr->pdata.i2c_adapter); | ||
2538 | + if (!adapt) { | ||
2539 | + printk(KERN_ERR DRIVER_NAME": No I2C bus\n"); | ||
2540 | + err = -ENODEV; | ||
2541 | + goto out; | ||
2542 | + } | ||
2543 | + | ||
2544 | + /* now find the tuner and dsp */ | ||
2545 | + tuner = find_client(adapt, tr->pdata.tuner); | ||
2546 | + dsp = find_client(adapt, tr->pdata.dsp); | ||
2547 | + | ||
2548 | + i2c_put_adapter(adapt); | ||
2549 | + | ||
2550 | + if (!tuner || !dsp) { | ||
2551 | + printk(KERN_ERR DRIVER_NAME | ||
2552 | + ": Failed to get tuner or DSP\n"); | ||
2553 | + err = -ENODEV; | ||
2554 | + goto out; | ||
2555 | + } | ||
2556 | + | ||
2557 | + tr->sd_tuner = i2c_get_clientdata(tuner); | ||
2558 | + tr->sd_dsp = i2c_get_clientdata(dsp); | ||
2559 | + | ||
2560 | + tr->tuner_owner = tr->sd_tuner->owner; | ||
2561 | + tr->dsp_owner = tr->sd_dsp->owner; | ||
2562 | + /* Lock the modules */ | ||
2563 | + if (!try_module_get(tr->tuner_owner)) { | ||
2564 | + err = -ENODEV; | ||
2565 | + goto err_get_tuner; | ||
2566 | + } | ||
2567 | + | ||
2568 | + if (!try_module_get(tr->dsp_owner)) { | ||
2569 | + err = -ENODEV; | ||
2570 | + goto err_get_dsp; | ||
2571 | + } | ||
2572 | + } | ||
2573 | + | ||
2574 | + /* enable the IRQ for receiving RDS data */ | ||
2575 | + err = request_irq(tr->rds_irq, timbradio_irq, 0, DRIVER_NAME, | ||
2576 | + tr); | ||
2577 | + } | ||
2578 | + goto out; | ||
2579 | + | ||
2580 | +err_get_dsp: | ||
2581 | + module_put(tr->tuner_owner); | ||
2582 | +err_get_tuner: | ||
2583 | + tr->sd_tuner = NULL; | ||
2584 | + tr->sd_dsp = NULL; | ||
2585 | +out: | ||
2586 | + if (!err) | ||
2587 | + tr->open_count++; | ||
2588 | + mutex_unlock(&tr->lock); | ||
2589 | + return err; | ||
2590 | +} | ||
2591 | + | ||
2592 | +static int timbradio_rds_fops_release(struct file *file) | ||
2593 | +{ | ||
2594 | + struct timbradio *tr = video_drvdata(file); | ||
2595 | + | ||
2596 | + mutex_lock(&tr->lock); | ||
2597 | + tr->open_count--; | ||
2598 | + if (!tr->open_count) { | ||
2599 | + free_irq(tr->rds_irq, tr); | ||
2600 | + | ||
2601 | + tr->wr_index = 0; | ||
2602 | + tr->rd_index = 0; | ||
2603 | + | ||
2604 | + /* cancel read processes */ | ||
2605 | + wake_up_interruptible(&tr->read_queue); | ||
2606 | + } | ||
2607 | + mutex_unlock(&tr->lock); | ||
2608 | + | ||
2609 | + return 0; | ||
2610 | +} | ||
2611 | + | ||
2612 | + | ||
2613 | +static const struct v4l2_file_operations timbradio_fops = { | ||
2614 | + .owner = THIS_MODULE, | ||
2615 | + .ioctl = video_ioctl2, | ||
2616 | + .read = timbradio_rds_fops_read, | ||
2617 | + .poll = timbradio_rds_fops_poll, | ||
2618 | + .open = timbradio_rds_fops_open, | ||
2619 | + .release = timbradio_rds_fops_release, | ||
2620 | +}; | ||
2621 | + | ||
2622 | +static const struct video_device timbradio_template = { | ||
2623 | + .name = "Timberdale Radio", | ||
2624 | + .fops = &timbradio_fops, | ||
2625 | + .ioctl_ops = &timbradio_ioctl_ops, | ||
2626 | + .release = video_device_release_empty, | ||
2627 | + .minor = -1 | ||
2628 | +}; | ||
2629 | + | ||
2630 | + | ||
2631 | + | ||
2632 | +static int timbradio_probe(struct platform_device *pdev) | ||
2633 | +{ | ||
2634 | + struct timb_radio_platform_data *pdata = pdev->dev.platform_data; | ||
2635 | + struct timbradio *tr; | ||
2636 | + struct resource *iomem; | ||
2637 | + int irq; | ||
2638 | + int err; | ||
2639 | + | ||
2640 | + if (!pdata) { | ||
2641 | + printk(KERN_ERR DRIVER_NAME": Platform data missing\n"); | ||
2642 | + err = -EINVAL; | ||
2643 | + goto err; | ||
2644 | + } | ||
2645 | + | ||
2646 | + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
2647 | + if (!iomem) { | ||
2648 | + err = -ENODEV; | ||
2649 | + goto err; | ||
2650 | + } | ||
2651 | + | ||
2652 | + irq = platform_get_irq(pdev, 0); | ||
2653 | + if (irq < 0) { | ||
2654 | + err = -ENODEV; | ||
2655 | + goto err; | ||
2656 | + } | ||
2657 | + | ||
2658 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
2659 | + DRIVER_NAME)) { | ||
2660 | + err = -EBUSY; | ||
2661 | + goto err; | ||
2662 | + } | ||
2663 | + | ||
2664 | + tr = kzalloc(sizeof(*tr), GFP_KERNEL); | ||
2665 | + if (!tr) { | ||
2666 | + err = -ENOMEM; | ||
2667 | + goto err_alloc; | ||
2668 | + } | ||
2669 | + mutex_init(&tr->lock); | ||
2670 | + | ||
2671 | + tr->membase = ioremap(iomem->start, resource_size(iomem)); | ||
2672 | + if (!tr->membase) { | ||
2673 | + err = -ENOMEM; | ||
2674 | + goto err_ioremap; | ||
2675 | + } | ||
2676 | + | ||
2677 | + memcpy(&tr->pdata, pdata, sizeof(tr->pdata)); | ||
2678 | + | ||
2679 | + tr->video_dev = video_device_alloc(); | ||
2680 | + if (!tr->video_dev) { | ||
2681 | + err = -ENOMEM; | ||
2682 | + goto err_video_req; | ||
2683 | + } | ||
2684 | + *tr->video_dev = timbradio_template; | ||
2685 | + tr->rds_irq = irq; | ||
2686 | + init_waitqueue_head(&tr->read_queue); | ||
2687 | + | ||
2688 | + err = video_register_device(tr->video_dev, VFL_TYPE_RADIO, -1); | ||
2689 | + if (err) { | ||
2690 | + printk(KERN_ALERT DRIVER_NAME": Error reg video\n"); | ||
2691 | + goto err_video_req; | ||
2692 | + } | ||
2693 | + | ||
2694 | + video_set_drvdata(tr->video_dev, tr); | ||
2695 | + | ||
2696 | + platform_set_drvdata(pdev, tr); | ||
2697 | + return 0; | ||
2698 | + | ||
2699 | +err_video_req: | ||
2700 | + if (tr->video_dev->minor != -1) | ||
2701 | + video_unregister_device(tr->video_dev); | ||
2702 | + else | ||
2703 | + video_device_release(tr->video_dev); | ||
2704 | + iounmap(tr->membase); | ||
2705 | +err_ioremap: | ||
2706 | + kfree(tr); | ||
2707 | +err_alloc: | ||
2708 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2709 | +err: | ||
2710 | + printk(KERN_ERR DRIVER_NAME ": Failed to register: %d\n", err); | ||
2711 | + | ||
2712 | + return err; | ||
2713 | +} | ||
2714 | + | ||
2715 | +static int timbradio_remove(struct platform_device *pdev) | ||
2716 | +{ | ||
2717 | + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
2718 | + struct timbradio *tr = platform_get_drvdata(pdev); | ||
2719 | + | ||
2720 | + if (tr->video_dev->minor != -1) | ||
2721 | + video_unregister_device(tr->video_dev); | ||
2722 | + else | ||
2723 | + video_device_release(tr->video_dev); | ||
2724 | + | ||
2725 | + if (tr->sd_tuner) { | ||
2726 | + module_put(tr->tuner_owner); | ||
2727 | + module_put(tr->dsp_owner); | ||
2728 | + } | ||
2729 | + | ||
2730 | + iounmap(tr->membase); | ||
2731 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
2732 | + kfree(tr); | ||
2733 | + | ||
2734 | + return 0; | ||
2735 | +} | ||
2736 | + | ||
2737 | +static struct platform_driver timbradio_platform_driver = { | ||
2738 | + .driver = { | ||
2739 | + .name = DRIVER_NAME, | ||
2740 | + .owner = THIS_MODULE, | ||
2741 | + }, | ||
2742 | + .probe = timbradio_probe, | ||
2743 | + .remove = timbradio_remove, | ||
2744 | +}; | ||
2745 | + | ||
2746 | +/*--------------------------------------------------------------------------*/ | ||
2747 | + | ||
2748 | +static int __init timbradio_init(void) | ||
2749 | +{ | ||
2750 | + return platform_driver_register(&timbradio_platform_driver); | ||
2751 | +} | ||
2752 | + | ||
2753 | +static void __exit timbradio_exit(void) | ||
2754 | +{ | ||
2755 | + platform_driver_unregister(&timbradio_platform_driver); | ||
2756 | +} | ||
2757 | + | ||
2758 | +module_init(timbradio_init); | ||
2759 | +module_exit(timbradio_exit); | ||
2760 | + | ||
2761 | +MODULE_DESCRIPTION("Timberdale Radio driver"); | ||
2762 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
2763 | +MODULE_LICENSE("GPL v2"); | ||
2764 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
2765 | + | ||
2766 | diff -uNr linux-2.6.31/drivers/media/radio/saa7706h.c linux-2.6.31.new/drivers/media/radio/saa7706h.c | ||
2767 | --- linux-2.6.31/drivers/media/radio/saa7706h.c 1969-12-31 16:00:00.000000000 -0800 | ||
2768 | +++ linux-2.6.31.new/drivers/media/radio/saa7706h.c 2009-10-23 11:17:28.000000000 -0700 | ||
2769 | @@ -0,0 +1,496 @@ | ||
2770 | +/* | ||
2771 | + * saa7706.c Philips SAA7706H Car Radio DSP driver | ||
2772 | + * Copyright (c) 2009 Intel Corporation | ||
2773 | + * | ||
2774 | + * This program is free software; you can redistribute it and/or modify | ||
2775 | + * it under the terms of the GNU General Public License version 2 as | ||
2776 | + * published by the Free Software Foundation. | ||
2777 | + * | ||
2778 | + * This program is distributed in the hope that it will be useful, | ||
2779 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2780 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2781 | + * GNU General Public License for more details. | ||
2782 | + * | ||
2783 | + * You should have received a copy of the GNU General Public License | ||
2784 | + * along with this program; if not, write to the Free Software | ||
2785 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
2786 | + */ | ||
2787 | + | ||
2788 | +#include <linux/module.h> | ||
2789 | +#include <linux/init.h> | ||
2790 | +#include <linux/errno.h> | ||
2791 | +#include <linux/kernel.h> | ||
2792 | +#include <linux/interrupt.h> | ||
2793 | +#include <linux/i2c.h> | ||
2794 | +#include <linux/i2c-id.h> | ||
2795 | +#include <media/v4l2-ioctl.h> | ||
2796 | +#include <media/v4l2-device.h> | ||
2797 | +#include <media/v4l2-chip-ident.h> | ||
2798 | + | ||
2799 | +#define DRIVER_NAME "saa7706h" | ||
2800 | + | ||
2801 | +/* the I2C memory map looks like this | ||
2802 | + | ||
2803 | + $1C00 - $FFFF Not Used | ||
2804 | + $2200 - $3FFF Reserved YRAM (DSP2) space | ||
2805 | + $2000 - $21FF YRAM (DSP2) | ||
2806 | + $1FF0 - $1FFF Hardware Registers | ||
2807 | + $1280 - $1FEF Reserved XRAM (DSP2) space | ||
2808 | + $1000 - $127F XRAM (DSP2) | ||
2809 | + $0FFF DSP CONTROL | ||
2810 | + $0A00 - $0FFE Reserved | ||
2811 | + $0980 - $09FF Reserved YRAM (DSP1) space | ||
2812 | + $0800 - $097F YRAM (DSP1) | ||
2813 | + $0200 - $07FF Not Used | ||
2814 | + $0180 - $01FF Reserved XRAM (DSP1) space | ||
2815 | + $0000 - $017F XRAM (DSP1) | ||
2816 | +*/ | ||
2817 | + | ||
2818 | +#define SAA7706H_REG_CTRL 0x0fff | ||
2819 | +#define SAA7706H_CTRL_BYP_PLL 0x0001 | ||
2820 | +#define SAA7706H_CTRL_PLL_DIV_MASK 0x003e | ||
2821 | +#define SAA7706H_CTRL_PLL3_62975MHZ 0x003e | ||
2822 | +#define SAA7706H_CTRL_DSP_TURBO 0x0040 | ||
2823 | +#define SAA7706H_CTRL_PC_RESET_DSP1 0x0080 | ||
2824 | +#define SAA7706H_CTRL_PC_RESET_DSP2 0x0100 | ||
2825 | +#define SAA7706H_CTRL_DSP1_ROM_EN_MASK 0x0600 | ||
2826 | +#define SAA7706H_CTRL_DSP1_FUNC_PROM 0x0000 | ||
2827 | +#define SAA7706H_CTRL_DSP2_ROM_EN_MASK 0x1800 | ||
2828 | +#define SAA7706H_CTRL_DSP2_FUNC_PROM 0x0000 | ||
2829 | +#define SAA7706H_CTRL_DIG_SIL_INTERPOL 0x8000 | ||
2830 | + | ||
2831 | +#define SAA7706H_REG_EVALUATION 0x1ff0 | ||
2832 | +#define SAA7706H_EVAL_DISABLE_CHARGE_PUMP 0x000001 | ||
2833 | +#define SAA7706H_EVAL_DCS_CLOCK 0x000002 | ||
2834 | +#define SAA7706H_EVAL_GNDRC1_ENABLE 0x000004 | ||
2835 | +#define SAA7706H_EVAL_GNDRC2_ENABLE 0x000008 | ||
2836 | + | ||
2837 | +#define SAA7706H_REG_CL_GEN1 0x1ff3 | ||
2838 | +#define SAA7706H_CL_GEN1_MIN_LOOPGAIN_MASK 0x00000f | ||
2839 | +#define SAA7706H_CL_GEN1_LOOPGAIN_MASK 0x0000f0 | ||
2840 | +#define SAA7706H_CL_GEN1_COARSE_RATION 0xffff00 | ||
2841 | + | ||
2842 | +#define SAA7706H_REG_CL_GEN2 0x1ff4 | ||
2843 | +#define SAA7706H_CL_GEN2_WSEDGE_FALLING 0x000001 | ||
2844 | +#define SAA7706H_CL_GEN2_STOP_VCO 0x000002 | ||
2845 | +#define SAA7706H_CL_GEN2_FRERUN 0x000004 | ||
2846 | +#define SAA7706H_CL_GEN2_ADAPTIVE 0x000008 | ||
2847 | +#define SAA7706H_CL_GEN2_FINE_RATIO_MASK 0x0ffff0 | ||
2848 | + | ||
2849 | +#define SAA7706H_REG_CL_GEN4 0x1ff6 | ||
2850 | +#define SAA7706H_CL_GEN4_BYPASS_PLL1 0x001000 | ||
2851 | +#define SAA7706H_CL_GEN4_PLL1_DIV_MASK 0x03e000 | ||
2852 | +#define SAA7706H_CL_GEN4_DSP1_TURBO 0x040000 | ||
2853 | + | ||
2854 | +#define SAA7706H_REG_SEL 0x1ff7 | ||
2855 | +#define SAA7706H_SEL_DSP2_SRCA_MASK 0x000007 | ||
2856 | +#define SAA7706H_SEL_DSP2_FMTA_MASK 0x000031 | ||
2857 | +#define SAA7706H_SEL_DSP2_SRCB_MASK 0x0001c0 | ||
2858 | +#define SAA7706H_SEL_DSP2_FMTB_MASK 0x000e00 | ||
2859 | +#define SAA7706H_SEL_DSP1_SRC_MASK 0x003000 | ||
2860 | +#define SAA7706H_SEL_DSP1_FMT_MASK 0x01c003 | ||
2861 | +#define SAA7706H_SEL_SPDIF2 0x020000 | ||
2862 | +#define SAA7706H_SEL_HOST_IO_FMT_MASK 0x1c0000 | ||
2863 | +#define SAA7706H_SEL_EN_HOST_IO 0x200000 | ||
2864 | + | ||
2865 | +#define SAA7706H_REG_IAC 0x1ff8 | ||
2866 | +#define SAA7706H_REG_CLK_SET 0x1ff9 | ||
2867 | +#define SAA7706H_REG_CLK_COEFF 0x1ffa | ||
2868 | +#define SAA7706H_REG_INPUT_SENS 0x1ffb | ||
2869 | +#define SAA7706H_INPUT_SENS_RDS_VOL_MASK 0x0003f | ||
2870 | +#define SAA7706H_INPUT_SENS_FM_VOL_MASK 0x00fc0 | ||
2871 | +#define SAA7706H_INPUT_SENS_FM_MPX 0x01000 | ||
2872 | +#define SAA7706H_INPUT_SENS_OFF_FILTER_A_EN 0x02000 | ||
2873 | +#define SAA7706H_INPUT_SENS_OFF_FILTER_B_EN 0x04000 | ||
2874 | +#define SAA7706H_REG_PHONE_NAV_AUDIO 0x1ffc | ||
2875 | +#define SAA7706H_REG_IO_CONF_DSP2 0x1ffd | ||
2876 | +#define SAA7706H_REG_STATUS_DSP2 0x1ffe | ||
2877 | +#define SAA7706H_REG_PC_DSP2 0x1fff | ||
2878 | + | ||
2879 | +#define SAA7706H_DSP1_MOD0 0x0800 | ||
2880 | +#define SAA7706H_DSP1_ROM_VER 0x097f | ||
2881 | +#define SAA7706H_DSP2_MPTR0 0x1000 | ||
2882 | + | ||
2883 | +#define SAA7706H_DSP1_MODPNTR 0x0000 | ||
2884 | + | ||
2885 | +#define SAA7706H_DSP2_XMEM_CONTLLCW 0x113e | ||
2886 | +#define SAA7706H_DSP2_XMEM_BUSAMP 0x114a | ||
2887 | +#define SAA7706H_DSP2_XMEM_FDACPNTR 0x11f9 | ||
2888 | +#define SAA7706H_DSP2_XMEM_IIS1PNTR 0x11fb | ||
2889 | + | ||
2890 | +#define SAA7706H_DSP2_YMEM_PVGA 0x212a | ||
2891 | +#define SAA7706H_DSP2_YMEM_PVAT1 0x212b | ||
2892 | +#define SAA7706H_DSP2_YMEM_PVAT 0x212c | ||
2893 | +#define SAA7706H_DSP2_YMEM_ROM_VER 0x21ff | ||
2894 | + | ||
2895 | +#define SUPPORTED_DSP1_ROM_VER 0x667 | ||
2896 | + | ||
2897 | +struct saa7706h_state { | ||
2898 | + struct v4l2_subdev sd; | ||
2899 | + unsigned muted; | ||
2900 | +}; | ||
2901 | + | ||
2902 | +static inline struct saa7706h_state *to_state(struct v4l2_subdev *sd) | ||
2903 | +{ | ||
2904 | + return container_of(sd, struct saa7706h_state, sd); | ||
2905 | +} | ||
2906 | + | ||
2907 | +static int saa7706h_i2c_send(struct i2c_client *client, const u8 *data, int len) | ||
2908 | +{ | ||
2909 | + int err = i2c_master_send(client, data, len); | ||
2910 | + if (err == len) | ||
2911 | + return 0; | ||
2912 | + else if (err > 0) | ||
2913 | + return -EIO; | ||
2914 | + return err; | ||
2915 | +} | ||
2916 | + | ||
2917 | +static int saa7706h_i2c_transfer(struct i2c_client *client, | ||
2918 | + struct i2c_msg *msgs, int num) | ||
2919 | +{ | ||
2920 | + int err = i2c_transfer(client->adapter, msgs, num); | ||
2921 | + if (err == num) | ||
2922 | + return 0; | ||
2923 | + else if (err > 0) | ||
2924 | + return -EIO; | ||
2925 | + else | ||
2926 | + return err; | ||
2927 | +} | ||
2928 | + | ||
2929 | +static int saa7706h_set_reg24(struct i2c_client *client, u16 reg, u32 val) | ||
2930 | +{ | ||
2931 | + u8 buf[5]; | ||
2932 | + int pos = 0; | ||
2933 | + | ||
2934 | + buf[pos++] = reg >> 8; | ||
2935 | + buf[pos++] = reg; | ||
2936 | + buf[pos++] = val >> 16; | ||
2937 | + buf[pos++] = val >> 8; | ||
2938 | + buf[pos++] = val; | ||
2939 | + | ||
2940 | + return saa7706h_i2c_send(client, buf, pos); | ||
2941 | +} | ||
2942 | + | ||
2943 | +static int saa7706h_set_reg16(struct i2c_client *client, u16 reg, u16 val) | ||
2944 | +{ | ||
2945 | + u8 buf[4]; | ||
2946 | + int pos = 0; | ||
2947 | + | ||
2948 | + buf[pos++] = reg >> 8; | ||
2949 | + buf[pos++] = reg; | ||
2950 | + buf[pos++] = val >> 8; | ||
2951 | + buf[pos++] = val; | ||
2952 | + | ||
2953 | + return saa7706h_i2c_send(client, buf, pos); | ||
2954 | +} | ||
2955 | + | ||
2956 | +static int saa7706h_get_reg16(struct i2c_client *client, u16 reg) | ||
2957 | +{ | ||
2958 | + u8 buf[2]; | ||
2959 | + int err; | ||
2960 | + u8 regaddr[] = {reg >> 8, reg}; | ||
2961 | + struct i2c_msg msg[] = { {client->addr, 0, sizeof(regaddr), regaddr}, | ||
2962 | + {client->addr, I2C_M_RD, sizeof(buf), buf} }; | ||
2963 | + | ||
2964 | + err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg)); | ||
2965 | + if (err) | ||
2966 | + return err; | ||
2967 | + | ||
2968 | + return buf[0] << 8 | buf[1]; | ||
2969 | +} | ||
2970 | + | ||
2971 | +static int saa7706h_unmute(struct v4l2_subdev *sd) | ||
2972 | +{ | ||
2973 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
2974 | + struct saa7706h_state *state = to_state(sd); | ||
2975 | + int err; | ||
2976 | + | ||
2977 | + err = saa7706h_set_reg16(client, SAA7706H_REG_CTRL, | ||
2978 | + SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 | | ||
2979 | + SAA7706H_CTRL_PC_RESET_DSP2); | ||
2980 | + if (err) | ||
2981 | + goto out; | ||
2982 | + | ||
2983 | + /* newer versions of the chip requires a small sleep after reset */ | ||
2984 | + msleep(1); | ||
2985 | + | ||
2986 | + err = saa7706h_set_reg16(client, SAA7706H_REG_CTRL, | ||
2987 | + SAA7706H_CTRL_PLL3_62975MHZ); | ||
2988 | + if (err) | ||
2989 | + goto out; | ||
2990 | + | ||
2991 | + err = saa7706h_set_reg24(client, SAA7706H_REG_EVALUATION, 0); | ||
2992 | + if (err) | ||
2993 | + goto out; | ||
2994 | + | ||
2995 | + err = saa7706h_set_reg24(client, SAA7706H_REG_CL_GEN1, 0x040022); | ||
2996 | + if (err) | ||
2997 | + goto out; | ||
2998 | + | ||
2999 | + err = saa7706h_set_reg24(client, SAA7706H_REG_CL_GEN2, | ||
3000 | + SAA7706H_CL_GEN2_WSEDGE_FALLING); | ||
3001 | + if (err) | ||
3002 | + goto out; | ||
3003 | + | ||
3004 | + err = saa7706h_set_reg24(client, SAA7706H_REG_CL_GEN4, 0x024080); | ||
3005 | + if (err) | ||
3006 | + goto out; | ||
3007 | + | ||
3008 | + err = saa7706h_set_reg24(client, SAA7706H_REG_SEL, 0x200080); | ||
3009 | + if (err) | ||
3010 | + goto out; | ||
3011 | + | ||
3012 | + err = saa7706h_set_reg24(client, SAA7706H_REG_IAC, 0xf4caed); | ||
3013 | + if (err) | ||
3014 | + goto out; | ||
3015 | + | ||
3016 | + err = saa7706h_set_reg24(client, SAA7706H_REG_CLK_SET, 0x124334); | ||
3017 | + if (err) | ||
3018 | + goto out; | ||
3019 | + | ||
3020 | + err = saa7706h_set_reg24(client, SAA7706H_REG_CLK_COEFF, 0x004a1a); | ||
3021 | + if (err) | ||
3022 | + goto out; | ||
3023 | + | ||
3024 | + err = saa7706h_set_reg24(client, SAA7706H_REG_INPUT_SENS, 0x0071c7); | ||
3025 | + if (err) | ||
3026 | + goto out; | ||
3027 | + | ||
3028 | + err = saa7706h_set_reg24(client, SAA7706H_REG_PHONE_NAV_AUDIO, | ||
3029 | + 0x0e22ff); | ||
3030 | + if (err) | ||
3031 | + goto out; | ||
3032 | + | ||
3033 | + err = saa7706h_set_reg24(client, SAA7706H_REG_IO_CONF_DSP2, 0x001ff8); | ||
3034 | + if (err) | ||
3035 | + goto out; | ||
3036 | + | ||
3037 | + err = saa7706h_set_reg24(client, SAA7706H_REG_STATUS_DSP2, 0x080003); | ||
3038 | + if (err) | ||
3039 | + goto out; | ||
3040 | + | ||
3041 | + err = saa7706h_set_reg24(client, SAA7706H_REG_PC_DSP2, 0x000004); | ||
3042 | + if (err) | ||
3043 | + goto out; | ||
3044 | + | ||
3045 | + err = saa7706h_set_reg16(client, SAA7706H_DSP1_MOD0, 0x0c6c); | ||
3046 | + if (err) | ||
3047 | + goto out; | ||
3048 | + | ||
3049 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_MPTR0, 0x000b4b); | ||
3050 | + if (err) | ||
3051 | + goto out; | ||
3052 | + | ||
3053 | + err = saa7706h_set_reg24(client, SAA7706H_DSP1_MODPNTR, 0x000600); | ||
3054 | + if (err) | ||
3055 | + goto out; | ||
3056 | + | ||
3057 | + err = saa7706h_set_reg24(client, SAA7706H_DSP1_MODPNTR, 0x0000c0); | ||
3058 | + if (err) | ||
3059 | + goto out; | ||
3060 | + | ||
3061 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000819); | ||
3062 | + if (err) | ||
3063 | + goto out; | ||
3064 | + | ||
3065 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_CONTLLCW, 0x00085a); | ||
3066 | + if (err) | ||
3067 | + goto out; | ||
3068 | + | ||
3069 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_BUSAMP, 0x7fffff); | ||
3070 | + if (err) | ||
3071 | + goto out; | ||
3072 | + | ||
3073 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_FDACPNTR, 0x2000cb); | ||
3074 | + if (err) | ||
3075 | + goto out; | ||
3076 | + | ||
3077 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_IIS1PNTR, 0x2000cb); | ||
3078 | + if (err) | ||
3079 | + goto out; | ||
3080 | + | ||
3081 | + err = saa7706h_set_reg16(client, SAA7706H_DSP2_YMEM_PVGA, 0x0f80); | ||
3082 | + if (err) | ||
3083 | + goto out; | ||
3084 | + | ||
3085 | + err = saa7706h_set_reg16(client, SAA7706H_DSP2_YMEM_PVAT1, 0x0800); | ||
3086 | + if (err) | ||
3087 | + goto out; | ||
3088 | + | ||
3089 | + err = saa7706h_set_reg16(client, SAA7706H_DSP2_YMEM_PVAT, 0x0800); | ||
3090 | + if (err) | ||
3091 | + goto out; | ||
3092 | + | ||
3093 | + err = saa7706h_set_reg24(client, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000905); | ||
3094 | + if (err) | ||
3095 | + goto out; | ||
3096 | + | ||
3097 | + state->muted = 0; | ||
3098 | +out: | ||
3099 | + return err; | ||
3100 | +} | ||
3101 | + | ||
3102 | +static int saa7706h_mute(struct v4l2_subdev *sd) | ||
3103 | +{ | ||
3104 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3105 | + struct saa7706h_state *state = to_state(sd); | ||
3106 | + int err; | ||
3107 | + | ||
3108 | + err = saa7706h_set_reg16(client, SAA7706H_REG_CTRL, | ||
3109 | + SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 | | ||
3110 | + SAA7706H_CTRL_PC_RESET_DSP2); | ||
3111 | + if (err) | ||
3112 | + goto out; | ||
3113 | + | ||
3114 | + state->muted = 1; | ||
3115 | +out: | ||
3116 | + return err; | ||
3117 | +} | ||
3118 | + | ||
3119 | +static int saa7706h_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) | ||
3120 | +{ | ||
3121 | + switch (qc->id) { | ||
3122 | + case V4L2_CID_AUDIO_MUTE: | ||
3123 | + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
3124 | + } | ||
3125 | + return -EINVAL; | ||
3126 | +} | ||
3127 | + | ||
3128 | +static int saa7706h_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
3129 | +{ | ||
3130 | + struct saa7706h_state *state = to_state(sd); | ||
3131 | + | ||
3132 | + switch (ctrl->id) { | ||
3133 | + case V4L2_CID_AUDIO_MUTE: | ||
3134 | + ctrl->value = state->muted; | ||
3135 | + return 0; | ||
3136 | + } | ||
3137 | + return -EINVAL; | ||
3138 | +} | ||
3139 | + | ||
3140 | +static int saa7706h_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
3141 | +{ | ||
3142 | + switch (ctrl->id) { | ||
3143 | + case V4L2_CID_AUDIO_MUTE: | ||
3144 | + if (ctrl->value) | ||
3145 | + return saa7706h_mute(sd); | ||
3146 | + else | ||
3147 | + return saa7706h_unmute(sd); | ||
3148 | + } | ||
3149 | + return -EINVAL; | ||
3150 | +} | ||
3151 | + | ||
3152 | +static int saa7706h_g_chip_ident(struct v4l2_subdev *sd, | ||
3153 | + struct v4l2_dbg_chip_ident *chip) | ||
3154 | +{ | ||
3155 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3156 | + | ||
3157 | + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7706H, 0); | ||
3158 | +} | ||
3159 | + | ||
3160 | +static const struct v4l2_subdev_core_ops saa7706h_core_ops = { | ||
3161 | + .g_chip_ident = saa7706h_g_chip_ident, | ||
3162 | + .queryctrl = saa7706h_queryctrl, | ||
3163 | + .g_ctrl = saa7706h_g_ctrl, | ||
3164 | + .s_ctrl = saa7706h_s_ctrl, | ||
3165 | +}; | ||
3166 | + | ||
3167 | +static const struct v4l2_subdev_ops saa7706h_ops = { | ||
3168 | + .core = &saa7706h_core_ops, | ||
3169 | +}; | ||
3170 | + | ||
3171 | +/* | ||
3172 | + * Generic i2c probe | ||
3173 | + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | ||
3174 | + */ | ||
3175 | + | ||
3176 | +static int __devinit saa7706h_probe(struct i2c_client *client, | ||
3177 | + const struct i2c_device_id *id) | ||
3178 | +{ | ||
3179 | + struct saa7706h_state *state; | ||
3180 | + struct v4l2_subdev *sd; | ||
3181 | + int err; | ||
3182 | + | ||
3183 | + /* Check if the adapter supports the needed features */ | ||
3184 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
3185 | + return -EIO; | ||
3186 | + | ||
3187 | + v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
3188 | + client->addr << 1, client->adapter->name); | ||
3189 | + | ||
3190 | + state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL); | ||
3191 | + if (state == NULL) | ||
3192 | + return -ENOMEM; | ||
3193 | + sd = &state->sd; | ||
3194 | + v4l2_i2c_subdev_init(sd, client, &saa7706h_ops); | ||
3195 | + | ||
3196 | + /* check the rom versions */ | ||
3197 | + err = saa7706h_get_reg16(client, SAA7706H_DSP1_ROM_VER); | ||
3198 | + if (err < 0) | ||
3199 | + goto err; | ||
3200 | + if (err != SUPPORTED_DSP1_ROM_VER) | ||
3201 | + printk(KERN_WARNING DRIVER_NAME | ||
3202 | + ": Unknown DSP1 ROM code version: 0x%x\n", err); | ||
3203 | + | ||
3204 | + state->muted = 1; | ||
3205 | + | ||
3206 | + /* startup in a muted state */ | ||
3207 | + err = saa7706h_mute(sd); | ||
3208 | + if (err) | ||
3209 | + goto err; | ||
3210 | + | ||
3211 | + return 0; | ||
3212 | + | ||
3213 | +err: | ||
3214 | + v4l2_device_unregister_subdev(sd); | ||
3215 | + kfree(to_state(sd)); | ||
3216 | + | ||
3217 | + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err); | ||
3218 | + | ||
3219 | + return err; | ||
3220 | +} | ||
3221 | + | ||
3222 | +static int __devexit saa7706h_remove(struct i2c_client *client) | ||
3223 | +{ | ||
3224 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
3225 | + | ||
3226 | + saa7706h_mute(sd); | ||
3227 | + v4l2_device_unregister_subdev(sd); | ||
3228 | + kfree(to_state(sd)); | ||
3229 | + return 0; | ||
3230 | +} | ||
3231 | + | ||
3232 | +static const struct i2c_device_id saa7706h_id[] = { | ||
3233 | + {DRIVER_NAME, 0}, | ||
3234 | + {}, | ||
3235 | +}; | ||
3236 | + | ||
3237 | +MODULE_DEVICE_TABLE(i2c, saa7706h_id); | ||
3238 | + | ||
3239 | +static struct i2c_driver saa7706h_driver = { | ||
3240 | + .driver = { | ||
3241 | + .owner = THIS_MODULE, | ||
3242 | + .name = DRIVER_NAME, | ||
3243 | + }, | ||
3244 | + .probe = saa7706h_probe, | ||
3245 | + .remove = saa7706h_remove, | ||
3246 | + .id_table = saa7706h_id, | ||
3247 | +}; | ||
3248 | + | ||
3249 | +static __init int saa7706h_init(void) | ||
3250 | +{ | ||
3251 | + return i2c_add_driver(&saa7706h_driver); | ||
3252 | +} | ||
3253 | + | ||
3254 | +static __exit void saa7706h_exit(void) | ||
3255 | +{ | ||
3256 | + i2c_del_driver(&saa7706h_driver); | ||
3257 | +} | ||
3258 | + | ||
3259 | +module_init(saa7706h_init); | ||
3260 | +module_exit(saa7706h_exit); | ||
3261 | + | ||
3262 | +MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver"); | ||
3263 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
3264 | +MODULE_LICENSE("GPL v2"); | ||
3265 | + | ||
3266 | diff -uNr linux-2.6.31/drivers/media/radio/tef6862.c linux-2.6.31.new/drivers/media/radio/tef6862.c | ||
3267 | --- linux-2.6.31/drivers/media/radio/tef6862.c 1969-12-31 16:00:00.000000000 -0800 | ||
3268 | +++ linux-2.6.31.new/drivers/media/radio/tef6862.c 2009-10-23 11:17:28.000000000 -0700 | ||
3269 | @@ -0,0 +1,232 @@ | ||
3270 | +/* | ||
3271 | + * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner | ||
3272 | + * Copyright (c) 2009 Intel Corporation | ||
3273 | + * | ||
3274 | + * This program is free software; you can redistribute it and/or modify | ||
3275 | + * it under the terms of the GNU General Public License version 2 as | ||
3276 | + * published by the Free Software Foundation. | ||
3277 | + * | ||
3278 | + * This program is distributed in the hope that it will be useful, | ||
3279 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3280 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3281 | + * GNU General Public License for more details. | ||
3282 | + * | ||
3283 | + * You should have received a copy of the GNU General Public License | ||
3284 | + * along with this program; if not, write to the Free Software | ||
3285 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
3286 | + */ | ||
3287 | + | ||
3288 | +#include <linux/module.h> | ||
3289 | +#include <linux/init.h> | ||
3290 | +#include <linux/errno.h> | ||
3291 | +#include <linux/kernel.h> | ||
3292 | +#include <linux/interrupt.h> | ||
3293 | +#include <linux/i2c.h> | ||
3294 | +#include <linux/i2c-id.h> | ||
3295 | +#include <media/v4l2-ioctl.h> | ||
3296 | +#include <media/v4l2-device.h> | ||
3297 | +#include <media/v4l2-chip-ident.h> | ||
3298 | + | ||
3299 | +#define DRIVER_NAME "tef6862" | ||
3300 | + | ||
3301 | +#define FREQ_MUL 16000 | ||
3302 | + | ||
3303 | +#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10) | ||
3304 | +#define TEF6862_HI_FREQ (108 * FREQ_MUL) | ||
3305 | + | ||
3306 | +/* Write mode sub addresses */ | ||
3307 | +#define WM_SUB_BANDWIDTH 0x0 | ||
3308 | +#define WM_SUB_PLLM 0x1 | ||
3309 | +#define WM_SUB_PLLL 0x2 | ||
3310 | +#define WM_SUB_DAA 0x3 | ||
3311 | +#define WM_SUB_AGC 0x4 | ||
3312 | +#define WM_SUB_BAND 0x5 | ||
3313 | +#define WM_SUB_CONTROL 0x6 | ||
3314 | +#define WM_SUB_LEVEL 0x7 | ||
3315 | +#define WM_SUB_IFCF 0x8 | ||
3316 | +#define WM_SUB_IFCAP 0x9 | ||
3317 | +#define WM_SUB_ACD 0xA | ||
3318 | +#define WM_SUB_TEST 0xF | ||
3319 | + | ||
3320 | +/* Different modes of the MSA register */ | ||
3321 | +#define MODE_BUFFER 0x0 | ||
3322 | +#define MODE_PRESET 0x1 | ||
3323 | +#define MODE_SEARCH 0x2 | ||
3324 | +#define MODE_AF_UPDATE 0x3 | ||
3325 | +#define MODE_JUMP 0x4 | ||
3326 | +#define MODE_CHECK 0x5 | ||
3327 | +#define MODE_LOAD 0x6 | ||
3328 | +#define MODE_END 0x7 | ||
3329 | +#define MODE_SHIFT 5 | ||
3330 | + | ||
3331 | +struct tef6862_state { | ||
3332 | + struct v4l2_subdev sd; | ||
3333 | + unsigned long freq; | ||
3334 | +}; | ||
3335 | + | ||
3336 | +static inline struct tef6862_state *to_state(struct v4l2_subdev *sd) | ||
3337 | +{ | ||
3338 | + return container_of(sd, struct tef6862_state, sd); | ||
3339 | +} | ||
3340 | + | ||
3341 | +static u16 tef6862_sigstr(struct i2c_client *client) | ||
3342 | +{ | ||
3343 | + u8 buf[4]; | ||
3344 | + int err = i2c_master_recv(client, buf, sizeof(buf)); | ||
3345 | + if (err == sizeof(buf)) | ||
3346 | + return buf[3] << 8; | ||
3347 | + return 0; | ||
3348 | +} | ||
3349 | + | ||
3350 | +static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) | ||
3351 | +{ | ||
3352 | + if (v->index > 0) | ||
3353 | + return -EINVAL; | ||
3354 | + | ||
3355 | + /* only support FM for now */ | ||
3356 | + strlcpy(v->name, "FM", sizeof(v->name)); | ||
3357 | + v->type = V4L2_TUNER_RADIO; | ||
3358 | + v->rangelow = TEF6862_LO_FREQ; | ||
3359 | + v->rangehigh = TEF6862_HI_FREQ; | ||
3360 | + v->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
3361 | + v->capability = V4L2_TUNER_CAP_LOW; | ||
3362 | + v->audmode = V4L2_TUNER_MODE_STEREO; | ||
3363 | + v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd)); | ||
3364 | + | ||
3365 | + return 0; | ||
3366 | +} | ||
3367 | + | ||
3368 | +static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) | ||
3369 | +{ | ||
3370 | + return v->index ? -EINVAL : 0; | ||
3371 | +} | ||
3372 | + | ||
3373 | +static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) | ||
3374 | +{ | ||
3375 | + struct tef6862_state *state = to_state(sd); | ||
3376 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3377 | + u16 pll; | ||
3378 | + u8 i2cmsg[3]; | ||
3379 | + int err; | ||
3380 | + | ||
3381 | + if (f->tuner != 0) | ||
3382 | + return -EINVAL; | ||
3383 | + | ||
3384 | + pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL; | ||
3385 | + i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM; | ||
3386 | + i2cmsg[1] = (pll >> 8) & 0xff; | ||
3387 | + i2cmsg[2] = pll & 0xff; | ||
3388 | + | ||
3389 | + err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg)); | ||
3390 | + if (!err) | ||
3391 | + state->freq = f->frequency; | ||
3392 | + return err; | ||
3393 | +} | ||
3394 | + | ||
3395 | +static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) | ||
3396 | +{ | ||
3397 | + struct tef6862_state *state = to_state(sd); | ||
3398 | + | ||
3399 | + if (f->tuner != 0) | ||
3400 | + return -EINVAL; | ||
3401 | + f->type = V4L2_TUNER_RADIO; | ||
3402 | + f->frequency = state->freq; | ||
3403 | + return 0; | ||
3404 | +} | ||
3405 | + | ||
3406 | +static int tef6862_g_chip_ident(struct v4l2_subdev *sd, | ||
3407 | + struct v4l2_dbg_chip_ident *chip) | ||
3408 | +{ | ||
3409 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3410 | + | ||
3411 | + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0); | ||
3412 | +} | ||
3413 | + | ||
3414 | +static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = { | ||
3415 | + .g_tuner = tef6862_g_tuner, | ||
3416 | + .s_tuner = tef6862_s_tuner, | ||
3417 | + .s_frequency = tef6862_s_frequency, | ||
3418 | + .g_frequency = tef6862_g_frequency, | ||
3419 | +}; | ||
3420 | + | ||
3421 | +static const struct v4l2_subdev_core_ops tef6862_core_ops = { | ||
3422 | + .g_chip_ident = tef6862_g_chip_ident, | ||
3423 | +}; | ||
3424 | + | ||
3425 | +static const struct v4l2_subdev_ops tef6862_ops = { | ||
3426 | + .core = &tef6862_core_ops, | ||
3427 | + .tuner = &tef6862_tuner_ops, | ||
3428 | +}; | ||
3429 | + | ||
3430 | +/* | ||
3431 | + * Generic i2c probe | ||
3432 | + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | ||
3433 | + */ | ||
3434 | + | ||
3435 | +static int __devinit tef6862_probe(struct i2c_client *client, | ||
3436 | + const struct i2c_device_id *id) | ||
3437 | +{ | ||
3438 | + struct tef6862_state *state; | ||
3439 | + struct v4l2_subdev *sd; | ||
3440 | + | ||
3441 | + /* Check if the adapter supports the needed features */ | ||
3442 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
3443 | + return -EIO; | ||
3444 | + | ||
3445 | + v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
3446 | + client->addr << 1, client->adapter->name); | ||
3447 | + | ||
3448 | + state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL); | ||
3449 | + if (state == NULL) | ||
3450 | + return -ENOMEM; | ||
3451 | + state->freq = TEF6862_LO_FREQ; | ||
3452 | + | ||
3453 | + sd = &state->sd; | ||
3454 | + v4l2_i2c_subdev_init(sd, client, &tef6862_ops); | ||
3455 | + | ||
3456 | + return 0; | ||
3457 | +} | ||
3458 | + | ||
3459 | +static int __devexit tef6862_remove(struct i2c_client *client) | ||
3460 | +{ | ||
3461 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
3462 | + | ||
3463 | + v4l2_device_unregister_subdev(sd); | ||
3464 | + kfree(to_state(sd)); | ||
3465 | + return 0; | ||
3466 | +} | ||
3467 | + | ||
3468 | +static const struct i2c_device_id tef6862_id[] = { | ||
3469 | + {DRIVER_NAME, 0}, | ||
3470 | + {}, | ||
3471 | +}; | ||
3472 | + | ||
3473 | +MODULE_DEVICE_TABLE(i2c, tef6862_id); | ||
3474 | + | ||
3475 | +static struct i2c_driver tef6862_driver = { | ||
3476 | + .driver = { | ||
3477 | + .owner = THIS_MODULE, | ||
3478 | + .name = DRIVER_NAME, | ||
3479 | + }, | ||
3480 | + .probe = tef6862_probe, | ||
3481 | + .remove = tef6862_remove, | ||
3482 | + .id_table = tef6862_id, | ||
3483 | +}; | ||
3484 | + | ||
3485 | +static __init int tef6862_init(void) | ||
3486 | +{ | ||
3487 | + return i2c_add_driver(&tef6862_driver); | ||
3488 | +} | ||
3489 | + | ||
3490 | +static __exit void tef6862_exit(void) | ||
3491 | +{ | ||
3492 | + i2c_del_driver(&tef6862_driver); | ||
3493 | +} | ||
3494 | + | ||
3495 | +module_init(tef6862_init); | ||
3496 | +module_exit(tef6862_exit); | ||
3497 | + | ||
3498 | +MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner"); | ||
3499 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
3500 | +MODULE_LICENSE("GPL v2"); | ||
3501 | + | ||
3502 | diff -uNr linux-2.6.31/drivers/media/video/adv7180.c linux-2.6.31.new/drivers/media/video/adv7180.c | ||
3503 | --- linux-2.6.31/drivers/media/video/adv7180.c 1969-12-31 16:00:00.000000000 -0800 | ||
3504 | +++ linux-2.6.31.new/drivers/media/video/adv7180.c 2009-10-23 11:17:28.000000000 -0700 | ||
3505 | @@ -0,0 +1,475 @@ | ||
3506 | +/* | ||
3507 | + * adv7180.c Analog Devices ADV7180 video decoder driver | ||
3508 | + * Copyright (c) 2009 Intel Corporation | ||
3509 | + * | ||
3510 | + * This program is free software; you can redistribute it and/or modify | ||
3511 | + * it under the terms of the GNU General Public License version 2 as | ||
3512 | + * published by the Free Software Foundation. | ||
3513 | + * | ||
3514 | + * This program is distributed in the hope that it will be useful, | ||
3515 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3516 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3517 | + * GNU General Public License for more details. | ||
3518 | + * | ||
3519 | + * You should have received a copy of the GNU General Public License | ||
3520 | + * along with this program; if not, write to the Free Software | ||
3521 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
3522 | + */ | ||
3523 | + | ||
3524 | +#include <linux/module.h> | ||
3525 | +#include <linux/init.h> | ||
3526 | +#include <linux/errno.h> | ||
3527 | +#include <linux/kernel.h> | ||
3528 | +#include <linux/interrupt.h> | ||
3529 | +#include <linux/i2c.h> | ||
3530 | +#include <linux/i2c-id.h> | ||
3531 | +#include <media/v4l2-ioctl.h> | ||
3532 | +#include <linux/videodev2.h> | ||
3533 | +#include <media/v4l2-device.h> | ||
3534 | +#include <media/v4l2-chip-ident.h> | ||
3535 | +#include <linux/mutex.h> | ||
3536 | + | ||
3537 | +#define DRIVER_NAME "adv7180" | ||
3538 | + | ||
3539 | +#define ADV7180_INPUT_CONTROL_REG 0x00 | ||
3540 | +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 | ||
3541 | +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 | ||
3542 | +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 | ||
3543 | +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 | ||
3544 | +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 | ||
3545 | +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 | ||
3546 | +#define ADV7180_INPUT_CONTROL_PAL60 0x60 | ||
3547 | +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 | ||
3548 | +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 | ||
3549 | +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 | ||
3550 | +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 | ||
3551 | +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 | ||
3552 | +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 | ||
3553 | +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 | ||
3554 | +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 | ||
3555 | +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 | ||
3556 | + | ||
3557 | +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 | ||
3558 | +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 | ||
3559 | + | ||
3560 | +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 | ||
3561 | +#define ADV7180_AUTODETECT_DEFAULT 0x7f | ||
3562 | + | ||
3563 | +#define ADV7180_ADI_CTRL_REG 0x0e | ||
3564 | +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 | ||
3565 | + | ||
3566 | +#define ADV7180_STATUS1_REG 0x10 | ||
3567 | +#define ADV7180_STATUS1_IN_LOCK 0x01 | ||
3568 | +#define ADV7180_STATUS1_AUTOD_MASK 0x70 | ||
3569 | +#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 | ||
3570 | +#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 | ||
3571 | +#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 | ||
3572 | +#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 | ||
3573 | +#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 | ||
3574 | +#define ADV7180_STATUS1_AUTOD_SECAM 0x50 | ||
3575 | +#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 | ||
3576 | +#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 | ||
3577 | + | ||
3578 | +#define ADV7180_IDENT_REG 0x11 | ||
3579 | +#define ADV7180_ID_7180 0x18 | ||
3580 | + | ||
3581 | +#define ADV7180_ICONF1_ADI 0x40 | ||
3582 | +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 | ||
3583 | +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 | ||
3584 | +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 | ||
3585 | + | ||
3586 | +#define ADV7180_IRQ1_LOCK 0x01 | ||
3587 | +#define ADV7180_IRQ1_UNLOCK 0x02 | ||
3588 | +#define ADV7180_ISR1_ADI 0x42 | ||
3589 | +#define ADV7180_ICR1_ADI 0x43 | ||
3590 | +#define ADV7180_IMR1_ADI 0x44 | ||
3591 | +#define ADV7180_IMR2_ADI 0x48 | ||
3592 | +#define ADV7180_IRQ3_AD_CHANGE 0x08 | ||
3593 | +#define ADV7180_ISR3_ADI 0x4A | ||
3594 | +#define ADV7180_ICR3_ADI 0x4B | ||
3595 | +#define ADV7180_IMR3_ADI 0x4C | ||
3596 | +#define ADV7180_IMR4_ADI 0x50 | ||
3597 | + | ||
3598 | +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 | ||
3599 | +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F | ||
3600 | + | ||
3601 | +struct adv7180_state { | ||
3602 | + struct v4l2_subdev sd; | ||
3603 | + struct work_struct work; | ||
3604 | + struct mutex mutex; /* mutual excl. when accessing chip */ | ||
3605 | + int irq; | ||
3606 | + v4l2_std_id curr_norm; | ||
3607 | + bool autodetect; | ||
3608 | +}; | ||
3609 | + | ||
3610 | +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) | ||
3611 | +{ | ||
3612 | + switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { | ||
3613 | + case ADV7180_STATUS1_AUTOD_NTSM_M_J: | ||
3614 | + return V4L2_STD_NTSC; | ||
3615 | + case ADV7180_STATUS1_AUTOD_NTSC_4_43: | ||
3616 | + return V4L2_STD_NTSC_443; | ||
3617 | + case ADV7180_STATUS1_AUTOD_PAL_M: | ||
3618 | + return V4L2_STD_PAL_M; | ||
3619 | + case ADV7180_STATUS1_AUTOD_PAL_60: | ||
3620 | + return V4L2_STD_PAL_60; | ||
3621 | + case ADV7180_STATUS1_AUTOD_PAL_B_G: | ||
3622 | + return V4L2_STD_PAL; | ||
3623 | + case ADV7180_STATUS1_AUTOD_SECAM: | ||
3624 | + return V4L2_STD_SECAM; | ||
3625 | + case ADV7180_STATUS1_AUTOD_PAL_COMB: | ||
3626 | + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; | ||
3627 | + case ADV7180_STATUS1_AUTOD_SECAM_525: | ||
3628 | + return V4L2_STD_SECAM; | ||
3629 | + default: | ||
3630 | + return V4L2_STD_UNKNOWN; | ||
3631 | + } | ||
3632 | +} | ||
3633 | + | ||
3634 | +static int v4l2_std_to_adv7180(v4l2_std_id std) | ||
3635 | +{ | ||
3636 | + if (std == V4L2_STD_PAL_60) | ||
3637 | + return ADV7180_INPUT_CONTROL_PAL60; | ||
3638 | + if (std == V4L2_STD_NTSC_443) | ||
3639 | + return ADV7180_INPUT_CONTROL_NTSC_443; | ||
3640 | + if (std == V4L2_STD_PAL_N) | ||
3641 | + return ADV7180_INPUT_CONTROL_PAL_N; | ||
3642 | + if (std == V4L2_STD_PAL_M) | ||
3643 | + return ADV7180_INPUT_CONTROL_PAL_M; | ||
3644 | + if (std == V4L2_STD_PAL_Nc) | ||
3645 | + return ADV7180_INPUT_CONTROL_PAL_COMB_N; | ||
3646 | + | ||
3647 | + /* pal is a combination of several variants */ | ||
3648 | + if (std & V4L2_STD_PAL) | ||
3649 | + return ADV7180_INPUT_CONTROL_PAL_BG; | ||
3650 | + if (std & V4L2_STD_NTSC) | ||
3651 | + return ADV7180_INPUT_CONTROL_NTSC_M; | ||
3652 | + if (std & V4L2_STD_SECAM) | ||
3653 | + return ADV7180_INPUT_CONTROL_PAL_SECAM; | ||
3654 | + | ||
3655 | + return -EINVAL; | ||
3656 | +} | ||
3657 | + | ||
3658 | +static u32 adv7180_status_to_v4l2(u8 status1) | ||
3659 | +{ | ||
3660 | + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) | ||
3661 | + return V4L2_IN_ST_NO_SIGNAL; | ||
3662 | + | ||
3663 | + return 0; | ||
3664 | +} | ||
3665 | + | ||
3666 | +static int __adv7180_status(struct i2c_client *client, u32 *status, | ||
3667 | + v4l2_std_id *std) | ||
3668 | +{ | ||
3669 | + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); | ||
3670 | + | ||
3671 | + if (status1 < 0) | ||
3672 | + return status1; | ||
3673 | + | ||
3674 | + if (status) | ||
3675 | + *status = adv7180_status_to_v4l2(status1); | ||
3676 | + if (std) | ||
3677 | + *std = adv7180_std_to_v4l2(status1); | ||
3678 | + | ||
3679 | + return 0; | ||
3680 | +} | ||
3681 | + | ||
3682 | +static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) | ||
3683 | +{ | ||
3684 | + return container_of(sd, struct adv7180_state, sd); | ||
3685 | +} | ||
3686 | + | ||
3687 | +static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) | ||
3688 | +{ | ||
3689 | + struct adv7180_state *state = to_state(sd); | ||
3690 | + int err = mutex_lock_interruptible(&state->mutex); | ||
3691 | + if (err) | ||
3692 | + return err; | ||
3693 | + | ||
3694 | + /* when we are interrupt driven we know the state */ | ||
3695 | + if (!state->autodetect || state->irq > 0) | ||
3696 | + *std = state->curr_norm; | ||
3697 | + else | ||
3698 | + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); | ||
3699 | + | ||
3700 | + mutex_unlock(&state->mutex); | ||
3701 | + return err; | ||
3702 | +} | ||
3703 | + | ||
3704 | +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) | ||
3705 | +{ | ||
3706 | + struct adv7180_state *state = to_state(sd); | ||
3707 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3708 | + int ret = mutex_lock_interruptible(&state->mutex); | ||
3709 | + if (ret) | ||
3710 | + return ret; | ||
3711 | + | ||
3712 | + /* all standards -> autodetect */ | ||
3713 | + if (std == V4L2_STD_ALL) { | ||
3714 | + ret = i2c_smbus_write_byte_data(client, | ||
3715 | + ADV7180_INPUT_CONTROL_REG, | ||
3716 | + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); | ||
3717 | + if (ret < 0) | ||
3718 | + goto out; | ||
3719 | + | ||
3720 | + __adv7180_status(client, NULL, &state->curr_norm); | ||
3721 | + state->autodetect = true; | ||
3722 | + } else { | ||
3723 | + ret = v4l2_std_to_adv7180(std); | ||
3724 | + if (ret < 0) | ||
3725 | + goto out; | ||
3726 | + | ||
3727 | + ret = i2c_smbus_write_byte_data(client, | ||
3728 | + ADV7180_INPUT_CONTROL_REG, ret); | ||
3729 | + if (ret < 0) | ||
3730 | + goto out; | ||
3731 | + | ||
3732 | + state->curr_norm = std; | ||
3733 | + state->autodetect = false; | ||
3734 | + } | ||
3735 | + ret = 0; | ||
3736 | +out: | ||
3737 | + mutex_unlock(&state->mutex); | ||
3738 | + return ret; | ||
3739 | +} | ||
3740 | + | ||
3741 | +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) | ||
3742 | +{ | ||
3743 | + struct adv7180_state *state = to_state(sd); | ||
3744 | + int ret = mutex_lock_interruptible(&state->mutex); | ||
3745 | + if (ret) | ||
3746 | + return ret; | ||
3747 | + | ||
3748 | + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); | ||
3749 | + mutex_unlock(&state->mutex); | ||
3750 | + return ret; | ||
3751 | +} | ||
3752 | + | ||
3753 | +static int adv7180_g_chip_ident(struct v4l2_subdev *sd, | ||
3754 | + struct v4l2_dbg_chip_ident *chip) | ||
3755 | +{ | ||
3756 | + struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
3757 | + | ||
3758 | + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); | ||
3759 | +} | ||
3760 | + | ||
3761 | +static const struct v4l2_subdev_video_ops adv7180_video_ops = { | ||
3762 | + .querystd = adv7180_querystd, | ||
3763 | + .g_input_status = adv7180_g_input_status, | ||
3764 | +}; | ||
3765 | + | ||
3766 | +static const struct v4l2_subdev_core_ops adv7180_core_ops = { | ||
3767 | + .g_chip_ident = adv7180_g_chip_ident, | ||
3768 | + .s_std = adv7180_s_std, | ||
3769 | +}; | ||
3770 | + | ||
3771 | +static const struct v4l2_subdev_ops adv7180_ops = { | ||
3772 | + .core = &adv7180_core_ops, | ||
3773 | + .video = &adv7180_video_ops, | ||
3774 | +}; | ||
3775 | + | ||
3776 | +static void adv7180_work(struct work_struct *work) | ||
3777 | +{ | ||
3778 | + struct adv7180_state *state = container_of(work, struct adv7180_state, | ||
3779 | + work); | ||
3780 | + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); | ||
3781 | + u8 isr3; | ||
3782 | + | ||
3783 | + mutex_lock(&state->mutex); | ||
3784 | + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | ||
3785 | + ADV7180_ADI_CTRL_IRQ_SPACE); | ||
3786 | + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); | ||
3787 | + /* clear */ | ||
3788 | + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); | ||
3789 | + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); | ||
3790 | + | ||
3791 | + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) | ||
3792 | + __adv7180_status(client, NULL, &state->curr_norm); | ||
3793 | + mutex_unlock(&state->mutex); | ||
3794 | + | ||
3795 | + enable_irq(state->irq); | ||
3796 | +} | ||
3797 | + | ||
3798 | +static irqreturn_t adv7180_irq(int irq, void *devid) | ||
3799 | +{ | ||
3800 | + struct adv7180_state *state = devid; | ||
3801 | + | ||
3802 | + schedule_work(&state->work); | ||
3803 | + | ||
3804 | + disable_irq_nosync(state->irq); | ||
3805 | + | ||
3806 | + return IRQ_HANDLED; | ||
3807 | +} | ||
3808 | + | ||
3809 | +/* | ||
3810 | + * Generic i2c probe | ||
3811 | + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | ||
3812 | + */ | ||
3813 | + | ||
3814 | +static int __devinit adv7180_probe(struct i2c_client *client, | ||
3815 | + const struct i2c_device_id *id) | ||
3816 | +{ | ||
3817 | + struct adv7180_state *state; | ||
3818 | + struct v4l2_subdev *sd; | ||
3819 | + int ret; | ||
3820 | + | ||
3821 | + /* Check if the adapter supports the needed features */ | ||
3822 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
3823 | + return -EIO; | ||
3824 | + | ||
3825 | + v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
3826 | + client->addr << 1, client->adapter->name); | ||
3827 | + | ||
3828 | + state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); | ||
3829 | + if (state == NULL) { | ||
3830 | + ret = -ENOMEM; | ||
3831 | + goto err; | ||
3832 | + } | ||
3833 | + | ||
3834 | + state->irq = client->irq; | ||
3835 | + INIT_WORK(&state->work, adv7180_work); | ||
3836 | + mutex_init(&state->mutex); | ||
3837 | + state->autodetect = true; | ||
3838 | + sd = &state->sd; | ||
3839 | + v4l2_i2c_subdev_init(sd, client, &adv7180_ops); | ||
3840 | + | ||
3841 | + /* Initialize adv7180 */ | ||
3842 | + /* Enable autodetection */ | ||
3843 | + ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, | ||
3844 | + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); | ||
3845 | + if (ret < 0) | ||
3846 | + goto err_unreg_subdev; | ||
3847 | + | ||
3848 | + ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, | ||
3849 | + ADV7180_AUTODETECT_DEFAULT); | ||
3850 | + if (ret < 0) | ||
3851 | + goto err_unreg_subdev; | ||
3852 | + | ||
3853 | + /* ITU-R BT.656-4 compatible */ | ||
3854 | + ret = i2c_smbus_write_byte_data(client, | ||
3855 | + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, | ||
3856 | + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); | ||
3857 | + if (ret < 0) | ||
3858 | + goto err_unreg_subdev; | ||
3859 | + | ||
3860 | + | ||
3861 | + /* Manually set V bit end position in NTSC mode */ | ||
3862 | + ret = i2c_smbus_write_byte_data(client, | ||
3863 | + ADV7180_NTSC_V_BIT_END_REG, | ||
3864 | + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); | ||
3865 | + if (ret < 0) | ||
3866 | + goto err_unreg_subdev; | ||
3867 | + | ||
3868 | + /* read current norm */ | ||
3869 | + __adv7180_status(client, NULL, &state->curr_norm); | ||
3870 | + | ||
3871 | + /* register for interrupts */ | ||
3872 | + if (state->irq > 0) { | ||
3873 | + ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, | ||
3874 | + state); | ||
3875 | + if (ret) | ||
3876 | + goto err_unreg_subdev; | ||
3877 | + | ||
3878 | + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | ||
3879 | + ADV7180_ADI_CTRL_IRQ_SPACE); | ||
3880 | + if (ret < 0) | ||
3881 | + goto err_unreg_subdev; | ||
3882 | + | ||
3883 | + /* config the Interrupt pin to be active low */ | ||
3884 | + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, | ||
3885 | + ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); | ||
3886 | + if (ret < 0) | ||
3887 | + goto err_unreg_subdev; | ||
3888 | + | ||
3889 | + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); | ||
3890 | + if (ret < 0) | ||
3891 | + goto err_unreg_subdev; | ||
3892 | + | ||
3893 | + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); | ||
3894 | + if (ret < 0) | ||
3895 | + goto err_unreg_subdev; | ||
3896 | + | ||
3897 | + /* enable AD change interrupts interrupts */ | ||
3898 | + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, | ||
3899 | + ADV7180_IRQ3_AD_CHANGE); | ||
3900 | + if (ret < 0) | ||
3901 | + goto err_unreg_subdev; | ||
3902 | + | ||
3903 | + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); | ||
3904 | + if (ret < 0) | ||
3905 | + goto err_unreg_subdev; | ||
3906 | + | ||
3907 | + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, | ||
3908 | + 0); | ||
3909 | + if (ret < 0) | ||
3910 | + goto err_unreg_subdev; | ||
3911 | + } | ||
3912 | + | ||
3913 | + return 0; | ||
3914 | + | ||
3915 | +err_unreg_subdev: | ||
3916 | + mutex_destroy(&state->mutex); | ||
3917 | + v4l2_device_unregister_subdev(sd); | ||
3918 | + kfree(state); | ||
3919 | +err: | ||
3920 | + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); | ||
3921 | + return ret; | ||
3922 | +} | ||
3923 | + | ||
3924 | +static int __devexit adv7180_remove(struct i2c_client *client) | ||
3925 | +{ | ||
3926 | + struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
3927 | + struct adv7180_state *state = to_state(sd); | ||
3928 | + | ||
3929 | + if (state->irq > 0) { | ||
3930 | + free_irq(client->irq, state); | ||
3931 | + if (cancel_work_sync(&state->work)) { | ||
3932 | + /* | ||
3933 | + * Work was pending, therefore we need to enable | ||
3934 | + * IRQ here to balance the disable_irq() done in the | ||
3935 | + * interrupt handler. | ||
3936 | + */ | ||
3937 | + enable_irq(state->irq); | ||
3938 | + } | ||
3939 | + } | ||
3940 | + | ||
3941 | + mutex_destroy(&state->mutex); | ||
3942 | + v4l2_device_unregister_subdev(sd); | ||
3943 | + kfree(to_state(sd)); | ||
3944 | + return 0; | ||
3945 | +} | ||
3946 | + | ||
3947 | +static const struct i2c_device_id adv7180_id[] = { | ||
3948 | + {DRIVER_NAME, 0}, | ||
3949 | + {}, | ||
3950 | +}; | ||
3951 | + | ||
3952 | +MODULE_DEVICE_TABLE(i2c, adv7180_id); | ||
3953 | + | ||
3954 | +static struct i2c_driver adv7180_driver = { | ||
3955 | + .driver = { | ||
3956 | + .owner = THIS_MODULE, | ||
3957 | + .name = DRIVER_NAME, | ||
3958 | + }, | ||
3959 | + .probe = adv7180_probe, | ||
3960 | + .remove = adv7180_remove, | ||
3961 | + .id_table = adv7180_id, | ||
3962 | +}; | ||
3963 | + | ||
3964 | +static __init int adv7180_init(void) | ||
3965 | +{ | ||
3966 | + return i2c_add_driver(&adv7180_driver); | ||
3967 | +} | ||
3968 | + | ||
3969 | +static __exit void adv7180_exit(void) | ||
3970 | +{ | ||
3971 | + i2c_del_driver(&adv7180_driver); | ||
3972 | +} | ||
3973 | + | ||
3974 | +module_init(adv7180_init); | ||
3975 | +module_exit(adv7180_exit); | ||
3976 | + | ||
3977 | +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); | ||
3978 | +MODULE_AUTHOR("Mocean Laboratories"); | ||
3979 | +MODULE_LICENSE("GPL v2"); | ||
3980 | + | ||
3981 | diff -uNr linux-2.6.31/drivers/media/video/Kconfig linux-2.6.31.new/drivers/media/video/Kconfig | ||
3982 | --- linux-2.6.31/drivers/media/video/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
3983 | +++ linux-2.6.31.new/drivers/media/video/Kconfig 2009-10-23 11:17:28.000000000 -0700 | ||
3984 | @@ -265,6 +265,15 @@ | ||
3985 | |||
3986 | comment "Video decoders" | ||
3987 | |||
3988 | +config VIDEO_ADV7180 | ||
3989 | + tristate "Analog Devices ADV7180 decoder" | ||
3990 | + depends on VIDEO_V4L2 && I2C | ||
3991 | + ---help--- | ||
3992 | + Support for the Analog Devices ADV7180 video decoder. | ||
3993 | + | ||
3994 | + To compile this driver as a module, choose M here: the | ||
3995 | + module will be called adv7180. | ||
3996 | + | ||
3997 | config VIDEO_BT819 | ||
3998 | tristate "BT819A VideoStream decoder" | ||
3999 | depends on VIDEO_V4L2 && I2C | ||
4000 | @@ -816,6 +825,13 @@ | ||
4001 | ---help--- | ||
4002 | This is a v4l2 driver for the TI OMAP2 camera capture interface | ||
4003 | |||
4004 | +config VIDEO_TIMBERDALE | ||
4005 | + tristate "Support for timberdale Video In/LogiWIN" | ||
4006 | + depends on VIDEO_V4L2 && MFD_TIMBERDALE_DMA | ||
4007 | + select VIDEO_ADV7180 | ||
4008 | + ---help--- | ||
4009 | + Add support for the Video In peripherial of the timberdale FPGA. | ||
4010 | + | ||
4011 | # | ||
4012 | # USB Multimedia device configuration | ||
4013 | # | ||
4014 | diff -uNr linux-2.6.31/drivers/media/video/Makefile linux-2.6.31.new/drivers/media/video/Makefile | ||
4015 | --- linux-2.6.31/drivers/media/video/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
4016 | +++ linux-2.6.31.new/drivers/media/video/Makefile 2009-10-23 11:17:27.000000000 -0700 | ||
4017 | @@ -45,6 +45,7 @@ | ||
4018 | obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o | ||
4019 | obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o | ||
4020 | obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o | ||
4021 | +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o | ||
4022 | obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o | ||
4023 | obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o | ||
4024 | obj-$(CONFIG_VIDEO_BT819) += bt819.o | ||
4025 | @@ -156,6 +157,8 @@ | ||
4026 | |||
4027 | obj-$(CONFIG_VIDEO_AU0828) += au0828/ | ||
4028 | |||
4029 | +obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o | ||
4030 | + | ||
4031 | obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ | ||
4032 | |||
4033 | obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o | ||
4034 | diff -uNr linux-2.6.31/drivers/media/video/timblogiw.c linux-2.6.31.new/drivers/media/video/timblogiw.c | ||
4035 | --- linux-2.6.31/drivers/media/video/timblogiw.c 1969-12-31 16:00:00.000000000 -0800 | ||
4036 | +++ linux-2.6.31.new/drivers/media/video/timblogiw.c 2009-10-23 11:17:28.000000000 -0700 | ||
4037 | @@ -0,0 +1,1058 @@ | ||
4038 | +/* | ||
4039 | + * timblogiw.c timberdale FPGA LogiWin Video In driver | ||
4040 | + * Copyright (c) 2009 Intel Corporation | ||
4041 | + * | ||
4042 | + * This program is free software; you can redistribute it and/or modify | ||
4043 | + * it under the terms of the GNU General Public License version 2 as | ||
4044 | + * published by the Free Software Foundation. | ||
4045 | + * | ||
4046 | + * This program is distributed in the hope that it will be useful, | ||
4047 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4048 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4049 | + * GNU General Public License for more details. | ||
4050 | + * | ||
4051 | + * You should have received a copy of the GNU General Public License | ||
4052 | + * along with this program; if not, write to the Free Software | ||
4053 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
4054 | + */ | ||
4055 | + | ||
4056 | +/* Supports: | ||
4057 | + * Timberdale FPGA LogiWin Video In | ||
4058 | + */ | ||
4059 | + | ||
4060 | +#include <linux/list.h> | ||
4061 | +#include <linux/version.h> | ||
4062 | +#include <linux/module.h> | ||
4063 | +#include <linux/dma-mapping.h> | ||
4064 | +#include <media/v4l2-common.h> | ||
4065 | +#include <media/v4l2-ioctl.h> | ||
4066 | +#include <media/v4l2-device.h> | ||
4067 | +#include <linux/platform_device.h> | ||
4068 | +#include <linux/interrupt.h> | ||
4069 | +#include "timblogiw.h" | ||
4070 | +#include <linux/mfd/timbdma.h> | ||
4071 | +#include <linux/i2c.h> | ||
4072 | + | ||
4073 | +#define DRIVER_NAME "timb-video" | ||
4074 | + | ||
4075 | +#define TIMBLOGIW_CTRL 0x40 | ||
4076 | + | ||
4077 | +#define TIMBLOGIW_H_SCALE 0x20 | ||
4078 | +#define TIMBLOGIW_V_SCALE 0x28 | ||
4079 | + | ||
4080 | +#define TIMBLOGIW_X_CROP 0x58 | ||
4081 | +#define TIMBLOGIW_Y_CROP 0x60 | ||
4082 | + | ||
4083 | +#define TIMBLOGIW_W_CROP 0x00 | ||
4084 | +#define TIMBLOGIW_H_CROP 0x08 | ||
4085 | + | ||
4086 | +#define TIMBLOGIW_VERSION_CODE 0x02 | ||
4087 | + | ||
4088 | +#define TIMBLOGIW_BUF 0x04 | ||
4089 | +#define TIMBLOGIW_TBI 0x2c | ||
4090 | +#define TIMBLOGIW_BPL 0x30 | ||
4091 | + | ||
4092 | +#define dbg(...) | ||
4093 | + | ||
4094 | +#define BYTES_PER_LINE (720 * 2) | ||
4095 | + | ||
4096 | +#define DMA_BUFFER_SIZE (BYTES_PER_LINE * 576) | ||
4097 | + | ||
4098 | +#define TIMBLOGIW_VIDEO_FORMAT V4L2_PIX_FMT_UYVY | ||
4099 | + | ||
4100 | +static void timblogiw_release_buffers(struct timblogiw *lw); | ||
4101 | + | ||
4102 | +const struct timblogiw_tvnorm timblogiw_tvnorms[] = { | ||
4103 | + { | ||
4104 | + .std = V4L2_STD_PAL, | ||
4105 | + .width = 720, | ||
4106 | + .height = 576 | ||
4107 | + }, | ||
4108 | + { | ||
4109 | + .std = V4L2_STD_NTSC, | ||
4110 | + .width = 720, | ||
4111 | + .height = 480 | ||
4112 | + } | ||
4113 | +}; | ||
4114 | + | ||
4115 | +static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm) | ||
4116 | +{ | ||
4117 | + return norm->width * 2; | ||
4118 | +} | ||
4119 | + | ||
4120 | + | ||
4121 | +static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm) | ||
4122 | +{ | ||
4123 | + return norm->height * timblogiw_bytes_per_line(norm); | ||
4124 | +} | ||
4125 | + | ||
4126 | +static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std) | ||
4127 | +{ | ||
4128 | + int i; | ||
4129 | + for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) | ||
4130 | + if (timblogiw_tvnorms[i].std & std) | ||
4131 | + return timblogiw_tvnorms + i; | ||
4132 | + | ||
4133 | + /* default to first element */ | ||
4134 | + return timblogiw_tvnorms; | ||
4135 | +} | ||
4136 | + | ||
4137 | +static void timblogiw_handleframe(unsigned long arg) | ||
4138 | +{ | ||
4139 | + struct timblogiw_frame *f; | ||
4140 | + struct timblogiw *lw = (struct timblogiw *)arg; | ||
4141 | + | ||
4142 | + spin_lock_bh(&lw->queue_lock); | ||
4143 | + if (lw->dma.filled && !list_empty(&lw->inqueue)) { | ||
4144 | + /* put the entry in the outqueue */ | ||
4145 | + f = list_entry(lw->inqueue.next, struct timblogiw_frame, frame); | ||
4146 | + | ||
4147 | + /* sync memory and unmap */ | ||
4148 | + dma_sync_single_for_cpu(lw->dev, lw->dma.filled->handle, | ||
4149 | + timblogiw_frame_size(lw->cur_norm), DMA_FROM_DEVICE); | ||
4150 | + | ||
4151 | + /* copy data from the DMA buffer */ | ||
4152 | + memcpy(f->bufmem, lw->dma.filled->buf, f->buf.length); | ||
4153 | + /* buffer consumed */ | ||
4154 | + lw->dma.filled = NULL; | ||
4155 | + | ||
4156 | + do_gettimeofday(&f->buf.timestamp); | ||
4157 | + f->buf.sequence = ++lw->frame_count; | ||
4158 | + f->buf.field = V4L2_FIELD_NONE; | ||
4159 | + f->state = F_DONE; | ||
4160 | + f->buf.bytesused = f->buf.length; | ||
4161 | + list_move_tail(&f->frame, &lw->outqueue); | ||
4162 | + /* wake up any waiter */ | ||
4163 | + wake_up(&lw->wait_frame); | ||
4164 | + } else { | ||
4165 | + /* No user buffer available, consume buffer anyway | ||
4166 | + * who wants an old video frame? | ||
4167 | + */ | ||
4168 | + lw->dma.filled = NULL; | ||
4169 | + } | ||
4170 | + spin_unlock_bh(&lw->queue_lock); | ||
4171 | +} | ||
4172 | + | ||
4173 | +static int __timblogiw_start_dma(struct timblogiw *lw) | ||
4174 | +{ | ||
4175 | + int size = timblogiw_frame_size(lw->cur_norm); | ||
4176 | + int ret; | ||
4177 | + struct timbdma_transfer *transfer = lw->dma.transfer + lw->dma.curr; | ||
4178 | + int bytes_per_line = timblogiw_bytes_per_line(lw->cur_norm); | ||
4179 | + | ||
4180 | + ret = timbdma_prep_desc(transfer->desc, transfer->handle, size); | ||
4181 | + if (ret) | ||
4182 | + goto err; | ||
4183 | + | ||
4184 | + ret = timbdma_start(DMA_IRQ_VIDEO_RX, transfer->desc, bytes_per_line); | ||
4185 | + if (ret) | ||
4186 | + goto err; | ||
4187 | + return ret; | ||
4188 | +err: | ||
4189 | + return ret; | ||
4190 | +} | ||
4191 | + | ||
4192 | +static int timblogiw_isr(u32 flag, void *pdev) | ||
4193 | +{ | ||
4194 | + struct timblogiw *lw = (struct timblogiw *)pdev; | ||
4195 | + | ||
4196 | + if (lw->stream == STREAM_OFF) { | ||
4197 | + timbdma_stop(DMA_IRQ_VIDEO_RX); | ||
4198 | + /* stream is stopped, signal that the current transfer is | ||
4199 | + * finished */ | ||
4200 | + complete(&lw->irq_done); | ||
4201 | + } else { | ||
4202 | + struct timeval timestamp; | ||
4203 | + | ||
4204 | + do_gettimeofday(×tamp); | ||
4205 | + | ||
4206 | + if (!lw->dma.filled && (flag & DMA_IRQ_VIDEO_RX)) { | ||
4207 | + /* Got a frame, store it, and flip to next DMA buffer */ | ||
4208 | + lw->dma.filled = lw->dma.transfer + lw->dma.curr; | ||
4209 | + lw->dma.curr = !lw->dma.curr; | ||
4210 | + } else if (lw->dma.filled && (flag & DMA_IRQ_VIDEO_RX)) | ||
4211 | + printk("No free frame\n"); | ||
4212 | + | ||
4213 | + __timblogiw_start_dma(lw); | ||
4214 | + | ||
4215 | + if (flag & DMA_IRQ_VIDEO_DROP) | ||
4216 | + dbg("%s: frame dropped\n", __func__); | ||
4217 | + if (flag & DMA_IRQ_VIDEO_RX) { | ||
4218 | + dbg("%s: frame RX\n", __func__); | ||
4219 | + tasklet_schedule(&lw->tasklet); | ||
4220 | + } | ||
4221 | + } | ||
4222 | + | ||
4223 | + return 0; | ||
4224 | +} | ||
4225 | + | ||
4226 | +static void timblogiw_empty_framequeues(struct timblogiw *lw) | ||
4227 | +{ | ||
4228 | + u32 i; | ||
4229 | + | ||
4230 | + dbg("%s\n", __func__); | ||
4231 | + | ||
4232 | + INIT_LIST_HEAD(&lw->inqueue); | ||
4233 | + INIT_LIST_HEAD(&lw->outqueue); | ||
4234 | + | ||
4235 | + for (i = 0; i < lw->num_frames; i++) { | ||
4236 | + lw->frame[i].state = F_UNUSED; | ||
4237 | + lw->frame[i].buf.bytesused = 0; | ||
4238 | + } | ||
4239 | +} | ||
4240 | + | ||
4241 | +u32 timblogiw_request_buffers(struct timblogiw *lw, u32 count) | ||
4242 | +{ | ||
4243 | + /* needs to be page aligned cause the */ | ||
4244 | + /* buffers can be mapped individually! */ | ||
4245 | + const size_t imagesize = PAGE_ALIGN(timblogiw_frame_size(lw->cur_norm)); | ||
4246 | + void *buff = NULL; | ||
4247 | + int ret; | ||
4248 | + u32 i; | ||
4249 | + | ||
4250 | + dbg("%s - request of %i buffers of size %zi\n", | ||
4251 | + __func__, count, imagesize); | ||
4252 | + | ||
4253 | + lw->dma.transfer[0].buf = kzalloc(DMA_BUFFER_SIZE, GFP_KERNEL); | ||
4254 | + if (!lw->dma.transfer[0].buf) | ||
4255 | + goto err; | ||
4256 | + | ||
4257 | + lw->dma.transfer[1].buf = kzalloc(DMA_BUFFER_SIZE, GFP_KERNEL); | ||
4258 | + if (!lw->dma.transfer[1].buf) | ||
4259 | + goto err; | ||
4260 | + | ||
4261 | + lw->dma.transfer[0].desc = | ||
4262 | + timbdma_alloc_desc(DMA_BUFFER_SIZE, BYTES_PER_LINE * 2); | ||
4263 | + if (!lw->dma.transfer[0].desc) | ||
4264 | + goto err; | ||
4265 | + | ||
4266 | + lw->dma.transfer[1].desc = | ||
4267 | + timbdma_alloc_desc(DMA_BUFFER_SIZE, BYTES_PER_LINE * 2); | ||
4268 | + if (!lw->dma.transfer[1].desc) | ||
4269 | + goto err; | ||
4270 | + | ||
4271 | + /* map up the DMA buffers */ | ||
4272 | + lw->dma.transfer[0].handle = dma_map_single(lw->dev, | ||
4273 | + lw->dma.transfer[0].buf, DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
4274 | + ret = dma_mapping_error(lw->dev, lw->dma.transfer[0].handle); | ||
4275 | + if (ret) { | ||
4276 | + lw->dma.transfer[0].handle = 0; | ||
4277 | + goto err; | ||
4278 | + } | ||
4279 | + | ||
4280 | + lw->dma.transfer[1].handle = dma_map_single(lw->dev, | ||
4281 | + lw->dma.transfer[1].buf, DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
4282 | + ret = dma_mapping_error(lw->dev, lw->dma.transfer[1].handle); | ||
4283 | + if (ret) { | ||
4284 | + lw->dma.transfer[1].handle = 0; | ||
4285 | + goto err; | ||
4286 | + } | ||
4287 | + | ||
4288 | + if (count > TIMBLOGIW_NUM_FRAMES) | ||
4289 | + count = TIMBLOGIW_NUM_FRAMES; | ||
4290 | + | ||
4291 | + lw->num_frames = count; | ||
4292 | + while (lw->num_frames > 0) { | ||
4293 | + buff = vmalloc_32(lw->num_frames * imagesize); | ||
4294 | + if (buff) { | ||
4295 | + memset(buff, 0, lw->num_frames * imagesize); | ||
4296 | + break; | ||
4297 | + } | ||
4298 | + lw->num_frames--; | ||
4299 | + } | ||
4300 | + | ||
4301 | + for (i = 0; i < lw->num_frames; i++) { | ||
4302 | + lw->frame[i].bufmem = buff + i * imagesize; | ||
4303 | + lw->frame[i].buf.index = i; | ||
4304 | + lw->frame[i].buf.m.offset = i * imagesize; | ||
4305 | + lw->frame[i].buf.length = timblogiw_frame_size(lw->cur_norm); | ||
4306 | + lw->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
4307 | + lw->frame[i].buf.sequence = 0; | ||
4308 | + lw->frame[i].buf.field = V4L2_FIELD_NONE; | ||
4309 | + lw->frame[i].buf.memory = V4L2_MEMORY_MMAP; | ||
4310 | + lw->frame[i].buf.flags = 0; | ||
4311 | + } | ||
4312 | + | ||
4313 | + lw->dma.curr = 0; | ||
4314 | + lw->dma.filled = NULL; | ||
4315 | + return lw->num_frames; | ||
4316 | +err: | ||
4317 | + timblogiw_release_buffers(lw); | ||
4318 | + | ||
4319 | + return 0; | ||
4320 | +} | ||
4321 | + | ||
4322 | +static void timblogiw_release_buffers(struct timblogiw *lw) | ||
4323 | +{ | ||
4324 | + dbg("%s\n", __func__); | ||
4325 | + | ||
4326 | + if (lw->frame[0].bufmem != NULL) { | ||
4327 | + vfree(lw->frame[0].bufmem); | ||
4328 | + lw->frame[0].bufmem = NULL; | ||
4329 | + } | ||
4330 | + | ||
4331 | + if (lw->dma.transfer[0].handle) | ||
4332 | + dma_unmap_single(lw->dev, lw->dma.transfer[0].handle, | ||
4333 | + DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
4334 | + | ||
4335 | + if (lw->dma.transfer[1].handle) | ||
4336 | + dma_unmap_single(lw->dev, lw->dma.transfer[1].handle, | ||
4337 | + DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
4338 | + | ||
4339 | + if (lw->dma.transfer[0].buf != NULL) | ||
4340 | + kfree(lw->dma.transfer[0].buf); | ||
4341 | + lw->dma.transfer[0].buf = NULL; | ||
4342 | + | ||
4343 | + if (lw->dma.transfer[1].buf != NULL) | ||
4344 | + kfree(lw->dma.transfer[1].buf); | ||
4345 | + lw->dma.transfer[1].buf = NULL; | ||
4346 | + | ||
4347 | + if (lw->dma.transfer[0].desc != NULL) | ||
4348 | + timbdma_free_desc(lw->dma.transfer[0].desc); | ||
4349 | + lw->dma.transfer[0].desc = NULL; | ||
4350 | + | ||
4351 | + if (lw->dma.transfer[1].desc != NULL) | ||
4352 | + timbdma_free_desc(lw->dma.transfer[1].desc); | ||
4353 | + lw->dma.transfer[1].desc = NULL; | ||
4354 | + | ||
4355 | + | ||
4356 | + lw->num_frames = TIMBLOGIW_NUM_FRAMES; | ||
4357 | +} | ||
4358 | + | ||
4359 | +/* IOCTL functions */ | ||
4360 | + | ||
4361 | +static int timblogiw_g_fmt(struct file *file, void *priv, | ||
4362 | + struct v4l2_format *format) | ||
4363 | +{ | ||
4364 | + struct video_device *vdev = video_devdata(file); | ||
4365 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4366 | + | ||
4367 | + dbg("%s\n", __func__); | ||
4368 | + | ||
4369 | + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
4370 | + return -EINVAL; | ||
4371 | + | ||
4372 | + format->fmt.pix.width = lw->cur_norm->width; | ||
4373 | + format->fmt.pix.height = lw->cur_norm->height; | ||
4374 | + format->fmt.pix.pixelformat = TIMBLOGIW_VIDEO_FORMAT; | ||
4375 | + format->fmt.pix.bytesperline = timblogiw_bytes_per_line(lw->cur_norm); | ||
4376 | + format->fmt.pix.sizeimage = timblogiw_frame_size(lw->cur_norm); | ||
4377 | + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; | ||
4378 | + format->fmt.pix.field = V4L2_FIELD_NONE; | ||
4379 | + return 0; | ||
4380 | +} | ||
4381 | + | ||
4382 | +static int timblogiw_try_fmt(struct file *file, void *priv, | ||
4383 | + struct v4l2_format *format) | ||
4384 | +{ | ||
4385 | + struct video_device *vdev = video_devdata(file); | ||
4386 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4387 | + struct v4l2_pix_format *pix = &format->fmt.pix; | ||
4388 | + | ||
4389 | + dbg("%s - width=%d, height=%d, pixelformat=%d, field=%d\n" | ||
4390 | + "bytes per line %d, size image: %d, colorspace: %d\n", | ||
4391 | + __func__, | ||
4392 | + pix->width, pix->height, pix->pixelformat, pix->field, | ||
4393 | + pix->bytesperline, pix->sizeimage, pix->colorspace); | ||
4394 | + | ||
4395 | + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
4396 | + return -EINVAL; | ||
4397 | + | ||
4398 | + if (pix->field != V4L2_FIELD_NONE) | ||
4399 | + return -EINVAL; | ||
4400 | + | ||
4401 | + if (pix->pixelformat != TIMBLOGIW_VIDEO_FORMAT) | ||
4402 | + return -EINVAL; | ||
4403 | + | ||
4404 | + if ((lw->cur_norm->height != pix->height) || | ||
4405 | + (lw->cur_norm->width != pix->width)) { | ||
4406 | + pix->width = lw->cur_norm->width; | ||
4407 | + pix->height = lw->cur_norm->height; | ||
4408 | + } | ||
4409 | + | ||
4410 | + return 0; | ||
4411 | +} | ||
4412 | + | ||
4413 | +static int timblogiw_querycap(struct file *file, void *priv, | ||
4414 | + struct v4l2_capability *cap) | ||
4415 | +{ | ||
4416 | + dbg("%s\n", __func__); | ||
4417 | + memset(cap, 0, sizeof(*cap)); | ||
4418 | + strncpy(cap->card, "Timberdale Video", sizeof(cap->card)-1); | ||
4419 | + strncpy(cap->driver, "Timblogiw", sizeof(cap->card)-1); | ||
4420 | + cap->version = TIMBLOGIW_VERSION_CODE; | ||
4421 | + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | | ||
4422 | + V4L2_CAP_STREAMING; | ||
4423 | + | ||
4424 | + return 0; | ||
4425 | +} | ||
4426 | + | ||
4427 | +static int timblogiw_enum_fmt(struct file *file, void *priv, | ||
4428 | + struct v4l2_fmtdesc *fmt) | ||
4429 | +{ | ||
4430 | + dbg("%s, index: %d\n", __func__, fmt->index); | ||
4431 | + | ||
4432 | + if (fmt->index != 0) | ||
4433 | + return -EINVAL; | ||
4434 | + memset(fmt, 0, sizeof(*fmt)); | ||
4435 | + fmt->index = 0; | ||
4436 | + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
4437 | + strncpy(fmt->description, "4:2:2, packed, YUYV", | ||
4438 | + sizeof(fmt->description)-1); | ||
4439 | + fmt->pixelformat = TIMBLOGIW_VIDEO_FORMAT; | ||
4440 | + memset(fmt->reserved, 0, sizeof(fmt->reserved)); | ||
4441 | + | ||
4442 | + return 0; | ||
4443 | +} | ||
4444 | + | ||
4445 | +static int timblogiw_reqbufs(struct file *file, void *priv, | ||
4446 | + struct v4l2_requestbuffers *rb) | ||
4447 | +{ | ||
4448 | + struct video_device *vdev = video_devdata(file); | ||
4449 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4450 | + | ||
4451 | + dbg("%s\n", __func__); | ||
4452 | + | ||
4453 | + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
4454 | + rb->memory != V4L2_MEMORY_MMAP) | ||
4455 | + return -EINVAL; | ||
4456 | + | ||
4457 | + timblogiw_empty_framequeues(lw); | ||
4458 | + | ||
4459 | + timblogiw_release_buffers(lw); | ||
4460 | + if (rb->count) | ||
4461 | + rb->count = timblogiw_request_buffers(lw, rb->count); | ||
4462 | + | ||
4463 | + dbg("%s - VIDIOC_REQBUFS: io method is mmap. num bufs %i\n", | ||
4464 | + __func__, rb->count); | ||
4465 | + | ||
4466 | + return 0; | ||
4467 | +} | ||
4468 | + | ||
4469 | +static int timblogiw_querybuf(struct file *file, void *priv, | ||
4470 | + struct v4l2_buffer *b) | ||
4471 | +{ | ||
4472 | + struct video_device *vdev = video_devdata(file); | ||
4473 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4474 | + | ||
4475 | + dbg("%s\n", __func__); | ||
4476 | + | ||
4477 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
4478 | + b->index >= lw->num_frames) | ||
4479 | + return -EINVAL; | ||
4480 | + | ||
4481 | + memcpy(b, &lw->frame[b->index].buf, sizeof(*b)); | ||
4482 | + | ||
4483 | + if (lw->frame[b->index].vma_use_count) | ||
4484 | + b->flags |= V4L2_BUF_FLAG_MAPPED; | ||
4485 | + | ||
4486 | + if (lw->frame[b->index].state == F_DONE) | ||
4487 | + b->flags |= V4L2_BUF_FLAG_DONE; | ||
4488 | + else if (lw->frame[b->index].state != F_UNUSED) | ||
4489 | + b->flags |= V4L2_BUF_FLAG_QUEUED; | ||
4490 | + | ||
4491 | + return 0; | ||
4492 | +} | ||
4493 | + | ||
4494 | +static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) | ||
4495 | +{ | ||
4496 | + struct video_device *vdev = video_devdata(file); | ||
4497 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4498 | + unsigned long lock_flags; | ||
4499 | + | ||
4500 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || | ||
4501 | + b->index >= lw->num_frames) | ||
4502 | + return -EINVAL; | ||
4503 | + | ||
4504 | + if (lw->frame[b->index].state != F_UNUSED) | ||
4505 | + return -EAGAIN; | ||
4506 | + | ||
4507 | + if (!lw->frame[b->index].bufmem) | ||
4508 | + return -EINVAL; | ||
4509 | + | ||
4510 | + if (b->memory != V4L2_MEMORY_MMAP) | ||
4511 | + return -EINVAL; | ||
4512 | + | ||
4513 | + lw->frame[b->index].state = F_QUEUED; | ||
4514 | + | ||
4515 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
4516 | + list_add_tail(&lw->frame[b->index].frame, &lw->inqueue); | ||
4517 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
4518 | + | ||
4519 | + return 0; | ||
4520 | +} | ||
4521 | + | ||
4522 | +static int timblogiw_dqbuf(struct file *file, void *priv, | ||
4523 | + struct v4l2_buffer *b) | ||
4524 | +{ | ||
4525 | + struct video_device *vdev = video_devdata(file); | ||
4526 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4527 | + struct timblogiw_frame *f; | ||
4528 | + unsigned long lock_flags; | ||
4529 | + int ret = 0; | ||
4530 | + | ||
4531 | + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
4532 | + dbg("%s - VIDIOC_DQBUF, illegal buf type!\n", | ||
4533 | + __func__); | ||
4534 | + return -EINVAL; | ||
4535 | + } | ||
4536 | + | ||
4537 | + if (list_empty(&lw->outqueue)) { | ||
4538 | + if (file->f_flags & O_NONBLOCK) | ||
4539 | + return -EAGAIN; | ||
4540 | + | ||
4541 | + ret = wait_event_interruptible(lw->wait_frame, | ||
4542 | + !list_empty(&lw->outqueue)); | ||
4543 | + if (ret) | ||
4544 | + return ret; | ||
4545 | + } | ||
4546 | + | ||
4547 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
4548 | + f = list_entry(lw->outqueue.next, | ||
4549 | + struct timblogiw_frame, frame); | ||
4550 | + list_del(lw->outqueue.next); | ||
4551 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
4552 | + | ||
4553 | + f->state = F_UNUSED; | ||
4554 | + memcpy(b, &f->buf, sizeof(*b)); | ||
4555 | + | ||
4556 | + if (f->vma_use_count) | ||
4557 | + b->flags |= V4L2_BUF_FLAG_MAPPED; | ||
4558 | + | ||
4559 | + return 0; | ||
4560 | +} | ||
4561 | + | ||
4562 | +static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) | ||
4563 | +{ | ||
4564 | + struct video_device *vdev = video_devdata(file); | ||
4565 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4566 | + | ||
4567 | + dbg("%s\n", __func__); | ||
4568 | + | ||
4569 | + *std = lw->cur_norm->std; | ||
4570 | + return 0; | ||
4571 | +} | ||
4572 | + | ||
4573 | +static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) | ||
4574 | +{ | ||
4575 | + struct video_device *vdev = video_devdata(file); | ||
4576 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4577 | + int err; | ||
4578 | + | ||
4579 | + dbg("%s\n", __func__); | ||
4580 | + | ||
4581 | + err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std); | ||
4582 | + if (!err) | ||
4583 | + lw->cur_norm = timblogiw_get_norm(*std); | ||
4584 | + | ||
4585 | + return err; | ||
4586 | +} | ||
4587 | + | ||
4588 | +static int timblogiw_enuminput(struct file *file, void *priv, | ||
4589 | + struct v4l2_input *inp) | ||
4590 | +{ | ||
4591 | + dbg("%s\n", __func__); | ||
4592 | + | ||
4593 | + if (inp->index != 0) | ||
4594 | + return -EINVAL; | ||
4595 | + | ||
4596 | + memset(inp, 0, sizeof(*inp)); | ||
4597 | + inp->index = 0; | ||
4598 | + | ||
4599 | + strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); | ||
4600 | + inp->type = V4L2_INPUT_TYPE_CAMERA; | ||
4601 | + inp->std = V4L2_STD_ALL; | ||
4602 | + | ||
4603 | + return 0; | ||
4604 | +} | ||
4605 | + | ||
4606 | +static int timblogiw_g_input(struct file *file, void *priv, | ||
4607 | + unsigned int *input) | ||
4608 | +{ | ||
4609 | + dbg("%s\n", __func__); | ||
4610 | + | ||
4611 | + *input = 0; | ||
4612 | + | ||
4613 | + return 0; | ||
4614 | +} | ||
4615 | + | ||
4616 | +static int timblogiw_s_input(struct file *file, void *priv, unsigned int input) | ||
4617 | +{ | ||
4618 | + dbg("%s\n", __func__); | ||
4619 | + | ||
4620 | + if (input != 0) | ||
4621 | + return -EINVAL; | ||
4622 | + return 0; | ||
4623 | +} | ||
4624 | + | ||
4625 | +static int timblogiw_streamon(struct file *file, void *priv, unsigned int type) | ||
4626 | +{ | ||
4627 | + struct video_device *vdev = video_devdata(file); | ||
4628 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4629 | + struct timblogiw_frame *f; | ||
4630 | + | ||
4631 | + dbg("%s\n", __func__); | ||
4632 | + | ||
4633 | + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
4634 | + dbg("%s - No capture device\n", __func__); | ||
4635 | + return -EINVAL; | ||
4636 | + } | ||
4637 | + | ||
4638 | + if (list_empty(&lw->inqueue)) { | ||
4639 | + dbg("%s - inqueue is empty\n", __func__); | ||
4640 | + return -EINVAL; | ||
4641 | + } | ||
4642 | + | ||
4643 | + if (lw->stream == STREAM_ON) | ||
4644 | + return 0; | ||
4645 | + | ||
4646 | + lw->stream = STREAM_ON; | ||
4647 | + | ||
4648 | + f = list_entry(lw->inqueue.next, | ||
4649 | + struct timblogiw_frame, frame); | ||
4650 | + | ||
4651 | + dbg("%s - f size: %d, bpr: %d, dma addr: %x\n", __func__, | ||
4652 | + timblogiw_frame_size(lw->cur_norm), | ||
4653 | + timblogiw_bytes_per_line(lw->cur_norm), | ||
4654 | + (unsigned int)lw->dma.transfer[lw->dma.curr].handle); | ||
4655 | + | ||
4656 | + __timblogiw_start_dma(lw); | ||
4657 | + | ||
4658 | + return 0; | ||
4659 | +} | ||
4660 | + | ||
4661 | +static void timblogiw_stopstream(struct timblogiw *lw) | ||
4662 | +{ | ||
4663 | + if (lw->stream == STREAM_ON) { | ||
4664 | + /* The FPGA might be busy copying the current frame, we have | ||
4665 | + * to wait for the frame to finish | ||
4666 | + */ | ||
4667 | + unsigned long lock_flags; | ||
4668 | + | ||
4669 | + init_completion(&lw->irq_done); | ||
4670 | + | ||
4671 | + spin_lock_irqsave(&lw->queue_lock, lock_flags); | ||
4672 | + lw->stream = STREAM_OFF; | ||
4673 | + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); | ||
4674 | + | ||
4675 | + wait_for_completion_timeout(&lw->irq_done, | ||
4676 | + msecs_to_jiffies(100)); | ||
4677 | + } | ||
4678 | +} | ||
4679 | + | ||
4680 | +static int timblogiw_streamoff(struct file *file, void *priv, | ||
4681 | + unsigned int type) | ||
4682 | +{ | ||
4683 | + struct video_device *vdev = video_devdata(file); | ||
4684 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4685 | + | ||
4686 | + dbg("%s\n", __func__); | ||
4687 | + | ||
4688 | + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
4689 | + return -EINVAL; | ||
4690 | + | ||
4691 | + timblogiw_stopstream(lw); | ||
4692 | + | ||
4693 | + timblogiw_empty_framequeues(lw); | ||
4694 | + | ||
4695 | + return 0; | ||
4696 | +} | ||
4697 | + | ||
4698 | +static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std) | ||
4699 | +{ | ||
4700 | + struct video_device *vdev = video_devdata(file); | ||
4701 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4702 | + | ||
4703 | + dbg("%s\n", __func__); | ||
4704 | + | ||
4705 | + return v4l2_subdev_call(lw->sd_enc, video, querystd, std); | ||
4706 | +} | ||
4707 | + | ||
4708 | +static int timblogiw_enum_framesizes(struct file *file, void *priv, | ||
4709 | + struct v4l2_frmsizeenum *fsize) | ||
4710 | +{ | ||
4711 | + struct video_device *vdev = video_devdata(file); | ||
4712 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4713 | + | ||
4714 | + dbg("%s - index: %d, format: %d\n", __func__, | ||
4715 | + fsize->index, fsize->pixel_format); | ||
4716 | + | ||
4717 | + if ((fsize->index != 0) || | ||
4718 | + (fsize->pixel_format != TIMBLOGIW_VIDEO_FORMAT)) | ||
4719 | + return -EINVAL; | ||
4720 | + | ||
4721 | + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | ||
4722 | + fsize->discrete.width = lw->cur_norm->width; | ||
4723 | + fsize->discrete.height = lw->cur_norm->height; | ||
4724 | + | ||
4725 | + return 0; | ||
4726 | +} | ||
4727 | + | ||
4728 | +struct find_addr_arg { | ||
4729 | + char const *name; | ||
4730 | + struct i2c_client *client; | ||
4731 | +}; | ||
4732 | + | ||
4733 | +static int find_name(struct device *dev, void *argp) | ||
4734 | +{ | ||
4735 | + struct find_addr_arg *arg = (struct find_addr_arg *)argp; | ||
4736 | + struct i2c_client *client = i2c_verify_client(dev); | ||
4737 | + | ||
4738 | + if (client && !strcmp(arg->name, client->name) && client->driver) | ||
4739 | + arg->client = client; | ||
4740 | + | ||
4741 | + return 0; | ||
4742 | +} | ||
4743 | + | ||
4744 | +static struct i2c_client *find_client(struct i2c_adapter *adapt, | ||
4745 | + const char *name) | ||
4746 | +{ | ||
4747 | + struct find_addr_arg find_arg; | ||
4748 | + /* now find the client */ | ||
4749 | +#ifdef MODULE | ||
4750 | + request_module(name); | ||
4751 | +#endif | ||
4752 | + /* code for finding the I2C child */ | ||
4753 | + find_arg.name = name; | ||
4754 | + find_arg.client = NULL; | ||
4755 | + device_for_each_child(&adapt->dev, &find_arg, find_name); | ||
4756 | + return find_arg.client; | ||
4757 | +} | ||
4758 | + | ||
4759 | +/******************************* | ||
4760 | + * Device Operations functions * | ||
4761 | + *******************************/ | ||
4762 | + | ||
4763 | +static int timblogiw_open(struct file *file) | ||
4764 | +{ | ||
4765 | + struct video_device *vdev = video_devdata(file); | ||
4766 | + struct timblogiw *lw = video_get_drvdata(vdev); | ||
4767 | + v4l2_std_id std = V4L2_STD_UNKNOWN; | ||
4768 | + int err = 0; | ||
4769 | + | ||
4770 | + dbg("%s -\n", __func__); | ||
4771 | + | ||
4772 | + mutex_init(&lw->fileop_lock); | ||
4773 | + spin_lock_init(&lw->queue_lock); | ||
4774 | + init_waitqueue_head(&lw->wait_frame); | ||
4775 | + | ||
4776 | + mutex_lock(&lw->lock); | ||
4777 | + | ||
4778 | + if (!lw->sd_enc) { | ||
4779 | + struct i2c_adapter *adapt; | ||
4780 | + struct i2c_client *encoder; | ||
4781 | + | ||
4782 | + /* find the video decoder */ | ||
4783 | + adapt = i2c_get_adapter(lw->pdata.i2c_adapter); | ||
4784 | + if (!adapt) { | ||
4785 | + printk(KERN_ERR DRIVER_NAME": No I2C bus\n"); | ||
4786 | + err = -ENODEV; | ||
4787 | + goto out; | ||
4788 | + } | ||
4789 | + | ||
4790 | + /* now find the encoder */ | ||
4791 | + encoder = find_client(adapt, lw->pdata.encoder); | ||
4792 | + | ||
4793 | + i2c_put_adapter(adapt); | ||
4794 | + | ||
4795 | + if (!encoder) { | ||
4796 | + printk(KERN_ERR DRIVER_NAME": Failed to get encoder\n"); | ||
4797 | + err = -ENODEV; | ||
4798 | + goto out; | ||
4799 | + } | ||
4800 | + | ||
4801 | + lw->sd_enc = i2c_get_clientdata(encoder); | ||
4802 | + lw->enc_owner = lw->sd_enc->owner; | ||
4803 | + /* Lock the module */ | ||
4804 | + if (!try_module_get(lw->enc_owner)) { | ||
4805 | + lw->sd_enc = NULL; | ||
4806 | + err = -ENODEV; | ||
4807 | + goto out; | ||
4808 | + } | ||
4809 | + } | ||
4810 | + | ||
4811 | + timblogiw_querystd(file, NULL, &std); | ||
4812 | + lw->cur_norm = timblogiw_get_norm(std); | ||
4813 | + | ||
4814 | + file->private_data = lw; | ||
4815 | + lw->stream = STREAM_OFF; | ||
4816 | + lw->num_frames = TIMBLOGIW_NUM_FRAMES; | ||
4817 | + | ||
4818 | + timblogiw_empty_framequeues(lw); | ||
4819 | + timbdma_set_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, | ||
4820 | + timblogiw_isr, (void *)lw); | ||
4821 | + | ||
4822 | +out: | ||
4823 | + mutex_unlock(&lw->lock); | ||
4824 | + | ||
4825 | + return err; | ||
4826 | +} | ||
4827 | + | ||
4828 | +static int timblogiw_close(struct file *file) | ||
4829 | +{ | ||
4830 | + struct timblogiw *lw = file->private_data; | ||
4831 | + | ||
4832 | + dbg("%s - entry\n", __func__); | ||
4833 | + | ||
4834 | + mutex_lock(&lw->lock); | ||
4835 | + | ||
4836 | + timblogiw_stopstream(lw); | ||
4837 | + | ||
4838 | + timbdma_set_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, NULL, | ||
4839 | + NULL); | ||
4840 | + timblogiw_release_buffers(lw); | ||
4841 | + | ||
4842 | + mutex_unlock(&lw->lock); | ||
4843 | + return 0; | ||
4844 | +} | ||
4845 | + | ||
4846 | +static ssize_t timblogiw_read(struct file *file, char __user *data, | ||
4847 | + size_t count, loff_t *ppos) | ||
4848 | +{ | ||
4849 | + dbg("%s - read request\n", __func__); | ||
4850 | + return -EINVAL; | ||
4851 | +} | ||
4852 | + | ||
4853 | +static void timblogiw_vm_open(struct vm_area_struct *vma) | ||
4854 | +{ | ||
4855 | + struct timblogiw_frame *f = vma->vm_private_data; | ||
4856 | + f->vma_use_count++; | ||
4857 | +} | ||
4858 | + | ||
4859 | +static void timblogiw_vm_close(struct vm_area_struct *vma) | ||
4860 | +{ | ||
4861 | + struct timblogiw_frame *f = vma->vm_private_data; | ||
4862 | + f->vma_use_count--; | ||
4863 | +} | ||
4864 | + | ||
4865 | +static struct vm_operations_struct timblogiw_vm_ops = { | ||
4866 | + .open = timblogiw_vm_open, | ||
4867 | + .close = timblogiw_vm_close, | ||
4868 | +}; | ||
4869 | + | ||
4870 | +static int timblogiw_mmap(struct file *filp, struct vm_area_struct *vma) | ||
4871 | +{ | ||
4872 | + unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; | ||
4873 | + void *pos; | ||
4874 | + u32 i; | ||
4875 | + int ret = -EINVAL; | ||
4876 | + | ||
4877 | + struct timblogiw *lw = filp->private_data; | ||
4878 | + dbg("%s\n", __func__); | ||
4879 | + | ||
4880 | + if (mutex_lock_interruptible(&lw->fileop_lock)) | ||
4881 | + return -ERESTARTSYS; | ||
4882 | + | ||
4883 | + if (!(vma->vm_flags & VM_WRITE) || | ||
4884 | + size != PAGE_ALIGN(lw->frame[0].buf.length)) | ||
4885 | + goto error_unlock; | ||
4886 | + | ||
4887 | + for (i = 0; i < lw->num_frames; i++) | ||
4888 | + if ((lw->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) | ||
4889 | + break; | ||
4890 | + | ||
4891 | + if (i == lw->num_frames) { | ||
4892 | + dbg("%s - user supplied mapping address is out of range\n", | ||
4893 | + __func__); | ||
4894 | + goto error_unlock; | ||
4895 | + } | ||
4896 | + | ||
4897 | + vma->vm_flags |= VM_IO; | ||
4898 | + vma->vm_flags |= VM_RESERVED; /* Do not swap out this VMA */ | ||
4899 | + | ||
4900 | + pos = lw->frame[i].bufmem; | ||
4901 | + while (size > 0) { /* size is page-aligned */ | ||
4902 | + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { | ||
4903 | + dbg("%s - vm_insert_page failed\n", __func__); | ||
4904 | + ret = -EAGAIN; | ||
4905 | + goto error_unlock; | ||
4906 | + } | ||
4907 | + start += PAGE_SIZE; | ||
4908 | + pos += PAGE_SIZE; | ||
4909 | + size -= PAGE_SIZE; | ||
4910 | + } | ||
4911 | + | ||
4912 | + vma->vm_ops = &timblogiw_vm_ops; | ||
4913 | + vma->vm_private_data = &lw->frame[i]; | ||
4914 | + timblogiw_vm_open(vma); | ||
4915 | + ret = 0; | ||
4916 | + | ||
4917 | +error_unlock: | ||
4918 | + mutex_unlock(&lw->fileop_lock); | ||
4919 | + return ret; | ||
4920 | +} | ||
4921 | + | ||
4922 | + | ||
4923 | +void timblogiw_vdev_release(struct video_device *vdev) | ||
4924 | +{ | ||
4925 | + kfree(vdev); | ||
4926 | +} | ||
4927 | + | ||
4928 | +static const struct v4l2_ioctl_ops timblogiw_ioctl_ops = { | ||
4929 | + .vidioc_querycap = timblogiw_querycap, | ||
4930 | + .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt, | ||
4931 | + .vidioc_g_fmt_vid_cap = timblogiw_g_fmt, | ||
4932 | + .vidioc_try_fmt_vid_cap = timblogiw_try_fmt, | ||
4933 | + .vidioc_s_fmt_vid_cap = timblogiw_try_fmt, | ||
4934 | + .vidioc_reqbufs = timblogiw_reqbufs, | ||
4935 | + .vidioc_querybuf = timblogiw_querybuf, | ||
4936 | + .vidioc_qbuf = timblogiw_qbuf, | ||
4937 | + .vidioc_dqbuf = timblogiw_dqbuf, | ||
4938 | + .vidioc_g_std = timblogiw_g_std, | ||
4939 | + .vidioc_s_std = timblogiw_s_std, | ||
4940 | + .vidioc_enum_input = timblogiw_enuminput, | ||
4941 | + .vidioc_g_input = timblogiw_g_input, | ||
4942 | + .vidioc_s_input = timblogiw_s_input, | ||
4943 | + .vidioc_streamon = timblogiw_streamon, | ||
4944 | + .vidioc_streamoff = timblogiw_streamoff, | ||
4945 | + .vidioc_querystd = timblogiw_querystd, | ||
4946 | + .vidioc_enum_framesizes = timblogiw_enum_framesizes, | ||
4947 | +}; | ||
4948 | + | ||
4949 | +static const struct v4l2_file_operations timblogiw_fops = { | ||
4950 | + .owner = THIS_MODULE, | ||
4951 | + .open = timblogiw_open, | ||
4952 | + .release = timblogiw_close, | ||
4953 | + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ | ||
4954 | + .mmap = timblogiw_mmap, | ||
4955 | + .read = timblogiw_read, | ||
4956 | +}; | ||
4957 | + | ||
4958 | +static const struct video_device timblogiw_template = { | ||
4959 | + .name = TIMBLOGIWIN_NAME, | ||
4960 | + .fops = &timblogiw_fops, | ||
4961 | + .ioctl_ops = &timblogiw_ioctl_ops, | ||
4962 | + .release = &timblogiw_vdev_release, | ||
4963 | + .minor = -1, | ||
4964 | + .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC | ||
4965 | +}; | ||
4966 | + | ||
4967 | +static int timblogiw_probe(struct platform_device *dev) | ||
4968 | +{ | ||
4969 | + int err; | ||
4970 | + struct timblogiw *lw = NULL; | ||
4971 | + struct resource *iomem; | ||
4972 | + struct timb_video_platform_data *pdata = dev->dev.platform_data; | ||
4973 | + | ||
4974 | + if (!pdata) { | ||
4975 | + printk(KERN_ERR DRIVER_NAME": Platform data missing\n"); | ||
4976 | + err = -EINVAL; | ||
4977 | + goto err_mem; | ||
4978 | + } | ||
4979 | + | ||
4980 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
4981 | + if (!iomem) { | ||
4982 | + err = -EINVAL; | ||
4983 | + goto err_mem; | ||
4984 | + } | ||
4985 | + | ||
4986 | + lw = kzalloc(sizeof(*lw), GFP_KERNEL); | ||
4987 | + if (!lw) { | ||
4988 | + err = -ENOMEM; | ||
4989 | + goto err_mem; | ||
4990 | + } | ||
4991 | + | ||
4992 | + if (dev->dev.parent) | ||
4993 | + lw->dev = dev->dev.parent; | ||
4994 | + else | ||
4995 | + lw->dev = &dev->dev; | ||
4996 | + | ||
4997 | + memcpy(&lw->pdata, pdata, sizeof(lw->pdata)); | ||
4998 | + | ||
4999 | + mutex_init(&lw->lock); | ||
5000 | + | ||
5001 | + lw->video_dev = video_device_alloc(); | ||
5002 | + if (!lw->video_dev) { | ||
5003 | + err = -ENOMEM; | ||
5004 | + goto err_mem; | ||
5005 | + } | ||
5006 | + *lw->video_dev = timblogiw_template; | ||
5007 | + | ||
5008 | + err = video_register_device(lw->video_dev, VFL_TYPE_GRABBER, 0); | ||
5009 | + if (err) { | ||
5010 | + printk(KERN_ALERT DRIVER_NAME": Error reg video\n"); | ||
5011 | + goto err_request; | ||
5012 | + } | ||
5013 | + | ||
5014 | + tasklet_init(&lw->tasklet, timblogiw_handleframe, (unsigned long)lw); | ||
5015 | + | ||
5016 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
5017 | + DRIVER_NAME)) { | ||
5018 | + err = -EBUSY; | ||
5019 | + goto err_request; | ||
5020 | + } | ||
5021 | + | ||
5022 | + lw->membase = ioremap(iomem->start, resource_size(iomem)); | ||
5023 | + if (!lw->membase) { | ||
5024 | + err = -ENOMEM; | ||
5025 | + goto err_ioremap; | ||
5026 | + } | ||
5027 | + | ||
5028 | + platform_set_drvdata(dev, lw); | ||
5029 | + video_set_drvdata(lw->video_dev, lw); | ||
5030 | + | ||
5031 | + return 0; | ||
5032 | + | ||
5033 | +err_ioremap: | ||
5034 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
5035 | +err_request: | ||
5036 | + if (-1 != lw->video_dev->minor) | ||
5037 | + video_unregister_device(lw->video_dev); | ||
5038 | + else | ||
5039 | + video_device_release(lw->video_dev); | ||
5040 | +err_mem: | ||
5041 | + kfree(lw); | ||
5042 | + printk(KERN_ERR DRIVER_NAME ": Failed to register: %d\n", err); | ||
5043 | + | ||
5044 | + return err; | ||
5045 | +} | ||
5046 | + | ||
5047 | +static int timblogiw_remove(struct platform_device *dev) | ||
5048 | +{ | ||
5049 | + struct timblogiw *lw = platform_get_drvdata(dev); | ||
5050 | + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
5051 | + | ||
5052 | + if (-1 != lw->video_dev->minor) | ||
5053 | + video_unregister_device(lw->video_dev); | ||
5054 | + else | ||
5055 | + video_device_release(lw->video_dev); | ||
5056 | + | ||
5057 | + if (lw->sd_enc) | ||
5058 | + module_put(lw->enc_owner); | ||
5059 | + tasklet_kill(&lw->tasklet); | ||
5060 | + iounmap(lw->membase); | ||
5061 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
5062 | + kfree(lw); | ||
5063 | + | ||
5064 | + return 0; | ||
5065 | +} | ||
5066 | + | ||
5067 | +static struct platform_driver timblogiw_platform_driver = { | ||
5068 | + .driver = { | ||
5069 | + .name = DRIVER_NAME, | ||
5070 | + .owner = THIS_MODULE, | ||
5071 | + }, | ||
5072 | + .probe = timblogiw_probe, | ||
5073 | + .remove = timblogiw_remove, | ||
5074 | +}; | ||
5075 | + | ||
5076 | +/*--------------------------------------------------------------------------*/ | ||
5077 | + | ||
5078 | +static int __init timblogiw_init(void) | ||
5079 | +{ | ||
5080 | + return platform_driver_register(&timblogiw_platform_driver); | ||
5081 | +} | ||
5082 | + | ||
5083 | +static void __exit timblogiw_exit(void) | ||
5084 | +{ | ||
5085 | + platform_driver_unregister(&timblogiw_platform_driver); | ||
5086 | +} | ||
5087 | + | ||
5088 | +module_init(timblogiw_init); | ||
5089 | +module_exit(timblogiw_exit); | ||
5090 | + | ||
5091 | +MODULE_DESCRIPTION("Timberdale Video In driver"); | ||
5092 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
5093 | +MODULE_LICENSE("GPL v2"); | ||
5094 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
5095 | + | ||
5096 | diff -uNr linux-2.6.31/drivers/media/video/timblogiw.h linux-2.6.31.new/drivers/media/video/timblogiw.h | ||
5097 | --- linux-2.6.31/drivers/media/video/timblogiw.h 1969-12-31 16:00:00.000000000 -0800 | ||
5098 | +++ linux-2.6.31.new/drivers/media/video/timblogiw.h 2009-10-23 11:17:28.000000000 -0700 | ||
5099 | @@ -0,0 +1,96 @@ | ||
5100 | +/* | ||
5101 | + * timblogiw.h timberdale FPGA LogiWin Video In driver defines | ||
5102 | + * Copyright (c) 2009 Intel Corporation | ||
5103 | + * | ||
5104 | + * This program is free software; you can redistribute it and/or modify | ||
5105 | + * it under the terms of the GNU General Public License version 2 as | ||
5106 | + * published by the Free Software Foundation. | ||
5107 | + * | ||
5108 | + * This program is distributed in the hope that it will be useful, | ||
5109 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5110 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5111 | + * GNU General Public License for more details. | ||
5112 | + * | ||
5113 | + * You should have received a copy of the GNU General Public License | ||
5114 | + * along with this program; if not, write to the Free Software | ||
5115 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5116 | + */ | ||
5117 | + | ||
5118 | +/* Supports: | ||
5119 | + * Timberdale FPGA LogiWin Video In | ||
5120 | + */ | ||
5121 | + | ||
5122 | +#ifndef _TIMBLOGIW_H | ||
5123 | +#define _TIMBLOGIW_H | ||
5124 | + | ||
5125 | +#include <linux/interrupt.h> | ||
5126 | +#include <media/timb_video.h> | ||
5127 | +#include <linux/completion.h> | ||
5128 | + | ||
5129 | +#define TIMBLOGIWIN_NAME "Timberdale Video-In" | ||
5130 | + | ||
5131 | +#define TIMBLOGIW_NUM_FRAMES 10 | ||
5132 | + | ||
5133 | + | ||
5134 | +enum timblogiw_stream_state { | ||
5135 | + STREAM_OFF, | ||
5136 | + STREAM_ON, | ||
5137 | +}; | ||
5138 | + | ||
5139 | +enum timblogiw_frame_state { | ||
5140 | + F_UNUSED = 0, | ||
5141 | + F_QUEUED, | ||
5142 | + F_DONE, | ||
5143 | +}; | ||
5144 | + | ||
5145 | +struct timblogiw_frame { | ||
5146 | + void *bufmem; | ||
5147 | + struct v4l2_buffer buf; | ||
5148 | + enum timblogiw_frame_state state; | ||
5149 | + struct list_head frame; | ||
5150 | + unsigned long vma_use_count; | ||
5151 | +}; | ||
5152 | + | ||
5153 | +struct timblogiw_tvnorm { | ||
5154 | + v4l2_std_id std; | ||
5155 | + u16 width; | ||
5156 | + u16 height; | ||
5157 | +}; | ||
5158 | + | ||
5159 | + | ||
5160 | + | ||
5161 | +struct timbdma_transfer { | ||
5162 | + dma_addr_t handle; | ||
5163 | + void *buf; | ||
5164 | + void *desc; | ||
5165 | +}; | ||
5166 | + | ||
5167 | +struct timblogiw_dma_control { | ||
5168 | + struct timbdma_transfer transfer[2]; | ||
5169 | + struct timbdma_transfer *filled; | ||
5170 | + int curr; | ||
5171 | +}; | ||
5172 | + | ||
5173 | +struct timblogiw { | ||
5174 | + struct timblogiw_frame frame[TIMBLOGIW_NUM_FRAMES]; | ||
5175 | + int num_frames; | ||
5176 | + unsigned int frame_count; | ||
5177 | + struct list_head inqueue, outqueue; | ||
5178 | + spinlock_t queue_lock; /* mutual exclusion */ | ||
5179 | + enum timblogiw_stream_state stream; | ||
5180 | + struct video_device *video_dev; | ||
5181 | + struct mutex lock, fileop_lock; | ||
5182 | + wait_queue_head_t wait_frame; | ||
5183 | + struct completion irq_done; | ||
5184 | + struct timblogiw_tvnorm const *cur_norm; | ||
5185 | + struct device *dev; | ||
5186 | + struct timblogiw_dma_control dma; | ||
5187 | + void __iomem *membase; | ||
5188 | + struct tasklet_struct tasklet; | ||
5189 | + struct timb_video_platform_data pdata; | ||
5190 | + struct v4l2_subdev *sd_enc; /* encoder */ | ||
5191 | + struct module *enc_owner; | ||
5192 | +}; | ||
5193 | + | ||
5194 | +#endif /* _TIMBLOGIW_H */ | ||
5195 | + | ||
5196 | diff -uNr linux-2.6.31/drivers/mfd/Kconfig linux-2.6.31.new/drivers/mfd/Kconfig | ||
5197 | --- linux-2.6.31/drivers/mfd/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
5198 | +++ linux-2.6.31.new/drivers/mfd/Kconfig 2009-10-23 11:17:29.000000000 -0700 | ||
5199 | @@ -263,6 +263,25 @@ | ||
5200 | This enables the PCAP ASIC present on EZX Phones. This is | ||
5201 | needed for MMC, TouchScreen, Sound, USB, etc.. | ||
5202 | |||
5203 | +config MFD_TIMBERDALE | ||
5204 | + tristate "Support for the Timberdale FPGA" | ||
5205 | + select MFD_CORE | ||
5206 | + depends on PCI | ||
5207 | + ---help--- | ||
5208 | + This is the core driver for the timberdale FPGA. This device is a | ||
5209 | + multifunctioanl device which may provide numerous interfaces. | ||
5210 | + | ||
5211 | + The timberdale FPGA can be found on the Intel Atom development board | ||
5212 | + for automotive in-vehicle infontainment board called Russellville. | ||
5213 | + | ||
5214 | +config MFD_TIMBERDALE_DMA | ||
5215 | + tristate "Support for timberdale DMA" | ||
5216 | + depends on MFD_TIMBERDALE | ||
5217 | + depends on HAS_IOMEM | ||
5218 | + ---help--- | ||
5219 | + Add support the DMA block inside the timberdale FPGA. This to be able | ||
5220 | + to do DMA transfers directly to some of the blocks inside the FPGA | ||
5221 | + | ||
5222 | endmenu | ||
5223 | |||
5224 | menu "Multimedia Capabilities Port drivers" | ||
5225 | diff -uNr linux-2.6.31/drivers/mfd/Makefile linux-2.6.31.new/drivers/mfd/Makefile | ||
5226 | --- linux-2.6.31/drivers/mfd/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
5227 | +++ linux-2.6.31.new/drivers/mfd/Makefile 2009-10-23 11:17:29.000000000 -0700 | ||
5228 | @@ -44,3 +44,7 @@ | ||
5229 | obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o | ||
5230 | obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o | ||
5231 | obj-$(CONFIG_AB3100_CORE) += ab3100-core.o | ||
5232 | + | ||
5233 | +obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o | ||
5234 | +obj-$(CONFIG_MFD_TIMBERDALE_DMA) += timbdma.o | ||
5235 | + | ||
5236 | diff -uNr linux-2.6.31/drivers/mfd/timbdma.c linux-2.6.31.new/drivers/mfd/timbdma.c | ||
5237 | --- linux-2.6.31/drivers/mfd/timbdma.c 1969-12-31 16:00:00.000000000 -0800 | ||
5238 | +++ linux-2.6.31.new/drivers/mfd/timbdma.c 2009-10-23 11:17:29.000000000 -0700 | ||
5239 | @@ -0,0 +1,542 @@ | ||
5240 | +/* | ||
5241 | + * timbdma.c timberdale FPGA DMA driver | ||
5242 | + * Copyright (c) 2009 Intel Corporation | ||
5243 | + * | ||
5244 | + * This program is free software; you can redistribute it and/or modify | ||
5245 | + * it under the terms of the GNU General Public License version 2 as | ||
5246 | + * published by the Free Software Foundation. | ||
5247 | + * | ||
5248 | + * This program is distributed in the hope that it will be useful, | ||
5249 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5250 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5251 | + * GNU General Public License for more details. | ||
5252 | + * | ||
5253 | + * You should have received a copy of the GNU General Public License | ||
5254 | + * along with this program; if not, write to the Free Software | ||
5255 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5256 | + */ | ||
5257 | + | ||
5258 | +/* Supports: | ||
5259 | + * Timberdale FPGA DMA engine | ||
5260 | + */ | ||
5261 | + | ||
5262 | +#include <linux/version.h> | ||
5263 | +#include <linux/module.h> | ||
5264 | +#include <linux/interrupt.h> | ||
5265 | +#include <linux/platform_device.h> | ||
5266 | +#include <linux/io-mapping.h> | ||
5267 | +#include <linux/dma-mapping.h> | ||
5268 | +#include <linux/mfd/timbdma.h> | ||
5269 | + | ||
5270 | +#define DRIVER_NAME "timb-dma" | ||
5271 | + | ||
5272 | +#define TIMBDMA_ACR 0x34 | ||
5273 | +#define TIMBDMA_32BIT_ADDR 0x01 | ||
5274 | + | ||
5275 | +#define TIMBDMA_ISR 0x080000 | ||
5276 | +#define TIMBDMA_IPR 0x080004 | ||
5277 | +#define TIMBDMA_IER 0x080008 | ||
5278 | + | ||
5279 | +/* DMA configuration registers */ | ||
5280 | +/* RX registers */ | ||
5281 | +#define TIMBDMA_OFFS_RX_DHAR 0x00 | ||
5282 | +#define TIMBDMA_OFFS_RX_DLAR 0x04 | ||
5283 | +#define TIMBDMA_OFFS_RX_LR 0x0C | ||
5284 | +#define TIMBDMA_OFFS_RX_BLR 0x10 | ||
5285 | +#define TIMBDMA_OFFS_RX_ER 0x14 | ||
5286 | +#define TIMBDMA_RX_EN 0x01 | ||
5287 | +/* bytes per Row, video specific register */ | ||
5288 | +#define TIMBDMA_OFFS_RX_BPRR 0x30 | ||
5289 | + | ||
5290 | +/* TX registers */ | ||
5291 | +#define TIMBDMA_OFFS_TX_DHAR 0x18 | ||
5292 | +#define TIMBDMA_OFFS_TX_DLAR 0x1C | ||
5293 | +#define TIMBDMA_OFFS_TX_BLR 0x24 | ||
5294 | +#define TIMBDMA_OFFS_TX_LR 0x28 | ||
5295 | + | ||
5296 | +#define DMA_DESC_SIZE 8 | ||
5297 | + | ||
5298 | +struct dma_desc { | ||
5299 | + u32 len; | ||
5300 | + u32 chunk_size; | ||
5301 | + u8 buf[0]; | ||
5302 | +}; | ||
5303 | + | ||
5304 | +struct timbdma_control { | ||
5305 | + timbdma_interruptcb callback; | ||
5306 | + void *callback_data; | ||
5307 | + dma_addr_t desc; | ||
5308 | + int desc_len; | ||
5309 | + /* the following are used to store a desc while the hw has not been | ||
5310 | + * probed yet | ||
5311 | + */ | ||
5312 | + struct dma_desc *stored_desc; | ||
5313 | + int stored_bytes_per_row; | ||
5314 | +}; | ||
5315 | + | ||
5316 | +struct timbdma_dev { | ||
5317 | + void __iomem *membase; | ||
5318 | + struct device *dev; | ||
5319 | + struct timbdma_control control[DMA_IRQS]; | ||
5320 | + spinlock_t lock; /* mutual exclusion */ | ||
5321 | +}; | ||
5322 | + | ||
5323 | +static struct timbdma_dev *self_g; | ||
5324 | + | ||
5325 | + | ||
5326 | +void *timbdma_alloc_desc(u32 size, u16 alignment) | ||
5327 | +{ | ||
5328 | + /* calculate the number of chunks needed */ | ||
5329 | + int chunk_size = USHORT_MAX - (USHORT_MAX % alignment); | ||
5330 | + int chunks = size / chunk_size; | ||
5331 | + int len; | ||
5332 | + struct dma_desc *dma_desc; | ||
5333 | + | ||
5334 | + if (size % chunk_size) | ||
5335 | + chunks++; | ||
5336 | + | ||
5337 | + len = sizeof(struct dma_desc) + DMA_DESC_SIZE * chunks; | ||
5338 | + | ||
5339 | + dma_desc = kzalloc(len, GFP_KERNEL); | ||
5340 | + if (dma_desc) { | ||
5341 | + dma_desc->len = DMA_DESC_SIZE * chunks; | ||
5342 | + dma_desc->chunk_size = chunk_size; | ||
5343 | + } | ||
5344 | + return dma_desc; | ||
5345 | +} | ||
5346 | +EXPORT_SYMBOL(timbdma_alloc_desc); | ||
5347 | + | ||
5348 | +void timbdma_free_desc(void *desc) | ||
5349 | +{ | ||
5350 | + kfree(desc); | ||
5351 | +} | ||
5352 | +EXPORT_SYMBOL(timbdma_free_desc); | ||
5353 | + | ||
5354 | +int timbdma_prep_desc(void *desc, dma_addr_t addr, u32 size) | ||
5355 | +{ | ||
5356 | + struct dma_desc *dma_desc = desc; | ||
5357 | + u8 *buf = dma_desc->buf; | ||
5358 | + dma_addr_t cur_addr = addr; | ||
5359 | + int chunks = size / dma_desc->chunk_size; | ||
5360 | + if (size % dma_desc->chunk_size) | ||
5361 | + chunks++; | ||
5362 | + | ||
5363 | + if (dma_desc->len < chunks * DMA_DESC_SIZE) | ||
5364 | + return -EINVAL; | ||
5365 | + | ||
5366 | + while (size > 0) { | ||
5367 | + int chunk_size = dma_desc->chunk_size; | ||
5368 | + if (chunk_size > size) | ||
5369 | + chunk_size = size; | ||
5370 | + buf[7] = (cur_addr >> 24) & 0xff; | ||
5371 | + buf[6] = (cur_addr >> 16) & 0xff; | ||
5372 | + buf[5] = (cur_addr >> 8) & 0xff; | ||
5373 | + buf[4] = (cur_addr >> 0) & 0xff; | ||
5374 | + | ||
5375 | + buf[3] = (chunk_size >> 8) & 0xff; | ||
5376 | + buf[2] = (chunk_size >> 0) & 0xff; | ||
5377 | + | ||
5378 | + buf[1] = 0x00; | ||
5379 | + buf[0] = 0x21; /* tran, valid */ | ||
5380 | + | ||
5381 | + buf += DMA_DESC_SIZE; | ||
5382 | + cur_addr += chunk_size; | ||
5383 | + size -= chunk_size; | ||
5384 | + } | ||
5385 | + | ||
5386 | + /* make sure to mark the last one as end */ | ||
5387 | + (buf-DMA_DESC_SIZE)[0] |= 0x2; | ||
5388 | + | ||
5389 | + return 0; | ||
5390 | +} | ||
5391 | +EXPORT_SYMBOL(timbdma_prep_desc); | ||
5392 | + | ||
5393 | +static irqreturn_t timbdma_handleinterrupt(int irq, void *devid) | ||
5394 | +{ | ||
5395 | + struct timbdma_dev *dev = (struct timbdma_dev *)devid; | ||
5396 | + u32 ipr, ier; | ||
5397 | + int i; | ||
5398 | + | ||
5399 | + ipr = ioread32(dev->membase + TIMBDMA_IPR); | ||
5400 | + /* the MSI-X controller is level triggered, help it a bit, | ||
5401 | + * by disabling interrupts and re-enable them in the end. | ||
5402 | + */ | ||
5403 | + ier = ioread32(dev->membase + TIMBDMA_IER); | ||
5404 | + iowrite32(0, dev->membase + TIMBDMA_IER); | ||
5405 | + | ||
5406 | + if (ipr) { | ||
5407 | + /* ack */ | ||
5408 | + iowrite32(ipr, dev->membase + TIMBDMA_ISR); | ||
5409 | + | ||
5410 | + /* call the callbacks */ | ||
5411 | + for (i = 0; i < DMA_IRQS; i++) { | ||
5412 | + int mask = 1 << i; | ||
5413 | + if (ipr & mask) { | ||
5414 | + struct timbdma_control *ctrl = dev->control + i; | ||
5415 | + struct timbdma_control *unmap_ctrl = ctrl; | ||
5416 | + | ||
5417 | + /* special case for video frame drop */ | ||
5418 | + if (mask == DMA_IRQ_VIDEO_DROP) | ||
5419 | + unmap_ctrl = dev->control + i - 1; | ||
5420 | + | ||
5421 | + /* unmap memory */ | ||
5422 | + dma_unmap_single(dev->dev, unmap_ctrl->desc, | ||
5423 | + unmap_ctrl->desc_len, DMA_TO_DEVICE); | ||
5424 | + unmap_ctrl->desc = 0; | ||
5425 | + | ||
5426 | + if (ctrl->callback) | ||
5427 | + ctrl->callback(mask, | ||
5428 | + ctrl->callback_data); | ||
5429 | + } | ||
5430 | + } | ||
5431 | + | ||
5432 | + iowrite32(ier, dev->membase + TIMBDMA_IER); | ||
5433 | + return IRQ_HANDLED; | ||
5434 | + } else { | ||
5435 | + iowrite32(ier, dev->membase + TIMBDMA_IER); | ||
5436 | + return IRQ_NONE; | ||
5437 | + } | ||
5438 | +} | ||
5439 | + | ||
5440 | +static int __timbdma_start(struct timbdma_dev *dev, int index, | ||
5441 | + struct dma_desc *dma_desc, int bytes_per_row) | ||
5442 | +{ | ||
5443 | + u32 offset; | ||
5444 | + unsigned long flags; | ||
5445 | + struct timbdma_control *ctrl; | ||
5446 | + int err; | ||
5447 | + | ||
5448 | + ctrl = dev->control + index; | ||
5449 | + | ||
5450 | + BUG_ON(ctrl->desc); | ||
5451 | + | ||
5452 | + /* check if we already have a descriptor */ | ||
5453 | + if (ctrl->desc) | ||
5454 | + return -EALREADY; | ||
5455 | + | ||
5456 | + /* map up the descriptor */ | ||
5457 | + ctrl->desc = dma_map_single(dev->dev, dma_desc->buf, dma_desc->len, | ||
5458 | + DMA_TO_DEVICE); | ||
5459 | + err = dma_mapping_error(dev->dev, ctrl->desc); | ||
5460 | + if (err) { | ||
5461 | + ctrl->desc = 0; | ||
5462 | + return err; | ||
5463 | + } | ||
5464 | + ctrl->desc_len = dma_desc->len; | ||
5465 | + | ||
5466 | + /* now enable the DMA transfer */ | ||
5467 | + offset = index / 2 * 0x40; | ||
5468 | + | ||
5469 | + spin_lock_irqsave(&dev->lock, flags); | ||
5470 | + if (!(index % 2)) { | ||
5471 | + /* RX */ | ||
5472 | + /* descriptor address */ | ||
5473 | + iowrite32(0, dev->membase + offset + TIMBDMA_OFFS_RX_DHAR); | ||
5474 | + iowrite32(ctrl->desc, dev->membase + offset + | ||
5475 | + TIMBDMA_OFFS_RX_DLAR); | ||
5476 | + /* Bytes per line */ | ||
5477 | + iowrite32(bytes_per_row, dev->membase + offset + | ||
5478 | + TIMBDMA_OFFS_RX_BPRR); | ||
5479 | + /* enable RX */ | ||
5480 | + iowrite32(TIMBDMA_RX_EN, dev->membase + offset + | ||
5481 | + TIMBDMA_OFFS_RX_ER); | ||
5482 | + } else { | ||
5483 | + /* TX */ | ||
5484 | + /* address high */ | ||
5485 | + iowrite32(0, dev->membase + offset + TIMBDMA_OFFS_TX_DHAR); | ||
5486 | + iowrite32(ctrl->desc, dev->membase + offset + | ||
5487 | + TIMBDMA_OFFS_TX_DLAR); | ||
5488 | + } | ||
5489 | + spin_unlock_irqrestore(&dev->lock, flags); | ||
5490 | + | ||
5491 | + return 0; | ||
5492 | +} | ||
5493 | +int timbdma_start(u32 flag, void *desc, int bytes_per_row) | ||
5494 | +{ | ||
5495 | + int i; | ||
5496 | + struct timbdma_dev *dev = self_g; | ||
5497 | + struct dma_desc *dma_desc = desc; | ||
5498 | + int ret = 0; | ||
5499 | + | ||
5500 | + /* only allow 1 flag bit to be set */ | ||
5501 | + for (i = 0; i < DMA_IRQS && !(flag & (1 << i)); i++) | ||
5502 | + ; | ||
5503 | + if (i == DMA_IRQS || (flag & ~(1 << i))) | ||
5504 | + return -EINVAL; | ||
5505 | + | ||
5506 | + if (!dev->membase) { | ||
5507 | + /* the physical DMA device has not showed up yet */ | ||
5508 | + unsigned long flags; | ||
5509 | + struct timbdma_control *ctrl = dev->control + i; | ||
5510 | + BUG_ON(ctrl->stored_desc); | ||
5511 | + if (ctrl->stored_desc) | ||
5512 | + ret = -EALREADY; | ||
5513 | + else { | ||
5514 | + spin_lock_irqsave(&dev->lock, flags); | ||
5515 | + ctrl->stored_desc = desc; | ||
5516 | + ctrl->stored_bytes_per_row = bytes_per_row; | ||
5517 | + spin_unlock_irqrestore(&dev->lock, flags); | ||
5518 | + } | ||
5519 | + } else | ||
5520 | + ret = __timbdma_start(dev, i, dma_desc, bytes_per_row); | ||
5521 | + | ||
5522 | + if (ret) | ||
5523 | + printk(KERN_ERR DRIVER_NAME": Failed to start DMA: %d\n", ret); | ||
5524 | + return ret; | ||
5525 | +} | ||
5526 | +EXPORT_SYMBOL(timbdma_start); | ||
5527 | + | ||
5528 | +int timbdma_stop(u32 flags) | ||
5529 | +{ | ||
5530 | + int i; | ||
5531 | + unsigned long irqflags; | ||
5532 | + struct timbdma_dev *dev = self_g; | ||
5533 | + int ret = 0; | ||
5534 | + | ||
5535 | + spin_lock_irqsave(&dev->lock, irqflags); | ||
5536 | + | ||
5537 | + /* now disable the DMA transfers */ | ||
5538 | + for (i = 0; i < DMA_IRQS; i++) | ||
5539 | + if (flags & (1 << i)) { | ||
5540 | + /* | ||
5541 | + RX enable registers are located at: | ||
5542 | + 0x14 | ||
5543 | + 0x54 | ||
5544 | + 0x94 | ||
5545 | + | ||
5546 | + TX DESC ADDR LOW registers are located at: | ||
5547 | + 0x1C | ||
5548 | + 0x5C | ||
5549 | + */ | ||
5550 | + struct timbdma_control *ctrl = dev->control + i; | ||
5551 | + if (ctrl->desc) { | ||
5552 | + u32 offset = i / 2 * 0x40; | ||
5553 | + | ||
5554 | + if (!(i % 2)) { | ||
5555 | + /* even -> RX enable */ | ||
5556 | + offset += TIMBDMA_OFFS_RX_ER; | ||
5557 | + /** TODO: FIX received length */ | ||
5558 | + } else { | ||
5559 | + /* odd -> TX desc addr low */ | ||
5560 | + offset += TIMBDMA_OFFS_TX_DLAR; | ||
5561 | + /** TODO: FIX written lenth */ | ||
5562 | + } | ||
5563 | + | ||
5564 | + if (dev->membase) | ||
5565 | + iowrite32(0, dev->membase + offset); | ||
5566 | + | ||
5567 | + dma_unmap_single(dev->dev, ctrl->desc, | ||
5568 | + ctrl->desc_len, DMA_TO_DEVICE); | ||
5569 | + ctrl->desc = 0; | ||
5570 | + } else if (ctrl->stored_desc) | ||
5571 | + ctrl->stored_desc = NULL; | ||
5572 | + } | ||
5573 | + | ||
5574 | + if (dev->membase) | ||
5575 | + /* ack any pending IRQs */ | ||
5576 | + iowrite32(flags, dev->membase + TIMBDMA_ISR); | ||
5577 | + | ||
5578 | + spin_unlock_irqrestore(&dev->lock, irqflags); | ||
5579 | + | ||
5580 | + return ret; | ||
5581 | +} | ||
5582 | +EXPORT_SYMBOL(timbdma_stop); | ||
5583 | + | ||
5584 | +void timbdma_set_interruptcb(u32 flags, timbdma_interruptcb icb, void *data) | ||
5585 | +{ | ||
5586 | + int i; | ||
5587 | + unsigned long irqflags; | ||
5588 | + struct timbdma_dev *dev = self_g; | ||
5589 | + u32 ier; | ||
5590 | + | ||
5591 | + spin_lock_irqsave(&dev->lock, irqflags); | ||
5592 | + | ||
5593 | + for (i = 0; i < DMA_IRQS; i++) | ||
5594 | + if (flags & (1 << i)) { | ||
5595 | + struct timbdma_control *ctrl = dev->control + i; | ||
5596 | + ctrl->callback = icb; | ||
5597 | + ctrl->callback_data = data; | ||
5598 | + } | ||
5599 | + | ||
5600 | + /* the DMA device might not have showed up yet */ | ||
5601 | + if (dev->membase) { | ||
5602 | + /* Ack any pending IRQ */ | ||
5603 | + iowrite32(flags, dev->membase + TIMBDMA_ISR); | ||
5604 | + | ||
5605 | + /* if a null callback is given -> clear interrupt, | ||
5606 | + * else -> enable | ||
5607 | + */ | ||
5608 | + ier = ioread32(dev->membase + TIMBDMA_IER); | ||
5609 | + if (icb != NULL) | ||
5610 | + ier |= flags; | ||
5611 | + else | ||
5612 | + ier &= ~flags; | ||
5613 | + iowrite32(ier, dev->membase + TIMBDMA_IER); | ||
5614 | + } | ||
5615 | + | ||
5616 | + spin_unlock_irqrestore(&dev->lock, irqflags); | ||
5617 | +} | ||
5618 | +EXPORT_SYMBOL(timbdma_set_interruptcb); | ||
5619 | + | ||
5620 | +static void timbdma_start_operations(struct timbdma_dev *self) | ||
5621 | +{ | ||
5622 | + int i; | ||
5623 | + u32 ier; | ||
5624 | + unsigned long flags; | ||
5625 | + | ||
5626 | + spin_lock_irqsave(&self->lock, flags); | ||
5627 | + ier = ioread32(self->membase + TIMBDMA_IER); | ||
5628 | + for (i = 0; i < DMA_IRQS; i++) | ||
5629 | + if (self->control[i].callback) | ||
5630 | + ier |= 1 << i; | ||
5631 | + iowrite32(ier, self->membase + TIMBDMA_IER); | ||
5632 | + spin_unlock_irqrestore(&self->lock, flags); | ||
5633 | + | ||
5634 | + /* look for any transfers that were started before the HW was | ||
5635 | + * available, and start them | ||
5636 | + */ | ||
5637 | + for (i = 0; i < DMA_IRQS; i++) { | ||
5638 | + struct timbdma_control *ctrl = self->control + i; | ||
5639 | + if (ctrl->stored_desc) { | ||
5640 | + struct dma_desc *dma_desc = ctrl->stored_desc; | ||
5641 | + ctrl->stored_desc = NULL; | ||
5642 | + if (__timbdma_start(self, i, dma_desc, | ||
5643 | + ctrl->stored_bytes_per_row)) | ||
5644 | + printk(KERN_ERR DRIVER_NAME | ||
5645 | + ": Failed to start DMA\n"); | ||
5646 | + } | ||
5647 | + } | ||
5648 | +} | ||
5649 | + | ||
5650 | + | ||
5651 | +static int timbdma_probe(struct platform_device *dev) | ||
5652 | +{ | ||
5653 | + int err, irq; | ||
5654 | + struct resource *iomem; | ||
5655 | + struct timbdma_dev *self = self_g; | ||
5656 | + | ||
5657 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
5658 | + if (!iomem) { | ||
5659 | + err = -EINVAL; | ||
5660 | + goto err_request; | ||
5661 | + } | ||
5662 | + | ||
5663 | + if (dev->dev.parent) | ||
5664 | + self->dev = dev->dev.parent; | ||
5665 | + else | ||
5666 | + self->dev = &dev->dev; | ||
5667 | + | ||
5668 | + if (!request_mem_region(iomem->start, | ||
5669 | + resource_size(iomem), DRIVER_NAME)) { | ||
5670 | + err = -EBUSY; | ||
5671 | + goto err_request; | ||
5672 | + } | ||
5673 | + | ||
5674 | + self->membase = ioremap(iomem->start, resource_size(iomem)); | ||
5675 | + if (!self->membase) { | ||
5676 | + printk(KERN_ERR DRIVER_NAME ": Failed to remap I/O memory\n"); | ||
5677 | + err = -ENOMEM; | ||
5678 | + goto err_ioremap; | ||
5679 | + } | ||
5680 | + | ||
5681 | + /* 32bit addressing */ | ||
5682 | + iowrite32(TIMBDMA_32BIT_ADDR, self->membase + TIMBDMA_ACR); | ||
5683 | + | ||
5684 | + /* disable and clear any interrupts */ | ||
5685 | + iowrite32(0x0, self->membase + TIMBDMA_IER); | ||
5686 | + iowrite32(0x0, self->membase + TIMBDMA_ISR); | ||
5687 | + | ||
5688 | + /* register interrupt */ | ||
5689 | + irq = platform_get_irq(dev, 0); | ||
5690 | + if (irq < 0) { | ||
5691 | + err = irq; | ||
5692 | + goto err_get_irq; | ||
5693 | + } | ||
5694 | + | ||
5695 | + /* request IRQ */ | ||
5696 | + err = request_irq(irq, timbdma_handleinterrupt, IRQF_SHARED, | ||
5697 | + DRIVER_NAME, self); | ||
5698 | + if (err) { | ||
5699 | + printk(KERN_ERR DRIVER_NAME ": Failed to request IRQ\n"); | ||
5700 | + goto err_get_irq; | ||
5701 | + } | ||
5702 | + | ||
5703 | + platform_set_drvdata(dev, self); | ||
5704 | + | ||
5705 | + /* assign the global pointer */ | ||
5706 | + self_g = self; | ||
5707 | + | ||
5708 | + timbdma_start_operations(self); | ||
5709 | + | ||
5710 | + return 0; | ||
5711 | + | ||
5712 | +err_get_irq: | ||
5713 | + iounmap(self->membase); | ||
5714 | +err_ioremap: | ||
5715 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
5716 | +err_request: | ||
5717 | + printk(KERN_ERR DRIVER_NAME ": Failed to register Timberdale DMA: %d\n", | ||
5718 | + err); | ||
5719 | + self->membase = NULL; | ||
5720 | + self->dev = NULL; | ||
5721 | + | ||
5722 | + return err; | ||
5723 | +} | ||
5724 | + | ||
5725 | +static int timbdma_remove(struct platform_device *dev) | ||
5726 | +{ | ||
5727 | + struct timbdma_dev *self = platform_get_drvdata(dev); | ||
5728 | + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
5729 | + | ||
5730 | + free_irq(platform_get_irq(dev, 0), self); | ||
5731 | + iounmap(self->membase); | ||
5732 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
5733 | + self->membase = NULL; | ||
5734 | + self->dev = NULL; | ||
5735 | + return 0; | ||
5736 | +} | ||
5737 | + | ||
5738 | +static struct platform_driver timbdma_platform_driver = { | ||
5739 | + .driver = { | ||
5740 | + .name = DRIVER_NAME, | ||
5741 | + .owner = THIS_MODULE, | ||
5742 | + }, | ||
5743 | + .probe = timbdma_probe, | ||
5744 | + .remove = timbdma_remove, | ||
5745 | +}; | ||
5746 | + | ||
5747 | +/*--------------------------------------------------------------------------*/ | ||
5748 | + | ||
5749 | +static int __init timbdma_init(void) | ||
5750 | +{ | ||
5751 | + struct timbdma_dev *self; | ||
5752 | + int err; | ||
5753 | + | ||
5754 | + self = kzalloc(sizeof(*self), GFP_KERNEL); | ||
5755 | + if (!self) | ||
5756 | + return -ENOMEM; | ||
5757 | + | ||
5758 | + spin_lock_init(&self->lock); | ||
5759 | + | ||
5760 | + self_g = self; | ||
5761 | + err = platform_driver_register(&timbdma_platform_driver); | ||
5762 | + if (err) | ||
5763 | + kfree(self); | ||
5764 | + | ||
5765 | + return err; | ||
5766 | +} | ||
5767 | + | ||
5768 | +static void __exit timbdma_exit(void) | ||
5769 | +{ | ||
5770 | + platform_driver_unregister(&timbdma_platform_driver); | ||
5771 | + kfree(self_g); | ||
5772 | +} | ||
5773 | + | ||
5774 | +module_init(timbdma_init); | ||
5775 | +module_exit(timbdma_exit); | ||
5776 | + | ||
5777 | +MODULE_DESCRIPTION("Timberdale DMA driver"); | ||
5778 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
5779 | +MODULE_LICENSE("GPL v2"); | ||
5780 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
5781 | + | ||
5782 | diff -uNr linux-2.6.31/drivers/mfd/timberdale.c linux-2.6.31.new/drivers/mfd/timberdale.c | ||
5783 | --- linux-2.6.31/drivers/mfd/timberdale.c 1969-12-31 16:00:00.000000000 -0800 | ||
5784 | +++ linux-2.6.31.new/drivers/mfd/timberdale.c 2009-10-23 11:17:29.000000000 -0700 | ||
5785 | @@ -0,0 +1,914 @@ | ||
5786 | +/* | ||
5787 | + * timberdale.c timberdale FPGA mfd shim driver | ||
5788 | + * Copyright (c) 2009 Intel Corporation | ||
5789 | + * | ||
5790 | + * This program is free software; you can redistribute it and/or modify | ||
5791 | + * it under the terms of the GNU General Public License version 2 as | ||
5792 | + * published by the Free Software Foundation. | ||
5793 | + * | ||
5794 | + * This program is distributed in the hope that it will be useful, | ||
5795 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
5796 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
5797 | + * GNU General Public License for more details. | ||
5798 | + * | ||
5799 | + * You should have received a copy of the GNU General Public License | ||
5800 | + * along with this program; if not, write to the Free Software | ||
5801 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
5802 | + */ | ||
5803 | + | ||
5804 | +/* Supports: | ||
5805 | + * Timberdale FPGA | ||
5806 | + */ | ||
5807 | + | ||
5808 | +#include <linux/kernel.h> | ||
5809 | +#include <linux/module.h> | ||
5810 | +#include <linux/pci.h> | ||
5811 | +#include <linux/msi.h> | ||
5812 | +#include <linux/init.h> | ||
5813 | +#include <linux/interrupt.h> | ||
5814 | +#include <linux/platform_device.h> | ||
5815 | +#include <linux/mfd/core.h> | ||
5816 | +#include <linux/irq.h> | ||
5817 | + | ||
5818 | +#include <linux/timb_gpio.h> | ||
5819 | + | ||
5820 | +#include <linux/i2c.h> | ||
5821 | +#include <linux/i2c-ocores.h> | ||
5822 | +#include <linux/i2c-xiic.h> | ||
5823 | +#include <linux/i2c/tsc2007.h> | ||
5824 | +#include <linux/can/platform/ascb.h> | ||
5825 | + | ||
5826 | +#include <linux/spi/spi.h> | ||
5827 | +#include <linux/spi/xilinx_spi.h> | ||
5828 | +#include <linux/spi/max7301.h> | ||
5829 | +#include <linux/spi/mc33880.h> | ||
5830 | + | ||
5831 | +#include <media/timb_video.h> | ||
5832 | +#include <media/timb_radio.h> | ||
5833 | +#include <linux/most/timbmlb.h> | ||
5834 | + | ||
5835 | +#include <sound/timbi2s.h> | ||
5836 | + | ||
5837 | +#include "timberdale.h" | ||
5838 | + | ||
5839 | +#define DRIVER_NAME "timberdale" | ||
5840 | + | ||
5841 | +struct timberdale_device { | ||
5842 | + resource_size_t intc_mapbase; | ||
5843 | + resource_size_t ctl_mapbase; | ||
5844 | + unsigned char __iomem *ctl_membase; | ||
5845 | + /* locking from interrupts while modifiying registers */ | ||
5846 | + spinlock_t lock; | ||
5847 | + struct { | ||
5848 | + u32 major; | ||
5849 | + u32 minor; | ||
5850 | + u32 config; | ||
5851 | + } fw; | ||
5852 | +}; | ||
5853 | + | ||
5854 | +/*--------------------------------------------------------------------------*/ | ||
5855 | + | ||
5856 | +static struct tsc2007_platform_data timberdale_tsc2007_platform_data = { | ||
5857 | + .model = 2003, | ||
5858 | + .x_plate_ohms = 100 | ||
5859 | +}; | ||
5860 | + | ||
5861 | +static struct ascb_platform_data timberdale_ascb_platform_data = { | ||
5862 | + .gpio_pin = GPIO_PIN_ASCB | ||
5863 | +}; | ||
5864 | + | ||
5865 | +static struct i2c_board_info timberdale_i2c_board_info[] = { | ||
5866 | + { | ||
5867 | + I2C_BOARD_INFO("tsc2007", 0x48), | ||
5868 | + .platform_data = &timberdale_tsc2007_platform_data, | ||
5869 | + .irq = IRQ_TIMBERDALE_TSC_INT | ||
5870 | + }, | ||
5871 | + { | ||
5872 | + /* Requires jumper JP9 to be off */ | ||
5873 | + I2C_BOARD_INFO("adv7180", 0x42 >> 1), | ||
5874 | + .irq = IRQ_TIMBERDALE_ADV7180 | ||
5875 | + }, | ||
5876 | + { | ||
5877 | + I2C_BOARD_INFO("tef6862", 0x60) | ||
5878 | + }, | ||
5879 | + { | ||
5880 | + I2C_BOARD_INFO("saa7706h", 0x1C) | ||
5881 | + }, | ||
5882 | + { | ||
5883 | + I2C_BOARD_INFO("ascb-can", 0x18), | ||
5884 | + .platform_data = &timberdale_ascb_platform_data, | ||
5885 | + } | ||
5886 | +}; | ||
5887 | + | ||
5888 | +static __devinitdata struct xiic_i2c_platform_data | ||
5889 | +timberdale_xiic_platform_data = { | ||
5890 | + .devices = timberdale_i2c_board_info, | ||
5891 | + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) | ||
5892 | +}; | ||
5893 | + | ||
5894 | +static __devinitdata struct ocores_i2c_platform_data | ||
5895 | +timberdale_ocores_platform_data = { | ||
5896 | + .regstep = 4, | ||
5897 | + .clock_khz = 62500, | ||
5898 | + .devices = timberdale_i2c_board_info, | ||
5899 | + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) | ||
5900 | +}; | ||
5901 | + | ||
5902 | +const static __devinitconst struct resource timberdale_xiic_resources[] = { | ||
5903 | + { | ||
5904 | + .start = XIICOFFSET, | ||
5905 | + .end = XIICEND, | ||
5906 | + .flags = IORESOURCE_MEM, | ||
5907 | + }, | ||
5908 | + { | ||
5909 | + .start = IRQ_TIMBERDALE_I2C, | ||
5910 | + .end = IRQ_TIMBERDALE_I2C, | ||
5911 | + .flags = IORESOURCE_IRQ, | ||
5912 | + }, | ||
5913 | +}; | ||
5914 | + | ||
5915 | +const static __devinitconst struct resource timberdale_ocores_resources[] = { | ||
5916 | + { | ||
5917 | + .start = OCORESOFFSET, | ||
5918 | + .end = OCORESEND, | ||
5919 | + .flags = IORESOURCE_MEM, | ||
5920 | + }, | ||
5921 | + { | ||
5922 | + .start = IRQ_TIMBERDALE_I2C, | ||
5923 | + .end = IRQ_TIMBERDALE_I2C, | ||
5924 | + .flags = IORESOURCE_IRQ, | ||
5925 | + }, | ||
5926 | +}; | ||
5927 | + | ||
5928 | +const struct max7301_platform_data timberdale_max7301_platform_data = { | ||
5929 | + .base = 200 | ||
5930 | +}; | ||
5931 | + | ||
5932 | +const struct mc33880_platform_data timberdale_mc33880_platform_data = { | ||
5933 | + .base = 100 | ||
5934 | +}; | ||
5935 | + | ||
5936 | +static struct spi_board_info timberdale_spi_16bit_board_info[] = { | ||
5937 | + { | ||
5938 | + .modalias = "max7301", | ||
5939 | + .max_speed_hz = 26000, | ||
5940 | + .chip_select = 2, | ||
5941 | + .mode = SPI_MODE_0, | ||
5942 | + .platform_data = &timberdale_max7301_platform_data | ||
5943 | + }, | ||
5944 | +}; | ||
5945 | + | ||
5946 | +static struct spi_board_info timberdale_spi_8bit_board_info[] = { | ||
5947 | + { | ||
5948 | + .modalias = "mc33880", | ||
5949 | + .max_speed_hz = 4000, | ||
5950 | + .chip_select = 1, | ||
5951 | + .mode = SPI_MODE_1, | ||
5952 | + .platform_data = &timberdale_mc33880_platform_data | ||
5953 | + }, | ||
5954 | +}; | ||
5955 | + | ||
5956 | +static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = { | ||
5957 | + /* Current(2009-03-06) revision of | ||
5958 | + * Timberdale we can handle 3 chip selects | ||
5959 | + */ | ||
5960 | + .num_chipselect = 3, | ||
5961 | + /* bits per word and devices will be filled in runtime depending | ||
5962 | + * on the HW config | ||
5963 | + */ | ||
5964 | +}; | ||
5965 | + | ||
5966 | +const static __devinitconst struct resource timberdale_spi_resources[] = { | ||
5967 | + { | ||
5968 | + .start = SPIOFFSET, | ||
5969 | + .end = SPIEND, | ||
5970 | + .flags = IORESOURCE_MEM, | ||
5971 | + }, | ||
5972 | + { | ||
5973 | + .start = IRQ_TIMBERDALE_SPI, | ||
5974 | + .end = IRQ_TIMBERDALE_SPI, | ||
5975 | + .flags = IORESOURCE_IRQ, | ||
5976 | + }, | ||
5977 | +}; | ||
5978 | + | ||
5979 | +const static __devinitconst struct resource timberdale_eth_resources[] = { | ||
5980 | + { | ||
5981 | + .start = ETHOFFSET, | ||
5982 | + .end = ETHEND, | ||
5983 | + .flags = IORESOURCE_MEM, | ||
5984 | + }, | ||
5985 | + { | ||
5986 | + .start = IRQ_TIMBERDALE_ETHSW_IF, | ||
5987 | + .end = IRQ_TIMBERDALE_ETHSW_IF, | ||
5988 | + .flags = IORESOURCE_IRQ, | ||
5989 | + }, | ||
5990 | +}; | ||
5991 | + | ||
5992 | +static __devinitdata struct timbgpio_platform_data | ||
5993 | + timberdale_gpio_platform_data = { | ||
5994 | + .gpio_base = 0, | ||
5995 | + .nr_pins = GPIO_NR_PINS, | ||
5996 | + .irq_base = 200, | ||
5997 | +}; | ||
5998 | + | ||
5999 | +const static __devinitconst struct resource timberdale_gpio_resources[] = { | ||
6000 | + { | ||
6001 | + .start = GPIOOFFSET, | ||
6002 | + .end = GPIOEND, | ||
6003 | + .flags = IORESOURCE_MEM, | ||
6004 | + }, | ||
6005 | + { | ||
6006 | + .start = IRQ_TIMBERDALE_GPIO, | ||
6007 | + .end = IRQ_TIMBERDALE_GPIO, | ||
6008 | + .flags = IORESOURCE_IRQ, | ||
6009 | + }, | ||
6010 | +}; | ||
6011 | + | ||
6012 | +static __devinitdata struct timbmlb_platform_data | ||
6013 | + timberdale_mlb_platform_data = { | ||
6014 | + .reset_pin = GPIO_PIN_INIC_RST | ||
6015 | +}; | ||
6016 | + | ||
6017 | +const static __devinitconst struct resource timberdale_most_resources[] = { | ||
6018 | + { | ||
6019 | + .start = MOSTOFFSET, | ||
6020 | + .end = MOSTEND, | ||
6021 | + .flags = IORESOURCE_MEM, | ||
6022 | + }, | ||
6023 | + { | ||
6024 | + .start = IRQ_TIMBERDALE_MLB, | ||
6025 | + .end = IRQ_TIMBERDALE_MLB, | ||
6026 | + .flags = IORESOURCE_IRQ, | ||
6027 | + }, | ||
6028 | +}; | ||
6029 | + | ||
6030 | +const static __devinitconst struct resource timberdale_mlogicore_resources[] = { | ||
6031 | + { | ||
6032 | + .start = MLCOREOFFSET, | ||
6033 | + .end = MLCOREEND, | ||
6034 | + .flags = IORESOURCE_MEM, | ||
6035 | + }, | ||
6036 | + { | ||
6037 | + .start = IRQ_TIMBERDALE_MLCORE, | ||
6038 | + .end = IRQ_TIMBERDALE_MLCORE, | ||
6039 | + .flags = IORESOURCE_IRQ, | ||
6040 | + }, | ||
6041 | + { | ||
6042 | + .start = IRQ_TIMBERDALE_MLCORE_BUF, | ||
6043 | + .end = IRQ_TIMBERDALE_MLCORE_BUF, | ||
6044 | + .flags = IORESOURCE_IRQ, | ||
6045 | + }, | ||
6046 | +}; | ||
6047 | + | ||
6048 | +const static __devinitconst struct resource timberdale_uart_resources[] = { | ||
6049 | + { | ||
6050 | + .start = UARTOFFSET, | ||
6051 | + .end = UARTEND, | ||
6052 | + .flags = IORESOURCE_MEM, | ||
6053 | + }, | ||
6054 | + { | ||
6055 | + .start = IRQ_TIMBERDALE_UART, | ||
6056 | + .end = IRQ_TIMBERDALE_UART, | ||
6057 | + .flags = IORESOURCE_IRQ, | ||
6058 | + }, | ||
6059 | +}; | ||
6060 | + | ||
6061 | +const static __devinitconst struct resource timberdale_uartlite_resources[] = { | ||
6062 | + { | ||
6063 | + .start = UARTLITEOFFSET, | ||
6064 | + .end = UARTLITEEND, | ||
6065 | + .flags = IORESOURCE_MEM, | ||
6066 | + }, | ||
6067 | + { | ||
6068 | + .start = IRQ_TIMBERDALE_UARTLITE, | ||
6069 | + .end = IRQ_TIMBERDALE_UARTLITE, | ||
6070 | + .flags = IORESOURCE_IRQ, | ||
6071 | + }, | ||
6072 | +}; | ||
6073 | + | ||
6074 | +static __devinitdata struct timbi2s_bus_data timbi2s_bus_data[] = { | ||
6075 | + { | ||
6076 | + .rx = 0, | ||
6077 | + .sample_rate = 8000, | ||
6078 | + }, | ||
6079 | + { | ||
6080 | + .rx = 1, | ||
6081 | + .sample_rate = 8000, | ||
6082 | + }, | ||
6083 | + { | ||
6084 | + .rx = 1, | ||
6085 | + .sample_rate = 44100, | ||
6086 | + }, | ||
6087 | +}; | ||
6088 | + | ||
6089 | +static __devinitdata struct timbi2s_platform_data timbi2s_platform_data = { | ||
6090 | + .busses = timbi2s_bus_data, | ||
6091 | + .num_busses = ARRAY_SIZE(timbi2s_bus_data), | ||
6092 | + .main_clk = 62500000, | ||
6093 | +}; | ||
6094 | + | ||
6095 | +const static __devinitconst struct resource timberdale_i2s_resources[] = { | ||
6096 | + { | ||
6097 | + .start = I2SOFFSET, | ||
6098 | + .end = I2SEND, | ||
6099 | + .flags = IORESOURCE_MEM, | ||
6100 | + }, | ||
6101 | + { | ||
6102 | + .start = IRQ_TIMBERDALE_I2S, | ||
6103 | + .end = IRQ_TIMBERDALE_I2S, | ||
6104 | + .flags = IORESOURCE_IRQ, | ||
6105 | + }, | ||
6106 | +}; | ||
6107 | + | ||
6108 | +static __devinitdata struct timb_video_platform_data | ||
6109 | + timberdale_video_platform_data = { | ||
6110 | + .i2c_adapter = 0, | ||
6111 | + .encoder = "adv7180" | ||
6112 | +}; | ||
6113 | + | ||
6114 | +const static __devinitconst struct resource timberdale_radio_resources[] = { | ||
6115 | + { | ||
6116 | + .start = RDSOFFSET, | ||
6117 | + .end = RDSEND, | ||
6118 | + .flags = IORESOURCE_MEM, | ||
6119 | + }, | ||
6120 | + { | ||
6121 | + .start = IRQ_TIMBERDALE_RDS, | ||
6122 | + .end = IRQ_TIMBERDALE_RDS, | ||
6123 | + .flags = IORESOURCE_IRQ, | ||
6124 | + }, | ||
6125 | +}; | ||
6126 | + | ||
6127 | +static __devinitdata struct timb_radio_platform_data | ||
6128 | + timberdale_radio_platform_data = { | ||
6129 | + .i2c_adapter = 0, | ||
6130 | + .tuner = "tef6862", | ||
6131 | + .dsp = "saa7706h" | ||
6132 | +}; | ||
6133 | + | ||
6134 | +const static __devinitconst struct resource timberdale_video_resources[] = { | ||
6135 | + { | ||
6136 | + .start = LOGIWOFFSET, | ||
6137 | + .end = LOGIWEND, | ||
6138 | + .flags = IORESOURCE_MEM, | ||
6139 | + }, | ||
6140 | + /* | ||
6141 | + note that the "frame buffer" is located in DMA area | ||
6142 | + starting at 0x1200000 | ||
6143 | + */ | ||
6144 | +}; | ||
6145 | + | ||
6146 | +const static __devinitconst struct resource timberdale_dma_resources[] = { | ||
6147 | + { | ||
6148 | + .start = DMAOFFSET, | ||
6149 | + .end = DMAEND, | ||
6150 | + .flags = IORESOURCE_MEM, | ||
6151 | + }, | ||
6152 | + { | ||
6153 | + .start = IRQ_TIMBERDALE_DMA, | ||
6154 | + .end = IRQ_TIMBERDALE_DMA, | ||
6155 | + .flags = IORESOURCE_IRQ, | ||
6156 | + }, | ||
6157 | +}; | ||
6158 | + | ||
6159 | +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { | ||
6160 | + { | ||
6161 | + .name = "timb-uart", | ||
6162 | + .num_resources = ARRAY_SIZE(timberdale_uart_resources), | ||
6163 | + .resources = timberdale_uart_resources, | ||
6164 | + }, | ||
6165 | + { | ||
6166 | + .name = "xiic-i2c", | ||
6167 | + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), | ||
6168 | + .resources = timberdale_xiic_resources, | ||
6169 | + .platform_data = &timberdale_xiic_platform_data, | ||
6170 | + .data_size = sizeof(timberdale_xiic_platform_data), | ||
6171 | + }, | ||
6172 | + { | ||
6173 | + .name = "timb-gpio", | ||
6174 | + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), | ||
6175 | + .resources = timberdale_gpio_resources, | ||
6176 | + .platform_data = &timberdale_gpio_platform_data, | ||
6177 | + .data_size = sizeof(timberdale_gpio_platform_data), | ||
6178 | + }, | ||
6179 | + { | ||
6180 | + .name = "timb-i2s", | ||
6181 | + .num_resources = ARRAY_SIZE(timberdale_i2s_resources), | ||
6182 | + .resources = timberdale_i2s_resources, | ||
6183 | + .platform_data = &timbi2s_platform_data, | ||
6184 | + .data_size = sizeof(timbi2s_platform_data), | ||
6185 | + }, | ||
6186 | + { | ||
6187 | + .name = "timb-most", | ||
6188 | + .num_resources = ARRAY_SIZE(timberdale_most_resources), | ||
6189 | + .resources = timberdale_most_resources, | ||
6190 | + .platform_data = &timberdale_mlb_platform_data, | ||
6191 | + .data_size = sizeof(timberdale_mlb_platform_data), | ||
6192 | + }, | ||
6193 | + { | ||
6194 | + .name = "timb-video", | ||
6195 | + .num_resources = ARRAY_SIZE(timberdale_video_resources), | ||
6196 | + .resources = timberdale_video_resources, | ||
6197 | + .platform_data = &timberdale_video_platform_data, | ||
6198 | + .data_size = sizeof(timberdale_video_platform_data), | ||
6199 | + }, | ||
6200 | + { | ||
6201 | + .name = "timb-radio", | ||
6202 | + .num_resources = ARRAY_SIZE(timberdale_radio_resources), | ||
6203 | + .resources = timberdale_radio_resources, | ||
6204 | + .platform_data = &timberdale_radio_platform_data, | ||
6205 | + .data_size = sizeof(timberdale_radio_platform_data), | ||
6206 | + }, | ||
6207 | + { | ||
6208 | + .name = "xilinx_spi", | ||
6209 | + .num_resources = ARRAY_SIZE(timberdale_spi_resources), | ||
6210 | + .resources = timberdale_spi_resources, | ||
6211 | + .platform_data = &timberdale_xspi_platform_data, | ||
6212 | + .data_size = sizeof(timberdale_xspi_platform_data), | ||
6213 | + }, | ||
6214 | + { | ||
6215 | + .name = "ks8842", | ||
6216 | + .num_resources = ARRAY_SIZE(timberdale_eth_resources), | ||
6217 | + .resources = timberdale_eth_resources, | ||
6218 | + }, | ||
6219 | + { | ||
6220 | + .name = "timb-dma", | ||
6221 | + .num_resources = ARRAY_SIZE(timberdale_dma_resources), | ||
6222 | + .resources = timberdale_dma_resources, | ||
6223 | + }, | ||
6224 | +}; | ||
6225 | + | ||
6226 | +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { | ||
6227 | + { | ||
6228 | + .name = "timb-uart", | ||
6229 | + .num_resources = ARRAY_SIZE(timberdale_uart_resources), | ||
6230 | + .resources = timberdale_uart_resources, | ||
6231 | + }, | ||
6232 | + { | ||
6233 | + .name = "uartlite", | ||
6234 | + .num_resources = ARRAY_SIZE(timberdale_uartlite_resources), | ||
6235 | + .resources = timberdale_uartlite_resources, | ||
6236 | + }, | ||
6237 | + { | ||
6238 | + .name = "xiic-i2c", | ||
6239 | + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), | ||
6240 | + .resources = timberdale_xiic_resources, | ||
6241 | + .platform_data = &timberdale_xiic_platform_data, | ||
6242 | + .data_size = sizeof(timberdale_xiic_platform_data), | ||
6243 | + }, | ||
6244 | + { | ||
6245 | + .name = "timb-gpio", | ||
6246 | + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), | ||
6247 | + .resources = timberdale_gpio_resources, | ||
6248 | + .platform_data = &timberdale_gpio_platform_data, | ||
6249 | + .data_size = sizeof(timberdale_gpio_platform_data), | ||
6250 | + }, | ||
6251 | + { | ||
6252 | + .name = "timb-mlogicore", | ||
6253 | + .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources), | ||
6254 | + .resources = timberdale_mlogicore_resources, | ||
6255 | + }, | ||
6256 | + { | ||
6257 | + .name = "timb-video", | ||
6258 | + .num_resources = ARRAY_SIZE(timberdale_video_resources), | ||
6259 | + .resources = timberdale_video_resources, | ||
6260 | + .platform_data = &timberdale_video_platform_data, | ||
6261 | + .data_size = sizeof(timberdale_video_platform_data), | ||
6262 | + }, | ||
6263 | + { | ||
6264 | + .name = "timb-radio", | ||
6265 | + .num_resources = ARRAY_SIZE(timberdale_radio_resources), | ||
6266 | + .resources = timberdale_radio_resources, | ||
6267 | + .platform_data = &timberdale_radio_platform_data, | ||
6268 | + .data_size = sizeof(timberdale_radio_platform_data), | ||
6269 | + }, | ||
6270 | + { | ||
6271 | + .name = "xilinx_spi", | ||
6272 | + .num_resources = ARRAY_SIZE(timberdale_spi_resources), | ||
6273 | + .resources = timberdale_spi_resources, | ||
6274 | + .platform_data = &timberdale_xspi_platform_data, | ||
6275 | + .data_size = sizeof(timberdale_xspi_platform_data), | ||
6276 | + }, | ||
6277 | + { | ||
6278 | + .name = "ks8842", | ||
6279 | + .num_resources = ARRAY_SIZE(timberdale_eth_resources), | ||
6280 | + .resources = timberdale_eth_resources, | ||
6281 | + }, | ||
6282 | + { | ||
6283 | + .name = "timb-dma", | ||
6284 | + .num_resources = ARRAY_SIZE(timberdale_dma_resources), | ||
6285 | + .resources = timberdale_dma_resources, | ||
6286 | + }, | ||
6287 | +}; | ||
6288 | + | ||
6289 | +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { | ||
6290 | + { | ||
6291 | + .name = "timb-uart", | ||
6292 | + .num_resources = ARRAY_SIZE(timberdale_uart_resources), | ||
6293 | + .resources = timberdale_uart_resources, | ||
6294 | + }, | ||
6295 | + { | ||
6296 | + .name = "xiic-i2c", | ||
6297 | + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), | ||
6298 | + .resources = timberdale_xiic_resources, | ||
6299 | + .platform_data = &timberdale_xiic_platform_data, | ||
6300 | + .data_size = sizeof(timberdale_xiic_platform_data), | ||
6301 | + }, | ||
6302 | + { | ||
6303 | + .name = "timb-gpio", | ||
6304 | + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), | ||
6305 | + .resources = timberdale_gpio_resources, | ||
6306 | + .platform_data = &timberdale_gpio_platform_data, | ||
6307 | + .data_size = sizeof(timberdale_gpio_platform_data), | ||
6308 | + }, | ||
6309 | + { | ||
6310 | + .name = "timb-video", | ||
6311 | + .num_resources = ARRAY_SIZE(timberdale_video_resources), | ||
6312 | + .resources = timberdale_video_resources, | ||
6313 | + .platform_data = &timberdale_video_platform_data, | ||
6314 | + .data_size = sizeof(timberdale_video_platform_data), | ||
6315 | + }, | ||
6316 | + { | ||
6317 | + .name = "timb-radio", | ||
6318 | + .num_resources = ARRAY_SIZE(timberdale_radio_resources), | ||
6319 | + .resources = timberdale_radio_resources, | ||
6320 | + .platform_data = &timberdale_radio_platform_data, | ||
6321 | + .data_size = sizeof(timberdale_radio_platform_data), | ||
6322 | + }, | ||
6323 | + { | ||
6324 | + .name = "xilinx_spi", | ||
6325 | + .num_resources = ARRAY_SIZE(timberdale_spi_resources), | ||
6326 | + .resources = timberdale_spi_resources, | ||
6327 | + .platform_data = &timberdale_xspi_platform_data, | ||
6328 | + .data_size = sizeof(timberdale_xspi_platform_data), | ||
6329 | + }, | ||
6330 | + { | ||
6331 | + .name = "timb-dma", | ||
6332 | + .num_resources = ARRAY_SIZE(timberdale_dma_resources), | ||
6333 | + .resources = timberdale_dma_resources, | ||
6334 | + }, | ||
6335 | +}; | ||
6336 | + | ||
6337 | +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { | ||
6338 | + { | ||
6339 | + .name = "timb-uart", | ||
6340 | + .num_resources = ARRAY_SIZE(timberdale_uart_resources), | ||
6341 | + .resources = timberdale_uart_resources, | ||
6342 | + }, | ||
6343 | + { | ||
6344 | + .name = "ocores-i2c", | ||
6345 | + .num_resources = ARRAY_SIZE(timberdale_ocores_resources), | ||
6346 | + .resources = timberdale_ocores_resources, | ||
6347 | + .platform_data = &timberdale_ocores_platform_data, | ||
6348 | + .data_size = sizeof(timberdale_ocores_platform_data), | ||
6349 | + }, | ||
6350 | + { | ||
6351 | + .name = "timb-gpio", | ||
6352 | + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), | ||
6353 | + .resources = timberdale_gpio_resources, | ||
6354 | + .platform_data = &timberdale_gpio_platform_data, | ||
6355 | + .data_size = sizeof(timberdale_gpio_platform_data), | ||
6356 | + }, | ||
6357 | + { | ||
6358 | + .name = "timb-i2s", | ||
6359 | + .num_resources = ARRAY_SIZE(timberdale_i2s_resources), | ||
6360 | + .resources = timberdale_i2s_resources, | ||
6361 | + .platform_data = &timbi2s_platform_data, | ||
6362 | + .data_size = sizeof(timbi2s_platform_data), | ||
6363 | + }, | ||
6364 | + { | ||
6365 | + .name = "timb-most", | ||
6366 | + .num_resources = ARRAY_SIZE(timberdale_most_resources), | ||
6367 | + .resources = timberdale_most_resources, | ||
6368 | + .platform_data = &timberdale_mlb_platform_data, | ||
6369 | + .data_size = sizeof(timberdale_mlb_platform_data), | ||
6370 | + }, | ||
6371 | + { | ||
6372 | + .name = "timb-video", | ||
6373 | + .num_resources = ARRAY_SIZE(timberdale_video_resources), | ||
6374 | + .resources = timberdale_video_resources, | ||
6375 | + .platform_data = &timberdale_video_platform_data, | ||
6376 | + .data_size = sizeof(timberdale_video_platform_data), | ||
6377 | + }, | ||
6378 | + { | ||
6379 | + .name = "timb-radio", | ||
6380 | + .num_resources = ARRAY_SIZE(timberdale_radio_resources), | ||
6381 | + .resources = timberdale_radio_resources, | ||
6382 | + .platform_data = &timberdale_radio_platform_data, | ||
6383 | + .data_size = sizeof(timberdale_radio_platform_data), | ||
6384 | + }, | ||
6385 | + { | ||
6386 | + .name = "xilinx_spi", | ||
6387 | + .num_resources = ARRAY_SIZE(timberdale_spi_resources), | ||
6388 | + .resources = timberdale_spi_resources, | ||
6389 | + .platform_data = &timberdale_xspi_platform_data, | ||
6390 | + .data_size = sizeof(timberdale_xspi_platform_data), | ||
6391 | + }, | ||
6392 | + { | ||
6393 | + .name = "ks8842", | ||
6394 | + .num_resources = ARRAY_SIZE(timberdale_eth_resources), | ||
6395 | + .resources = timberdale_eth_resources, | ||
6396 | + }, | ||
6397 | + { | ||
6398 | + .name = "timb-dma", | ||
6399 | + .num_resources = ARRAY_SIZE(timberdale_dma_resources), | ||
6400 | + .resources = timberdale_dma_resources, | ||
6401 | + }, | ||
6402 | +}; | ||
6403 | + | ||
6404 | +static const __devinitconst struct resource timberdale_sdhc_resources[] = { | ||
6405 | + /* located in bar 1 and bar 2 */ | ||
6406 | + { | ||
6407 | + .start = SDHC0OFFSET, | ||
6408 | + .end = SDHC0END, | ||
6409 | + .flags = IORESOURCE_MEM, | ||
6410 | + }, | ||
6411 | + { | ||
6412 | + .start = IRQ_TIMBERDALE_SDHC, | ||
6413 | + .end = IRQ_TIMBERDALE_SDHC, | ||
6414 | + .flags = IORESOURCE_IRQ, | ||
6415 | + }, | ||
6416 | +}; | ||
6417 | + | ||
6418 | +static __devinitdata struct mfd_cell timberdale_cells_bar1[] = { | ||
6419 | + { | ||
6420 | + .name = "sdhci", | ||
6421 | + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), | ||
6422 | + .resources = timberdale_sdhc_resources, | ||
6423 | + }, | ||
6424 | +}; | ||
6425 | + | ||
6426 | +static __devinitdata struct mfd_cell timberdale_cells_bar2[] = { | ||
6427 | + { | ||
6428 | + .name = "sdhci", | ||
6429 | + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), | ||
6430 | + .resources = timberdale_sdhc_resources, | ||
6431 | + }, | ||
6432 | +}; | ||
6433 | + | ||
6434 | +static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, | ||
6435 | + char *buf) | ||
6436 | +{ | ||
6437 | + struct pci_dev *pdev = to_pci_dev(dev); | ||
6438 | + struct timberdale_device *priv = pci_get_drvdata(pdev); | ||
6439 | + | ||
6440 | + return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor, | ||
6441 | + priv->fw.config); | ||
6442 | +} | ||
6443 | + | ||
6444 | +static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL); | ||
6445 | + | ||
6446 | +/*--------------------------------------------------------------------------*/ | ||
6447 | + | ||
6448 | +static int __devinit timb_probe(struct pci_dev *dev, | ||
6449 | + const struct pci_device_id *id) | ||
6450 | +{ | ||
6451 | + struct timberdale_device *priv; | ||
6452 | + int err, i; | ||
6453 | + resource_size_t mapbase; | ||
6454 | + struct msix_entry *msix_entries = NULL; | ||
6455 | + u8 ip_setup; | ||
6456 | + | ||
6457 | + priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
6458 | + if (!priv) | ||
6459 | + return -ENOMEM; | ||
6460 | + | ||
6461 | + spin_lock_init(&priv->lock); | ||
6462 | + pci_set_drvdata(dev, priv); | ||
6463 | + | ||
6464 | + err = pci_enable_device(dev); | ||
6465 | + if (err) | ||
6466 | + goto err_enable; | ||
6467 | + | ||
6468 | + mapbase = pci_resource_start(dev, 0); | ||
6469 | + if (!mapbase) { | ||
6470 | + printk(KERN_ERR DRIVER_NAME ": No resource\n"); | ||
6471 | + goto err_start; | ||
6472 | + } | ||
6473 | + | ||
6474 | + /* create a resource for the PCI master register */ | ||
6475 | + priv->ctl_mapbase = mapbase + CHIPCTLOFFSET; | ||
6476 | + if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) { | ||
6477 | + printk(KERN_ERR DRIVER_NAME ": Failed to request ctl mem\n"); | ||
6478 | + goto err_request; | ||
6479 | + } | ||
6480 | + | ||
6481 | + priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); | ||
6482 | + if (!priv->ctl_membase) { | ||
6483 | + printk(KERN_ALERT DRIVER_NAME": Map error, ctl\n"); | ||
6484 | + goto err_ioremap; | ||
6485 | + } | ||
6486 | + | ||
6487 | + /* read the HW config */ | ||
6488 | + priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR); | ||
6489 | + priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR); | ||
6490 | + priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG); | ||
6491 | + | ||
6492 | + if (priv->fw.major > TIMB_SUPPORTED_MAJOR) { | ||
6493 | + printk(KERN_ERR DRIVER_NAME": The driver supports an older " | ||
6494 | + "version of the FPGA, please update the driver to " | ||
6495 | + "support %d.%d\n", priv->fw.major, priv->fw.minor); | ||
6496 | + goto err_ioremap; | ||
6497 | + } | ||
6498 | + if (priv->fw.major < TIMB_SUPPORTED_MAJOR || | ||
6499 | + priv->fw.minor < TIMB_REQUIRED_MINOR) { | ||
6500 | + printk(KERN_ERR DRIVER_NAME | ||
6501 | + ": The FPGA image is too old (%d.%d), " | ||
6502 | + "please upgrade the FPGA to at least: %d.%d\n", | ||
6503 | + priv->fw.major, priv->fw.minor, | ||
6504 | + TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR); | ||
6505 | + goto err_ioremap; | ||
6506 | + } | ||
6507 | + | ||
6508 | + msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries), | ||
6509 | + GFP_KERNEL); | ||
6510 | + if (!msix_entries) | ||
6511 | + goto err_ioremap; | ||
6512 | + | ||
6513 | + for (i = 0; i < TIMBERDALE_NR_IRQS; i++) | ||
6514 | + msix_entries[i].entry = i; | ||
6515 | + | ||
6516 | + err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS); | ||
6517 | + if (err) { | ||
6518 | + printk(KERN_WARNING DRIVER_NAME | ||
6519 | + ": MSI-X init failed: %d, expected entries: %d\n", | ||
6520 | + err, TIMBERDALE_NR_IRQS); | ||
6521 | + goto err_msix; | ||
6522 | + } | ||
6523 | + | ||
6524 | + err = device_create_file(&dev->dev, &dev_attr_fw_ver); | ||
6525 | + if (err) | ||
6526 | + goto err_create_file; | ||
6527 | + | ||
6528 | + /* Reset all FPGA PLB peripherals */ | ||
6529 | + iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST); | ||
6530 | + | ||
6531 | + /* update IRQ offsets in I2C board info */ | ||
6532 | + for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++) | ||
6533 | + timberdale_i2c_board_info[i].irq = | ||
6534 | + msix_entries[timberdale_i2c_board_info[i].irq].vector; | ||
6535 | + | ||
6536 | + /* Update the SPI configuration depending on the HW (8 or 16 bit) */ | ||
6537 | + if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) { | ||
6538 | + timberdale_xspi_platform_data.bits_per_word = 8; | ||
6539 | + timberdale_xspi_platform_data.devices = | ||
6540 | + timberdale_spi_8bit_board_info; | ||
6541 | + timberdale_xspi_platform_data.num_devices = | ||
6542 | + ARRAY_SIZE(timberdale_spi_8bit_board_info); | ||
6543 | + } else { | ||
6544 | + timberdale_xspi_platform_data.bits_per_word = 16; | ||
6545 | + timberdale_xspi_platform_data.devices = | ||
6546 | + timberdale_spi_16bit_board_info; | ||
6547 | + timberdale_xspi_platform_data.num_devices = | ||
6548 | + ARRAY_SIZE(timberdale_spi_16bit_board_info); | ||
6549 | + } | ||
6550 | + | ||
6551 | + ip_setup = priv->fw.config & TIMB_HW_VER_MASK; | ||
6552 | + if (ip_setup == TIMB_HW_VER0) | ||
6553 | + err = mfd_add_devices(&dev->dev, -1, | ||
6554 | + timberdale_cells_bar0_cfg0, | ||
6555 | + ARRAY_SIZE(timberdale_cells_bar0_cfg0), | ||
6556 | + &dev->resource[0], msix_entries[0].vector); | ||
6557 | + else if (ip_setup == TIMB_HW_VER1) | ||
6558 | + err = mfd_add_devices(&dev->dev, -1, | ||
6559 | + timberdale_cells_bar0_cfg1, | ||
6560 | + ARRAY_SIZE(timberdale_cells_bar0_cfg1), | ||
6561 | + &dev->resource[0], msix_entries[0].vector); | ||
6562 | + else if (ip_setup == TIMB_HW_VER2) | ||
6563 | + err = mfd_add_devices(&dev->dev, -1, | ||
6564 | + timberdale_cells_bar0_cfg2, | ||
6565 | + ARRAY_SIZE(timberdale_cells_bar0_cfg2), | ||
6566 | + &dev->resource[0], msix_entries[0].vector); | ||
6567 | + else if (ip_setup == TIMB_HW_VER3) | ||
6568 | + err = mfd_add_devices(&dev->dev, -1, | ||
6569 | + timberdale_cells_bar0_cfg3, | ||
6570 | + ARRAY_SIZE(timberdale_cells_bar0_cfg3), | ||
6571 | + &dev->resource[0], msix_entries[0].vector); | ||
6572 | + else { | ||
6573 | + /* unknown version */ | ||
6574 | + printk(KERN_ERR"Uknown IP setup: %d.%d.%d\n", | ||
6575 | + priv->fw.major, priv->fw.minor, ip_setup); | ||
6576 | + err = -ENODEV; | ||
6577 | + goto err_mfd; | ||
6578 | + } | ||
6579 | + | ||
6580 | + if (err) { | ||
6581 | + printk(KERN_WARNING DRIVER_NAME | ||
6582 | + ": mfd_add_devices failed: %d\n", err); | ||
6583 | + goto err_mfd; | ||
6584 | + } | ||
6585 | + | ||
6586 | + err = mfd_add_devices(&dev->dev, 0, | ||
6587 | + timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1), | ||
6588 | + &dev->resource[1], msix_entries[0].vector); | ||
6589 | + if (err) { | ||
6590 | + printk(KERN_WARNING DRIVER_NAME | ||
6591 | + "mfd_add_devices failed: %d\n", err); | ||
6592 | + goto err_mfd2; | ||
6593 | + } | ||
6594 | + | ||
6595 | + /* only version 0 and 3 have the iNand routed to SDHCI */ | ||
6596 | + if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) || | ||
6597 | + ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) { | ||
6598 | + err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2, | ||
6599 | + ARRAY_SIZE(timberdale_cells_bar2), | ||
6600 | + &dev->resource[2], msix_entries[0].vector); | ||
6601 | + if (err) { | ||
6602 | + printk(KERN_WARNING DRIVER_NAME | ||
6603 | + ": mfd_add_devices failed: %d\n", err); | ||
6604 | + goto err_mfd2; | ||
6605 | + } | ||
6606 | + } | ||
6607 | + | ||
6608 | + kfree(msix_entries); | ||
6609 | + | ||
6610 | + printk(KERN_INFO | ||
6611 | + "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n", | ||
6612 | + priv->fw.major, priv->fw.minor, priv->fw.config); | ||
6613 | + | ||
6614 | + return 0; | ||
6615 | + | ||
6616 | +err_mfd2: | ||
6617 | + mfd_remove_devices(&dev->dev); | ||
6618 | +err_mfd: | ||
6619 | + device_remove_file(&dev->dev, &dev_attr_fw_ver); | ||
6620 | +err_create_file: | ||
6621 | + pci_disable_msix(dev); | ||
6622 | +err_msix: | ||
6623 | + iounmap(priv->ctl_membase); | ||
6624 | +err_ioremap: | ||
6625 | + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); | ||
6626 | +err_request: | ||
6627 | + pci_set_drvdata(dev, NULL); | ||
6628 | +err_start: | ||
6629 | + pci_disable_device(dev); | ||
6630 | +err_enable: | ||
6631 | + kfree(msix_entries); | ||
6632 | + kfree(priv); | ||
6633 | + pci_set_drvdata(dev, NULL); | ||
6634 | + return -ENODEV; | ||
6635 | +} | ||
6636 | + | ||
6637 | +static void __devexit timb_remove(struct pci_dev *dev) | ||
6638 | +{ | ||
6639 | + struct timberdale_device *priv = pci_get_drvdata(dev); | ||
6640 | + | ||
6641 | + mfd_remove_devices(&dev->dev); | ||
6642 | + | ||
6643 | + device_remove_file(&dev->dev, &dev_attr_fw_ver); | ||
6644 | + | ||
6645 | + iounmap(priv->ctl_membase); | ||
6646 | + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); | ||
6647 | + | ||
6648 | + pci_disable_msix(dev); | ||
6649 | + pci_disable_device(dev); | ||
6650 | + pci_set_drvdata(dev, NULL); | ||
6651 | + kfree(priv); | ||
6652 | +} | ||
6653 | + | ||
6654 | +static struct pci_device_id timberdale_pci_tbl[] = { | ||
6655 | + { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) }, | ||
6656 | + { 0 } | ||
6657 | +}; | ||
6658 | +MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl); | ||
6659 | + | ||
6660 | +static struct pci_driver timberdale_pci_driver = { | ||
6661 | + .name = DRIVER_NAME, | ||
6662 | + .id_table = timberdale_pci_tbl, | ||
6663 | + .probe = timb_probe, | ||
6664 | + .remove = __devexit_p(timb_remove), | ||
6665 | +}; | ||
6666 | + | ||
6667 | +static int __init timberdale_init(void) | ||
6668 | +{ | ||
6669 | + int err; | ||
6670 | + | ||
6671 | + err = pci_register_driver(&timberdale_pci_driver); | ||
6672 | + if (err < 0) { | ||
6673 | + printk(KERN_ERR | ||
6674 | + "Failed to register PCI driver for %s device.\n", | ||
6675 | + timberdale_pci_driver.name); | ||
6676 | + return -ENODEV; | ||
6677 | + } | ||
6678 | + | ||
6679 | + printk(KERN_INFO "Driver for %s has been successfully registered.\n", | ||
6680 | + timberdale_pci_driver.name); | ||
6681 | + | ||
6682 | + return 0; | ||
6683 | +} | ||
6684 | + | ||
6685 | +static void __exit timberdale_exit(void) | ||
6686 | +{ | ||
6687 | + pci_unregister_driver(&timberdale_pci_driver); | ||
6688 | + | ||
6689 | + printk(KERN_INFO "Driver for %s has been successfully unregistered.\n", | ||
6690 | + timberdale_pci_driver.name); | ||
6691 | +} | ||
6692 | + | ||
6693 | +module_init(timberdale_init); | ||
6694 | +module_exit(timberdale_exit); | ||
6695 | + | ||
6696 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
6697 | +MODULE_VERSION(DRV_VERSION); | ||
6698 | +MODULE_LICENSE("GPL v2"); | ||
6699 | + | ||
6700 | diff -uNr linux-2.6.31/drivers/mfd/timberdale.h linux-2.6.31.new/drivers/mfd/timberdale.h | ||
6701 | --- linux-2.6.31/drivers/mfd/timberdale.h 1969-12-31 16:00:00.000000000 -0800 | ||
6702 | +++ linux-2.6.31.new/drivers/mfd/timberdale.h 2009-10-23 11:17:29.000000000 -0700 | ||
6703 | @@ -0,0 +1,152 @@ | ||
6704 | +/* | ||
6705 | + * timberdale.h timberdale FPGA mfd shim driver defines | ||
6706 | + * Copyright (c) 2009 Intel Corporation | ||
6707 | + * | ||
6708 | + * This program is free software; you can redistribute it and/or modify | ||
6709 | + * it under the terms of the GNU General Public License version 2 as | ||
6710 | + * published by the Free Software Foundation. | ||
6711 | + * | ||
6712 | + * This program is distributed in the hope that it will be useful, | ||
6713 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
6714 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
6715 | + * GNU General Public License for more details. | ||
6716 | + * | ||
6717 | + * You should have received a copy of the GNU General Public License | ||
6718 | + * along with this program; if not, write to the Free Software | ||
6719 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
6720 | + */ | ||
6721 | + | ||
6722 | +/* Supports: | ||
6723 | + * Timberdale FPGA | ||
6724 | + */ | ||
6725 | + | ||
6726 | +#ifndef MFD_TIMBERDALE_H | ||
6727 | +#define MFD_TIMBERDALE_H | ||
6728 | + | ||
6729 | +#define DRV_VERSION "1.0" | ||
6730 | + | ||
6731 | +/* This driver only support versions >= 3.8 and < 4.0 */ | ||
6732 | +#define TIMB_SUPPORTED_MAJOR 3 | ||
6733 | + | ||
6734 | +/* This driver only support minor >= 8 */ | ||
6735 | +#define TIMB_REQUIRED_MINOR 8 | ||
6736 | + | ||
6737 | +/* Registers of the interrupt controller */ | ||
6738 | +#define ISR 0x00 | ||
6739 | +#define IPR 0x04 | ||
6740 | +#define IER 0x08 | ||
6741 | +#define IAR 0x0c | ||
6742 | +#define SIE 0x10 | ||
6743 | +#define CIE 0x14 | ||
6744 | +#define MER 0x1c | ||
6745 | + | ||
6746 | +/* Registers of the control area */ | ||
6747 | +#define TIMB_REV_MAJOR 0x00 | ||
6748 | +#define TIMB_REV_MINOR 0x04 | ||
6749 | +#define TIMB_HW_CONFIG 0x08 | ||
6750 | +#define TIMB_SW_RST 0x40 | ||
6751 | + | ||
6752 | +/* bits in the TIMB_HW_CONFIG register */ | ||
6753 | +#define TIMB_HW_CONFIG_SPI_8BIT 0x80 | ||
6754 | + | ||
6755 | +#define TIMB_HW_VER_MASK 0x0f | ||
6756 | +#define TIMB_HW_VER0 0x00 | ||
6757 | +#define TIMB_HW_VER1 0x01 | ||
6758 | +#define TIMB_HW_VER2 0x02 | ||
6759 | +#define TIMB_HW_VER3 0x03 | ||
6760 | + | ||
6761 | +#define OCORESOFFSET 0x0 | ||
6762 | +#define OCORESEND 0x1f | ||
6763 | + | ||
6764 | +#define SPIOFFSET 0x80 | ||
6765 | +#define SPIEND 0xff | ||
6766 | + | ||
6767 | +#define UARTLITEOFFSET 0x100 | ||
6768 | +#define UARTLITEEND 0x10f | ||
6769 | + | ||
6770 | +#define RDSOFFSET 0x180 | ||
6771 | +#define RDSEND 0x183 | ||
6772 | + | ||
6773 | +#define ETHOFFSET 0x300 | ||
6774 | +#define ETHEND 0x3ff | ||
6775 | + | ||
6776 | +#define GPIOOFFSET 0x400 | ||
6777 | +#define GPIOEND 0x7ff | ||
6778 | + | ||
6779 | +#define CHIPCTLOFFSET 0x800 | ||
6780 | +#define CHIPCTLEND 0x8ff | ||
6781 | +#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) | ||
6782 | + | ||
6783 | +#define INTCOFFSET 0xc00 | ||
6784 | +#define INTCEND 0xfff | ||
6785 | +#define INTCSIZE (INTCEND - INTCOFFSET) | ||
6786 | + | ||
6787 | +#define MOSTOFFSET 0x1000 | ||
6788 | +#define MOSTEND 0x13ff | ||
6789 | + | ||
6790 | +#define UARTOFFSET 0x1400 | ||
6791 | +#define UARTEND 0x17ff | ||
6792 | + | ||
6793 | +#define XIICOFFSET 0x1800 | ||
6794 | +#define XIICEND 0x19ff | ||
6795 | + | ||
6796 | +#define I2SOFFSET 0x1C00 | ||
6797 | +#define I2SEND 0x1fff | ||
6798 | + | ||
6799 | +#define LOGIWOFFSET 0x30000 | ||
6800 | +#define LOGIWEND 0x37fff | ||
6801 | + | ||
6802 | +#define MLCOREOFFSET 0x40000 | ||
6803 | +#define MLCOREEND 0x43fff | ||
6804 | + | ||
6805 | +#define DMAOFFSET 0x01000000 | ||
6806 | +#define DMAEND 0x013fffff | ||
6807 | + | ||
6808 | +/* SDHC0 is placed in PCI bar 1 */ | ||
6809 | +#define SDHC0OFFSET 0x00 | ||
6810 | +#define SDHC0END 0xff | ||
6811 | + | ||
6812 | +/* SDHC1 is placed in PCI bar 2 */ | ||
6813 | +#define SDHC1OFFSET 0x00 | ||
6814 | +#define SDHC1END 0xff | ||
6815 | + | ||
6816 | +#define PCI_VENDOR_ID_TIMB 0x10ee | ||
6817 | +#define PCI_DEVICE_ID_TIMB 0xa123 | ||
6818 | + | ||
6819 | +#define IRQ_TIMBERDALE_INIC 0 | ||
6820 | +#define IRQ_TIMBERDALE_MLB 1 | ||
6821 | +#define IRQ_TIMBERDALE_GPIO 2 | ||
6822 | +#define IRQ_TIMBERDALE_I2C 3 | ||
6823 | +#define IRQ_TIMBERDALE_UART 4 | ||
6824 | +#define IRQ_TIMBERDALE_DMA 5 | ||
6825 | +#define IRQ_TIMBERDALE_I2S 6 | ||
6826 | +#define IRQ_TIMBERDALE_TSC_INT 7 | ||
6827 | +#define IRQ_TIMBERDALE_SDHC 8 | ||
6828 | +#define IRQ_TIMBERDALE_ADV7180 9 | ||
6829 | +#define IRQ_TIMBERDALE_ETHSW_IF 10 | ||
6830 | +#define IRQ_TIMBERDALE_SPI 11 | ||
6831 | +#define IRQ_TIMBERDALE_UARTLITE 12 | ||
6832 | +#define IRQ_TIMBERDALE_MLCORE 13 | ||
6833 | +#define IRQ_TIMBERDALE_MLCORE_BUF 14 | ||
6834 | +#define IRQ_TIMBERDALE_RDS 15 | ||
6835 | + | ||
6836 | +#define TIMBERDALE_NR_IRQS 16 | ||
6837 | + | ||
6838 | +/* Some of the interrupts are level triggered, some are edge triggered */ | ||
6839 | +#define IRQ_TIMBERDALE_EDGE_MASK ((1 << IRQ_TIMBERDALE_ADV7180) | \ | ||
6840 | + (1 << IRQ_TIMBERDALE_TSC_INT) | \ | ||
6841 | + (1 << IRQ_TIMBERDALE_MLB) | (1 << IRQ_TIMBERDALE_INIC)) | ||
6842 | + | ||
6843 | +#define IRQ_TIMBERDALE_LEVEL_MASK ((1 << IRQ_TIMBERDALE_SPI) | \ | ||
6844 | + (1 << IRQ_TIMBERDALE_ETHSW_IF) | (1 << IRQ_TIMBERDALE_SDHC) | \ | ||
6845 | + (1 << IRQ_TIMBERDALE_I2S) | (1 << IRQ_TIMBERDALE_UART) | \ | ||
6846 | + (1 << IRQ_TIMBERDALE_I2C) | (1 << IRQ_TIMBERDALE_GPIO) | \ | ||
6847 | + (1 << IRQ_TIMBERDALE_DMA)) | ||
6848 | + | ||
6849 | +#define GPIO_PIN_ASCB 8 | ||
6850 | +#define GPIO_PIN_INIC_RST 14 | ||
6851 | +#define GPIO_PIN_BT_RST 15 | ||
6852 | +#define GPIO_NR_PINS 16 | ||
6853 | + | ||
6854 | +#endif | ||
6855 | + | ||
6856 | diff -uNr linux-2.6.31/drivers/mmc/host/sdhci.c linux-2.6.31.new/drivers/mmc/host/sdhci.c | ||
6857 | --- linux-2.6.31/drivers/mmc/host/sdhci.c 2009-10-23 11:18:30.000000000 -0700 | ||
6858 | +++ linux-2.6.31.new/drivers/mmc/host/sdhci.c 2009-10-23 11:17:25.000000000 -0700 | ||
6859 | @@ -652,7 +652,7 @@ | ||
6860 | count = sdhci_calc_timeout(host, data); | ||
6861 | sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); | ||
6862 | |||
6863 | - if (host->flags & SDHCI_USE_DMA) | ||
6864 | + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) | ||
6865 | host->flags |= SDHCI_REQ_USE_DMA; | ||
6866 | |||
6867 | /* | ||
6868 | @@ -1597,7 +1597,7 @@ | ||
6869 | { | ||
6870 | int ret; | ||
6871 | |||
6872 | - if (host->flags & SDHCI_USE_DMA) { | ||
6873 | + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { | ||
6874 | if (host->ops->enable_dma) | ||
6875 | host->ops->enable_dma(host); | ||
6876 | } | ||
6877 | @@ -1678,23 +1678,20 @@ | ||
6878 | caps = sdhci_readl(host, SDHCI_CAPABILITIES); | ||
6879 | |||
6880 | if (host->quirks & SDHCI_QUIRK_FORCE_DMA) | ||
6881 | - host->flags |= SDHCI_USE_DMA; | ||
6882 | - else if (!(caps & SDHCI_CAN_DO_DMA)) | ||
6883 | - DBG("Controller doesn't have DMA capability\n"); | ||
6884 | + host->flags |= SDHCI_USE_SDMA; | ||
6885 | + else if (!(caps & SDHCI_CAN_DO_SDMA)) | ||
6886 | + DBG("Controller doesn't have SDMA capability\n"); | ||
6887 | else | ||
6888 | - host->flags |= SDHCI_USE_DMA; | ||
6889 | + host->flags |= SDHCI_USE_SDMA; | ||
6890 | |||
6891 | if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) && | ||
6892 | - (host->flags & SDHCI_USE_DMA)) { | ||
6893 | + (host->flags & SDHCI_USE_SDMA)) { | ||
6894 | DBG("Disabling DMA as it is marked broken\n"); | ||
6895 | - host->flags &= ~SDHCI_USE_DMA; | ||
6896 | + host->flags &= ~SDHCI_USE_SDMA; | ||
6897 | } | ||
6898 | |||
6899 | - if (host->flags & SDHCI_USE_DMA) { | ||
6900 | - if ((host->version >= SDHCI_SPEC_200) && | ||
6901 | - (caps & SDHCI_CAN_DO_ADMA2)) | ||
6902 | - host->flags |= SDHCI_USE_ADMA; | ||
6903 | - } | ||
6904 | + if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) | ||
6905 | + host->flags |= SDHCI_USE_ADMA; | ||
6906 | |||
6907 | if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) && | ||
6908 | (host->flags & SDHCI_USE_ADMA)) { | ||
6909 | @@ -1702,13 +1699,14 @@ | ||
6910 | host->flags &= ~SDHCI_USE_ADMA; | ||
6911 | } | ||
6912 | |||
6913 | - if (host->flags & SDHCI_USE_DMA) { | ||
6914 | + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { | ||
6915 | if (host->ops->enable_dma) { | ||
6916 | if (host->ops->enable_dma(host)) { | ||
6917 | printk(KERN_WARNING "%s: No suitable DMA " | ||
6918 | "available. Falling back to PIO.\n", | ||
6919 | mmc_hostname(mmc)); | ||
6920 | - host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA); | ||
6921 | + host->flags &= | ||
6922 | + ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); | ||
6923 | } | ||
6924 | } | ||
6925 | } | ||
6926 | @@ -1736,7 +1734,7 @@ | ||
6927 | * mask, but PIO does not need the hw shim so we set a new | ||
6928 | * mask here in that case. | ||
6929 | */ | ||
6930 | - if (!(host->flags & SDHCI_USE_DMA)) { | ||
6931 | + if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) { | ||
6932 | host->dma_mask = DMA_BIT_MASK(64); | ||
6933 | mmc_dev(host->mmc)->dma_mask = &host->dma_mask; | ||
6934 | } | ||
6935 | @@ -1810,7 +1808,7 @@ | ||
6936 | */ | ||
6937 | if (host->flags & SDHCI_USE_ADMA) | ||
6938 | mmc->max_hw_segs = 128; | ||
6939 | - else if (host->flags & SDHCI_USE_DMA) | ||
6940 | + else if (host->flags & SDHCI_USE_SDMA) | ||
6941 | mmc->max_hw_segs = 1; | ||
6942 | else /* PIO */ | ||
6943 | mmc->max_hw_segs = 128; | ||
6944 | @@ -1893,10 +1891,10 @@ | ||
6945 | |||
6946 | mmc_add_host(mmc); | ||
6947 | |||
6948 | - printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n", | ||
6949 | + printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n", | ||
6950 | mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), | ||
6951 | - (host->flags & SDHCI_USE_ADMA)?"A":"", | ||
6952 | - (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); | ||
6953 | + (host->flags & SDHCI_USE_ADMA) ? "ADMA" : | ||
6954 | + (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); | ||
6955 | |||
6956 | sdhci_enable_card_detection(host); | ||
6957 | |||
6958 | diff -uNr linux-2.6.31/drivers/mmc/host/sdhci.h linux-2.6.31.new/drivers/mmc/host/sdhci.h | ||
6959 | --- linux-2.6.31/drivers/mmc/host/sdhci.h 2009-10-23 11:18:30.000000000 -0700 | ||
6960 | +++ linux-2.6.31.new/drivers/mmc/host/sdhci.h 2009-10-23 11:17:25.000000000 -0700 | ||
6961 | @@ -143,7 +143,7 @@ | ||
6962 | #define SDHCI_CAN_DO_ADMA2 0x00080000 | ||
6963 | #define SDHCI_CAN_DO_ADMA1 0x00100000 | ||
6964 | #define SDHCI_CAN_DO_HISPD 0x00200000 | ||
6965 | -#define SDHCI_CAN_DO_DMA 0x00400000 | ||
6966 | +#define SDHCI_CAN_DO_SDMA 0x00400000 | ||
6967 | #define SDHCI_CAN_VDD_330 0x01000000 | ||
6968 | #define SDHCI_CAN_VDD_300 0x02000000 | ||
6969 | #define SDHCI_CAN_VDD_180 0x04000000 | ||
6970 | @@ -250,7 +250,7 @@ | ||
6971 | spinlock_t lock; /* Mutex */ | ||
6972 | |||
6973 | int flags; /* Host attributes */ | ||
6974 | -#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */ | ||
6975 | +#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ | ||
6976 | #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ | ||
6977 | #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ | ||
6978 | #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ | ||
6979 | diff -uNr linux-2.6.31/drivers/mmc/host/sdhci-pci.c linux-2.6.31.new/drivers/mmc/host/sdhci-pci.c | ||
6980 | --- linux-2.6.31/drivers/mmc/host/sdhci-pci.c 2009-10-23 11:18:30.000000000 -0700 | ||
6981 | +++ linux-2.6.31.new/drivers/mmc/host/sdhci-pci.c 2009-10-23 11:17:25.000000000 -0700 | ||
6982 | @@ -395,7 +395,7 @@ | ||
6983 | |||
6984 | if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && | ||
6985 | ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && | ||
6986 | - (host->flags & SDHCI_USE_DMA)) { | ||
6987 | + (host->flags & SDHCI_USE_SDMA)) { | ||
6988 | dev_warn(&pdev->dev, "Will use DMA mode even though HW " | ||
6989 | "doesn't fully claim to support it.\n"); | ||
6990 | } | ||
6991 | diff -uNr linux-2.6.31/drivers/net/Kconfig linux-2.6.31.new/drivers/net/Kconfig | ||
6992 | --- linux-2.6.31/drivers/net/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
6993 | +++ linux-2.6.31.new/drivers/net/Kconfig 2009-10-23 11:17:23.000000000 -0700 | ||
6994 | @@ -1730,6 +1730,16 @@ | ||
6995 | This platform driver is for Micrel KSZ8842 / KS8842 | ||
6996 | 2-port ethernet switch chip (managed, VLAN, QoS). | ||
6997 | |||
6998 | +config KS8842_TIMB_DMA | ||
6999 | + bool "Use Timberdale specific DMA engine" | ||
7000 | + depends on KS8842 && MFD_TIMBERDALE | ||
7001 | + select MFD_TIMBERDALE_DMA | ||
7002 | + help | ||
7003 | + This option enables usage of the timberdale specific DMA engine | ||
7004 | + for the KS8842 driver. Rather than using PIO which results in | ||
7005 | + single accesses over PCIe, the DMA block of the timberdale FPGA | ||
7006 | + will burst data to and from the KS8842. | ||
7007 | + | ||
7008 | config KS8851 | ||
7009 | tristate "Micrel KS8851 SPI" | ||
7010 | depends on SPI | ||
7011 | diff -uNr linux-2.6.31/drivers/net/ks8842.c linux-2.6.31.new/drivers/net/ks8842.c | ||
7012 | --- linux-2.6.31/drivers/net/ks8842.c 2009-10-23 11:18:30.000000000 -0700 | ||
7013 | +++ linux-2.6.31.new/drivers/net/ks8842.c 2009-10-23 11:17:22.000000000 -0700 | ||
7014 | @@ -26,11 +26,17 @@ | ||
7015 | #include <linux/netdevice.h> | ||
7016 | #include <linux/etherdevice.h> | ||
7017 | #include <linux/ethtool.h> | ||
7018 | +#include <linux/mfd/timbdma.h> | ||
7019 | |||
7020 | #define DRV_NAME "ks8842" | ||
7021 | |||
7022 | /* Timberdale specific Registers */ | ||
7023 | -#define REG_TIMB_RST 0x1c | ||
7024 | +#define REG_TIMB_RST 0x1c | ||
7025 | +#define REG_TIMB_FIFO 0x20 | ||
7026 | +#define REG_TIMB_ISR 0x24 | ||
7027 | +#define REG_TIMB_IER 0x28 | ||
7028 | +#define REG_TIMB_IAR 0x2C | ||
7029 | +#define REQ_TIMB_DMA_RESUME 0x30 | ||
7030 | |||
7031 | /* KS8842 registers */ | ||
7032 | |||
7033 | @@ -73,6 +79,11 @@ | ||
7034 | #define IRQ_RX_ERROR 0x0080 | ||
7035 | #define ENABLED_IRQS (IRQ_LINK_CHANGE | IRQ_TX | IRQ_RX | IRQ_RX_STOPPED | \ | ||
7036 | IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR) | ||
7037 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7038 | + #define ENABLED_IRQS_IP (IRQ_LINK_CHANGE | IRQ_RX_STOPPED | \ | ||
7039 | + IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR) | ||
7040 | + #define ENABLED_IRQS_DMA (ENABLED_IRQS_IP | IRQ_RX) | ||
7041 | +#endif | ||
7042 | #define REG_ISR 0x02 | ||
7043 | #define REG_RXSR 0x04 | ||
7044 | #define RXSR_VALID 0x8000 | ||
7045 | @@ -111,14 +122,50 @@ | ||
7046 | #define REG_P1CR4 0x02 | ||
7047 | #define REG_P1SR 0x04 | ||
7048 | |||
7049 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7050 | +#define DMA_BUFFER_SIZE 2048 | ||
7051 | + | ||
7052 | +#define DMA_DEV(a) ((a->dev->parent) ? a->dev->parent : a->dev) | ||
7053 | + | ||
7054 | +#define DMA_ONGOING(a) (a->dma_tx.ongoing | a->dma_rx.ongoing) | ||
7055 | + | ||
7056 | +struct ks8842_dma_ctl { | ||
7057 | + void *desc; | ||
7058 | + void *buf; | ||
7059 | + dma_addr_t addr; | ||
7060 | + unsigned ongoing; | ||
7061 | +}; | ||
7062 | + | ||
7063 | +struct ks8842_rx_dma_ctl { | ||
7064 | + void *desc; | ||
7065 | + struct sk_buff *skb; | ||
7066 | + dma_addr_t addr; | ||
7067 | +}; | ||
7068 | + | ||
7069 | +#endif | ||
7070 | + | ||
7071 | struct ks8842_adapter { | ||
7072 | void __iomem *hw_addr; | ||
7073 | int irq; | ||
7074 | struct tasklet_struct tasklet; | ||
7075 | spinlock_t lock; /* spinlock to be interrupt safe */ | ||
7076 | - struct platform_device *pdev; | ||
7077 | + struct device *dev; | ||
7078 | + struct work_struct timeout_work; | ||
7079 | + struct net_device *netdev; | ||
7080 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7081 | + unsigned use_dma; | ||
7082 | + struct ks8842_dma_ctl dma_tx; | ||
7083 | + struct ks8842_rx_dma_ctl dma_rx; | ||
7084 | +#endif | ||
7085 | }; | ||
7086 | |||
7087 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7088 | +static inline void ks8842_resume_dma(struct ks8842_adapter *adapter) | ||
7089 | +{ | ||
7090 | + iowrite32(1, adapter->hw_addr + REQ_TIMB_DMA_RESUME); | ||
7091 | +} | ||
7092 | +#endif | ||
7093 | + | ||
7094 | static inline void ks8842_select_bank(struct ks8842_adapter *adapter, u16 bank) | ||
7095 | { | ||
7096 | iowrite16(bank, adapter->hw_addr + REG_SELECT_BANK); | ||
7097 | @@ -195,7 +242,6 @@ | ||
7098 | msleep(10); | ||
7099 | iowrite16(0, adapter->hw_addr + REG_GRR); | ||
7100 | */ | ||
7101 | - iowrite16(32, adapter->hw_addr + REG_SELECT_BANK); | ||
7102 | iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST); | ||
7103 | msleep(20); | ||
7104 | } | ||
7105 | @@ -203,8 +249,10 @@ | ||
7106 | static void ks8842_update_link_status(struct net_device *netdev, | ||
7107 | struct ks8842_adapter *adapter) | ||
7108 | { | ||
7109 | + u16 p1mbsr = ks8842_read16(adapter, 45, REG_P1MBSR); | ||
7110 | + | ||
7111 | /* check the status of the link */ | ||
7112 | - if (ks8842_read16(adapter, 45, REG_P1MBSR) & 0x4) { | ||
7113 | + if (p1mbsr & 0x4) { | ||
7114 | netif_carrier_on(netdev); | ||
7115 | netif_wake_queue(netdev); | ||
7116 | } else { | ||
7117 | @@ -241,10 +289,8 @@ | ||
7118 | /* Enable QMU Transmit flow control / transmit padding / Transmit CRC */ | ||
7119 | ks8842_write16(adapter, 16, 0x000E, REG_TXCR); | ||
7120 | |||
7121 | - /* enable the receiver, uni + multi + broadcast + flow ctrl | ||
7122 | - + crc strip */ | ||
7123 | - ks8842_write16(adapter, 16, 0x8 | 0x20 | 0x40 | 0x80 | 0x400, | ||
7124 | - REG_RXCR); | ||
7125 | + /* enable the receiver, uni + multi + broadcast + crc strip */ | ||
7126 | + ks8842_write16(adapter, 16, 0x8 | 0x20 | 0x40 | 0x80, REG_RXCR); | ||
7127 | |||
7128 | /* TX frame pointer autoincrement */ | ||
7129 | ks8842_write16(adapter, 17, 0x4000, REG_TXFDPR); | ||
7130 | @@ -261,13 +307,11 @@ | ||
7131 | /* enable no excessive collison drop */ | ||
7132 | ks8842_enable_bits(adapter, 32, 1 << 3, REG_SGCR2); | ||
7133 | |||
7134 | - /* Enable port 1 force flow control / back pressure / transmit / recv */ | ||
7135 | - ks8842_write16(adapter, 48, 0x1E07, REG_P1CR2); | ||
7136 | + /* Enable port 1 / back pressure / transmit / recv */ | ||
7137 | + ks8842_write16(adapter, 48, 0xE07, REG_P1CR2); | ||
7138 | |||
7139 | /* restart port auto-negotiation */ | ||
7140 | ks8842_enable_bits(adapter, 49, 1 << 13, REG_P1CR4); | ||
7141 | - /* only advertise 10Mbps */ | ||
7142 | - ks8842_clear_bits(adapter, 49, 3 << 2, REG_P1CR4); | ||
7143 | |||
7144 | /* Enable the transmitter */ | ||
7145 | ks8842_enable_tx(adapter); | ||
7146 | @@ -279,7 +323,17 @@ | ||
7147 | ks8842_write16(adapter, 18, 0xffff, REG_ISR); | ||
7148 | |||
7149 | /* enable interrupts */ | ||
7150 | - ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER); | ||
7151 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7152 | + if (adapter->use_dma) { | ||
7153 | + iowrite16(ENABLED_IRQS_IP, adapter->hw_addr + REG_TIMB_IER); | ||
7154 | + ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER); | ||
7155 | + } else { | ||
7156 | +#endif | ||
7157 | + ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER); | ||
7158 | + iowrite16(ENABLED_IRQS, adapter->hw_addr + REG_TIMB_IER); | ||
7159 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7160 | + } | ||
7161 | +#endif | ||
7162 | |||
7163 | /* enable the switch */ | ||
7164 | ks8842_write16(adapter, 32, 0x1, REG_SW_ID_AND_ENABLE); | ||
7165 | @@ -302,11 +356,73 @@ | ||
7166 | ks8842_write16(adapter, 39, mac, REG_MACAR3); | ||
7167 | } | ||
7168 | |||
7169 | +static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac) | ||
7170 | +{ | ||
7171 | + unsigned long flags; | ||
7172 | + unsigned i; | ||
7173 | + | ||
7174 | + spin_lock_irqsave(&adapter->lock, flags); | ||
7175 | + for (i = 0; i < ETH_ALEN; i++) { | ||
7176 | + ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i); | ||
7177 | + ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1], | ||
7178 | + REG_MACAR1 + i); | ||
7179 | + } | ||
7180 | + spin_unlock_irqrestore(&adapter->lock, flags); | ||
7181 | +} | ||
7182 | + | ||
7183 | static inline u16 ks8842_tx_fifo_space(struct ks8842_adapter *adapter) | ||
7184 | { | ||
7185 | return ks8842_read16(adapter, 16, REG_TXMIR) & 0x1fff; | ||
7186 | } | ||
7187 | |||
7188 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7189 | +static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev) | ||
7190 | +{ | ||
7191 | + struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7192 | + struct ks8842_dma_ctl *ctl = &adapter->dma_tx; | ||
7193 | + int err; | ||
7194 | + int len = skb->len + sizeof(u32); | ||
7195 | + u8 *buf = ctl->buf; | ||
7196 | + | ||
7197 | + if (ctl->ongoing) { | ||
7198 | + dev_dbg(adapter->dev, "%s: TX ongoing\n", __func__); | ||
7199 | + /* transfer ongoing */ | ||
7200 | + return NETDEV_TX_BUSY; | ||
7201 | + } | ||
7202 | + | ||
7203 | + /* copy data to the TX buffer */ | ||
7204 | + /* the control word, enable IRQ, port 1 and the length */ | ||
7205 | + *buf++ = 0x00; | ||
7206 | + *buf++ = 0x01; /* Port 1 */ | ||
7207 | + *buf++ = skb->len & 0xff; | ||
7208 | + *buf++ = (skb->len >> 8) & 0xff; | ||
7209 | + skb_copy_from_linear_data(skb, buf, skb->len); | ||
7210 | + | ||
7211 | + dma_sync_single_range_for_device(DMA_DEV(adapter), ctl->addr, 0, len, | ||
7212 | + DMA_TO_DEVICE); | ||
7213 | + | ||
7214 | + /* make sure the length is a multiple of 4 */ | ||
7215 | + if (len % 4) | ||
7216 | + len += 4 - len % 4; | ||
7217 | + | ||
7218 | + err = timbdma_prep_desc(ctl->desc, ctl->addr, len); | ||
7219 | + if (err) | ||
7220 | + return NETDEV_TX_BUSY; | ||
7221 | + | ||
7222 | + ctl->ongoing = 1; | ||
7223 | + err = timbdma_start(DMA_IRQ_ETH_TX, ctl->desc, 0); | ||
7224 | + if (err) { | ||
7225 | + ctl->ongoing = 0; | ||
7226 | + return NETDEV_TX_BUSY; | ||
7227 | + } | ||
7228 | + netdev->stats.tx_bytes += skb->len; | ||
7229 | + | ||
7230 | + dev_kfree_skb(skb); | ||
7231 | + | ||
7232 | + return NETDEV_TX_OK; | ||
7233 | +} | ||
7234 | +#endif | ||
7235 | + | ||
7236 | static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev) | ||
7237 | { | ||
7238 | struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7239 | @@ -314,7 +430,7 @@ | ||
7240 | u32 *ptr = (u32 *)skb->data; | ||
7241 | u32 ctrl; | ||
7242 | |||
7243 | - dev_dbg(&adapter->pdev->dev, | ||
7244 | + dev_dbg(adapter->dev, | ||
7245 | "%s: len %u head %p data %p tail %p end %p\n", | ||
7246 | __func__, skb->len, skb->head, skb->data, | ||
7247 | skb_tail_pointer(skb), skb_end_pointer(skb)); | ||
7248 | @@ -344,6 +460,104 @@ | ||
7249 | return NETDEV_TX_OK; | ||
7250 | } | ||
7251 | |||
7252 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7253 | +static int __ks8842_start_new_rx_dma(struct net_device *netdev, | ||
7254 | + struct ks8842_adapter *adapter) | ||
7255 | +{ | ||
7256 | + struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx; | ||
7257 | + int err; | ||
7258 | + | ||
7259 | + ctl->skb = netdev_alloc_skb(netdev, DMA_BUFFER_SIZE); | ||
7260 | + if (ctl->skb) { | ||
7261 | + ctl->addr = dma_map_single(DMA_DEV(adapter), ctl->skb->data, | ||
7262 | + DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
7263 | + err = dma_mapping_error(DMA_DEV(adapter), ctl->addr); | ||
7264 | + if (unlikely(err)) { | ||
7265 | + ctl->addr = 0; | ||
7266 | + goto out; | ||
7267 | + } | ||
7268 | + err = timbdma_prep_desc(ctl->desc, ctl->addr, DMA_BUFFER_SIZE); | ||
7269 | + if (unlikely(err)) | ||
7270 | + goto out; | ||
7271 | + err = timbdma_start(DMA_IRQ_ETH_RX, ctl->desc, 0); | ||
7272 | + if (unlikely(err)) | ||
7273 | + goto out; | ||
7274 | + } else { | ||
7275 | + err = -ENOMEM; | ||
7276 | + ctl->addr = 0; | ||
7277 | + goto out; | ||
7278 | + } | ||
7279 | + | ||
7280 | + return err; | ||
7281 | +out: | ||
7282 | + if (ctl->addr) | ||
7283 | + dma_unmap_single(DMA_DEV(adapter), ctl->addr, DMA_BUFFER_SIZE, | ||
7284 | + DMA_FROM_DEVICE); | ||
7285 | + ctl->addr = 0; | ||
7286 | + if (ctl->skb) | ||
7287 | + dev_kfree_skb(ctl->skb); | ||
7288 | + | ||
7289 | + ctl->skb = NULL; | ||
7290 | + | ||
7291 | + printk(KERN_ERR DRV_NAME": Failed to start RX DMA: %d\n", err); | ||
7292 | + return err; | ||
7293 | +} | ||
7294 | + | ||
7295 | +static void ks8842_rx_frame_dma(struct net_device *netdev, | ||
7296 | + struct ks8842_adapter *adapter) | ||
7297 | +{ | ||
7298 | + struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx; | ||
7299 | + struct sk_buff *skb = ctl->skb; | ||
7300 | + dma_addr_t addr = ctl->addr; | ||
7301 | + u32 status; | ||
7302 | + | ||
7303 | + /* kick next transfer going */ | ||
7304 | + __ks8842_start_new_rx_dma(netdev, adapter); | ||
7305 | + | ||
7306 | + /* now handle the data we got */ | ||
7307 | + dma_unmap_single(DMA_DEV(adapter), addr, DMA_BUFFER_SIZE, | ||
7308 | + DMA_FROM_DEVICE); | ||
7309 | + | ||
7310 | + status = *((u32 *)skb->data); | ||
7311 | + | ||
7312 | + dev_dbg(adapter->dev, "%s - rx_data: status: %x\n", | ||
7313 | + __func__, status & 0xffff); | ||
7314 | + | ||
7315 | + /* check the status */ | ||
7316 | + if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) { | ||
7317 | + int len = (status >> 16) & 0x7ff; | ||
7318 | + | ||
7319 | + dev_dbg(adapter->dev, "%s, got package, len: %d, skb: %p\n", | ||
7320 | + __func__, len, skb); | ||
7321 | + | ||
7322 | + netdev->stats.rx_packets++; | ||
7323 | + netdev->stats.rx_bytes += len; | ||
7324 | + if (status & RXSR_MULTICAST) | ||
7325 | + netdev->stats.multicast++; | ||
7326 | + | ||
7327 | + /* we are not nice to the stack, we want to be nice | ||
7328 | + * to our DMA engine instead, reserve 4 bytes | ||
7329 | + * which is the status word | ||
7330 | + */ | ||
7331 | + skb_reserve(skb, 4); | ||
7332 | + skb_put(skb, len); | ||
7333 | + | ||
7334 | + skb->protocol = eth_type_trans(skb, netdev); | ||
7335 | + netif_rx(skb); | ||
7336 | + } else { | ||
7337 | + dev_dbg(adapter->dev, "RX error, status: %x\n", status); | ||
7338 | + netdev->stats.rx_errors++; | ||
7339 | + if (status & RXSR_TOO_LONG) | ||
7340 | + netdev->stats.rx_length_errors++; | ||
7341 | + if (status & RXSR_CRC_ERROR) | ||
7342 | + netdev->stats.rx_crc_errors++; | ||
7343 | + if (status & RXSR_RUNT) | ||
7344 | + netdev->stats.rx_frame_errors++; | ||
7345 | + dev_kfree_skb(skb); | ||
7346 | + } | ||
7347 | +} | ||
7348 | +#endif | ||
7349 | + | ||
7350 | static void ks8842_rx_frame(struct net_device *netdev, | ||
7351 | struct ks8842_adapter *adapter) | ||
7352 | { | ||
7353 | @@ -352,14 +566,14 @@ | ||
7354 | |||
7355 | status &= 0xffff; | ||
7356 | |||
7357 | - dev_dbg(&adapter->pdev->dev, "%s - rx_data: status: %x\n", | ||
7358 | + dev_dbg(adapter->dev, "%s - rx_data: status: %x\n", | ||
7359 | __func__, status); | ||
7360 | |||
7361 | /* check the status */ | ||
7362 | if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) { | ||
7363 | struct sk_buff *skb = netdev_alloc_skb(netdev, len + 2); | ||
7364 | |||
7365 | - dev_dbg(&adapter->pdev->dev, "%s, got package, len: %d\n", | ||
7366 | + dev_dbg(adapter->dev, "%s, got package, len: %d\n", | ||
7367 | __func__, len); | ||
7368 | if (skb) { | ||
7369 | u32 *data; | ||
7370 | @@ -386,7 +600,7 @@ | ||
7371 | } else | ||
7372 | netdev->stats.rx_dropped++; | ||
7373 | } else { | ||
7374 | - dev_dbg(&adapter->pdev->dev, "RX error, status: %x\n", status); | ||
7375 | + dev_dbg(adapter->dev, "RX error, status: %x\n", status); | ||
7376 | netdev->stats.rx_errors++; | ||
7377 | if (status & RXSR_TOO_LONG) | ||
7378 | netdev->stats.rx_length_errors++; | ||
7379 | @@ -409,7 +623,7 @@ | ||
7380 | void ks8842_handle_rx(struct net_device *netdev, struct ks8842_adapter *adapter) | ||
7381 | { | ||
7382 | u16 rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff; | ||
7383 | - dev_dbg(&adapter->pdev->dev, "%s Entry - rx_data: %d\n", | ||
7384 | + dev_dbg(adapter->dev, "%s Entry - rx_data: %d\n", | ||
7385 | __func__, rx_data); | ||
7386 | while (rx_data) { | ||
7387 | ks8842_rx_frame(netdev, adapter); | ||
7388 | @@ -420,7 +634,7 @@ | ||
7389 | void ks8842_handle_tx(struct net_device *netdev, struct ks8842_adapter *adapter) | ||
7390 | { | ||
7391 | u16 sr = ks8842_read16(adapter, 16, REG_TXSR); | ||
7392 | - dev_dbg(&adapter->pdev->dev, "%s - entry, sr: %x\n", __func__, sr); | ||
7393 | + dev_dbg(adapter->dev, "%s - entry, sr: %x\n", __func__, sr); | ||
7394 | netdev->stats.tx_packets++; | ||
7395 | if (netif_queue_stopped(netdev)) | ||
7396 | netif_wake_queue(netdev); | ||
7397 | @@ -429,7 +643,7 @@ | ||
7398 | void ks8842_handle_rx_overrun(struct net_device *netdev, | ||
7399 | struct ks8842_adapter *adapter) | ||
7400 | { | ||
7401 | - dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__); | ||
7402 | + dev_dbg(adapter->dev, "%s: entry\n", __func__); | ||
7403 | netdev->stats.rx_errors++; | ||
7404 | netdev->stats.rx_fifo_errors++; | ||
7405 | } | ||
7406 | @@ -448,20 +662,33 @@ | ||
7407 | spin_unlock_irqrestore(&adapter->lock, flags); | ||
7408 | |||
7409 | isr = ks8842_read16(adapter, 18, REG_ISR); | ||
7410 | - dev_dbg(&adapter->pdev->dev, "%s - ISR: 0x%x\n", __func__, isr); | ||
7411 | + dev_dbg(adapter->dev, "%s - ISR: 0x%x\n", __func__, isr); | ||
7412 | + | ||
7413 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7414 | + if (adapter->use_dma) | ||
7415 | + isr &= ~IRQ_RX; | ||
7416 | +#endif | ||
7417 | |||
7418 | /* Ack */ | ||
7419 | ks8842_write16(adapter, 18, isr, REG_ISR); | ||
7420 | |||
7421 | + /* Ack in the timberdale IP as well */ | ||
7422 | + iowrite32(0x1, adapter->hw_addr + REG_TIMB_IAR); | ||
7423 | + | ||
7424 | if (!netif_running(netdev)) | ||
7425 | return; | ||
7426 | |||
7427 | if (isr & IRQ_LINK_CHANGE) | ||
7428 | ks8842_update_link_status(netdev, adapter); | ||
7429 | |||
7430 | + /* should not get IRQ_RX when in DMA mode */ | ||
7431 | if (isr & (IRQ_RX | IRQ_RX_ERROR)) | ||
7432 | - ks8842_handle_rx(netdev, adapter); | ||
7433 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7434 | + if (!adapter->use_dma) | ||
7435 | +#endif | ||
7436 | + ks8842_handle_rx(netdev, adapter); | ||
7437 | |||
7438 | + /* should only happen when not doing DMA */ | ||
7439 | if (isr & IRQ_TX) | ||
7440 | ks8842_handle_tx(netdev, adapter); | ||
7441 | |||
7442 | @@ -480,8 +707,18 @@ | ||
7443 | |||
7444 | /* re-enable interrupts, put back the bank selection register */ | ||
7445 | spin_lock_irqsave(&adapter->lock, flags); | ||
7446 | - ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER); | ||
7447 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7448 | + if (adapter->use_dma) | ||
7449 | + ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER); | ||
7450 | + else | ||
7451 | +#endif | ||
7452 | + ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER); | ||
7453 | + | ||
7454 | iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK); | ||
7455 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7456 | + /* resume DMA operations */ | ||
7457 | + ks8842_resume_dma(adapter); | ||
7458 | +#endif | ||
7459 | spin_unlock_irqrestore(&adapter->lock, flags); | ||
7460 | } | ||
7461 | |||
7462 | @@ -493,11 +730,17 @@ | ||
7463 | irqreturn_t ret = IRQ_NONE; | ||
7464 | |||
7465 | isr = ks8842_read16(adapter, 18, REG_ISR); | ||
7466 | - dev_dbg(&adapter->pdev->dev, "%s - ISR: 0x%x\n", __func__, isr); | ||
7467 | + dev_dbg(adapter->dev, "%s - ISR: 0x%x\n", __func__, isr); | ||
7468 | |||
7469 | if (isr) { | ||
7470 | - /* disable IRQ */ | ||
7471 | - ks8842_write16(adapter, 18, 0x00, REG_IER); | ||
7472 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7473 | + if (adapter->use_dma) | ||
7474 | + /* disable all but RX IRQ, since the FPGA relies on it*/ | ||
7475 | + ks8842_write16(adapter, 18, IRQ_RX, REG_IER); | ||
7476 | + else | ||
7477 | +#endif | ||
7478 | + /* disable IRQ */ | ||
7479 | + ks8842_write16(adapter, 18, 0x00, REG_IER); | ||
7480 | |||
7481 | /* schedule tasklet */ | ||
7482 | tasklet_schedule(&adapter->tasklet); | ||
7483 | @@ -506,23 +749,129 @@ | ||
7484 | } | ||
7485 | |||
7486 | iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK); | ||
7487 | - | ||
7488 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7489 | + ks8842_resume_dma(adapter); | ||
7490 | +#endif | ||
7491 | return ret; | ||
7492 | } | ||
7493 | |||
7494 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7495 | +static int ks8842_dma_irq(u32 flag, void *data) | ||
7496 | +{ | ||
7497 | + struct net_device *netdev = data; | ||
7498 | + struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7499 | + | ||
7500 | + if (flag & DMA_IRQ_ETH_RX) { | ||
7501 | + dev_dbg(adapter->dev, "RX DMA finished\n"); | ||
7502 | + ks8842_rx_frame_dma(netdev, adapter); | ||
7503 | + } | ||
7504 | + if (flag & DMA_IRQ_ETH_TX) { | ||
7505 | + struct ks8842_dma_ctl *ctl = &adapter->dma_tx; | ||
7506 | + dev_dbg(adapter->dev, "TX DMA finished\n"); | ||
7507 | + | ||
7508 | + netdev->stats.tx_packets++; | ||
7509 | + ctl->ongoing = 0; | ||
7510 | + | ||
7511 | + if (netif_queue_stopped(netdev)) | ||
7512 | + netif_wake_queue(netdev); | ||
7513 | + } | ||
7514 | + | ||
7515 | + return 0; | ||
7516 | +} | ||
7517 | + | ||
7518 | +static void ks8842_dealloc_dma_bufs(struct ks8842_adapter *adapter) | ||
7519 | +{ | ||
7520 | + struct ks8842_dma_ctl *tx_ctl = &adapter->dma_tx; | ||
7521 | + struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx; | ||
7522 | + | ||
7523 | + if (tx_ctl->ongoing) | ||
7524 | + timbdma_stop(DMA_IRQ_ETH_TX); | ||
7525 | + tx_ctl->ongoing = 0; | ||
7526 | + if (rx_ctl->skb) | ||
7527 | + timbdma_stop(DMA_IRQ_ETH_RX); | ||
7528 | + | ||
7529 | + timbdma_set_interruptcb(DMA_IRQ_ETH_RX | DMA_IRQ_ETH_TX, NULL, NULL); | ||
7530 | + | ||
7531 | + if (rx_ctl->desc) | ||
7532 | + timbdma_free_desc(rx_ctl->desc); | ||
7533 | + rx_ctl->desc = NULL; | ||
7534 | + if (tx_ctl->desc) | ||
7535 | + timbdma_free_desc(tx_ctl->desc); | ||
7536 | + tx_ctl->desc = NULL; | ||
7537 | + if (rx_ctl->addr) | ||
7538 | + dma_unmap_single(DMA_DEV(adapter), rx_ctl->addr, | ||
7539 | + DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
7540 | + rx_ctl->addr = 0; | ||
7541 | + if (tx_ctl->addr) | ||
7542 | + dma_unmap_single(DMA_DEV(adapter), tx_ctl->addr, | ||
7543 | + DMA_BUFFER_SIZE, DMA_TO_DEVICE); | ||
7544 | + tx_ctl->addr = 0; | ||
7545 | + dev_kfree_skb(rx_ctl->skb); | ||
7546 | + rx_ctl->skb = NULL; | ||
7547 | + kfree(tx_ctl->buf); | ||
7548 | + tx_ctl->buf = NULL; | ||
7549 | +} | ||
7550 | +#endif | ||
7551 | |||
7552 | /* Netdevice operations */ | ||
7553 | |||
7554 | static int ks8842_open(struct net_device *netdev) | ||
7555 | { | ||
7556 | struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7557 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7558 | + struct ks8842_dma_ctl *tx_ctl = &adapter->dma_tx; | ||
7559 | + struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx; | ||
7560 | + int use_dma = 0; | ||
7561 | +#endif | ||
7562 | int err; | ||
7563 | |||
7564 | - dev_dbg(&adapter->pdev->dev, "%s - entry\n", __func__); | ||
7565 | + dev_dbg(adapter->dev, "%s - entry\n", __func__); | ||
7566 | + | ||
7567 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7568 | + if (adapter->use_dma) { | ||
7569 | + /* allocate SG descriptor */ | ||
7570 | + tx_ctl->buf = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); | ||
7571 | + if (!tx_ctl->buf) | ||
7572 | + goto no_dma; | ||
7573 | + tx_ctl->addr = dma_map_single(DMA_DEV(adapter), tx_ctl->buf, | ||
7574 | + DMA_BUFFER_SIZE, DMA_TO_DEVICE); | ||
7575 | + err = dma_mapping_error(DMA_DEV(adapter), tx_ctl->addr); | ||
7576 | + if (err) { | ||
7577 | + tx_ctl->addr = 0; | ||
7578 | + goto no_dma; | ||
7579 | + } | ||
7580 | + tx_ctl->desc = timbdma_alloc_desc(DMA_BUFFER_SIZE, 1); | ||
7581 | + if (!tx_ctl->desc) | ||
7582 | + goto no_dma; | ||
7583 | + | ||
7584 | + rx_ctl->desc = timbdma_alloc_desc(DMA_BUFFER_SIZE, 1); | ||
7585 | + if (!rx_ctl->desc) | ||
7586 | + goto no_dma; | ||
7587 | + | ||
7588 | + timbdma_set_interruptcb(DMA_IRQ_ETH_RX | DMA_IRQ_ETH_TX, | ||
7589 | + ks8842_dma_irq, (void *)netdev); | ||
7590 | + | ||
7591 | + /* start RX dma */ | ||
7592 | + err = __ks8842_start_new_rx_dma(netdev, adapter); | ||
7593 | + if (err) | ||
7594 | + goto no_dma; | ||
7595 | + | ||
7596 | + use_dma = 1; | ||
7597 | + } | ||
7598 | +no_dma: | ||
7599 | + if (!use_dma) { | ||
7600 | + printk(KERN_WARNING DRV_NAME | ||
7601 | + ": Failed to initiate DMA, falling back to PIO\n"); | ||
7602 | + ks8842_dealloc_dma_bufs(adapter); | ||
7603 | + adapter->use_dma = 0; | ||
7604 | + } | ||
7605 | +#endif | ||
7606 | |||
7607 | /* reset the HW */ | ||
7608 | ks8842_reset_hw(adapter); | ||
7609 | |||
7610 | + ks8842_write_mac_addr(adapter, netdev->dev_addr); | ||
7611 | + | ||
7612 | ks8842_update_link_status(netdev, adapter); | ||
7613 | |||
7614 | err = request_irq(adapter->irq, ks8842_irq, IRQF_SHARED, DRV_NAME, | ||
7615 | @@ -536,11 +885,19 @@ | ||
7616 | return 0; | ||
7617 | } | ||
7618 | |||
7619 | + | ||
7620 | static int ks8842_close(struct net_device *netdev) | ||
7621 | { | ||
7622 | struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7623 | |||
7624 | - dev_dbg(&adapter->pdev->dev, "%s - entry\n", __func__); | ||
7625 | + dev_dbg(adapter->dev, "%s - entry\n", __func__); | ||
7626 | + | ||
7627 | + cancel_work_sync(&adapter->timeout_work); | ||
7628 | + | ||
7629 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7630 | + if (adapter->use_dma) | ||
7631 | + ks8842_dealloc_dma_bufs(adapter); | ||
7632 | +#endif | ||
7633 | |||
7634 | /* free the irq */ | ||
7635 | free_irq(adapter->irq, adapter); | ||
7636 | @@ -556,8 +913,20 @@ | ||
7637 | int ret; | ||
7638 | struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7639 | |||
7640 | - dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__); | ||
7641 | + dev_dbg(adapter->dev, "%s: entry\n", __func__); | ||
7642 | |||
7643 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7644 | + if (adapter->use_dma) { | ||
7645 | + unsigned long flags; | ||
7646 | + ret = ks8842_tx_frame_dma(skb, netdev); | ||
7647 | + /* for now only allow one transfer at the time */ | ||
7648 | + spin_lock_irqsave(&adapter->lock, flags); | ||
7649 | + if (adapter->dma_tx.ongoing) | ||
7650 | + netif_stop_queue(netdev); | ||
7651 | + spin_unlock_irqrestore(&adapter->lock, flags); | ||
7652 | + return ret; | ||
7653 | + } | ||
7654 | +#endif | ||
7655 | ret = ks8842_tx_frame(skb, netdev); | ||
7656 | |||
7657 | if (ks8842_tx_fifo_space(adapter) < netdev->mtu + 8) | ||
7658 | @@ -569,44 +938,77 @@ | ||
7659 | static int ks8842_set_mac(struct net_device *netdev, void *p) | ||
7660 | { | ||
7661 | struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7662 | - unsigned long flags; | ||
7663 | struct sockaddr *addr = p; | ||
7664 | char *mac = (u8 *)addr->sa_data; | ||
7665 | - int i; | ||
7666 | |||
7667 | - dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__); | ||
7668 | + dev_dbg(adapter->dev, "%s: entry\n", __func__); | ||
7669 | |||
7670 | if (!is_valid_ether_addr(addr->sa_data)) | ||
7671 | return -EADDRNOTAVAIL; | ||
7672 | |||
7673 | memcpy(netdev->dev_addr, mac, netdev->addr_len); | ||
7674 | |||
7675 | - spin_lock_irqsave(&adapter->lock, flags); | ||
7676 | - for (i = 0; i < ETH_ALEN; i++) { | ||
7677 | - ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i); | ||
7678 | - ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1], | ||
7679 | - REG_MACAR1 + i); | ||
7680 | - } | ||
7681 | - spin_unlock_irqrestore(&adapter->lock, flags); | ||
7682 | + ks8842_write_mac_addr(adapter, mac); | ||
7683 | return 0; | ||
7684 | } | ||
7685 | |||
7686 | -static void ks8842_tx_timeout(struct net_device *netdev) | ||
7687 | +static void ks8842_tx_timeout_work(struct work_struct *work) | ||
7688 | { | ||
7689 | - struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7690 | + struct ks8842_adapter *adapter = | ||
7691 | + container_of(work, struct ks8842_adapter, timeout_work); | ||
7692 | + struct net_device *netdev = adapter->netdev; | ||
7693 | unsigned long flags; | ||
7694 | |||
7695 | - dev_dbg(&adapter->pdev->dev, "%s: entry\n", __func__); | ||
7696 | + dev_dbg(adapter->dev, "%s: entry\n", __func__); | ||
7697 | |||
7698 | spin_lock_irqsave(&adapter->lock, flags); | ||
7699 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7700 | + if (adapter->use_dma) { | ||
7701 | + struct ks8842_dma_ctl *tx_ctl = &adapter->dma_tx; | ||
7702 | + struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx; | ||
7703 | + | ||
7704 | + if (tx_ctl->ongoing) | ||
7705 | + timbdma_stop(DMA_IRQ_ETH_TX); | ||
7706 | + tx_ctl->ongoing = 0; | ||
7707 | + | ||
7708 | + timbdma_stop(DMA_IRQ_ETH_RX); | ||
7709 | + | ||
7710 | + dma_unmap_single(DMA_DEV(adapter), rx_ctl->addr, | ||
7711 | + DMA_BUFFER_SIZE, DMA_FROM_DEVICE); | ||
7712 | + rx_ctl->addr = 0; | ||
7713 | + | ||
7714 | + dev_kfree_skb(rx_ctl->skb); | ||
7715 | + rx_ctl->skb = NULL; | ||
7716 | + } | ||
7717 | +#endif | ||
7718 | + | ||
7719 | /* disable interrupts */ | ||
7720 | ks8842_write16(adapter, 18, 0, REG_IER); | ||
7721 | ks8842_write16(adapter, 18, 0xFFFF, REG_ISR); | ||
7722 | + | ||
7723 | + netif_stop_queue(netdev); | ||
7724 | + | ||
7725 | spin_unlock_irqrestore(&adapter->lock, flags); | ||
7726 | |||
7727 | ks8842_reset_hw(adapter); | ||
7728 | |||
7729 | + ks8842_write_mac_addr(adapter, netdev->dev_addr); | ||
7730 | + | ||
7731 | ks8842_update_link_status(netdev, adapter); | ||
7732 | + | ||
7733 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7734 | + if (adapter->use_dma) | ||
7735 | + __ks8842_start_new_rx_dma(netdev, adapter); | ||
7736 | +#endif | ||
7737 | +} | ||
7738 | + | ||
7739 | +static void ks8842_tx_timeout(struct net_device *netdev) | ||
7740 | +{ | ||
7741 | + struct ks8842_adapter *adapter = netdev_priv(netdev); | ||
7742 | + | ||
7743 | + dev_dbg(adapter->dev, "%s: entry\n", __func__); | ||
7744 | + | ||
7745 | + schedule_work(&adapter->timeout_work); | ||
7746 | } | ||
7747 | |||
7748 | static const struct net_device_ops ks8842_netdev_ops = { | ||
7749 | @@ -641,6 +1043,8 @@ | ||
7750 | SET_NETDEV_DEV(netdev, &pdev->dev); | ||
7751 | |||
7752 | adapter = netdev_priv(netdev); | ||
7753 | + adapter->netdev = netdev; | ||
7754 | + INIT_WORK(&adapter->timeout_work, ks8842_tx_timeout_work); | ||
7755 | adapter->hw_addr = ioremap(iomem->start, resource_size(iomem)); | ||
7756 | if (!adapter->hw_addr) | ||
7757 | goto err_ioremap; | ||
7758 | @@ -651,8 +1055,10 @@ | ||
7759 | goto err_get_irq; | ||
7760 | } | ||
7761 | |||
7762 | - adapter->pdev = pdev; | ||
7763 | - | ||
7764 | + adapter->dev = &pdev->dev; | ||
7765 | +#ifdef CONFIG_KS8842_TIMB_DMA | ||
7766 | + adapter->use_dma = 1; | ||
7767 | +#endif | ||
7768 | tasklet_init(&adapter->tasklet, ks8842_tasklet, (unsigned long)netdev); | ||
7769 | spin_lock_init(&adapter->lock); | ||
7770 | |||
7771 | @@ -660,6 +1066,8 @@ | ||
7772 | netdev->ethtool_ops = &ks8842_ethtool_ops; | ||
7773 | |||
7774 | ks8842_read_mac_addr(adapter, netdev->dev_addr); | ||
7775 | + if (!is_valid_ether_addr(netdev->dev_addr)) | ||
7776 | + random_ether_addr(netdev->dev_addr); | ||
7777 | |||
7778 | id = ks8842_read16(adapter, 32, REG_SW_ID_AND_ENABLE); | ||
7779 | |||
7780 | diff -uNr linux-2.6.31/drivers/net/Makefile linux-2.6.31.new/drivers/net/Makefile | ||
7781 | --- linux-2.6.31/drivers/net/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
7782 | +++ linux-2.6.31.new/drivers/net/Makefile 2009-10-23 11:17:22.000000000 -0700 | ||
7783 | @@ -16,6 +16,7 @@ | ||
7784 | obj-$(CONFIG_CHELSIO_T3) += cxgb3/ | ||
7785 | obj-$(CONFIG_EHEA) += ehea/ | ||
7786 | obj-$(CONFIG_CAN) += can/ | ||
7787 | +obj-$(CONFIG_MOST) += most/ | ||
7788 | obj-$(CONFIG_BONDING) += bonding/ | ||
7789 | obj-$(CONFIG_ATL1) += atlx/ | ||
7790 | obj-$(CONFIG_ATL2) += atlx/ | ||
7791 | diff -uNr linux-2.6.31/drivers/net/most/Kconfig linux-2.6.31.new/drivers/net/most/Kconfig | ||
7792 | --- linux-2.6.31/drivers/net/most/Kconfig 1969-12-31 16:00:00.000000000 -0800 | ||
7793 | +++ linux-2.6.31.new/drivers/net/most/Kconfig 2009-10-23 11:17:22.000000000 -0700 | ||
7794 | @@ -0,0 +1,14 @@ | ||
7795 | +menu "MOST Device Drivers" | ||
7796 | + depends on MOST | ||
7797 | + | ||
7798 | +config MOST_TIMB_MLB | ||
7799 | + tristate "The timberdale MOST block" | ||
7800 | + depends on MOST | ||
7801 | + depends on MFD_TIMBERDALE_DMA | ||
7802 | + depends on GENERIC_GPIO | ||
7803 | + depends on HAS_IOMEM | ||
7804 | + default N | ||
7805 | + ---help--- | ||
7806 | + Adds support for MOST on the timberdale FPGA. | ||
7807 | + | ||
7808 | +endmenu | ||
7809 | diff -uNr linux-2.6.31/drivers/net/most/Makefile linux-2.6.31.new/drivers/net/most/Makefile | ||
7810 | --- linux-2.6.31/drivers/net/most/Makefile 1969-12-31 16:00:00.000000000 -0800 | ||
7811 | +++ linux-2.6.31.new/drivers/net/most/Makefile 2009-10-23 11:17:22.000000000 -0700 | ||
7812 | @@ -0,0 +1,6 @@ | ||
7813 | +# | ||
7814 | +# Makefile for the Linux Media Oriented Systems Transport drivers. | ||
7815 | +# | ||
7816 | + | ||
7817 | +obj-$(CONFIG_MOST_TIMB_MLB) += timbmlb.o | ||
7818 | + | ||
7819 | diff -uNr linux-2.6.31/drivers/net/most/timbmlb.c linux-2.6.31.new/drivers/net/most/timbmlb.c | ||
7820 | --- linux-2.6.31/drivers/net/most/timbmlb.c 1969-12-31 16:00:00.000000000 -0800 | ||
7821 | +++ linux-2.6.31.new/drivers/net/most/timbmlb.c 2009-10-23 11:17:22.000000000 -0700 | ||
7822 | @@ -0,0 +1,1087 @@ | ||
7823 | +/* | ||
7824 | + * timbmlb.c Driver for the timberdale MLB block | ||
7825 | + * Copyright (c) 2009 Intel Corporation | ||
7826 | + * | ||
7827 | + * This program is free software; you can redistribute it and/or modify | ||
7828 | + * it under the terms of the GNU General Public License version 2 as | ||
7829 | + * published by the Free Software Foundation. | ||
7830 | + * | ||
7831 | + * This program is distributed in the hope that it will be useful, | ||
7832 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
7833 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
7834 | + * GNU General Public License for more details. | ||
7835 | + * | ||
7836 | + * You should have received a copy of the GNU General Public License | ||
7837 | + * along with this program; if not, write to the Free Software | ||
7838 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
7839 | + */ | ||
7840 | +#include <linux/module.h> | ||
7841 | +#include <linux/interrupt.h> | ||
7842 | +#include <linux/platform_device.h> | ||
7843 | +#include <linux/mfd/timbdma.h> | ||
7844 | +#include <linux/spinlock.h> | ||
7845 | +#include <net/most/most_core.h> | ||
7846 | +#include <linux/gpio.h> | ||
7847 | +#include <linux/most/timbmlb.h> | ||
7848 | + | ||
7849 | +#define DRIVER_NAME "timb-most" | ||
7850 | + | ||
7851 | +#define MLB_REG_CFG 0x00 | ||
7852 | +#define MLB_REG_CH_CTRL 0x04 | ||
7853 | +#define MLB_REG_ISR 0x08 | ||
7854 | +#define MLB_REG_IMR 0x0C | ||
7855 | +#define MLB_REG_CH_CFG_1 0x10 | ||
7856 | +#define MLB_REG_CH_CFG_2 0x14 | ||
7857 | +#define MLB_REG_CH_CFG_3 0x18 | ||
7858 | +#define MLB_REG_CH_CFG_4 0x1C | ||
7859 | +#define MLB_REG_CH_CFG_5 0x20 | ||
7860 | +#define MLB_REG_CH_CFG_6 0x24 | ||
7861 | +#define MLB_REG_CH_CFG_7 0x28 | ||
7862 | +#define MLB_REG_CTRL_RX 0x2C /* 8 bits */ | ||
7863 | +#define MLB_REG_CTRL_TX MLB_REG_CTRL_RX | ||
7864 | +#define MLB_REG_ASYNC_RX 0x30 /* 32 bits */ | ||
7865 | +#define MLB_REG_ASYNC_TX MLB_REG_ASYNC_RX | ||
7866 | +#define MLB_REG_SYNC_RX 0x34 /* 32 bits */ | ||
7867 | +#define MLB_REG_SYNC_TX MLB_REG_SYNC_RX | ||
7868 | +#define MLB_REG_FIFO_RST 0x38 | ||
7869 | + | ||
7870 | +#define MLB_WR_CFG_CTRL_RX_EMPTY 0x20000 | ||
7871 | +#define MLB_WR_CFG_ASYNC_RX_EMPTY 0x10000 | ||
7872 | +#define MLB_CFG_SYNC_TX_EN 0x00200 | ||
7873 | +#define MLB_CFG_SYNC_RX_EN 0x00100 | ||
7874 | +#define MLB_CFG_ASYNC_RX_EN 0x00080 | ||
7875 | +#define MLB_CFG_CTRL_RX_EN 0x00040 | ||
7876 | + | ||
7877 | +#define MLB_CH_CTRL_ASYNC_TX_START 0x8000 | ||
7878 | +#define MLB_CH_CTRL_ASYNC_RX_BREAK 0x4000 | ||
7879 | +#define MLB_CH_CTRL_CTRL_TX_START 0x0800 | ||
7880 | +#define MLB_CH_CTRL_CTRL_RX_BREAK 0x0400 | ||
7881 | + | ||
7882 | +#define MLB_WR_I_SYNC_RX_EMPTY 0x80000 | ||
7883 | +#define MLB_WR_I_SYNC_RX_ALMOST_FULL 0x40000 | ||
7884 | +#define MLB_WR_I_SYNC_TX_FULL 0x20000 | ||
7885 | +#define MLB_WR_I_SYNC_TX_ALMOST_EMPTY 0x10000 | ||
7886 | +#define MLB_I_ASYNC_TX_READY 0x08000 | ||
7887 | +#define MLB_I_ASYNC_TX_PROT_ERR 0x04000 | ||
7888 | +#define MLB_I_ASYNC_TX_RX_BREAK 0x02000 | ||
7889 | +#define MLB_I_ASYNC_TX_BUSY_BREAK 0x01000 | ||
7890 | +#define MLB_I_ASYNC_RX_READY 0x00800 | ||
7891 | +#define MLB_I_ASYNC_RX_PROT_ERR 0x00400 | ||
7892 | +#define MLB_I_ASYNC_RX_CMD_BREAK 0x00200 | ||
7893 | +#define MLB_I_SYNC_LOCK 0x00100 | ||
7894 | +#define MLB_I_CTRL_TX_READY 0x00080 | ||
7895 | +#define MLB_I_CTRL_TX_PROT_ERR 0x00040 | ||
7896 | +#define MLB_I_CTRL_TX_RX_BREAK 0x00020 | ||
7897 | +#define MLB_I_CTRL_TX_BUSY_BREAK 0x00010 | ||
7898 | +#define MLB_I_CTRL_RX_READY 0x00008 | ||
7899 | +#define MLB_I_CTRL_RX_PROT_ERR 0x00004 | ||
7900 | +#define MLB_I_CTRL_RX_CMD_BREAK 0x00002 | ||
7901 | +#define MLB_I_SYNC_RX_PROT_ERR 0x00001 | ||
7902 | + | ||
7903 | +#define MLB_CH_CFG_NOT_ALLOCATED 0x0000 | ||
7904 | +#define MLB_CH_CFG_SYNC_TX 0x0001 | ||
7905 | +#define MLB_CH_CFG_SYNC_RX 0x0002 | ||
7906 | +#define MLB_CH_CFG_ASYNC_TX 0x0003 | ||
7907 | +#define MLB_CH_CFG_ASYNC_RX 0x0004 | ||
7908 | +#define MLB_CH_CFG_CTRL_TX 0x0005 | ||
7909 | +#define MLB_CH_CFG_CTRL_RX 0x0006 | ||
7910 | + | ||
7911 | +#define MLB_FIFO_RST_CTRL_TX 0x010000 | ||
7912 | +#define MLB_FIFO_RST_CTRL_RX 0x020000 | ||
7913 | +#define MLB_FIFO_RST_ASYNC_TX 0x040000 | ||
7914 | +#define MLB_FIFO_RST_ASYNC_RX 0x080000 | ||
7915 | +#define MLB_FIFO_RST_SYNC_TX 0x100000 | ||
7916 | +#define MLB_FIFO_RST_SYNC_RX 0x200000 | ||
7917 | +#define MLB_FIFO_RST_MLB 0x400000 | ||
7918 | +#define MLB_FIFO_RST_ALL (MLB_FIFO_RST_CTRL_TX | \ | ||
7919 | + MLB_FIFO_RST_CTRL_RX | \ | ||
7920 | + MLB_FIFO_RST_ASYNC_TX | \ | ||
7921 | + MLB_FIFO_RST_ASYNC_RX | \ | ||
7922 | + MLB_FIFO_RST_SYNC_TX | \ | ||
7923 | + MLB_FIFO_RST_SYNC_RX | \ | ||
7924 | + MLB_FIFO_RST_MLB) | ||
7925 | + | ||
7926 | +#define ASYNC_SKB_SIZE 1024 | ||
7927 | +#define SYNC_SKB_SIZE 32 | ||
7928 | + | ||
7929 | +#define SYNC_MAX_DMA_SIZE 4096 | ||
7930 | + | ||
7931 | +#define RX_CHAN 0 | ||
7932 | +#define TX_CHAN 1 | ||
7933 | +#define CHANNELS 2 | ||
7934 | + | ||
7935 | +#define DMA_DEV(s) ((s->mdev->parent->parent) ? \ | ||
7936 | + s->mdev->parent->parent : s->mdev->parent) | ||
7937 | + | ||
7938 | +struct timbmost { | ||
7939 | + void __iomem *membase; | ||
7940 | + struct most_dev *mdev; | ||
7941 | + int irq; | ||
7942 | + int reset_pin; | ||
7943 | + spinlock_t lock; /* mutual exclusion */ | ||
7944 | + | ||
7945 | + /* one queue per channel (type) */ | ||
7946 | + struct sk_buff_head ctl_q; | ||
7947 | + struct sk_buff_head async_q; | ||
7948 | + struct sk_buff_head sync_q; | ||
7949 | + | ||
7950 | + /* The SKB currently written/read into by the DMA engine | ||
7951 | + * only used for the synchronous channel | ||
7952 | + */ | ||
7953 | + struct sk_buff *sync_read_skb; | ||
7954 | + dma_addr_t sync_read_handle; | ||
7955 | + void *sync_read_desc; | ||
7956 | + struct sk_buff *sync_write_skb; | ||
7957 | + void *sync_write_desc; | ||
7958 | + int sync_write_next_map; | ||
7959 | + | ||
7960 | + /* channel numbers */ | ||
7961 | + u8 ctl_channels[CHANNELS]; | ||
7962 | + u8 sync_channels[CHANNELS]; | ||
7963 | + u8 async_channels[CHANNELS]; | ||
7964 | +}; | ||
7965 | + | ||
7966 | +static void timbmost_ctl_write_wake(struct timbmost *self); | ||
7967 | +static void timbmost_async_write_wake(struct timbmost *self); | ||
7968 | + | ||
7969 | +static void __timbmost_dump_regs(struct timbmost *self, const char *caption) | ||
7970 | +{ | ||
7971 | + dev_dbg(self->mdev->parent, "%s\nMLB_CFG:\t%x\tCH_CTRL:\t%x\n", | ||
7972 | + caption, | ||
7973 | + ioread32(self->membase + MLB_REG_CFG), | ||
7974 | + ioread32(self->membase + MLB_REG_CH_CTRL)); | ||
7975 | + | ||
7976 | + dev_dbg(self->mdev->parent, "ISTAT:\t%x\tIMASK:\t%x\n", | ||
7977 | + ioread32(self->membase + MLB_REG_ISR), | ||
7978 | + ioread32(self->membase + MLB_REG_IMR)); | ||
7979 | + | ||
7980 | + dev_dbg(self->mdev->parent, "CH_CFG1:\t%x\tCH_CFG2:\t%x\n", | ||
7981 | + ioread32(self->membase + MLB_REG_CH_CFG_1), | ||
7982 | + ioread32(self->membase + MLB_REG_CH_CFG_2)); | ||
7983 | + | ||
7984 | + dev_dbg(self->mdev->parent, "CH_CFG3:\t%x\tCH_CFG4:\t%x\n", | ||
7985 | + ioread32(self->membase + MLB_REG_CH_CFG_3), | ||
7986 | + ioread32(self->membase + MLB_REG_CH_CFG_4)); | ||
7987 | + | ||
7988 | + dev_dbg(self->mdev->parent, "CH_CFG5:\t%x\tCH_CFG6:\t%x\n", | ||
7989 | + ioread32(self->membase + MLB_REG_CH_CFG_5), | ||
7990 | + ioread32(self->membase + MLB_REG_CH_CFG_6)); | ||
7991 | + | ||
7992 | + dev_dbg(self->mdev->parent, "CH_CFG7:\t%x\n", | ||
7993 | + ioread32(self->membase + MLB_REG_CH_CFG_7)); | ||
7994 | +} | ||
7995 | + | ||
7996 | +static void __timbmost_hw_reset(struct timbmost *self) | ||
7997 | +{ | ||
7998 | + /* disable all interrupts */ | ||
7999 | + iowrite32(0, self->membase + MLB_REG_IMR); | ||
8000 | + iowrite32(0, self->membase + MLB_REG_ISR); | ||
8001 | + | ||
8002 | + /* disable RX and TX */ | ||
8003 | + iowrite32(0, self->membase + MLB_REG_CFG); | ||
8004 | + iowrite32(0, self->membase + MLB_REG_CH_CTRL); | ||
8005 | + | ||
8006 | + /* make sure the channels are not allocated */ | ||
8007 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_1); | ||
8008 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_2); | ||
8009 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_3); | ||
8010 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_4); | ||
8011 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_5); | ||
8012 | + iowrite32(MLB_CH_CFG_NOT_ALLOCATED, self->membase + MLB_REG_CH_CFG_6); | ||
8013 | + | ||
8014 | + /* reset */ | ||
8015 | + iowrite32(MLB_FIFO_RST_ALL, self->membase + MLB_REG_FIFO_RST); | ||
8016 | + | ||
8017 | + /* reset the INIC */ | ||
8018 | + gpio_direction_output(self->reset_pin, 0); | ||
8019 | + msleep(10); | ||
8020 | + gpio_set_value(self->reset_pin, 1); | ||
8021 | +} | ||
8022 | + | ||
8023 | +static void __timbmost_ctl_rx(struct timbmost *self) | ||
8024 | +{ | ||
8025 | + u32 cfg; | ||
8026 | + do { | ||
8027 | + struct sk_buff *skb = | ||
8028 | + most_skb_alloc(CTL_FRAME_SIZE, GFP_ATOMIC); | ||
8029 | + if (!skb) | ||
8030 | + return; | ||
8031 | + | ||
8032 | + do { | ||
8033 | + u32 word = ioread32(self->membase + MLB_REG_CTRL_RX); | ||
8034 | + int i; | ||
8035 | + | ||
8036 | + for (i = 0; i < 4; i++) | ||
8037 | + *skb_put(skb, 1) = (word >> (i * 8)) & 0xff; | ||
8038 | + | ||
8039 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8040 | + } while ((skb->len < CTL_FRAME_SIZE) && | ||
8041 | + !(cfg & MLB_WR_CFG_CTRL_RX_EMPTY)); | ||
8042 | + | ||
8043 | + /* deliver SKB upstreams */ | ||
8044 | + skb->dev = (void *)self->mdev; | ||
8045 | + most_cb(skb)->channel_type = CHAN_CTL; | ||
8046 | + /* only one channel is supported... */ | ||
8047 | + most_cb(skb)->channel = self->ctl_channels[RX_CHAN]; | ||
8048 | + | ||
8049 | + most_recv_frame(skb); | ||
8050 | + } while (!(cfg & MLB_WR_CFG_CTRL_RX_EMPTY)); | ||
8051 | +} | ||
8052 | + | ||
8053 | +static void __timbmost_async_rx(struct timbmost *self) | ||
8054 | +{ | ||
8055 | + /* TODO: The FIFO is 32bit not 8bit */ | ||
8056 | + u32 cfg; | ||
8057 | + | ||
8058 | + __timbmost_dump_regs(self, "Before read"); | ||
8059 | + | ||
8060 | + do { | ||
8061 | + struct sk_buff *skb = | ||
8062 | + most_skb_alloc(ASYNC_SKB_SIZE, GFP_ATOMIC); | ||
8063 | + if (!skb) | ||
8064 | + return; | ||
8065 | + | ||
8066 | + do { | ||
8067 | + *skb_put(skb, 1) = | ||
8068 | + ioread32(self->membase + MLB_REG_ASYNC_RX); | ||
8069 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8070 | + } while ((skb->len < ASYNC_SKB_SIZE) && | ||
8071 | + !(cfg & MLB_WR_CFG_ASYNC_RX_EMPTY)); | ||
8072 | + | ||
8073 | + /* deliver SKB upstreams */ | ||
8074 | + skb->dev = (void *)self->mdev; | ||
8075 | + most_cb(skb)->channel_type = CHAN_ASYNC; | ||
8076 | + /* only one channel is supported... */ | ||
8077 | + most_cb(skb)->channel = self->async_channels[RX_CHAN]; | ||
8078 | + | ||
8079 | + most_recv_frame(skb); | ||
8080 | + } while (!(cfg & MLB_WR_CFG_ASYNC_RX_EMPTY)); | ||
8081 | +} | ||
8082 | + | ||
8083 | +static void __timbmost_sync_read_wake(struct timbmost *self) | ||
8084 | +{ | ||
8085 | + struct sk_buff *skb = self->sync_read_skb; | ||
8086 | + dma_addr_t map; | ||
8087 | + int err; | ||
8088 | + | ||
8089 | + if (skb) | ||
8090 | + return; | ||
8091 | + | ||
8092 | + skb = most_skb_alloc(SYNC_SKB_SIZE, GFP_ATOMIC); | ||
8093 | + if (!skb) | ||
8094 | + return; | ||
8095 | + | ||
8096 | + map = dma_map_single(DMA_DEV(self), skb->data, SYNC_SKB_SIZE, | ||
8097 | + DMA_FROM_DEVICE); | ||
8098 | + if (dma_mapping_error(DMA_DEV(self), map)) | ||
8099 | + goto map_failed; | ||
8100 | + | ||
8101 | + err = timbdma_prep_desc(self->sync_read_desc, map, SYNC_SKB_SIZE); | ||
8102 | + if (err) | ||
8103 | + goto prep_failed; | ||
8104 | + | ||
8105 | + dev_dbg(self->mdev->parent, "%s: will start RX: to: %x, size: %d\n", | ||
8106 | + __func__, (u32)map, SYNC_SKB_SIZE); | ||
8107 | + | ||
8108 | + err = timbdma_start(DMA_IRQ_MLB_RX, self->sync_read_desc, 0); | ||
8109 | + if (err) | ||
8110 | + goto start_failed; | ||
8111 | + | ||
8112 | + self->sync_read_skb = skb; | ||
8113 | + self->sync_read_handle = map; | ||
8114 | + return; | ||
8115 | +start_failed: | ||
8116 | +prep_failed: | ||
8117 | + dma_unmap_single(DMA_DEV(self), map, SYNC_SKB_SIZE, DMA_FROM_DEVICE); | ||
8118 | +map_failed: | ||
8119 | + dev_kfree_skb(skb); | ||
8120 | +} | ||
8121 | + | ||
8122 | +static void __timbmost_sync_rx_done(struct timbmost *self) | ||
8123 | +{ | ||
8124 | + struct sk_buff *skb = self->sync_read_skb; | ||
8125 | + int len; | ||
8126 | + | ||
8127 | + BUG_ON(!skb); | ||
8128 | + | ||
8129 | + /* unmap DMA */ | ||
8130 | + dma_unmap_single(DMA_DEV(self), self->sync_read_handle, SYNC_SKB_SIZE, | ||
8131 | + DMA_FROM_DEVICE); | ||
8132 | + | ||
8133 | + /* set the length */ | ||
8134 | + len = timbdma_stop(DMA_IRQ_MLB_RX); | ||
8135 | + skb_put(skb, len); | ||
8136 | + /* send the SKB upwards */ | ||
8137 | + skb->dev = (void *)self->mdev; | ||
8138 | + most_cb(skb)->channel_type = CHAN_SYNC; | ||
8139 | + /* only one channel is supported... */ | ||
8140 | + most_cb(skb)->channel = self->sync_channels[RX_CHAN]; | ||
8141 | + most_recv_frame(skb); | ||
8142 | + self->sync_read_skb = NULL; | ||
8143 | + | ||
8144 | + __timbmost_sync_read_wake(self); | ||
8145 | +} | ||
8146 | + | ||
8147 | +static void __timbmost_sync_write_wake(struct timbmost *self) | ||
8148 | +{ | ||
8149 | + unsigned long flags; | ||
8150 | + int len; | ||
8151 | + dma_addr_t map; | ||
8152 | + struct sk_buff *skb = self->sync_write_skb; | ||
8153 | + u32 isr; | ||
8154 | + | ||
8155 | + dev_dbg(self->mdev->parent, "%s entry\n", __func__); | ||
8156 | + | ||
8157 | + if (!skb) { | ||
8158 | + /* check for next SKB */ | ||
8159 | + skb = skb_dequeue(&self->sync_q); | ||
8160 | + if (!skb) | ||
8161 | + return; | ||
8162 | + | ||
8163 | + if (skb_dma_map(DMA_DEV(self), skb, DMA_TO_DEVICE)) { | ||
8164 | + /* failed to dma map? */ | ||
8165 | + dev_kfree_skb(skb); | ||
8166 | + return; | ||
8167 | + } | ||
8168 | + /* next dma map to write is the first ... */ | ||
8169 | + self->sync_write_next_map = -1; | ||
8170 | + self->sync_write_skb = skb; | ||
8171 | + dev_dbg(self->mdev->parent, "%s: New skb: fragments: %d\n", | ||
8172 | + __func__, skb_shinfo(skb)->nr_frags); | ||
8173 | + } | ||
8174 | + | ||
8175 | + /* check if there is space in the FIFO */ | ||
8176 | + spin_lock_irqsave(&self->lock, flags); | ||
8177 | + isr = ioread32(self->membase + MLB_REG_ISR); | ||
8178 | + if (isr & MLB_WR_I_SYNC_TX_FULL) { | ||
8179 | + /* FIFO full enable, almost empty interrupt */ | ||
8180 | + u32 imr = ioread32(self->membase + MLB_REG_IMR); | ||
8181 | + imr |= MLB_WR_I_SYNC_TX_ALMOST_EMPTY; | ||
8182 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8183 | + } | ||
8184 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8185 | + | ||
8186 | + /* exit if the FIFO is full, we will continue when the almost empty | ||
8187 | + * interrupt occurs | ||
8188 | + */ | ||
8189 | + if (isr & MLB_WR_I_SYNC_TX_FULL) | ||
8190 | + return; | ||
8191 | + | ||
8192 | + /* send next fragment */ | ||
8193 | + if (self->sync_write_next_map < 0) { | ||
8194 | + len = skb_headlen(skb); | ||
8195 | + map = skb_shinfo(skb)->dma_head; | ||
8196 | + } else { | ||
8197 | + len = skb_shinfo(skb)->frags[self->sync_write_next_map].size; | ||
8198 | + map = skb_shinfo(skb)->dma_maps[self->sync_write_next_map]; | ||
8199 | + } | ||
8200 | + self->sync_write_next_map++; | ||
8201 | + dev_dbg(self->mdev->parent, "%s: Will send %x, len: %d\n", | ||
8202 | + __func__, (uint32_t)map, len); | ||
8203 | + timbdma_prep_desc(self->sync_write_desc, map, len); | ||
8204 | + timbdma_start(DMA_IRQ_MLB_TX, self->sync_write_desc, 0); | ||
8205 | +} | ||
8206 | + | ||
8207 | +static void __timbmost_sync_tx_done(struct timbmost *self) | ||
8208 | +{ | ||
8209 | + struct sk_buff *skb = self->sync_write_skb; | ||
8210 | + | ||
8211 | + /* TX done, free current SKB, and check for next */ | ||
8212 | + BUG_ON(!skb); | ||
8213 | + | ||
8214 | + /* check if this was the last DMA map */ | ||
8215 | + if (self->sync_write_next_map >= skb_shinfo(skb)->nr_frags) { | ||
8216 | + | ||
8217 | + /* it was the last... */ | ||
8218 | + skb_dma_unmap(DMA_DEV(self), skb, DMA_TO_DEVICE); | ||
8219 | + dev_kfree_skb(skb); | ||
8220 | + self->sync_write_skb = NULL; | ||
8221 | + } | ||
8222 | + | ||
8223 | + __timbmost_sync_write_wake(self); | ||
8224 | +} | ||
8225 | + | ||
8226 | +static void timbmost_sync_start_write(struct timbmost *self) | ||
8227 | +{ | ||
8228 | + unsigned long flags; | ||
8229 | + struct sk_buff *skb; | ||
8230 | + | ||
8231 | + spin_lock_irqsave(&self->lock, flags); | ||
8232 | + skb = self->sync_write_skb; | ||
8233 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8234 | + | ||
8235 | + /* transfer is ongoing */ | ||
8236 | + if (skb) | ||
8237 | + return; | ||
8238 | + | ||
8239 | + __timbmost_sync_write_wake(self); | ||
8240 | +} | ||
8241 | + | ||
8242 | +/* function called in interrupt context by the timberdale DMA engine | ||
8243 | + * when a transfer is finished | ||
8244 | + */ | ||
8245 | +static int timbmost_dma_irq(u32 flag, void *devid) | ||
8246 | +{ | ||
8247 | + struct timbmost *self = (struct timbmost *)devid; | ||
8248 | + | ||
8249 | + if (flag & DMA_IRQ_MLB_RX) | ||
8250 | + __timbmost_sync_rx_done(self); | ||
8251 | + | ||
8252 | + if (flag & DMA_IRQ_MLB_TX) | ||
8253 | + __timbmost_sync_tx_done(self); | ||
8254 | + | ||
8255 | + return 0; | ||
8256 | +} | ||
8257 | + | ||
8258 | +static irqreturn_t timbmost_irq(int irq, void *devid) | ||
8259 | +{ | ||
8260 | + struct timbmost *self = (struct timbmost *)devid; | ||
8261 | + u32 isr, imr; | ||
8262 | + | ||
8263 | + isr = ioread32(self->membase + MLB_REG_ISR); | ||
8264 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8265 | + | ||
8266 | + dev_dbg(self->mdev->parent, "%s: entry, isr: %x, imr: %x\n", __func__, | ||
8267 | + isr, imr); | ||
8268 | + | ||
8269 | + /* mask out only enabled interrupts */ | ||
8270 | + isr &= imr; | ||
8271 | + | ||
8272 | + /* ack */ | ||
8273 | + iowrite32(isr, self->membase + MLB_REG_ISR); | ||
8274 | + | ||
8275 | + if (isr & MLB_WR_I_SYNC_TX_ALMOST_EMPTY) { | ||
8276 | + /* disable the interrupt */ | ||
8277 | + imr &= ~MLB_WR_I_SYNC_TX_ALMOST_EMPTY; | ||
8278 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8279 | + __timbmost_sync_write_wake(self); | ||
8280 | + } | ||
8281 | + | ||
8282 | + if (isr & MLB_I_ASYNC_TX_READY) { | ||
8283 | + /* disable TX interrupts */ | ||
8284 | + imr &= ~(MLB_I_ASYNC_TX_READY | MLB_I_ASYNC_TX_PROT_ERR); | ||
8285 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8286 | + /* schedule to send next package */ | ||
8287 | + timbmost_async_write_wake(self); | ||
8288 | + } | ||
8289 | + | ||
8290 | + if (isr & MLB_I_ASYNC_RX_READY) | ||
8291 | + /* pass data upstreams */ | ||
8292 | + __timbmost_async_rx(self); | ||
8293 | + | ||
8294 | + if (isr & MLB_I_CTRL_TX_READY) { | ||
8295 | + /* disable TX interrupts */ | ||
8296 | + imr &= ~(MLB_I_CTRL_TX_READY | MLB_I_CTRL_TX_PROT_ERR); | ||
8297 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8298 | + /* schedule to send next package */ | ||
8299 | + timbmost_ctl_write_wake(self); | ||
8300 | + } | ||
8301 | + | ||
8302 | + if (isr & MLB_I_CTRL_RX_READY) | ||
8303 | + /* pass data upstreams */ | ||
8304 | + __timbmost_ctl_rx(self); | ||
8305 | + | ||
8306 | + if (isr) | ||
8307 | + return IRQ_HANDLED; | ||
8308 | + else | ||
8309 | + return IRQ_NONE; | ||
8310 | +} | ||
8311 | + | ||
8312 | +static int timbmost_open(struct most_dev *mdev) | ||
8313 | +{ | ||
8314 | + struct timbmost *self = (struct timbmost *)mdev->driver_data; | ||
8315 | + int err; | ||
8316 | + | ||
8317 | + dev_dbg(mdev->parent, "%s\n", __func__); | ||
8318 | + | ||
8319 | + skb_queue_head_init(&self->ctl_q); | ||
8320 | + skb_queue_head_init(&self->sync_q); | ||
8321 | + skb_queue_head_init(&self->async_q); | ||
8322 | + | ||
8323 | + spin_lock_init(&self->lock); | ||
8324 | + | ||
8325 | + /* request the GPIO reset pin */ | ||
8326 | + err = gpio_request(self->reset_pin, DRIVER_NAME); | ||
8327 | + if (err) { | ||
8328 | + printk(KERN_ERR DRIVER_NAME | ||
8329 | + " Failed to request reset pin: %d, err: %d\n", | ||
8330 | + self->reset_pin, err); | ||
8331 | + return err; | ||
8332 | + } | ||
8333 | + | ||
8334 | + __timbmost_hw_reset(self); | ||
8335 | + | ||
8336 | + /* set DMA callback */ | ||
8337 | + timbdma_set_interruptcb(DMA_IRQ_MLB_RX | DMA_IRQ_MLB_TX, | ||
8338 | + timbmost_dma_irq, (void *)self); | ||
8339 | + | ||
8340 | + self->sync_read_desc = timbdma_alloc_desc(SYNC_MAX_DMA_SIZE, 1); | ||
8341 | + if (!self->sync_read_desc) { | ||
8342 | + err = -ENOMEM; | ||
8343 | + goto err_alloc_r_desc; | ||
8344 | + } | ||
8345 | + | ||
8346 | + self->sync_write_desc = timbdma_alloc_desc(SYNC_MAX_DMA_SIZE, 1); | ||
8347 | + if (!self->sync_write_desc) { | ||
8348 | + err = -ENOMEM; | ||
8349 | + goto err_alloc_w_desc; | ||
8350 | + } | ||
8351 | + | ||
8352 | + /* request IRQ */ | ||
8353 | + err = request_irq(self->irq, timbmost_irq, IRQF_SHARED, "timb-most", | ||
8354 | + self); | ||
8355 | + if (err) | ||
8356 | + goto err_req_irq; | ||
8357 | + | ||
8358 | + return 0; | ||
8359 | + | ||
8360 | +err_req_irq: | ||
8361 | + timbdma_free_desc(self->sync_write_desc); | ||
8362 | +err_alloc_w_desc: | ||
8363 | + timbdma_free_desc(self->sync_read_desc); | ||
8364 | +err_alloc_r_desc: | ||
8365 | + gpio_free(self->reset_pin); | ||
8366 | + return err; | ||
8367 | +} | ||
8368 | + | ||
8369 | +static void timbmost_stop_sync_dma(struct timbmost *self) | ||
8370 | +{ | ||
8371 | + if (self->sync_read_skb) { | ||
8372 | + timbdma_stop(DMA_IRQ_MLB_RX); | ||
8373 | + dma_unmap_single(DMA_DEV(self), self->sync_read_handle, | ||
8374 | + SYNC_SKB_SIZE, DMA_FROM_DEVICE); | ||
8375 | + kfree_skb(self->sync_read_skb); | ||
8376 | + self->sync_read_skb = NULL; | ||
8377 | + } | ||
8378 | + | ||
8379 | + if (self->sync_write_skb) { | ||
8380 | + timbdma_stop(DMA_IRQ_MLB_TX); | ||
8381 | + skb_dma_unmap(DMA_DEV(self), self->sync_write_skb, | ||
8382 | + DMA_TO_DEVICE); | ||
8383 | + kfree_skb(self->sync_write_skb); | ||
8384 | + self->sync_write_skb = NULL; | ||
8385 | + } | ||
8386 | +} | ||
8387 | + | ||
8388 | +static int timbmost_close(struct most_dev *mdev) | ||
8389 | +{ | ||
8390 | + struct timbmost *self = (struct timbmost *)mdev->driver_data; | ||
8391 | + | ||
8392 | + dev_dbg(mdev->parent, "%s\n", __func__); | ||
8393 | + | ||
8394 | + /* free IRQ */ | ||
8395 | + free_irq(self->irq, self); | ||
8396 | + | ||
8397 | + __timbmost_hw_reset(self); | ||
8398 | + | ||
8399 | + /* free GPIO */ | ||
8400 | + gpio_free(self->reset_pin); | ||
8401 | + | ||
8402 | + /* empty all queues */ | ||
8403 | + skb_queue_purge(&self->ctl_q); | ||
8404 | + skb_queue_purge(&self->sync_q); | ||
8405 | + skb_queue_purge(&self->async_q); | ||
8406 | + | ||
8407 | + /* clear DMA callback */ | ||
8408 | + timbdma_set_interruptcb(DMA_IRQ_MLB_RX | DMA_IRQ_MLB_TX, NULL, NULL); | ||
8409 | + | ||
8410 | + return 0; | ||
8411 | +} | ||
8412 | + | ||
8413 | +static int __timbmost_conf_channel(struct timbmost *self, u8 channel, | ||
8414 | + u8 channel_mask) | ||
8415 | +{ | ||
8416 | + int register_offset; | ||
8417 | + int shift; | ||
8418 | + u32 ch_cfg; | ||
8419 | + | ||
8420 | + /* only even channel numbers are allowed */ | ||
8421 | + if (channel % 2 || channel > 0x3e || channel == 0) { | ||
8422 | + printk(KERN_WARNING DRIVER_NAME": Invalid channel: %d\n", | ||
8423 | + channel); | ||
8424 | + return -EINVAL; | ||
8425 | + } | ||
8426 | + | ||
8427 | + channel = (channel / 2) - 1; | ||
8428 | + /* the channel conf is spread out over the 7 channel config registers | ||
8429 | + * each register configures 5 channels, each reg is 32bit | ||
8430 | + */ | ||
8431 | + register_offset = MLB_REG_CH_CFG_1 + (channel / 5) * 4; | ||
8432 | + | ||
8433 | + /* each register configures 5 channels, 3 bit per channel | ||
8434 | + * lowest bits configures highest channel | ||
8435 | + */ | ||
8436 | + shift = (4 - (channel % 5)) * 3; | ||
8437 | + | ||
8438 | + ch_cfg = ioread32(self->membase + register_offset); | ||
8439 | + ch_cfg &= ~(0x7 << shift); | ||
8440 | + ch_cfg |= (channel_mask & 0x7) << shift; | ||
8441 | + iowrite32(ch_cfg, self->membase + register_offset); | ||
8442 | + return 0; | ||
8443 | +} | ||
8444 | + | ||
8445 | +static int timbmost_conf_channel(struct most_dev *mdev, | ||
8446 | + enum most_chan_type type, u8 channel, u8 flags) | ||
8447 | +{ | ||
8448 | + struct timbmost *self = (struct timbmost *)mdev->driver_data; | ||
8449 | + unsigned long irq_flags; | ||
8450 | + u32 imr, cfg; | ||
8451 | + int err = -EINVAL; | ||
8452 | + int chan_idx = (flags & MOST_CONF_FLAG_TX) ? TX_CHAN : RX_CHAN; | ||
8453 | + | ||
8454 | + dev_dbg(mdev->parent, "%s: channel: %d, flags: %x\n", | ||
8455 | + __func__, channel, flags); | ||
8456 | + | ||
8457 | + if (flags & MOST_CONF_FLAG_UP) { | ||
8458 | + switch (type) { | ||
8459 | + case CHAN_CTL: | ||
8460 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8461 | + /* we only support one channel at the time */ | ||
8462 | + if (self->ctl_channels[chan_idx]) | ||
8463 | + goto error; | ||
8464 | + | ||
8465 | + /* reset the FIFO */ | ||
8466 | + iowrite32((chan_idx == TX_CHAN) ? MLB_FIFO_RST_CTRL_TX : | ||
8467 | + MLB_FIFO_RST_CTRL_RX, | ||
8468 | + self->membase + MLB_REG_FIFO_RST); | ||
8469 | + | ||
8470 | + err = __timbmost_conf_channel(self, channel, | ||
8471 | + (chan_idx == TX_CHAN) ? MLB_CH_CFG_CTRL_TX : | ||
8472 | + MLB_CH_CFG_CTRL_RX); | ||
8473 | + if (err) | ||
8474 | + goto error; | ||
8475 | + | ||
8476 | + if (chan_idx == RX_CHAN) { | ||
8477 | + /* enable the receiver */ | ||
8478 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8479 | + cfg |= MLB_CFG_CTRL_RX_EN; | ||
8480 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8481 | + | ||
8482 | + /* enable RX interrupts */ | ||
8483 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8484 | + imr |= (MLB_I_CTRL_RX_READY | | ||
8485 | + MLB_I_CTRL_RX_PROT_ERR | | ||
8486 | + MLB_I_CTRL_RX_CMD_BREAK); | ||
8487 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8488 | + } | ||
8489 | + self->ctl_channels[chan_idx] = channel; | ||
8490 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8491 | + break; | ||
8492 | + case CHAN_SYNC: | ||
8493 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8494 | + /* we only support one channel at the time */ | ||
8495 | + if (self->sync_channels[chan_idx]) | ||
8496 | + goto error; | ||
8497 | + | ||
8498 | + /* reset the FIFO */ | ||
8499 | + iowrite32((chan_idx == TX_CHAN) ? MLB_FIFO_RST_SYNC_TX : | ||
8500 | + MLB_FIFO_RST_SYNC_RX, | ||
8501 | + self->membase + MLB_REG_FIFO_RST); | ||
8502 | + | ||
8503 | + err = __timbmost_conf_channel(self, channel, | ||
8504 | + (chan_idx == TX_CHAN) ? MLB_CH_CFG_SYNC_TX : | ||
8505 | + MLB_CH_CFG_SYNC_RX); | ||
8506 | + if (err) | ||
8507 | + goto error; | ||
8508 | + | ||
8509 | + if (chan_idx == RX_CHAN) { | ||
8510 | + /* enable the receiver */ | ||
8511 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8512 | + cfg |= MLB_CFG_SYNC_RX_EN; | ||
8513 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8514 | + | ||
8515 | + /* enable prot error interrupts */ | ||
8516 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8517 | + imr |= MLB_I_SYNC_RX_PROT_ERR; | ||
8518 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8519 | + /* start RX DMA */ | ||
8520 | + __timbmost_sync_read_wake(self); | ||
8521 | + } | ||
8522 | + self->sync_channels[chan_idx] = channel; | ||
8523 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8524 | + | ||
8525 | + break; | ||
8526 | + case CHAN_ASYNC: | ||
8527 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8528 | + /* we only support one channel at the time */ | ||
8529 | + if (self->async_channels[chan_idx]) | ||
8530 | + goto error; | ||
8531 | + /* reset the FIFO */ | ||
8532 | + iowrite32((chan_idx == TX_CHAN) ? | ||
8533 | + MLB_FIFO_RST_ASYNC_TX : MLB_FIFO_RST_ASYNC_RX, | ||
8534 | + self->membase + MLB_REG_FIFO_RST); | ||
8535 | + | ||
8536 | + err = __timbmost_conf_channel(self, channel, | ||
8537 | + (chan_idx == TX_CHAN) ? MLB_CH_CFG_ASYNC_TX : | ||
8538 | + MLB_CH_CFG_ASYNC_RX); | ||
8539 | + if (err) | ||
8540 | + goto error; | ||
8541 | + | ||
8542 | + if (chan_idx == RX_CHAN) { | ||
8543 | + /* enable the receiver */ | ||
8544 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8545 | + cfg |= MLB_CFG_ASYNC_RX_EN; | ||
8546 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8547 | + | ||
8548 | + /* enable RX interrupts */ | ||
8549 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8550 | + imr |= (MLB_I_ASYNC_RX_READY | | ||
8551 | + MLB_I_ASYNC_RX_PROT_ERR | | ||
8552 | + MLB_I_ASYNC_RX_CMD_BREAK); | ||
8553 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8554 | + } | ||
8555 | + self->async_channels[chan_idx] = channel; | ||
8556 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8557 | + break; | ||
8558 | + default: | ||
8559 | + printk(KERN_WARNING "timbmlb: Uknown channel type\n"); | ||
8560 | + return -EINVAL; | ||
8561 | + } | ||
8562 | + } else { | ||
8563 | + switch (type) { | ||
8564 | + case CHAN_CTL: | ||
8565 | + /* stop any ongoing transfer */ | ||
8566 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8567 | + if (self->ctl_channels[chan_idx] != channel) | ||
8568 | + goto error; | ||
8569 | + | ||
8570 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8571 | + imr &= ~(MLB_I_CTRL_TX_READY | | ||
8572 | + MLB_I_CTRL_TX_PROT_ERR | | ||
8573 | + MLB_I_CTRL_TX_RX_BREAK | | ||
8574 | + MLB_I_CTRL_TX_BUSY_BREAK | | ||
8575 | + MLB_I_CTRL_RX_READY | | ||
8576 | + MLB_I_CTRL_RX_PROT_ERR | | ||
8577 | + MLB_I_CTRL_RX_CMD_BREAK); | ||
8578 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8579 | + | ||
8580 | + /* disable CTL RX */ | ||
8581 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8582 | + cfg &= ~MLB_CFG_CTRL_RX_EN; | ||
8583 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8584 | + | ||
8585 | + err = __timbmost_conf_channel(self, channel, | ||
8586 | + MLB_CH_CFG_NOT_ALLOCATED); | ||
8587 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8588 | + skb_queue_purge(&self->ctl_q); | ||
8589 | + self->ctl_channels[chan_idx] = 0; | ||
8590 | + return err; | ||
8591 | + case CHAN_SYNC: | ||
8592 | + | ||
8593 | + /* stop any ongoing transfer */ | ||
8594 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8595 | + if (self->sync_channels[chan_idx] != channel) | ||
8596 | + goto error; | ||
8597 | + | ||
8598 | + /* stop DMA */ | ||
8599 | + timbmost_stop_sync_dma(self); | ||
8600 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8601 | + imr &= ~MLB_I_SYNC_RX_PROT_ERR; | ||
8602 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8603 | + | ||
8604 | + /* disable SYNC TX/RX */ | ||
8605 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8606 | + cfg &= ~(MLB_CFG_SYNC_TX_EN | | ||
8607 | + MLB_CFG_SYNC_RX_EN); | ||
8608 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8609 | + | ||
8610 | + err = __timbmost_conf_channel(self, channel, | ||
8611 | + MLB_CH_CFG_NOT_ALLOCATED); | ||
8612 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8613 | + skb_queue_purge(&self->sync_q); | ||
8614 | + self->sync_channels[chan_idx] = 0; | ||
8615 | + return err; | ||
8616 | + case CHAN_ASYNC: | ||
8617 | + /* stop any ongoing transfer */ | ||
8618 | + spin_lock_irqsave(&self->lock, irq_flags); | ||
8619 | + if (self->async_channels[chan_idx] != channel) | ||
8620 | + goto error; | ||
8621 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8622 | + imr &= ~(MLB_I_ASYNC_TX_READY | | ||
8623 | + MLB_I_ASYNC_TX_PROT_ERR | | ||
8624 | + MLB_I_ASYNC_TX_RX_BREAK | | ||
8625 | + MLB_I_ASYNC_TX_BUSY_BREAK | | ||
8626 | + MLB_I_ASYNC_RX_READY | | ||
8627 | + MLB_I_ASYNC_RX_PROT_ERR | | ||
8628 | + MLB_I_ASYNC_RX_CMD_BREAK); | ||
8629 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8630 | + | ||
8631 | + /* disable CTL RX */ | ||
8632 | + cfg = ioread32(self->membase + MLB_REG_CFG); | ||
8633 | + cfg &= ~MLB_CFG_ASYNC_RX_EN; | ||
8634 | + iowrite32(cfg, self->membase + MLB_REG_CFG); | ||
8635 | + | ||
8636 | + err = __timbmost_conf_channel(self, channel, | ||
8637 | + MLB_CH_CFG_NOT_ALLOCATED); | ||
8638 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8639 | + skb_queue_purge(&self->async_q); | ||
8640 | + self->async_channels[chan_idx] = 0; | ||
8641 | + return err; | ||
8642 | + default: | ||
8643 | + return -EINVAL; | ||
8644 | + } | ||
8645 | + } | ||
8646 | + return 0; | ||
8647 | + | ||
8648 | +error: | ||
8649 | + spin_unlock_irqrestore(&self->lock, irq_flags); | ||
8650 | + return err; | ||
8651 | +} | ||
8652 | + | ||
8653 | +static void timbmost_ctl_write_wake(struct timbmost *self) | ||
8654 | +{ | ||
8655 | + unsigned long flags; | ||
8656 | + u32 imr; | ||
8657 | + u32 isr; | ||
8658 | + struct sk_buff *skb; | ||
8659 | + int i; | ||
8660 | + | ||
8661 | + dev_dbg(self->mdev->parent, "%s entry\n", __func__); | ||
8662 | + __timbmost_dump_regs(self, "Before write"); | ||
8663 | + | ||
8664 | + spin_lock_irqsave(&self->lock, flags); | ||
8665 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8666 | + isr = ioread32(self->membase + MLB_REG_ISR); | ||
8667 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8668 | + | ||
8669 | + /* check if the hardware is currently writing */ | ||
8670 | + if (imr & MLB_I_CTRL_TX_READY) | ||
8671 | + return; | ||
8672 | + | ||
8673 | + /* check if we have sync */ | ||
8674 | + if (!(isr & MLB_I_SYNC_LOCK)) | ||
8675 | + return; | ||
8676 | + | ||
8677 | + skb = skb_dequeue(&self->ctl_q); | ||
8678 | + if (!skb) | ||
8679 | + return; | ||
8680 | + | ||
8681 | + /* now write to the FIFO */ | ||
8682 | + for (i = 0; i < skb->len;) { | ||
8683 | + u32 word = 0; | ||
8684 | + int j; | ||
8685 | + | ||
8686 | + for (j = 0; j < 4 && i < skb->len; j++, i++) | ||
8687 | + word |= ((u8 *)skb->data)[i] << j * 8; | ||
8688 | + | ||
8689 | + iowrite32(word, self->membase + MLB_REG_CTRL_TX); | ||
8690 | + } | ||
8691 | + | ||
8692 | + /* data is in the FIFO, enable proper interrupts */ | ||
8693 | + spin_lock_irqsave(&self->lock, flags); | ||
8694 | + imr = ioread32(self->membase + MLB_REG_IMR) | MLB_I_CTRL_TX_READY | | ||
8695 | + MLB_I_CTRL_TX_PROT_ERR; | ||
8696 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8697 | + /* start TX */ | ||
8698 | + iowrite32(MLB_CH_CTRL_CTRL_TX_START, self->membase + MLB_REG_CH_CTRL); | ||
8699 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8700 | + | ||
8701 | + kfree_skb(skb); | ||
8702 | +} | ||
8703 | + | ||
8704 | +static void timbmost_async_write_wake(struct timbmost *self) | ||
8705 | +{ | ||
8706 | + unsigned long flags; | ||
8707 | + u32 imr; | ||
8708 | + u32 isr; | ||
8709 | + struct sk_buff *skb; | ||
8710 | + int i; | ||
8711 | + | ||
8712 | + spin_lock_irqsave(&self->lock, flags); | ||
8713 | + imr = ioread32(self->membase + MLB_REG_IMR); | ||
8714 | + isr = ioread32(self->membase + MLB_REG_ISR); | ||
8715 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8716 | + | ||
8717 | + /* check if the hardware is currently writing */ | ||
8718 | + if (imr & MLB_I_ASYNC_TX_READY) | ||
8719 | + return; | ||
8720 | + | ||
8721 | + /* check if we have sync */ | ||
8722 | + if (!(isr & MLB_I_SYNC_LOCK)) | ||
8723 | + return; | ||
8724 | + | ||
8725 | + skb = skb_dequeue(&self->async_q); | ||
8726 | + if (!skb) | ||
8727 | + return; | ||
8728 | + | ||
8729 | + /* TODO: The FIFO is 32bit not 8bit */ | ||
8730 | + /* now write to the FIFO */ | ||
8731 | + for (i = 0; i < skb->len; i++) | ||
8732 | + iowrite32(skb->data[i], self->membase + MLB_REG_ASYNC_TX); | ||
8733 | + | ||
8734 | + /* data is in the FIFO, enable proper interrupts */ | ||
8735 | + spin_lock_irqsave(&self->lock, flags); | ||
8736 | + imr = ioread32(self->membase + MLB_REG_IMR) | MLB_I_ASYNC_TX_READY | | ||
8737 | + MLB_I_ASYNC_TX_PROT_ERR; | ||
8738 | + iowrite32(imr, self->membase + MLB_REG_IMR); | ||
8739 | + /* start TX */ | ||
8740 | + iowrite32(MLB_CH_CTRL_ASYNC_TX_START, self->membase + MLB_REG_CH_CTRL); | ||
8741 | + spin_unlock_irqrestore(&self->lock, flags); | ||
8742 | + | ||
8743 | + kfree_skb(skb); | ||
8744 | +} | ||
8745 | + | ||
8746 | +static int timbmost_send(struct sk_buff *skb) | ||
8747 | +{ | ||
8748 | + struct most_dev *mdev = (struct most_dev *)skb->dev; | ||
8749 | + struct timbmost *self = (struct timbmost *)mdev->driver_data; | ||
8750 | + | ||
8751 | + dev_dbg(mdev->parent, "%s, type: %d\n", | ||
8752 | + __func__, most_cb(skb)->channel_type); | ||
8753 | + | ||
8754 | + switch (most_cb(skb)->channel_type) { | ||
8755 | + case CHAN_CTL: | ||
8756 | + skb_queue_tail(&self->ctl_q, skb); | ||
8757 | + timbmost_ctl_write_wake(self); | ||
8758 | + break; | ||
8759 | + case CHAN_ASYNC: | ||
8760 | + skb_queue_tail(&self->async_q, skb); | ||
8761 | + timbmost_async_write_wake(self); | ||
8762 | + break; | ||
8763 | + case CHAN_SYNC: | ||
8764 | + skb_queue_tail(&self->sync_q, skb); | ||
8765 | + timbmost_sync_start_write(self); | ||
8766 | + break; | ||
8767 | + default: | ||
8768 | + printk(KERN_WARNING "%s: Got unsupported channel type: %d\n", | ||
8769 | + __func__, most_cb(skb)->channel_type); | ||
8770 | + kfree_skb(skb); | ||
8771 | + break; | ||
8772 | + } | ||
8773 | + | ||
8774 | + return 0; | ||
8775 | +} | ||
8776 | + | ||
8777 | +static int timbmost_probe(struct platform_device *dev) | ||
8778 | +{ | ||
8779 | + int err; | ||
8780 | + struct timbmost *self = NULL; | ||
8781 | + struct resource *iomem; | ||
8782 | + struct timbmlb_platform_data *pdata = dev->dev.platform_data; | ||
8783 | + | ||
8784 | + if (!pdata) { | ||
8785 | + printk(KERN_ERR DRIVER_NAME "No platform data supplied\n"); | ||
8786 | + err = -EINVAL; | ||
8787 | + goto err_mem; | ||
8788 | + } | ||
8789 | + | ||
8790 | + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
8791 | + if (!iomem) { | ||
8792 | + err = -EINVAL; | ||
8793 | + goto err_mem; | ||
8794 | + } | ||
8795 | + | ||
8796 | + self = kzalloc(sizeof(*self), GFP_KERNEL); | ||
8797 | + if (!self) { | ||
8798 | + err = -ENOMEM; | ||
8799 | + goto err_mem; | ||
8800 | + } | ||
8801 | + | ||
8802 | + self->mdev = most_alloc_dev(); | ||
8803 | + if (!self->mdev) { | ||
8804 | + err = -ENOMEM; | ||
8805 | + goto err_mem; | ||
8806 | + } | ||
8807 | + | ||
8808 | + self->mdev->owner = THIS_MODULE; | ||
8809 | + self->mdev->driver_data = self; | ||
8810 | + self->mdev->parent = &dev->dev; | ||
8811 | + self->mdev->open = timbmost_open; | ||
8812 | + self->mdev->close = timbmost_close; | ||
8813 | + self->mdev->send = timbmost_send; | ||
8814 | + self->mdev->conf_channel = timbmost_conf_channel; | ||
8815 | + | ||
8816 | + if (!request_mem_region(iomem->start, | ||
8817 | + resource_size(iomem), "timb-most")) { | ||
8818 | + err = -EBUSY; | ||
8819 | + goto err_mem; | ||
8820 | + } | ||
8821 | + | ||
8822 | + self->membase = ioremap(iomem->start, resource_size(iomem)); | ||
8823 | + if (!self->membase) { | ||
8824 | + printk(KERN_ERR "timbmost: Failed to remap I/O memory\n"); | ||
8825 | + err = -ENOMEM; | ||
8826 | + goto err_ioremap; | ||
8827 | + } | ||
8828 | + | ||
8829 | + self->reset_pin = pdata->reset_pin; | ||
8830 | + | ||
8831 | + /* find interrupt */ | ||
8832 | + self->irq = platform_get_irq(dev, 0); | ||
8833 | + if (self->irq < 0) { | ||
8834 | + err = self->irq; | ||
8835 | + goto err_get_irq; | ||
8836 | + } | ||
8837 | + | ||
8838 | + /* register to the MOST layer */ | ||
8839 | + err = most_register_dev(self->mdev); | ||
8840 | + if (err) | ||
8841 | + goto err_register; | ||
8842 | + | ||
8843 | + | ||
8844 | + platform_set_drvdata(dev, self); | ||
8845 | + | ||
8846 | + return 0; | ||
8847 | + | ||
8848 | +err_get_irq: | ||
8849 | +err_register: | ||
8850 | + iounmap(self->membase); | ||
8851 | +err_ioremap: | ||
8852 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
8853 | +err_mem: | ||
8854 | + if (self) { | ||
8855 | + if (self->mdev) | ||
8856 | + most_free_dev(self->mdev); | ||
8857 | + | ||
8858 | + timbdma_free_desc(self->sync_read_desc); | ||
8859 | + timbdma_free_desc(self->sync_write_desc); | ||
8860 | + | ||
8861 | + kfree(self); | ||
8862 | + } | ||
8863 | + printk(KERN_ERR "timb-most: Failed to register: %d\n", err); | ||
8864 | + | ||
8865 | + return err; | ||
8866 | +} | ||
8867 | + | ||
8868 | +static int timbmost_remove(struct platform_device *dev) | ||
8869 | +{ | ||
8870 | + struct timbmost *self = platform_get_drvdata(dev); | ||
8871 | + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
8872 | + | ||
8873 | + most_unregister_dev(self->mdev); | ||
8874 | + iounmap(self->membase); | ||
8875 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
8876 | + most_free_dev(self->mdev); | ||
8877 | + kfree(self); | ||
8878 | + return 0; | ||
8879 | +} | ||
8880 | + | ||
8881 | +static struct platform_driver timbmost_platform_driver = { | ||
8882 | + .driver = { | ||
8883 | + .name = DRIVER_NAME, | ||
8884 | + .owner = THIS_MODULE, | ||
8885 | + }, | ||
8886 | + .probe = timbmost_probe, | ||
8887 | + .remove = timbmost_remove, | ||
8888 | +}; | ||
8889 | + | ||
8890 | +/*--------------------------------------------------------------------------*/ | ||
8891 | + | ||
8892 | +static int __init timbmost_init(void) | ||
8893 | +{ | ||
8894 | + return platform_driver_register(&timbmost_platform_driver); | ||
8895 | +} | ||
8896 | + | ||
8897 | +static void __exit timbmost_exit(void) | ||
8898 | +{ | ||
8899 | + platform_driver_unregister(&timbmost_platform_driver); | ||
8900 | +} | ||
8901 | + | ||
8902 | +module_init(timbmost_init); | ||
8903 | +module_exit(timbmost_exit); | ||
8904 | + | ||
8905 | +MODULE_DESCRIPTION("Timberdale MLB driver"); | ||
8906 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
8907 | +MODULE_LICENSE("GPL v2"); | ||
8908 | +MODULE_ALIAS("platform:timb-most"); | ||
8909 | + | ||
8910 | diff -uNr linux-2.6.31/drivers/serial/Kconfig linux-2.6.31.new/drivers/serial/Kconfig | ||
8911 | --- linux-2.6.31/drivers/serial/Kconfig 2009-10-23 11:18:08.000000000 -0700 | ||
8912 | +++ linux-2.6.31.new/drivers/serial/Kconfig 2009-10-23 11:17:29.000000000 -0700 | ||
8913 | @@ -855,7 +855,7 @@ | ||
8914 | |||
8915 | config SERIAL_UARTLITE | ||
8916 | tristate "Xilinx uartlite serial port support" | ||
8917 | - depends on PPC32 || MICROBLAZE | ||
8918 | + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE | ||
8919 | select SERIAL_CORE | ||
8920 | help | ||
8921 | Say Y here if you want to use the Xilinx uartlite serial controller. | ||
8922 | diff -uNr linux-2.6.31/drivers/serial/timbuart.c linux-2.6.31.new/drivers/serial/timbuart.c | ||
8923 | --- linux-2.6.31/drivers/serial/timbuart.c 2009-10-23 11:18:30.000000000 -0700 | ||
8924 | +++ linux-2.6.31.new/drivers/serial/timbuart.c 2009-10-23 11:17:29.000000000 -0700 | ||
8925 | @@ -31,6 +31,7 @@ | ||
8926 | |||
8927 | struct timbuart_port { | ||
8928 | struct uart_port port; | ||
8929 | + struct uart_driver uart_driver; | ||
8930 | struct tasklet_struct tasklet; | ||
8931 | int usedma; | ||
8932 | u32 last_ier; | ||
8933 | @@ -410,7 +411,7 @@ | ||
8934 | .verify_port = timbuart_verify_port | ||
8935 | }; | ||
8936 | |||
8937 | -static struct uart_driver timbuart_driver = { | ||
8938 | +static const __devinitconst struct uart_driver timbuart_driver_template = { | ||
8939 | .owner = THIS_MODULE, | ||
8940 | .driver_name = "timberdale_uart", | ||
8941 | .dev_name = "ttyTU", | ||
8942 | @@ -419,7 +420,7 @@ | ||
8943 | .nr = 1 | ||
8944 | }; | ||
8945 | |||
8946 | -static int timbuart_probe(struct platform_device *dev) | ||
8947 | +static int __devinit timbuart_probe(struct platform_device *dev) | ||
8948 | { | ||
8949 | int err; | ||
8950 | struct timbuart_port *uart; | ||
8951 | @@ -433,6 +434,8 @@ | ||
8952 | goto err_mem; | ||
8953 | } | ||
8954 | |||
8955 | + uart->uart_driver = timbuart_driver_template; | ||
8956 | + | ||
8957 | uart->usedma = 0; | ||
8958 | |||
8959 | uart->port.uartclk = 3250000 * 16; | ||
8960 | @@ -461,11 +464,11 @@ | ||
8961 | |||
8962 | tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); | ||
8963 | |||
8964 | - err = uart_register_driver(&timbuart_driver); | ||
8965 | + err = uart_register_driver(&uart->uart_driver); | ||
8966 | if (err) | ||
8967 | goto err_register; | ||
8968 | |||
8969 | - err = uart_add_one_port(&timbuart_driver, &uart->port); | ||
8970 | + err = uart_add_one_port(&uart->uart_driver, &uart->port); | ||
8971 | if (err) | ||
8972 | goto err_add_port; | ||
8973 | |||
8974 | @@ -474,7 +477,7 @@ | ||
8975 | return 0; | ||
8976 | |||
8977 | err_add_port: | ||
8978 | - uart_unregister_driver(&timbuart_driver); | ||
8979 | + uart_unregister_driver(&uart->uart_driver); | ||
8980 | err_register: | ||
8981 | kfree(uart); | ||
8982 | err_mem: | ||
8983 | @@ -484,13 +487,13 @@ | ||
8984 | return err; | ||
8985 | } | ||
8986 | |||
8987 | -static int timbuart_remove(struct platform_device *dev) | ||
8988 | +static int __devexit timbuart_remove(struct platform_device *dev) | ||
8989 | { | ||
8990 | struct timbuart_port *uart = platform_get_drvdata(dev); | ||
8991 | |||
8992 | tasklet_kill(&uart->tasklet); | ||
8993 | - uart_remove_one_port(&timbuart_driver, &uart->port); | ||
8994 | - uart_unregister_driver(&timbuart_driver); | ||
8995 | + uart_remove_one_port(&uart->uart_driver, &uart->port); | ||
8996 | + uart_unregister_driver(&uart->uart_driver); | ||
8997 | kfree(uart); | ||
8998 | |||
8999 | return 0; | ||
9000 | diff -uNr linux-2.6.31/drivers/spi/Kconfig linux-2.6.31.new/drivers/spi/Kconfig | ||
9001 | --- linux-2.6.31/drivers/spi/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
9002 | +++ linux-2.6.31.new/drivers/spi/Kconfig 2009-10-23 11:17:32.000000000 -0700 | ||
9003 | @@ -218,8 +218,8 @@ | ||
9004 | SPI driver for Toshiba TXx9 MIPS SoCs | ||
9005 | |||
9006 | config SPI_XILINX | ||
9007 | - tristate "Xilinx SPI controller" | ||
9008 | - depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL | ||
9009 | + tristate "Xilinx SPI controller common module" | ||
9010 | + depends on EXPERIMENTAL | ||
9011 | select SPI_BITBANG | ||
9012 | help | ||
9013 | This exposes the SPI controller IP from the Xilinx EDK. | ||
9014 | @@ -227,6 +227,25 @@ | ||
9015 | See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
9016 | Product Specification document (DS464) for hardware details. | ||
9017 | |||
9018 | +config SPI_XILINX_OF | ||
9019 | + tristate "Xilinx SPI controller OF device" | ||
9020 | + depends on SPI_XILINX && XILINX_VIRTEX | ||
9021 | + help | ||
9022 | + This exposes the SPI controller IP from the Xilinx EDK. | ||
9023 | + | ||
9024 | + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
9025 | + Product Specification document (DS464) for hardware details. | ||
9026 | + | ||
9027 | +config SPI_XILINX_PLTFM | ||
9028 | + tristate "Xilinx SPI controller platform device" | ||
9029 | + depends on SPI_XILINX | ||
9030 | + help | ||
9031 | + This exposes the SPI controller IP from the Xilinx EDK. | ||
9032 | + | ||
9033 | + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" | ||
9034 | + Product Specification document (DS464) for hardware details. | ||
9035 | + | ||
9036 | + | ||
9037 | # | ||
9038 | # Add new SPI master controllers in alphabetical order above this line | ||
9039 | # | ||
9040 | diff -uNr linux-2.6.31/drivers/spi/Makefile linux-2.6.31.new/drivers/spi/Makefile | ||
9041 | --- linux-2.6.31/drivers/spi/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
9042 | +++ linux-2.6.31.new/drivers/spi/Makefile 2009-10-23 11:17:32.000000000 -0700 | ||
9043 | @@ -30,6 +30,8 @@ | ||
9044 | obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o | ||
9045 | obj-$(CONFIG_SPI_TXX9) += spi_txx9.o | ||
9046 | obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o | ||
9047 | +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o | ||
9048 | +obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o | ||
9049 | obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o | ||
9050 | # ... add above this line ... | ||
9051 | |||
9052 | diff -uNr linux-2.6.31/drivers/spi/xilinx_spi.c linux-2.6.31.new/drivers/spi/xilinx_spi.c | ||
9053 | --- linux-2.6.31/drivers/spi/xilinx_spi.c 2009-10-23 11:18:30.000000000 -0700 | ||
9054 | +++ linux-2.6.31.new/drivers/spi/xilinx_spi.c 2009-10-23 11:17:32.000000000 -0700 | ||
9055 | @@ -14,22 +14,35 @@ | ||
9056 | #include <linux/module.h> | ||
9057 | #include <linux/init.h> | ||
9058 | #include <linux/interrupt.h> | ||
9059 | -#include <linux/platform_device.h> | ||
9060 | - | ||
9061 | -#include <linux/of_platform.h> | ||
9062 | -#include <linux/of_device.h> | ||
9063 | -#include <linux/of_spi.h> | ||
9064 | |||
9065 | #include <linux/spi/spi.h> | ||
9066 | #include <linux/spi/spi_bitbang.h> | ||
9067 | #include <linux/io.h> | ||
9068 | |||
9069 | -#define XILINX_SPI_NAME "xilinx_spi" | ||
9070 | +#include "xilinx_spi.h" | ||
9071 | + | ||
9072 | +struct xilinx_spi { | ||
9073 | + /* bitbang has to be first */ | ||
9074 | + struct spi_bitbang bitbang; | ||
9075 | + struct completion done; | ||
9076 | + struct resource mem; /* phys mem */ | ||
9077 | + void __iomem *regs; /* virt. address of the control registers */ | ||
9078 | + u32 irq; | ||
9079 | + u8 *rx_ptr; /* pointer in the Tx buffer */ | ||
9080 | + const u8 *tx_ptr; /* pointer in the Rx buffer */ | ||
9081 | + int remaining_bytes; /* the number of bytes left to transfer */ | ||
9082 | + /* offset to the XSPI regs, these might vary... */ | ||
9083 | + u8 bits_per_word; | ||
9084 | + bool big_endian; /* The device could be accessed big or little | ||
9085 | + * endian | ||
9086 | + */ | ||
9087 | +}; | ||
9088 | + | ||
9089 | |||
9090 | /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) | ||
9091 | * Product Specification", DS464 | ||
9092 | */ | ||
9093 | -#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ | ||
9094 | +#define XSPI_CR_OFFSET 0x60 /* Control Register */ | ||
9095 | |||
9096 | #define XSPI_CR_ENABLE 0x02 | ||
9097 | #define XSPI_CR_MASTER_MODE 0x04 | ||
9098 | @@ -40,8 +53,9 @@ | ||
9099 | #define XSPI_CR_RXFIFO_RESET 0x40 | ||
9100 | #define XSPI_CR_MANUAL_SSELECT 0x80 | ||
9101 | #define XSPI_CR_TRANS_INHIBIT 0x100 | ||
9102 | +#define XSPI_CR_LSB_FIRST 0x200 | ||
9103 | |||
9104 | -#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ | ||
9105 | +#define XSPI_SR_OFFSET 0x64 /* Status Register */ | ||
9106 | |||
9107 | #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ | ||
9108 | #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ | ||
9109 | @@ -49,8 +63,8 @@ | ||
9110 | #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ | ||
9111 | #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ | ||
9112 | |||
9113 | -#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ | ||
9114 | -#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ | ||
9115 | +#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */ | ||
9116 | +#define XSPI_RXD_OFFSET 0x6C /* Data Receive Register */ | ||
9117 | |||
9118 | #define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ | ||
9119 | |||
9120 | @@ -70,43 +84,72 @@ | ||
9121 | #define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */ | ||
9122 | #define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */ | ||
9123 | #define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */ | ||
9124 | +#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */ | ||
9125 | |||
9126 | #define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ | ||
9127 | #define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ | ||
9128 | |||
9129 | -struct xilinx_spi { | ||
9130 | - /* bitbang has to be first */ | ||
9131 | - struct spi_bitbang bitbang; | ||
9132 | - struct completion done; | ||
9133 | +/* to follow are some functions that does little of big endian read and | ||
9134 | + * write depending on the config of the device. | ||
9135 | + */ | ||
9136 | +static inline void xspi_write8(struct xilinx_spi *xspi, u32 offs, u8 val) | ||
9137 | +{ | ||
9138 | + iowrite8(val, xspi->regs + offs + ((xspi->big_endian) ? 3 : 0)); | ||
9139 | +} | ||
9140 | |||
9141 | - void __iomem *regs; /* virt. address of the control registers */ | ||
9142 | +static inline void xspi_write16(struct xilinx_spi *xspi, u32 offs, u16 val) | ||
9143 | +{ | ||
9144 | + if (xspi->big_endian) | ||
9145 | + iowrite16be(val, xspi->regs + offs + 2); | ||
9146 | + else | ||
9147 | + iowrite16(val, xspi->regs + offs); | ||
9148 | +} | ||
9149 | |||
9150 | - u32 irq; | ||
9151 | +static inline void xspi_write32(struct xilinx_spi *xspi, u32 offs, u32 val) | ||
9152 | +{ | ||
9153 | + if (xspi->big_endian) | ||
9154 | + iowrite32be(val, xspi->regs + offs); | ||
9155 | + else | ||
9156 | + iowrite32(val, xspi->regs + offs); | ||
9157 | +} | ||
9158 | |||
9159 | - u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ | ||
9160 | +static inline u8 xspi_read8(struct xilinx_spi *xspi, u32 offs) | ||
9161 | +{ | ||
9162 | + return ioread8(xspi->regs + offs + ((xspi->big_endian) ? 3 : 0)); | ||
9163 | +} | ||
9164 | |||
9165 | - u8 *rx_ptr; /* pointer in the Tx buffer */ | ||
9166 | - const u8 *tx_ptr; /* pointer in the Rx buffer */ | ||
9167 | - int remaining_bytes; /* the number of bytes left to transfer */ | ||
9168 | -}; | ||
9169 | +static inline u16 xspi_read16(struct xilinx_spi *xspi, u32 offs) | ||
9170 | +{ | ||
9171 | + if (xspi->big_endian) | ||
9172 | + return ioread16be(xspi->regs + offs + 2); | ||
9173 | + else | ||
9174 | + return ioread16(xspi->regs + offs); | ||
9175 | +} | ||
9176 | + | ||
9177 | +static inline u32 xspi_read32(struct xilinx_spi *xspi, u32 offs) | ||
9178 | +{ | ||
9179 | + if (xspi->big_endian) | ||
9180 | + return ioread32be(xspi->regs + offs); | ||
9181 | + else | ||
9182 | + return ioread32(xspi->regs + offs); | ||
9183 | +} | ||
9184 | |||
9185 | -static void xspi_init_hw(void __iomem *regs_base) | ||
9186 | +static void xspi_init_hw(struct xilinx_spi *xspi) | ||
9187 | { | ||
9188 | /* Reset the SPI device */ | ||
9189 | - out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, | ||
9190 | - XIPIF_V123B_RESET_MASK); | ||
9191 | + xspi_write32(xspi, XIPIF_V123B_RESETR_OFFSET, XIPIF_V123B_RESET_MASK); | ||
9192 | /* Disable all the interrupts just in case */ | ||
9193 | - out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0); | ||
9194 | + xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET, 0); | ||
9195 | /* Enable the global IPIF interrupt */ | ||
9196 | - out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, | ||
9197 | - XIPIF_V123B_GINTR_ENABLE); | ||
9198 | + xspi_write32(xspi, XIPIF_V123B_DGIER_OFFSET, XIPIF_V123B_GINTR_ENABLE); | ||
9199 | /* Deselect the slave on the SPI bus */ | ||
9200 | - out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); | ||
9201 | + xspi_write32(xspi, XSPI_SSR_OFFSET, 0xffff); | ||
9202 | /* Disable the transmitter, enable Manual Slave Select Assertion, | ||
9203 | * put SPI controller into master mode, and enable it */ | ||
9204 | - out_be16(regs_base + XSPI_CR_OFFSET, | ||
9205 | - XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | ||
9206 | - | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); | ||
9207 | + xspi_write16(xspi, XSPI_CR_OFFSET, | ||
9208 | + XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | | ||
9209 | + XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | | ||
9210 | + XSPI_CR_RXFIFO_RESET); | ||
9211 | } | ||
9212 | |||
9213 | static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) | ||
9214 | @@ -115,16 +158,16 @@ | ||
9215 | |||
9216 | if (is_on == BITBANG_CS_INACTIVE) { | ||
9217 | /* Deselect the slave on the SPI bus */ | ||
9218 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); | ||
9219 | + xspi_write32(xspi, XSPI_SSR_OFFSET, 0xffff); | ||
9220 | } else if (is_on == BITBANG_CS_ACTIVE) { | ||
9221 | /* Set the SPI clock phase and polarity */ | ||
9222 | - u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) | ||
9223 | + u16 cr = xspi_read16(xspi, XSPI_CR_OFFSET) | ||
9224 | & ~XSPI_CR_MODE_MASK; | ||
9225 | if (spi->mode & SPI_CPHA) | ||
9226 | cr |= XSPI_CR_CPHA; | ||
9227 | if (spi->mode & SPI_CPOL) | ||
9228 | cr |= XSPI_CR_CPOL; | ||
9229 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
9230 | + xspi_write16(xspi, XSPI_CR_OFFSET, cr); | ||
9231 | |||
9232 | /* We do not check spi->max_speed_hz here as the SPI clock | ||
9233 | * frequency is not software programmable (the IP block design | ||
9234 | @@ -132,24 +175,27 @@ | ||
9235 | */ | ||
9236 | |||
9237 | /* Activate the chip select */ | ||
9238 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, | ||
9239 | + xspi_write32(xspi, XSPI_SSR_OFFSET, | ||
9240 | ~(0x0001 << spi->chip_select)); | ||
9241 | } | ||
9242 | } | ||
9243 | |||
9244 | /* spi_bitbang requires custom setup_transfer() to be defined if there is a | ||
9245 | * custom txrx_bufs(). We have nothing to setup here as the SPI IP block | ||
9246 | - * supports just 8 bits per word, and SPI clock can't be changed in software. | ||
9247 | - * Check for 8 bits per word. Chip select delay calculations could be | ||
9248 | + * supports 8 or 16 bits per word, which can not be changed in software. | ||
9249 | + * SPI clock can't be changed in software. | ||
9250 | + * Check for correct bits per word. Chip select delay calculations could be | ||
9251 | * added here as soon as bitbang_work() can be made aware of the delay value. | ||
9252 | */ | ||
9253 | static int xilinx_spi_setup_transfer(struct spi_device *spi, | ||
9254 | - struct spi_transfer *t) | ||
9255 | + struct spi_transfer *t) | ||
9256 | { | ||
9257 | u8 bits_per_word; | ||
9258 | + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); | ||
9259 | |||
9260 | - bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | ||
9261 | - if (bits_per_word != 8) { | ||
9262 | + bits_per_word = (t->bits_per_word) ? t->bits_per_word : | ||
9263 | + spi->bits_per_word; | ||
9264 | + if (bits_per_word != xspi->bits_per_word) { | ||
9265 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | ||
9266 | __func__, bits_per_word); | ||
9267 | return -EINVAL; | ||
9268 | @@ -160,34 +206,50 @@ | ||
9269 | |||
9270 | static int xilinx_spi_setup(struct spi_device *spi) | ||
9271 | { | ||
9272 | - struct spi_bitbang *bitbang; | ||
9273 | - struct xilinx_spi *xspi; | ||
9274 | - int retval; | ||
9275 | - | ||
9276 | - xspi = spi_master_get_devdata(spi->master); | ||
9277 | - bitbang = &xspi->bitbang; | ||
9278 | - | ||
9279 | - retval = xilinx_spi_setup_transfer(spi, NULL); | ||
9280 | - if (retval < 0) | ||
9281 | - return retval; | ||
9282 | - | ||
9283 | + /* always return 0, we can not check the number of bits. | ||
9284 | + * There are cases when SPI setup is called before any driver is | ||
9285 | + * there, in that case the SPI core defaults to 8 bits, which we | ||
9286 | + * do not support in some cases. But if we return an error, the | ||
9287 | + * SPI device would not be registered and no driver can get hold of it | ||
9288 | + * When the driver is there, it will call SPI setup again with the | ||
9289 | + * correct number of bits per transfer. | ||
9290 | + * If a driver setups with the wrong bit number, it will fail when | ||
9291 | + * it tries to do a transfer | ||
9292 | + */ | ||
9293 | return 0; | ||
9294 | } | ||
9295 | |||
9296 | static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) | ||
9297 | { | ||
9298 | u8 sr; | ||
9299 | + u8 wsize; | ||
9300 | + if (xspi->bits_per_word == 8) | ||
9301 | + wsize = 1; | ||
9302 | + else if (xspi->bits_per_word == 16) | ||
9303 | + wsize = 2; | ||
9304 | + else | ||
9305 | + wsize = 4; | ||
9306 | |||
9307 | /* Fill the Tx FIFO with as many bytes as possible */ | ||
9308 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
9309 | - while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { | ||
9310 | + sr = xspi_read8(xspi, XSPI_SR_OFFSET); | ||
9311 | + while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && | ||
9312 | + xspi->remaining_bytes > 0) { | ||
9313 | if (xspi->tx_ptr) { | ||
9314 | - out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); | ||
9315 | - } else { | ||
9316 | - out_8(xspi->regs + XSPI_TXD_OFFSET, 0); | ||
9317 | - } | ||
9318 | - xspi->remaining_bytes--; | ||
9319 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
9320 | + if (wsize == 1) | ||
9321 | + xspi_write8(xspi, XSPI_TXD_OFFSET, | ||
9322 | + *xspi->tx_ptr); | ||
9323 | + else if (wsize == 2) | ||
9324 | + xspi_write16(xspi, XSPI_TXD_OFFSET, | ||
9325 | + *(u16 *)(xspi->tx_ptr)); | ||
9326 | + else if (wsize == 4) | ||
9327 | + xspi_write32(xspi, XSPI_TXD_OFFSET, | ||
9328 | + *(u32 *)(xspi->tx_ptr)); | ||
9329 | + | ||
9330 | + xspi->tx_ptr += wsize; | ||
9331 | + } else | ||
9332 | + xspi_write8(xspi, XSPI_TXD_OFFSET, 0); | ||
9333 | + xspi->remaining_bytes -= wsize; | ||
9334 | + sr = xspi_read8(xspi, XSPI_SR_OFFSET); | ||
9335 | } | ||
9336 | } | ||
9337 | |||
9338 | @@ -209,23 +271,22 @@ | ||
9339 | /* Enable the transmit empty interrupt, which we use to determine | ||
9340 | * progress on the transmission. | ||
9341 | */ | ||
9342 | - ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET); | ||
9343 | - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, | ||
9344 | + ipif_ier = xspi_read32(xspi, XIPIF_V123B_IIER_OFFSET); | ||
9345 | + xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET, | ||
9346 | ipif_ier | XSPI_INTR_TX_EMPTY); | ||
9347 | |||
9348 | /* Start the transfer by not inhibiting the transmitter any longer */ | ||
9349 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; | ||
9350 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
9351 | + cr = xspi_read16(xspi, XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; | ||
9352 | + xspi_write16(xspi, XSPI_CR_OFFSET, cr); | ||
9353 | |||
9354 | wait_for_completion(&xspi->done); | ||
9355 | |||
9356 | /* Disable the transmit empty interrupt */ | ||
9357 | - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier); | ||
9358 | + xspi_write32(xspi, XIPIF_V123B_IIER_OFFSET, ipif_ier); | ||
9359 | |||
9360 | return t->len - xspi->remaining_bytes; | ||
9361 | } | ||
9362 | |||
9363 | - | ||
9364 | /* This driver supports single master mode only. Hence Tx FIFO Empty | ||
9365 | * is the only interrupt we care about. | ||
9366 | * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode | ||
9367 | @@ -237,32 +298,50 @@ | ||
9368 | u32 ipif_isr; | ||
9369 | |||
9370 | /* Get the IPIF interrupts, and clear them immediately */ | ||
9371 | - ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET); | ||
9372 | - out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr); | ||
9373 | + ipif_isr = xspi_read32(xspi, XIPIF_V123B_IISR_OFFSET); | ||
9374 | + xspi_write32(xspi, XIPIF_V123B_IISR_OFFSET, ipif_isr); | ||
9375 | |||
9376 | if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ | ||
9377 | u16 cr; | ||
9378 | u8 sr; | ||
9379 | + u8 rsize; | ||
9380 | + if (xspi->bits_per_word == 8) | ||
9381 | + rsize = 1; | ||
9382 | + else if (xspi->bits_per_word == 16) | ||
9383 | + rsize = 2; | ||
9384 | + else | ||
9385 | + rsize = 4; | ||
9386 | |||
9387 | /* A transmit has just completed. Process received data and | ||
9388 | * check for more data to transmit. Always inhibit the | ||
9389 | * transmitter while the Isr refills the transmit register/FIFO, | ||
9390 | * or make sure it is stopped if we're done. | ||
9391 | */ | ||
9392 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET); | ||
9393 | - out_be16(xspi->regs + XSPI_CR_OFFSET, | ||
9394 | - cr | XSPI_CR_TRANS_INHIBIT); | ||
9395 | + cr = xspi_read16(xspi, XSPI_CR_OFFSET); | ||
9396 | + xspi_write16(xspi, XSPI_CR_OFFSET, cr | XSPI_CR_TRANS_INHIBIT); | ||
9397 | |||
9398 | /* Read out all the data from the Rx FIFO */ | ||
9399 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
9400 | + sr = xspi_read8(xspi, XSPI_SR_OFFSET); | ||
9401 | while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { | ||
9402 | - u8 data; | ||
9403 | + u32 data; | ||
9404 | + if (rsize == 1) | ||
9405 | + data = xspi_read8(xspi, XSPI_RXD_OFFSET); | ||
9406 | + else if (rsize == 2) | ||
9407 | + data = xspi_read16(xspi, XSPI_RXD_OFFSET); | ||
9408 | + else | ||
9409 | + data = xspi_read32(xspi, XSPI_RXD_OFFSET); | ||
9410 | |||
9411 | - data = in_8(xspi->regs + XSPI_RXD_OFFSET); | ||
9412 | if (xspi->rx_ptr) { | ||
9413 | - *xspi->rx_ptr++ = data; | ||
9414 | + if (rsize == 1) | ||
9415 | + *xspi->rx_ptr = data & 0xff; | ||
9416 | + else if (rsize == 2) | ||
9417 | + *(u16 *)(xspi->rx_ptr) = data & 0xffff; | ||
9418 | + else | ||
9419 | + *((u32 *)(xspi->rx_ptr)) = data; | ||
9420 | + xspi->rx_ptr += rsize; | ||
9421 | } | ||
9422 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | ||
9423 | + | ||
9424 | + sr = xspi_read8(xspi, XSPI_SR_OFFSET); | ||
9425 | } | ||
9426 | |||
9427 | /* See if there is more data to send */ | ||
9428 | @@ -271,7 +350,7 @@ | ||
9429 | /* Start the transfer by not inhibiting the | ||
9430 | * transmitter any longer | ||
9431 | */ | ||
9432 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | ||
9433 | + xspi_write16(xspi, XSPI_CR_OFFSET, cr); | ||
9434 | } else { | ||
9435 | /* No more data to send. | ||
9436 | * Indicate the transfer is completed. | ||
9437 | @@ -279,44 +358,21 @@ | ||
9438 | complete(&xspi->done); | ||
9439 | } | ||
9440 | } | ||
9441 | - | ||
9442 | return IRQ_HANDLED; | ||
9443 | } | ||
9444 | |||
9445 | -static int __init xilinx_spi_of_probe(struct of_device *ofdev, | ||
9446 | - const struct of_device_id *match) | ||
9447 | +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, | ||
9448 | + u32 irq, s16 bus_num, u16 num_chipselect, u8 bits_per_word, | ||
9449 | + bool big_endian) | ||
9450 | { | ||
9451 | struct spi_master *master; | ||
9452 | struct xilinx_spi *xspi; | ||
9453 | - struct resource r_irq_struct; | ||
9454 | - struct resource r_mem_struct; | ||
9455 | + int ret = 0; | ||
9456 | |||
9457 | - struct resource *r_irq = &r_irq_struct; | ||
9458 | - struct resource *r_mem = &r_mem_struct; | ||
9459 | - int rc = 0; | ||
9460 | - const u32 *prop; | ||
9461 | - int len; | ||
9462 | + master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); | ||
9463 | |||
9464 | - /* Get resources(memory, IRQ) associated with the device */ | ||
9465 | - master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); | ||
9466 | - | ||
9467 | - if (master == NULL) { | ||
9468 | - return -ENOMEM; | ||
9469 | - } | ||
9470 | - | ||
9471 | - dev_set_drvdata(&ofdev->dev, master); | ||
9472 | - | ||
9473 | - rc = of_address_to_resource(ofdev->node, 0, r_mem); | ||
9474 | - if (rc) { | ||
9475 | - dev_warn(&ofdev->dev, "invalid address\n"); | ||
9476 | - goto put_master; | ||
9477 | - } | ||
9478 | - | ||
9479 | - rc = of_irq_to_resource(ofdev->node, 0, r_irq); | ||
9480 | - if (rc == NO_IRQ) { | ||
9481 | - dev_warn(&ofdev->dev, "no IRQ found\n"); | ||
9482 | - goto put_master; | ||
9483 | - } | ||
9484 | + if (master == NULL) | ||
9485 | + return ERR_PTR(-ENOMEM); | ||
9486 | |||
9487 | /* the spi->mode bits understood by this driver: */ | ||
9488 | master->mode_bits = SPI_CPOL | SPI_CPHA; | ||
9489 | @@ -329,128 +385,73 @@ | ||
9490 | xspi->bitbang.master->setup = xilinx_spi_setup; | ||
9491 | init_completion(&xspi->done); | ||
9492 | |||
9493 | - xspi->irq = r_irq->start; | ||
9494 | - | ||
9495 | - if (!request_mem_region(r_mem->start, | ||
9496 | - r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { | ||
9497 | - rc = -ENXIO; | ||
9498 | - dev_warn(&ofdev->dev, "memory request failure\n"); | ||
9499 | + if (!request_mem_region(mem->start, resource_size(mem), | ||
9500 | + XILINX_SPI_NAME)) { | ||
9501 | + ret = -ENXIO; | ||
9502 | goto put_master; | ||
9503 | } | ||
9504 | |||
9505 | - xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); | ||
9506 | + xspi->regs = ioremap(mem->start, resource_size(mem)); | ||
9507 | if (xspi->regs == NULL) { | ||
9508 | - rc = -ENOMEM; | ||
9509 | - dev_warn(&ofdev->dev, "ioremap failure\n"); | ||
9510 | - goto release_mem; | ||
9511 | + ret = -ENOMEM; | ||
9512 | + dev_warn(dev, "ioremap failure\n"); | ||
9513 | + goto map_failed; | ||
9514 | } | ||
9515 | - xspi->irq = r_irq->start; | ||
9516 | |||
9517 | - /* dynamic bus assignment */ | ||
9518 | - master->bus_num = -1; | ||
9519 | - | ||
9520 | - /* number of slave select bits is required */ | ||
9521 | - prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | ||
9522 | - if (!prop || len < sizeof(*prop)) { | ||
9523 | - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | ||
9524 | - goto unmap_io; | ||
9525 | - } | ||
9526 | - master->num_chipselect = *prop; | ||
9527 | + master->bus_num = bus_num; | ||
9528 | + master->num_chipselect = num_chipselect; | ||
9529 | + | ||
9530 | + xspi->mem = *mem; | ||
9531 | + xspi->irq = irq; | ||
9532 | + xspi->bits_per_word = bits_per_word; | ||
9533 | + xspi->big_endian = big_endian; | ||
9534 | |||
9535 | /* SPI controller initializations */ | ||
9536 | - xspi_init_hw(xspi->regs); | ||
9537 | + xspi_init_hw(xspi); | ||
9538 | |||
9539 | /* Register for SPI Interrupt */ | ||
9540 | - rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | ||
9541 | - if (rc != 0) { | ||
9542 | - dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); | ||
9543 | + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | ||
9544 | + if (ret != 0) | ||
9545 | goto unmap_io; | ||
9546 | - } | ||
9547 | |||
9548 | - rc = spi_bitbang_start(&xspi->bitbang); | ||
9549 | - if (rc != 0) { | ||
9550 | - dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); | ||
9551 | + ret = spi_bitbang_start(&xspi->bitbang); | ||
9552 | + if (ret != 0) { | ||
9553 | + dev_err(dev, "spi_bitbang_start FAILED\n"); | ||
9554 | goto free_irq; | ||
9555 | } | ||
9556 | |||
9557 | - dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | ||
9558 | - (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); | ||
9559 | - | ||
9560 | - /* Add any subnodes on the SPI bus */ | ||
9561 | - of_register_spi_devices(master, ofdev->node); | ||
9562 | - | ||
9563 | - return rc; | ||
9564 | + dev_info(dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | ||
9565 | + (u32)mem->start, (u32)xspi->regs, xspi->irq); | ||
9566 | + return master; | ||
9567 | |||
9568 | free_irq: | ||
9569 | free_irq(xspi->irq, xspi); | ||
9570 | unmap_io: | ||
9571 | iounmap(xspi->regs); | ||
9572 | -release_mem: | ||
9573 | - release_mem_region(r_mem->start, resource_size(r_mem)); | ||
9574 | +map_failed: | ||
9575 | + release_mem_region(mem->start, resource_size(mem)); | ||
9576 | put_master: | ||
9577 | spi_master_put(master); | ||
9578 | - return rc; | ||
9579 | + return ERR_PTR(ret); | ||
9580 | } | ||
9581 | +EXPORT_SYMBOL(xilinx_spi_init); | ||
9582 | |||
9583 | -static int __devexit xilinx_spi_remove(struct of_device *ofdev) | ||
9584 | +void xilinx_spi_deinit(struct spi_master *master) | ||
9585 | { | ||
9586 | struct xilinx_spi *xspi; | ||
9587 | - struct spi_master *master; | ||
9588 | - struct resource r_mem; | ||
9589 | |||
9590 | - master = platform_get_drvdata(ofdev); | ||
9591 | xspi = spi_master_get_devdata(master); | ||
9592 | |||
9593 | spi_bitbang_stop(&xspi->bitbang); | ||
9594 | free_irq(xspi->irq, xspi); | ||
9595 | iounmap(xspi->regs); | ||
9596 | - if (!of_address_to_resource(ofdev->node, 0, &r_mem)) | ||
9597 | - release_mem_region(r_mem.start, resource_size(&r_mem)); | ||
9598 | - dev_set_drvdata(&ofdev->dev, 0); | ||
9599 | - spi_master_put(xspi->bitbang.master); | ||
9600 | - | ||
9601 | - return 0; | ||
9602 | -} | ||
9603 | - | ||
9604 | -/* work with hotplug and coldplug */ | ||
9605 | -MODULE_ALIAS("platform:" XILINX_SPI_NAME); | ||
9606 | - | ||
9607 | -static int __exit xilinx_spi_of_remove(struct of_device *op) | ||
9608 | -{ | ||
9609 | - return xilinx_spi_remove(op); | ||
9610 | -} | ||
9611 | |||
9612 | -static struct of_device_id xilinx_spi_of_match[] = { | ||
9613 | - { .compatible = "xlnx,xps-spi-2.00.a", }, | ||
9614 | - { .compatible = "xlnx,xps-spi-2.00.b", }, | ||
9615 | - {} | ||
9616 | -}; | ||
9617 | - | ||
9618 | -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | ||
9619 | - | ||
9620 | -static struct of_platform_driver xilinx_spi_of_driver = { | ||
9621 | - .owner = THIS_MODULE, | ||
9622 | - .name = "xilinx-xps-spi", | ||
9623 | - .match_table = xilinx_spi_of_match, | ||
9624 | - .probe = xilinx_spi_of_probe, | ||
9625 | - .remove = __exit_p(xilinx_spi_of_remove), | ||
9626 | - .driver = { | ||
9627 | - .name = "xilinx-xps-spi", | ||
9628 | - .owner = THIS_MODULE, | ||
9629 | - }, | ||
9630 | -}; | ||
9631 | - | ||
9632 | -static int __init xilinx_spi_init(void) | ||
9633 | -{ | ||
9634 | - return of_register_platform_driver(&xilinx_spi_of_driver); | ||
9635 | + release_mem_region(xspi->mem.start, resource_size(&xspi->mem)); | ||
9636 | + spi_master_put(xspi->bitbang.master); | ||
9637 | } | ||
9638 | -module_init(xilinx_spi_init); | ||
9639 | +EXPORT_SYMBOL(xilinx_spi_deinit); | ||
9640 | |||
9641 | -static void __exit xilinx_spi_exit(void) | ||
9642 | -{ | ||
9643 | - of_unregister_platform_driver(&xilinx_spi_of_driver); | ||
9644 | -} | ||
9645 | -module_exit(xilinx_spi_exit); | ||
9646 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | ||
9647 | MODULE_DESCRIPTION("Xilinx SPI driver"); | ||
9648 | MODULE_LICENSE("GPL"); | ||
9649 | + | ||
9650 | diff -uNr linux-2.6.31/drivers/spi/xilinx_spi.h linux-2.6.31.new/drivers/spi/xilinx_spi.h | ||
9651 | --- linux-2.6.31/drivers/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 | ||
9652 | +++ linux-2.6.31.new/drivers/spi/xilinx_spi.h 2009-10-23 11:17:32.000000000 -0700 | ||
9653 | @@ -0,0 +1,33 @@ | ||
9654 | +/* | ||
9655 | + * xilinx_spi.h | ||
9656 | + * Copyright (c) 2009 Intel Corporation | ||
9657 | + * | ||
9658 | + * This program is free software; you can redistribute it and/or modify | ||
9659 | + * it under the terms of the GNU General Public License version 2 as | ||
9660 | + * published by the Free Software Foundation. | ||
9661 | + * | ||
9662 | + * This program is distributed in the hope that it will be useful, | ||
9663 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9664 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9665 | + * GNU General Public License for more details. | ||
9666 | + * | ||
9667 | + * You should have received a copy of the GNU General Public License | ||
9668 | + * along with this program; if not, write to the Free Software | ||
9669 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
9670 | + */ | ||
9671 | + | ||
9672 | +#ifndef _XILINX_SPI_H_ | ||
9673 | +#define _XILINX_SPI_H_ 1 | ||
9674 | + | ||
9675 | +#include <linux/spi/spi.h> | ||
9676 | +#include <linux/spi/spi_bitbang.h> | ||
9677 | +#include <linux/spi/xilinx_spi.h> | ||
9678 | + | ||
9679 | +#define XILINX_SPI_NAME "xilinx_spi" | ||
9680 | + | ||
9681 | +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, | ||
9682 | + u32 irq, s16 bus_num, u16 num_chipselect, u8 bits_per_word, | ||
9683 | + bool big_endian); | ||
9684 | + | ||
9685 | +void xilinx_spi_deinit(struct spi_master *master); | ||
9686 | +#endif | ||
9687 | diff -uNr linux-2.6.31/drivers/spi/xilinx_spi_of.c linux-2.6.31.new/drivers/spi/xilinx_spi_of.c | ||
9688 | --- linux-2.6.31/drivers/spi/xilinx_spi_of.c 1969-12-31 16:00:00.000000000 -0800 | ||
9689 | +++ linux-2.6.31.new/drivers/spi/xilinx_spi_of.c 2009-10-23 11:17:32.000000000 -0700 | ||
9690 | @@ -0,0 +1,120 @@ | ||
9691 | +/* | ||
9692 | + * xilinx_spi_of.c | ||
9693 | + * | ||
9694 | + * Xilinx SPI controller driver (master mode only) | ||
9695 | + * | ||
9696 | + * Author: MontaVista Software, Inc. | ||
9697 | + * source@mvista.com | ||
9698 | + * | ||
9699 | + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the | ||
9700 | + * terms of the GNU General Public License version 2. This program is licensed | ||
9701 | + * "as is" without any warranty of any kind, whether express or implied. | ||
9702 | + */ | ||
9703 | + | ||
9704 | +#include <linux/module.h> | ||
9705 | +#include <linux/init.h> | ||
9706 | +#include <linux/interrupt.h> | ||
9707 | +#include <linux/io.h> | ||
9708 | +#include <linux/platform_device.h> | ||
9709 | + | ||
9710 | +#include <linux/of_platform.h> | ||
9711 | +#include <linux/of_device.h> | ||
9712 | +#include <linux/of_spi.h> | ||
9713 | + | ||
9714 | +#include <linux/spi/spi.h> | ||
9715 | +#include <linux/spi/spi_bitbang.h> | ||
9716 | + | ||
9717 | +#include "xilinx_spi.h" | ||
9718 | + | ||
9719 | + | ||
9720 | +static int __init xilinx_spi_of_probe(struct of_device *ofdev, | ||
9721 | + const struct of_device_id *match) | ||
9722 | +{ | ||
9723 | + struct resource r_irq_struct; | ||
9724 | + struct resource r_mem_struct; | ||
9725 | + struct spi_master *master; | ||
9726 | + | ||
9727 | + struct resource *r_irq = &r_irq_struct; | ||
9728 | + struct resource *r_mem = &r_mem_struct; | ||
9729 | + int rc = 0; | ||
9730 | + const u32 *prop; | ||
9731 | + int len; | ||
9732 | + | ||
9733 | + rc = of_address_to_resource(ofdev->node, 0, r_mem); | ||
9734 | + if (rc) { | ||
9735 | + dev_warn(&ofdev->dev, "invalid address\n"); | ||
9736 | + return rc; | ||
9737 | + } | ||
9738 | + | ||
9739 | + rc = of_irq_to_resource(ofdev->node, 0, r_irq); | ||
9740 | + if (rc == NO_IRQ) { | ||
9741 | + dev_warn(&ofdev->dev, "no IRQ found\n"); | ||
9742 | + return -ENODEV; | ||
9743 | + } | ||
9744 | + | ||
9745 | + /* number of slave select bits is required */ | ||
9746 | + prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | ||
9747 | + if (!prop || len < sizeof(*prop)) { | ||
9748 | + dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | ||
9749 | + return -EINVAL; | ||
9750 | + } | ||
9751 | + master = xilinx_spi_init(&ofdev->dev, r_mem, r_irq->start, -1, *prop, 8, | ||
9752 | + true); | ||
9753 | + if (IS_ERR(master)) | ||
9754 | + return PTR_ERR(master); | ||
9755 | + | ||
9756 | + dev_set_drvdata(&ofdev->dev, master); | ||
9757 | + | ||
9758 | + /* Add any subnodes on the SPI bus */ | ||
9759 | + of_register_spi_devices(master, ofdev->node); | ||
9760 | + | ||
9761 | + return 0; | ||
9762 | +} | ||
9763 | + | ||
9764 | +static int __devexit xilinx_spi_remove(struct of_device *ofdev) | ||
9765 | +{ | ||
9766 | + xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); | ||
9767 | + dev_set_drvdata(&ofdev->dev, 0); | ||
9768 | + return 0; | ||
9769 | +} | ||
9770 | + | ||
9771 | +static int __exit xilinx_spi_of_remove(struct of_device *op) | ||
9772 | +{ | ||
9773 | + return xilinx_spi_remove(op); | ||
9774 | +} | ||
9775 | + | ||
9776 | +static struct of_device_id xilinx_spi_of_match[] = { | ||
9777 | + { .compatible = "xlnx,xps-spi-2.00.a", }, | ||
9778 | + { .compatible = "xlnx,xps-spi-2.00.b", }, | ||
9779 | + {} | ||
9780 | +}; | ||
9781 | + | ||
9782 | +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | ||
9783 | + | ||
9784 | +static struct of_platform_driver xilinx_spi_of_driver = { | ||
9785 | + .owner = THIS_MODULE, | ||
9786 | + .name = "xilinx-xps-spi", | ||
9787 | + .match_table = xilinx_spi_of_match, | ||
9788 | + .probe = xilinx_spi_of_probe, | ||
9789 | + .remove = __exit_p(xilinx_spi_of_remove), | ||
9790 | + .driver = { | ||
9791 | + .name = "xilinx-xps-spi", | ||
9792 | + .owner = THIS_MODULE, | ||
9793 | + }, | ||
9794 | +}; | ||
9795 | + | ||
9796 | +static int __init xilinx_spi_of_init(void) | ||
9797 | +{ | ||
9798 | + return of_register_platform_driver(&xilinx_spi_of_driver); | ||
9799 | +} | ||
9800 | +module_init(xilinx_spi_of_init); | ||
9801 | + | ||
9802 | +static void __exit xilinx_spi_of_exit(void) | ||
9803 | +{ | ||
9804 | + of_unregister_platform_driver(&xilinx_spi_of_driver); | ||
9805 | +} | ||
9806 | +module_exit(xilinx_spi_of_exit); | ||
9807 | +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | ||
9808 | +MODULE_DESCRIPTION("Xilinx SPI driver"); | ||
9809 | +MODULE_LICENSE("GPL"); | ||
9810 | + | ||
9811 | diff -uNr linux-2.6.31/drivers/spi/xilinx_spi_pltfm.c linux-2.6.31.new/drivers/spi/xilinx_spi_pltfm.c | ||
9812 | --- linux-2.6.31/drivers/spi/xilinx_spi_pltfm.c 1969-12-31 16:00:00.000000000 -0800 | ||
9813 | +++ linux-2.6.31.new/drivers/spi/xilinx_spi_pltfm.c 2009-10-23 11:17:32.000000000 -0700 | ||
9814 | @@ -0,0 +1,104 @@ | ||
9815 | +/* | ||
9816 | + * xilinx_spi_pltfm.c Support for Xilinx SPI platform devices | ||
9817 | + * Copyright (c) 2009 Intel Corporation | ||
9818 | + * | ||
9819 | + * This program is free software; you can redistribute it and/or modify | ||
9820 | + * it under the terms of the GNU General Public License version 2 as | ||
9821 | + * published by the Free Software Foundation. | ||
9822 | + * | ||
9823 | + * This program is distributed in the hope that it will be useful, | ||
9824 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9825 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9826 | + * GNU General Public License for more details. | ||
9827 | + * | ||
9828 | + * You should have received a copy of the GNU General Public License | ||
9829 | + * along with this program; if not, write to the Free Software | ||
9830 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
9831 | + */ | ||
9832 | + | ||
9833 | +/* Supports: | ||
9834 | + * Xilinx SPI devices as platform devices | ||
9835 | + * | ||
9836 | + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. | ||
9837 | + */ | ||
9838 | + | ||
9839 | +#include <linux/module.h> | ||
9840 | +#include <linux/init.h> | ||
9841 | +#include <linux/interrupt.h> | ||
9842 | +#include <linux/io.h> | ||
9843 | +#include <linux/platform_device.h> | ||
9844 | + | ||
9845 | +#include <linux/spi/spi.h> | ||
9846 | +#include <linux/spi/spi_bitbang.h> | ||
9847 | +#include <linux/spi/xilinx_spi.h> | ||
9848 | + | ||
9849 | +#include "xilinx_spi.h" | ||
9850 | + | ||
9851 | +static int __devinit xilinx_spi_probe(struct platform_device *dev) | ||
9852 | +{ | ||
9853 | + struct xspi_platform_data *pdata; | ||
9854 | + struct resource *r; | ||
9855 | + int irq; | ||
9856 | + struct spi_master *master; | ||
9857 | + u8 i; | ||
9858 | + | ||
9859 | + pdata = dev->dev.platform_data; | ||
9860 | + if (pdata == NULL) | ||
9861 | + return -ENODEV; | ||
9862 | + | ||
9863 | + r = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
9864 | + if (r == NULL) | ||
9865 | + return -ENODEV; | ||
9866 | + | ||
9867 | + irq = platform_get_irq(dev, 0); | ||
9868 | + if (irq < 0) | ||
9869 | + return -ENXIO; | ||
9870 | + | ||
9871 | + master = xilinx_spi_init(&dev->dev, r, irq, dev->id, | ||
9872 | + pdata->num_chipselect, pdata->bits_per_word, false); | ||
9873 | + if (IS_ERR(master)) | ||
9874 | + return PTR_ERR(master); | ||
9875 | + | ||
9876 | + for (i = 0; i < pdata->num_devices; i++) | ||
9877 | + spi_new_device(master, pdata->devices + i); | ||
9878 | + | ||
9879 | + platform_set_drvdata(dev, master); | ||
9880 | + return 0; | ||
9881 | +} | ||
9882 | + | ||
9883 | +static int __devexit xilinx_spi_remove(struct platform_device *dev) | ||
9884 | +{ | ||
9885 | + xilinx_spi_deinit(platform_get_drvdata(dev)); | ||
9886 | + platform_set_drvdata(dev, 0); | ||
9887 | + | ||
9888 | + return 0; | ||
9889 | +} | ||
9890 | + | ||
9891 | +/* work with hotplug and coldplug */ | ||
9892 | +MODULE_ALIAS("platform:" XILINX_SPI_NAME); | ||
9893 | + | ||
9894 | +static struct platform_driver xilinx_spi_driver = { | ||
9895 | + .probe = xilinx_spi_probe, | ||
9896 | + .remove = __devexit_p(xilinx_spi_remove), | ||
9897 | + .driver = { | ||
9898 | + .name = XILINX_SPI_NAME, | ||
9899 | + .owner = THIS_MODULE, | ||
9900 | + }, | ||
9901 | +}; | ||
9902 | + | ||
9903 | +static int __init xilinx_spi_pltfm_init(void) | ||
9904 | +{ | ||
9905 | + return platform_driver_register(&xilinx_spi_driver); | ||
9906 | +} | ||
9907 | +module_init(xilinx_spi_pltfm_init); | ||
9908 | + | ||
9909 | +static void __exit xilinx_spi_pltfm_exit(void) | ||
9910 | +{ | ||
9911 | + platform_driver_unregister(&xilinx_spi_driver); | ||
9912 | +} | ||
9913 | +module_exit(xilinx_spi_pltfm_exit); | ||
9914 | + | ||
9915 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
9916 | +MODULE_DESCRIPTION("Xilinx SPI platform driver"); | ||
9917 | +MODULE_LICENSE("GPL v2"); | ||
9918 | + | ||
9919 | diff -uNr linux-2.6.31/include/linux/can/platform/ascb.h linux-2.6.31.new/include/linux/can/platform/ascb.h | ||
9920 | --- linux-2.6.31/include/linux/can/platform/ascb.h 1969-12-31 16:00:00.000000000 -0800 | ||
9921 | +++ linux-2.6.31.new/include/linux/can/platform/ascb.h 2009-10-23 11:16:56.000000000 -0700 | ||
9922 | @@ -0,0 +1,8 @@ | ||
9923 | +#ifndef _CAN_PLATFORM_ASCB_H_ | ||
9924 | +#define _CAN_PLATFORM_ASCB_H_ | ||
9925 | + | ||
9926 | +struct ascb_platform_data { | ||
9927 | + int gpio_pin; | ||
9928 | +}; | ||
9929 | + | ||
9930 | +#endif | ||
9931 | diff -uNr linux-2.6.31/include/linux/i2c-xiic.h linux-2.6.31.new/include/linux/i2c-xiic.h | ||
9932 | --- linux-2.6.31/include/linux/i2c-xiic.h 1969-12-31 16:00:00.000000000 -0800 | ||
9933 | +++ linux-2.6.31.new/include/linux/i2c-xiic.h 2009-10-23 11:16:56.000000000 -0700 | ||
9934 | @@ -0,0 +1,31 @@ | ||
9935 | +/* | ||
9936 | + * i2c-xiic.h | ||
9937 | + * Copyright (c) 2009 Intel Corporation | ||
9938 | + * | ||
9939 | + * This program is free software; you can redistribute it and/or modify | ||
9940 | + * it under the terms of the GNU General Public License version 2 as | ||
9941 | + * published by the Free Software Foundation. | ||
9942 | + * | ||
9943 | + * This program is distributed in the hope that it will be useful, | ||
9944 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9945 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9946 | + * GNU General Public License for more details. | ||
9947 | + * | ||
9948 | + * You should have received a copy of the GNU General Public License | ||
9949 | + * along with this program; if not, write to the Free Software | ||
9950 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
9951 | + */ | ||
9952 | + | ||
9953 | +/* Supports: | ||
9954 | + * Xilinx IIC | ||
9955 | + */ | ||
9956 | + | ||
9957 | +#ifndef _LINUX_I2C_XIIC_H | ||
9958 | +#define _LINUX_I2C_XIIC_H | ||
9959 | + | ||
9960 | +struct xiic_i2c_platform_data { | ||
9961 | + u8 num_devices; /* number of devices in the devices list */ | ||
9962 | + struct i2c_board_info const *devices; /* devices connected to the bus */ | ||
9963 | +}; | ||
9964 | + | ||
9965 | +#endif /* _LINUX_I2C_XIIC_H */ | ||
9966 | diff -uNr linux-2.6.31/include/linux/mfd/timbdma.h linux-2.6.31.new/include/linux/mfd/timbdma.h | ||
9967 | --- linux-2.6.31/include/linux/mfd/timbdma.h 1969-12-31 16:00:00.000000000 -0800 | ||
9968 | +++ linux-2.6.31.new/include/linux/mfd/timbdma.h 2009-10-23 11:16:56.000000000 -0700 | ||
9969 | @@ -0,0 +1,58 @@ | ||
9970 | +/* | ||
9971 | + * timbdma.h timberdale FPGA DMA driver defines | ||
9972 | + * Copyright (c) 2009 Intel Corporation | ||
9973 | + * | ||
9974 | + * This program is free software; you can redistribute it and/or modify | ||
9975 | + * it under the terms of the GNU General Public License version 2 as | ||
9976 | + * published by the Free Software Foundation. | ||
9977 | + * | ||
9978 | + * This program is distributed in the hope that it will be useful, | ||
9979 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9980 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9981 | + * GNU General Public License for more details. | ||
9982 | + * | ||
9983 | + * You should have received a copy of the GNU General Public License | ||
9984 | + * along with this program; if not, write to the Free Software | ||
9985 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
9986 | + */ | ||
9987 | + | ||
9988 | +/* Supports: | ||
9989 | + * Timberdale FPGA DMA engine | ||
9990 | + */ | ||
9991 | + | ||
9992 | +#ifndef _TIMBDMA_H | ||
9993 | +#define _TIMBDMA_H | ||
9994 | + | ||
9995 | +#include <linux/spinlock.h> | ||
9996 | + | ||
9997 | + | ||
9998 | +#define DMA_IRQ_UART_RX 0x001 | ||
9999 | +#define DMA_IRQ_UART_TX 0x002 | ||
10000 | +#define DMA_IRQ_MLB_RX 0x004 | ||
10001 | +#define DMA_IRQ_MLB_TX 0x008 | ||
10002 | +#define DMA_IRQ_VIDEO_RX 0x010 | ||
10003 | +#define DMA_IRQ_VIDEO_DROP 0x020 | ||
10004 | +#define DMA_IRQ_SDHCI_RX 0x040 | ||
10005 | +#define DMA_IRQ_SDHCI_TX 0x080 | ||
10006 | +#define DMA_IRQ_ETH_RX 0x100 | ||
10007 | +#define DMA_IRQ_ETH_TX 0x200 | ||
10008 | +#define DMA_IRQS 10 | ||
10009 | + | ||
10010 | + | ||
10011 | +typedef int (*timbdma_interruptcb)(u32 flag, void *data); | ||
10012 | + | ||
10013 | + | ||
10014 | +int timbdma_start(u32 flag, void *desc, int bytes_per_row); | ||
10015 | + | ||
10016 | +int timbdma_stop(u32 flags); | ||
10017 | + | ||
10018 | +void timbdma_set_interruptcb(u32 flags, timbdma_interruptcb icb, void *data); | ||
10019 | + | ||
10020 | +void *timbdma_alloc_desc(u32 size, u16 alignment); | ||
10021 | + | ||
10022 | +void timbdma_free_desc(void *desc); | ||
10023 | + | ||
10024 | +int timbdma_prep_desc(void *desc, dma_addr_t buf, u32 size); | ||
10025 | + | ||
10026 | +#endif /* _TIMBDMA_H */ | ||
10027 | + | ||
10028 | diff -uNr linux-2.6.31/include/linux/most/timbmlb.h linux-2.6.31.new/include/linux/most/timbmlb.h | ||
10029 | --- linux-2.6.31/include/linux/most/timbmlb.h 1969-12-31 16:00:00.000000000 -0800 | ||
10030 | +++ linux-2.6.31.new/include/linux/most/timbmlb.h 2009-10-23 11:16:56.000000000 -0700 | ||
10031 | @@ -0,0 +1,9 @@ | ||
10032 | +#ifndef __LINUX_MOST_TIMBMLB_H | ||
10033 | +#define __LINUX_MOST_TIMBMLB_H | ||
10034 | + | ||
10035 | +/* Timberdale MLB IP */ | ||
10036 | +struct timbmlb_platform_data { | ||
10037 | + int reset_pin; /* pin used for reset of the INIC */ | ||
10038 | +}; | ||
10039 | + | ||
10040 | +#endif | ||
10041 | diff -uNr linux-2.6.31/include/linux/socket.h linux-2.6.31.new/include/linux/socket.h | ||
10042 | --- linux-2.6.31/include/linux/socket.h 2009-10-23 11:18:30.000000000 -0700 | ||
10043 | +++ linux-2.6.31.new/include/linux/socket.h 2009-10-23 11:16:56.000000000 -0700 | ||
10044 | @@ -195,7 +195,8 @@ | ||
10045 | #define AF_ISDN 34 /* mISDN sockets */ | ||
10046 | #define AF_PHONET 35 /* Phonet sockets */ | ||
10047 | #define AF_IEEE802154 36 /* IEEE802154 sockets */ | ||
10048 | -#define AF_MAX 37 /* For now.. */ | ||
10049 | +#define AF_MOST 37 /* Media Oriented Systems Transport */ | ||
10050 | +#define AF_MAX 38 /* For now.. */ | ||
10051 | |||
10052 | /* Protocol families, same as address families. */ | ||
10053 | #define PF_UNSPEC AF_UNSPEC | ||
10054 | @@ -235,6 +236,7 @@ | ||
10055 | #define PF_ISDN AF_ISDN | ||
10056 | #define PF_PHONET AF_PHONET | ||
10057 | #define PF_IEEE802154 AF_IEEE802154 | ||
10058 | +#define PF_MOST AF_MOST | ||
10059 | #define PF_MAX AF_MAX | ||
10060 | |||
10061 | /* Maximum queue length specifiable by listen. */ | ||
10062 | diff -uNr linux-2.6.31/include/linux/spi/mc33880.h linux-2.6.31.new/include/linux/spi/mc33880.h | ||
10063 | --- linux-2.6.31/include/linux/spi/mc33880.h 1969-12-31 16:00:00.000000000 -0800 | ||
10064 | +++ linux-2.6.31.new/include/linux/spi/mc33880.h 2009-10-23 11:16:56.000000000 -0700 | ||
10065 | @@ -0,0 +1,10 @@ | ||
10066 | +#ifndef LINUX_SPI_MC33880_H | ||
10067 | +#define LINUX_SPI_MC33880_H | ||
10068 | + | ||
10069 | +struct mc33880_platform_data { | ||
10070 | + /* number assigned to the first GPIO */ | ||
10071 | + unsigned base; | ||
10072 | +}; | ||
10073 | + | ||
10074 | +#endif | ||
10075 | + | ||
10076 | diff -uNr linux-2.6.31/include/linux/spi/xilinx_spi.h linux-2.6.31.new/include/linux/spi/xilinx_spi.h | ||
10077 | --- linux-2.6.31/include/linux/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 | ||
10078 | +++ linux-2.6.31.new/include/linux/spi/xilinx_spi.h 2009-10-23 11:16:56.000000000 -0700 | ||
10079 | @@ -0,0 +1,19 @@ | ||
10080 | +#ifndef __LINUX_SPI_XILINX_SPI_H | ||
10081 | +#define __LINUX_SPI_XILINX_SPI_H | ||
10082 | + | ||
10083 | +/** | ||
10084 | + * struct xspi_platform_data - Platform data of the Xilinx SPI driver | ||
10085 | + * @num_chipselect: Number of chip select by the IP | ||
10086 | + * @bits_per_word: Number of bits per word. 8/16/32, Note that the DS464 | ||
10087 | + * only support 8bit SPI. | ||
10088 | + * @devices: Devices to add when the driver is probed. | ||
10089 | + * @num_devices: Number of devices in the devices array. | ||
10090 | + */ | ||
10091 | +struct xspi_platform_data { | ||
10092 | + u16 num_chipselect; | ||
10093 | + u8 bits_per_word; | ||
10094 | + struct spi_board_info *devices; | ||
10095 | + u8 num_devices; | ||
10096 | +}; | ||
10097 | + | ||
10098 | +#endif /* __LINUX_SPI_XILINX_SPI_H */ | ||
10099 | diff -uNr linux-2.6.31/include/linux/timb_gpio.h linux-2.6.31.new/include/linux/timb_gpio.h | ||
10100 | --- linux-2.6.31/include/linux/timb_gpio.h 1969-12-31 16:00:00.000000000 -0800 | ||
10101 | +++ linux-2.6.31.new/include/linux/timb_gpio.h 2009-10-23 11:16:56.000000000 -0700 | ||
10102 | @@ -0,0 +1,28 @@ | ||
10103 | +/* | ||
10104 | + * timb_gpio.h timberdale FPGA GPIO driver, platform data definition | ||
10105 | + * Copyright (c) 2009 Intel Corporation | ||
10106 | + * | ||
10107 | + * This program is free software; you can redistribute it and/or modify | ||
10108 | + * it under the terms of the GNU General Public License version 2 as | ||
10109 | + * published by the Free Software Foundation. | ||
10110 | + * | ||
10111 | + * This program is distributed in the hope that it will be useful, | ||
10112 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10113 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10114 | + * GNU General Public License for more details. | ||
10115 | + * | ||
10116 | + * You should have received a copy of the GNU General Public License | ||
10117 | + * along with this program; if not, write to the Free Software | ||
10118 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10119 | + */ | ||
10120 | + | ||
10121 | +#ifndef _LINUX_TIMB_GPIO_H | ||
10122 | +#define _LINUX_TIMB_GPIO_H | ||
10123 | + | ||
10124 | +struct timbgpio_platform_data { | ||
10125 | + int gpio_base; | ||
10126 | + int nr_pins; | ||
10127 | + int irq_base; | ||
10128 | +}; | ||
10129 | + | ||
10130 | +#endif | ||
10131 | diff -uNr linux-2.6.31/include/media/timb_radio.h linux-2.6.31.new/include/media/timb_radio.h | ||
10132 | --- linux-2.6.31/include/media/timb_radio.h 1969-12-31 16:00:00.000000000 -0800 | ||
10133 | +++ linux-2.6.31.new/include/media/timb_radio.h 2009-10-23 11:16:55.000000000 -0700 | ||
10134 | @@ -0,0 +1,31 @@ | ||
10135 | +/* | ||
10136 | + * timb_radio.h Platform struct for the Timberdale radio driver | ||
10137 | + * Copyright (c) 2009 Intel Corporation | ||
10138 | + * | ||
10139 | + * This program is free software; you can redistribute it and/or modify | ||
10140 | + * it under the terms of the GNU General Public License version 2 as | ||
10141 | + * published by the Free Software Foundation. | ||
10142 | + * | ||
10143 | + * This program is distributed in the hope that it will be useful, | ||
10144 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10145 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10146 | + * GNU General Public License for more details. | ||
10147 | + * | ||
10148 | + * You should have received a copy of the GNU General Public License | ||
10149 | + * along with this program; if not, write to the Free Software | ||
10150 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10151 | + */ | ||
10152 | + | ||
10153 | +#ifndef _TIMB_RADIO_ | ||
10154 | +#define _TIMB_RADIO_ 1 | ||
10155 | + | ||
10156 | +#include <linux/i2c.h> | ||
10157 | + | ||
10158 | +struct timb_radio_platform_data { | ||
10159 | + int i2c_adapter; /* I2C adapter where the tuner and dsp are attached */ | ||
10160 | + char tuner[32]; | ||
10161 | + char dsp[32]; | ||
10162 | +}; | ||
10163 | + | ||
10164 | +#endif | ||
10165 | + | ||
10166 | diff -uNr linux-2.6.31/include/media/timb_video.h linux-2.6.31.new/include/media/timb_video.h | ||
10167 | --- linux-2.6.31/include/media/timb_video.h 1969-12-31 16:00:00.000000000 -0800 | ||
10168 | +++ linux-2.6.31.new/include/media/timb_video.h 2009-10-23 11:16:55.000000000 -0700 | ||
10169 | @@ -0,0 +1,30 @@ | ||
10170 | +/* | ||
10171 | + * timb_video.h Platform struct for the Timberdale video driver | ||
10172 | + * Copyright (c) 2009 Intel Corporation | ||
10173 | + * | ||
10174 | + * This program is free software; you can redistribute it and/or modify | ||
10175 | + * it under the terms of the GNU General Public License version 2 as | ||
10176 | + * published by the Free Software Foundation. | ||
10177 | + * | ||
10178 | + * This program is distributed in the hope that it will be useful, | ||
10179 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10180 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10181 | + * GNU General Public License for more details. | ||
10182 | + * | ||
10183 | + * You should have received a copy of the GNU General Public License | ||
10184 | + * along with this program; if not, write to the Free Software | ||
10185 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10186 | + */ | ||
10187 | + | ||
10188 | +#ifndef _TIMB_VIDEO_ | ||
10189 | +#define _TIMB_VIDEO_ 1 | ||
10190 | + | ||
10191 | +#include <linux/i2c.h> | ||
10192 | + | ||
10193 | +struct timb_video_platform_data { | ||
10194 | + int i2c_adapter; /* The I2C adapter where the encoder is attached */ | ||
10195 | + char encoder[32]; | ||
10196 | +}; | ||
10197 | + | ||
10198 | +#endif | ||
10199 | + | ||
10200 | diff -uNr linux-2.6.31/include/media/v4l2-chip-ident.h linux-2.6.31.new/include/media/v4l2-chip-ident.h | ||
10201 | --- linux-2.6.31/include/media/v4l2-chip-ident.h 2009-10-23 11:18:30.000000000 -0700 | ||
10202 | +++ linux-2.6.31.new/include/media/v4l2-chip-ident.h 2009-10-23 11:16:55.000000000 -0700 | ||
10203 | @@ -129,12 +129,18 @@ | ||
10204 | V4L2_IDENT_SAA6752HS = 6752, | ||
10205 | V4L2_IDENT_SAA6752HS_AC3 = 6753, | ||
10206 | |||
10207 | + /* modules tef6862: just ident 6862 */ | ||
10208 | + V4L2_IDENT_TEF6862 = 6862, | ||
10209 | + | ||
10210 | /* module adv7170: just ident 7170 */ | ||
10211 | V4L2_IDENT_ADV7170 = 7170, | ||
10212 | |||
10213 | /* module adv7175: just ident 7175 */ | ||
10214 | V4L2_IDENT_ADV7175 = 7175, | ||
10215 | |||
10216 | + /* module adv7180: just ident 7180 */ | ||
10217 | + V4L2_IDENT_ADV7180 = 7180, | ||
10218 | + | ||
10219 | /* module saa7185: just ident 7185 */ | ||
10220 | V4L2_IDENT_SAA7185 = 7185, | ||
10221 | |||
10222 | @@ -147,6 +153,9 @@ | ||
10223 | /* module adv7343: just ident 7343 */ | ||
10224 | V4L2_IDENT_ADV7343 = 7343, | ||
10225 | |||
10226 | + /* module saa7706h: just ident 7706 */ | ||
10227 | + V4L2_IDENT_SAA7706H = 7706, | ||
10228 | + | ||
10229 | /* module wm8739: just ident 8739 */ | ||
10230 | V4L2_IDENT_WM8739 = 8739, | ||
10231 | |||
10232 | diff -uNr linux-2.6.31/include/net/most/async.h linux-2.6.31.new/include/net/most/async.h | ||
10233 | --- linux-2.6.31/include/net/most/async.h 1969-12-31 16:00:00.000000000 -0800 | ||
10234 | +++ linux-2.6.31.new/include/net/most/async.h 2009-10-23 11:16:55.000000000 -0700 | ||
10235 | @@ -0,0 +1,12 @@ | ||
10236 | +#ifndef __ASYNC_H | ||
10237 | +#define __ASYNC_H | ||
10238 | + | ||
10239 | +struct sockaddr_mostasync { | ||
10240 | + sa_family_t most_family; | ||
10241 | + unsigned short most_dev; | ||
10242 | + unsigned char rx_channel; | ||
10243 | + unsigned char tx_channel; | ||
10244 | +}; | ||
10245 | + | ||
10246 | +#endif | ||
10247 | + | ||
10248 | diff -uNr linux-2.6.31/include/net/most/ctl.h linux-2.6.31.new/include/net/most/ctl.h | ||
10249 | --- linux-2.6.31/include/net/most/ctl.h 1969-12-31 16:00:00.000000000 -0800 | ||
10250 | +++ linux-2.6.31.new/include/net/most/ctl.h 2009-10-23 11:16:55.000000000 -0700 | ||
10251 | @@ -0,0 +1,12 @@ | ||
10252 | +#ifndef __CTL_H | ||
10253 | +#define __CTL_H | ||
10254 | + | ||
10255 | +struct sockaddr_mostctl { | ||
10256 | + sa_family_t most_family; | ||
10257 | + unsigned short most_dev; | ||
10258 | + unsigned char rx_channel; | ||
10259 | + unsigned char tx_channel; | ||
10260 | +}; | ||
10261 | + | ||
10262 | +#endif | ||
10263 | + | ||
10264 | diff -uNr linux-2.6.31/include/net/most/dev.h linux-2.6.31.new/include/net/most/dev.h | ||
10265 | --- linux-2.6.31/include/net/most/dev.h 1969-12-31 16:00:00.000000000 -0800 | ||
10266 | +++ linux-2.6.31.new/include/net/most/dev.h 2009-10-23 11:16:55.000000000 -0700 | ||
10267 | @@ -0,0 +1,27 @@ | ||
10268 | +#ifndef __DEV_H | ||
10269 | +#define __DEV_H | ||
10270 | + | ||
10271 | +struct sockaddr_mostdev { | ||
10272 | + sa_family_t most_family; | ||
10273 | + unsigned short most_dev; | ||
10274 | +}; | ||
10275 | + | ||
10276 | + | ||
10277 | +/* MOST Dev ioctl defines */ | ||
10278 | +#define MOSTDEVUP _IOW('M', 201, int) | ||
10279 | +#define MOSTDEVDOWN _IOW('M', 202, int) | ||
10280 | + | ||
10281 | +#define MOSTGETDEVLIST _IOR('M', 210, int) | ||
10282 | + | ||
10283 | +struct most_dev_req { | ||
10284 | + uint16_t dev_id; | ||
10285 | +}; | ||
10286 | + | ||
10287 | +struct most_dev_list_req { | ||
10288 | + uint16_t dev_num; | ||
10289 | + struct most_dev_req dev_req[0]; | ||
10290 | +}; | ||
10291 | + | ||
10292 | + | ||
10293 | +#endif | ||
10294 | + | ||
10295 | diff -uNr linux-2.6.31/include/net/most/most_core.h linux-2.6.31.new/include/net/most/most_core.h | ||
10296 | --- linux-2.6.31/include/net/most/most_core.h 1969-12-31 16:00:00.000000000 -0800 | ||
10297 | +++ linux-2.6.31.new/include/net/most/most_core.h 2009-10-23 11:16:55.000000000 -0700 | ||
10298 | @@ -0,0 +1,133 @@ | ||
10299 | +#ifndef __MOST_CORE_H | ||
10300 | +#define __MOST_CORE_H | ||
10301 | + | ||
10302 | +#include <net/most/most.h> | ||
10303 | + | ||
10304 | +enum most_chan_type { | ||
10305 | + CHAN_CTL = 0, | ||
10306 | + CHAN_SYNC, | ||
10307 | + CHAN_ASYNC, | ||
10308 | + CHAN_DEV | ||
10309 | +}; | ||
10310 | + | ||
10311 | +#define MOST_CONF_FLAG_UP 0x01 | ||
10312 | +#define MOST_CONF_FLAG_TX 0x02 | ||
10313 | + | ||
10314 | +enum most_dev_state { | ||
10315 | + MOST_DEV_DOWN = 0, | ||
10316 | + MOST_DEV_UP | ||
10317 | +}; | ||
10318 | + | ||
10319 | +struct most_dev { | ||
10320 | + | ||
10321 | + struct list_head list; | ||
10322 | + atomic_t refcnt; | ||
10323 | + | ||
10324 | + char name[8]; | ||
10325 | + | ||
10326 | + __u16 id; | ||
10327 | + enum most_dev_state state; | ||
10328 | + | ||
10329 | + struct module *owner; | ||
10330 | + | ||
10331 | + struct tasklet_struct rx_task; | ||
10332 | + struct tasklet_struct tx_task; | ||
10333 | + | ||
10334 | + struct sk_buff_head rx_q; | ||
10335 | + struct sk_buff_head ctl_q; | ||
10336 | + struct sk_buff_head async_q; | ||
10337 | + struct sk_buff_head sync_q; | ||
10338 | + | ||
10339 | + /* set by the driver */ | ||
10340 | + | ||
10341 | + void *driver_data; | ||
10342 | + struct device *parent; | ||
10343 | + | ||
10344 | + int (*open)(struct most_dev *mdev); | ||
10345 | + int (*close)(struct most_dev *mdev); | ||
10346 | + int (*conf_channel)(struct most_dev *mdev, enum most_chan_type type, | ||
10347 | + u8 channel, u8 flags); | ||
10348 | + int (*send)(struct sk_buff *skb); | ||
10349 | + int (*can_send)(struct sk_buff *skb); | ||
10350 | +}; | ||
10351 | + | ||
10352 | +#define most_dbg(...) printk(__VA_ARGS__) | ||
10353 | + | ||
10354 | +static inline struct most_dev *most_dev_hold(struct most_dev *d) | ||
10355 | +{ | ||
10356 | + if (try_module_get(d->owner)) | ||
10357 | + return d; | ||
10358 | + return NULL; | ||
10359 | +} | ||
10360 | + | ||
10361 | +static inline void most_dev_put(struct most_dev *d) | ||
10362 | +{ | ||
10363 | + module_put(d->owner); | ||
10364 | +} | ||
10365 | + | ||
10366 | +static inline void most_sched_tx(struct most_dev *mdev) | ||
10367 | +{ | ||
10368 | + tasklet_schedule(&mdev->tx_task); | ||
10369 | +} | ||
10370 | + | ||
10371 | +static inline void most_sched_rx(struct most_dev *mdev) | ||
10372 | +{ | ||
10373 | + tasklet_schedule(&mdev->rx_task); | ||
10374 | +} | ||
10375 | + | ||
10376 | +static inline int most_recv_frame(struct sk_buff *skb) | ||
10377 | +{ | ||
10378 | + struct most_dev *mdev = (struct most_dev *) skb->dev; | ||
10379 | + | ||
10380 | + /* Time stamp */ | ||
10381 | + __net_timestamp(skb); | ||
10382 | + | ||
10383 | + /* Queue frame for rx task */ | ||
10384 | + skb_queue_tail(&mdev->rx_q, skb); | ||
10385 | + most_sched_rx(mdev); | ||
10386 | + return 0; | ||
10387 | +} | ||
10388 | + | ||
10389 | +static inline int __most_configure_channel(struct most_dev *mdev, | ||
10390 | + u8 channel_type, u8 channel, u8 up) | ||
10391 | +{ | ||
10392 | + if (mdev->state != MOST_DEV_UP) | ||
10393 | + return -ENETDOWN; | ||
10394 | + | ||
10395 | + if (mdev->conf_channel) | ||
10396 | + if (channel != MOST_NO_CHANNEL) | ||
10397 | + return mdev->conf_channel(mdev, channel_type, channel, | ||
10398 | + up); | ||
10399 | + return 0; | ||
10400 | +} | ||
10401 | + | ||
10402 | +static inline int most_configure_channels(struct most_dev *mdev, | ||
10403 | + struct most_sock *sk, u8 up) | ||
10404 | +{ | ||
10405 | + int err; | ||
10406 | + u8 flags = (up) ? MOST_CONF_FLAG_UP : 0; | ||
10407 | + | ||
10408 | + err = __most_configure_channel(mdev, sk->channel_type, sk->rx_channel, | ||
10409 | + flags); | ||
10410 | + if (err) | ||
10411 | + return err; | ||
10412 | + | ||
10413 | + err = __most_configure_channel(mdev, sk->channel_type, sk->tx_channel, | ||
10414 | + flags | MOST_CONF_FLAG_TX); | ||
10415 | + if (err) | ||
10416 | + __most_configure_channel(mdev, sk->channel_type, sk->rx_channel, | ||
10417 | + (up) ? 0 : MOST_CONF_FLAG_UP); | ||
10418 | + return err; | ||
10419 | +} | ||
10420 | + | ||
10421 | +struct most_dev *most_alloc_dev(void); | ||
10422 | +void most_free_dev(struct most_dev *mdev); | ||
10423 | +int most_register_dev(struct most_dev *mdev); | ||
10424 | +int most_unregister_dev(struct most_dev *mdev); | ||
10425 | + | ||
10426 | +int most_get_dev_list(void __user *arg); | ||
10427 | +int most_open_dev(u16 dev_id); | ||
10428 | +int most_close_dev(u16 dev_id); | ||
10429 | + | ||
10430 | +#endif | ||
10431 | + | ||
10432 | diff -uNr linux-2.6.31/include/net/most/most.h linux-2.6.31.new/include/net/most/most.h | ||
10433 | --- linux-2.6.31/include/net/most/most.h 1969-12-31 16:00:00.000000000 -0800 | ||
10434 | +++ linux-2.6.31.new/include/net/most/most.h 2009-10-23 11:16:55.000000000 -0700 | ||
10435 | @@ -0,0 +1,110 @@ | ||
10436 | +#ifndef __MOST_H | ||
10437 | +#define __MOST_H | ||
10438 | + | ||
10439 | +#include <net/sock.h> | ||
10440 | + | ||
10441 | +#ifndef AF_MOST | ||
10442 | +#define AF_MOST 37 | ||
10443 | +#define PF_MOST AF_MOST | ||
10444 | +#endif | ||
10445 | + | ||
10446 | +/* Reserve for core and drivers use */ | ||
10447 | +#define MOST_SKB_RESERVE 8 | ||
10448 | + | ||
10449 | +#define CTL_FRAME_SIZE 32 | ||
10450 | + | ||
10451 | +#define MOSTPROTO_DEV 0 | ||
10452 | +#define MOSTPROTO_CTL 1 | ||
10453 | +#define MOSTPROTO_SYNC 2 | ||
10454 | +#define MOSTPROTO_ASYNC 3 | ||
10455 | + | ||
10456 | +#define MOST_NO_CHANNEL 0xFE | ||
10457 | + | ||
10458 | +enum { | ||
10459 | + MOST_CONNECTED = 1, /* Equal to TCP_ESTABLISHED makes net code happy */ | ||
10460 | + MOST_OPEN, | ||
10461 | + MOST_BOUND, | ||
10462 | +}; | ||
10463 | + | ||
10464 | + | ||
10465 | +struct most_skb_cb { | ||
10466 | + __u8 channel_type; | ||
10467 | + __u8 channel; | ||
10468 | +}; | ||
10469 | +#define most_cb(skb) ((struct most_skb_cb *)(skb->cb)) | ||
10470 | + | ||
10471 | +struct most_sock { | ||
10472 | + struct sock sk; | ||
10473 | + u8 channel_type; | ||
10474 | + u8 rx_channel; | ||
10475 | + u8 tx_channel; | ||
10476 | + int dev_id; | ||
10477 | + struct most_dev *mdev; | ||
10478 | +}; | ||
10479 | +#define most_sk(sk) ((struct most_sock *)sk) | ||
10480 | + | ||
10481 | +static inline struct sock *most_sk_alloc(struct net *net, | ||
10482 | + struct proto *pops, u8 channel_type) | ||
10483 | +{ | ||
10484 | + struct sock *sk = sk_alloc(net, PF_MOST, GFP_ATOMIC, pops); | ||
10485 | + if (sk) { | ||
10486 | + most_sk(sk)->channel_type = channel_type; | ||
10487 | + most_sk(sk)->dev_id = -1; | ||
10488 | + } | ||
10489 | + | ||
10490 | + return sk; | ||
10491 | +} | ||
10492 | +static inline struct sk_buff *most_skb_alloc(unsigned int len, gfp_t how) | ||
10493 | +{ | ||
10494 | + struct sk_buff *skb = alloc_skb(len + MOST_SKB_RESERVE, how); | ||
10495 | + | ||
10496 | + if (skb) | ||
10497 | + skb_reserve(skb, MOST_SKB_RESERVE); | ||
10498 | + | ||
10499 | + return skb; | ||
10500 | +} | ||
10501 | + | ||
10502 | +static inline struct sk_buff *most_skb_send_alloc(struct sock *sk, | ||
10503 | + unsigned long len, int nb, int *err) | ||
10504 | +{ | ||
10505 | + struct sk_buff *skb = | ||
10506 | + sock_alloc_send_skb(sk, len + MOST_SKB_RESERVE, nb, err); | ||
10507 | + | ||
10508 | + if (skb) | ||
10509 | + skb_reserve(skb, MOST_SKB_RESERVE); | ||
10510 | + | ||
10511 | + return skb; | ||
10512 | +} | ||
10513 | + | ||
10514 | +struct most_sock_list { | ||
10515 | + struct hlist_head head; | ||
10516 | + rwlock_t lock; | ||
10517 | +}; | ||
10518 | + | ||
10519 | +struct most_dev *most_dev_get(int index); | ||
10520 | + | ||
10521 | +int most_sock_register(int proto, struct net_proto_family *ops); | ||
10522 | +int most_sock_unregister(int proto); | ||
10523 | +void most_sock_link(struct sock *s); | ||
10524 | +void most_sock_unlink(struct sock *sk); | ||
10525 | + | ||
10526 | +int most_send_to_sock(int dev_id, struct sk_buff *skb); | ||
10527 | + | ||
10528 | +/* default implementation of socket operations */ | ||
10529 | +int most_sock_release(struct socket *sock); | ||
10530 | +int most_sock_bind(struct socket *sock, int dev_id, u8 rx_chan, u8 tx_chan); | ||
10531 | +int most_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); | ||
10532 | +int most_sock_recvmsg(struct kiocb *iocb, struct socket *sock, | ||
10533 | + struct msghdr *msg, size_t len, int flags); | ||
10534 | +int most_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
10535 | + struct msghdr *msg, size_t len); | ||
10536 | +int most_sock_setsockopt(struct socket *sock, int level, int optname, | ||
10537 | + char __user *optval, int len); | ||
10538 | +int most_sock_getsockopt(struct socket *sock, int level, int optname, | ||
10539 | + char __user *optval, int __user *optlen); | ||
10540 | + | ||
10541 | +extern int dev_sock_init(void); | ||
10542 | +extern void dev_sock_cleanup(void); | ||
10543 | + | ||
10544 | +#endif /* __MOST_H */ | ||
10545 | + | ||
10546 | diff -uNr linux-2.6.31/include/net/most/sync.h linux-2.6.31.new/include/net/most/sync.h | ||
10547 | --- linux-2.6.31/include/net/most/sync.h 1969-12-31 16:00:00.000000000 -0800 | ||
10548 | +++ linux-2.6.31.new/include/net/most/sync.h 2009-10-23 11:16:55.000000000 -0700 | ||
10549 | @@ -0,0 +1,12 @@ | ||
10550 | +#ifndef __SYNC_H | ||
10551 | +#define __SYNC_H | ||
10552 | + | ||
10553 | +struct sockaddr_mostsync { | ||
10554 | + sa_family_t most_family; | ||
10555 | + unsigned short most_dev; | ||
10556 | + unsigned char rx_channel; | ||
10557 | + unsigned char tx_channel; | ||
10558 | +}; | ||
10559 | + | ||
10560 | +#endif | ||
10561 | + | ||
10562 | diff -uNr linux-2.6.31/include/sound/timbi2s.h linux-2.6.31.new/include/sound/timbi2s.h | ||
10563 | --- linux-2.6.31/include/sound/timbi2s.h 1969-12-31 16:00:00.000000000 -0800 | ||
10564 | +++ linux-2.6.31.new/include/sound/timbi2s.h 2009-10-23 11:16:55.000000000 -0700 | ||
10565 | @@ -0,0 +1,32 @@ | ||
10566 | +/* | ||
10567 | + * timbi2s.h timberdale FPGA I2S platform data | ||
10568 | + * Copyright (c) 2009 Intel Corporation | ||
10569 | + * | ||
10570 | + * This program is free software; you can redistribute it and/or modify | ||
10571 | + * it under the terms of the GNU General Public License version 2 as | ||
10572 | + * published by the Free Software Foundation. | ||
10573 | + * | ||
10574 | + * This program is distributed in the hope that it will be useful, | ||
10575 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10576 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10577 | + * GNU General Public License for more details. | ||
10578 | + * | ||
10579 | + * You should have received a copy of the GNU General Public License | ||
10580 | + * along with this program; if not, write to the Free Software | ||
10581 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10582 | + */ | ||
10583 | +#ifndef __INCLUDE_SOUND_TIMBI2S_H | ||
10584 | +#define __INCLUDE_SOUND_TIMBI2S_H | ||
10585 | + | ||
10586 | +struct timbi2s_bus_data { | ||
10587 | + u8 rx; | ||
10588 | + u16 sample_rate; | ||
10589 | +}; | ||
10590 | + | ||
10591 | +struct timbi2s_platform_data { | ||
10592 | + const struct timbi2s_bus_data *busses; | ||
10593 | + int num_busses; | ||
10594 | + u32 main_clk; | ||
10595 | +}; | ||
10596 | + | ||
10597 | +#endif | ||
10598 | diff -uNr linux-2.6.31/net/Kconfig linux-2.6.31.new/net/Kconfig | ||
10599 | --- linux-2.6.31/net/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
10600 | +++ linux-2.6.31.new/net/Kconfig 2009-10-23 11:17:37.000000000 -0700 | ||
10601 | @@ -235,6 +235,7 @@ | ||
10602 | source "net/irda/Kconfig" | ||
10603 | source "net/bluetooth/Kconfig" | ||
10604 | source "net/rxrpc/Kconfig" | ||
10605 | +source "net/most/Kconfig" | ||
10606 | |||
10607 | config FIB_RULES | ||
10608 | bool | ||
10609 | diff -uNr linux-2.6.31/net/Makefile linux-2.6.31.new/net/Makefile | ||
10610 | --- linux-2.6.31/net/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
10611 | +++ linux-2.6.31.new/net/Makefile 2009-10-23 11:17:36.000000000 -0700 | ||
10612 | @@ -44,6 +44,7 @@ | ||
10613 | obj-$(CONFIG_DECNET) += decnet/ | ||
10614 | obj-$(CONFIG_ECONET) += econet/ | ||
10615 | obj-$(CONFIG_PHONET) += phonet/ | ||
10616 | +obj-$(CONFIG_MOST) += most/ | ||
10617 | ifneq ($(CONFIG_VLAN_8021Q),) | ||
10618 | obj-y += 8021q/ | ||
10619 | endif | ||
10620 | diff -uNr linux-2.6.31/net/most/af_most.c linux-2.6.31.new/net/most/af_most.c | ||
10621 | --- linux-2.6.31/net/most/af_most.c 1969-12-31 16:00:00.000000000 -0800 | ||
10622 | +++ linux-2.6.31.new/net/most/af_most.c 2009-10-23 11:17:37.000000000 -0700 | ||
10623 | @@ -0,0 +1,169 @@ | ||
10624 | +/* | ||
10625 | + * af_most.c Support for the MOST address family | ||
10626 | + * Copyright (c) 2009 Intel Corporation | ||
10627 | + * | ||
10628 | + * This program is free software; you can redistribute it and/or modify | ||
10629 | + * it under the terms of the GNU General Public License version 2 as | ||
10630 | + * published by the Free Software Foundation. | ||
10631 | + * | ||
10632 | + * This program is distributed in the hope that it will be useful, | ||
10633 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10634 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10635 | + * GNU General Public License for more details. | ||
10636 | + * | ||
10637 | + * You should have received a copy of the GNU General Public License | ||
10638 | + * along with this program; if not, write to the Free Software | ||
10639 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10640 | + */ | ||
10641 | + | ||
10642 | +#include <linux/module.h> | ||
10643 | +#include <net/most/most.h> | ||
10644 | + | ||
10645 | +#define MOST_MAX_PROTO 4 | ||
10646 | +static struct net_proto_family *most_proto[MOST_MAX_PROTO]; | ||
10647 | +static DEFINE_RWLOCK(most_proto_lock); | ||
10648 | + | ||
10649 | +#ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
10650 | +static struct lock_class_key most_lock_key[MOST_MAX_PROTO]; | ||
10651 | +static const char *most_key_strings[MOST_MAX_PROTO] = { | ||
10652 | + "sk_lock-AF_MOST-MOSTPROTO_DEV", | ||
10653 | + "sk_lock-AF_MOST-MOSTPROTO_CTL", | ||
10654 | + "sk_lock-AF_MOST-MOSTPROTO_SYNC", | ||
10655 | + "sk_lock-AF_MOST-MOSTPROTO_ASYNC", | ||
10656 | +}; | ||
10657 | + | ||
10658 | +static struct lock_class_key most_slock_key[MOST_MAX_PROTO]; | ||
10659 | +static const char *most_slock_key_strings[MOST_MAX_PROTO] = { | ||
10660 | + "slock-AF_MOST-MOSTPROTO_DEV", | ||
10661 | + "slock-AF_MOST-MOSTPROTO_CTL", | ||
10662 | + "slock-AF_MOST-MOSTPROTO_SYNC", | ||
10663 | + "slock-AF_MOST-MOSTPROTO_ASYNC", | ||
10664 | +}; | ||
10665 | + | ||
10666 | +static inline void most_sock_reclassify_lock(struct socket *sock, int proto) | ||
10667 | +{ | ||
10668 | + struct sock *sk = sock->sk; | ||
10669 | + | ||
10670 | + if (!sk) | ||
10671 | + return; | ||
10672 | + | ||
10673 | + BUG_ON(sock_owned_by_user(sk)); | ||
10674 | + | ||
10675 | + sock_lock_init_class_and_name(sk, | ||
10676 | + most_slock_key_strings[proto], &most_slock_key[proto], | ||
10677 | + most_key_strings[proto], &most_lock_key[proto]); | ||
10678 | +} | ||
10679 | +#else | ||
10680 | +static inline void most_sock_reclassify_lock(struct socket *sock, int proto) | ||
10681 | +{ | ||
10682 | +} | ||
10683 | +#endif | ||
10684 | + | ||
10685 | + | ||
10686 | +int most_sock_register(int proto, struct net_proto_family *ops) | ||
10687 | +{ | ||
10688 | + int err = 0; | ||
10689 | + | ||
10690 | + if (proto < 0 || proto >= MOST_MAX_PROTO) | ||
10691 | + return -EINVAL; | ||
10692 | + | ||
10693 | + write_lock(&most_proto_lock); | ||
10694 | + | ||
10695 | + if (most_proto[proto]) | ||
10696 | + err = -EEXIST; | ||
10697 | + else | ||
10698 | + most_proto[proto] = ops; | ||
10699 | + | ||
10700 | + write_unlock(&most_proto_lock); | ||
10701 | + | ||
10702 | + return err; | ||
10703 | +} | ||
10704 | +EXPORT_SYMBOL(most_sock_register); | ||
10705 | + | ||
10706 | +int most_sock_unregister(int proto) | ||
10707 | +{ | ||
10708 | + int err = 0; | ||
10709 | + | ||
10710 | + if (proto < 0 || proto >= MOST_MAX_PROTO) | ||
10711 | + return -EINVAL; | ||
10712 | + | ||
10713 | + write_lock(&most_proto_lock); | ||
10714 | + | ||
10715 | + if (!most_proto[proto]) | ||
10716 | + err = -ENOENT; | ||
10717 | + else | ||
10718 | + most_proto[proto] = NULL; | ||
10719 | + | ||
10720 | + write_unlock(&most_proto_lock); | ||
10721 | + | ||
10722 | + return err; | ||
10723 | +} | ||
10724 | +EXPORT_SYMBOL(most_sock_unregister); | ||
10725 | + | ||
10726 | +static int most_sock_create(struct net *net, struct socket *sock, int proto) | ||
10727 | +{ | ||
10728 | + int err; | ||
10729 | + | ||
10730 | + if (net != &init_net) | ||
10731 | + return -EAFNOSUPPORT; | ||
10732 | + | ||
10733 | + if (proto < 0 || proto >= MOST_MAX_PROTO) | ||
10734 | + return -EINVAL; | ||
10735 | + | ||
10736 | + if (!most_proto[proto]) | ||
10737 | + request_module("most-proto-%d", proto); | ||
10738 | + | ||
10739 | + err = -EPROTONOSUPPORT; | ||
10740 | + | ||
10741 | + read_lock(&most_proto_lock); | ||
10742 | + | ||
10743 | + if (most_proto[proto] && try_module_get(most_proto[proto]->owner)) { | ||
10744 | + err = most_proto[proto]->create(net, sock, proto); | ||
10745 | + most_sock_reclassify_lock(sock, proto); | ||
10746 | + module_put(most_proto[proto]->owner); | ||
10747 | + } | ||
10748 | + | ||
10749 | + read_unlock(&most_proto_lock); | ||
10750 | + | ||
10751 | + return err; | ||
10752 | +} | ||
10753 | + | ||
10754 | +static struct net_proto_family most_sock_family_ops = { | ||
10755 | + .owner = THIS_MODULE, | ||
10756 | + .family = PF_MOST, | ||
10757 | + .create = most_sock_create, | ||
10758 | +}; | ||
10759 | + | ||
10760 | +static int __init most_init(void) | ||
10761 | +{ | ||
10762 | + int err; | ||
10763 | + | ||
10764 | + err = sock_register(&most_sock_family_ops); | ||
10765 | + if (err < 0) | ||
10766 | + return err; | ||
10767 | + | ||
10768 | + err = dev_sock_init(); | ||
10769 | + if (err < 0) { | ||
10770 | + sock_unregister(PF_MOST); | ||
10771 | + return err; | ||
10772 | + } | ||
10773 | + | ||
10774 | + printk(KERN_INFO "MOST is initialized\n"); | ||
10775 | + | ||
10776 | + return 0; | ||
10777 | +} | ||
10778 | + | ||
10779 | +static void __exit most_exit(void) | ||
10780 | +{ | ||
10781 | + dev_sock_cleanup(); | ||
10782 | + | ||
10783 | + sock_unregister(PF_MOST); | ||
10784 | +} | ||
10785 | + | ||
10786 | +subsys_initcall(most_init); | ||
10787 | +module_exit(most_exit); | ||
10788 | + | ||
10789 | +MODULE_DESCRIPTION("MOST Core"); | ||
10790 | +MODULE_LICENSE("GPL v2"); | ||
10791 | +MODULE_ALIAS_NETPROTO(PF_MOST); | ||
10792 | + | ||
10793 | diff -uNr linux-2.6.31/net/most/async_sock.c linux-2.6.31.new/net/most/async_sock.c | ||
10794 | --- linux-2.6.31/net/most/async_sock.c 1969-12-31 16:00:00.000000000 -0800 | ||
10795 | +++ linux-2.6.31.new/net/most/async_sock.c 2009-10-23 11:17:37.000000000 -0700 | ||
10796 | @@ -0,0 +1,154 @@ | ||
10797 | +/* | ||
10798 | + * async_sock.c MOST asyncronous socket support | ||
10799 | + * Copyright (c) 2009 Intel Corporation | ||
10800 | + * | ||
10801 | + * This program is free software; you can redistribute it and/or modify | ||
10802 | + * it under the terms of the GNU General Public License version 2 as | ||
10803 | + * published by the Free Software Foundation. | ||
10804 | + * | ||
10805 | + * This program is distributed in the hope that it will be useful, | ||
10806 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10807 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10808 | + * GNU General Public License for more details. | ||
10809 | + * | ||
10810 | + * You should have received a copy of the GNU General Public License | ||
10811 | + * along with this program; if not, write to the Free Software | ||
10812 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10813 | + */ | ||
10814 | + | ||
10815 | +/* Supports: | ||
10816 | + * Support for MOST asynchronous sockets | ||
10817 | + */ | ||
10818 | + | ||
10819 | +#include <linux/module.h> | ||
10820 | +#include <net/most/most.h> | ||
10821 | +#include <net/most/most_core.h> | ||
10822 | +#include <net/most/async.h> | ||
10823 | + | ||
10824 | +static int async_sock_bind(struct socket *sock, struct sockaddr *addr, | ||
10825 | + int addr_len) | ||
10826 | +{ | ||
10827 | + struct sockaddr_mostasync *aaddr = (struct sockaddr_mostasync *)addr; | ||
10828 | + | ||
10829 | + if (!aaddr || aaddr->most_family != AF_MOST) | ||
10830 | + return -EINVAL; | ||
10831 | + | ||
10832 | + return most_sock_bind(sock, aaddr->most_dev, aaddr->rx_channel, | ||
10833 | + aaddr->tx_channel); | ||
10834 | +} | ||
10835 | + | ||
10836 | +static int async_sock_getname(struct socket *sock, struct sockaddr *addr, | ||
10837 | + int *addr_len, int peer) | ||
10838 | +{ | ||
10839 | + struct sockaddr_mostasync *aaddr = (struct sockaddr_mostasync *)addr; | ||
10840 | + struct sock *sk = sock->sk; | ||
10841 | + struct most_dev *mdev = most_sk(sk)->mdev; | ||
10842 | + | ||
10843 | + if (!mdev) | ||
10844 | + return -EBADFD; | ||
10845 | + | ||
10846 | + lock_sock(sk); | ||
10847 | + | ||
10848 | + *addr_len = sizeof(*aaddr); | ||
10849 | + aaddr->most_family = AF_MOST; | ||
10850 | + aaddr->most_dev = mdev->id; | ||
10851 | + aaddr->rx_channel = most_sk(sk)->rx_channel; | ||
10852 | + aaddr->tx_channel = most_sk(sk)->tx_channel; | ||
10853 | + | ||
10854 | + release_sock(sk); | ||
10855 | + return 0; | ||
10856 | +} | ||
10857 | + | ||
10858 | + | ||
10859 | +static const struct proto_ops async_sock_ops = { | ||
10860 | + .family = PF_MOST, | ||
10861 | + .owner = THIS_MODULE, | ||
10862 | + .release = most_sock_release, | ||
10863 | + .bind = async_sock_bind, | ||
10864 | + .getname = async_sock_getname, | ||
10865 | + .sendmsg = most_sock_sendmsg, | ||
10866 | + .recvmsg = most_sock_recvmsg, | ||
10867 | + .ioctl = most_sock_ioctl, | ||
10868 | + .poll = datagram_poll, | ||
10869 | + .listen = sock_no_listen, | ||
10870 | + .shutdown = sock_no_shutdown, | ||
10871 | + .setsockopt = most_sock_setsockopt, | ||
10872 | + .getsockopt = most_sock_getsockopt, | ||
10873 | + .connect = sock_no_connect, | ||
10874 | + .socketpair = sock_no_socketpair, | ||
10875 | + .accept = sock_no_accept, | ||
10876 | + .mmap = sock_no_mmap | ||
10877 | +}; | ||
10878 | +static struct proto async_sk_proto = { | ||
10879 | + .name = "ASYNC", | ||
10880 | + .owner = THIS_MODULE, | ||
10881 | + .obj_size = sizeof(struct most_sock) | ||
10882 | +}; | ||
10883 | + | ||
10884 | +static int async_sock_create(struct net *net, struct socket *sock, int protocol) | ||
10885 | +{ | ||
10886 | + struct sock *sk; | ||
10887 | + | ||
10888 | + if (sock->type != SOCK_DGRAM) | ||
10889 | + return -ESOCKTNOSUPPORT; | ||
10890 | + | ||
10891 | + sock->ops = &async_sock_ops; | ||
10892 | + | ||
10893 | + sk = most_sk_alloc(net, &async_sk_proto, CHAN_ASYNC); | ||
10894 | + if (!sk) | ||
10895 | + return -ENOMEM; | ||
10896 | + | ||
10897 | + sock_init_data(sock, sk); | ||
10898 | + | ||
10899 | + sock_reset_flag(sk, SOCK_ZAPPED); | ||
10900 | + | ||
10901 | + sk->sk_protocol = protocol; | ||
10902 | + | ||
10903 | + sock->state = SS_UNCONNECTED; | ||
10904 | + sk->sk_state = MOST_OPEN; | ||
10905 | + | ||
10906 | + most_sock_link(sk); | ||
10907 | + return 0; | ||
10908 | +} | ||
10909 | + | ||
10910 | +static struct net_proto_family async_sock_family_ops = { | ||
10911 | + .family = PF_MOST, | ||
10912 | + .owner = THIS_MODULE, | ||
10913 | + .create = async_sock_create, | ||
10914 | +}; | ||
10915 | + | ||
10916 | + | ||
10917 | +static int __init async_init(void) | ||
10918 | +{ | ||
10919 | + int err; | ||
10920 | + | ||
10921 | + err = proto_register(&async_sk_proto, 0); | ||
10922 | + if (err < 0) | ||
10923 | + return err; | ||
10924 | + | ||
10925 | + err = most_sock_register(MOSTPROTO_ASYNC, &async_sock_family_ops); | ||
10926 | + if (err < 0) { | ||
10927 | + printk(KERN_ERR "MOST socket registration failed\n"); | ||
10928 | + return err; | ||
10929 | + } | ||
10930 | + | ||
10931 | + printk(KERN_INFO "MOST asynchronous socket layer initialized\n"); | ||
10932 | + | ||
10933 | + return 0; | ||
10934 | +} | ||
10935 | + | ||
10936 | +static void __exit async_exit(void) | ||
10937 | +{ | ||
10938 | + if (most_sock_unregister(MOSTPROTO_ASYNC) < 0) | ||
10939 | + printk(KERN_ERR "ASYNC socket unregistration failed\n"); | ||
10940 | + | ||
10941 | + proto_unregister(&async_sk_proto); | ||
10942 | +} | ||
10943 | + | ||
10944 | +module_init(async_init); | ||
10945 | +module_exit(async_exit); | ||
10946 | + | ||
10947 | +MODULE_DESCRIPTION("Most Asyncronous"); | ||
10948 | +MODULE_LICENSE("GPL v2"); | ||
10949 | +MODULE_ALIAS("most-proto-3"); | ||
10950 | + | ||
10951 | diff -uNr linux-2.6.31/net/most/ctl_sock.c linux-2.6.31.new/net/most/ctl_sock.c | ||
10952 | --- linux-2.6.31/net/most/ctl_sock.c 1969-12-31 16:00:00.000000000 -0800 | ||
10953 | +++ linux-2.6.31.new/net/most/ctl_sock.c 2009-10-23 11:17:37.000000000 -0700 | ||
10954 | @@ -0,0 +1,159 @@ | ||
10955 | +/* | ||
10956 | + * ctl_sock.c Support for MOST control sockets | ||
10957 | + * Copyright (c) 2009 Intel Corporation | ||
10958 | + * | ||
10959 | + * This program is free software; you can redistribute it and/or modify | ||
10960 | + * it under the terms of the GNU General Public License version 2 as | ||
10961 | + * published by the Free Software Foundation. | ||
10962 | + * | ||
10963 | + * This program is distributed in the hope that it will be useful, | ||
10964 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10965 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10966 | + * GNU General Public License for more details. | ||
10967 | + * | ||
10968 | + * You should have received a copy of the GNU General Public License | ||
10969 | + * along with this program; if not, write to the Free Software | ||
10970 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
10971 | + */ | ||
10972 | + | ||
10973 | +#include <linux/module.h> | ||
10974 | +#include <net/most/most.h> | ||
10975 | +#include <net/most/most_core.h> | ||
10976 | +#include <net/most/ctl.h> | ||
10977 | + | ||
10978 | + | ||
10979 | +static int ctl_sock_bind(struct socket *sock, struct sockaddr *addr, | ||
10980 | + int addr_len) | ||
10981 | +{ | ||
10982 | + struct sockaddr_mostctl *caddr = (struct sockaddr_mostctl *) addr; | ||
10983 | + | ||
10984 | + if (!caddr || caddr->most_family != AF_MOST) | ||
10985 | + return -EINVAL; | ||
10986 | + | ||
10987 | + return most_sock_bind(sock, caddr->most_dev, caddr->rx_channel, | ||
10988 | + caddr->tx_channel); | ||
10989 | +} | ||
10990 | + | ||
10991 | +static int ctl_sock_getname(struct socket *sock, struct sockaddr *addr, | ||
10992 | + int *addr_len, int peer) | ||
10993 | +{ | ||
10994 | + struct sockaddr_mostctl *caddr = (struct sockaddr_mostctl *) addr; | ||
10995 | + struct sock *sk = sock->sk; | ||
10996 | + struct most_dev *mdev = most_sk(sk)->mdev; | ||
10997 | + | ||
10998 | + if (!mdev) | ||
10999 | + return -EBADFD; | ||
11000 | + | ||
11001 | + lock_sock(sk); | ||
11002 | + | ||
11003 | + *addr_len = sizeof(*caddr); | ||
11004 | + caddr->most_family = AF_MOST; | ||
11005 | + caddr->most_dev = mdev->id; | ||
11006 | + caddr->rx_channel = most_sk(sk)->rx_channel; | ||
11007 | + caddr->tx_channel = most_sk(sk)->tx_channel; | ||
11008 | + | ||
11009 | + release_sock(sk); | ||
11010 | + return 0; | ||
11011 | +} | ||
11012 | + | ||
11013 | +int ctl_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
11014 | + struct msghdr *msg, size_t len) | ||
11015 | +{ | ||
11016 | + if (len != CTL_FRAME_SIZE) | ||
11017 | + return -EINVAL; | ||
11018 | + | ||
11019 | + return most_sock_sendmsg(iocb, sock, msg, len); | ||
11020 | +} | ||
11021 | + | ||
11022 | +static const struct proto_ops ctl_sock_ops = { | ||
11023 | + .family = PF_MOST, | ||
11024 | + .owner = THIS_MODULE, | ||
11025 | + .release = most_sock_release, | ||
11026 | + .bind = ctl_sock_bind, | ||
11027 | + .getname = ctl_sock_getname, | ||
11028 | + .sendmsg = most_sock_sendmsg, | ||
11029 | + .recvmsg = most_sock_recvmsg, | ||
11030 | + .ioctl = most_sock_ioctl, | ||
11031 | + .poll = datagram_poll, | ||
11032 | + .listen = sock_no_listen, | ||
11033 | + .shutdown = sock_no_shutdown, | ||
11034 | + .setsockopt = most_sock_setsockopt, | ||
11035 | + .getsockopt = most_sock_getsockopt, | ||
11036 | + .connect = sock_no_connect, | ||
11037 | + .socketpair = sock_no_socketpair, | ||
11038 | + .accept = sock_no_accept, | ||
11039 | + .mmap = sock_no_mmap | ||
11040 | +}; | ||
11041 | +static struct proto ctl_sk_proto = { | ||
11042 | + .name = "CTL", | ||
11043 | + .owner = THIS_MODULE, | ||
11044 | + .obj_size = sizeof(struct most_sock) | ||
11045 | +}; | ||
11046 | + | ||
11047 | +static int ctl_sock_create(struct net *net, struct socket *sock, int protocol) | ||
11048 | +{ | ||
11049 | + struct sock *sk; | ||
11050 | + | ||
11051 | + if (sock->type != SOCK_RAW) | ||
11052 | + return -ESOCKTNOSUPPORT; | ||
11053 | + | ||
11054 | + sock->ops = &ctl_sock_ops; | ||
11055 | + | ||
11056 | + sk = most_sk_alloc(net, &ctl_sk_proto, CHAN_CTL); | ||
11057 | + if (!sk) | ||
11058 | + return -ENOMEM; | ||
11059 | + | ||
11060 | + sock_init_data(sock, sk); | ||
11061 | + | ||
11062 | + sock_reset_flag(sk, SOCK_ZAPPED); | ||
11063 | + | ||
11064 | + sk->sk_protocol = protocol; | ||
11065 | + | ||
11066 | + sock->state = SS_UNCONNECTED; | ||
11067 | + sk->sk_state = MOST_OPEN; | ||
11068 | + | ||
11069 | + most_sock_link(sk); | ||
11070 | + return 0; | ||
11071 | +} | ||
11072 | + | ||
11073 | +static struct net_proto_family ctl_sock_family_ops = { | ||
11074 | + .family = PF_MOST, | ||
11075 | + .owner = THIS_MODULE, | ||
11076 | + .create = ctl_sock_create, | ||
11077 | +}; | ||
11078 | + | ||
11079 | + | ||
11080 | +static int __init ctl_init(void) | ||
11081 | +{ | ||
11082 | + int err; | ||
11083 | + | ||
11084 | + err = proto_register(&ctl_sk_proto, 0); | ||
11085 | + if (err < 0) | ||
11086 | + return err; | ||
11087 | + | ||
11088 | + err = most_sock_register(MOSTPROTO_CTL, &ctl_sock_family_ops); | ||
11089 | + if (err < 0) { | ||
11090 | + printk(KERN_ERR "MOST socket registration failed\n"); | ||
11091 | + return err; | ||
11092 | + } | ||
11093 | + | ||
11094 | + printk(KERN_INFO "MOST control socket layer initialized\n"); | ||
11095 | + | ||
11096 | + return 0; | ||
11097 | +} | ||
11098 | + | ||
11099 | +static void __exit ctl_exit(void) | ||
11100 | +{ | ||
11101 | + if (most_sock_unregister(MOSTPROTO_CTL) < 0) | ||
11102 | + printk(KERN_ERR "Control socket unregistration failed\n"); | ||
11103 | + | ||
11104 | + proto_unregister(&ctl_sk_proto); | ||
11105 | +} | ||
11106 | + | ||
11107 | +module_init(ctl_init); | ||
11108 | +module_exit(ctl_exit); | ||
11109 | + | ||
11110 | +MODULE_DESCRIPTION("Most Control"); | ||
11111 | +MODULE_LICENSE("GPL v2"); | ||
11112 | +MODULE_ALIAS("most-proto-1"); | ||
11113 | + | ||
11114 | diff -uNr linux-2.6.31/net/most/dev_sock.c linux-2.6.31.new/net/most/dev_sock.c | ||
11115 | --- linux-2.6.31/net/most/dev_sock.c 1969-12-31 16:00:00.000000000 -0800 | ||
11116 | +++ linux-2.6.31.new/net/most/dev_sock.c 2009-10-23 11:17:37.000000000 -0700 | ||
11117 | @@ -0,0 +1,170 @@ | ||
11118 | +/* | ||
11119 | + * dev_sock.c Device MOST sockets, to control the underlaying devices | ||
11120 | + * Copyright (c) 2009 Intel Corporation | ||
11121 | + * | ||
11122 | + * This program is free software; you can redistribute it and/or modify | ||
11123 | + * it under the terms of the GNU General Public License version 2 as | ||
11124 | + * published by the Free Software Foundation. | ||
11125 | + * | ||
11126 | + * This program is distributed in the hope that it will be useful, | ||
11127 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11128 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11129 | + * GNU General Public License for more details. | ||
11130 | + * | ||
11131 | + * You should have received a copy of the GNU General Public License | ||
11132 | + * along with this program; if not, write to the Free Software | ||
11133 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
11134 | + */ | ||
11135 | + | ||
11136 | +#include <linux/module.h> | ||
11137 | +#include <net/most/most.h> | ||
11138 | +#include <net/most/most_core.h> | ||
11139 | +#include <net/most/dev.h> | ||
11140 | + | ||
11141 | +/* Ioctls that require bound socket */ | ||
11142 | +static inline int dev_sock_bound_ioctl(struct sock *sk, unsigned int cmd, | ||
11143 | + unsigned long arg) | ||
11144 | +{ | ||
11145 | + return -ENOSYS; | ||
11146 | +} | ||
11147 | + | ||
11148 | +static int dev_sock_ioctl(struct socket *sock, unsigned int cmd, | ||
11149 | + unsigned long arg) | ||
11150 | +{ | ||
11151 | + void __user *argp = (void __user *) arg; | ||
11152 | + | ||
11153 | + switch (cmd) { | ||
11154 | + case MOSTDEVUP: | ||
11155 | + return most_open_dev(arg & 0xffff); | ||
11156 | + case MOSTDEVDOWN: | ||
11157 | + return most_close_dev(arg & 0xffff); | ||
11158 | + case MOSTGETDEVLIST: | ||
11159 | + return most_get_dev_list(argp); | ||
11160 | + default: | ||
11161 | + return -EINVAL; | ||
11162 | + } | ||
11163 | +} | ||
11164 | + | ||
11165 | +static int dev_sock_bind(struct socket *sock, struct sockaddr *addr, | ||
11166 | + int addr_len) | ||
11167 | +{ | ||
11168 | + return -ENOSYS; | ||
11169 | +} | ||
11170 | + | ||
11171 | +static int dev_sock_getname(struct socket *sock, struct sockaddr *addr, | ||
11172 | + int *addr_len, int peer) | ||
11173 | +{ | ||
11174 | + struct sockaddr_mostdev *daddr = (struct sockaddr_mostdev *) addr; | ||
11175 | + struct sock *sk = sock->sk; | ||
11176 | + struct most_dev *mdev = most_sk(sk)->mdev; | ||
11177 | + | ||
11178 | + if (!mdev) | ||
11179 | + return -EBADFD; | ||
11180 | + | ||
11181 | + lock_sock(sk); | ||
11182 | + | ||
11183 | + *addr_len = sizeof(*daddr); | ||
11184 | + daddr->most_family = AF_MOST; | ||
11185 | + daddr->most_dev = mdev->id; | ||
11186 | + | ||
11187 | + release_sock(sk); | ||
11188 | + return 0; | ||
11189 | +} | ||
11190 | + | ||
11191 | +static int dev_sock_setsockopt(struct socket *sock, int level, int optname, | ||
11192 | + char __user *optval, int len) | ||
11193 | +{ | ||
11194 | + return -ENOSYS; | ||
11195 | +} | ||
11196 | + | ||
11197 | +static int dev_sock_getsockopt(struct socket *sock, int level, int optname, | ||
11198 | + char __user *optval, int __user *optlen) | ||
11199 | +{ | ||
11200 | + return -ENOSYS; | ||
11201 | +} | ||
11202 | + | ||
11203 | +static const struct proto_ops dev_sock_ops = { | ||
11204 | + .family = PF_MOST, | ||
11205 | + .owner = THIS_MODULE, | ||
11206 | + .release = most_sock_release, | ||
11207 | + .bind = dev_sock_bind, | ||
11208 | + .getname = dev_sock_getname, | ||
11209 | + .sendmsg = sock_no_sendmsg, | ||
11210 | + .recvmsg = sock_no_recvmsg, | ||
11211 | + .ioctl = dev_sock_ioctl, | ||
11212 | + .poll = sock_no_poll, | ||
11213 | + .listen = sock_no_listen, | ||
11214 | + .shutdown = sock_no_shutdown, | ||
11215 | + .setsockopt = dev_sock_setsockopt, | ||
11216 | + .getsockopt = dev_sock_getsockopt, | ||
11217 | + .connect = sock_no_connect, | ||
11218 | + .socketpair = sock_no_socketpair, | ||
11219 | + .accept = sock_no_accept, | ||
11220 | + .mmap = sock_no_mmap | ||
11221 | +}; | ||
11222 | +static struct proto dev_sk_proto = { | ||
11223 | + .name = "DEV", | ||
11224 | + .owner = THIS_MODULE, | ||
11225 | + .obj_size = sizeof(struct most_sock) | ||
11226 | +}; | ||
11227 | + | ||
11228 | +static int dev_sock_create(struct net *net, struct socket *sock, int protocol) | ||
11229 | +{ | ||
11230 | + struct sock *sk; | ||
11231 | + | ||
11232 | + if (sock->type != SOCK_RAW) | ||
11233 | + return -ESOCKTNOSUPPORT; | ||
11234 | + | ||
11235 | + sock->ops = &dev_sock_ops; | ||
11236 | + | ||
11237 | + sk = most_sk_alloc(net, &dev_sk_proto, CHAN_DEV); | ||
11238 | + if (!sk) | ||
11239 | + return -ENOMEM; | ||
11240 | + | ||
11241 | + sock_init_data(sock, sk); | ||
11242 | + | ||
11243 | + sock_reset_flag(sk, SOCK_ZAPPED); | ||
11244 | + | ||
11245 | + sk->sk_protocol = protocol; | ||
11246 | + | ||
11247 | + sock->state = SS_UNCONNECTED; | ||
11248 | + sk->sk_state = MOST_OPEN; | ||
11249 | + | ||
11250 | + most_sock_link(sk); | ||
11251 | + return 0; | ||
11252 | +} | ||
11253 | + | ||
11254 | +static struct net_proto_family dev_sock_family_ops = { | ||
11255 | + .family = PF_MOST, | ||
11256 | + .owner = THIS_MODULE, | ||
11257 | + .create = dev_sock_create, | ||
11258 | +}; | ||
11259 | + | ||
11260 | + | ||
11261 | +int __init dev_sock_init(void) | ||
11262 | +{ | ||
11263 | + int err; | ||
11264 | + | ||
11265 | + err = proto_register(&dev_sk_proto, 0); | ||
11266 | + if (err < 0) | ||
11267 | + return err; | ||
11268 | + | ||
11269 | + err = most_sock_register(MOSTPROTO_DEV, &dev_sock_family_ops); | ||
11270 | + if (err < 0) { | ||
11271 | + printk(KERN_ERR "MOST socket registration failed\n"); | ||
11272 | + return err; | ||
11273 | + } | ||
11274 | + | ||
11275 | + printk(KERN_INFO "MOST device socket layer initialized\n"); | ||
11276 | + | ||
11277 | + return 0; | ||
11278 | +} | ||
11279 | + | ||
11280 | +void __exit dev_sock_cleanup(void) | ||
11281 | +{ | ||
11282 | + if (most_sock_unregister(MOSTPROTO_DEV) < 0) | ||
11283 | + printk(KERN_ERR "Device socket unregistration failed\n"); | ||
11284 | + | ||
11285 | + proto_unregister(&dev_sk_proto); | ||
11286 | +} | ||
11287 | + | ||
11288 | diff -uNr linux-2.6.31/net/most/Kconfig linux-2.6.31.new/net/most/Kconfig | ||
11289 | --- linux-2.6.31/net/most/Kconfig 1969-12-31 16:00:00.000000000 -0800 | ||
11290 | +++ linux-2.6.31.new/net/most/Kconfig 2009-10-23 11:17:37.000000000 -0700 | ||
11291 | @@ -0,0 +1,38 @@ | ||
11292 | +# | ||
11293 | +# Media Oriented Systems Transport (MOST) network layer core configuration | ||
11294 | +# | ||
11295 | + | ||
11296 | +menuconfig MOST | ||
11297 | + depends on NET | ||
11298 | + tristate "MOST bus subsystem support" | ||
11299 | + ---help--- | ||
11300 | + Media Oriented Systems Transport (MOST) is a multimedia | ||
11301 | + communications protocol in the automotive industry. | ||
11302 | + | ||
11303 | + If you want MOST support you should say Y here. | ||
11304 | + | ||
11305 | +config MOST_CTL | ||
11306 | + tristate "Support for Control data over MOST" | ||
11307 | + depends on MOST | ||
11308 | + default N | ||
11309 | + ---help--- | ||
11310 | + Support for the control channel of the MOST bus. | ||
11311 | + | ||
11312 | +config MOST_ASYNC | ||
11313 | + tristate "Support for Asynchronous data over MOST" | ||
11314 | + depends on MOST | ||
11315 | + default N | ||
11316 | + ---help--- | ||
11317 | + Support for the asyncronous channel of the MOST bus. Normally | ||
11318 | + used for software download od file transfers. | ||
11319 | + | ||
11320 | +config MOST_SYNC | ||
11321 | + tristate "Support for Synchronous data over MOST" | ||
11322 | + depends on MOST | ||
11323 | + default N | ||
11324 | + ---help--- | ||
11325 | + Support for synchronous channles of the MOST bus. Normally used | ||
11326 | + for streaming media such as audio and video. | ||
11327 | + | ||
11328 | + | ||
11329 | +source "drivers/net/most/Kconfig" | ||
11330 | diff -uNr linux-2.6.31/net/most/Makefile linux-2.6.31.new/net/most/Makefile | ||
11331 | --- linux-2.6.31/net/most/Makefile 1969-12-31 16:00:00.000000000 -0800 | ||
11332 | +++ linux-2.6.31.new/net/most/Makefile 2009-10-23 11:17:37.000000000 -0700 | ||
11333 | @@ -0,0 +1,15 @@ | ||
11334 | +# | ||
11335 | +# Makefile for the Linux Media Oriented Systems Transport core. | ||
11336 | +# | ||
11337 | + | ||
11338 | +obj-$(CONFIG_MOST) += most.o | ||
11339 | +most-objs := af_most.o most_core.o most_sock.o dev_sock.o | ||
11340 | + | ||
11341 | +obj-$(CONFIG_MOST_CTL) += ctl.o | ||
11342 | +ctl-objs := ctl_sock.o | ||
11343 | + | ||
11344 | +obj-$(CONFIG_MOST_SYNC) += sync.o | ||
11345 | +sync-objs := sync_sock.o | ||
11346 | + | ||
11347 | +obj-$(CONFIG_MOST_ASYNC) += async.o | ||
11348 | +async-objs := async_sock.o | ||
11349 | diff -uNr linux-2.6.31/net/most/most_core.c linux-2.6.31.new/net/most/most_core.c | ||
11350 | --- linux-2.6.31/net/most/most_core.c 1969-12-31 16:00:00.000000000 -0800 | ||
11351 | +++ linux-2.6.31.new/net/most/most_core.c 2009-10-23 11:17:37.000000000 -0700 | ||
11352 | @@ -0,0 +1,287 @@ | ||
11353 | +/* | ||
11354 | + * most_core.c The MOST core functions | ||
11355 | + * Copyright (c) 2009 Intel Corporation | ||
11356 | + * | ||
11357 | + * This program is free software; you can redistribute it and/or modify | ||
11358 | + * it under the terms of the GNU General Public License version 2 as | ||
11359 | + * published by the Free Software Foundation. | ||
11360 | + * | ||
11361 | + * This program is distributed in the hope that it will be useful, | ||
11362 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11363 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11364 | + * GNU General Public License for more details. | ||
11365 | + * | ||
11366 | + * You should have received a copy of the GNU General Public License | ||
11367 | + * along with this program; if not, write to the Free Software | ||
11368 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
11369 | + */ | ||
11370 | + | ||
11371 | +#include <linux/kernel.h> | ||
11372 | +#include <linux/slab.h> | ||
11373 | +#include <linux/module.h> | ||
11374 | + | ||
11375 | +#include <net/most/most_core.h> | ||
11376 | +#include <net/most/dev.h> | ||
11377 | + | ||
11378 | +/* MOST device list */ | ||
11379 | +LIST_HEAD(most_dev_list); | ||
11380 | +DEFINE_RWLOCK(most_dev_list_lock); | ||
11381 | + | ||
11382 | + | ||
11383 | +int most_open_dev(u16 dev_id) | ||
11384 | +{ | ||
11385 | + struct most_dev *mdev = most_dev_get(dev_id); | ||
11386 | + int err = 0; | ||
11387 | + | ||
11388 | + if (!mdev) | ||
11389 | + return -ENODEV; | ||
11390 | + | ||
11391 | + most_dbg("%s: %s, state: %d\n", __func__, mdev->name, mdev->state); | ||
11392 | + | ||
11393 | + if (mdev->state == MOST_DEV_UP) | ||
11394 | + err = -EALREADY; | ||
11395 | + | ||
11396 | + if (!err) | ||
11397 | + err = mdev->open(mdev); | ||
11398 | + if (!err) | ||
11399 | + mdev->state = MOST_DEV_UP; | ||
11400 | + | ||
11401 | + most_dev_put(mdev); | ||
11402 | + most_dbg("%s: %s, state: %d, err: %d\n", __func__, | ||
11403 | + mdev->name, mdev->state, err); | ||
11404 | + return err; | ||
11405 | +} | ||
11406 | + | ||
11407 | +static int __most_close_dev(struct most_dev *mdev) | ||
11408 | +{ | ||
11409 | + int err = 0; | ||
11410 | + | ||
11411 | + most_dbg("%s: %s, state: %d\n", __func__, mdev ? mdev->name : "nil", | ||
11412 | + mdev ? mdev->state : -1); | ||
11413 | + | ||
11414 | + if (!mdev) | ||
11415 | + return -ENODEV; | ||
11416 | + | ||
11417 | + if (mdev->state == MOST_DEV_DOWN) | ||
11418 | + err = -EALREADY; | ||
11419 | + | ||
11420 | + if (!err) | ||
11421 | + err = mdev->close(mdev); | ||
11422 | + if (!err) | ||
11423 | + mdev->state = MOST_DEV_DOWN; | ||
11424 | + | ||
11425 | + most_dev_put(mdev); | ||
11426 | + most_dbg("%s: %s, state: %d, err: %d\n", __func__, | ||
11427 | + mdev->name, mdev->state, err); | ||
11428 | + return err; | ||
11429 | +} | ||
11430 | + | ||
11431 | +int most_close_dev(u16 dev_id) | ||
11432 | +{ | ||
11433 | + return __most_close_dev(most_dev_get(dev_id)); | ||
11434 | +} | ||
11435 | + | ||
11436 | +int most_get_dev_list(void __user *arg) | ||
11437 | +{ | ||
11438 | + struct most_dev_list_req *dl; | ||
11439 | + struct most_dev_req *dr; | ||
11440 | + struct list_head *p; | ||
11441 | + int n = 0, size, err; | ||
11442 | + u16 dev_num; | ||
11443 | + | ||
11444 | + if (get_user(dev_num, (u16 __user *) arg)) | ||
11445 | + return -EFAULT; | ||
11446 | + | ||
11447 | + if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr)) | ||
11448 | + return -EINVAL; | ||
11449 | + | ||
11450 | + size = sizeof(*dl) + dev_num * sizeof(*dr); | ||
11451 | + | ||
11452 | + dl = kzalloc(size, GFP_KERNEL); | ||
11453 | + if (!dl) | ||
11454 | + return -ENOMEM; | ||
11455 | + | ||
11456 | + dr = dl->dev_req; | ||
11457 | + | ||
11458 | + read_lock_bh(&most_dev_list_lock); | ||
11459 | + list_for_each(p, &most_dev_list) { | ||
11460 | + struct most_dev *mdev; | ||
11461 | + mdev = list_entry(p, struct most_dev, list); | ||
11462 | + (dr + n)->dev_id = mdev->id; | ||
11463 | + if (++n >= dev_num) | ||
11464 | + break; | ||
11465 | + } | ||
11466 | + read_unlock_bh(&most_dev_list_lock); | ||
11467 | + | ||
11468 | + dl->dev_num = n; | ||
11469 | + size = sizeof(*dl) + n * sizeof(*dr); | ||
11470 | + | ||
11471 | + err = copy_to_user(arg, dl, size); | ||
11472 | + kfree(dl); | ||
11473 | + | ||
11474 | + return err ? -EFAULT : 0; | ||
11475 | +} | ||
11476 | + | ||
11477 | +static int most_send_frame(struct sk_buff *skb) | ||
11478 | +{ | ||
11479 | + struct most_dev *mdev = (struct most_dev *) skb->dev; | ||
11480 | + | ||
11481 | + if (!mdev) { | ||
11482 | + kfree_skb(skb); | ||
11483 | + return -ENODEV; | ||
11484 | + } | ||
11485 | + | ||
11486 | + most_dbg("%s: %s type %d len %d\n", __func__, mdev->name, | ||
11487 | + most_cb(skb)->channel_type, skb->len); | ||
11488 | + | ||
11489 | + /* Get rid of skb owner, prior to sending to the driver. */ | ||
11490 | + skb_orphan(skb); | ||
11491 | + | ||
11492 | + return mdev->send(skb); | ||
11493 | +} | ||
11494 | + | ||
11495 | +static void most_send_queue(struct sk_buff_head *q) | ||
11496 | +{ | ||
11497 | + struct sk_buff *skb; | ||
11498 | + | ||
11499 | + while ((skb = skb_dequeue(q))) { | ||
11500 | + struct most_dev *mdev = (struct most_dev *)skb->dev; | ||
11501 | + | ||
11502 | + most_dbg("%s: skb %p len %d\n", __func__, skb, skb->len); | ||
11503 | + | ||
11504 | + if (!mdev->can_send || mdev->can_send(skb)) | ||
11505 | + most_send_frame(skb); | ||
11506 | + else { | ||
11507 | + most_dbg("%s, could not send frame, requeueing\n", | ||
11508 | + __func__); | ||
11509 | + skb_queue_tail(q, skb); | ||
11510 | + break; | ||
11511 | + } | ||
11512 | + } | ||
11513 | +} | ||
11514 | + | ||
11515 | +static void most_tx_task(unsigned long arg) | ||
11516 | +{ | ||
11517 | + struct most_dev *mdev = (struct most_dev *) arg; | ||
11518 | + | ||
11519 | + most_dbg("%s: %s\n", __func__, mdev->name); | ||
11520 | + | ||
11521 | + most_send_queue(&mdev->ctl_q); | ||
11522 | + most_send_queue(&mdev->sync_q); | ||
11523 | + most_send_queue(&mdev->async_q); | ||
11524 | +} | ||
11525 | + | ||
11526 | +static void most_rx_task(unsigned long arg) | ||
11527 | +{ | ||
11528 | + struct most_dev *mdev = (struct most_dev *) arg; | ||
11529 | + struct sk_buff *skb = skb_dequeue(&mdev->rx_q); | ||
11530 | + | ||
11531 | + most_dbg("%s: %s\n", __func__, mdev->name); | ||
11532 | + | ||
11533 | + while (skb) { | ||
11534 | + /* Send to the sockets */ | ||
11535 | + most_send_to_sock(mdev->id, skb); | ||
11536 | + kfree_skb(skb); | ||
11537 | + skb = skb_dequeue(&mdev->rx_q); | ||
11538 | + } | ||
11539 | +} | ||
11540 | + | ||
11541 | + | ||
11542 | +/* Get MOST device by index. | ||
11543 | + * Device is held on return. */ | ||
11544 | +struct most_dev *most_dev_get(int index) | ||
11545 | +{ | ||
11546 | + struct most_dev *mdev = NULL; | ||
11547 | + struct list_head *p; | ||
11548 | + | ||
11549 | + if (index < 0) | ||
11550 | + return NULL; | ||
11551 | + | ||
11552 | + read_lock(&most_dev_list_lock); | ||
11553 | + list_for_each(p, &most_dev_list) { | ||
11554 | + struct most_dev *d = list_entry(p, struct most_dev, list); | ||
11555 | + if (d->id == index) { | ||
11556 | + mdev = most_dev_hold(d); | ||
11557 | + break; | ||
11558 | + } | ||
11559 | + } | ||
11560 | + read_unlock(&most_dev_list_lock); | ||
11561 | + return mdev; | ||
11562 | +} | ||
11563 | +EXPORT_SYMBOL(most_dev_get); | ||
11564 | + | ||
11565 | + | ||
11566 | +/* Alloc MOST device */ | ||
11567 | +struct most_dev *most_alloc_dev(void) | ||
11568 | +{ | ||
11569 | + struct most_dev *mdev; | ||
11570 | + | ||
11571 | + mdev = kzalloc(sizeof(struct most_dev), GFP_KERNEL); | ||
11572 | + if (!mdev) | ||
11573 | + return NULL; | ||
11574 | + | ||
11575 | + mdev->state = MOST_DEV_DOWN; | ||
11576 | + | ||
11577 | + return mdev; | ||
11578 | +} | ||
11579 | +EXPORT_SYMBOL(most_alloc_dev); | ||
11580 | + | ||
11581 | + | ||
11582 | +void most_free_dev(struct most_dev *mdev) | ||
11583 | +{ | ||
11584 | + kfree(mdev); | ||
11585 | +} | ||
11586 | +EXPORT_SYMBOL(most_free_dev); | ||
11587 | + | ||
11588 | + | ||
11589 | +/* Register MOST device */ | ||
11590 | +int most_register_dev(struct most_dev *mdev) | ||
11591 | +{ | ||
11592 | + struct list_head *head = &most_dev_list, *p; | ||
11593 | + int id = 0; | ||
11594 | + | ||
11595 | + if (!mdev->open || !mdev->close || !mdev->send || !mdev->owner) | ||
11596 | + return -EINVAL; | ||
11597 | + | ||
11598 | + write_lock_bh(&most_dev_list_lock); | ||
11599 | + | ||
11600 | + /* Find first available device id */ | ||
11601 | + list_for_each(p, &most_dev_list) { | ||
11602 | + if (list_entry(p, struct most_dev, list)->id != id) | ||
11603 | + break; | ||
11604 | + head = p; id++; | ||
11605 | + } | ||
11606 | + | ||
11607 | + sprintf(mdev->name, "most%d", id); | ||
11608 | + mdev->id = id; | ||
11609 | + list_add(&mdev->list, head); | ||
11610 | + | ||
11611 | + tasklet_init(&mdev->rx_task, most_rx_task, (unsigned long) mdev); | ||
11612 | + tasklet_init(&mdev->tx_task, most_tx_task, (unsigned long) mdev); | ||
11613 | + | ||
11614 | + skb_queue_head_init(&mdev->rx_q); | ||
11615 | + skb_queue_head_init(&mdev->ctl_q); | ||
11616 | + skb_queue_head_init(&mdev->sync_q); | ||
11617 | + skb_queue_head_init(&mdev->async_q); | ||
11618 | + | ||
11619 | + write_unlock_bh(&most_dev_list_lock); | ||
11620 | + return 0; | ||
11621 | +} | ||
11622 | +EXPORT_SYMBOL(most_register_dev); | ||
11623 | + | ||
11624 | +int most_unregister_dev(struct most_dev *mdev) | ||
11625 | +{ | ||
11626 | + int ret = 0; | ||
11627 | + most_dbg("%s: %s: state: %d\n", __func__, mdev->name, mdev->state); | ||
11628 | + | ||
11629 | + if (mdev->state != MOST_DEV_DOWN) | ||
11630 | + ret = __most_close_dev(mdev); | ||
11631 | + | ||
11632 | + write_lock_bh(&most_dev_list_lock); | ||
11633 | + list_del(&mdev->list); | ||
11634 | + write_unlock_bh(&most_dev_list_lock); | ||
11635 | + | ||
11636 | + return ret; | ||
11637 | +} | ||
11638 | +EXPORT_SYMBOL(most_unregister_dev); | ||
11639 | + | ||
11640 | diff -uNr linux-2.6.31/net/most/most_sock.c linux-2.6.31.new/net/most/most_sock.c | ||
11641 | --- linux-2.6.31/net/most/most_sock.c 1969-12-31 16:00:00.000000000 -0800 | ||
11642 | +++ linux-2.6.31.new/net/most/most_sock.c 2009-10-23 11:17:37.000000000 -0700 | ||
11643 | @@ -0,0 +1,315 @@ | ||
11644 | +/* | ||
11645 | + * most_sock.c Generic functions for MOST sockets | ||
11646 | + * Copyright (c) 2009 Intel Corporation | ||
11647 | + * | ||
11648 | + * This program is free software; you can redistribute it and/or modify | ||
11649 | + * it under the terms of the GNU General Public License version 2 as | ||
11650 | + * published by the Free Software Foundation. | ||
11651 | + * | ||
11652 | + * This program is distributed in the hope that it will be useful, | ||
11653 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11654 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11655 | + * GNU General Public License for more details. | ||
11656 | + * | ||
11657 | + * You should have received a copy of the GNU General Public License | ||
11658 | + * along with this program; if not, write to the Free Software | ||
11659 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
11660 | + */ | ||
11661 | + | ||
11662 | +#include <linux/module.h> | ||
11663 | +#include <net/most/most_core.h> | ||
11664 | + | ||
11665 | +static struct most_sock_list most_sk_list = { | ||
11666 | + .lock = __RW_LOCK_UNLOCKED(ctl_sk_list.lock) | ||
11667 | +}; | ||
11668 | + | ||
11669 | +void most_sock_link(struct sock *sk) | ||
11670 | +{ | ||
11671 | + write_lock_bh(&most_sk_list.lock); | ||
11672 | + sk_add_node(sk, &most_sk_list.head); | ||
11673 | + write_unlock_bh(&most_sk_list.lock); | ||
11674 | +} | ||
11675 | +EXPORT_SYMBOL(most_sock_link); | ||
11676 | + | ||
11677 | +void most_sock_unlink(struct sock *sk) | ||
11678 | +{ | ||
11679 | + write_lock_bh(&most_sk_list.lock); | ||
11680 | + sk_del_node_init(sk); | ||
11681 | + write_unlock_bh(&most_sk_list.lock); | ||
11682 | +} | ||
11683 | +EXPORT_SYMBOL(most_sock_unlink); | ||
11684 | + | ||
11685 | +static int channel_in_use(int dev_id, u8 channel) | ||
11686 | +{ | ||
11687 | + struct sock *sk; | ||
11688 | + struct hlist_node *node; | ||
11689 | + | ||
11690 | + read_lock_bh(&most_sk_list.lock); | ||
11691 | + | ||
11692 | + sk_for_each(sk, node, &most_sk_list.head) | ||
11693 | + if (most_sk(sk)->dev_id == dev_id && | ||
11694 | + sk->sk_state == MOST_BOUND && | ||
11695 | + (most_sk(sk)->rx_channel == channel || | ||
11696 | + most_sk(sk)->tx_channel == channel)) | ||
11697 | + goto found; | ||
11698 | + | ||
11699 | + sk = NULL; | ||
11700 | +found: | ||
11701 | + read_unlock_bh(&most_sk_list.lock); | ||
11702 | + | ||
11703 | + return sk != NULL; | ||
11704 | +} | ||
11705 | + | ||
11706 | +int most_send_to_sock(int dev_id, struct sk_buff *skb) | ||
11707 | +{ | ||
11708 | + struct sock *sk; | ||
11709 | + struct hlist_node *node; | ||
11710 | + | ||
11711 | + read_lock(&most_sk_list.lock); | ||
11712 | + sk_for_each(sk, node, &most_sk_list.head) { | ||
11713 | + if (most_sk(sk)->dev_id == dev_id && | ||
11714 | + most_sk(sk)->channel_type == most_cb(skb)->channel_type | ||
11715 | + && most_sk(sk)->rx_channel == most_cb(skb)->channel && | ||
11716 | + sk->sk_state == MOST_BOUND) { | ||
11717 | + | ||
11718 | + struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); | ||
11719 | + if (nskb) | ||
11720 | + if (sock_queue_rcv_skb(sk, nskb)) | ||
11721 | + kfree_skb(nskb); | ||
11722 | + } | ||
11723 | + | ||
11724 | + } | ||
11725 | + read_unlock(&most_sk_list.lock); | ||
11726 | + | ||
11727 | + return 0; | ||
11728 | +} | ||
11729 | +EXPORT_SYMBOL(most_send_to_sock); | ||
11730 | + | ||
11731 | +int most_sock_release(struct socket *sock) | ||
11732 | +{ | ||
11733 | + struct sock *sk = sock->sk; | ||
11734 | + struct most_dev *mdev; | ||
11735 | + | ||
11736 | + most_dbg("%s: sock %p sk %p\n", __func__, sock, sk); | ||
11737 | + | ||
11738 | + if (!sk) | ||
11739 | + return 0; | ||
11740 | + | ||
11741 | + mdev = most_sk(sk)->mdev; | ||
11742 | + | ||
11743 | + most_sock_unlink(sk); | ||
11744 | + | ||
11745 | + if (mdev) { | ||
11746 | + if (sk->sk_state == MOST_BOUND) | ||
11747 | + most_configure_channels(mdev, most_sk(sk), 0); | ||
11748 | + | ||
11749 | + most_dev_put(mdev); | ||
11750 | + } | ||
11751 | + | ||
11752 | + sock_orphan(sk); | ||
11753 | + sock_put(sk); | ||
11754 | + return 0; | ||
11755 | +} | ||
11756 | +EXPORT_SYMBOL(most_sock_release); | ||
11757 | + | ||
11758 | +int most_sock_bind(struct socket *sock, int dev_id, u8 rx_chan, u8 tx_chan) | ||
11759 | +{ | ||
11760 | + struct sock *sk = sock->sk; | ||
11761 | + struct most_dev *mdev = NULL; | ||
11762 | + int err = 0; | ||
11763 | + | ||
11764 | + most_dbg("%s: sock %p sk %p, rx: %d, tx: %d\n", | ||
11765 | + __func__, sock, sk, rx_chan, tx_chan); | ||
11766 | + | ||
11767 | + lock_sock(sk); | ||
11768 | + | ||
11769 | + if (sk->sk_state != MOST_OPEN) { | ||
11770 | + err = -EBADFD; | ||
11771 | + goto done; | ||
11772 | + } | ||
11773 | + | ||
11774 | + if (most_sk(sk)->mdev) { | ||
11775 | + err = -EALREADY; | ||
11776 | + goto done; | ||
11777 | + } | ||
11778 | + | ||
11779 | + if (channel_in_use(dev_id, rx_chan) || | ||
11780 | + channel_in_use(dev_id, tx_chan)) { | ||
11781 | + err = -EADDRINUSE; | ||
11782 | + goto done; | ||
11783 | + } else { | ||
11784 | + most_sk(sk)->rx_channel = rx_chan; | ||
11785 | + most_sk(sk)->tx_channel = tx_chan; | ||
11786 | + } | ||
11787 | + | ||
11788 | + mdev = most_dev_get(dev_id); | ||
11789 | + if (!mdev) { | ||
11790 | + err = -ENODEV; | ||
11791 | + goto done; | ||
11792 | + } | ||
11793 | + | ||
11794 | + err = most_configure_channels(mdev, most_sk(sk), 1); | ||
11795 | + if (err) { | ||
11796 | + most_dev_put(mdev); | ||
11797 | + goto done; | ||
11798 | + } | ||
11799 | + | ||
11800 | + most_sk(sk)->mdev = mdev; | ||
11801 | + most_sk(sk)->dev_id = mdev->id; | ||
11802 | + | ||
11803 | + sk->sk_state = MOST_BOUND; | ||
11804 | + | ||
11805 | +done: | ||
11806 | + release_sock(sk); | ||
11807 | + return err; | ||
11808 | +} | ||
11809 | +EXPORT_SYMBOL(most_sock_bind); | ||
11810 | + | ||
11811 | + | ||
11812 | +int most_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
11813 | +{ | ||
11814 | + most_dbg("%s\n", __func__); | ||
11815 | + return -EINVAL; | ||
11816 | +} | ||
11817 | +EXPORT_SYMBOL(most_sock_ioctl); | ||
11818 | + | ||
11819 | +int most_sock_recvmsg(struct kiocb *iocb, struct socket *sock, | ||
11820 | + struct msghdr *msg, size_t len, int flags) | ||
11821 | +{ | ||
11822 | + int noblock = flags & MSG_DONTWAIT; | ||
11823 | + struct sock *sk = sock->sk; | ||
11824 | + struct sk_buff *skb; | ||
11825 | + int copied, err; | ||
11826 | + | ||
11827 | + most_dbg("%s\n", __func__); | ||
11828 | + | ||
11829 | + if (most_sk(sk)->rx_channel == MOST_NO_CHANNEL) | ||
11830 | + return -EOPNOTSUPP; | ||
11831 | + | ||
11832 | + if (flags & (MSG_OOB)) | ||
11833 | + return -EOPNOTSUPP; | ||
11834 | + | ||
11835 | + if (sk->sk_state != MOST_BOUND) | ||
11836 | + return 0; | ||
11837 | + | ||
11838 | + skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
11839 | + if (!skb) | ||
11840 | + return err; | ||
11841 | + | ||
11842 | + msg->msg_namelen = 0; | ||
11843 | + | ||
11844 | + copied = skb->len; | ||
11845 | + if (len < copied) { | ||
11846 | + msg->msg_flags |= MSG_TRUNC; | ||
11847 | + copied = len; | ||
11848 | + } | ||
11849 | + | ||
11850 | + skb_reset_transport_header(skb); | ||
11851 | + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
11852 | + | ||
11853 | + skb_free_datagram(sk, skb); | ||
11854 | + | ||
11855 | + return err ? : copied; | ||
11856 | +} | ||
11857 | +EXPORT_SYMBOL(most_sock_recvmsg); | ||
11858 | + | ||
11859 | +int most_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
11860 | + struct msghdr *msg, size_t len) | ||
11861 | +{ | ||
11862 | + struct sock *sk = sock->sk; | ||
11863 | + struct most_dev *mdev; | ||
11864 | + struct sk_buff *skb; | ||
11865 | + int err; | ||
11866 | + | ||
11867 | + most_dbg("%s: sock %p sk %p, channeltype: %d\n", | ||
11868 | + __func__, sock, sk, most_sk(sk)->channel_type); | ||
11869 | + | ||
11870 | + if (most_sk(sk)->tx_channel == MOST_NO_CHANNEL) | ||
11871 | + return -EOPNOTSUPP; | ||
11872 | + | ||
11873 | + if (msg->msg_flags & MSG_OOB) | ||
11874 | + return -EOPNOTSUPP; | ||
11875 | + | ||
11876 | + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) | ||
11877 | + return -EINVAL; | ||
11878 | + | ||
11879 | + lock_sock(sk); | ||
11880 | + | ||
11881 | + mdev = most_sk(sk)->mdev; | ||
11882 | + if (!mdev) { | ||
11883 | + err = -EBADFD; | ||
11884 | + goto done; | ||
11885 | + } | ||
11886 | + | ||
11887 | + skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); | ||
11888 | + if (!skb) | ||
11889 | + goto done; | ||
11890 | + | ||
11891 | + most_cb(skb)->channel = most_sk(sk)->tx_channel; | ||
11892 | + most_cb(skb)->channel_type = most_sk(sk)->channel_type; | ||
11893 | + | ||
11894 | + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { | ||
11895 | + err = -EFAULT; | ||
11896 | + goto drop; | ||
11897 | + } | ||
11898 | + | ||
11899 | + skb->dev = (void *) mdev; | ||
11900 | + | ||
11901 | + skb_queue_tail(&mdev->ctl_q, skb); | ||
11902 | + most_sched_tx(mdev); | ||
11903 | + | ||
11904 | + err = len; | ||
11905 | + | ||
11906 | +done: | ||
11907 | + release_sock(sk); | ||
11908 | + return err; | ||
11909 | + | ||
11910 | +drop: | ||
11911 | + kfree_skb(skb); | ||
11912 | + goto done; | ||
11913 | +} | ||
11914 | +EXPORT_SYMBOL(most_sock_sendmsg); | ||
11915 | + | ||
11916 | +int most_sock_setsockopt(struct socket *sock, int level, int optname, | ||
11917 | + char __user *optval, int len) | ||
11918 | +{ | ||
11919 | + struct sock *sk = sock->sk; | ||
11920 | + int err = 0; | ||
11921 | + | ||
11922 | + most_dbg("%s: sk %p", __func__, sk); | ||
11923 | + | ||
11924 | + lock_sock(sk); | ||
11925 | + | ||
11926 | + switch (optname) { | ||
11927 | + default: | ||
11928 | + err = -ENOPROTOOPT; | ||
11929 | + break; | ||
11930 | + } | ||
11931 | + | ||
11932 | + release_sock(sk); | ||
11933 | + return err; | ||
11934 | +} | ||
11935 | +EXPORT_SYMBOL(most_sock_setsockopt); | ||
11936 | + | ||
11937 | + | ||
11938 | +int most_sock_getsockopt(struct socket *sock, int level, int optname, | ||
11939 | + char __user *optval, int __user *optlen) | ||
11940 | +{ | ||
11941 | + struct sock *sk = sock->sk; | ||
11942 | + int err = 0; | ||
11943 | + | ||
11944 | + most_dbg("%s: sk %p", __func__, sk); | ||
11945 | + | ||
11946 | + lock_sock(sk); | ||
11947 | + | ||
11948 | + switch (optname) { | ||
11949 | + default: | ||
11950 | + err = -ENOPROTOOPT; | ||
11951 | + break; | ||
11952 | + } | ||
11953 | + | ||
11954 | + release_sock(sk); | ||
11955 | + return err; | ||
11956 | +} | ||
11957 | +EXPORT_SYMBOL(most_sock_getsockopt); | ||
11958 | + | ||
11959 | diff -uNr linux-2.6.31/net/most/sync_sock.c linux-2.6.31.new/net/most/sync_sock.c | ||
11960 | --- linux-2.6.31/net/most/sync_sock.c 1969-12-31 16:00:00.000000000 -0800 | ||
11961 | +++ linux-2.6.31.new/net/most/sync_sock.c 2009-10-23 11:17:37.000000000 -0700 | ||
11962 | @@ -0,0 +1,150 @@ | ||
11963 | +/* | ||
11964 | + * sync_sock.c Support for MOST synchronous sockets | ||
11965 | + * Copyright (c) 2009 Intel Corporation | ||
11966 | + * | ||
11967 | + * This program is free software; you can redistribute it and/or modify | ||
11968 | + * it under the terms of the GNU General Public License version 2 as | ||
11969 | + * published by the Free Software Foundation. | ||
11970 | + * | ||
11971 | + * This program is distributed in the hope that it will be useful, | ||
11972 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11973 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11974 | + * GNU General Public License for more details. | ||
11975 | + * | ||
11976 | + * You should have received a copy of the GNU General Public License | ||
11977 | + * along with this program; if not, write to the Free Software | ||
11978 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
11979 | + */ | ||
11980 | + | ||
11981 | +#include <linux/module.h> | ||
11982 | +#include <net/most/most.h> | ||
11983 | +#include <net/most/most_core.h> | ||
11984 | +#include <net/most/sync.h> | ||
11985 | + | ||
11986 | +static int sync_sock_bind(struct socket *sock, struct sockaddr *addr, | ||
11987 | + int addr_len) | ||
11988 | +{ | ||
11989 | + struct sockaddr_mostsync *saddr = (struct sockaddr_mostsync *)addr; | ||
11990 | + | ||
11991 | + if (!saddr || saddr->most_family != AF_MOST) | ||
11992 | + return -EINVAL; | ||
11993 | + | ||
11994 | + return most_sock_bind(sock, saddr->most_dev, saddr->rx_channel, | ||
11995 | + saddr->tx_channel); | ||
11996 | +} | ||
11997 | + | ||
11998 | +static int sync_sock_getname(struct socket *sock, struct sockaddr *addr, | ||
11999 | + int *addr_len, int peer) | ||
12000 | +{ | ||
12001 | + struct sockaddr_mostsync *saddr = (struct sockaddr_mostsync *)addr; | ||
12002 | + struct sock *sk = sock->sk; | ||
12003 | + struct most_dev *mdev = most_sk(sk)->mdev; | ||
12004 | + | ||
12005 | + if (!mdev) | ||
12006 | + return -EBADFD; | ||
12007 | + | ||
12008 | + lock_sock(sk); | ||
12009 | + | ||
12010 | + *addr_len = sizeof(*saddr); | ||
12011 | + saddr->most_family = AF_MOST; | ||
12012 | + saddr->most_dev = mdev->id; | ||
12013 | + saddr->rx_channel = most_sk(sk)->rx_channel; | ||
12014 | + saddr->tx_channel = most_sk(sk)->tx_channel; | ||
12015 | + | ||
12016 | + release_sock(sk); | ||
12017 | + return 0; | ||
12018 | +} | ||
12019 | + | ||
12020 | + | ||
12021 | +static const struct proto_ops sync_sock_ops = { | ||
12022 | + .family = PF_MOST, | ||
12023 | + .owner = THIS_MODULE, | ||
12024 | + .release = most_sock_release, | ||
12025 | + .bind = sync_sock_bind, | ||
12026 | + .getname = sync_sock_getname, | ||
12027 | + .sendmsg = most_sock_sendmsg, | ||
12028 | + .recvmsg = most_sock_recvmsg, | ||
12029 | + .ioctl = most_sock_ioctl, | ||
12030 | + .poll = datagram_poll, | ||
12031 | + .listen = sock_no_listen, | ||
12032 | + .shutdown = sock_no_shutdown, | ||
12033 | + .setsockopt = most_sock_setsockopt, | ||
12034 | + .getsockopt = most_sock_getsockopt, | ||
12035 | + .connect = sock_no_connect, | ||
12036 | + .socketpair = sock_no_socketpair, | ||
12037 | + .accept = sock_no_accept, | ||
12038 | + .mmap = sock_no_mmap | ||
12039 | +}; | ||
12040 | +static struct proto sync_sk_proto = { | ||
12041 | + .name = "SYNC", | ||
12042 | + .owner = THIS_MODULE, | ||
12043 | + .obj_size = sizeof(struct most_sock) | ||
12044 | +}; | ||
12045 | + | ||
12046 | +static int sync_sock_create(struct net *net, struct socket *sock, int protocol) | ||
12047 | +{ | ||
12048 | + struct sock *sk; | ||
12049 | + | ||
12050 | + if (sock->type != SOCK_STREAM) | ||
12051 | + return -ESOCKTNOSUPPORT; | ||
12052 | + | ||
12053 | + sock->ops = &sync_sock_ops; | ||
12054 | + | ||
12055 | + sk = most_sk_alloc(net, &sync_sk_proto, CHAN_SYNC); | ||
12056 | + if (!sk) | ||
12057 | + return -ENOMEM; | ||
12058 | + | ||
12059 | + sock_init_data(sock, sk); | ||
12060 | + | ||
12061 | + sock_reset_flag(sk, SOCK_ZAPPED); | ||
12062 | + | ||
12063 | + sk->sk_protocol = protocol; | ||
12064 | + | ||
12065 | + sock->state = SS_UNCONNECTED; | ||
12066 | + sk->sk_state = MOST_OPEN; | ||
12067 | + | ||
12068 | + most_sock_link(sk); | ||
12069 | + return 0; | ||
12070 | +} | ||
12071 | + | ||
12072 | +static struct net_proto_family sync_sock_family_ops = { | ||
12073 | + .family = PF_MOST, | ||
12074 | + .owner = THIS_MODULE, | ||
12075 | + .create = sync_sock_create, | ||
12076 | +}; | ||
12077 | + | ||
12078 | + | ||
12079 | +static int __init sync_init(void) | ||
12080 | +{ | ||
12081 | + int err; | ||
12082 | + | ||
12083 | + err = proto_register(&sync_sk_proto, 0); | ||
12084 | + if (err < 0) | ||
12085 | + return err; | ||
12086 | + | ||
12087 | + err = most_sock_register(MOSTPROTO_SYNC, &sync_sock_family_ops); | ||
12088 | + if (err < 0) { | ||
12089 | + printk(KERN_ERR "MOST socket registration failed\n"); | ||
12090 | + return err; | ||
12091 | + } | ||
12092 | + | ||
12093 | + printk(KERN_INFO "MOST synchronous socket layer initialized\n"); | ||
12094 | + | ||
12095 | + return 0; | ||
12096 | +} | ||
12097 | + | ||
12098 | +static void __exit sync_exit(void) | ||
12099 | +{ | ||
12100 | + if (most_sock_unregister(MOSTPROTO_SYNC) < 0) | ||
12101 | + printk(KERN_ERR "SYNC socket unregistration failed\n"); | ||
12102 | + | ||
12103 | + proto_unregister(&sync_sk_proto); | ||
12104 | +} | ||
12105 | + | ||
12106 | +module_init(sync_init); | ||
12107 | +module_exit(sync_exit); | ||
12108 | + | ||
12109 | +MODULE_DESCRIPTION("Most Syncronous"); | ||
12110 | +MODULE_LICENSE("GPL v2"); | ||
12111 | +MODULE_ALIAS("most-proto-2"); | ||
12112 | + | ||
12113 | diff -uNr linux-2.6.31/sound/drivers/Kconfig linux-2.6.31.new/sound/drivers/Kconfig | ||
12114 | --- linux-2.6.31/sound/drivers/Kconfig 2009-10-23 11:18:30.000000000 -0700 | ||
12115 | +++ linux-2.6.31.new/sound/drivers/Kconfig 2009-10-23 11:16:57.000000000 -0700 | ||
12116 | @@ -182,4 +182,17 @@ | ||
12117 | The default time-out value in seconds for AC97 automatic | ||
12118 | power-save mode. 0 means to disable the power-save mode. | ||
12119 | |||
12120 | +config SND_TIMBERDALE_I2S | ||
12121 | + tristate "The timberdale FPGA I2S driver" | ||
12122 | + depends on MFD_TIMBERDALE && HAS_IOMEM | ||
12123 | + default y | ||
12124 | + help | ||
12125 | + Say Y here to enable driver for the I2S block found within the | ||
12126 | + Timberdale FPGA. | ||
12127 | + There is support for up to 8 I2S channels, in either transmitter | ||
12128 | + or receiver mode. | ||
12129 | + | ||
12130 | + To compile this driver as a module, choose M here: the module | ||
12131 | + will be called snd-timbi2s. | ||
12132 | + | ||
12133 | endif # SND_DRIVERS | ||
12134 | diff -uNr linux-2.6.31/sound/drivers/Makefile linux-2.6.31.new/sound/drivers/Makefile | ||
12135 | --- linux-2.6.31/sound/drivers/Makefile 2009-10-23 11:18:30.000000000 -0700 | ||
12136 | +++ linux-2.6.31.new/sound/drivers/Makefile 2009-10-23 11:16:57.000000000 -0700 | ||
12137 | @@ -10,6 +10,7 @@ | ||
12138 | snd-serial-u16550-objs := serial-u16550.o | ||
12139 | snd-virmidi-objs := virmidi.o | ||
12140 | snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o | ||
12141 | +snd-timbi2s-objs := timbi2s.o | ||
12142 | |||
12143 | # Toplevel Module Dependency | ||
12144 | obj-$(CONFIG_SND_DUMMY) += snd-dummy.o | ||
12145 | @@ -19,5 +20,6 @@ | ||
12146 | obj-$(CONFIG_SND_MTS64) += snd-mts64.o | ||
12147 | obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o | ||
12148 | obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o | ||
12149 | +obj-$(CONFIG_SND_TIMBERDALE_I2S) += snd-timbi2s.o | ||
12150 | |||
12151 | obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ | ||
12152 | diff -uNr linux-2.6.31/sound/drivers/timbi2s.c linux-2.6.31.new/sound/drivers/timbi2s.c | ||
12153 | --- linux-2.6.31/sound/drivers/timbi2s.c 1969-12-31 16:00:00.000000000 -0800 | ||
12154 | +++ linux-2.6.31.new/sound/drivers/timbi2s.c 2009-10-23 11:16:57.000000000 -0700 | ||
12155 | @@ -0,0 +1,755 @@ | ||
12156 | +/* | ||
12157 | + * timbi2s.c timberdale FPGA I2S driver | ||
12158 | + * Copyright (c) 2009 Intel Corporation | ||
12159 | + * | ||
12160 | + * This program is free software; you can redistribute it and/or modify | ||
12161 | + * it under the terms of the GNU General Public License version 2 as | ||
12162 | + * published by the Free Software Foundation. | ||
12163 | + * | ||
12164 | + * This program is distributed in the hope that it will be useful, | ||
12165 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12166 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12167 | + * GNU General Public License for more details. | ||
12168 | + * | ||
12169 | + * You should have received a copy of the GNU General Public License | ||
12170 | + * along with this program; if not, write to the Free Software | ||
12171 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
12172 | + */ | ||
12173 | + | ||
12174 | +/* Supports: | ||
12175 | + * Timberdale FPGA I2S | ||
12176 | + * | ||
12177 | + */ | ||
12178 | + | ||
12179 | +#include <linux/io.h> | ||
12180 | +#include <linux/interrupt.h> | ||
12181 | +#include <linux/platform_device.h> | ||
12182 | +#include <sound/core.h> | ||
12183 | +#include <sound/pcm.h> | ||
12184 | +#include <sound/pcm_params.h> | ||
12185 | +#include <sound/initval.h> | ||
12186 | +#include <sound/timbi2s.h> | ||
12187 | + | ||
12188 | +#define DRIVER_NAME "timb-i2s" | ||
12189 | + | ||
12190 | +#define MAX_BUSSES 8 | ||
12191 | + | ||
12192 | +#define TIMBI2S_REG_VER 0x00 | ||
12193 | +#define TIMBI2S_REG_UIR 0x04 | ||
12194 | + | ||
12195 | +#define TIMBI2S_BUS_PRESCALE 0x00 | ||
12196 | +#define TIMBI2S_BUS_ICLR 0x04 | ||
12197 | +#define TIMBI2S_BUS_IPR 0x08 | ||
12198 | +#define TIMBI2S_BUS_ISR 0x0c | ||
12199 | +#define TIMBI2S_BUS_IER 0x10 | ||
12200 | + | ||
12201 | + | ||
12202 | +#define TIMBI2S_IRQ_TX_FULL 0x01 | ||
12203 | +#define TIMBI2S_IRQ_TX_ALMOST_FULL 0x02 | ||
12204 | +#define TIMBI2S_IRQ_TX_ALMOST_EMPTY 0x04 | ||
12205 | +#define TIMBI2S_IRQ_TX_EMPTY 0x08 | ||
12206 | + | ||
12207 | +#define TIMBI2S_IRQ_RX_FULL 0x10 | ||
12208 | +#define TIMBI2S_IRQ_RX_ALMOST_FULL 0x20 | ||
12209 | +#define TIMBI2S_IRQ_RX_ALMOST_EMPTY 0x40 | ||
12210 | +#define TIMBI2S_IRQ_RX_NOT_EMPTY 0x80 | ||
12211 | + | ||
12212 | +#define TIMBI2S_BUS_ICOR 0x14 | ||
12213 | +#define TIMBI2S_ICOR_TX_ENABLE 0x00000001 | ||
12214 | +#define TIMBI2S_ICOR_RX_ENABLE 0x00000002 | ||
12215 | +#define TIMBI2S_ICOR_LFIFO_RST 0x00000004 | ||
12216 | +#define TIMBI2S_ICOR_RFIFO_RST 0x00000008 | ||
12217 | +#define TIMBI2S_ICOR_FIFO_RST (TIMBI2S_ICOR_LFIFO_RST | TIMBI2S_ICOR_RFIFO_RST) | ||
12218 | +#define TIMBI2S_ICOR_SOFT_RST 0x00000010 | ||
12219 | +#define TIMBI2S_ICOR_WORD_SEL_LEFT_SHIFT 8 | ||
12220 | +#define TIMBI2S_ICOR_WORD_SEL_LEFT_MASK (0xff << 8) | ||
12221 | +#define TIMBI2S_ICOR_WORD_SEL_RIGHT_SHIFT 16 | ||
12222 | +#define TIMBI2S_ICOR_WORD_SEL_RIGHT_MASK (0xff << 16) | ||
12223 | +#define TIMBI2S_ICOR_CLK_MASTER 0x10000000 | ||
12224 | +#define TIMBI2S_ICOR_RX_ID 0x20000000 | ||
12225 | +#define TIMBI2S_ICOR_TX_ID 0x40000000 | ||
12226 | +#define TIMBI2S_ICOR_WORD_SEL 0x80000000 | ||
12227 | +#define TIMBI2S_BUS_FIFO 0x18 | ||
12228 | + | ||
12229 | +#define TIMBI2S_BUS_REG_AREA_SIZE (TIMBI2S_BUS_FIFO - \ | ||
12230 | + TIMBI2S_BUS_PRESCALE + 4) | ||
12231 | +#define TIMBI2S_FIRST_BUS_AREA_OFS 0x08 | ||
12232 | + | ||
12233 | +struct timbi2s_bus { | ||
12234 | + u32 flags; | ||
12235 | + u32 prescale; | ||
12236 | + struct snd_pcm *pcm; | ||
12237 | + struct snd_card *card; | ||
12238 | + struct snd_pcm_substream *substream; | ||
12239 | + unsigned buf_pos; | ||
12240 | + spinlock_t lock; /* mutual exclusion */ | ||
12241 | + u16 sample_rate; | ||
12242 | +}; | ||
12243 | + | ||
12244 | +#define BUS_RX 0x200 | ||
12245 | +#define BUS_MASTER 0x100 | ||
12246 | +#define BUS_INDEX_MASK 0xff | ||
12247 | +#define BUS_INDEX(b) ((b)->flags & BUS_INDEX_MASK) | ||
12248 | +#define BUS_IS_MASTER(b) ((b)->flags & BUS_MASTER) | ||
12249 | +#define BUS_IS_RX(b) ((b)->flags & BUS_RX) | ||
12250 | + | ||
12251 | +#define SET_BUS_INDEX(b, id) ((b)->flags = ((b)->flags & ~BUS_INDEX_MASK) | id) | ||
12252 | +#define SET_BUS_MASTER(b) ((b)->flags |= BUS_MASTER) | ||
12253 | +#define SET_BUS_RX(b) ((b)->flags |= BUS_RX) | ||
12254 | + | ||
12255 | +#define TIMBI2S_BUS_OFFSET(bus) (TIMBI2S_FIRST_BUS_AREA_OFS + \ | ||
12256 | + TIMBI2S_BUS_REG_AREA_SIZE * BUS_INDEX(bus)) | ||
12257 | + | ||
12258 | +struct timbi2s { | ||
12259 | + void __iomem *membase; | ||
12260 | + int irq; | ||
12261 | + struct tasklet_struct tasklet; | ||
12262 | + u32 main_clk; | ||
12263 | + unsigned num_busses; | ||
12264 | + struct timbi2s_bus busses[0]; | ||
12265 | +}; | ||
12266 | + | ||
12267 | +#define BITS_PER_CHANNEL 16 | ||
12268 | +#define NUM_CHANNELS 2 | ||
12269 | + | ||
12270 | +#define SAMPLE_SIZE ((NUM_CHANNELS * BITS_PER_CHANNEL) / 8) | ||
12271 | +#define NUM_PERIODS 32 | ||
12272 | +#define NUM_SAMPLES 256 | ||
12273 | + | ||
12274 | +static struct snd_pcm_hardware timbi2s_rx_hw = { | ||
12275 | + .info = (SNDRV_PCM_INFO_MMAP | ||
12276 | + | SNDRV_PCM_INFO_MMAP_VALID | ||
12277 | + | SNDRV_PCM_INFO_INTERLEAVED), | ||
12278 | + .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
12279 | + .rates = SNDRV_PCM_RATE_44100, | ||
12280 | + .rate_min = 44100, | ||
12281 | + .rate_max = 44100, | ||
12282 | + .channels_min = 2, /* only stereo */ | ||
12283 | + .channels_max = 2, | ||
12284 | + .buffer_bytes_max = NUM_PERIODS * SAMPLE_SIZE * NUM_SAMPLES, | ||
12285 | + .period_bytes_min = SAMPLE_SIZE * NUM_SAMPLES, | ||
12286 | + .period_bytes_max = SAMPLE_SIZE * NUM_SAMPLES, | ||
12287 | + .periods_min = NUM_PERIODS, | ||
12288 | + .periods_max = NUM_PERIODS, | ||
12289 | +}; | ||
12290 | + | ||
12291 | +static struct snd_pcm_hardware timbi2s_tx_hw = { | ||
12292 | + .info = (SNDRV_PCM_INFO_MMAP | ||
12293 | + | SNDRV_PCM_INFO_MMAP_VALID | ||
12294 | + | SNDRV_PCM_INFO_INTERLEAVED), | ||
12295 | + .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
12296 | + .rates = SNDRV_PCM_RATE_8000, | ||
12297 | + .rate_min = 8000, | ||
12298 | + .rate_max = 8000, | ||
12299 | + .channels_min = 2, /* only stereo */ | ||
12300 | + .channels_max = 2, | ||
12301 | + .buffer_bytes_max = NUM_PERIODS * SAMPLE_SIZE * NUM_SAMPLES, | ||
12302 | + .period_bytes_min = SAMPLE_SIZE * NUM_SAMPLES, | ||
12303 | + .period_bytes_max = SAMPLE_SIZE * NUM_SAMPLES, | ||
12304 | + .periods_min = NUM_PERIODS, | ||
12305 | + .periods_max = NUM_PERIODS, | ||
12306 | +}; | ||
12307 | + | ||
12308 | +static inline void timbi2s_bus_write(struct timbi2s_bus *bus, u32 val, u32 reg) | ||
12309 | +{ | ||
12310 | + struct timbi2s *i2s = snd_pcm_chip(bus->card); | ||
12311 | + | ||
12312 | + iowrite32(val, i2s->membase + TIMBI2S_BUS_OFFSET(bus) + reg); | ||
12313 | +} | ||
12314 | + | ||
12315 | +static inline u32 timbi2s_bus_read(struct timbi2s_bus *bus, u32 reg) | ||
12316 | +{ | ||
12317 | + struct timbi2s *i2s = snd_pcm_chip(bus->card); | ||
12318 | + | ||
12319 | + return ioread32(i2s->membase + TIMBI2S_BUS_OFFSET(bus) + reg); | ||
12320 | +} | ||
12321 | + | ||
12322 | +static u32 timbi2s_calc_prescale(u32 main_clk, u32 sample_rate) | ||
12323 | +{ | ||
12324 | + u32 halfbit_rate = sample_rate * BITS_PER_CHANNEL * NUM_CHANNELS * 2; | ||
12325 | + return main_clk / halfbit_rate; | ||
12326 | +} | ||
12327 | + | ||
12328 | +static int timbi2s_open(struct snd_pcm_substream *substream) | ||
12329 | +{ | ||
12330 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12331 | + struct snd_card *card = bus->card; | ||
12332 | + struct snd_pcm_runtime *runtime = substream->runtime; | ||
12333 | + dev_dbg(snd_card_get_device_link(card), | ||
12334 | + "%s: Entry, substream: %p, bus: %d\n", __func__, substream, | ||
12335 | + BUS_INDEX(bus)); | ||
12336 | + | ||
12337 | + if (BUS_IS_RX(bus)) { | ||
12338 | + runtime->hw = timbi2s_rx_hw; | ||
12339 | + if (bus->sample_rate == 8000) { | ||
12340 | + runtime->hw.rates = SNDRV_PCM_RATE_8000; | ||
12341 | + runtime->hw.rate_min = 8000; | ||
12342 | + runtime->hw.rate_max = 8000; | ||
12343 | + } | ||
12344 | + } else | ||
12345 | + runtime->hw = timbi2s_tx_hw; | ||
12346 | + | ||
12347 | + bus->substream = substream; | ||
12348 | + | ||
12349 | + return 0; | ||
12350 | +} | ||
12351 | + | ||
12352 | +static int timbi2s_close(struct snd_pcm_substream *substream) | ||
12353 | +{ | ||
12354 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12355 | + struct snd_card *card = bus->card; | ||
12356 | + dev_dbg(snd_card_get_device_link(card), | ||
12357 | + "%s: Entry, substream: %p, bus: %d\n", __func__, substream, | ||
12358 | + BUS_INDEX(bus)); | ||
12359 | + | ||
12360 | + bus->substream = NULL; | ||
12361 | + | ||
12362 | + return 0; | ||
12363 | +} | ||
12364 | + | ||
12365 | +static int timbi2s_hw_params(struct snd_pcm_substream *substream, | ||
12366 | + struct snd_pcm_hw_params *hw_params) | ||
12367 | +{ | ||
12368 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12369 | + struct snd_card *card = bus->card; | ||
12370 | + struct timbi2s *i2s = snd_pcm_chip(card); | ||
12371 | + int err; | ||
12372 | + | ||
12373 | + dev_dbg(snd_card_get_device_link(card), | ||
12374 | + "%s: Entry, substream: %p, bus: %d\n", __func__, | ||
12375 | + substream, BUS_INDEX(bus)); | ||
12376 | + | ||
12377 | + bus->prescale = timbi2s_calc_prescale(i2s->main_clk, | ||
12378 | + params_rate(hw_params)); | ||
12379 | + | ||
12380 | + err = snd_pcm_lib_malloc_pages(substream, | ||
12381 | + params_buffer_bytes(hw_params)); | ||
12382 | + if (err < 0) | ||
12383 | + return err; | ||
12384 | + | ||
12385 | + dev_dbg(snd_card_get_device_link(card), | ||
12386 | + "%s: Rate: %d, format: %d\n", __func__, params_rate(hw_params), | ||
12387 | + params_format(hw_params)); | ||
12388 | + | ||
12389 | + return 0; | ||
12390 | +} | ||
12391 | + | ||
12392 | +static int timbi2s_hw_free(struct snd_pcm_substream *substream) | ||
12393 | +{ | ||
12394 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12395 | + struct snd_card *card = bus->card; | ||
12396 | + unsigned long flags; | ||
12397 | + | ||
12398 | + dev_dbg(snd_card_get_device_link(card), | ||
12399 | + "%s: Entry, substream: %p\n", __func__, substream); | ||
12400 | + | ||
12401 | + spin_lock_irqsave(&bus->lock, flags); | ||
12402 | + /* disable interrupts */ | ||
12403 | + timbi2s_bus_write(bus, 0, TIMBI2S_BUS_IER); | ||
12404 | + spin_unlock_irqrestore(&bus->lock, flags); | ||
12405 | + | ||
12406 | + /* disable TX and RX */ | ||
12407 | + timbi2s_bus_write(bus, TIMBI2S_ICOR_FIFO_RST | TIMBI2S_ICOR_SOFT_RST, | ||
12408 | + TIMBI2S_BUS_ICOR); | ||
12409 | + | ||
12410 | + return snd_pcm_lib_free_pages(substream); | ||
12411 | +} | ||
12412 | + | ||
12413 | +static int timbi2s_prepare(struct snd_pcm_substream *substream) | ||
12414 | +{ | ||
12415 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12416 | + struct snd_card *card = bus->card; | ||
12417 | + struct snd_pcm_runtime *runtime = substream->runtime; | ||
12418 | + u32 data; | ||
12419 | + | ||
12420 | + dev_dbg(snd_card_get_device_link(card), | ||
12421 | + "%s: Entry, substream: %p, bus: %d, buffer: %d, period: %d\n", | ||
12422 | + __func__, substream, | ||
12423 | + BUS_INDEX(bus), (int)snd_pcm_lib_buffer_bytes(substream), | ||
12424 | + (int)snd_pcm_lib_period_bytes(substream)); | ||
12425 | + | ||
12426 | + if (runtime->dma_addr & 3 || runtime->buffer_size & 3) { | ||
12427 | + dev_err(snd_card_get_device_link(card), | ||
12428 | + "%s: Only word aligned data allowed\n", __func__); | ||
12429 | + return -EINVAL; | ||
12430 | + } | ||
12431 | + | ||
12432 | + if (runtime->channels != NUM_CHANNELS) { | ||
12433 | + dev_err(snd_card_get_device_link(card), | ||
12434 | + "%s: Number of channels unsupported %d\n", __func__, | ||
12435 | + runtime->channels); | ||
12436 | + return -EINVAL; | ||
12437 | + } | ||
12438 | + | ||
12439 | + /* reset */ | ||
12440 | + timbi2s_bus_write(bus, TIMBI2S_ICOR_FIFO_RST | TIMBI2S_ICOR_SOFT_RST, | ||
12441 | + TIMBI2S_BUS_ICOR); | ||
12442 | + | ||
12443 | + /* only masters have prescaling, don't write if not needed */ | ||
12444 | + if (BUS_IS_MASTER(bus)) | ||
12445 | + timbi2s_bus_write(bus, bus->prescale, TIMBI2S_BUS_PRESCALE); | ||
12446 | + | ||
12447 | + /* write word select */ | ||
12448 | + data = ((BITS_PER_CHANNEL << TIMBI2S_ICOR_WORD_SEL_LEFT_SHIFT) & | ||
12449 | + TIMBI2S_ICOR_WORD_SEL_LEFT_MASK) | | ||
12450 | + ((BITS_PER_CHANNEL << TIMBI2S_ICOR_WORD_SEL_RIGHT_SHIFT) & | ||
12451 | + TIMBI2S_ICOR_WORD_SEL_RIGHT_MASK); | ||
12452 | + timbi2s_bus_write(bus, data, TIMBI2S_BUS_ICOR); | ||
12453 | + | ||
12454 | + bus->buf_pos = 0; | ||
12455 | + | ||
12456 | + return 0; | ||
12457 | +} | ||
12458 | + | ||
12459 | +static int | ||
12460 | +timbi2s_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
12461 | +{ | ||
12462 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12463 | + struct snd_card *card = bus->card; | ||
12464 | + unsigned long flags; | ||
12465 | + u32 data; | ||
12466 | + | ||
12467 | + dev_dbg(snd_card_get_device_link(card), | ||
12468 | + "%s: Entry, substream: %p, bus: %d, cmd: %d\n", __func__, | ||
12469 | + substream, BUS_INDEX(bus), cmd); | ||
12470 | + | ||
12471 | + switch (cmd) { | ||
12472 | + case SNDRV_PCM_TRIGGER_START: | ||
12473 | + dev_dbg(snd_card_get_device_link(card), | ||
12474 | + "%s: Got TRIGGER_START command\n", __func__); | ||
12475 | + | ||
12476 | + /* start */ | ||
12477 | + data = timbi2s_bus_read(bus, TIMBI2S_BUS_ICOR); | ||
12478 | + data |= TIMBI2S_ICOR_TX_ENABLE; | ||
12479 | + timbi2s_bus_write(bus, data, TIMBI2S_BUS_ICOR); | ||
12480 | + | ||
12481 | + /* enable interrupts */ | ||
12482 | + timbi2s_bus_write(bus, TIMBI2S_IRQ_TX_ALMOST_EMPTY, | ||
12483 | + TIMBI2S_BUS_IER); | ||
12484 | + dev_dbg(snd_card_get_device_link(card), | ||
12485 | + "%s: ISR: %x, ICOR: %x\n", __func__, | ||
12486 | + timbi2s_bus_read(bus, TIMBI2S_BUS_ISR), | ||
12487 | + timbi2s_bus_read(bus, TIMBI2S_BUS_ICOR)); | ||
12488 | + break; | ||
12489 | + case SNDRV_PCM_TRIGGER_STOP: | ||
12490 | + dev_dbg(snd_card_get_device_link(card), | ||
12491 | + "%s: Got TRIGGER_STOP command\n", __func__); | ||
12492 | + | ||
12493 | + spin_lock_irqsave(&bus->lock, flags); | ||
12494 | + /* disable interrupts */ | ||
12495 | + timbi2s_bus_write(bus, 0, TIMBI2S_BUS_IER); | ||
12496 | + spin_unlock_irqrestore(&bus->lock, flags); | ||
12497 | + | ||
12498 | + /* reset */ | ||
12499 | + data = timbi2s_bus_read(bus, TIMBI2S_BUS_ICOR); | ||
12500 | + data &= ~TIMBI2S_ICOR_TX_ENABLE; | ||
12501 | + | ||
12502 | + timbi2s_bus_write(bus, data, TIMBI2S_BUS_ICOR); | ||
12503 | + break; | ||
12504 | + default: | ||
12505 | + dev_dbg(snd_card_get_device_link(card), | ||
12506 | + "%s: Got unsupported command\n", __func__); | ||
12507 | + | ||
12508 | + return -EINVAL; | ||
12509 | + } | ||
12510 | + | ||
12511 | + return 0; | ||
12512 | +} | ||
12513 | + | ||
12514 | +static int | ||
12515 | +timbi2s_capture_trigger(struct snd_pcm_substream *substream, int cmd) | ||
12516 | +{ | ||
12517 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12518 | + struct snd_card *card = bus->card; | ||
12519 | + unsigned long flags; | ||
12520 | + | ||
12521 | + dev_dbg(snd_card_get_device_link(card), | ||
12522 | + "%s: Entry, substream: %p, bus: %d, cmd: %d\n", __func__, | ||
12523 | + substream, BUS_INDEX(bus), cmd); | ||
12524 | + | ||
12525 | + switch (cmd) { | ||
12526 | + case SNDRV_PCM_TRIGGER_START: | ||
12527 | + dev_dbg(snd_card_get_device_link(card), | ||
12528 | + "%s: Got TRIGGER_START command\n", __func__); | ||
12529 | + | ||
12530 | + timbi2s_bus_write(bus, TIMBI2S_ICOR_RX_ENABLE | | ||
12531 | + TIMBI2S_ICOR_FIFO_RST, TIMBI2S_BUS_ICOR); | ||
12532 | + | ||
12533 | + timbi2s_bus_write(bus, TIMBI2S_IRQ_RX_ALMOST_FULL, | ||
12534 | + TIMBI2S_BUS_IER); | ||
12535 | + break; | ||
12536 | + case SNDRV_PCM_TRIGGER_STOP: | ||
12537 | + dev_dbg(snd_card_get_device_link(card), | ||
12538 | + "%s: Got TRIGGER_STOP command\n", __func__); | ||
12539 | + /* disable interrupts */ | ||
12540 | + spin_lock_irqsave(&bus->lock, flags); | ||
12541 | + timbi2s_bus_write(bus, 0, TIMBI2S_BUS_IER); | ||
12542 | + spin_unlock_irqrestore(&bus->lock, flags); | ||
12543 | + /* Stop RX */ | ||
12544 | + timbi2s_bus_write(bus, 0, TIMBI2S_BUS_ICOR); | ||
12545 | + break; | ||
12546 | + default: | ||
12547 | + dev_dbg(snd_card_get_device_link(card), | ||
12548 | + "%s: Got unsupported command\n", __func__); | ||
12549 | + | ||
12550 | + return -EINVAL; | ||
12551 | + } | ||
12552 | + | ||
12553 | + return 0; | ||
12554 | +} | ||
12555 | + | ||
12556 | +static snd_pcm_uframes_t | ||
12557 | +timbi2s_pointer(struct snd_pcm_substream *substream) | ||
12558 | +{ | ||
12559 | + struct timbi2s_bus *bus = snd_pcm_substream_chip(substream); | ||
12560 | + struct snd_card *card = bus->card; | ||
12561 | + snd_pcm_uframes_t ret; | ||
12562 | + | ||
12563 | + dev_dbg(snd_card_get_device_link(card), | ||
12564 | + "%s: Entry, substream: %p\n", __func__, substream); | ||
12565 | + | ||
12566 | + ret = bytes_to_frames(substream->runtime, bus->buf_pos); | ||
12567 | + if (ret >= substream->runtime->buffer_size) | ||
12568 | + ret -= substream->runtime->buffer_size; | ||
12569 | + | ||
12570 | + return ret; | ||
12571 | +} | ||
12572 | + | ||
12573 | +static struct snd_pcm_ops timbi2s_playback_ops = { | ||
12574 | + .open = timbi2s_open, | ||
12575 | + .close = timbi2s_close, | ||
12576 | + .ioctl = snd_pcm_lib_ioctl, | ||
12577 | + .hw_params = timbi2s_hw_params, | ||
12578 | + .hw_free = timbi2s_hw_free, | ||
12579 | + .prepare = timbi2s_prepare, | ||
12580 | + .trigger = timbi2s_playback_trigger, | ||
12581 | + .pointer = timbi2s_pointer, | ||
12582 | +}; | ||
12583 | + | ||
12584 | +static struct snd_pcm_ops timbi2s_capture_ops = { | ||
12585 | + .open = timbi2s_open, | ||
12586 | + .close = timbi2s_close, | ||
12587 | + .ioctl = snd_pcm_lib_ioctl, | ||
12588 | + .hw_params = timbi2s_hw_params, | ||
12589 | + .hw_free = timbi2s_hw_free, | ||
12590 | + .prepare = timbi2s_prepare, | ||
12591 | + .trigger = timbi2s_capture_trigger, | ||
12592 | + .pointer = timbi2s_pointer, | ||
12593 | +}; | ||
12594 | + | ||
12595 | +static void timbi2s_irq_process_rx(struct timbi2s_bus *bus) | ||
12596 | +{ | ||
12597 | + struct snd_pcm_runtime *runtime = bus->substream->runtime; | ||
12598 | + u32 buffer_size = snd_pcm_lib_buffer_bytes(bus->substream); | ||
12599 | + u32 ipr = timbi2s_bus_read(bus, TIMBI2S_BUS_IPR); | ||
12600 | + int i; | ||
12601 | + | ||
12602 | + dev_dbg(snd_card_get_device_link(bus->card), | ||
12603 | + "%s: Entry, bus: %d, IPR %x\n", __func__, BUS_INDEX(bus), ipr); | ||
12604 | + | ||
12605 | + for (i = 0; i < NUM_SAMPLES; i++) { | ||
12606 | + *(u32 *)(runtime->dma_area + bus->buf_pos) = | ||
12607 | + timbi2s_bus_read(bus, TIMBI2S_BUS_FIFO); | ||
12608 | + bus->buf_pos += SAMPLE_SIZE; | ||
12609 | + bus->buf_pos %= buffer_size; | ||
12610 | + } | ||
12611 | + | ||
12612 | + timbi2s_bus_write(bus, ipr, TIMBI2S_BUS_ICLR); | ||
12613 | + | ||
12614 | + /* inform ALSA that a period was received */ | ||
12615 | + snd_pcm_period_elapsed(bus->substream); | ||
12616 | +} | ||
12617 | + | ||
12618 | +static void timbi2s_irq_process_tx(struct timbi2s_bus *bus) | ||
12619 | +{ | ||
12620 | + struct snd_pcm_runtime *runtime = bus->substream->runtime; | ||
12621 | + u32 buffer_size = snd_pcm_lib_buffer_bytes(bus->substream); | ||
12622 | + u32 ipr = timbi2s_bus_read(bus, TIMBI2S_BUS_IPR); | ||
12623 | + int i; | ||
12624 | + | ||
12625 | + dev_dbg(snd_card_get_device_link(bus->card), | ||
12626 | + "%s: Entry, bus: %d, IPR %x\n", __func__, BUS_INDEX(bus), ipr); | ||
12627 | + | ||
12628 | + for (i = 0; i < NUM_SAMPLES; i++) { | ||
12629 | + timbi2s_bus_write(bus, | ||
12630 | + *(u32 *)(runtime->dma_area + bus->buf_pos), | ||
12631 | + TIMBI2S_BUS_FIFO); | ||
12632 | + bus->buf_pos += SAMPLE_SIZE; | ||
12633 | + bus->buf_pos %= buffer_size; | ||
12634 | + } | ||
12635 | + | ||
12636 | + dev_dbg(snd_card_get_device_link(bus->card), "%s: ISR: %x, ICOR: %x\n", | ||
12637 | + __func__, timbi2s_bus_read(bus, TIMBI2S_BUS_ISR), | ||
12638 | + timbi2s_bus_read(bus, TIMBI2S_BUS_ICOR)); | ||
12639 | + | ||
12640 | + timbi2s_bus_write(bus, ipr, TIMBI2S_BUS_ICLR); | ||
12641 | + | ||
12642 | + /* inform ALSA that a period was received */ | ||
12643 | + snd_pcm_period_elapsed(bus->substream); | ||
12644 | +} | ||
12645 | + | ||
12646 | +static void timbi2s_tasklet(unsigned long arg) | ||
12647 | +{ | ||
12648 | + struct snd_card *card = (struct snd_card *)arg; | ||
12649 | + struct timbi2s *i2s = snd_pcm_chip(card); | ||
12650 | + u32 uir = ioread32(i2s->membase + TIMBI2S_REG_UIR); | ||
12651 | + unsigned i; | ||
12652 | + | ||
12653 | + dev_dbg(snd_card_get_device_link(card), "%s: Entry, UIR %x\n", | ||
12654 | + __func__, uir); | ||
12655 | + | ||
12656 | + for (i = 0; i < i2s->num_busses; i++) | ||
12657 | + if (uir & (1 << i)) { | ||
12658 | + struct timbi2s_bus *bus = i2s->busses + i; | ||
12659 | + if (BUS_IS_RX(bus)) | ||
12660 | + timbi2s_irq_process_rx(bus); | ||
12661 | + else | ||
12662 | + timbi2s_irq_process_tx(bus); | ||
12663 | + } | ||
12664 | + | ||
12665 | + enable_irq(i2s->irq); | ||
12666 | +} | ||
12667 | + | ||
12668 | +static irqreturn_t timbi2s_irq(int irq, void *devid) | ||
12669 | +{ | ||
12670 | + struct timbi2s *i2s = devid; | ||
12671 | + | ||
12672 | + tasklet_schedule(&i2s->tasklet); | ||
12673 | + disable_irq_nosync(i2s->irq); | ||
12674 | + | ||
12675 | + return IRQ_HANDLED; | ||
12676 | +} | ||
12677 | + | ||
12678 | +static int timbi2s_setup_busses(struct snd_card *card, | ||
12679 | + struct platform_device *pdev) | ||
12680 | +{ | ||
12681 | + const struct timbi2s_platform_data *pdata = pdev->dev.platform_data; | ||
12682 | + unsigned i; | ||
12683 | + | ||
12684 | + dev_dbg(&pdev->dev, "%s: Entry, no busses: %d, busses: %p\n", __func__, | ||
12685 | + pdata->num_busses, pdata->busses); | ||
12686 | + | ||
12687 | + for (i = 0; i < pdata->num_busses; i++) { | ||
12688 | + int capture = pdata->busses[i].rx; | ||
12689 | + int err; | ||
12690 | + u32 ctl; | ||
12691 | + struct timbi2s *i2s = snd_pcm_chip(card); | ||
12692 | + struct timbi2s_bus *bus = i2s->busses + i; | ||
12693 | + | ||
12694 | + dev_dbg(&pdev->dev, "%s: Setting up bus: %d\n", __func__, i); | ||
12695 | + | ||
12696 | + SET_BUS_INDEX(bus, i); | ||
12697 | + bus->sample_rate = pdata->busses[i].sample_rate; | ||
12698 | + bus->card = card; | ||
12699 | + /* prescaling only applies to master busses, we use the | ||
12700 | + * knowledge of that to identify the direction later | ||
12701 | + * eg, bus->prescale != 0 -> master bus | ||
12702 | + */ | ||
12703 | + if (capture) | ||
12704 | + SET_BUS_RX(bus); | ||
12705 | + | ||
12706 | + spin_lock_init(&bus->lock); | ||
12707 | + | ||
12708 | + if (bus->sample_rate != 44100 && bus->sample_rate != 8000) { | ||
12709 | + dev_err(&pdev->dev, | ||
12710 | + "Unsupported bitrate: %d\n", bus->sample_rate); | ||
12711 | + return -EINVAL; | ||
12712 | + } | ||
12713 | + | ||
12714 | + dev_dbg(&pdev->dev, "%s: Will check HW direction on bus: %d\n", | ||
12715 | + __func__, BUS_INDEX(bus)); | ||
12716 | + | ||
12717 | + /* check that the HW agrees with the direction */ | ||
12718 | + ctl = timbi2s_bus_read(bus, TIMBI2S_BUS_ICOR); | ||
12719 | + if ((capture && !(ctl & TIMBI2S_ICOR_RX_ID)) || | ||
12720 | + (!capture && !(ctl & TIMBI2S_ICOR_TX_ID))) { | ||
12721 | + dev_dbg(&pdev->dev, | ||
12722 | + "HW and platform data disagree on direction\n"); | ||
12723 | + return -EINVAL; | ||
12724 | + } | ||
12725 | + | ||
12726 | + dev_dbg(&pdev->dev, "%s: Will create PCM channel for bus: %d\n", | ||
12727 | + __func__, BUS_INDEX(bus)); | ||
12728 | + err = snd_pcm_new(card, card->shortname, i, !capture, | ||
12729 | + capture, &bus->pcm); | ||
12730 | + if (err) { | ||
12731 | + dev_dbg(&pdev->dev, "%s, Failed to create pcm: %d\n", | ||
12732 | + __func__, err); | ||
12733 | + return err; | ||
12734 | + } | ||
12735 | + | ||
12736 | + if (capture) | ||
12737 | + snd_pcm_set_ops(bus->pcm, SNDRV_PCM_STREAM_CAPTURE, | ||
12738 | + &timbi2s_capture_ops); | ||
12739 | + if (!capture) | ||
12740 | + snd_pcm_set_ops(bus->pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
12741 | + &timbi2s_playback_ops); | ||
12742 | + | ||
12743 | + dev_dbg(&pdev->dev, "%s: Will preallocate buffers to bus: %d\n", | ||
12744 | + __func__, BUS_INDEX(bus)); | ||
12745 | + | ||
12746 | + err = snd_pcm_lib_preallocate_pages_for_all(bus->pcm, | ||
12747 | + SNDRV_DMA_TYPE_CONTINUOUS, | ||
12748 | + snd_dma_continuous_data(GFP_KERNEL), | ||
12749 | + NUM_SAMPLES * NUM_PERIODS * SAMPLE_SIZE * 2, | ||
12750 | + NUM_SAMPLES * NUM_PERIODS * SAMPLE_SIZE * 2); | ||
12751 | + if (err) { | ||
12752 | + dev_dbg(&pdev->dev, "%s, Failed to create pcm: %d\n", | ||
12753 | + __func__, err); | ||
12754 | + | ||
12755 | + return err; | ||
12756 | + } | ||
12757 | + | ||
12758 | + bus->pcm->private_data = bus; | ||
12759 | + bus->pcm->info_flags = 0; | ||
12760 | + strcpy(bus->pcm->name, card->shortname); | ||
12761 | + i2s->num_busses++; | ||
12762 | + } | ||
12763 | + | ||
12764 | + return 0; | ||
12765 | +} | ||
12766 | + | ||
12767 | +static int __devinit timbi2s_probe(struct platform_device *pdev) | ||
12768 | +{ | ||
12769 | + int err; | ||
12770 | + int irq; | ||
12771 | + struct timbi2s *i2s; | ||
12772 | + struct resource *iomem; | ||
12773 | + const struct timbi2s_platform_data *pdata = pdev->dev.platform_data; | ||
12774 | + struct snd_card *card; | ||
12775 | + u32 ver; | ||
12776 | + | ||
12777 | + if (!pdata) { | ||
12778 | + err = -ENODEV; | ||
12779 | + goto out; | ||
12780 | + } | ||
12781 | + | ||
12782 | + if (pdata->num_busses > MAX_BUSSES) { | ||
12783 | + err = -EINVAL; | ||
12784 | + goto out; | ||
12785 | + } | ||
12786 | + | ||
12787 | + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
12788 | + if (!iomem) { | ||
12789 | + err = -ENODEV; | ||
12790 | + goto out; | ||
12791 | + } | ||
12792 | + | ||
12793 | + irq = platform_get_irq(pdev, 0); | ||
12794 | + if (irq < 0) { | ||
12795 | + err = -ENODEV; | ||
12796 | + goto out; | ||
12797 | + } | ||
12798 | + | ||
12799 | + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | ||
12800 | + THIS_MODULE, sizeof(struct timbi2s) + | ||
12801 | + sizeof(struct timbi2s_bus) * pdata->num_busses, &card); | ||
12802 | + if (err) | ||
12803 | + goto out; | ||
12804 | + | ||
12805 | + strcpy(card->driver, "Timberdale I2S"); | ||
12806 | + strcpy(card->shortname, "Timberdale I2S"); | ||
12807 | + sprintf(card->longname, "Timberdale I2S Driver"); | ||
12808 | + | ||
12809 | + snd_card_set_dev(card, &pdev->dev); | ||
12810 | + | ||
12811 | + i2s = snd_pcm_chip(card); | ||
12812 | + | ||
12813 | + if (!request_mem_region(iomem->start, resource_size(iomem), | ||
12814 | + DRIVER_NAME)) { | ||
12815 | + err = -EBUSY; | ||
12816 | + goto err_region; | ||
12817 | + } | ||
12818 | + | ||
12819 | + i2s->membase = ioremap(iomem->start, resource_size(iomem)); | ||
12820 | + if (!i2s->membase) { | ||
12821 | + err = -ENOMEM; | ||
12822 | + goto err_ioremap; | ||
12823 | + } | ||
12824 | + | ||
12825 | + err = timbi2s_setup_busses(card, pdev); | ||
12826 | + if (err) | ||
12827 | + goto err_setup; | ||
12828 | + | ||
12829 | + tasklet_init(&i2s->tasklet, timbi2s_tasklet, (unsigned long)card); | ||
12830 | + i2s->irq = irq; | ||
12831 | + i2s->main_clk = pdata->main_clk; | ||
12832 | + | ||
12833 | + err = request_irq(irq, timbi2s_irq, 0, DRIVER_NAME, i2s); | ||
12834 | + if (err) | ||
12835 | + goto err_request_irq; | ||
12836 | + | ||
12837 | + err = snd_card_register(card); | ||
12838 | + if (err) | ||
12839 | + goto err_register; | ||
12840 | + | ||
12841 | + platform_set_drvdata(pdev, card); | ||
12842 | + | ||
12843 | + ver = ioread32(i2s->membase + TIMBI2S_REG_VER); | ||
12844 | + | ||
12845 | + printk(KERN_INFO | ||
12846 | + "Driver for Timberdale I2S (ver: %d.%d) successfully probed.\n", | ||
12847 | + ver >> 16 , ver & 0xffff); | ||
12848 | + | ||
12849 | + return 0; | ||
12850 | + | ||
12851 | +err_register: | ||
12852 | + free_irq(irq, card); | ||
12853 | +err_request_irq: | ||
12854 | +err_setup: | ||
12855 | + iounmap(i2s->membase); | ||
12856 | +err_ioremap: | ||
12857 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
12858 | +err_region: | ||
12859 | + snd_card_free(card); | ||
12860 | +out: | ||
12861 | + printk(KERN_ERR DRIVER_NAME": Failed to register: %d\n", err); | ||
12862 | + | ||
12863 | + return err; | ||
12864 | +} | ||
12865 | + | ||
12866 | +static int __devexit timbi2s_remove(struct platform_device *pdev) | ||
12867 | +{ | ||
12868 | + struct snd_card *card = platform_get_drvdata(pdev); | ||
12869 | + struct timbi2s *i2s = snd_pcm_chip(card); | ||
12870 | + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
12871 | + | ||
12872 | + tasklet_kill(&i2s->tasklet); | ||
12873 | + free_irq(i2s->irq, i2s); | ||
12874 | + | ||
12875 | + iounmap(i2s->membase); | ||
12876 | + release_mem_region(iomem->start, resource_size(iomem)); | ||
12877 | + snd_card_free(card); | ||
12878 | + | ||
12879 | + platform_set_drvdata(pdev, 0); | ||
12880 | + return 0; | ||
12881 | +} | ||
12882 | + | ||
12883 | +static struct platform_driver timbi2s_platform_driver = { | ||
12884 | + .driver = { | ||
12885 | + .name = DRIVER_NAME, | ||
12886 | + .owner = THIS_MODULE, | ||
12887 | + }, | ||
12888 | + .probe = timbi2s_probe, | ||
12889 | + .remove = __devexit_p(timbi2s_remove), | ||
12890 | +}; | ||
12891 | + | ||
12892 | +/*--------------------------------------------------------------------------*/ | ||
12893 | + | ||
12894 | +static int __init timbi2s_init(void) | ||
12895 | +{ | ||
12896 | + return platform_driver_register(&timbi2s_platform_driver); | ||
12897 | +} | ||
12898 | + | ||
12899 | +static void __exit timbi2s_exit(void) | ||
12900 | +{ | ||
12901 | + platform_driver_unregister(&timbi2s_platform_driver); | ||
12902 | +} | ||
12903 | + | ||
12904 | +module_init(timbi2s_init); | ||
12905 | +module_exit(timbi2s_exit); | ||
12906 | + | ||
12907 | +MODULE_ALIAS("platform:"DRIVER_NAME); | ||
12908 | +MODULE_DESCRIPTION("Timberdale I2S bus driver"); | ||
12909 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | ||
12910 | +MODULE_LICENSE("GPL v2"); | ||