mirror of
				https://github.com/riscv-software-src/opensbi
				synced 2025-11-04 14:00:31 +00:00 
			
		
		
		
	Let us add a simple FDT based system regmap driver which follows the device tree bindings already defined in the Linux kernel. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Reviewed-by: Xiang W <wxjstz@126.com>
		
			
				
	
	
		
			263 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-License-Identifier: BSD-2-Clause
 | 
						|
 *
 | 
						|
 * Copyright (c) 2023 Ventana Micro Systems Inc.
 | 
						|
 *
 | 
						|
 * Authors:
 | 
						|
 *   Anup Patel <apatel@ventanamicro.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <libfdt.h>
 | 
						|
#include <sbi/riscv_io.h>
 | 
						|
#include <sbi/sbi_byteorder.h>
 | 
						|
#include <sbi/sbi_error.h>
 | 
						|
#include <sbi/sbi_heap.h>
 | 
						|
#include <sbi_utils/fdt/fdt_helper.h>
 | 
						|
#include <sbi_utils/regmap/fdt_regmap.h>
 | 
						|
 | 
						|
enum syscon_regmap_endian {
 | 
						|
	SYSCON_ENDIAN_NATIVE = 0,
 | 
						|
	SYSCON_ENDIAN_LITTLE,
 | 
						|
	SYSCON_ENDIAN_BIG,
 | 
						|
	SYSCON_ENDIAN_MAX
 | 
						|
};
 | 
						|
 | 
						|
struct syscon_regmap {
 | 
						|
	u32 reg_io_width;
 | 
						|
	enum syscon_regmap_endian reg_endian;
 | 
						|
	unsigned long addr;
 | 
						|
	struct regmap rmap;
 | 
						|
};
 | 
						|
 | 
						|
#define to_syscon_regmap(__rmap)	\
 | 
						|
	container_of((__rmap), struct syscon_regmap, rmap)
 | 
						|
 | 
						|
static int regmap_syscon_read_8(struct regmap *rmap, unsigned int reg,
 | 
						|
				unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = readb((volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_8(struct regmap *rmap, unsigned int reg,
 | 
						|
				 unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writeb(val, (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_16(struct regmap *rmap, unsigned int reg,
 | 
						|
				unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = readw((volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_16(struct regmap *rmap, unsigned int reg,
 | 
						|
				 unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writew(val, (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_32(struct regmap *rmap, unsigned int reg,
 | 
						|
				unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = readl((volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_32(struct regmap *rmap, unsigned int reg,
 | 
						|
				 unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writel(val, (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_le16(struct regmap *rmap, unsigned int reg,
 | 
						|
				   unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = le16_to_cpu(readw((volatile void *)(srm->addr + reg)));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_le16(struct regmap *rmap, unsigned int reg,
 | 
						|
				    unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writew(cpu_to_le16(val), (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_le32(struct regmap *rmap, unsigned int reg,
 | 
						|
				   unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = le32_to_cpu(readl((volatile void *)(srm->addr + reg)));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_le32(struct regmap *rmap, unsigned int reg,
 | 
						|
				    unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writel(cpu_to_le32(val), (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_be16(struct regmap *rmap, unsigned int reg,
 | 
						|
				   unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = be16_to_cpu(readl((volatile void *)(srm->addr + reg)));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_be16(struct regmap *rmap, unsigned int reg,
 | 
						|
				    unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writel(cpu_to_be16(val), (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_read_be32(struct regmap *rmap, unsigned int reg,
 | 
						|
				   unsigned int *val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	*val = be32_to_cpu(readl((volatile void *)(srm->addr + reg)));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_write_be32(struct regmap *rmap, unsigned int reg,
 | 
						|
				    unsigned int val)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm = to_syscon_regmap(rmap);
 | 
						|
 | 
						|
	writel(cpu_to_be32(val), (volatile void *)(srm->addr + reg));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int regmap_syscon_init(void *fdt, int nodeoff, u32 phandle,
 | 
						|
			      const struct fdt_match *match)
 | 
						|
{
 | 
						|
	struct syscon_regmap *srm;
 | 
						|
	uint64_t addr, size;
 | 
						|
	const fdt32_t *val;
 | 
						|
	int rc, len;
 | 
						|
 | 
						|
	srm = sbi_zalloc(sizeof(*srm));
 | 
						|
	if (!srm)
 | 
						|
		return SBI_ENOMEM;
 | 
						|
 | 
						|
	val = fdt_getprop(fdt, nodeoff, "reg-io-width", &len);
 | 
						|
	srm->reg_io_width = val ? fdt32_to_cpu(*val) : 4;
 | 
						|
 | 
						|
	if (fdt_getprop(fdt, nodeoff, "native-endian", &len))
 | 
						|
		srm->reg_endian = SYSCON_ENDIAN_NATIVE;
 | 
						|
	else if (fdt_getprop(fdt, nodeoff, "little-endian", &len))
 | 
						|
		srm->reg_endian = SYSCON_ENDIAN_LITTLE;
 | 
						|
	else if (fdt_getprop(fdt, nodeoff, "big-endian", &len))
 | 
						|
		srm->reg_endian = SYSCON_ENDIAN_BIG;
 | 
						|
	else
 | 
						|
		srm->reg_endian = SYSCON_ENDIAN_NATIVE;
 | 
						|
 | 
						|
	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, &size);
 | 
						|
	if (rc)
 | 
						|
		goto fail_free_syscon;
 | 
						|
	srm->addr = addr;
 | 
						|
 | 
						|
	srm->rmap.id = phandle;
 | 
						|
	srm->rmap.reg_shift = 0;
 | 
						|
	srm->rmap.reg_stride = srm->reg_io_width * 8;
 | 
						|
	srm->rmap.reg_base = 0;
 | 
						|
	srm->rmap.reg_max = size / srm->reg_io_width;
 | 
						|
	switch (srm->reg_io_width) {
 | 
						|
	case 1:
 | 
						|
		srm->rmap.reg_read = regmap_syscon_read_8;
 | 
						|
		srm->rmap.reg_write = regmap_syscon_write_8;
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		switch (srm->reg_endian) {
 | 
						|
		case SYSCON_ENDIAN_NATIVE:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_16;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_16;
 | 
						|
			break;
 | 
						|
		case SYSCON_ENDIAN_LITTLE:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_le16;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_le16;
 | 
						|
			break;
 | 
						|
		case SYSCON_ENDIAN_BIG:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_be16;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_be16;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			rc = SBI_EINVAL;
 | 
						|
			goto fail_free_syscon;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case 4:
 | 
						|
		switch (srm->reg_endian) {
 | 
						|
		case SYSCON_ENDIAN_NATIVE:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_32;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_32;
 | 
						|
			break;
 | 
						|
		case SYSCON_ENDIAN_LITTLE:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_le32;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_le32;
 | 
						|
			break;
 | 
						|
		case SYSCON_ENDIAN_BIG:
 | 
						|
			srm->rmap.reg_read = regmap_syscon_read_be32;
 | 
						|
			srm->rmap.reg_write = regmap_syscon_write_be32;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			rc = SBI_EINVAL;
 | 
						|
			goto fail_free_syscon;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		rc = SBI_EINVAL;
 | 
						|
		goto fail_free_syscon;
 | 
						|
	}
 | 
						|
 | 
						|
	rc = regmap_add(&srm->rmap);
 | 
						|
	if (rc)
 | 
						|
		goto fail_free_syscon;
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
fail_free_syscon:
 | 
						|
	sbi_free(srm);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static const struct fdt_match regmap_syscon_match[] = {
 | 
						|
	{ .compatible = "syscon" },
 | 
						|
	{ },
 | 
						|
};
 | 
						|
 | 
						|
struct fdt_regmap fdt_regmap_syscon = {
 | 
						|
	.match_table = regmap_syscon_match,
 | 
						|
	.init = regmap_syscon_init,
 | 
						|
};
 |