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
|
// SPDX-License-Identifier: BSD-2-Clause
/*
* andes_pmu.c - Andes PMU device callbacks and platform overrides
*
* Copyright (C) 2023 Andes Technology Corporation
*/
#include <andes/andes45.h>
#include <andes/andes_pmu.h>
#include <sbi/sbi_bitops.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_pmu.h>
#include <libfdt.h>
static void andes_hw_counter_enable_irq(uint32_t ctr_idx)
{
unsigned long mip_val;
if (ctr_idx >= SBI_PMU_HW_CTR_MAX)
return;
mip_val = csr_read(CSR_MIP);
if (!(mip_val & MIP_PMOVI))
csr_clear(CSR_MCOUNTEROVF, BIT(ctr_idx));
csr_set(CSR_MCOUNTERINTEN, BIT(ctr_idx));
}
static void andes_hw_counter_disable_irq(uint32_t ctr_idx)
{
csr_clear(CSR_MCOUNTERINTEN, BIT(ctr_idx));
}
static void andes_hw_counter_filter_mode(unsigned long flags, int ctr_idx)
{
if (flags & SBI_PMU_CFG_FLAG_SET_UINH)
csr_set(CSR_MCOUNTERMASK_U, BIT(ctr_idx));
else
csr_clear(CSR_MCOUNTERMASK_U, BIT(ctr_idx));
if (flags & SBI_PMU_CFG_FLAG_SET_SINH)
csr_set(CSR_MCOUNTERMASK_S, BIT(ctr_idx));
else
csr_clear(CSR_MCOUNTERMASK_S, BIT(ctr_idx));
}
static struct sbi_pmu_device andes_pmu = {
.name = "andes_pmu",
.hw_counter_enable_irq = andes_hw_counter_enable_irq,
.hw_counter_disable_irq = andes_hw_counter_disable_irq,
/*
* We set delegation of supervisor local interrupts via
* 18th bit on mslideleg instead of mideleg, so leave
* hw_counter_irq_bit() callback unimplemented.
*/
.hw_counter_irq_bit = NULL,
.hw_counter_filter_mode = andes_hw_counter_filter_mode
};
int andes_pmu_extensions_init(const struct fdt_match *match,
struct sbi_hart_features *hfeatures)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
if (!has_andes_pmu())
return 0;
/*
* Don't expect both Andes PMU and standard Sscofpmf/Smcntrpmf,
* are supported as they serve the same purpose.
*/
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF) ||
sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCNTRPMF))
return SBI_EINVAL;
sbi_hart_update_extension(scratch, SBI_HART_EXT_XANDESPMU, true);
/* Inhibit all HPM counters in M-mode */
csr_write(CSR_MCOUNTERMASK_M, 0xfffffffd);
/* Delegate counter overflow interrupt to S-mode */
csr_write(CSR_MSLIDELEG, MIP_PMOVI);
return 0;
}
int andes_pmu_init(const struct fdt_match *match)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_XANDESPMU))
sbi_pmu_set_device(&andes_pmu);
return 0;
}
|