Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index cf82a88..66ed0c3 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -1,5 +1,6 @@
 config ARM_GIC
 	bool
+	select MSM_SHOW_RESUME_IRQ
 
 config ARM_VIC
 	bool
diff --git a/arch/arm/common/cpaccess.c b/arch/arm/common/cpaccess.c
new file mode 100644
index 0000000..241e339
--- /dev/null
+++ b/arch/arm/common/cpaccess.c
@@ -0,0 +1,253 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/sysrq.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel_stat.h>
+#include <linux/uaccess.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/file.h>
+#include <linux/percpu.h>
+#include <linux/string.h>
+#include <linux/smp.h>
+#include <asm/cacheflush.h>
+
+/*
+ * CP parameters
+ */
+struct cp_params {
+	unsigned long cp;
+	unsigned long op1;
+	unsigned long op2;
+	unsigned long crn;
+	unsigned long crm;
+	unsigned long write_value;
+	char rw;
+};
+
+static struct semaphore cp_sem;
+static int cpu;
+
+static DEFINE_PER_CPU(struct cp_params, cp_param)
+	 = { 15, 0, 0, 0, 0, 0, 'r' };
+
+static struct sysdev_class cpaccess_sysclass = {
+	.name = "cpaccess",
+};
+
+/*
+ * get_asm_value - Dummy fuction
+ * @write_val:	Write value incase of a CP register write operation.
+ *
+ * This function is just a placeholder. The first 2 instructions
+ * will be inserted to perform MRC/MCR instruction and a return.
+ * See do_cpregister_rw function. Value passed to function is
+ * accessed from r0 register.
+ */
+static noinline unsigned long cpaccess_dummy(unsigned long write_val)
+{
+	asm("mrc p15, 0, r0, c0, c0, 0\n\t");
+	asm("bx	lr\n\t");
+	return 0xBEEF;
+} __attribute__((aligned(32)))
+
+/*
+ * get_asm_value - Read/Write CP registers
+ * @ret:	Pointer	to return value in case of CP register
+ * read op.
+ *
+ */
+static void get_asm_value(void *ret)
+{
+	*(unsigned long *)ret =
+	 cpaccess_dummy(per_cpu(cp_param.write_value, cpu));
+}
+
+/*
+ * dp_cpregister_rw - Read/Write CP registers
+ * @write:		1 for Write and 0 for Read operation
+ *
+ * Returns value read from CP register
+ */
+static unsigned long do_cpregister_rw(int write)
+{
+	unsigned long opcode, ret, *p_opcode;
+
+	/*
+	 * Mask the crn, crm, op1, op2 and cp values so they do not
+	 * interfer with other fields of the op code.
+	 */
+	per_cpu(cp_param.cp, cpu)  &= 0xF;
+	per_cpu(cp_param.crn, cpu) &= 0xF;
+	per_cpu(cp_param.crm, cpu) &= 0xF;
+	per_cpu(cp_param.op1, cpu) &= 0x7;
+	per_cpu(cp_param.op2, cpu) &= 0x7;
+
+	/*
+	 * Base MRC opcode for MIDR is EE100010,
+	 * MCR is 0xEE000010
+	 */
+	opcode = (write == 1 ? 0xEE000010 : 0xEE100010);
+	opcode |= (per_cpu(cp_param.crn, cpu)<<16) |
+	(per_cpu(cp_param.crm, cpu)<<0) |
+	(per_cpu(cp_param.op1, cpu)<<21) |
+	(per_cpu(cp_param.op2, cpu)<<5) |
+	(per_cpu(cp_param.cp, cpu) << 8);
+
+	/*
+	 * Grab address of the Dummy function, insert MRC/MCR
+	 * instruction and a return instruction ("bx lr"). Do
+	 * a D cache clean and I cache invalidate after inserting
+	 * new code.
+	 */
+	p_opcode = (unsigned long *)&cpaccess_dummy;
+	*p_opcode++ = opcode;
+	*p_opcode-- = 0xE12FFF1E;
+	__cpuc_coherent_kern_range((unsigned long)p_opcode,
+	 ((unsigned long)p_opcode + (sizeof(long) * 2)));
+
+#ifdef CONFIG_SMP
+	/*
+	 * Use smp_call_function_single to do CPU core specific
+	 * get_asm_value function call.
+	 */
+	if (smp_call_function_single(cpu, get_asm_value, &ret, 1))
+		printk(KERN_ERR "Error cpaccess smp call single\n");
+#else
+		get_asm_value(&ret);
+#endif
+
+	return ret;
+}
+
+/*
+ * cp_register_write_sysfs - sysfs interface for writing to
+ * CP register
+ * @dev:	sys device
+ * @attr:	device attribute
+ * @buf:	write value
+ * @cnt:	not used
+ *
+ */
+static ssize_t cp_register_write_sysfs(struct sys_device *dev,
+ struct sysdev_attribute *attr, const char *buf, size_t cnt)
+{
+	unsigned long op1, op2, crn, crm, cp = 15, write_value, ret;
+	char rw;
+	if (down_timeout(&cp_sem, 6000))
+		return -ERESTARTSYS;
+
+	sscanf(buf, "%lu:%lu:%lu:%lu:%lu:%c:%lx:%d", &cp, &op1, &crn,
+	 &crm, &op2, &rw, &write_value, &cpu);
+	per_cpu(cp_param.cp, cpu) = cp;
+	per_cpu(cp_param.op1, cpu) = op1;
+	per_cpu(cp_param.crn, cpu) = crn;
+	per_cpu(cp_param.crm, cpu) = crm;
+	per_cpu(cp_param.op2, cpu) = op2;
+	per_cpu(cp_param.rw, cpu) = rw;
+	per_cpu(cp_param.write_value, cpu) = write_value;
+
+	if (per_cpu(cp_param.rw, cpu) == 'w') {
+		do_cpregister_rw(1);
+		ret = cnt;
+	}
+
+	if ((per_cpu(cp_param.rw, cpu) != 'w') &&
+	(per_cpu(cp_param.rw, cpu) != 'r')) {
+		ret = -1;
+		printk(KERN_INFO "Wrong Entry for 'r' or 'w'. \
+			Use cp:op1:crn:crm:op2:r/w:write_value.\n");
+	}
+
+	return cnt;
+}
+
+/*
+ * cp_register_read_sysfs - sysfs interface for reading CP registers
+ * @dev:        sys device
+ * @attr:       device attribute
+ * @buf:        write value
+ *
+ * Code to read in the CPxx crn, crm, op1, op2 variables, or into
+ * the base MRC opcode, store to executable memory, clean/invalidate
+ * caches and then execute the new instruction and provide the
+ * result to the caller.
+ */
+static ssize_t cp_register_read_sysfs(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+	int ret;
+	ret = sprintf(buf, "%lx\n", do_cpregister_rw(0));
+
+	if (cp_sem.count <= 0)
+		up(&cp_sem);
+
+	return ret;
+}
+
+/*
+ * Setup sysfs files
+ */
+SYSDEV_ATTR(cp_rw, 0644, cp_register_read_sysfs,
+ cp_register_write_sysfs);
+
+static struct sys_device device_cpaccess = {
+	.id     = 0,
+	.cls    = &cpaccess_sysclass,
+};
+
+/*
+ * init_cpaccess_sysfs - initialize sys devices
+ */
+static int __init init_cpaccess_sysfs(void)
+{
+	int error = sysdev_class_register(&cpaccess_sysclass);
+
+	if (!error)
+		error = sysdev_register(&device_cpaccess);
+	else
+		printk(KERN_ERR "Error initializing cpaccess \
+		interface\n");
+
+	if (!error)
+		error = sysdev_create_file(&device_cpaccess,
+		 &attr_cp_rw);
+	else {
+		printk(KERN_ERR "Error initializing cpaccess \
+		interface\n");
+		sysdev_unregister(&device_cpaccess);
+		sysdev_class_unregister(&cpaccess_sysclass);
+	}
+
+	sema_init(&cp_sem, 1);
+
+	return error;
+}
+
+static void __exit exit_cpaccess_sysfs(void)
+{
+	sysdev_remove_file(&device_cpaccess, &attr_cp_rw);
+	sysdev_unregister(&device_cpaccess);
+	sysdev_class_unregister(&cpaccess_sysclass);
+}
+
+module_init(init_cpaccess_sysfs);
+module_exit(exit_cpaccess_sysfs);
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 4ddd0a6..8996f06 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -28,10 +28,12 @@
 #include <linux/smp.h>
 #include <linux/cpumask.h>
 #include <linux/io.h>
+#include <linux/syscore_ops.h>
 
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
+#include <asm/system.h>
 
 static DEFINE_SPINLOCK(irq_controller_lock);
 
@@ -42,6 +44,11 @@
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+	unsigned int max_irq;
+#ifdef CONFIG_PM
+	unsigned int wakeup_irqs[32];
+	unsigned int enabled_irqs[32];
+#endif
 };
 
 /*
@@ -55,6 +62,7 @@
 	.irq_retrigger	= NULL,
 	.irq_set_type	= NULL,
 	.irq_set_wake	= NULL,
+	.irq_disable	= NULL,
 };
 
 #ifndef MAX_GIC_NR
@@ -93,6 +101,7 @@
 	if (gic_arch_extn.irq_mask)
 		gic_arch_extn.irq_mask(d);
 	spin_unlock(&irq_controller_lock);
+
 }
 
 static void gic_unmask_irq(struct irq_data *d)
@@ -106,6 +115,104 @@
 	spin_unlock(&irq_controller_lock);
 }
 
+static void gic_disable_irq(struct irq_data *d)
+{
+	if (gic_arch_extn.irq_disable)
+		gic_arch_extn.irq_disable(d);
+}
+
+#ifdef CONFIG_PM
+static int gic_suspend_one(struct gic_chip_data *gic)
+{
+	unsigned int i;
+	void __iomem *base = gic->dist_base;
+
+	for (i = 0; i * 32 < gic->max_irq; i++) {
+		gic->enabled_irqs[i]
+			= readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4);
+		/* disable all of them */
+		writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4);
+		/* enable the wakeup set */
+		writel_relaxed(gic->wakeup_irqs[i],
+			base + GIC_DIST_ENABLE_SET + i * 4);
+	}
+	mb();
+	return 0;
+}
+
+static int gic_suspend(void)
+{
+	int i;
+	for (i = 0; i < MAX_GIC_NR; i++)
+		gic_suspend_one(&gic_data[i]);
+	return 0;
+}
+
+extern int msm_show_resume_irq_mask;
+
+static void gic_show_resume_irq(struct gic_chip_data *gic)
+{
+	unsigned int i;
+	u32 enabled;
+	unsigned long pending[32];
+	void __iomem *base = gic->dist_base;
+
+	if (!msm_show_resume_irq_mask)
+		return;
+
+	spin_lock(&irq_controller_lock);
+	for (i = 0; i * 32 < gic->max_irq; i++) {
+		enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4);
+		pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4);
+		pending[i] &= enabled;
+	}
+	spin_unlock(&irq_controller_lock);
+
+	for (i = find_first_bit(pending, gic->max_irq);
+	     i < gic->max_irq;
+	     i = find_next_bit(pending, gic->max_irq, i+1)) {
+		pr_warning("%s: %d triggered", __func__,
+					i + gic->irq_offset);
+	}
+}
+
+static void gic_resume_one(struct gic_chip_data *gic)
+{
+	unsigned int i;
+	void __iomem *base = gic->dist_base;
+
+	gic_show_resume_irq(gic);
+	for (i = 0; i * 32 < gic->max_irq; i++) {
+		/* disable all of them */
+		writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4);
+		/* enable the enabled set */
+		writel_relaxed(gic->enabled_irqs[i],
+			base + GIC_DIST_ENABLE_SET + i * 4);
+	}
+	mb();
+}
+
+static void gic_resume(void)
+{
+	int i;
+	for (i = 0; i < MAX_GIC_NR; i++)
+		gic_resume_one(&gic_data[i]);
+}
+
+static struct syscore_ops gic_syscore_ops = {
+	.suspend = gic_suspend,
+	.resume = gic_resume,
+};
+
+static int __init gic_init_sys(void)
+{
+	register_syscore_ops(&gic_syscore_ops);
+	return 0;
+}
+arch_initcall(gic_init_sys);
+
+#endif
+
 static void gic_eoi_irq(struct irq_data *d)
 {
 	if (gic_arch_extn.irq_eoi) {
@@ -202,6 +309,20 @@
 static int gic_set_wake(struct irq_data *d, unsigned int on)
 {
 	int ret = -ENXIO;
+	unsigned int reg_offset, bit_offset;
+	unsigned int gicirq = gic_irq(d);
+	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
+
+	/* per-cpu interrupts cannot be wakeup interrupts */
+	WARN_ON(gicirq < 32);
+
+	reg_offset = gicirq / 32;
+	bit_offset = gicirq % 32;
+
+	if (on)
+		gic_data->wakeup_irqs[reg_offset] |=  1 << bit_offset;
+	else
+		gic_data->wakeup_irqs[reg_offset] &=  ~(1 << bit_offset);
 
 	if (gic_arch_extn.irq_set_wake)
 		ret = gic_arch_extn.irq_set_wake(d, on);
@@ -250,6 +371,7 @@
 #ifdef CONFIG_SMP
 	.irq_set_affinity	= gic_set_affinity,
 #endif
+	.irq_disable		= gic_disable_irq,
 	.irq_set_wake		= gic_set_wake,
 };
 
@@ -324,7 +446,10 @@
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
 	}
 
+	gic->max_irq = gic_irqs;
+
 	writel_relaxed(1, base + GIC_DIST_CTRL);
+	mb();
 }
 
 static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -348,6 +473,7 @@
 
 	writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
 	writel_relaxed(1, base + GIC_CPU_CTRL);
+    mb();
 }
 
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
@@ -399,5 +525,47 @@
 
 	/* this always happens on GIC0 */
 	writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
+	mb();
 }
 #endif
+
+/* before calling this function the interrupts should be disabled
+ * and the irq must be disabled at gic to avoid spurious interrupts */
+bool gic_is_spi_pending(unsigned int irq)
+{
+	struct irq_data *d = irq_get_irq_data(irq);
+	struct gic_chip_data *gic_data = &gic_data[0];
+	u32 mask, val;
+
+	WARN_ON(!irqs_disabled());
+	spin_lock(&irq_controller_lock);
+	mask = 1 << (gic_irq(d) % 32);
+	val = readl(gic_dist_base(d) +
+			GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
+	/* warn if the interrupt is enabled */
+	WARN_ON(val & mask);
+	val = readl(gic_dist_base(d) +
+			GIC_DIST_PENDING_SET + (gic_irq(d) / 32) * 4);
+	spin_unlock(&irq_controller_lock);
+	return (bool) (val & mask);
+}
+
+/* before calling this function the interrupts should be disabled
+ * and the irq must be disabled at gic to avoid spurious interrupts */
+void gic_clear_spi_pending(unsigned int irq)
+{
+	struct gic_chip_data *gic_data = &gic_data[0];
+	struct irq_data *d = irq_get_irq_data(irq);
+
+	u32 mask, val;
+	WARN_ON(!irqs_disabled());
+	spin_lock(&irq_controller_lock);
+	mask = 1 << (gic_irq(d) % 32);
+	val = readl(gic_dist_base(d) +
+			GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
+	/* warn if the interrupt is enabled */
+	WARN_ON(val & mask);
+	writel(mask, gic_dist_base(d) +
+			GIC_DIST_PENDING_CLEAR + (gic_irq(d) / 32) * 4);
+	spin_unlock(&irq_controller_lock);
+}