drivers: misc: Add socfpga_dtreg driver for Intel SoCFPGA

Add socfpga_dtreg driver enablement for Intel SoCFPGA.

Signed-off-by: Wan Yee Lau <wan.yee.lau@intel.com>
Reviewed-by: Tien Fong Chee <tien.fong.chee@intel.com>
This commit is contained in:
Wan Yee Lau 2024-02-05 11:47:16 +08:00 committed by Tien Fong Chee
parent 86fd291a79
commit 3f190c55a4
5 changed files with 205 additions and 0 deletions

View file

@ -1116,6 +1116,8 @@ config ARCH_SOCFPGA
select SPL_LIBGENERIC_SUPPORT
select SPL_OF_CONTROL
select SPL_SEPARATE_BSS if TARGET_SOCFPGA_SOC64
select SPL_DRIVERS_MISC if TARGET_SOCFPGA_SOC64
select SPL_SOCFPGA_DT_REG if TARGET_SOCFPGA_SOC64
select SPL_SERIAL
select SPL_SYSRESET
select SPL_WATCHDOG

View file

@ -0,0 +1,80 @@
* Firewall and privilege register settings in device tree
Required properties:
--------------------
- compatible: should contain "intel,socfpga-dtreg"
- reg: Physical base address and size of block register.
- intel,offset-settings: 32-bit offset address of block register,
followed by 32-bit value settings and
the masking bits, only masking bit
set to 1 allows modification.
The device tree node which describes secure and privilege register access
configuration in compile time.
Most of these registers are expected to work except for the case which some
registers configuration are required for granting access to some other
registers, for example CCU registers have to be properly configured before
allowing register configuration access to fpga2sdram firewall as shown in
below example.
Some registers depend on runtime data for proper configuration are expected
to be part of driver that generating these data for example configuration for
soc_noc_fw_ddr_mpu_inst_0_ddr_scr block register depend on DDR size parsed from
memory device tree node.
Please refer details of tested examples below for both fpga2sdram and QoS
configuration with default reset value and the comments.
Example:
--------
Configuration for multiple dtreg node support in device tree:
socfpga_dtreg0: socfpga-dtreg0 {
compatible = "intel,socfpga-dtreg";
#address-cells = <1>;
#size-cells = <1>;
bootph-all;
coh_cpu0_bypass_OC_Firewall_main_Firewall@f7100200 {
reg = <0xf7100200 0x00000014>;
intel,offset-settings =
/*
* Disable ocram security at CCU for
* non secure access
*/
<0x0000004 0x8000ffff 0xe007ffff>,
<0x0000008 0x8000ffff 0xe007ffff>,
<0x000000c 0x8000ffff 0xe007ffff>,
<0x0000010 0x8000ffff 0xe007ffff>;
bootph-all;
};
};
socfpga_dtreg1: socfpga-dtreg1 {
compatible = "intel,socfpga-dtreg";
#address-cells = <1>;
#size-cells = <1>;
bootph-all;
soc_noc_fw_mpfe_csr_inst_0_mpfe_scr@f8020000 {
reg = <0xf8020000 0x0000001c>;
intel,offset-settings =
/* Disable MPFE firewall for SMMU */
<0x00000000 0x00010101 0x00010101>,
/*
* Disable MPFE firewall for HMC
* adapter
*/
<0x00000004 0x00000001 0x00010101>;
bootph-all;
};
};
To call the nodes use:
ret = uclass_get_device_by_name(UCLASS_NOP, "socfpga-dtreg0", &dev);
ret = uclass_get_device_by_name(UCLASS_NOP, "socfpga-dtreg1", &dev);

View file

@ -689,4 +689,11 @@ config SL28CPLD
the base driver which provides common access methods for the
sub-drivers.
config SPL_SOCFPGA_DT_REG
bool "Enable register setting from device tree in SPL"
depends on SPL
help
Enable register setting from device tree. This also
provides user a clean interface and all register settings are
centralized in one place, device tree.
endmenu

View file

@ -90,3 +90,4 @@ obj-$(CONFIG_K3_AVS0) += k3_avs.o
obj-$(CONFIG_ESM_K3) += k3_esm.o
obj-$(CONFIG_ESM_PMIC) += esm_pmic.o
obj-$(CONFIG_SL28CPLD) += sl28cpld.o
obj-$(CONFIG_SPL_SOCFPGA_SEC_REG) += socfpga_dtreg.o

View file

@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024 Intel Corporation <www.intel.com>
*/
#include <dm.h>
#include <errno.h>
#include <asm/io.h>
#include <linux/sizes.h>
#define NUMBER_OF_ELEMENTS 3
static int socfpga_dtreg_probe(struct udevice *dev)
{
const fdt32_t *list;
fdt_addr_t offset, base;
fdt_val_t val, read_val, mask, set_mask;
int size, i;
u32 blk_sz, reg;
ofnode node;
const char *name = NULL;
debug("%s(dev=%p)\n", __func__, dev);
if (!dev_has_ofnode(dev))
return 0;
dev_for_each_subnode(node, dev) {
name = ofnode_get_name(node);
if (!name)
return -EINVAL;
if (ofnode_read_u32_index(node, "reg", 1, &blk_sz))
return -EINVAL;
base = ofnode_get_addr(node);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
debug("%s(node_offset 0x%lx node_name %s ", __func__,
node.of_offset, name);
debug("node addr 0x%llx blk sz 0x%x)\n", base, blk_sz);
list = ofnode_read_prop(node, "intel,offset-settings", &size);
if (!list)
return -EINVAL;
debug("%s(intel,offset-settings property size=%x)\n", __func__,
size);
size /= sizeof(*list) * NUMBER_OF_ELEMENTS;
/*
* First element: offset
* Second element: val
* Third element: mask
*/
for (i = 0; i < size; i++) {
offset = fdt32_to_cpu(*list++);
val = fdt32_to_cpu(*list++);
/* Reads the masking bit value from the list */
mask = fdt32_to_cpu(*list++);
/*
* Reads out the offsets, value and masking bits
* Ex: <0x00000000 0x00000230 0xffffffff>
*/
debug("%s(intel,offset-settings 0x%llx : 0x%llx : 0x%llx)\n",
__func__, offset, val, mask);
if (blk_sz < offset + SZ_4) {
printf("%s: Overflow as offset 0x%llx or reg",
__func__, offset);
printf(" write is more than block size 0x%x\n",
blk_sz);
return -EINVAL;
}
if (mask != 0) {
if (mask == 0xffffffff) {
reg = base + offset;
writel(val, (uintptr_t)reg);
} else {
/* Mask the value with the masking bits */
set_mask = val & mask;
reg = base + offset;
/* Clears and sets specific bits in the register */
clrsetbits_le32((uintptr_t)reg, mask, set_mask);
}
}
read_val = readl((uintptr_t)reg);
/* Reads out the register, masked value and the read value */
debug("%s(reg 0x%x = wr : 0x%llx rd : 0x%llx)\n",
__func__, reg, set_mask, read_val);
}
}
return 0;
};
static const struct udevice_id socfpga_dtreg_ids[] = {
{.compatible = "intel,socfpga-dtreg"},
{ }
};
U_BOOT_DRIVER(socfpga_dtreg) = {
.name = "socfpga-dtreg",
.id = UCLASS_NOP,
.of_match = socfpga_dtreg_ids,
.probe = socfpga_dtreg_probe,
};