[PATCH 4/7] misc: mvebu: Add sample at reset driver

Chris Packham
2022-09-16T04:55:34Z

Add a new UCLASS_SAR, the generic SAR code and an Alleycat5 driver. This
has been adapted from the Marvell SDK but only the AC5 driver has been
brought through (other drivers exist for the ap806, ap807 and cp110 IP
blocks).

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---

drivers/misc/Kconfig | 6 ++
drivers/misc/Makefile | 1 +
drivers/misc/mvebu_sar/Makefile | 4 +
drivers/misc/mvebu_sar/ac5_sar.c | 119 +++++++++++++++++++++++
drivers/misc/mvebu_sar/sar-uclass.c | 146 ++++++++++++++++++++++++++++
include/dm/uclass-id.h | 1 +
include/fdtdec.h | 4 +
include/mvebu/mvebu_chip_sar.h | 73 ++++++++++++++
include/mvebu/sar.h | 57 +++++++++++
include/mvebu/var.h | 28 ++++++
include/sar-uclass.h | 23 +++++
lib/fdtdec.c | 6 +-
12 files changed, 467 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/mvebu_sar/Makefile
create mode 100644 drivers/misc/mvebu_sar/ac5_sar.c
create mode 100644 drivers/misc/mvebu_sar/sar-uclass.c
create mode 100644 include/mvebu/mvebu_chip_sar.h
create mode 100644 include/mvebu/sar.h
create mode 100644 include/mvebu/var.h
create mode 100644 include/sar-uclass.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a6da6e215d..4e99ef29d9 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -645,4 +645,10 @@ config SL28CPLD
the base driver which provides common access methods for the
sub-drivers.

+config MVEBU_SAR
+ bool "MVEBU SAR driver"
+ depends on ARCH_MVEBU
+ help
+ Support MVEBU SAR driver
+
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d494639cd9..3cf976edd8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
obj-$(CONFIG_P2SB) += p2sb-uclass.o
obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
+obj-$(CONFIG_MVEBU_SAR) += mvebu_sar/
ifdef CONFIG_QFW
obj-y += qfw.o
obj-$(CONFIG_QFW_PIO) += qfw_pio.o
diff --git a/drivers/misc/mvebu_sar/Makefile b/drivers/misc/mvebu_sar/Makefile
new file mode 100644
index 0000000000..6f339fb7b4
--- /dev/null
+++ b/drivers/misc/mvebu_sar/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_MVEBU_SAR) += sar-uclass.o
+obj-$(CONFIG_ALLEYCAT_5) += ac5_sar.o
diff --git a/drivers/misc/mvebu_sar/ac5_sar.c b/drivers/misc/mvebu_sar/ac5_sar.c
new file mode 100644
index 0000000000..67b2110bc8
--- /dev/null
+++ b/drivers/misc/mvebu_sar/ac5_sar.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <mvebu/mvebu_chip_sar.h>
+#include <dm.h>
+
+#include <sar-uclass.h>
+
+#define MHz 1000000
+
+#define BIT_VAL(b) ((1ULL << ((b) + 1)) - 1)
+#define BIT_RANGE(Bl, Bh) (BIT_VAL(Bh) - BIT_VAL((Bl) - 1))
+
+#define PLL_MAX_CHOICE 4
+
+#define CPU_TYPE_AC5 0
+#define CPU_TYPE_AC5x 1
+#define CPU_TYPE_LAST 2
+
+static const u32 pll_freq_tbl[CPU_TYPE_LAST][SAR_AP_FABRIC_FREQ + 1][PLL_MAX_CHOICE] = {
+ [CPU_TYPE_AC5] = {
+ [SAR_CPU_FREQ] = {
+ 800 * MHz, 1200 * MHz, 1400 * MHz, 1000 * MHz
+ },
+ [SAR_DDR_FREQ] = {
+ 1200 * MHz, 800 * MHz, 0, 0
+ },
+ [SAR_AP_FABRIC_FREQ] = {
+ 396 * MHz, 290 * MHz, 197 * MHz, 0
+ },
+ },
+ [CPU_TYPE_AC5x] = {
+ [SAR_CPU_FREQ] = {
+ 800 * MHz, 1200 * MHz, 1500 * MHz, 1600 * MHz
+ },
+ [SAR_DDR_FREQ] = {
+ 1200 * MHz, 800 * MHz, 0, 0
+ },
+ [SAR_AP_FABRIC_FREQ] = {
+ 0, 0, 0, 0
+ }
+ }
+};
+
+static const u32 soc_sar_masks_tbl[CPU_TYPE_LAST][SAR_AP_FABRIC_FREQ + 1] = {
+ [CPU_TYPE_AC5] = {
+ [SAR_CPU_FREQ] = BIT_RANGE(18, 20),
+ [SAR_DDR_FREQ] = BIT_RANGE(16, 17),
+ [SAR_AP_FABRIC_FREQ] = BIT_RANGE(22, 23),
+ },
+ [CPU_TYPE_AC5x] = {
+ [SAR_CPU_FREQ] = BIT_RANGE(8, 10),
+ [SAR_DDR_FREQ] = BIT_RANGE(6, 7),
+ [SAR_AP_FABRIC_FREQ] = 1,
+ },
+};
+
+static int ac5_sar_value_get(struct udevice *dev, enum mvebu_sar_opts sar_opt,
+ struct sar_val *val)
+{
+ u32 *pSarBase = (u32 *)(((struct dm_sar_pdata *)dev_get_priv(dev))->sar_base);
+ u32 sar_reg_val = readl(pSarBase);
+ u32 mask;
+ unsigned char choice;
+ int cpu_type = ((readl(0x7f90004c) & 0xFF000) >> 12) == 0x98 ? CPU_TYPE_AC5x : CPU_TYPE_AC5;
+
+ if (sar_opt > SAR_AP_FABRIC_FREQ) {
+ pr_err("AC5-SAR: Unsupported SAR option %d.\n", sar_opt);
+ return -EINVAL;
+ }
+
+ mask = soc_sar_masks_tbl[cpu_type][sar_opt];
+ /* The choice within the bits allocated for a specific PLL */
+ choice = (sar_reg_val & mask) >> (__builtin_ffs(mask) - 1);
+
+ val->freq = pll_freq_tbl[cpu_type][sar_opt][choice];
+ return 0;
+}
+
+static int ac5_sar_init(struct udevice *dev)
+{
+ int ret, i;
+
+ u32 sar_list[] = {
+ SAR_CPU_FREQ,
+ SAR_DDR_FREQ,
+ SAR_AP_FABRIC_FREQ
+ };
+
+ for (i = 0; i < ARRAY_SIZE(sar_list); i++) {
+ ret = mvebu_sar_id_register(dev, sar_list[i]);
+ if (ret) {
+ pr_err("Failed to register SAR %d, for AC5\n",
+ sar_list[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct sar_ops ac5_sar_ops = {
+ .sar_init_func = ac5_sar_init,
+ .sar_value_get_func = ac5_sar_value_get,
+ .sar_dump_func = NULL,
+};
+
+U_BOOT_DRIVER(ac5_sar) = {
+ .name = "ac5_sar",
+ .id = UCLASS_SAR,
+ .priv_auto = sizeof(struct dm_sar_pdata),
+ .ops = &ac5_sar_ops,
+};
diff --git a/drivers/misc/mvebu_sar/sar-uclass.c b/drivers/misc/mvebu_sar/sar-uclass.c
new file mode 100644
index 0000000000..d9cb019d5d
--- /dev/null
+++ b/drivers/misc/mvebu_sar/sar-uclass.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <sar-uclass.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define SAR_MAX_CHIP 4
+
+UCLASS_DRIVER(sar) = {
+ .name = "sar",
+ .id = UCLASS_SAR,
+};
+
+struct udevice *__section(".data") soc_sar_info[SAR_MAX_IDX];
+
+int mvebu_sar_id_register(struct udevice *dev, u32 sar_id)
+{
+ if (soc_sar_info[sar_id]) {
+ pr_err("sar %d was already registered.\n", sar_id);
+ return -EBUSY;
+ }
+ soc_sar_info[sar_id] = dev;
+
+ return 0;
+}
+
+/* find all drivers for sar device and initialize each driver */
+int mvebu_sar_init(void)
+{
+ int ret, i;
+ int node, chip_count, sar_list[SAR_MAX_CHIP];
+ const void *blob = gd->fdt_blob;
+ struct udevice *sar_chip[SAR_MAX_CHIP];
+ struct dm_sar_pdata *priv;
+ const struct sar_ops *ops;
+ struct udevice *parent;
+ struct driver_info sar_drivers[SAR_MAX_CHIP];
+ void *sar_base = NULL;
+ const char *sar_driver, *sar_name;
+
+ chip_count = fdtdec_find_aliases_for_id(blob, "sar-reg",
+ COMPAT_MVEBU_SAR_REG_COMMON,
+ sar_list, SAR_MAX_CHIP);
+
+ if (chip_count <= 0) {
+ pr_err("Cannot find sample-at-reset dt entry (%d).\n",
+ chip_count);
+ return -ENODEV;
+ }
+ uclass_get_device_by_name(UCLASS_ROOT, "root_driver", &parent);
+ memset(soc_sar_info, 0, sizeof(soc_sar_info));
+
+ for (i = 0; i < chip_count ; i++) {
+ node = sar_list[i];
+ if (node <= 0)
+ continue;
+
+ /* Skip if Node is disabled */
+ if (!fdtdec_get_is_enabled(blob, node))
+ continue;
+ /* Binding stage */
+ sar_driver = fdt_getprop(blob, node, "sar-driver", NULL);
+ sar_drivers[i].name = sar_driver;
+ ret = device_bind_by_name(parent, false,
+ &sar_drivers[i], &sar_chip[i]);
+
+ if (!sar_chip[i]) {
+ pr_err("SAR driver binding failed\n");
+ return 0;
+ }
+
+ /* fetch driver info from device-tree */
+ sar_base = (void *)fdtdec_get_addr_size_auto_noparent(blob,
+ node, "reg", 0, NULL, true);
+ if (!sar_base) {
+ pr_err("SAR address isn't found in the device-tree\n");
+ return 0;
+ }
+ sar_name = fdt_getprop(blob, node, "sar-name", NULL);
+ /* Initialize driver priv struct */
+ device_probe(sar_chip[i]);
+ priv = dev_get_priv(sar_chip[i]);
+ priv->sar_base = sar_base;
+ priv->sar_name = sar_name;
+ ops = device_get_ops(sar_chip[i]);
+ if (!ops->sar_init_func)
+ return -EINVAL;
+
+ ret = ops->sar_init_func(sar_chip[i]);
+ if (ret) {
+ pr_err("sar_init failed (%d).\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mvebu_sar_value_get(enum mvebu_sar_opts opt, struct sar_val *val)
+{
+ const struct sar_ops *ops;
+
+ if (soc_sar_info[opt]) {
+ ops = device_get_ops(soc_sar_info[opt]);
+ return ops->sar_value_get_func(soc_sar_info[opt], opt, val);
+ }
+
+ pr_err("SAR - No chip registered on sar %d.\n", opt);
+ return -ENODEV;
+}
+
+char *mvebu_sar_bootsrc_to_name(enum mvebu_bootsrc_type src)
+{
+ switch (src) {
+ case(BOOTSRC_NAND):
+ return "nand";
+ case(BOOTSRC_SPI):
+ case(BOOTSRC_AP_SPI):
+ return "spi";
+ case(BOOTSRC_SD_EMMC):
+ case(BOOTSRC_AP_SD_EMMC):
+ return "mmc";
+ case(BOOTSRC_NOR):
+ return "nor";
+ default:
+ return "unknown";
+ }
+}
+
+void mvebu_sar_dump(struct udevice *dev)
+{
+ const struct sar_ops *ops;
+
+ ops = device_get_ops(dev);
+
+ ops->sar_dump_func(dev);
+}
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index a432e43871..2213e76d38 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -107,6 +107,7 @@ enum uclass_id {
UCLASS_RESET, /* Reset controller device */
UCLASS_RNG, /* Random Number Generator */
UCLASS_RTC, /* Real time clock device */
+ UCLASS_SAR, /* Reset Sample Configuration */
UCLASS_SCMI_AGENT, /* Interface with an SCMI server */
UCLASS_SCSI, /* SCSI device */
UCLASS_SERIAL, /* Serial UART */
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 12355afd7f..d8923ba443 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -199,6 +199,10 @@ enum fdt_compat_id {
COMPAT_ALTERA_SOCFPGA_FPGA0, /* SOCFPGA FPGA manager */
COMPAT_ALTERA_SOCFPGA_NOC, /* SOCFPGA Arria 10 NOC */
COMPAT_ALTERA_SOCFPGA_CLK_INIT, /* SOCFPGA Arria 10 clk init */
+ COMPAT_MVEBU_SAR, /* Marvell Sampled-at-Reset */
+ COMPAT_MVEBU_SAR_REG_COMMON, /* Marvell Sampled-at-Reset Common */
+ COMPAT_MVEBU_SAR_REG_AP806, /* Marvell Sampled-at-Reset for ap806/ap807 */
+ COMPAT_MVEBU_SAR_REG_CP110, /* Marvell Sampled-at-Reset for cp110 */

COMPAT_COUNT,
};
diff --git a/include/mvebu/mvebu_chip_sar.h b/include/mvebu/mvebu_chip_sar.h
new file mode 100644
index 0000000000..4781aa06c4
--- /dev/null
+++ b/include/mvebu/mvebu_chip_sar.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef _MVEBU_CHIP_SAR_H_
+#define _MVEBU_CHIP_SAR_H_
+
+#include <asm/types.h>
+
+struct udevice;
+
+/*
+ * List of boot source options.
+ * Return value for each of the options:
+ * - SAR_CPU_FREQ: Frequency (Hz)
+ * - SAR_DDR_FREQ: Frequency (Hz)
+ * - SAR_AP_FABRIC_FREQ: Frequency (Hz)
+ * - SAR_CP_FABRIC_FREQ: Frequency (Hz)
+ * - SAR_BOOT_SRC: Boot source type (see mvebu_bootsrc_type)
+ */
+enum mvebu_sar_opts {
+ SAR_CPU_FREQ = 0,
+ SAR_DDR_FREQ,
+ SAR_AP_FABRIC_FREQ,
+ SAR_CP_FABRIC_FREQ,
+ SAR_CP0_PCIE0_CLK,
+ SAR_CP0_PCIE1_CLK,
+ SAR_CP1_PCIE0_CLK,
+ SAR_CP1_PCIE1_CLK,
+ SAR_BOOT_SRC,
+ SAR_MAX_IDX
+};
+
+enum mvebu_bootsrc_type {
+ BOOTSRC_NAND,
+ BOOTSRC_SPI,
+ BOOTSRC_AP_SPI,
+ BOOTSRC_SD_EMMC,
+ BOOTSRC_AP_SD_EMMC,
+ BOOTSRC_NOR,
+ BOOTSRC_MAX_IDX
+};
+
+/*
+ * sample-at-reset information
+ * raw_sar_val: Raw value out of the sample-at-reset register.
+ * This is hw dependent and should not be used for comparison
+ * purposes (useful for debug, or verbose information).
+ * bootsrc (SAR_BOOT_SRC):
+ * type: Boot source interface type.
+ * index: When applicable, indicates the interface index (e.g. SPI #1,
+ * NAND #0).
+ * freq: Frequency in Hz.
+ */
+struct sar_val {
+ u32 raw_sar_val;
+ union {
+ struct {
+ enum mvebu_bootsrc_type type;
+ int index;
+ } bootsrc;
+ u32 freq;
+ u32 clk_direction; /* input = 0 */
+ };
+};
+
+int mvebu_sar_init(void);
+int mvebu_sar_value_get(enum mvebu_sar_opts opt, struct sar_val *val);
+void mvebu_sar_dump(struct udevice *dev);
+char *mvebu_sar_bootsrc_to_name(enum mvebu_bootsrc_type src);
+
+#endif /* _MVEBU_CHIP_SAR_H_ */
diff --git a/include/mvebu/sar.h b/include/mvebu/sar.h
new file mode 100644
index 0000000000..bd0837d6a3
--- /dev/null
+++ b/include/mvebu/sar.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef _SAR_H_
+#define _SAR_H_
+
+#include <linux/compiler.h>
+#include <mvebu/var.h>
+
+#define MAX_SAR_CHIPS 4
+#define MAX_SAR 8
+
+enum sar_variables {
+ CPUS_NUM_SAR = 0,
+ CPU0_ENDIANESS_SAR,
+ FREQ_SAR,
+ CPU_FREQ_SAR,
+ FAB_REQ_SAR,
+ BOOT_SRC_SAR,
+ BOOT_WIDTH_SAR,
+ PEX_MODE_SAR,
+ L2_SIZE_SAR,
+ DRAM_ECC_SAR,
+ DRAM_BUS_WIDTH_SAR,
+};
+
+struct sar_var {
+ u8 start_bit;
+ u8 bit_length;
+ u8 option_cnt;
+ u8 active;
+ bool swap_bit;
+ char *desc;
+ char *key;
+ struct var_opts option_desc[MAX_VAR_OPTIONS];
+};
+
+struct sar_data {
+ u32 chip_addr[MAX_SAR_CHIPS];
+ u8 chip_count;
+ u8 bit_width;
+ struct sar_var sar_lookup[MAX_SAR];
+};
+
+int sar_read_all(void);
+int sar_default_key(const char *key);
+int sar_default_all(void);
+int sar_write_key(const char *key, int val);
+int sar_print_key(const char *key);
+void sar_list_keys(void);
+int sar_list_key_opts(const char *key);
+int sar_is_available(void);
+void sar_init(void);
+
+#endif /* _SAR_H_ */
diff --git a/include/mvebu/var.h b/include/mvebu/var.h
new file mode 100644
index 0000000000..6bd315c841
--- /dev/null
+++ b/include/mvebu/var.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef _VAR_H_
+#define _VAR_H_
+
+#include <linux/compiler.h>
+
+#define INVALID_KEY 0xFF
+#define MAX_VAR_OPTIONS 10
+
+#define VAR_IS_DEFAULT 0x1
+#define VAR_IS_LAST 0x2
+
+struct var_opts {
+ u8 value;
+ char *desc;
+ u8 flags;
+};
+
+struct var_desc {
+ char *key;
+ char *description;
+};
+
+#endif /* _VAR_H_ */
diff --git a/include/sar-uclass.h b/include/sar-uclass.h
new file mode 100644
index 0000000000..8b9cc08847
--- /dev/null
+++ b/include/sar-uclass.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <mvebu/mvebu_chip_sar.h>
+
+struct udevice;
+
+struct sar_ops {
+ int (*sar_init_func)(struct udevice *dev);
+ int (*sar_dump_func)(struct udevice *dev);
+ int (*sar_value_get_func)(struct udevice *dev, enum mvebu_sar_opts sar,
+ struct sar_val *val);
+ int (*sar_bootsrc_get)(struct udevice *dev, u32 *idx);
+};
+
+struct dm_sar_pdata {
+ void __iomem *sar_base;
+ const char *sar_name;
+};
+
+int mvebu_sar_id_register(struct udevice *dev, u32 sar_id);
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 96b6b71a60..26e5c82bcd 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -74,7 +74,11 @@ static const char * const compat_names[COMPAT_COUNT] = {
COMPAT(ALTERA_SOCFPGA_F2SDR2, "altr,socfpga-fpga2sdram2-bridge"),
COMPAT(ALTERA_SOCFPGA_FPGA0, "altr,socfpga-a10-fpga-mgr"),
COMPAT(ALTERA_SOCFPGA_NOC, "altr,socfpga-a10-noc"),
- COMPAT(ALTERA_SOCFPGA_CLK_INIT, "altr,socfpga-a10-clk-init")
+ COMPAT(ALTERA_SOCFPGA_CLK_INIT, "altr,socfpga-a10-clk-init"),
+ COMPAT(MVEBU_SAR, "marvell,sample-at-reset"),
+ COMPAT(MVEBU_SAR_REG_COMMON, "marvell,sample-at-reset-common"),
+ COMPAT(MVEBU_SAR_REG_AP806, "marvell,sample-at-reset-ap806"),
+ COMPAT(MVEBU_SAR_REG_CP110, "marvell,sample-at-reset-cp110"),
};

static const char *const fdt_src_name[] = {
--
2.37.3