blob: b2d38b1494617807fca4a5fad1bd20aa83b8d861 [file] [log] [blame]
/* Copyright (c) 2011, 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include "qdss.h"
#define debug_writel(debug, cpu, val, off) \
__raw_writel((val), debug.base[cpu] + off)
#define debug_readl(debug, cpu, off) \
__raw_readl(debug.base[cpu] + off)
#define DBGDIDR (0x000)
#define DBGWFAR (0x018)
#define DBGVCR (0x01C)
#define DBGECR (0x024)
#define DBGDTRRX (0x080)
#define DBGITR (0x084)
#define DBGDSCR (0x088)
#define DBGDTRTX (0x08C)
#define DBGDRCR (0x090)
#define DBGEACR (0x094)
#define DBGPCSR (0x0A0)
#define DBGCIDSR (0x0A4)
#define DBGVIDSR (0x0A8)
#define DBGBVRm(n) (0x100 + (n * 4))
#define DBGBCRm(n) (0x140 + (n * 4))
#define DBGWVRm(n) (0x180 + (n * 4))
#define DBGWCRm(n) (0x1C0 + (n * 4))
#define DBGBXVRm(n) (0x240 + (n * 4))
#define DBGOSLAR (0x300)
#define DBGOSLSR (0x304)
#define DBGPRCR (0x310)
#define DBGPRSR (0x314)
#define DEBUG_LOCK(cpu) \
do { \
mb(); \
debug_writel(debug, cpu, MAGIC2, CS_LAR); \
} while (0)
#define DEBUG_UNLOCK(cpu) \
do { \
debug_writel(debug, cpu, MAGIC1, CS_LAR); \
mb(); \
} while (0)
#define DEBUG_OS_LOCK(cpu) \
do { \
debug_writel(debug, cpu, MAGIC1, DBGOSLAR); \
mb(); \
} while (0)
#define DEBUG_OS_UNLOCK(cpu) \
do { \
mb(); \
debug_writel(debug, cpu, MAGIC2, DBGOSLAR); \
mb(); \
} while (0)
#define MAX_DEBUG_REGS (90)
#define MAX_STATE_SIZE (MAX_DEBUG_REGS * num_possible_cpus())
#define DBGDSCR_MASK (0x6C30FC3C)
struct debug_config {
/* read only config register */
uint32_t dbg_id;
/* derived values */
uint8_t nr_watch_pts;
uint8_t nr_brk_pts;
uint8_t nr_ctx_comp;
};
struct debug_ctx {
struct debug_config cfg;
void __iomem **base;
uint32_t *state;
struct device *dev;
};
static struct debug_ctx debug;
static void debug_save_reg(int cpu)
{
uint32_t i;
int j;
DEBUG_UNLOCK(cpu);
DEBUG_OS_LOCK(cpu);
i = cpu * MAX_DEBUG_REGS;
debug.state[i++] = debug_readl(debug, cpu, DBGWFAR);
for (j = 0; j < debug.cfg.nr_brk_pts; j++) {
debug.state[i++] = debug_readl(debug, cpu, DBGBCRm(j));
debug.state[i++] = debug_readl(debug, cpu, DBGBVRm(j));
}
for (j = 0; j < debug.cfg.nr_ctx_comp; j++)
debug.state[i++] = debug_readl(debug, cpu, DBGBXVRm(j));
for (j = 0; j < debug.cfg.nr_watch_pts; j++) {
debug.state[i++] = debug_readl(debug, cpu, DBGWVRm(j));
debug.state[i++] = debug_readl(debug, cpu, DBGWCRm(j));
}
debug.state[i++] = debug_readl(debug, cpu, DBGVCR);
debug.state[i++] = debug_readl(debug, cpu, CS_CLAIMSET);
debug.state[i++] = debug_readl(debug, cpu, CS_CLAIMCLR);
debug.state[i++] = debug_readl(debug, cpu, DBGDTRTX);
debug.state[i++] = debug_readl(debug, cpu, DBGDTRRX);
debug.state[i++] = debug_readl(debug, cpu, DBGDSCR);
DEBUG_LOCK(cpu);
}
static void debug_restore_reg(int cpu)
{
uint32_t i;
int j;
DEBUG_UNLOCK(cpu);
DEBUG_OS_LOCK(cpu);
i = cpu * MAX_DEBUG_REGS;
debug_writel(debug, cpu, debug.state[i++], DBGWFAR);
for (j = 0; j < debug.cfg.nr_brk_pts; j++) {
debug_writel(debug, cpu, debug.state[i++], DBGBCRm(j));
debug_writel(debug, cpu, debug.state[i++], DBGBVRm(j));
}
for (j = 0; j < debug.cfg.nr_ctx_comp; j++)
debug_writel(debug, cpu, debug.state[i++], DBGBXVRm(j));
for (j = 0; j < debug.cfg.nr_watch_pts; j++) {
debug_writel(debug, cpu, debug.state[i++], DBGWVRm(j));
debug_writel(debug, cpu, debug.state[i++], DBGWCRm(j));
}
debug_writel(debug, cpu, debug.state[i++], DBGVCR);
debug_writel(debug, cpu, debug.state[i++], CS_CLAIMSET);
debug_writel(debug, cpu, debug.state[i++], CS_CLAIMCLR);
debug_writel(debug, cpu, debug.state[i++], DBGDTRTX);
debug_writel(debug, cpu, debug.state[i++], DBGDTRRX);
debug_writel(debug, cpu, debug.state[i++] & DBGDSCR_MASK, DBGDSCR);
DEBUG_OS_UNLOCK(cpu);
DEBUG_LOCK(cpu);
}
/* msm_save_jtag_debug and msm_restore_jtag_debug should be fast
*
* These functions will be called either from:
* 1. per_cpu idle thread context for idle power collapses.
* 2. per_cpu idle thread context for hotplug/suspend power collapse for
* nonboot cpus.
* 3. suspend thread context for core0.
*
* In all cases we are guaranteed to be running on the same cpu for the
* entire duration.
*/
void msm_save_jtag_debug(void)
{
int cpu = smp_processor_id();
debug_save_reg(cpu);
}
void msm_restore_jtag_debug(void)
{
int cpu = smp_processor_id();
debug_restore_reg(cpu);
}
static void debug_cfg_ro_init(void)
{
/* use cpu 0 for setup */
int cpu = 0;
DEBUG_UNLOCK(cpu);
debug.cfg.dbg_id = debug_readl(debug, cpu, DBGDIDR);
debug.cfg.nr_ctx_comp = BMVAL(debug.cfg.dbg_id, 20, 23) + 1;
debug.cfg.nr_brk_pts = BMVAL(debug.cfg.dbg_id, 24, 27) + 1;
debug.cfg.nr_watch_pts = BMVAL(debug.cfg.dbg_id, 28, 31) + 1;
DEBUG_LOCK(cpu);
}
static int __devinit debug_probe(struct platform_device *pdev)
{
int i, ret;
struct resource *res;
debug.base = kzalloc(pdev->num_resources * sizeof(void *), GFP_KERNEL);
if (!debug.base) {
ret = -ENOMEM;
goto err_base_kzalloc;
}
for (i = 0; i < pdev->num_resources; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res) {
ret = -EINVAL;
goto err_res;
}
debug.base[i] = ioremap_nocache(res->start, resource_size(res));
if (!debug.base[i]) {
ret = -EINVAL;
goto err_ioremap;
}
}
debug.dev = &pdev->dev;
debug.state = kzalloc(MAX_STATE_SIZE * sizeof(uint32_t), GFP_KERNEL);
if (!debug.state) {
ret = -ENOMEM;
goto err_state_kzalloc;
}
debug_cfg_ro_init();
dev_info(debug.dev, "Debug intialized.\n");
return 0;
err_state_kzalloc:
err_ioremap:
err_res:
while (i) {
iounmap(debug.base[i-1]);
i--;
}
kfree(debug.base);
err_base_kzalloc:
return ret;
}
static int __devexit debug_remove(struct platform_device *pdev)
{
int i;
kfree(debug.state);
for (i = pdev->num_resources; i > 0; i--)
iounmap(debug.base[i-1]);
kfree(debug.base);
return 0;
}
static struct platform_driver debug_driver = {
.probe = debug_probe,
.remove = __devexit_p(debug_remove),
.driver = {
.name = "msm_debug",
},
};
static int __init debug_init(void)
{
return platform_driver_register(&debug_driver);
}
module_init(debug_init);
static void __exit debug_exit(void)
{
platform_driver_unregister(&debug_driver);
}
module_exit(debug_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Coresight Debug driver");