Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102636050
D41811.id127495.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
26 KB
Referenced Files
None
Subscribers
None
D41811.id127495.diff
View Options
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -698,3 +698,5 @@
arm/xilinx/uart_dev_cdnc.c optional uart soc_xilinx_zynq fdt
arm/xilinx/zy7_gpio.c optional gpio soc_xilinx_zynq fdt
dev/usb/controller/xlnx_dwc3.c optional xhci soc_xilinx_zynq fdt
+dev/firmware/xilinx/zynqmp_firmware.c optional fdt soc_xilinx_zynq
+dev/firmware/xilinx/zynqmp_firmware_if.m optional fdt soc_xilinx_zynq
diff --git a/sys/dev/firmware/xilinx/pm_defs.h b/sys/dev/firmware/xilinx/pm_defs.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/firmware/xilinx/pm_defs.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/* ZynqMP power management enums and defines */
+
+#ifndef PM_DEFS_H
+#define PM_DEFS_H
+
+/*********************************************************************
+ * Macro definitions
+ ********************************************************************/
+
+/*
+ * Version number is a 32bit value, like:
+ * (PM_VERSION_MAJOR << 16) | PM_VERSION_MINOR
+ */
+#define PM_VERSION_MAJOR 1U
+#define PM_VERSION_MINOR 1U
+
+#define PM_VERSION ((PM_VERSION_MAJOR << 16U) | PM_VERSION_MINOR)
+
+/**
+ * PM API versions
+ */
+/* Expected version of firmware APIs */
+#define FW_API_BASE_VERSION (1U)
+/* Expected version of firmware API for feature check */
+#define FW_API_VERSION_2 (2U)
+/* Version of APIs implemented in ATF */
+#define ATF_API_BASE_VERSION (1U)
+
+/* Capabilities for RAM */
+#define PM_CAP_ACCESS 0x1U
+#define PM_CAP_CONTEXT 0x2U
+
+#define MAX_LATENCY (~0U)
+#define MAX_QOS 100U
+
+/* State arguments of the self suspend */
+#define PM_STATE_CPU_IDLE 0x0U
+#define PM_STATE_SUSPEND_TO_RAM 0xFU
+
+/* APU processor states */
+#define PM_PROC_STATE_FORCEDOFF 0U
+#define PM_PROC_STATE_ACTIVE 1U
+#define PM_PROC_STATE_SLEEP 2U
+#define PM_PROC_STATE_SUSPENDING 3U
+
+#define EM_FUNID_NUM_MASK 0xF0000U
+
+#define PM_GET_CALLBACK_DATA 0xa01
+#define PM_SET_SUSPEND_MODE 0xa02
+#define PM_GET_TRUSTZONE_VERSION 0xa03
+
+/*********************************************************************
+ * Enum definitions
+ ********************************************************************/
+
+enum pm_api_id {
+ /* Miscellaneous API functions: */
+ PM_GET_API_VERSION = 1, /* Do not change or move */
+ PM_SET_CONFIGURATION,
+ PM_GET_NODE_STATUS,
+ PM_GET_OP_CHARACTERISTIC,
+ PM_REGISTER_NOTIFIER,
+ /* API for suspending of PUs: */
+ PM_REQ_SUSPEND,
+ PM_SELF_SUSPEND,
+ PM_FORCE_POWERDOWN,
+ PM_ABORT_SUSPEND,
+ PM_REQ_WAKEUP,
+ PM_SET_WAKEUP_SOURCE,
+ PM_SYSTEM_SHUTDOWN,
+ /* API for managing PM slaves: */
+ PM_REQ_NODE,
+ PM_RELEASE_NODE,
+ PM_SET_REQUIREMENT,
+ PM_SET_MAX_LATENCY,
+ /* Direct control API functions: */
+ PM_RESET_ASSERT,
+ PM_RESET_GET_STATUS,
+ PM_MMIO_WRITE,
+ PM_MMIO_READ,
+ PM_INIT_FINALIZE,
+ PM_FPGA_LOAD,
+ PM_FPGA_GET_STATUS,
+ PM_GET_CHIPID,
+ PM_SECURE_RSA_AES,
+ PM_SECURE_SHA,
+ PM_SECURE_RSA,
+ PM_PINCTRL_REQUEST,
+ PM_PINCTRL_RELEASE,
+ PM_PINCTRL_GET_FUNCTION,
+ PM_PINCTRL_SET_FUNCTION,
+ PM_PINCTRL_CONFIG_PARAM_GET,
+ PM_PINCTRL_CONFIG_PARAM_SET,
+ PM_IOCTL,
+ /* API to query information from firmware */
+ PM_QUERY_DATA,
+ /* Clock control API functions */
+ PM_CLOCK_ENABLE,
+ PM_CLOCK_DISABLE,
+ PM_CLOCK_GETSTATE,
+ PM_CLOCK_SETDIVIDER,
+ PM_CLOCK_GETDIVIDER,
+ PM_CLOCK_SETRATE,
+ PM_CLOCK_GETRATE,
+ PM_CLOCK_SETPARENT,
+ PM_CLOCK_GETPARENT,
+ PM_SECURE_IMAGE,
+ /* FPGA PL Readback */
+ PM_FPGA_READ,
+ PM_SECURE_AES,
+ /* PLL control API functions */
+ PM_PLL_SET_PARAMETER,
+ PM_PLL_GET_PARAMETER,
+ PM_PLL_SET_MODE,
+ PM_PLL_GET_MODE,
+ /* PM Register Access API */
+ PM_REGISTER_ACCESS,
+ PM_EFUSE_ACCESS,
+ PM_FPGA_GET_VERSION,
+ PM_FPGA_GET_FEATURE_LIST,
+ PM_FEATURE_CHECK = 63,
+ PM_API_MAX
+};
+
+enum pm_query_id {
+ PM_QID_INVALID = 0,
+ PM_QID_CLOCK_GET_NAME,
+ PM_QID_CLOCK_GET_TOPOLOGY,
+ PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
+ PM_QID_CLOCK_GET_PARENTS,
+ PM_QID_CLOCK_GET_ATTRIBUTES,
+ PM_QID_PINCTRL_GET_NUM_PINS,
+ PM_QID_PINCTRL_GET_NUM_FUNCTIONS,
+ PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS,
+ PM_QID_PINCTRL_GET_FUNCTION_NAME,
+ PM_QID_PINCTRL_GET_FUNCTION_GROUPS,
+ PM_QID_PINCTRL_GET_PIN_GROUPS,
+ PM_QID_CLOCK_GET_NUM_CLOCKS,
+ PM_QID_CLOCK_GET_MAX_DIVISOR,
+};
+
+enum pm_node_id {
+ NODE_UNKNOWN = 0,
+ NODE_APU,
+ NODE_APU_0,
+ NODE_APU_1,
+ NODE_APU_2,
+ NODE_APU_3,
+ NODE_RPU,
+ NODE_RPU_0,
+ NODE_RPU_1,
+ NODE_PLD,
+ NODE_FPD,
+ NODE_OCM_BANK_0,
+ NODE_OCM_BANK_1,
+ NODE_OCM_BANK_2,
+ NODE_OCM_BANK_3,
+ NODE_TCM_0_A,
+ NODE_TCM_0_B,
+ NODE_TCM_1_A,
+ NODE_TCM_1_B,
+ NODE_L2,
+ NODE_GPU_PP_0,
+ NODE_GPU_PP_1,
+ NODE_USB_0,
+ NODE_USB_1,
+ NODE_TTC_0,
+ NODE_TTC_1,
+ NODE_TTC_2,
+ NODE_TTC_3,
+ NODE_SATA,
+ NODE_ETH_0,
+ NODE_ETH_1,
+ NODE_ETH_2,
+ NODE_ETH_3,
+ NODE_UART_0,
+ NODE_UART_1,
+ NODE_SPI_0,
+ NODE_SPI_1,
+ NODE_I2C_0,
+ NODE_I2C_1,
+ NODE_SD_0,
+ NODE_SD_1,
+ NODE_DP,
+ NODE_GDMA,
+ NODE_ADMA,
+ NODE_NAND,
+ NODE_QSPI,
+ NODE_GPIO,
+ NODE_CAN_0,
+ NODE_CAN_1,
+ NODE_EXTERN,
+ NODE_APLL,
+ NODE_VPLL,
+ NODE_DPLL,
+ NODE_RPLL,
+ NODE_IOPLL,
+ NODE_DDR,
+ NODE_IPI_APU,
+ NODE_IPI_RPU_0,
+ NODE_GPU,
+ NODE_PCIE,
+ NODE_PCAP,
+ NODE_RTC,
+ NODE_LPD,
+ NODE_VCU,
+ NODE_IPI_RPU_1,
+ NODE_IPI_PL_0,
+ NODE_IPI_PL_1,
+ NODE_IPI_PL_2,
+ NODE_IPI_PL_3,
+ NODE_PL,
+ NODE_GEM_TSU,
+ NODE_SWDT_0,
+ NODE_SWDT_1,
+ NODE_CSU,
+ NODE_PJTAG,
+ NODE_TRACE,
+ NODE_TESTSCAN,
+ NODE_PMU,
+ NODE_MAX,
+};
+
+enum pm_request_ack {
+ REQ_ACK_NO = 1,
+ REQ_ACK_BLOCKING,
+ REQ_ACK_NON_BLOCKING,
+};
+
+enum pm_abort_reason {
+ ABORT_REASON_WKUP_EVENT = 100,
+ ABORT_REASON_PU_BUSY,
+ ABORT_REASON_NO_PWRDN,
+ ABORT_REASON_UNKNOWN,
+};
+
+enum pm_suspend_reason {
+ SUSPEND_REASON_PU_REQ = 201,
+ SUSPEND_REASON_ALERT,
+ SUSPEND_REASON_SYS_SHUTDOWN,
+};
+
+enum pm_ram_state {
+ PM_RAM_STATE_OFF = 1,
+ PM_RAM_STATE_RETENTION,
+ PM_RAM_STATE_ON,
+};
+
+enum pm_opchar_type {
+ PM_OPCHAR_TYPE_POWER = 1,
+ PM_OPCHAR_TYPE_TEMP,
+ PM_OPCHAR_TYPE_LATENCY,
+};
+
+/**
+ * @PM_RET_SUCCESS: success
+ * @PM_RET_ERROR_ARGS: illegal arguments provided (deprecated)
+ * @PM_RET_ERROR_NOTSUPPORTED: feature not supported (deprecated)
+ * @PM_RET_ERROR_NOT_ENABLED: feature is not enabled
+ * @PM_RET_ERROR_INTERNAL: internal error
+ * @PM_RET_ERROR_CONFLICT: conflict
+ * @PM_RET_ERROR_ACCESS: access rights violation
+ * @PM_RET_ERROR_INVALID_NODE: invalid node
+ * @PM_RET_ERROR_DOUBLE_REQ: duplicate request for same node
+ * @PM_RET_ERROR_ABORT_SUSPEND: suspend procedure has been aborted
+ * @PM_RET_ERROR_TIMEOUT: timeout in communication with PMU
+ * @PM_RET_ERROR_NODE_USED: node is already in use
+ */
+enum pm_ret_status {
+ PM_RET_SUCCESS = (0U),
+ PM_RET_ERROR_ARGS = (1U),
+ PM_RET_ERROR_NOTSUPPORTED = (4U),
+ PM_RET_ERROR_NOT_ENABLED = (29U),
+ PM_RET_ERROR_INTERNAL = (2000U),
+ PM_RET_ERROR_CONFLICT = (2001U),
+ PM_RET_ERROR_ACCESS = (2002U),
+ PM_RET_ERROR_INVALID_NODE = (2003U),
+ PM_RET_ERROR_DOUBLE_REQ = (2004U),
+ PM_RET_ERROR_ABORT_SUSPEND = (2005U),
+ PM_RET_ERROR_TIMEOUT = (2006U),
+ PM_RET_ERROR_NODE_USED = (2007U),
+ PM_RET_ERROR_NO_FEATURE = (2008U)
+};
+
+/**
+ * @PM_INITIAL_BOOT: boot is a fresh system startup
+ * @PM_RESUME: boot is a resume
+ * @PM_BOOT_ERROR: error, boot cause cannot be identified
+ */
+enum pm_boot_status {
+ PM_INITIAL_BOOT,
+ PM_RESUME,
+ PM_BOOT_ERROR,
+};
+
+/**
+ * @PMF_SHUTDOWN_TYPE_SHUTDOWN: shutdown
+ * @PMF_SHUTDOWN_TYPE_RESET: reset/reboot
+ * @PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY: set the shutdown/reboot scope
+ */
+enum pm_shutdown_type {
+ PMF_SHUTDOWN_TYPE_SHUTDOWN,
+ PMF_SHUTDOWN_TYPE_RESET,
+ PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY,
+};
+
+/**
+ * @PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM: shutdown/reboot APU subsystem only
+ * @PMF_SHUTDOWN_SUBTYPE_PS_ONLY: shutdown/reboot entire PS (but not PL)
+ * @PMF_SHUTDOWN_SUBTYPE_SYSTEM: shutdown/reboot entire system
+ */
+enum pm_shutdown_subtype {
+ PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM,
+ PMF_SHUTDOWN_SUBTYPE_PS_ONLY,
+ PMF_SHUTDOWN_SUBTYPE_SYSTEM,
+};
+
+/**
+ * @PM_PLL_PARAM_DIV2: Enable for divide by 2 function inside the PLL
+ * @PM_PLL_PARAM_FBDIV: Feedback divisor integer portion for the PLL
+ * @PM_PLL_PARAM_DATA: Feedback divisor fractional portion for the PLL
+ * @PM_PLL_PARAM_PRE_SRC: Clock source for PLL input
+ * @PM_PLL_PARAM_POST_SRC: Clock source for PLL Bypass mode
+ * @PM_PLL_PARAM_LOCK_DLY: Lock circuit config settings for lock windowsize
+ * @PM_PLL_PARAM_LOCK_CNT: Lock circuit counter setting
+ * @PM_PLL_PARAM_LFHF: PLL loop filter high frequency capacitor control
+ * @PM_PLL_PARAM_CP: PLL charge pump control
+ * @PM_PLL_PARAM_RES: PLL loop filter resistor control
+ */
+enum pm_pll_param {
+ PM_PLL_PARAM_DIV2,
+ PM_PLL_PARAM_FBDIV,
+ PM_PLL_PARAM_DATA,
+ PM_PLL_PARAM_PRE_SRC,
+ PM_PLL_PARAM_POST_SRC,
+ PM_PLL_PARAM_LOCK_DLY,
+ PM_PLL_PARAM_LOCK_CNT,
+ PM_PLL_PARAM_LFHF,
+ PM_PLL_PARAM_CP,
+ PM_PLL_PARAM_RES,
+ PM_PLL_PARAM_MAX,
+};
+
+/**
+ * @PM_PLL_MODE_RESET: PLL is in reset (not locked)
+ * @PM_PLL_MODE_INTEGER: PLL is locked in integer mode
+ * @PM_PLL_MODE_FRACTIONAL: PLL is locked in fractional mode
+ */
+enum pm_pll_mode {
+ PM_PLL_MODE_RESET,
+ PM_PLL_MODE_INTEGER,
+ PM_PLL_MODE_FRACTIONAL,
+ PM_PLL_MODE_MAX,
+};
+
+/**
+ * @PM_CLOCK_DIV0_ID: Clock divider 0
+ * @PM_CLOCK_DIV1_ID: Clock divider 1
+ */
+enum pm_clock_div_id {
+ PM_CLOCK_DIV0_ID,
+ PM_CLOCK_DIV1_ID,
+};
+
+/**
+ * EM API IDs
+ */
+enum em_api_id {
+ EM_SET_ACTION = 1,
+ EM_REMOVE_ACTION,
+ EM_SEND_ERRORS,
+};
+
+#endif /* PM_DEFS_H */
diff --git a/sys/dev/firmware/xilinx/zynqmp_firmware.c b/sys/dev/firmware/xilinx/zynqmp_firmware.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/firmware/xilinx/zynqmp_firmware.c
@@ -0,0 +1,511 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/psci/smccc.h>
+
+#include <dev/firmware/xilinx/pm_defs.h>
+
+#include "zynqmp_firmware_if.h"
+
+enum {
+ IOCTL_GET_RPU_OPER_MODE = 0,
+ IOCTL_SET_RPU_OPER_MODE = 1,
+ IOCTL_RPU_BOOT_ADDR_CONFIG = 2,
+ IOCTL_TCM_COMB_CONFIG = 3,
+ IOCTL_SET_TAPDELAY_BYPASS = 4,
+ IOCTL_SET_SGMII_MODE = 5,
+ IOCTL_SD_DLL_RESET = 6,
+ IOCTL_SET_SD_TAPDELAY = 7,
+ /* Ioctl for clock driver */
+ IOCTL_SET_PLL_FRAC_MODE = 8,
+ IOCTL_GET_PLL_FRAC_MODE = 9,
+ IOCTL_SET_PLL_FRAC_DATA = 10,
+ IOCTL_GET_PLL_FRAC_DATA = 11,
+ IOCTL_WRITE_GGS = 12,
+ IOCTL_READ_GGS = 13,
+ IOCTL_WRITE_PGGS = 14,
+ IOCTL_READ_PGGS = 15,
+ /* IOCTL for ULPI reset */
+ IOCTL_ULPI_RESET = 16,
+ /* Set healthy bit value */
+ IOCTL_SET_BOOT_HEALTH_STATUS = 17,
+ IOCTL_AFI = 18,
+ /* Probe counter read/write */
+ IOCTL_PROBE_COUNTER_READ = 19,
+ IOCTL_PROBE_COUNTER_WRITE = 20,
+ IOCTL_OSPI_MUX_SELECT = 21,
+ /* IOCTL for USB power request */
+ IOCTL_USB_SET_STATE = 22,
+ /* IOCTL to get last reset reason */
+ IOCTL_GET_LAST_RESET_REASON = 23,
+ /* AI engine NPI ISR clear */
+ IOCTL_AIE_ISR_CLEAR = 24,
+ /* Register SGI to ATF */
+ IOCTL_REGISTER_SGI = 25,
+};
+
+typedef int (*zynqmp_callfn_t)(register_t, register_t, register_t, uint32_t *payload);
+
+struct zynqmp_firmware_softc {
+ struct simplebus_softc sc;
+ device_t dev;
+ zynqmp_callfn_t callfn;
+};
+
+/* SMC calling methods */
+#define PM_SIP_SVC 0xC2000000
+
+static int
+zynqmp_call_smc(uint32_t id, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t *payload, bool ignore_error)
+{
+ struct arm_smccc_res res;
+ uint64_t args[3];
+
+ args[0] = id | PM_SIP_SVC;
+ args[1] = ((uint64_t)a1 << 32) | a0;
+ args[2] = ((uint64_t)a3 << 32) | a2;
+ arm_smccc_smc(args[0], args[1], args[2], 0, 0, 0, 0, 0, &res);
+ if (payload != NULL) {
+ payload[0] = res.a0 & 0xFFFFFFFF;
+ payload[1] = res.a0 >> 32;
+ payload[2] = res.a1 & 0xFFFFFFFF;
+ payload[3] = res.a1 >> 32;
+ if (!ignore_error && payload[0] != PM_RET_SUCCESS) {
+ printf("%s: fail %x\n", __func__, payload[0]);
+ return (EINVAL);
+ }
+ }
+ return (0);
+}
+
+/* Firmware methods */
+static int
+zynqmp_get_api_version(struct zynqmp_firmware_softc *sc)
+{
+ uint32_t payload[4];
+ int rv;
+
+ rv = zynqmp_call_smc(PM_GET_API_VERSION, 0, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ device_printf(sc->dev, "API version = %d.%d\n",
+ payload[1] >> 16, payload[1] & 0xFFFF);
+out:
+ return (rv);
+}
+
+static int
+zynqmp_get_chipid(struct zynqmp_firmware_softc *sc)
+{
+ uint32_t payload[4];
+ int rv;
+
+ rv = zynqmp_call_smc(PM_GET_CHIPID, 0, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ device_printf(sc->dev, "ID Code = %x Version = %x\n",
+ payload[1], payload[2]);
+out:
+ return (rv);
+}
+
+static int
+zynqmp_get_trustzone_version(struct zynqmp_firmware_softc *sc)
+{
+ uint32_t payload[4];
+ int rv;
+
+ rv = zynqmp_call_smc(PM_GET_TRUSTZONE_VERSION, 0, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ device_printf(sc->dev, "Trustzone Version = %x\n",
+ payload[1]);
+out:
+ return (rv);
+}
+
+/* zynqmp_firmware methods */
+static int
+zynqmp_firmware_clock_enable(device_t dev, uint32_t clkid)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_ENABLE, clkid, 0, 0, 0, payload, false);
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_disable(device_t dev, uint32_t clkid)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_DISABLE, clkid, 0, 0, 0, payload, false);
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_getstate(device_t dev, uint32_t clkid, bool *enabled)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_GETSTATE, clkid, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *enabled = payload[1] == 1 ? true : false;
+
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_setdivider(device_t dev, uint32_t clkid, uint32_t div)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_SETDIVIDER, clkid, div, 0, 0, payload, false);
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_getdivider(device_t dev, uint32_t clkid, uint32_t *div)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_GETDIVIDER, clkid, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *div = payload[1];
+
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_setparent(device_t dev, uint32_t clkid, uint32_t parentid)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_SETPARENT, clkid, parentid, 0, 0, payload, false);
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_getparent(device_t dev, uint32_t clkid, uint32_t *parentid)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_CLOCK_GETPARENT, clkid, 0, 0, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *parentid = payload[1];
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_pll_get_mode(device_t dev, uint32_t pllid, uint32_t *mode)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_MODE, pllid, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *mode = payload[1];
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_pll_get_frac_data(device_t dev, uint32_t pllid, uint32_t *data)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_DATA, pllid, 0, payload, false);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *data = payload[1];
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_clock_get_fixedfactor(device_t dev, uint32_t clkid, uint32_t *mult, uint32_t *div)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_QUERY_DATA, PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, clkid, 0, 0, payload, true);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ goto out;
+ }
+ *mult = payload[1];
+ *div = payload[2];
+out:
+ return (rv);
+}
+
+static int
+zynqmp_firmware_query_data(device_t dev, uint32_t qid, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t *data)
+{
+ struct zynqmp_firmware_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_QUERY_DATA, qid, arg1, arg2, arg3, data, true);
+ /*
+ * PM_QID_CLOCK_GET_NAME always success and if the clock name couldn't
+ * be found the clock name will be all null byte
+ */
+ if (qid == 1)
+ rv = 0;
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+}
+
+static int
+zynqmp_firmware_reset_assert(device_t dev, uint32_t resetid, bool enable)
+{
+ struct zynqmp_firmware_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_RESET_ASSERT, resetid, enable, 0, 0, NULL, true);
+ if (rv != 0)
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+
+ return (rv);
+}
+
+static int
+zynqmp_firmware_reset_get_status(device_t dev, uint32_t resetid, bool *status)
+{
+ struct zynqmp_firmware_softc *sc;
+ uint32_t payload[4];
+ int rv;
+
+ sc = device_get_softc(dev);
+ rv = zynqmp_call_smc(PM_RESET_GET_STATUS, resetid, 0, 0, 0, payload, true);
+ if (rv != 0) {
+ device_printf(sc->dev, "SMC Call fail %d\n", rv);
+ return (rv);
+ }
+ *status = payload[1];
+
+ return (rv);
+}
+
+/* Simplebus methods */
+static struct simplebus_devinfo *
+zynqmp_firmware_setup_dinfo(device_t dev, phandle_t node,
+ struct simplebus_devinfo *di)
+{
+ struct simplebus_softc *sc;
+ struct simplebus_devinfo *ndi;
+
+ sc = device_get_softc(dev);
+ if (di == NULL)
+ ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
+ else
+ ndi = di;
+ if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
+ if (di == NULL)
+ free(ndi, M_DEVBUF);
+ return (NULL);
+ }
+
+ /* reg resources is from the parent but interrupts is on the node itself */
+ resource_list_init(&ndi->rl);
+ ofw_bus_reg_to_rl(dev, OF_parent(node), sc->acells, sc->scells, &ndi->rl);
+ ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL);
+
+ return (ndi);
+}
+
+static device_t
+zynqmp_firmware_add_device(device_t dev, phandle_t node, u_int order,
+ const char *name, int unit, struct simplebus_devinfo *di)
+{
+ struct simplebus_devinfo *ndi;
+ device_t cdev;
+
+ if ((ndi = zynqmp_firmware_setup_dinfo(dev, node, di)) == NULL)
+ return (NULL);
+ cdev = device_add_child_ordered(dev, order, name, unit);
+ if (cdev == NULL) {
+ device_printf(dev, "<%s>: device_add_child failed\n",
+ ndi->obdinfo.obd_name);
+ resource_list_free(&ndi->rl);
+ ofw_bus_gen_destroy_devinfo(&ndi->obdinfo);
+ if (di == NULL)
+ free(ndi, M_DEVBUF);
+ return (NULL);
+ }
+ device_set_ivars(cdev, ndi);
+
+ return(cdev);
+}
+
+static int
+zynqmp_firmware_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-firmware"))
+ return (ENXIO);
+ device_set_desc(dev, "ZynqMP Firmware");
+ return (0);
+}
+
+static int
+zynqmp_firmware_attach(device_t dev)
+{
+ struct zynqmp_firmware_softc *sc;
+ phandle_t node, child;
+ device_t cdev;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bootverbose) {
+ zynqmp_get_api_version(sc);
+ zynqmp_get_chipid(sc);
+ zynqmp_get_trustzone_version(sc);
+ }
+
+ /* Attach children */
+ node = ofw_bus_get_node(dev);
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ cdev = zynqmp_firmware_add_device(dev, child, 0, NULL, -1, NULL);
+ if (cdev != NULL)
+ device_probe_and_attach(cdev);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static device_method_t zynqmp_firmware_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, zynqmp_firmware_probe),
+ DEVMETHOD(device_attach, zynqmp_firmware_attach),
+
+ /* zynqmp_firmware_if */
+ DEVMETHOD(zynqmp_firmware_clock_enable, zynqmp_firmware_clock_enable),
+ DEVMETHOD(zynqmp_firmware_clock_disable, zynqmp_firmware_clock_disable),
+ DEVMETHOD(zynqmp_firmware_clock_getstate, zynqmp_firmware_clock_getstate),
+ DEVMETHOD(zynqmp_firmware_clock_setdivider, zynqmp_firmware_clock_setdivider),
+ DEVMETHOD(zynqmp_firmware_clock_getdivider, zynqmp_firmware_clock_getdivider),
+ DEVMETHOD(zynqmp_firmware_clock_setparent, zynqmp_firmware_clock_setparent),
+ DEVMETHOD(zynqmp_firmware_clock_getparent, zynqmp_firmware_clock_getparent),
+ DEVMETHOD(zynqmp_firmware_pll_get_mode, zynqmp_firmware_pll_get_mode),
+ DEVMETHOD(zynqmp_firmware_pll_get_frac_data, zynqmp_firmware_pll_get_frac_data),
+ DEVMETHOD(zynqmp_firmware_clock_get_fixedfactor, zynqmp_firmware_clock_get_fixedfactor),
+ DEVMETHOD(zynqmp_firmware_query_data, zynqmp_firmware_query_data),
+ DEVMETHOD(zynqmp_firmware_reset_assert, zynqmp_firmware_reset_assert),
+ DEVMETHOD(zynqmp_firmware_reset_get_status, zynqmp_firmware_reset_get_status),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(zynqmp_firmware, zynqmp_firmware_driver, zynqmp_firmware_methods,
+ sizeof(struct zynqmp_firmware_softc), simplebus_driver);
+
+EARLY_DRIVER_MODULE(zynqmp_firmware, simplebus, zynqmp_firmware_driver, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
+MODULE_VERSION(zynqmp_firmware, 1);
diff --git a/sys/dev/firmware/xilinx/zynqmp_firmware_if.m b/sys/dev/firmware/xilinx/zynqmp_firmware_if.m
new file mode 100644
--- /dev/null
+++ b/sys/dev/firmware/xilinx/zynqmp_firmware_if.m
@@ -0,0 +1,109 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+INTERFACE zynqmp_firmware;
+
+METHOD int clock_enable {
+ device_t dev;
+ uint32_t clkid;
+};
+
+METHOD int clock_disable {
+ device_t dev;
+ uint32_t clkid;
+};
+
+METHOD int clock_getstate {
+ device_t dev;
+ uint32_t clkid;
+ bool *enabled;
+};
+
+METHOD int clock_setdivider {
+ device_t dev;
+ uint32_t clkid;
+ uint32_t div;
+};
+
+METHOD int clock_getdivider {
+ device_t dev;
+ uint32_t clkid;
+ uint32_t *div;
+};
+
+METHOD int clock_setparent {
+ device_t dev;
+ uint32_t clkid;
+ uint32_t parentid;
+};
+
+METHOD int clock_getparent {
+ device_t dev;
+ uint32_t clkid;
+ uint32_t *parentid;
+};
+
+METHOD int pll_get_mode {
+ device_t dev;
+ uint32_t pllid;
+ uint32_t *mode;
+};
+
+METHOD int pll_get_frac_data {
+ device_t dev;
+ uint32_t pllid;
+ uint32_t *data;
+};
+
+METHOD int clock_get_fixedfactor {
+ device_t dev;
+ uint32_t clkid;
+ uint32_t *mult;
+ uint32_t *div;
+};
+
+METHOD int query_data {
+ device_t dev;
+ uint32_t qid;
+ uint32_t arg1;
+ uint32_t arg2;
+ uint32_t arg3;
+ uint32_t *data;
+};
+
+METHOD int reset_assert {
+ device_t dev;
+ uint32_t resetid;
+ bool enable;
+};
+
+METHOD int reset_get_status {
+ device_t dev;
+ uint32_t resetid;
+ bool *status;
+};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 16, 3:40 AM (20 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14652609
Default Alt Text
D41811.id127495.diff (26 KB)
Attached To
Mode
D41811: arm64: zynqmp: Add firmware driver
Attached
Detach File
Event Timeline
Log In to Comment