summaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux/acern30/wingel-hacking.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux/acern30/wingel-hacking.patch')
-rw-r--r--recipes/linux/linux/acern30/wingel-hacking.patch1146
1 files changed, 1146 insertions, 0 deletions
diff --git a/recipes/linux/linux/acern30/wingel-hacking.patch b/recipes/linux/linux/acern30/wingel-hacking.patch
new file mode 100644
index 0000000000..e87de5a460
--- /dev/null
+++ b/recipes/linux/linux/acern30/wingel-hacking.patch
@@ -0,0 +1,1146 @@
+This patch contains some hacks that I'm playing around with right now.
+
+This isn't anything that should be merged into the official kernel.
+
+Index: linux-2.6.14/arch/arm/mach-s3c2410/mach-n30.c
+===================================================================
+--- linux-2.6.14.orig/arch/arm/mach-s3c2410/mach-n30.c
++++ linux-2.6.14/arch/arm/mach-s3c2410/mach-n30.c
+@@ -22,6 +22,12 @@
+ * published by the Free Software Foundation.
+ */
+
++// #define USBHACK 1
++#define SPIHACK 1
++
++void s3c2410_capture_regs(void);
++void s3c2410_dump_regs(void);
++
+ #include <linux/kernel.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+@@ -272,14 +278,18 @@ static struct s3c2410_button n35_buttons
+
+ { IRQ_EINT13, S3C2410_GPG5, S3C2410_GPG5_EINT13, KEY_LEFT,
+ "Left_arrow", 0 },
++#ifndef SPIHACK
+ { IRQ_EINT14, S3C2410_GPG6, S3C2410_GPG6_EINT14, KEY_RIGHT,
+ "Right_arrow", 0 },
++#endif
+ { IRQ_EINT17, S3C2410_GPG9, S3C2410_GPG9_EINT17, KEY_UP,
+ "Up_arrow", 0 },
+ { IRQ_EINT16, S3C2410_GPG8, S3C2410_GPG8_EINT16, KEY_DOWN,
+ "Down_arrow", 0 },
++#ifndef SPIHACK
+ { IRQ_EINT15, S3C2410_GPG7, S3C2410_GPG7_EINT15, KEY_ENTER,
+ "Select", 0 },
++#endif
+
+ { IRQ_EINT7, S3C2410_GPF7, S3C2410_GPF7_EINT7, KEY_HOMEPAGE,
+ "Home", 0 },
+@@ -367,7 +377,6 @@ static struct platform_device *n30_devic
+ &s3c_device_usbgadget,
+ &s3c_device_sdi,
+ &s3c_device_nand,
+- &s3c_device_spi1,
+ };
+
+ static struct s3c2410_platform_i2c n30_i2ccfg = {
+@@ -382,6 +391,27 @@ static struct s3c24xx_board n30_board __
+ .devices_count = ARRAY_SIZE(n30_devices)
+ };
+
++static struct platform_device *n35_devices[] __initdata = {
++ &s3c_device_lcd,
++ &s3c_device_bl,
++ &s3c_device_ts,
++ &s3c_device_buttons,
++ &s3c_device_wdt,
++ &s3c_device_i2c,
++ &s3c_device_iis,
++ &s3c_device_usbgadget,
++ &s3c_device_sdi,
++ &s3c_device_nand,
++#ifdef SPIHACK
++ &s3c_device_spi1,
++#endif
++};
++
++static struct s3c24xx_board n35_board __initdata = {
++ .devices = n35_devices,
++ .devices_count = ARRAY_SIZE(n35_devices)
++};
++
+ /* Lots of hardcoded stuff, but it sets up the hardware in a useful
+ * state so that we can boot Linux directly from flash. */
+ static void __init n30_hwinit(void)
+@@ -674,7 +704,10 @@ static void __init n30_map_io(void)
+ n30_hwinit();
+ s3c24xx_init_clocks(0);
+ s3c24xx_init_uarts(n30_uartcfgs, ARRAY_SIZE(n30_uartcfgs));
+- s3c24xx_set_board(&n30_board);
++ if (machine_is_n30())
++ s3c24xx_set_board(&n30_board);
++ if (machine_is_n35())
++ s3c24xx_set_board(&n35_board);
+ }
+
+ static void __init n30_init_irq(void)
+@@ -710,90 +743,6 @@ static int n30_usbstart_thread(void *unu
+ return 0;
+ }
+
+-static int spi_thread(void *regs)
+-{
+- unsigned sptdat1 = ~0, sprdat1 = ~0, spsta1 = ~0;
+- unsigned value;
+-
+- writel(0x01, regs + S3C2410_SPCON);
+- writel(0x02, regs + S3C2410_SPPIN);
+-
+- s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_SPIMOSI1);
+- s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1);
+-
+- s3c2410_gpio_cfgpin(S3C2410_GPG3, 0x3 << 6);
+-
+- printk("GPGCON=0x%x\n", readl(S3C2410_GPGCON));
+-
+- msleep(10);
+-
+- while (1) {
+- value = readl(regs + S3C2410_SPTDAT);
+- if (sptdat1 != value) {
+- printk(KERN_INFO "SPTDAT1=0x%x\n", value);
+- sptdat1 = value;
+- }
+- value = readl(regs + S3C2410_SPRDAT);
+- if (sprdat1 != value) {
+- printk(KERN_INFO "SPRDAT1=0x%x\n", value);
+- sprdat1 = value;
+- }
+- value = readl(regs + S3C2410_SPSTA);
+- if (spsta1 != value) {
+- printk(KERN_INFO "SPSTA1=0x%x\n", value);
+- spsta1 = value;
+- }
+-
+- msleep(10);
+- }
+-}
+-
+-static int s3c24xx_spi_probe(struct device *dev)
+-{
+- struct platform_device *pdev = to_platform_device(dev);
+- struct resource *res;
+- int ret;
+- void *regs;
+- struct resource *ioarea;
+-
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- if (res == NULL) {
+- dev_err(dev, "cannot find IO resource\n");
+- ret = -ENOENT;
+- goto out;
+- }
+-
+- ioarea = request_mem_region(res->start, (res->end-res->start)+1,
+- pdev->name);
+- if (ioarea == NULL) {
+- dev_err(dev, "cannot request IO\n");
+- ret = -ENXIO;
+- goto out;
+- }
+-
+- regs = ioremap(res->start, (res->end-res->start)+1);
+- if (regs == NULL) {
+- dev_err(dev, "cannot map IO\n");
+- ret = -ENXIO;
+- goto out;
+- }
+-
+- dev_info(dev, "registers %p (%p, %p)\n", regs, ioarea, res);
+-
+- kthread_run(spi_thread, regs, "spi_debug");
+-
+- ret = 0;
+-
+- out:
+- return ret;
+-}
+-
+-static struct device_driver s3c24xx_spi_driver = {
+- .name = "s3c2410-spi",
+- .bus = &platform_bus_type,
+- .probe = s3c24xx_spi_probe,
+-};
+-
+ #ifdef CONFIG_APM
+ static void n30_get_power_status(struct apm_power_info *info)
+ {
+@@ -825,6 +774,128 @@ static void n30_get_power_status(struct
+ }
+ #endif
+
++#define DEBUG_GPIO 1
++
++#ifdef DEBUG_GPIO
++static int n30_debug_thread(void *unused)
++{
++ int i;
++#define PIN(x) { x, #x }
++ static struct pin {
++ unsigned int pin;
++ const char *name;
++ } pins[] = {
++ PIN(S3C2410_GPA12),
++ PIN(S3C2410_GPB2),
++ PIN(S3C2410_GPB4),
++ PIN(S3C2410_GPB5),
++ PIN(S3C2410_GPB6),
++ PIN(S3C2410_GPB7),
++ PIN(S3C2410_GPC0),
++ PIN(S3C2410_GPC3),
++ PIN(S3C2410_GPC6),
++ PIN(S3C2410_GPC7),
++ PIN(S3C2410_GPC8),
++ PIN(S3C2410_GPD0),
++ PIN(S3C2410_GPD8),
++ PIN(S3C2410_GPD9),
++ PIN(S3C2410_GPD10),
++ PIN(S3C2410_GPE11),
++ PIN(S3C2410_GPE12),
++ PIN(S3C2410_GPE13),
++ PIN(S3C2410_GPF0),
++ PIN(S3C2410_GPF1),
++ PIN(S3C2410_GPF2),
++ PIN(S3C2410_GPF3),
++ PIN(S3C2410_GPG0),
++ PIN(S3C2410_GPG1),
++ PIN(S3C2410_GPG2),
++ PIN(S3C2410_GPG3),
++ PIN(S3C2410_GPG4),
++ PIN(S3C2410_GPH8),
++ };
++#undef PIN
++ enum { PIN_CNT = (sizeof(pins) / sizeof(struct pin)) };
++ static int values[PIN_CNT];
++ static int changes[PIN_CNT];
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1 * HZ);
++
++// s3c2410_dump_regs();
++
++ printk("AFTER\n");
++
++ s3c2410_capture_regs();
++
++// s3c2410_dump_regs();
++
++ for (i = 0; i < PIN_CNT; i++)
++ values[i] = s3c2410_gpio_getpin(pins[i].pin) ? 1 : 0;
++
++ printk("MISCCR=0x%08x\n", readl(S3C2410_MISCCR));
++
++ while (!kthread_should_stop()) {
++ for (i = 0; i < PIN_CNT; i++) {
++ int value = s3c2410_gpio_getpin(pins[i].pin) ? 1 : 0;
++
++ if (values[i] != value && changes[i] < 100) {
++ changes[i]++;
++ values[i] = value;
++ printk(KERN_CRIT "%s: %d (%d)\n", pins[i].name, value, changes[i]);
++ }
++ }
++
++ if (0) {
++ s3c2410_gpio_pullup(S3C2410_GPE15, 1);
++ s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_OUTP);
++ s3c2410_gpio_setpin(S3C2410_GPE15, !s3c2410_gpio_getpin(S3C2410_GPE15));
++ }
++
++ if (0)
++ s3c2410_gpio_setpin(S3C2410_GPB3, !s3c2410_gpio_getpin(S3C2410_GPB3));
++
++ if (1) {
++ static int last_f0;
++ int f0 = s3c2410_gpio_getpin(S3C2410_GPF0);
++
++ if (last_f0 != f0) {
++ last_f0 = f0;
++ if (!f0) {
++ int r;
++ printk("entering suspend\n");
++ r = pm_suspend(PM_SUSPEND_MEM);
++ printk("after suspend %d\n", r);
++ }
++ }
++ }
++
++
++ if (0) {
++ static int last_f6;
++ int f6 = s3c2410_gpio_getpin(S3C2410_GPF6);
++
++ if (last_f6 != f6) {
++ last_f6 = f6;
++ if (!f6) {
++ if (s3c2410_gpio_getcfg(S3C2410_GPB6) != S3C2410_GPB6_OUTP) {
++ printk("B6 configured as output");
++ s3c2410_gpio_cfgpin(S3C2410_GPB6, S3C2410_GPB6_OUTP);
++ } else {
++ printk("B6 configured as input");
++ s3c2410_gpio_cfgpin(S3C2410_GPB6, S3C2410_GPB6_INP);
++ }
++ }
++ }
++ }
++
++ msleep(10);
++ }
++
++ return 0;
++}
++#endif
++
+ static void __init n30_init(void)
+ {
+ s3c24xx_fb_set_platdata(&n30_lcdcfg);
+@@ -846,9 +917,6 @@ static void __init n30_init(void)
+ memcpy_toio(N30_RESUME_VA, (void *)n30_resume,
+ &n30_resume_end - (void *)n30_resume);
+
+- if (driver_register(&s3c24xx_spi_driver) < 0)
+- printk(KERN_ERR "failed to register spi driver\n");
+-
+ /* Clear any locks and write protects on the flash. */
+ s3c2410_gpio_setpin(S3C2410_GPC5, 1);
+ msleep(1);
+@@ -861,6 +929,8 @@ static void __init n30_init(void)
+ #ifdef CONFIG_APM
+ apm_get_power_status = n30_get_power_status;
+ #endif
++
++ kthread_run(n30_debug_thread, NULL, "n30_debug");
+ }
+
+ MACHINE_START(N30, "Acer-N30")
+Index: linux-2.6.14/arch/arm/mach-s3c2410/Makefile
+===================================================================
+--- linux-2.6.14.orig/arch/arm/mach-s3c2410/Makefile
++++ linux-2.6.14/arch/arm/mach-s3c2410/Makefile
+@@ -46,3 +46,5 @@ obj-$(CONFIG_MACH_NEXCODER_2440) += mach
+ obj-y += gpio-sysfs.o
+ obj-y += regdump.o
+
++obj-m += spi-int.o
++obj-m += spi-dma.o
+Index: linux-2.6.14/arch/arm/mach-s3c2410/spi-int.c
+===================================================================
+--- /dev/null
++++ linux-2.6.14/arch/arm/mach-s3c2410/spi-int.c
+@@ -0,0 +1,261 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/kthread.h>
++#include <linux/delay.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/hardware/clock.h>
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-spi.h>
++#include <asm/arch/regs-irq.h>
++
++struct spi_info {
++ void *regs;
++ struct device *dev;
++ struct clk *clk;
++ struct resource *ioarea;
++ struct resource *irq;
++ struct task_struct *debug_thread;
++};
++
++static int spi_debug_thread(void *data)
++{
++ struct spi_info *info = data;
++ unsigned sptdat1 = ~0, sprdat1 = ~0, spsta1 = ~0;
++ unsigned value;
++
++ while (!kthread_should_stop()) {
++ if (1) {
++ value = readl(info->regs + S3C2410_SPTDAT);
++ if (sptdat1 != value) {
++ printk(KERN_INFO "SPTDAT1=0x%x\n", value);
++ sptdat1 = value;
++ }
++ value = readl(info->regs + S3C2410_SPRDAT);
++ if (sprdat1 != value) {
++ printk(KERN_INFO "SPRDAT1=0x%x\n", value);
++ sprdat1 = value;
++ }
++ value = readl(info->regs + S3C2410_SPSTA);
++ if (spsta1 != value) {
++ printk("SRCPND=0x%08x\n", readl(S3C2410_SRCPND));
++ printk("INTMOD=0x%08x\n", readl(S3C2410_INTMOD));
++ printk("INTMSK=0x%08x\n", readl(S3C2410_INTMSK));
++ printk("INTPND=0x%08x\n", readl(S3C2410_INTPND));
++
++ printk(KERN_INFO "SPSTA1=0x%x\n", value);
++ spsta1 = value;
++ }
++ }
++ if (1) {
++ if (readl(info->regs + S3C2410_SPSTA) & S3C2410_SPSTA_READY) {
++ value = readl(info->regs + S3C2410_SPRDAT);
++ printk("DAT=0x%02x\n", value);
++ writel(1, info->regs + S3C2410_SPTDAT);
++ }
++ }
++
++ msleep(1);
++ }
++
++ return 0;
++}
++
++static irqreturn_t spi_int_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++ struct spi_info *info = dev_id;
++ unsigned long status;
++
++ status = readl(info->regs + S3C2410_SPSTA);
++
++ if (status & S3C2410_SPSTA_READY) {
++ printk("INT %02x\n", readb(info->regs + S3C2410_SPRDAT));
++ writeb(0xff, info->regs + S3C2410_SPTDAT);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static void spi_int_free(struct spi_info *info)
++{
++ if (!info)
++ return;
++
++ if (info->debug_thread)
++ kthread_stop(info->debug_thread);
++ if (info->irq)
++ free_irq(info->irq->start, info);
++ if (info->regs)
++ iounmap(info->regs);
++ if (info->ioarea) {
++ release_resource(info->ioarea);
++ kfree(info->ioarea);
++ }
++ if (info->clk) {
++ clk_unuse(info->clk);
++ clk_disable(info->clk);
++ }
++ kfree(info);
++}
++
++static int spi_int_probe(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ int ret;
++ struct spi_info *info;
++ struct resource *res;
++
++ printk("spi_int_probe\n");
++
++ info = kmalloc(sizeof(struct spi_info), GFP_KERNEL);
++ if (!info) {
++ dev_err(dev, "failed to allocate info structure\n");
++ ret = -ENOMEM;
++ goto out;
++ }
++ memset(info, 0, sizeof(*info));
++ info->dev = dev;
++
++ dev_info(dev, "got info %p\n", info);
++
++ info->clk = clk_get(dev, "spi");
++ if (IS_ERR(info->clk)) {
++ dev_err(dev, "clk_get failed\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ dev_dbg(dev, "clk %p\n", info->clk);
++
++ clk_use(info->clk);
++ clk_enable(info->clk);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ dev_err(dev, "unable to find registers\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ dev_info(dev, "got res %p\n", res);
++
++ info->ioarea = request_mem_region(res->start,
++ (res->end-res->start)+1,
++ pdev->name);
++ if (!info->ioarea) {
++ dev_err(dev, "request_mem_region failed\n");
++ ret = -ENXIO;
++ goto out;
++ }
++
++ dev_info(dev, "got ioarea %p\n", info->ioarea);
++
++ info->regs = ioremap(res->start, (res->end-res->start)+1);
++ if (!info->regs) {
++ dev_err(dev, "ioremap failed\n");
++ ret = -ENXIO;
++ goto out;
++ }
++
++ dev_info(dev, "got regs %p\n", info->regs);
++
++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
++ if (!res) {
++ dev_err(dev, "unable to find irq\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ writel(S3C2410_SPCON_SMOD_INT /* | S3C2410_SPCON_TAGD */,
++ info->regs + S3C2410_SPCON);
++ writel(S3C2410_SPPIN_RESERVED, info->regs + S3C2410_SPPIN);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPG5_SPIMISO1);
++ s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_SPIMOSI1);
++ s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPG3, 0x3 << 6);
++
++ ret = request_irq(res->start, spi_int_irq, 0, pdev->name, info);
++ if (ret) {
++ dev_err(dev, "request_irq failed\n");
++ goto out;
++ }
++ info->irq = res;
++
++ dev_info(dev, "got irq %ld\n", info->irq->start);
++
++ dev_info(dev, "SPI driver active\n");
++
++ dev_set_drvdata(dev, info);
++ ret = 0;
++
++ writeb(0x00, info->regs + S3C2410_SPTDAT);
++
++ if (0) {
++ info->debug_thread = kthread_run(spi_debug_thread, info,
++ "spi_debug_thread");
++ }
++
++ printk("SRCPND=0x%08x\n", readl(S3C2410_SRCPND));
++ printk("INTMOD=0x%08x\n", readl(S3C2410_INTMOD));
++ printk("INTMSK=0x%08x\n", readl(S3C2410_INTMSK));
++ printk("INTPND=0x%08x\n", readl(S3C2410_INTPND));
++ printk("EINTMASK=0x%08x\n", readl(S3C2410_EINTMASK));
++
++ printk("SPCON=0x%08x\n", readl(info->regs + S3C2410_SPCON));
++ __raw_writel(S3C2410_SPCON_SMOD_INT | S3C2410_SPCON_TAGD,
++ info->regs + S3C2410_SPCON);
++ printk("SPCON=0x%08x\n", readl(info->regs + S3C2410_SPCON));
++ printk("FOO=0x%08x\n", S3C2410_SPCON_SMOD_INT);
++
++ out:
++ if (ret)
++ spi_int_free(info);
++
++ return ret;
++}
++
++static int spi_int_remove(struct device *dev)
++{
++ struct spi_info *info = dev_get_drvdata(dev);
++
++ dev_set_drvdata(dev, NULL);
++
++ spi_int_free(info);
++
++ return 0;
++}
++
++static struct device_driver spi_int_driver = {
++ .name = "s3c2410-spi",
++ .bus = &platform_bus_type,
++ .probe = spi_int_probe,
++ .remove = spi_int_remove,
++};
++
++static int __init spi_int_init(void)
++{
++ printk("SPI interrput driver loaded\n");
++
++ return driver_register(&spi_int_driver);
++}
++
++static void __exit spi_int_exit(void)
++{
++ driver_unregister(&spi_int_driver);
++}
++
++module_init(spi_int_init);
++module_exit(spi_int_exit);
++
++MODULE_DESCRIPTION("S3C2410 interrupt driven SPI driver");
++MODULE_AUTHOR("Christer Weinigel <christer@weinigel.se>");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.14/arch/arm/mach-s3c2410/devs.c
+===================================================================
+--- linux-2.6.14.orig/arch/arm/mach-s3c2410/devs.c
++++ linux-2.6.14/arch/arm/mach-s3c2410/devs.c
+@@ -400,11 +400,17 @@ static struct resource s3c_spi0_resource
+
+ };
+
++static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
++
+ struct platform_device s3c_device_spi0 = {
+ .name = "s3c2410-spi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c_spi0_resource),
+ .resource = s3c_spi0_resource,
++ .dev = {
++ .dma_mask = &s3c_device_spi0_dmamask,
++ .coherent_dma_mask = 0xffffffffUL
++ }
+ };
+
+ EXPORT_SYMBOL(s3c_device_spi0);
+@@ -425,11 +431,17 @@ static struct resource s3c_spi1_resource
+
+ };
+
++static u64 s3c_device_spi1_dmamask = 0xffffffffUL;
++
+ struct platform_device s3c_device_spi1 = {
+ .name = "s3c2410-spi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(s3c_spi1_resource),
+ .resource = s3c_spi1_resource,
++ .dev = {
++ .dma_mask = &s3c_device_spi1_dmamask,
++ .coherent_dma_mask = 0xffffffffUL
++ }
+ };
+
+ EXPORT_SYMBOL(s3c_device_spi1);
+Index: linux-2.6.14/arch/arm/mach-s3c2410/spi-dma.c
+===================================================================
+--- /dev/null
++++ linux-2.6.14/arch/arm/mach-s3c2410/spi-dma.c
+@@ -0,0 +1,498 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/kthread.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/spinlock.h>
++
++#include <asm/uaccess.h>
++#include <asm/dma.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/hardware/clock.h>
++
++#include <asm/arch/dma.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-spi.h>
++
++#define BUFFER_SIZE (1024*1024)
++#define CHUNK_SIZE (64*1024)
++#define NUM_CHUNKS (BUFFER_SIZE / CHUNK_SIZE)
++
++static char spi_dma_name[] = "s3c2410-spi-dma-reader";
++
++static struct s3c2410_dma_client spi_dma_client = {
++ .name = spi_dma_name,
++};
++
++struct spi_dma_state {
++ struct spi_dma_info *info;
++ int status; /* 0=inactive, 1=running, <0 = -errno */
++ unsigned tail;
++};
++
++struct spi_chunk {
++ int index;
++ dma_addr_t handle;
++ struct spi_dma_info *info;
++};
++
++struct spi_dma_info {
++ void *cpu_addr;
++ dma_addr_t handle;
++ size_t size;
++
++ spinlock_t head_lock;
++ unsigned head;
++ wait_queue_head_t wait_q;
++
++ void *regs;
++
++ struct device *dev;
++ struct clk *clk;
++ struct resource *iomem;
++ int major;
++ dmach_t dmach;
++ int dcon;
++ int flushing;
++ char have_dma;
++ struct spi_chunk chunks[NUM_CHUNKS];
++};
++
++static void spi_queue_dma(struct spi_chunk *chunk)
++{
++ s3c2410_dma_enqueue(chunk->info->dmach,
++ chunk, chunk->handle, CHUNK_SIZE);
++}
++
++static void spi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,
++ int size, s3c2410_dma_buffresult_t result)
++{
++ struct spi_chunk *chunk = buf_id;
++ struct spi_dma_info *info = chunk->info;
++
++ if (info->flushing)
++ return;
++
++ spin_lock(&info->head_lock);
++ if ((info->head / CHUNK_SIZE) % NUM_CHUNKS != chunk->index)
++ dev_warn(info->dev, "out of sync, head=0x%x, index=0x%x\n",
++ info->head, chunk->index);
++
++ info->head += size;
++ spin_unlock(&info->head_lock);
++
++ wake_up_interruptible(&info->wait_q);
++
++ spi_queue_dma(chunk);
++}
++
++static struct spi_dma_info *global_info;
++
++static ssize_t spi_dma_read(struct file *file, char __user *buf,
++ size_t len, loff_t *ppos)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct spi_dma_state *state = file->private_data;
++ struct spi_dma_info *info = state->info;
++ unsigned head;
++ ssize_t start, end;
++ ssize_t n;
++
++ if (state->status <= 0) {
++ if (state->status) {
++ state->status = 0;
++ return state->status;
++ }
++ state->tail = info->head;
++ state->status = 1;
++ }
++
++ add_wait_queue(&info->wait_q, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if ((head = info->head) != state->tail)
++ break;
++ if (signal_pending(current))
++ break;
++ schedule();
++ }
++ remove_wait_queue(&info->wait_q, &wait);
++ set_current_state(TASK_RUNNING);
++
++ if (signal_pending(current))
++ return -EINTR;
++
++ if ((head - state->tail) > BUFFER_SIZE)
++ return -ENOBUFS;
++
++ start = state->tail % BUFFER_SIZE;
++ end = head % BUFFER_SIZE;
++
++ if (end > start)
++ n = end - start;
++ else
++ n = BUFFER_SIZE - start;
++
++ if (n > len)
++ n = len;
++
++ if (copy_to_user(buf, info->cpu_addr + start, n))
++ return -EFAULT;
++
++ state->tail += n;
++
++ return n;
++}
++
++static int spi_dma_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ struct spi_dma_state *state = file->private_data;
++ struct spi_dma_info *info = state->info;
++
++ return dma_mmap_coherent(info->dev, vma,
++ info->cpu_addr, info->handle, info->size);
++}
++
++static int spi_dma_get_pos(struct spi_dma_info *info, unsigned *arg)
++{
++ if (put_user(info->head, arg))
++ return -EFAULT;
++
++ return 0;
++}
++
++static int spi_dma_wait_pos(struct spi_dma_info *info, unsigned *arg)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned pos;
++ int diff;
++
++ if (get_user(pos, arg))
++ return -EFAULT;
++
++ add_wait_queue(&info->wait_q, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if ((diff = info->head - pos) > 0)
++ break;
++ if (signal_pending(current))
++ break;
++ schedule();
++ }
++ remove_wait_queue(&info->wait_q, &wait);
++ set_current_state(TASK_RUNNING);
++
++ if (signal_pending(current))
++ return -EINTR;
++
++ if (diff > BUFFER_SIZE)
++ return -ENOBUFS;
++
++ if (put_user(info->head, arg))
++ return -EFAULT;
++
++ return 0;
++}
++
++#define SPI_DMA_IOCTL_BASE ('N' ^ 'M')
++#define SPI_DMA_GET_BUFFER_SIZE _IOR(SPI_DMA_IOCTL_BASE, 0, unsigned)
++#define SPI_DMA_GET_POS _IOR(SPI_DMA_IOCTL_BASE, 1, unsigned)
++#define SPI_DMA_WAIT_POS _IOWR(SPI_DMA_IOCTL_BASE, 2, unsigned)
++
++static int spi_dma_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct spi_dma_state *state = file->private_data;
++ struct spi_dma_info *info = state->info;
++
++ switch (cmd) {
++ case SPI_DMA_GET_BUFFER_SIZE:
++ if (put_user((unsigned)BUFFER_SIZE, (unsigned *)arg))
++ return -EFAULT;
++ return 0;
++
++ case SPI_DMA_GET_POS:
++ return spi_dma_get_pos(info, (unsigned *)arg);
++
++ case SPI_DMA_WAIT_POS:
++ return spi_dma_wait_pos(info, (unsigned *)arg);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int spi_dma_open(struct inode *inode, struct file *file)
++{
++ struct spi_dma_info *info = global_info;
++ struct spi_dma_state *state;
++ int ret;
++
++ state = kmalloc(sizeof(struct spi_dma_state), GFP_KERNEL);
++ if (!state)
++ return -ENOMEM;
++ state->info = info;
++ state->status = 0;
++ file->private_data = state;
++
++ ret = nonseekable_open(inode, file);
++
++ if (ret)
++ kfree(state);
++
++ return ret;
++}
++
++static int spi_dma_release(struct inode *inode, struct file *file)
++{
++ kfree(file->private_data);
++ return 0;
++}
++
++static struct file_operations spi_dma_fops = {
++ .owner = THIS_MODULE,
++ .read = spi_dma_read,
++ .open = spi_dma_open,
++ .release = spi_dma_release,
++ .mmap = spi_dma_mmap,
++ .ioctl = spi_dma_ioctl,
++};
++
++static void spi_dma_free(struct spi_dma_info *info)
++{
++ if (!info)
++ return;
++
++ if (info->major)
++ unregister_chrdev(info->major, "s3c2410-spi");
++ if (info->have_dma) {
++ int value;
++
++ printk("flush\n");
++ info->flushing = 1;
++ s3c2410_dma_ctrl(info->dmach, S3C2410_DMAOP_FLUSH);
++ msleep(100);
++ printk("stop\n");
++ s3c2410_dma_ctrl(info->dmach, S3C2410_DMAOP_STOP);
++ msleep(100);
++
++ printk("poll\n");
++ writel(S3C2410_SPCON_SMOD_POLL, info->regs + S3C2410_SPCON);
++ value = readb(info->regs + S3C2410_SPRDAT);
++ printk("%02x\n", value);
++
++ s3c2410_dma_free(info->dmach, &spi_dma_client);
++ }
++ if (info->regs)
++ iounmap(info->regs);
++ if (info->iomem) {
++ release_resource(info->iomem);
++ kfree(info->iomem);
++ }
++ if (info->clk) {
++ clk_unuse(info->clk);
++ clk_disable(info->clk);
++ }
++ if (info->cpu_addr) {
++ dma_free_coherent(info->dev,
++ info->size, info->cpu_addr,
++ info->handle);
++
++ }
++ kfree(info);
++}
++
++static int spi_dma_probe(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct spi_dma_info *info;
++ struct resource *res;
++ int ret;
++ int i;
++
++ printk("spi_dma_probe\n");
++
++ info = kmalloc(sizeof(struct spi_dma_info), GFP_KERNEL);
++ if (!info) {
++ dev_err(dev, "failed to allocate info structure\n");
++ ret = -ENOMEM;
++ goto out;
++ }
++ memset(info, 0, sizeof(*info));
++ info->dev = dev;
++
++ dev_info(dev, "got info %p\n", info);
++
++ // TODO figure this out in a better way
++ info->dmach = 3;
++ info->dcon = S3C2410_DCON_CH3_SPI;
++ info->size = BUFFER_SIZE;
++
++ spin_lock_init(&info->head_lock);
++ init_waitqueue_head(&info->wait_q);
++
++ info->cpu_addr = dma_alloc_coherent(dev, info->size, &info->handle, GFP_KERNEL);
++ if (!info->cpu_addr) {
++ dev_err(dev, "failed to allocate DMA buffer\n");
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ dev_info(dev, "got DMA buffer at %p, handle 0x%08lx, size %d\n",
++ info->cpu_addr, (long)info->handle,
++ info->size);
++
++ info->clk = clk_get(dev, "spi");
++ if (IS_ERR(info->clk)) {
++ dev_err(dev, "clk_get failed\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ dev_dbg(dev, "clk %p\n", info->clk);
++
++ clk_use(info->clk);
++ clk_enable(info->clk);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ dev_err(dev, "unable to find registers\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ dev_info(dev, "got iomem %p\n", res);
++
++ info->iomem = request_mem_region(res->start,
++ (res->end-res->start)+1,
++ pdev->name);
++ if (!info->iomem) {
++ dev_err(dev, "request_mem_region failed\n");
++ ret = -ENXIO;
++ goto out;
++ }
++
++ dev_info(dev, "got iomem %p\n", info->iomem);
++
++ dev_info(dev, "res->start=0x%lx, res->end=0x%lx\n", res->start, res->end);
++ dev_info(dev, "iomem->start=0x%lx, iomem->end=0x%lx\n", info->iomem->start, info->iomem->end);
++
++ info->regs = ioremap(res->start, (res->end-res->start)+1);
++ if (!info->regs) {
++ dev_err(dev, "ioremap failed\n");
++ ret = -ENXIO;
++ goto out;
++ }
++
++ dev_info(dev, "got regs %p\n", info->regs);
++
++ if (s3c2410_dma_request(info->dmach, &spi_dma_client, NULL)) {
++ dev_err(dev, "unable to allocate dma channel\n");
++ ret = -ENOENT;
++ goto out;
++ }
++ info->have_dma = 1;
++
++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
++ if (!res) {
++ dev_err(dev, "unable to find irq\n");
++ ret = -ENOENT;
++ goto out;
++ }
++
++ ret = register_chrdev(0, "s3c2410-spi", &spi_dma_fops);
++ if (ret < 0) {
++ dev_err(dev, "unable to register character device\n");
++ goto out;
++ }
++ info->major = ret;
++ global_info = info;
++
++ dev_info(dev, "SPI driver active\n");
++
++ dev_set_drvdata(dev, info);
++
++ writel(S3C2410_SPCON_SMOD_DMA | S3C2410_SPCON_TAGD,
++ info->regs + S3C2410_SPCON);
++ writel(S3C2410_SPPIN_RESERVED, info->regs + S3C2410_SPPIN);
++
++ // s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPG5_SPIMISO1);
++ s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_SPIMOSI1);
++ s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPG3, 0x3 << 6);
++
++ s3c2410_dma_devconfig(info->dmach, S3C2410_DMASRC_HW,
++ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
++ info->iomem->start + S3C2410_SPRDAT);
++
++ s3c2410_dma_config(info->dmach, 1, info->dcon);
++
++ s3c2410_dma_set_buffdone_fn(info->dmach, spi_dma_done_callback);
++ s3c2410_dma_setflags(info->dmach, S3C2410_DMAF_AUTOSTART);
++
++ writeb(0, info->regs + S3C2410_SPTDAT);
++ msleep(10);
++
++ for (i = 0; i < NUM_CHUNKS; i++) {
++ int offset = i * CHUNK_SIZE;
++ info->chunks[i].index = i;
++ info->chunks[i].handle = info->handle + offset;
++ info->chunks[i].info = info;
++ }
++
++ for (i = 0; i < NUM_CHUNKS; i++) {
++ spi_queue_dma(&info->chunks[i]);
++ }
++
++ ret = 0;
++
++ out:
++ if (ret)
++ spi_dma_free(info);
++
++ return ret;
++}
++
++static int spi_dma_remove(struct device *dev)
++{
++ struct spi_dma_info *info = dev_get_drvdata(dev);
++
++ dev_set_drvdata(dev, NULL);
++
++ spi_dma_free(info);
++
++ return 0;
++}
++
++static struct device_driver spi_dma_driver = {
++ .name = "s3c2410-spi",
++ .bus = &platform_bus_type,
++ .probe = spi_dma_probe,
++ .remove = spi_dma_remove,
++};
++
++static int __init spi_dma_init(void)
++{
++ printk("SPI dma driver loaded\n");
++
++ return driver_register(&spi_dma_driver);
++}
++
++static void __exit spi_dma_exit(void)
++{
++ driver_unregister(&spi_dma_driver);
++}
++
++module_init(spi_dma_init);
++module_exit(spi_dma_exit);
++
++MODULE_DESCRIPTION("S3C2410 DMA driven SPI driver");
++MODULE_AUTHOR("Christer Weinigel <christer@weinigel.se>");
++MODULE_LICENSE("GPL");