git: ab8f34675ae7 - stable/14 - arm64: zynqmp: Add firmware driver
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 18 Oct 2023 14:31:36 UTC
The branch stable/14 has been updated by manu: URL: https://cgit.FreeBSD.org/src/commit/?id=ab8f34675ae705060a6160521b1e7a91c5e00a9a commit ab8f34675ae705060a6160521b1e7a91c5e00a9a Author: Emmanuel Vadot <manu@FreeBSD.org> AuthorDate: 2023-09-07 07:56:45 +0000 Commit: Emmanuel Vadot <manu@FreeBSD.org> CommitDate: 2023-10-18 14:31:01 +0000 arm64: zynqmp: Add firmware driver The ZynqMP SoC have a MCU running a firmware to control clocks, resets, fpga loading etc ... Add a driver that can be use to communicate with it. For now only the clock and reset part are implemented. Differential Revision: https://reviews.freebsd.org/D41811 Sponsored by: Beckhoff Automation GmbH & Co. KG (cherry picked from commit 9e88711f28dc9afa7d68ae8dd027d2399a2a290b) --- sys/conf/files.arm64 | 2 + sys/dev/firmware/xilinx/pm_defs.h | 380 ++++++++++++++++++++ sys/dev/firmware/xilinx/zynqmp_firmware.c | 511 +++++++++++++++++++++++++++ sys/dev/firmware/xilinx/zynqmp_firmware_if.m | 109 ++++++ 4 files changed, 1002 insertions(+) diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 02dcadc0aa5c..987a1ea6cd09 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -698,3 +698,5 @@ arm64/rockchip/clk/rk3568_pmucru.c optional fdt soc_rockchip_rk3568 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 index 000000000000..3461dd7820ad --- /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 index 000000000000..8ee6c9a21377 --- /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 index 000000000000..1007d721952d --- /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; +}; *** 70 LINES SKIPPED ***