diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2013-08-20 10:36:40 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2013-08-20 10:36:40 +1000 |
commit | e17b4934fae1e131c8ad11da6cd6cd078dd84484 (patch) | |
tree | 728708c3d36f48f16b3920030e6b95e51a67ac8d /arch/sh | |
parent | 306935f626b7fa4dca08bda87e68ffbe89297adc (diff) | |
parent | 37284bd93103ae36b5ea274a985d688fb16f7a38 (diff) |
Merge remote-tracking branch 'sh/sh-latest'
Conflicts:
arch/sh/kernel/cpu/sh2a/Makefile
include/linux/serial_sci.h
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/include/asm/hw_breakpoint.h | 12 | ||||
-rw-r--r-- | arch/sh/include/cpu-common/cpu/ubc.h | 17 | ||||
-rw-r--r-- | arch/sh/include/cpu-sh2a/cpu/ubc.h | 14 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh2a/Makefile | 1 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh2a/ubc.c | 154 | ||||
-rw-r--r-- | arch/sh/kernel/hw_breakpoint.c | 8 |
6 files changed, 195 insertions, 11 deletions
diff --git a/arch/sh/include/asm/hw_breakpoint.h b/arch/sh/include/asm/hw_breakpoint.h index ec9ad593c3da..01a38696137e 100644 --- a/arch/sh/include/asm/hw_breakpoint.h +++ b/arch/sh/include/asm/hw_breakpoint.h @@ -7,6 +7,7 @@ #include <linux/kdebug.h> #include <linux/types.h> +#include <cpu/ubc.h> struct arch_hw_breakpoint { char *name; /* Contains name of the symbol to set bkpt */ @@ -15,17 +16,6 @@ struct arch_hw_breakpoint { u16 type; }; -enum { - SH_BREAKPOINT_READ = (1 << 1), - SH_BREAKPOINT_WRITE = (1 << 2), - SH_BREAKPOINT_RW = SH_BREAKPOINT_READ | SH_BREAKPOINT_WRITE, - - SH_BREAKPOINT_LEN_1 = (1 << 12), - SH_BREAKPOINT_LEN_2 = (1 << 13), - SH_BREAKPOINT_LEN_4 = SH_BREAKPOINT_LEN_1 | SH_BREAKPOINT_LEN_2, - SH_BREAKPOINT_LEN_8 = (1 << 14), -}; - struct sh_ubc { const char *name; unsigned int num_events; diff --git a/arch/sh/include/cpu-common/cpu/ubc.h b/arch/sh/include/cpu-common/cpu/ubc.h new file mode 100644 index 000000000000..b60461930a32 --- /dev/null +++ b/arch/sh/include/cpu-common/cpu/ubc.h @@ -0,0 +1,17 @@ +#ifndef __ARCH_SH_CPU_UBC_H__ +#define __ARCH_SH_CPU_UBC_H__ + +enum { + SH_BREAKPOINT_READ = (1 << 1), + SH_BREAKPOINT_WRITE = (1 << 2), + SH_BREAKPOINT_RW = SH_BREAKPOINT_READ | SH_BREAKPOINT_WRITE, + + SH_BREAKPOINT_LEN_1 = (1 << 12), + SH_BREAKPOINT_LEN_2 = (1 << 13), + SH_BREAKPOINT_LEN_4 = SH_BREAKPOINT_LEN_1 | SH_BREAKPOINT_LEN_2, + SH_BREAKPOINT_LEN_8 = (1 << 14), +}; + +#define UBC_64BIT 1 + +#endif /* __ARCH_SH_CPU_UBC_H__ */ diff --git a/arch/sh/include/cpu-sh2a/cpu/ubc.h b/arch/sh/include/cpu-sh2a/cpu/ubc.h new file mode 100644 index 000000000000..3371f9042184 --- /dev/null +++ b/arch/sh/include/cpu-sh2a/cpu/ubc.h @@ -0,0 +1,14 @@ +#ifndef __ARCH_SH_CPU_UBC_H__ +#define __ARCH_SH_CPU_UBC_H__ + +enum { + SH_BREAKPOINT_READ = (1 << 2), + SH_BREAKPOINT_WRITE = (1 << 3), + SH_BREAKPOINT_RW = SH_BREAKPOINT_READ | SH_BREAKPOINT_WRITE, + + SH_BREAKPOINT_LEN_1 = (1 << 0), + SH_BREAKPOINT_LEN_2 = (1 << 1), + SH_BREAKPOINT_LEN_4 = SH_BREAKPOINT_LEN_1 | SH_BREAKPOINT_LEN_2, +}; + +#endif /* __ARCH_SH_CPU_UBC_H__ */ diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile index 990195d98456..92f0da4c86a7 100644 --- a/arch/sh/kernel/cpu/sh2a/Makefile +++ b/arch/sh/kernel/cpu/sh2a/Makefile @@ -22,3 +22,4 @@ pinmux-$(CONFIG_CPU_SUBTYPE_SH7264) := pinmux-sh7264.o pinmux-$(CONFIG_CPU_SUBTYPE_SH7269) := pinmux-sh7269.o obj-$(CONFIG_GPIOLIB) += $(pinmux-y) +obj-$(CONFIG_HAVE_HW_BREAKPOINT) += ubc.o diff --git a/arch/sh/kernel/cpu/sh2a/ubc.c b/arch/sh/kernel/cpu/sh2a/ubc.c new file mode 100644 index 000000000000..ef95a9b483e4 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/ubc.c @@ -0,0 +1,154 @@ +/* + * arch/sh/kernel/cpu/sh2a/ubc.c + * + * On-chip UBC support for SH-2A 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_BAR(idx) (0xfffc0400 + (0x10 * idx)) +#define UBC_BAMR(idx) (0xfffc0404 + (0x10 * idx)) +#define UBC_BBR(idx) (0xfffc04A0 + (0x10 * idx)) +#define UBC_BDR(idx) (0xfffc0408 + (0x10 * idx)) +#define UBC_BDMR(idx) (0xfffc040C + (0x10 * idx)) + +#define UBC_BRCR 0xfffc04C0 + +/* BBR */ +#define UBC_BBR_UBID (1 << 13) /* User Break Interrupt Disable */ +#define UBC_BBR_DBE (1 << 12) /* Data Break Enable */ +#define UBC_BBR_CD_C (1 << 6) /* C Bus Cycle */ +#define UBC_BBR_CD_I (2 << 6) /* I Bus Cycle */ +#define UBC_BBR_ID_I (1 << 4) /* Break Condition is instruction fetch cycle */ +#define UBC_BBR_ID_D (2 << 4) /* Break Condition is data access cycle */ +#define UBC_BBR_ID_ID (3 << 4) /* Break Condition is instruction fetch or data access cycle */ + +#define UBC_CRR_BIE (1 << 0) + +/* CBR */ +#define UBC_CBR_CE (1 << 0) + +static struct sh_ubc sh2a_ubc; + +static void sh2a_ubc_enable(struct arch_hw_breakpoint *info, int idx) +{ + __raw_writel(UBC_BBR_DBE | UBC_BBR_CD_C | UBC_BBR_ID_ID | + info->len | info->type, UBC_BBR(idx)); + __raw_writel(info->address, UBC_BAR(idx)); +} + +static void sh2a_ubc_disable(struct arch_hw_breakpoint *info, int idx) +{ + __raw_writel(UBC_BBR_UBID, UBC_BBR(idx)); + __raw_writel(0, UBC_BAR(idx)); +} + +static void sh2a_ubc_enable_all(unsigned long mask) +{ + int i; + + for (i = 0; i < sh2a_ubc.num_events; i++) + if (mask & (1 << i)) + __raw_writel(__raw_readl(UBC_BBR(i)) & ~UBC_BBR_UBID, + UBC_BBR(i)); +} + +static void sh2a_ubc_disable_all(void) +{ + int i; + + for (i = 0; i < sh2a_ubc.num_events; i++) + __raw_writel(__raw_readl(UBC_BBR(i)) | UBC_BBR_UBID, + UBC_BBR(i)); +} + +static unsigned long sh2a_ubc_active_mask(void) +{ + unsigned long active = 0; + int i; + + for (i = 0; i < sh2a_ubc.num_events; i++) + if (!(__raw_readl(UBC_BBR(i)) & UBC_BBR_UBID)) + active |= (1 << i); + + return active; +} + +static unsigned long sh2a_ubc_triggered_mask(void) +{ + unsigned int ret, mask; + + mask = 0; + ret = __raw_readl(UBC_BRCR); + if ((ret & (1 << 15)) || (ret & (1 << 13))) { + mask |= (1 << 0); /* Match condition for channel 0 */ + } else + mask &= ~(1 << 0); + + if ((ret & (1 << 14)) || (ret & (1 << 12))) { + mask |= (1 << 1); /* Match condition for channel 1 */ + } else + mask &= ~(1 << 1); + + return mask; +} + +static void sh2a_ubc_clear_triggered_mask(unsigned long mask) +{ + if (mask & (1 << 0)) /* Channel 0 statisfied break condition */ + __raw_writel(__raw_readl(UBC_BRCR) & + ~((1 << 15) | (1 << 13)), UBC_BRCR); + + if (mask & (1 << 1)) /* Channel 1 statisfied break condition */ + __raw_writel(__raw_readl(UBC_BRCR) & + ~((1 << 14) | (1 << 12)), UBC_BRCR); +} + +static struct sh_ubc sh2a_ubc = { + .name = "SH-2A", + .num_events = 2, + .trap_nr = 0x1e0, + .enable = sh2a_ubc_enable, + .disable = sh2a_ubc_disable, + .enable_all = sh2a_ubc_enable_all, + .disable_all = sh2a_ubc_disable_all, + .active_mask = sh2a_ubc_active_mask, + .triggered_mask = sh2a_ubc_triggered_mask, + .clear_triggered_mask = sh2a_ubc_clear_triggered_mask, +}; + +static int __init sh2a_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); + + for (i = 0; i < sh2a_ubc.num_events; i++) { + __raw_writel(0, UBC_BAMR(i)); + __raw_writel(0, UBC_BBR(i)); + } + + clk_disable(ubc_iclk); + + sh2a_ubc.clk = ubc_iclk; + + return register_sh_ubc(&sh2a_ubc); +} +arch_initcall(sh2a_ubc_init); diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index f9173766ec4b..ac4922ad3c14 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -113,9 +113,11 @@ static int get_hbp_len(u16 hbp_len) case SH_BREAKPOINT_LEN_4: len_in_bytes = 4; break; +#ifdef UBC_64BIT case SH_BREAKPOINT_LEN_8: len_in_bytes = 8; break; +#endif } return len_in_bytes; } @@ -149,9 +151,11 @@ int arch_bp_generic_fields(int sh_len, int sh_type, case SH_BREAKPOINT_LEN_4: *gen_len = HW_BREAKPOINT_LEN_4; break; +#ifdef UBC_64BIT case SH_BREAKPOINT_LEN_8: *gen_len = HW_BREAKPOINT_LEN_8; break; +#endif default: return -EINVAL; } @@ -190,9 +194,11 @@ static int arch_build_bp_info(struct perf_event *bp) case HW_BREAKPOINT_LEN_4: info->len = SH_BREAKPOINT_LEN_4; break; +#ifdef UBC_64BIT case HW_BREAKPOINT_LEN_8: info->len = SH_BREAKPOINT_LEN_8; break; +#endif default: return -EINVAL; } @@ -240,9 +246,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) case SH_BREAKPOINT_LEN_4: align = 3; break; +#ifdef UBC_64BIT case SH_BREAKPOINT_LEN_8: align = 7; break; +#endif default: return ret; } |