mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
2.8 KiB
130 lines
2.8 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* 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);
|
|
|