2018-12-28 16:32:11 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2010-01-05 18:06:45 +08:00
|
|
|
/*
|
|
|
|
* arch/sh/kernel/cpu/sh4a/ubc.c
|
|
|
|
*
|
|
|
|
* On-chip UBC support for SH-4A CPUs.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 - 2010 Paul Mundt
|
|
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/hw_breakpoint.h>
|
|
|
|
|
|
|
|
#define UBC_CBR(idx) (0xff200000 + (0x20 * idx))
|
|
|
|
#define UBC_CRR(idx) (0xff200004 + (0x20 * idx))
|
|
|
|
#define UBC_CAR(idx) (0xff200008 + (0x20 * idx))
|
|
|
|
#define UBC_CAMR(idx) (0xff20000c + (0x20 * idx))
|
|
|
|
|
|
|
|
#define UBC_CCMFR 0xff200600
|
|
|
|
#define UBC_CBCR 0xff200620
|
|
|
|
|
|
|
|
/* CRR */
|
|
|
|
#define UBC_CRR_PCB (1 << 1)
|
|
|
|
#define UBC_CRR_BIE (1 << 0)
|
|
|
|
|
|
|
|
/* CBR */
|
|
|
|
#define UBC_CBR_CE (1 << 0)
|
|
|
|
|
|
|
|
static struct sh_ubc sh4a_ubc;
|
|
|
|
|
|
|
|
static void sh4a_ubc_enable(struct arch_hw_breakpoint *info, int idx)
|
|
|
|
{
|
|
|
|
__raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR(idx));
|
|
|
|
__raw_writel(info->address, UBC_CAR(idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sh4a_ubc_disable(struct arch_hw_breakpoint *info, int idx)
|
|
|
|
{
|
|
|
|
__raw_writel(0, UBC_CBR(idx));
|
|
|
|
__raw_writel(0, UBC_CAR(idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sh4a_ubc_enable_all(unsigned long mask)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sh4a_ubc.num_events; i++)
|
|
|
|
if (mask & (1 << i))
|
|
|
|
__raw_writel(__raw_readl(UBC_CBR(i)) | UBC_CBR_CE,
|
|
|
|
UBC_CBR(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sh4a_ubc_disable_all(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sh4a_ubc.num_events; i++)
|
|
|
|
__raw_writel(__raw_readl(UBC_CBR(i)) & ~UBC_CBR_CE,
|
|
|
|
UBC_CBR(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long sh4a_ubc_active_mask(void)
|
|
|
|
{
|
|
|
|
unsigned long active = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sh4a_ubc.num_events; i++)
|
|
|
|
if (__raw_readl(UBC_CBR(i)) & UBC_CBR_CE)
|
|
|
|
active |= (1 << i);
|
|
|
|
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long sh4a_ubc_triggered_mask(void)
|
|
|
|
{
|
|
|
|
return __raw_readl(UBC_CCMFR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sh4a_ubc_clear_triggered_mask(unsigned long mask)
|
|
|
|
{
|
|
|
|
__raw_writel(__raw_readl(UBC_CCMFR) & ~mask, UBC_CCMFR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sh_ubc sh4a_ubc = {
|
|
|
|
.name = "SH-4A",
|
|
|
|
.num_events = 2,
|
|
|
|
.trap_nr = 0x1e0,
|
|
|
|
.enable = sh4a_ubc_enable,
|
|
|
|
.disable = sh4a_ubc_disable,
|
|
|
|
.enable_all = sh4a_ubc_enable_all,
|
|
|
|
.disable_all = sh4a_ubc_disable_all,
|
|
|
|
.active_mask = sh4a_ubc_active_mask,
|
|
|
|
.triggered_mask = sh4a_ubc_triggered_mask,
|
|
|
|
.clear_triggered_mask = sh4a_ubc_clear_triggered_mask,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init sh4a_ubc_init(void)
|
|
|
|
{
|
|
|
|
struct clk *ubc_iclk = clk_get(NULL, "ubc0");
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The UBC MSTP bit is optional, as not all platforms will have
|
|
|
|
* it. Just ignore it if we can't find it.
|
|
|
|
*/
|
|
|
|
if (IS_ERR(ubc_iclk))
|
|
|
|
ubc_iclk = NULL;
|
|
|
|
|
|
|
|
clk_enable(ubc_iclk);
|
|
|
|
|
|
|
|
__raw_writel(0, UBC_CBCR);
|
|
|
|
|
|
|
|
for (i = 0; i < sh4a_ubc.num_events; i++) {
|
|
|
|
__raw_writel(0, UBC_CAMR(i));
|
|
|
|
__raw_writel(0, UBC_CBR(i));
|
|
|
|
|
|
|
|
__raw_writel(UBC_CRR_BIE | UBC_CRR_PCB, UBC_CRR(i));
|
|
|
|
|
|
|
|
/* dummy read for write posting */
|
|
|
|
(void)__raw_readl(UBC_CRR(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
clk_disable(ubc_iclk);
|
|
|
|
|
|
|
|
sh4a_ubc.clk = ubc_iclk;
|
|
|
|
|
|
|
|
return register_sh_ubc(&sh4a_ubc);
|
|
|
|
}
|
|
|
|
arch_initcall(sh4a_ubc_init);
|