239 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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)
 | |
| 		;
 | |
| }
 |