Index: linux-2.6.24/drivers/misc/Kconfig
===================================================================
--- linux-2.6.24.orig/drivers/misc/Kconfig	2008-02-13 10:21:40.000000000 +0100
+++ linux-2.6.24/drivers/misc/Kconfig	2008-02-13 10:26:21.000000000 +0100
@@ -232,4 +232,12 @@
 
 	  If unsure, say N.
 
+config MEM_ACCESS
+	tristate "Direct memory access for G500 hardware investigation"
+	depends on MACH_G500
+	---help---
+	Direct memory access for G500 hardware investigation
+
+	  If unsure, say N.
+
 endif # MISC_DEVICES
Index: linux-2.6.24/drivers/misc/Makefile
===================================================================
--- linux-2.6.24.orig/drivers/misc/Makefile	2008-02-13 10:21:40.000000000 +0100
+++ linux-2.6.24/drivers/misc/Makefile	2008-02-13 10:26:21.000000000 +0100
@@ -17,3 +17,4 @@
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
+obj-$(CONFIG_MEM_ACCESS)	+= mem_access.o
Index: linux-2.6.24/drivers/misc/mem_access.c
===================================================================
--- linux-2.6.24.orig/drivers/misc/mem_access.c	2008-02-13 10:21:40.000000000 +0100
+++ linux-2.6.24/drivers/misc/mem_access.c	2008-02-20 14:39:15.000000000 +0100
@@ -1,10 +1,271 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#define MEM_ACCESS_FILE         "mem_access"
+#define MEM_CONFIG_DEFAULT      CONFIG_GPIO
+
+enum { CONFIG_NONE, CONFIG_GPIO, CONFIG_UART, CONFIG_CLOCK, CONFIG_AC97 };
+
+static void __iomem *mem = NULL;
+static int start = 0, size = 0;
+static int current_config = CONFIG_NONE;
+static struct platform_device *mem_access_device;
+
+static int configure_mem(int config)
+{
+	if (mem != NULL)
+		iounmap(mem);
+
+	switch (config) {
+	case CONFIG_GPIO:
+		start = 0x56000000;
+		size = 0x100;
+		break;
+
+	case CONFIG_UART:
+		start = 0x50000000;
+		size = 0x9000;
+		break;
+
+	case CONFIG_CLOCK:
+		start = 0x4c000000;
+		size = 0x100;
+		break;
+
+	case CONFIG_AC97:
+		start = 0x5b000000;
+		size = 0x20;
+		break;
+
+	default:
+		start = 0;
+		size = 0;
+		break;
+	}
+
+	if (size > 0) {
+		printk("configure_mem : start=%08x, size=%08x\n", start, size);
+		mem = ioremap(start, size);
+		if (mem == NULL) {
+			current_config = CONFIG_NONE;
+			return -ENXIO;
+		}
+	} else {
+		mem = NULL;
+	}
+
+	current_config = config;
+
+	return 0;
+}
+
+static ssize_t show_mem(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int i = 0;
+
+	switch (current_config) {
+	case CONFIG_GPIO:
+		i += sprintf(buf + i, "GPACON : %08x\n", readl(mem + 0x00));
+		i += sprintf(buf + i, "GPBCON : %08x\n", readl(mem + 0x10));
+		i += sprintf(buf + i, "GPCCON : %08x\n", readl(mem + 0x20));
+		i += sprintf(buf + i, "GPDCON : %08x\n", readl(mem + 0x30));
+		i += sprintf(buf + i, "GPECON : %08x\n", readl(mem + 0x40));
+		i += sprintf(buf + i, "GPFCON : %08x\n", readl(mem + 0x50));
+		i += sprintf(buf + i, "GPGCON : %08x\n", readl(mem + 0x60));
+		i += sprintf(buf + i, "GPHCON : %08x\n", readl(mem + 0x70));
+		i += sprintf(buf + i, "GPJCON : %08x\n", readl(mem + 0xd0));
+		i += sprintf(buf + i, "\n");
+		i += sprintf(buf + i, "GPADAT : %08x\n", readl(mem + 0x04));
+		i += sprintf(buf + i, "GPBDAT : %08x\n", readl(mem + 0x14));
+		i += sprintf(buf + i, "GPCDAT : %08x\n", readl(mem + 0x24));
+		i += sprintf(buf + i, "GPDDAT : %08x\n", readl(mem + 0x34));
+		i += sprintf(buf + i, "GPEDAT : %08x\n", readl(mem + 0x44));
+		i += sprintf(buf + i, "GPFDAT : %08x\n", readl(mem + 0x54));
+		i += sprintf(buf + i, "GPGDAT : %08x\n", readl(mem + 0x64));
+		i += sprintf(buf + i, "GPHDAT : %08x\n", readl(mem + 0x74));
+		i += sprintf(buf + i, "GPJDAT : %08x\n", readl(mem + 0xd4));
+		i += sprintf(buf + i, "\n");
+		i += sprintf(buf + i, "GPBUP : %08x\n", readl(mem + 0x18));
+		i += sprintf(buf + i, "GPCUP : %08x\n", readl(mem + 0x28));
+		i += sprintf(buf + i, "GPDUP : %08x\n", readl(mem + 0x38));
+		i += sprintf(buf + i, "GPEUP : %08x\n", readl(mem + 0x48));
+		i += sprintf(buf + i, "GPFUP : %08x\n", readl(mem + 0x58));
+		i += sprintf(buf + i, "GPGUP : %08x\n", readl(mem + 0x68));
+		i += sprintf(buf + i, "GPHUP : %08x\n", readl(mem + 0x78));
+		i += sprintf(buf + i, "GPJUP : %08x\n", readl(mem + 0xd8));
+		break;
+
+	case CONFIG_UART:
+		i += sprintf(buf + i, "ULCON0 : %08x\n", readl(mem + 0x0000));
+		i += sprintf(buf + i, "ULCON1 : %08x\n", readl(mem + 0x4000));
+		i += sprintf(buf + i, "ULCON2 : %08x\n", readl(mem + 0x8000));
+		i += sprintf(buf + i, "UCON0 : %08x\n", readl(mem + 0x0004));
+		i += sprintf(buf + i, "UCON1 : %08x\n", readl(mem + 0x4004));
+		i += sprintf(buf + i, "UCON2 : %08x\n", readl(mem + 0x8004));
+		i += sprintf(buf + i, "UBRDIV0 : %08x\n", readl(mem + 0x0028));
+		i += sprintf(buf + i, "UBRDIV1 : %08x\n", readl(mem + 0x4028));
+		i += sprintf(buf + i, "UBRDIV2 : %08x\n", readl(mem + 0x8028));
+		break;
+
+	case CONFIG_CLOCK:
+		i += sprintf(buf + i, "MPLLCON : %08x\n", readl(mem + 0x04));
+		i += sprintf(buf + i, "CLKCON : %08x\n", readl(mem + 0x0c));
+		i += sprintf(buf + i, "CLKDIVN : %08x\n", readl(mem + 0x14));
+		i += sprintf(buf + i, "CAMDIVN : %08x\n", readl(mem + 0x18));
+		break;
+
+	case CONFIG_AC97:
+		i += sprintf(buf + i, "ACGLBCTRL     : %08x\n", readl(mem + 0x00));
+		i += sprintf(buf + i, "ACGLBSTAT     : %08x\n", readl(mem + 0x04));
+		i += sprintf(buf + i, "AC_CODEC_CMD  : %08x\n", readl(mem + 0x08));
+		i += sprintf(buf + i, "AC_CODEC_STAT : %08x\n", readl(mem + 0x0c));
+		break;
+
+	}
+
+	return i;
+}
+
+static ssize_t store_mem(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	int offset, value, shift;
+
+	if (!strncmp(buf, "set ", 4)) {
+		if (sscanf(buf+4, "%x=%x", &offset, &value) == 2) {
+			if (offset < size) {
+				printk("mem_access : set %x=%x\n", offset, value);
+				writel(value, mem + offset);
+				return count;
+			} else {
+				printk("mem_access : offset too big (%x)\n", offset);
+				return -EINVAL;
+			}
+		}
+	} else if(!strncmp(buf, "bic ", 4)) {
+		if (sscanf(buf+4, "%x/%d", &offset, &shift) == 2) {
+			if (offset < size) {
+				printk("mem_access : bic %x/%d\n", offset, shift);
+				// interrupts ?
+				value = readl(mem + offset);
+				writel(value & ~(1<<shift), mem + offset);
+				return count;
+			} else {
+				printk("mem_access : offset too big (%x)\n", offset);
+				return -EINVAL;
+			}
+		}
+	} else if(!strncmp(buf, "orr ", 4)) {
+		if (sscanf(buf+4, "%x/%d", &offset, &shift) == 2) {
+			if (offset < size) {
+				printk("mem_access : orr %x/%d\n", offset, shift);
+				// interrupts ?
+				value = readl(mem + offset);
+				writel(value | (1<<shift), mem + offset);
+				return count;
+			} else {
+				printk("mem_access : offset too big (%x)\n", offset);
+				return -EINVAL;
+			}
+		}
+	} else if(!strncmp(buf, "conf ", 5)) {
+		if (!strncmp(buf+5, "gpio", count-5)) {
+			configure_mem(CONFIG_GPIO);
+			return count;
+		} else if (!strncmp(buf+5, "uart", count-5)) {
+			configure_mem(CONFIG_UART);
+			return count;
+		} else if (!strncmp(buf+5, "clock", count-5)) {
+			configure_mem(CONFIG_CLOCK);
+			return count;
+		} else if (!strncmp(buf+5, "ac97", count-5)) {
+			configure_mem(CONFIG_AC97);
+			return count;
+		}
+	}
+
+	printk("mem_access : unknown command\n");
+	return -EINVAL;
+}
+
+struct device_attribute dev_attr_mem_access = {
+	.attr = {
+		 .name = "mem_access",
+		 .mode = 0},
+	.show = NULL,
+	.store = NULL,
+};
+
+static struct attribute *mem_access_attributes[] = {
+	&dev_attr_mem_access.attr,
+};
+
+static struct attribute_group mem_access_attribute_group = {
+	.attrs = mem_access_attributes
+};
+
 static int __init mem_access_init(void)
 {
+	int result;
+
+	result = configure_mem(MEM_CONFIG_DEFAULT);
+	if (result != 0)
+		return result;
+
+	mem_access_device = platform_device_alloc(MEM_ACCESS_FILE, -1);
+	if (!mem_access_device) {
+		result = -ENOMEM;
+		goto fail_platform_device1;
+	}
+
+	result = platform_device_add(mem_access_device);
+	if (result)
+		goto fail_platform_device2;
+
+	dev_attr_mem_access.attr.mode = 0644;
+	dev_attr_mem_access.show = show_mem;
+	dev_attr_mem_access.store = store_mem;
+
+  printk("before\n");
+  msleep(10);
+	result = sysfs_create_group(&mem_access_device->dev.kobj,
+				    &mem_access_attribute_group);
+  printk("after\n");
+	if (result)
+		goto fail_sysfs;
+
+	return 0;
+
+      fail_sysfs:
+	platform_device_del(mem_access_device);
+
+      fail_platform_device2:
+	platform_device_put(mem_access_device);
+
+      fail_platform_device1:
+	configure_mem(CONFIG_NONE);
+
+	return result;
 }
 
 static void __exit mem_access_exit(void)
 {
+	sysfs_remove_group(&mem_access_device->dev.kobj,
+			   &mem_access_attribute_group);
+
+	platform_device_unregister(mem_access_device);
+
+	configure_mem(CONFIG_NONE);
 }
 
 module_init(mem_access_init);
 module_exit(mem_access_exit);
+
+MODULE_AUTHOR("Pierre Hebert");
+MODULE_DESCRIPTION("G500 direct mem access for hardware investigation");
+MODULE_LICENSE("GPL");

