/* * Copyright (C) 2010 Google, Inc. * * Author: * Colin Cross * * Copyright (C) 2010-2011, NVIDIA Corporation * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "board.h" #include "power.h" #define PMC_CTRL 0x0 #define PMC_CTRL_LATCH_WAKEUPS (1 << 5) #define PMC_WAKE_MASK 0xc #define PMC_WAKE_LEVEL 0x10 #define PMC_WAKE_STATUS 0x14 #define PMC_SW_WAKE_STATUS 0x18 #define PMC_DPD_SAMPLE 0x20 #ifndef CONFIG_ARCH_TEGRA_2x_SOC #define PMC_WAKE2_MASK 0x160 #define PMC_WAKE2_LEVEL 0x164 #define PMC_WAKE2_STATUS 0x168 #define PMC_SW_WAKE2_STATUS 0x16C #endif static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); static u64 tegra_lp0_wake_enb; static u64 tegra_lp0_wake_level; static u64 tegra_lp0_wake_level_any; static unsigned int tegra_wake_irq_count[64]; /* ensures that sufficient time is passed for a register write to * serialize into the 32KHz domain */ static void pmc_32kwritel(u32 val, unsigned long offs) { writel(val, pmc + offs); udelay(130); } static inline void write_pmc_wake_mask(u64 value) { writel((u32)value, pmc + PMC_WAKE_MASK); #ifndef CONFIG_ARCH_TEGRA_2x_SOC __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_MASK); #endif } static inline u64 read_pmc_wake_level(void) { u64 reg; #ifdef CONFIG_ARCH_TEGRA_2x_SOC reg = readl(pmc + PMC_WAKE_LEVEL); #else reg = __raw_readl(pmc + PMC_WAKE_LEVEL); reg |= ((u64)readl(pmc + PMC_WAKE2_LEVEL)) << 32; #endif return reg; } static inline void write_pmc_wake_level(u64 value) { writel((u32)value, pmc + PMC_WAKE_LEVEL); #ifndef CONFIG_ARCH_TEGRA_2x_SOC __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_LEVEL); #endif } static inline u64 read_pmc_wake_status(void) { u64 reg; #ifdef CONFIG_ARCH_TEGRA_2x_SOC reg = readl(pmc + PMC_WAKE_STATUS); #else reg = __raw_readl(pmc + PMC_WAKE_STATUS); reg |= ((u64)readl(pmc + PMC_WAKE2_STATUS)) << 32; #endif return reg; } static inline u64 read_pmc_sw_wake_status(void) { u64 reg; #ifdef CONFIG_ARCH_TEGRA_2x_SOC reg = readl(pmc + PMC_SW_WAKE_STATUS); #else reg = __raw_readl(pmc + PMC_SW_WAKE_STATUS); reg |= ((u64)readl(pmc + PMC_SW_WAKE2_STATUS)) << 32; #endif return reg; } static inline void clear_pmc_sw_wake_status(void) { pmc_32kwritel(0, PMC_SW_WAKE_STATUS); #ifndef CONFIG_ARCH_TEGRA_2x_SOC pmc_32kwritel(0, PMC_SW_WAKE2_STATUS); #endif } int tegra_set_lp0_wake(int irq, int enable) { int wake = tegra_irq_to_wake(irq); if (wake < 0) return -EINVAL; if (enable) tegra_lp0_wake_enb |= 1ull << wake; else tegra_lp0_wake_enb &= ~(1ull << wake); return 0; } int tegra_set_lp0_wake_type(int irq, int flow_type) { int wake = tegra_irq_to_wake(irq); if (wake < 0) return 0; switch (flow_type) { case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: tegra_lp0_wake_level &= ~(1 << wake); tegra_lp0_wake_level_any &= ~(1 << wake); break; case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: tegra_lp0_wake_level |= 1 << wake; tegra_lp0_wake_level_any &= ~(1 << wake); break; case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING: tegra_lp0_wake_level_any |= 1 << wake; break; default: return -EINVAL; } return 0; } int tegra_set_lp1_wake(int irq, int enable) { return tegra_legacy_irq_set_wake(irq, enable); } void tegra_set_lp0_wake_pads(u64 wake_enb, u64 wake_level, u64 wake_any) { u32 temp; u64 status; u64 lvl; wake_level &= wake_enb; wake_any &= wake_enb; wake_level |= (tegra_lp0_wake_level & tegra_lp0_wake_enb); wake_any |= (tegra_lp0_wake_level_any & tegra_lp0_wake_enb); wake_enb |= tegra_lp0_wake_enb; clear_pmc_sw_wake_status(); temp = readl(pmc + PMC_CTRL); temp |= PMC_CTRL_LATCH_WAKEUPS; pmc_32kwritel(temp, PMC_CTRL); temp &= ~PMC_CTRL_LATCH_WAKEUPS; pmc_32kwritel(temp, PMC_CTRL); status = read_pmc_sw_wake_status(); lvl = read_pmc_wake_level(); /* flip the wakeup trigger for any-edge triggered pads * which are currently asserting as wakeups */ lvl ^= status; lvl &= wake_any; wake_level |= lvl; write_pmc_wake_level(wake_level); /* Enable DPD sample to trigger sampling pads data and direction * in which pad will be driven during lp0 mode*/ writel(0x1, pmc + PMC_DPD_SAMPLE); write_pmc_wake_mask(wake_enb); } #ifdef CONFIG_PM static void tegra_irq_handle_wake_helper(unsigned long wake_status) { int wake; int irq; struct irq_desc *desc; for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) { irq = tegra_wake_to_irq(wake); if (!irq) { pr_info("Resume caused by WAKE%d\n", wake); continue; } desc = irq_to_desc(irq); if (!desc || !desc->action || !desc->action->name) { pr_info("Resume caused by WAKE%d, irq %d\n", wake, irq); continue; } pr_info("Resume caused by WAKE%d, %s\n", wake, desc->action->name); tegra_wake_irq_count[wake]++; generic_handle_irq(irq); } } static void tegra_irq_handle_wake(void) { u64 wake_status = read_pmc_wake_status(); tegra_irq_handle_wake_helper((unsigned long)wake_status); #ifndef CONFIG_ARCH_TEGRA_2x_SOC tegra_irq_handle_wake_helper((unsigned long)(wake_status >> 32)); #endif } #endif static void tegra_mask(unsigned int irq) { gic_mask_irq(irq); tegra_legacy_mask_irq(irq); } static void tegra_unmask(unsigned int irq) { gic_unmask_irq(irq); tegra_legacy_unmask_irq(irq); } static int tegra_set_wake(unsigned int irq, unsigned int enable) { int ret; ret = tegra_set_lp1_wake(irq, enable); if (ret) return ret; if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0) return tegra_set_lp0_wake(irq, enable); return 0; } static int tegra_set_type(unsigned int irq, unsigned int flow_type) { return tegra_set_lp0_wake_type(irq, flow_type); } static void tegra_ack(unsigned int irq) { tegra_legacy_force_irq_clr(irq); gic_ack_irq(irq); } static int tegra_retrigger(unsigned int irq) { tegra_legacy_force_irq_set(irq); return 1; } static struct irq_chip tegra_irq = { .name = "PPI", .ack = tegra_ack, .mask = tegra_mask, .unmask = tegra_unmask, .set_wake = tegra_set_wake, .set_type = tegra_set_type, #ifdef CONFIG_SMP .set_affinity = gic_set_cpu, #endif .retrigger = tegra_retrigger, }; void __init tegra_init_irq(void) { unsigned int i; int irq; tegra_init_legacy_irq(); gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29); gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); for (i = 0; i < INT_MAIN_NR; i++) { irq = INT_PRI_BASE + i; set_irq_chip(irq, &tegra_irq); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } } #ifdef CONFIG_PM void tegra_irq_suspend(void) { tegra_legacy_irq_suspend(); } void tegra_irq_resume(void) { tegra_legacy_irq_resume(); tegra_irq_handle_wake(); } #endif #ifndef CONFIG_ARCH_TEGRA_2x_SOC static u32 gic_affinity[INT_GIC_NR/4]; void tegra_irq_disable_affinity(void) { void __iomem *gic_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); unsigned int i; BUG_ON(is_lp_cluster()); /* The GIC distributor TARGET register is one byte per IRQ. */ for (i = 32; i < INT_GIC_NR; i += 4) { /* Save the affinity. */ gic_affinity[i/4] = __raw_readl(gic_base + GIC_DIST_TARGET + i); /* Force this interrupt to CPU0. */ __raw_writel(0x01010101, gic_base + GIC_DIST_TARGET + i); } wmb(); } void tegra_irq_restore_affinity(void) { void __iomem *gic_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); unsigned int i; BUG_ON(is_lp_cluster()); /* The GIC distributor TARGET register is one byte per IRQ. */ for (i = 32; i < INT_GIC_NR; i += 4) { #ifdef CONFIG_BUG u32 reg = __raw_readl(gic_base + GIC_DIST_TARGET + i); if (reg & 0xFEFEFEFE) panic("GIC affinity changed!"); #endif /* Restore this interrupt's affinity. */ __raw_writel(gic_affinity[i/4], gic_base + GIC_DIST_TARGET + i); } wmb(); } void tegra_irq_affinity_to_cpu0(void) { void __iomem *gic_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); unsigned int i; BUG_ON(is_lp_cluster()); for (i = 32; i < INT_GIC_NR; i += 4) __raw_writel(0x01010101, gic_base + GIC_DIST_TARGET + i); wmb(); } void tegra_irq_pass_through_disable(void) { void __iomem *base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100); u32 val = readl(base + GIC_CPU_CTRL); val |= 2; /* enableNS = disable pass through */ writel(1, base + GIC_CPU_CTRL); } #endif #ifdef CONFIG_DEBUG_FS static int tegra_wake_irq_debug_show(struct seq_file *s, void *data) { int wake; int irq; struct irq_desc *desc; const char *irq_name; seq_printf(s, "wake irq count name\n"); seq_printf(s, "----------------------\n"); for (wake = 0; wake < 32; wake++) { irq = tegra_wake_to_irq(wake); if (irq < 0) continue; desc = irq_to_desc(irq); if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL) continue; if (!(desc->status & IRQ_WAKEUP)) continue; irq_name = (desc->action && desc->action->name) ? desc->action->name : "???"; seq_printf(s, "%4d %3d %5d %s\n", wake, irq, tegra_wake_irq_count[wake], irq_name); } return 0; } static int tegra_wake_irq_debug_open(struct inode *inode, struct file *file) { return single_open(file, tegra_wake_irq_debug_show, NULL); } static const struct file_operations tegra_wake_irq_debug_fops = { .open = tegra_wake_irq_debug_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init tegra_irq_debug_init(void) { struct dentry *d; d = debugfs_create_file("wake_irq", 0755, NULL, NULL, &tegra_wake_irq_debug_fops); if (!d) { pr_info("Failed to create suspend_mode debug file\n"); return -ENOMEM; } return 0; } late_initcall(tegra_irq_debug_init); #endif