302 lines
5.6 KiB
C
302 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2019 MediaTek Inc.
|
|
*/
|
|
#include "mdla_debug.h"
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/types.h>
|
|
#include <linux/bitmap.h>
|
|
|
|
#include "mdla_hw_reg.h"
|
|
#include "mdla_pmu.h"
|
|
#include "mdla.h"
|
|
|
|
#define COUNTER_CLEAR 0xFFFFFFFF
|
|
|
|
DECLARE_BITMAP(pmu_bitmap, MDLA_PMU_COUNTERS);
|
|
DEFINE_SPINLOCK(pmu_lock);
|
|
|
|
/* saved registers, used to restore config after pmu reset */
|
|
u32 cfg_pmu_event[MDLA_PMU_COUNTERS];
|
|
static u32 cfg_pmu_clr_mode;
|
|
/* lastest register values, since last command end */
|
|
static u32 l_counters[MDLA_PMU_COUNTERS];
|
|
static u32 l_start_t;
|
|
static u32 l_end_t;
|
|
static u32 l_cycle;
|
|
|
|
unsigned int pmu_reg_read(u32 offset)
|
|
{
|
|
return ioread32(apu_mdla_biu_top + offset);
|
|
}
|
|
|
|
static void pmu_reg_write(u32 value, u32 offset)
|
|
{
|
|
iowrite32(value, apu_mdla_biu_top + offset);
|
|
}
|
|
|
|
#define pmu_reg_set(mask, offset) \
|
|
pmu_reg_write(pmu_reg_read(offset) | (mask), (offset))
|
|
|
|
#define pmu_reg_clear(mask, offset) \
|
|
pmu_reg_write(pmu_reg_read(offset) & ~(mask), (offset))
|
|
|
|
/*
|
|
* API naming rules
|
|
* pmu_xxx_save(): save registers to variables
|
|
* pmu_xxx_get(): load values from saved variables.
|
|
* pmu_xxx_read(): read values from registers.
|
|
* pmu_xxx_write(): write values to registers.
|
|
*/
|
|
|
|
static int pmu_event_write(u32 handle, u32 val)
|
|
{
|
|
u32 mask;
|
|
|
|
if (handle >= MDLA_PMU_COUNTERS)
|
|
return -EINVAL;
|
|
|
|
mask = 1 << (handle+17);
|
|
|
|
if (val == COUNTER_CLEAR) {
|
|
mdla_pmu_debug("%s: clear pmu counter[%d]\n",
|
|
__func__, handle);
|
|
pmu_reg_write(0, PMU_EVENT_OFFSET +
|
|
(handle) * PMU_CNT_SHIFT);
|
|
pmu_reg_clear(mask, PMU_CFG_PMCR);
|
|
} else {
|
|
mdla_pmu_debug("%s: set pmu counter[%d] = 0x%x\n",
|
|
__func__, handle, val);
|
|
pmu_reg_write(val, PMU_EVENT_OFFSET +
|
|
(handle) * PMU_CNT_SHIFT);
|
|
pmu_reg_set(mask, PMU_CFG_PMCR);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pmu_counter_alloc(u32 interface, u32 event)
|
|
{
|
|
unsigned long flags;
|
|
int handle;
|
|
|
|
mutex_lock(&cmd_lock);
|
|
mutex_lock(&power_lock);
|
|
|
|
spin_lock_irqsave(&pmu_lock, flags);
|
|
handle = bitmap_find_free_region(pmu_bitmap, MDLA_PMU_COUNTERS, 0);
|
|
spin_unlock_irqrestore(&pmu_lock, flags);
|
|
if (unlikely(handle < 0))
|
|
goto out;
|
|
|
|
pmu_counter_event_save(handle, ((interface << 16) | event));
|
|
|
|
out:
|
|
mutex_unlock(&power_lock);
|
|
mutex_unlock(&cmd_lock);
|
|
return handle;
|
|
}
|
|
EXPORT_SYMBOL(pmu_counter_alloc);
|
|
|
|
int pmu_counter_free(int handle)
|
|
{
|
|
if ((handle >= MDLA_PMU_COUNTERS) || (handle < 0))
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&cmd_lock);
|
|
mutex_lock(&power_lock);
|
|
|
|
bitmap_release_region(pmu_bitmap, handle, 0);
|
|
|
|
if (get_power_on_status())
|
|
pmu_event_write(handle, COUNTER_CLEAR);
|
|
|
|
mutex_unlock(&power_lock);
|
|
mutex_unlock(&cmd_lock);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(pmu_counter_free);
|
|
|
|
int pmu_counter_event_save(u32 handle, u32 val)
|
|
{
|
|
if (handle >= MDLA_PMU_COUNTERS)
|
|
return -EINVAL;
|
|
|
|
cfg_pmu_event[handle] = val;
|
|
|
|
if (!get_power_on_status())
|
|
return 0;
|
|
|
|
return pmu_event_write(handle, val);
|
|
}
|
|
|
|
int pmu_counter_event_get(int handle)
|
|
{
|
|
u32 event;
|
|
|
|
if ((handle >= MDLA_PMU_COUNTERS) || (handle < 0))
|
|
return -EINVAL;
|
|
|
|
event = cfg_pmu_event[handle];
|
|
|
|
return (event == COUNTER_CLEAR) ? -ENOENT : event;
|
|
}
|
|
|
|
int pmu_counter_event_get_all(u32 out[MDLA_PMU_COUNTERS])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MDLA_PMU_COUNTERS; i++)
|
|
out[i] = cfg_pmu_event[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void pmu_counter_read_all(u32 out[MDLA_PMU_COUNTERS])
|
|
{
|
|
int i;
|
|
u32 offset;
|
|
u32 reg;
|
|
|
|
offset = PMU_CNT_OFFSET;
|
|
reg = pmu_reg_read(PMU_CFG_PMCR);
|
|
|
|
if ((1<<PMU_CLR_CMDE_SHIFT) & reg)
|
|
offset = offset + 4;
|
|
|
|
for (i = 0; i < MDLA_PMU_COUNTERS; i++)
|
|
out[i] = pmu_reg_read(offset + (i * PMU_CNT_SHIFT));
|
|
}
|
|
|
|
u32 pmu_counter_get(int handle)
|
|
{
|
|
if ((handle >= MDLA_PMU_COUNTERS) || (handle < 0))
|
|
return -EINVAL;
|
|
|
|
return l_counters[handle];
|
|
}
|
|
|
|
void pmu_counter_get_all(u32 out[MDLA_PMU_COUNTERS])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MDLA_PMU_COUNTERS; i++)
|
|
out[i] = l_counters[i];
|
|
}
|
|
|
|
u32 pmu_get_perf_start(void)
|
|
{
|
|
return l_start_t;
|
|
}
|
|
|
|
u32 pmu_get_perf_end(void)
|
|
{
|
|
return l_end_t;
|
|
}
|
|
|
|
u32 pmu_get_perf_cycle(void)
|
|
{
|
|
return l_cycle;
|
|
}
|
|
|
|
static void pmu_reset_counter(void)
|
|
{
|
|
mdla_pmu_debug("mdla: %s\n", __func__);
|
|
|
|
if (!get_power_on_status())
|
|
return;
|
|
|
|
pmu_reg_set(PMU_PMCR_CNT_RST, PMU_CFG_PMCR);
|
|
while (pmu_reg_read(PMU_CFG_PMCR) &
|
|
PMU_PMCR_CNT_RST) {
|
|
}
|
|
}
|
|
|
|
static void pmu_reset_cycle(void)
|
|
{
|
|
mdla_pmu_debug("mdla: %s\n", __func__);
|
|
|
|
if (!get_power_on_status())
|
|
return;
|
|
|
|
pmu_reg_set((PMU_PMCR_CCNT_EN | PMU_PMCR_CCNT_RST), PMU_CFG_PMCR);
|
|
while (pmu_reg_read(PMU_CFG_PMCR) &
|
|
PMU_PMCR_CCNT_RST) {
|
|
}
|
|
}
|
|
|
|
void pmu_reset_saved_counter(void)
|
|
{
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < MDLA_PMU_COUNTERS; i++)
|
|
l_counters[i] = 0;
|
|
|
|
pmu_reset_counter();
|
|
}
|
|
|
|
void pmu_reset_saved_cycle(void)
|
|
{
|
|
|
|
l_cycle = 0;
|
|
pmu_reset_cycle();
|
|
}
|
|
|
|
/* 1: PMU cleary by each command end */
|
|
static void pmu_clr_mode_write(u32 mode)
|
|
{
|
|
u32 mask = (1 << PMU_CLR_CMDE_SHIFT);
|
|
|
|
if (!get_power_on_status())
|
|
return;
|
|
|
|
if (mode)
|
|
pmu_reg_set(mask, PMU_CFG_PMCR);
|
|
else
|
|
pmu_reg_clear(mask, PMU_CFG_PMCR);
|
|
}
|
|
|
|
void pmu_clr_mode_save(u32 mode)
|
|
{
|
|
cfg_pmu_clr_mode = mode;
|
|
pmu_clr_mode_write(mode);
|
|
}
|
|
|
|
/* save pmu registers for query after power off */
|
|
void pmu_reg_save(void)
|
|
{
|
|
l_cycle = pmu_reg_read(PMU_CYCLE);
|
|
l_end_t = pmu_reg_read(PMU_END_TSTAMP);
|
|
l_start_t = pmu_reg_read(PMU_START_TSTAMP);
|
|
|
|
pmu_counter_read_all(l_counters);
|
|
}
|
|
|
|
void pmu_reset(void)
|
|
{
|
|
int i;
|
|
|
|
pmu_reg_write((CFG_PMCR_DEFAULT|
|
|
PMU_PMCR_CCNT_RST|PMU_PMCR_CNT_RST), PMU_CFG_PMCR);
|
|
|
|
while (pmu_reg_read(PMU_CFG_PMCR) &
|
|
(PMU_PMCR_CCNT_RST|PMU_PMCR_CNT_RST)) {
|
|
}
|
|
|
|
for (i = 0; i < MDLA_PMU_COUNTERS; i++)
|
|
pmu_event_write(i, cfg_pmu_event[i]);
|
|
|
|
pmu_clr_mode_write(cfg_pmu_clr_mode);
|
|
}
|
|
|
|
void pmu_init(void)
|
|
{
|
|
cfg_pmu_clr_mode = 0;
|
|
memset(cfg_pmu_event, 0xFF, sizeof(cfg_pmu_event));
|
|
}
|
|
|