Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | // 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); |