/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Ventana Micro Systems Inc. * * Authors: * Anup Patel */ #include #include #include #include #include #include #include #include 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(const void *fdt, int nodeoff, 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 = nodeoff; 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" }, { }, }; const struct fdt_driver fdt_regmap_syscon = { .match_table = regmap_syscon_match, .init = regmap_syscon_init, };