// 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);