aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDima Zavin <dima@android.com>2011-10-02 18:59:49 -0700
committerDima Zavin <dima@android.com>2011-10-25 23:10:19 -0700
commit31763ea4205553ec79a24f24bf355219fb4d9041 (patch)
tree948ad14828c27907ad679dfa6bbcf0eaf2076a7b /arch
parent7400e81d5726164b6f9d114a0f073e104ef46416 (diff)
downloadkernel_samsung_tuna-31763ea4205553ec79a24f24bf355219fb4d9041.zip
kernel_samsung_tuna-31763ea4205553ec79a24f24bf355219fb4d9041.tar.gz
kernel_samsung_tuna-31763ea4205553ec79a24f24bf355219fb4d9041.tar.bz2
ARM: omap4: add OMAP support for the fiq debugger
Due to platform limitations, it only currently supports IRQ-only fiq debugger. Change-Id: I95508183a40793e1220769e34305bbe9fb6ede4e Signed-off-by: Dima Zavin <dima@android.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/Kconfig7
-rw-r--r--arch/arm/mach-omap2/Makefile1
-rw-r--r--arch/arm/mach-omap2/include/mach/omap_fiq_debugger.h35
-rw-r--r--arch/arm/mach-omap2/omap_fiq_debugger.c315
4 files changed, 358 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 1cf7d51..fe0bed4 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -356,6 +356,13 @@ config OMAP_ALLOW_OSWR
Which means the Logic of power domains can be lost now
unlike the CSWR wherein the logic is retained
+config OMAP_FIQ_DEBUGGER
+ bool "Enable the serial FIQ debugger on OMAP"
+ default y
+ select FIQ_DEBUGGER
+ help
+ Enables the serial FIQ debugger on OMAP"
+
endmenu
endif
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index c0f2be2..b4049d3 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -292,3 +292,4 @@ obj-y += common-board-devices.o
obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o
obj-$(CONFIG_OMAP_HSI_DEVICE) += omap_hsi.o
obj-$(CONFIG_ARCH_OMAP4) += omap_dmm.o
+obj-$(CONFIG_OMAP_FIQ_DEBUGGER) += omap_fiq_debugger.o
diff --git a/arch/arm/mach-omap2/include/mach/omap_fiq_debugger.h b/arch/arm/mach-omap2/include/mach/omap_fiq_debugger.h
new file mode 100644
index 0000000..c49608e
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/omap_fiq_debugger.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_OMAP_FIQ_DEBUGGER_H
+#define __MACH_OMAP_FIQ_DEBUGGER_H
+
+#ifdef CONFIG_OMAP_FIQ_DEBUGGER
+int __init omap_serial_debug_init(int id, bool is_fiq, bool is_high_prio_irq,
+ struct omap_device_pad *pads, int num_pads);
+u32 omap_debug_uart_resume_idle(void);
+
+#else
+static inline int __init omap_serial_debug_init(int id, bool is_fiq, bool is_high_prio_irq,
+ struct omap_device_pad *pads, int num_pads)
+{
+}
+
+static inline u32 omap_debug_uart_resume_idle(void)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/arch/arm/mach-omap2/omap_fiq_debugger.c b/arch/arm/mach-omap2/omap_fiq_debugger.c
new file mode 100644
index 0000000..7fff162
--- /dev/null
+++ b/arch/arm/mach-omap2/omap_fiq_debugger.c
@@ -0,0 +1,315 @@
+/*
+ * Serial Debugger Interface for Omap
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * 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 <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stacktrace.h>
+#include <linux/uaccess.h>
+
+#include <plat/omap_device.h>
+#include <plat/omap-pm.h>
+#include <plat/omap-serial.h>
+
+#include <asm/fiq_debugger.h>
+
+#include <mach/omap_fiq_debugger.h>
+#include <mach/system.h>
+
+#include "mux.h"
+
+struct omap_fiq_debugger {
+ struct fiq_debugger_pdata pdata;
+ struct platform_device *pdev;
+ void __iomem *debug_port_base;
+ bool suspended;
+ spinlock_t lock;
+};
+
+static struct omap_fiq_debugger *dbgs[OMAP_MAX_HSUART_PORTS];
+
+static inline struct omap_fiq_debugger *get_dbg(struct platform_device *pdev)
+{
+ struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev);
+ return container_of(pdata, struct omap_fiq_debugger, pdata);
+}
+
+static inline void omap_write(struct omap_fiq_debugger *dbg,
+ unsigned int val, unsigned int off)
+{
+ __raw_writel(val, dbg->debug_port_base + off * 4);
+}
+
+static inline unsigned int omap_read(struct omap_fiq_debugger *dbg,
+ unsigned int off)
+{
+ return __raw_readl(dbg->debug_port_base + off * 4);
+}
+
+static void debug_omap_port_enable(struct platform_device *pdev)
+{
+ pm_runtime_get_sync(&pdev->dev);
+}
+
+static void debug_omap_port_disable(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbg->lock, flags);
+ if (!dbg->suspended) {
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+ } else {
+ pm_runtime_put_sync_suspend(&pdev->dev);
+ }
+ spin_unlock_irqrestore(&dbg->lock, flags);
+}
+
+static int debug_omap_port_resume(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+
+ dbg->suspended = false;
+ barrier();
+ return 0;
+}
+
+static int debug_omap_port_suspend(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+ unsigned long flags;
+
+ /* this will force the device to be idle'd now, in case it was
+ * autosuspended but timer has not yet run out.
+ */
+ spin_lock_irqsave(&dbg->lock, flags);
+ dbg->suspended = true;
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_put_sync_suspend(&pdev->dev);
+ spin_unlock_irqrestore(&dbg->lock, flags);
+
+ return 0;
+}
+
+static int debug_port_config(struct omap_fiq_debugger *dbg)
+{
+ if (omap_read(dbg, UART_LSR) & UART_LSR_DR)
+ omap_read(dbg, UART_RX);
+
+ /* enable rx and lsr interrupt */
+ omap_write(dbg, UART_IER_RLSI | UART_IER_RDI, UART_IER);
+
+ /* interrupt on every character */
+ omap_write(dbg, 0, UART_IIR);
+
+ omap_write(dbg, 0, UART_OMAP_WER);
+
+ return 0;
+}
+
+u32 omap_debug_uart_resume_idle(void)
+{
+ int i;
+ u32 ret = 0;
+
+ for (i = 0; i < OMAP_MAX_HSUART_PORTS; i++) {
+ struct omap_fiq_debugger *dbg = dbgs[i];
+ struct omap_device *od;
+
+ if (!dbg || !dbg->pdev)
+ continue;
+
+ od = to_omap_device(dbg->pdev);
+ if (omap_hwmod_pad_get_wakeup_status(od->hwmods[0])) {
+ /*
+ * poke the uart and let it stay on long enough
+ * to process any further data. It's ok to use
+ * autosuspend here since this is on the resume path
+ * during the wakeup. We'll still go through a full
+ * resume cycle, so if we go back to suspend
+ * the suspended flag will properly get reset.
+ */
+ pm_runtime_get_sync(&dbg->pdev->dev);
+ pm_runtime_mark_last_busy(&dbg->pdev->dev);
+ pm_runtime_put_autosuspend(&dbg->pdev->dev);
+ dev_dbg(&dbg->pdev->dev, "woke up from IO pad\n");
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
+static int debug_port_init(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+
+ device_init_wakeup(&pdev->dev, true);
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, DEFAULT_AUTOSUSPEND_DELAY);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
+
+ omap_hwmod_idle(to_omap_device(pdev)->hwmods[0]);
+ debug_omap_port_enable(pdev);
+ debug_omap_port_disable(pdev);
+
+ debug_omap_port_enable(pdev);
+
+ if (device_may_wakeup(&pdev->dev))
+ omap_hwmod_enable_wakeup(to_omap_device(pdev)->hwmods[0]);
+
+ debug_port_config(dbg);
+
+ debug_omap_port_disable(pdev);
+ return 0;
+}
+
+static int debug_getc(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+ unsigned int lsr;
+ int ret = FIQ_DEBUGGER_NO_CHAR;
+
+ lsr = omap_read(dbg, UART_LSR);
+ if (lsr & UART_LSR_BI) {
+ /* need to read RHR to clear the BI condition */
+ omap_read(dbg, UART_RX);
+ ret = FIQ_DEBUGGER_BREAK;
+ } else if (lsr & UART_LSR_DR) {
+ ret = omap_read(dbg, UART_RX);
+ }
+
+ return ret;
+}
+
+static void debug_putc(struct platform_device *pdev, unsigned int c)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+
+ while (!(omap_read(dbg, UART_LSR) & UART_LSR_THRE))
+ cpu_relax();
+
+ omap_write(dbg, c, UART_TX);
+}
+
+static void debug_flush(struct platform_device *pdev)
+{
+ struct omap_fiq_debugger *dbg = get_dbg(pdev);
+
+ while (!(omap_read(dbg, UART_LSR) & UART_LSR_TEMT))
+ cpu_relax();
+}
+
+static int uart_idle_hwmod(struct omap_device *od)
+{
+ omap_hwmod_idle(od->hwmods[0]);
+
+ return 0;
+}
+
+static int uart_enable_hwmod(struct omap_device *od)
+{
+ omap_hwmod_enable(od->hwmods[0]);
+
+ return 0;
+}
+
+static struct omap_device_pm_latency omap_uart_latency[] = {
+ {
+ .deactivate_func = uart_idle_hwmod,
+ .activate_func = uart_enable_hwmod,
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+ },
+};
+
+extern struct omap_hwmod *omap_uart_hwmod_lookup(int num);
+
+int __init omap_serial_debug_init(int id, bool is_fiq, bool is_high_prio_irq,
+ struct omap_device_pad *pads, int num_pads)
+{
+ struct omap_fiq_debugger *dbg;
+ struct omap_hwmod *oh;
+ struct omap_device *od;
+ int ret;
+
+ if (id >= OMAP_MAX_HSUART_PORTS)
+ return -EINVAL;
+ if (dbgs[id])
+ return -EBUSY;
+
+ oh = omap_uart_hwmod_lookup(id);
+ if (!oh)
+ return -ENODEV;
+
+ oh->mpu_irqs[0].name = "uart_irq";
+ oh->mux = omap_hwmod_mux_init(pads, num_pads);
+
+ dbg = kzalloc(sizeof(struct omap_fiq_debugger), GFP_KERNEL);
+ if (!dbg) {
+ pr_err("Failed to allocate for fiq debugger\n");
+ return -ENOMEM;
+ }
+
+ dbg->debug_port_base = ioremap(oh->slaves[0]->addr[0].pa_start,
+ PAGE_SIZE);
+ if (!dbg->debug_port_base) {
+ pr_err("Failed to ioremap for fiq debugger\n");
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ spin_lock_init(&dbg->lock);
+
+ dbg->pdata.uart_init = debug_port_init;
+ dbg->pdata.uart_getc = debug_getc;
+ dbg->pdata.uart_putc = debug_putc;
+ dbg->pdata.uart_flush = debug_flush;
+ dbg->pdata.uart_enable = debug_omap_port_enable;
+ dbg->pdata.uart_disable = debug_omap_port_disable;
+ dbg->pdata.uart_dev_suspend = debug_omap_port_suspend;
+ dbg->pdata.uart_dev_resume = debug_omap_port_resume;
+
+ od = omap_device_build("fiq_debugger", id,
+ oh, dbg, sizeof(*dbg), omap_uart_latency,
+ ARRAY_SIZE(omap_uart_latency), false);
+ if (IS_ERR(od)) {
+ pr_err("Could not build omap_device for fiq_debugger: %s\n",
+ oh->name);
+ ret = PTR_ERR(od);
+ goto err_dev_build;
+ }
+
+ dbg->pdev = &od->pdev;
+ dbgs[id] = dbg;
+
+ return 0;
+
+err_dev_build:
+ iounmap(dbg->debug_port_base);
+err_ioremap:
+ kfree(dbg);
+ return ret;
+}