Skip to content

Commit

Permalink
sh: Abstracted SH-4A UBC support on hw-breakpoint core.
Browse files Browse the repository at this point in the history
This is the next big chunk of hw_breakpoint support. This decouples
the SH-4A support from the core and moves it out in to its own stub,
following many of the conventions established with the perf events
layering.

In addition to extending SH-4A support to encapsulate the remainder
of the UBC channels, clock framework support for handling the UBC
interface clock is added as well, allowing for dynamic clock gating.

This also fixes up a regression introduced by the SIGTRAP handling that
broke the ksym_tracer, to the extent that the current support works well
with all of the ksym_tracer/ptrace/kgdb. The kprobes singlestep code will
follow in turn.

With this in place, the remaining UBC variants (SH-2A and SH-4) can now
be trivially plugged in.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed Jan 5, 2010
1 parent c476181 commit 4352fc1
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 78 deletions.
22 changes: 18 additions & 4 deletions arch/sh/include/asm/hw_breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@

#include <linux/kdebug.h>
#include <linux/types.h>
#include <asm/ubc.h>

#ifdef __KERNEL__
#define __ARCH_HW_BREAKPOINT_H

struct arch_hw_breakpoint {
char *name; /* Contains name of the symbol to set bkpt */
unsigned long address;
unsigned long asid;
u16 len;
u16 type;
};
Expand All @@ -27,13 +25,28 @@ enum {
SH_BREAKPOINT_LEN_8 = (1 << 14),
};

/* Total number of available UBC channels */
#define HBP_NUM 1 /* XXX */
struct sh_ubc {
const char *name;
unsigned int num_events;
unsigned int trap_nr;
void (*enable)(struct arch_hw_breakpoint *, int);
void (*disable)(struct arch_hw_breakpoint *, int);
void (*enable_all)(unsigned long);
void (*disable_all)(void);
unsigned long (*active_mask)(void);
unsigned long (*triggered_mask)(void);
void (*clear_triggered_mask)(unsigned long);
struct clk *clk; /* optional interface clock / MSTP bit */
};

struct perf_event;
struct task_struct;
struct pmu;

/* Maximum number of UBC channels */
#define HBP_NUM 2

/* arch/sh/kernel/hw_breakpoint.c */
extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp,
struct task_struct *tsk);
Expand All @@ -46,6 +59,7 @@ void hw_breakpoint_pmu_read(struct perf_event *bp);
void hw_breakpoint_pmu_unthrottle(struct perf_event *bp);

extern void arch_fill_perf_breakpoint(struct perf_event *bp);
extern int register_sh_ubc(struct sh_ubc *);

extern struct pmu perf_ops_bp;

Expand Down
4 changes: 2 additions & 2 deletions arch/sh/include/asm/processor_32.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <asm/page.h>
#include <asm/types.h>
#include <asm/ptrace.h>
#include <asm/ubc.h>
#include <asm/hw_breakpoint.h>

/*
* Default implementation of macro that returns current
Expand Down Expand Up @@ -102,7 +102,7 @@ struct thread_struct {
unsigned long pc;

/* Save middle states of ptrace breakpoints */
struct perf_event *ptrace_bps[NR_UBC_CHANNELS];
struct perf_event *ptrace_bps[HBP_NUM];

/* floating point info */
union sh_fpu_union fpu;
Expand Down
9 changes: 5 additions & 4 deletions arch/sh/kernel/cpu/sh4a/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ pinmux-$(CONFIG_CPU_SUBTYPE_SH7757) := pinmux-sh7757.o
pinmux-$(CONFIG_CPU_SUBTYPE_SH7785) := pinmux-sh7785.o
pinmux-$(CONFIG_CPU_SUBTYPE_SH7786) := pinmux-sh7786.o

obj-y += $(clock-y)
obj-$(CONFIG_SMP) += $(smp-y)
obj-$(CONFIG_GENERIC_GPIO) += $(pinmux-y)
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-y += $(clock-y)
obj-$(CONFIG_SMP) += $(smp-y)
obj-$(CONFIG_GENERIC_GPIO) += $(pinmux-y)
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += ubc.o
133 changes: 133 additions & 0 deletions arch/sh/kernel/cpu/sh4a/ubc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* arch/sh/kernel/cpu/sh4a/ubc.c
*
* On-chip UBC support for SH-4A CPUs.
*
* Copyright (C) 2009 - 2010 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#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);
Loading

0 comments on commit 4352fc1

Please sign in to comment.