blob: d87b6e0926e0812a231dfbdf004463183250ee5d [file] [log] [blame]
/*
* 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_asm.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 = sbi_domain_root_add_memrange(addr, size, PAGE_SIZE,
(SBI_DOMAIN_MEMREGION_MMIO |
SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW));
if (rc)
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,
};