blob: 6458ec77386d7f10fbecf27cb1dc68ee3d5bc21a [file] [log] [blame]
/*
* platform/tegra/fiq_debugger.c
*
* Serial Debugger Interface for Tegra
*
* Copyright (C) 2008 Google, Inc.
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
*
* 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 <stdarg.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/stacktrace.h>
#include <linux/irqchip/tegra.h>
#include <linux/tegra_fiq_debugger.h>
#include <linux/trusty/trusty.h>
#include <linux/trusty/smcall.h>
#include "../../../drivers/staging/android/fiq_debugger/fiq_debugger.h"
#include <linux/uaccess.h>
struct tegra_fiq_debugger {
struct fiq_debugger_pdata pdata;
void __iomem *debug_port_base;
bool break_seen;
struct device *trusty_dev;
};
static inline void tegra_write(struct tegra_fiq_debugger *t,
unsigned int val, unsigned int off)
{
__raw_writeb(val, t->debug_port_base + off * 4);
}
static inline unsigned int tegra_read(struct tegra_fiq_debugger *t,
unsigned int off)
{
return __raw_readb(t->debug_port_base + off * 4);
}
static inline unsigned int tegra_read_lsr(struct tegra_fiq_debugger *t)
{
unsigned int lsr;
lsr = tegra_read(t, UART_LSR);
if (lsr & UART_LSR_BI)
t->break_seen = true;
return lsr;
}
static int debug_port_init(struct platform_device *pdev)
{
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
/* enable and clear FIFO */
tegra_write(t, UART_FCR_ENABLE_FIFO, UART_FCR);
tegra_write(t, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT, UART_FCR);
tegra_write(t, 0, UART_FCR);
tegra_write(t, UART_FCR_ENABLE_FIFO, UART_FCR);
/* clear LSR */
tegra_read(t, UART_LSR);
t->break_seen = false;
/* enable rx interrupt */
tegra_write(t, UART_IER_RDI, UART_IER);
return 0;
}
static int debug_getc(struct platform_device *pdev)
{
unsigned int lsr;
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
lsr = tegra_read_lsr(t);
if (lsr & UART_LSR_BI || t->break_seen) {
t->break_seen = false;
return FIQ_DEBUGGER_BREAK;
}
if (lsr & UART_LSR_DR)
return tegra_read(t, UART_RX);
return FIQ_DEBUGGER_NO_CHAR;
}
static void debug_putc(struct platform_device *pdev, unsigned int c)
{
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
while (!(tegra_read_lsr(t) & UART_LSR_THRE))
cpu_relax();
tegra_write(t, c, UART_TX);
}
static void debug_flush(struct platform_device *pdev)
{
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
while (!(tegra_read_lsr(t) & UART_LSR_TEMT))
cpu_relax();
}
static int debug_suspend(struct platform_device *pdev)
{
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
tegra_write(t, 0, UART_IER);
return 0;
}
static int debug_resume(struct platform_device *pdev)
{
return debug_port_init(pdev);
}
#ifdef CONFIG_FIQ
static void fiq_enable(struct platform_device *pdev, unsigned int irq, bool on)
{
if (on)
tegra_fiq_enable(irq);
else
tegra_fiq_disable(irq);
}
#endif
static void trusty_fiq_enable(struct platform_device *pdev,
unsigned int fiq, bool enable)
{
int ret;
struct tegra_fiq_debugger *t;
t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
ret = trusty_fast_call32(t->trusty_dev, SMC_FC_REQUEST_FIQ,
fiq, enable, 0);
if (ret)
dev_err(&pdev->dev, "SMC_FC_REQUEST_FIQ failed: %d\n", ret);
}
static int tegra_fiq_debugger_id;
static void __tegra_serial_debug_init(unsigned int base, int fiq, int irq,
struct clk *clk, int signal_irq, int wakeup_irq,
struct device *trusty_dev)
{
struct tegra_fiq_debugger *t;
struct platform_device *pdev;
struct resource *res;
int res_count;
t = kzalloc(sizeof(struct tegra_fiq_debugger), GFP_KERNEL);
if (!t) {
pr_err("Failed to allocate for fiq debugger\n");
return;
}
t->pdata.uart_init = debug_port_init;
t->pdata.uart_getc = debug_getc;
t->pdata.uart_putc = debug_putc;
t->pdata.uart_flush = debug_flush;
t->pdata.uart_dev_suspend = debug_suspend;
t->pdata.uart_dev_resume = debug_resume;
#ifdef CONFIG_FIQ
t->pdata.fiq_enable = fiq_enable;
#else
BUG_ON(fiq >= 0 && !trusty_dev);
#endif
if (trusty_dev)
t->pdata.fiq_enable = trusty_fiq_enable;
t->trusty_dev = trusty_dev;
t->debug_port_base = ioremap(base, PAGE_SIZE);
if (!t->debug_port_base) {
pr_err("Failed to ioremap for fiq debugger\n");
goto out1;
}
res = kzalloc(sizeof(struct resource) * 3, GFP_KERNEL);
if (!res) {
pr_err("Failed to alloc fiq debugger resources\n");
goto out2;
}
pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
if (!pdev) {
pr_err("Failed to alloc fiq debugger platform device\n");
goto out3;
};
if (fiq >= 0) {
res[0].flags = IORESOURCE_IRQ;
res[0].start = fiq;
res[0].end = fiq;
res[0].name = "fiq";
} else {
res[0].flags = IORESOURCE_IRQ;
res[0].start = irq;
res[0].end = irq;
res[0].name = "uart_irq";
}
res[1].flags = IORESOURCE_IRQ;
res[1].start = signal_irq;
res[1].end = signal_irq;
res[1].name = "signal";
res_count = 2;
if (wakeup_irq >= 0) {
res[2].flags = IORESOURCE_IRQ;
res[2].start = wakeup_irq;
res[2].end = wakeup_irq;
res[2].name = "wakeup";
res_count++;
}
pdev->name = "fiq_debugger";
pdev->id = tegra_fiq_debugger_id++;
pdev->dev.platform_data = &t->pdata;
pdev->resource = res;
pdev->num_resources = res_count;
if (platform_device_register(pdev)) {
pr_err("Failed to register fiq debugger\n");
goto out4;
}
return;
out4:
kfree(pdev);
out3:
kfree(res);
out2:
iounmap(t->debug_port_base);
out1:
kfree(t);
}
#ifdef CONFIG_FIQ
void tegra_serial_debug_init(unsigned int base, int fiq,
struct clk *clk, int signal_irq, int wakeup_irq)
{
__tegra_serial_debug_init(base, fiq, -1, clk, signal_irq,
wakeup_irq, NULL);
}
#endif
void tegra_serial_debug_init_irq_mode(unsigned int base, int irq,
struct clk *clk, int signal_irq, int wakeup_irq)
{
__tegra_serial_debug_init(base, -1, irq, clk, signal_irq,
wakeup_irq, NULL);
}
static int tegra_serial_debug_trusty_probe(struct platform_device *pdev)
{
struct resource *res;
int fiq;
int signal_irq;
int wakeup_irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No mem resource\n");
return -EINVAL;
}
fiq = platform_get_irq_byname(pdev, "fiq");
if (fiq < 0) {
dev_err(&pdev->dev, "No IRQ for fiq, error=%d\n", fiq);
return fiq;
}
signal_irq = platform_get_irq_byname(pdev, "signal");
if (signal_irq < 0) {
dev_err(&pdev->dev, "No signal IRQ, error=%d\n", signal_irq);
return signal_irq;
}
wakeup_irq = platform_get_irq_byname(pdev, "wakeup");
if (wakeup_irq < 0)
wakeup_irq = -1;
__tegra_serial_debug_init(res->start, fiq, -1, NULL, signal_irq,
wakeup_irq, pdev->dev.parent->parent);
return 0;
}
static const struct of_device_id tegra_serial_debug_trusty_match[] = {
{ .compatible = "android,trusty-fiq-v1-tegra-uart", },
{}
};
static struct platform_driver tegra_serial_debug_trusty_driver = {
.probe = tegra_serial_debug_trusty_probe,
.driver = {
.name = "tegra-fiq-debug",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_serial_debug_trusty_match),
},
};
module_platform_driver(tegra_serial_debug_trusty_driver);