blob: 191b0cae076465673438e90560e2062123ac154e [file] [log] [blame]
/*
* Copyright (c) 2024, Rockchip, Inc. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <arch_helpers.h>
#include <bl31/bl31.h>
#include <common/debug.h>
#include <drivers/console.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>
#include <platform_def.h>
#include <plat_pm_helpers.h>
#define ROCKCHIP_PM_REG_REGION_MEM_LEN (ROCKCHIP_PM_REG_REGION_MEM_SIZE / sizeof(uint32_t))
/* REG region */
#define RGN_LEN(_rgn) (((_rgn)->end - (_rgn)->start) / (_rgn)->stride + 1)
#ifndef ROCKCHIP_PM_REG_REGION_MEM_SIZE
#define ROCKCHIP_PM_REG_REGION_MEM_SIZE 0
#endif
#ifdef ROCKCHIP_REG_RGN_MEM_BASE
static uint32_t *region_mem = (uint32_t *)ROCKCHIP_REG_RGN_MEM_BASE;
#else
static uint32_t region_mem[ROCKCHIP_PM_REG_REGION_MEM_LEN];
#endif
static int region_mem_idx;
static int alloc_region_mem(uint32_t *buf, int max_len,
struct reg_region *rgns, uint32_t rgn_num)
{
int i;
int total_len = 0, len = 0;
struct reg_region *r = rgns;
assert(buf && rgns && rgn_num);
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 (total_len > max_len) {
ERROR("%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);
panic();
}
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 rockchip_alloc_region_mem(struct reg_region *rgns, uint32_t rgn_num)
{
int max_len = 0, len;
assert(rgns && rgn_num);
max_len = ROCKCHIP_PM_REG_REGION_MEM_LEN - region_mem_idx;
len = alloc_region_mem(region_mem + region_mem_idx, max_len,
rgns, rgn_num);
region_mem_idx += len;
}
/**
* Save (reg_region->start ~ reg_region->end) to reg_region->buf.
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rockchip_reg_rgn_save(struct reg_region *rgns, uint32_t rgn_num)
{
struct reg_region *r;
uint32_t addr;
int i, j;
assert(rgns && rgn_num);
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++)
r->buf[j] = mmio_read_32(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 rockchip_reg_rgn_restore(struct reg_region *rgns, uint32_t rgn_num)
{
struct reg_region *r;
uint32_t addr;
int i, j;
assert(rgns && rgn_num);
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++)
mmio_write_32(addr, r->buf[j] | r->wmsk);
dsb();
}
}
/**
* Restore reg_region->buf to (reg_region->start ~ reg_region->end) reversely.
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rockchip_reg_rgn_restore_reverse(struct reg_region *rgns, uint32_t rgn_num)
{
struct reg_region *r;
uint32_t addr;
int i, j;
assert(rgns && rgn_num);
for (i = rgn_num - 1; i >= 0; i--) {
r = &rgns[i];
j = RGN_LEN(r) - 1;
for (addr = r->end; addr >= r->start; addr -= r->stride, j--)
mmio_write_32(addr, r->buf[j] | r->wmsk);
dsb();
}
}
static void rockchip_print_hex(uint32_t val)
{
int i;
unsigned char tmp;
putchar('0');
putchar('x');
for (i = 0; i < 8; val <<= 4, ++i) {
tmp = (val & 0xf0000000) >> 28;
if (tmp < 10)
putchar('0' + tmp);
else
putchar('a' + tmp - 10);
}
}
/**
* Dump registers (base + start_offset ~ base + end_offset)
* @base - the base addr of the register.
* @start_offset - the start offset to dump.
* @end_offset - the end offset to dump.
* @stride - the stride of the registers.
*/
void rockchip_regs_dump(uint32_t base,
uint32_t start_offset,
uint32_t end_offset,
uint32_t stride)
{
uint32_t i;
for (i = start_offset; i <= end_offset; i += stride) {
if ((i - start_offset) % 16 == 0) {
putchar('\n');
rockchip_print_hex(base + i);
putchar(':');
putchar(' ');
putchar(' ');
putchar(' ');
putchar(' ');
}
rockchip_print_hex(mmio_read_32(base + i));
putchar(' ');
putchar(' ');
putchar(' ');
putchar(' ');
}
putchar('\n');
}
/**
* Dump reg regions
* @rgns - struct reg_region array.
* @rgn_num - struct reg_region array length.
*/
void rockchip_dump_reg_rgns(struct reg_region *rgns, uint32_t rgn_num)
{
struct reg_region *r;
int i;
assert(rgns && rgn_num);
for (i = 0; i < rgn_num; i++) {
r = &rgns[i];
rockchip_regs_dump(0x0, r->start, r->end, r->stride);
}
}