239 lines
4.7 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
*/
#include <asm/arch_timer.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "rkpm_helpers.h"
/* REG region */
#define RGN_LEN(_rgn) (((_rgn)->end - (_rgn)->start) / (_rgn)->stride + 1)
static u32 *region_mem;
static u32 region_mem_size;
static int region_mem_idx;
static int alloc_region_mem(u32 *buf, int max_len,
struct reg_region *rgns, u32 rgn_num)
{
int i;
int total_len = 0, len = 0;
struct reg_region *r = rgns;
if (!buf || !rgns) {
pr_err("%s invalid parameter\n", __func__);
return 0;
}
for (i = 0; i < rgn_num; i++, r++) {
if (total_len < max_len)
r->buf = &buf[total_len];
len = RGN_LEN(r);
total_len += len;
}
if (len >= max_len) {
pr_err("%s The buffer remain length:%d is too small for region:0x%x, at least %d\n",
__func__, max_len, rgns[0].start, total_len);
return -ENOMEM;
}
return total_len;
}
/**
* Alloc memory to reg_region->buf from region_mem.
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rkpm_alloc_region_mem(struct reg_region *rgns, u32 rgn_num)
{
int max_len = 0, len;
max_len = region_mem_size / sizeof(u32) -
region_mem_idx;
len = alloc_region_mem(region_mem + region_mem_idx, max_len,
rgns, rgn_num);
region_mem_idx += len;
}
void rkpm_region_mem_init(u32 size)
{
if (!size) {
pr_err("%s invalid param\n", __func__);
return;
}
region_mem = kmalloc(size, GFP_KERNEL);
if (!region_mem) {
pr_err("%s malloc region memory (0x%x) err\n", __func__, size);
return;
}
region_mem_size = size;
}
/**
* Save (reg_region->start ~ reg_region->end) to reg_region->buf.
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rkpm_reg_rgn_save(struct reg_region *rgns, u32 rgn_num)
{
struct reg_region *r;
u8 *addr;
u8 *start, *end;
int i, j;
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
start = (char *)(*r->base) + r->start;
end = (char *)(*r->base) + r->end;
for (j = 0, addr = start; addr <= end; addr += r->stride, j++)
r->buf[j] = readl_relaxed(addr);
}
}
/**
* Restore reg_region->buf to (reg_region->start ~ reg_region->end).
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rkpm_reg_rgn_restore(struct reg_region *rgns, u32 rgn_num)
{
struct reg_region *r;
u8 *addr;
u8 *start, *end;
int i, j;
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
start = (char *)(*r->base) + r->start;
end = (char *)(*r->base) + r->end;
for (j = 0, addr = start; addr <= end; addr += r->stride, j++)
writel_relaxed(r->buf[j] | r->wmsk, addr);
}
}
void rkpm_reg_rgn_restore_reverse(struct reg_region *rgns, u32 rgn_num)
{
struct reg_region *r;
u8 *addr;
u8 *start, *end;
int i, j;
for (i = rgn_num - 1; i >= 0; i--) {
r = &rgns[i];
start = (char *)(*r->base) + r->start;
end = (char *)(*r->base) + r->end;
j = RGN_LEN(r) - 1;
for (addr = end; addr >= start; addr -= r->stride, j--)
writel_relaxed(r->buf[j] | r->wmsk, addr);
}
}
/**
* Dump reg regions
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rkpm_dump_reg_rgns(struct reg_region *rgns, u32 rgn_num)
{
struct reg_region *r;
int i;
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
rkpm_regs_dump(*r->base, r->start, r->end, r->stride);
}
}
#pragma weak rkpm_printch
void rkpm_printch(int c)
{
}
void rkpm_printstr(const char *s)
{
while (*s) {
rkpm_printch(*s);
s++;
}
}
void rkpm_printhex(u32 hex)
{
u8 i = 8;
u8 c;
rkpm_printch('0');
rkpm_printch('x');
while (i--) {
c = (hex & 0xf0000000) >> 28;
rkpm_printch(c < 0xa ? c + '0' : c - 0xa + 'a');
hex <<= 4;
}
}
void rkpm_printdec(int dec)
{
int i, tmp = dec;
if (dec < 0) {
rkpm_printch('-');
tmp = -dec;
dec = -dec;
}
for (i = 1; tmp / 10; tmp /= 10, i *= 10)
;
for (; i >= 1; i /= 10) {
rkpm_printch('0' + (char)(dec / i));
dec %= i;
}
}
void rkpm_regs_dump(void __iomem *base,
u32 start_offset,
u32 end_offset,
u32 stride)
{
u32 i;
for (i = start_offset; i <= end_offset; i += stride) {
if ((i - start_offset) % 16 == 0) {
rkpm_printch('\n');
rkpm_printhex((u32)base + i);
rkpm_printch(':');
rkpm_printch(' ');
rkpm_printch(' ');
rkpm_printch(' ');
rkpm_printch(' ');
}
rkpm_printhex(readl_relaxed(base + i));
rkpm_printch(' ');
rkpm_printch(' ');
rkpm_printch(' ');
rkpm_printch(' ');
}
rkpm_printch('\n');
}
void rkpm_raw_udelay(int us)
{
u64 cur_cnt = __arch_counter_get_cntpct();
u64 del = us * 24;
while (__arch_counter_get_cntpct() - cur_cnt < del)
;
}