mirror of
https://github.com/opnsense/src.git
synced 2026-06-04 14:26:03 -04:00
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
This commit is contained in:
parent
8dc9b10e38
commit
9e88711f28
4 changed files with 1002 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
380
sys/dev/firmware/xilinx/pm_defs.h
Normal file
380
sys/dev/firmware/xilinx/pm_defs.h
Normal file
|
|
@ -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 */
|
||||
511
sys/dev/firmware/xilinx/zynqmp_firmware.c
Normal file
511
sys/dev/firmware/xilinx/zynqmp_firmware.c
Normal file
|
|
@ -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);
|
||||
109
sys/dev/firmware/xilinx/zynqmp_firmware_if.m
Normal file
109
sys/dev/firmware/xilinx/zynqmp_firmware_if.m
Normal file
|
|
@ -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;
|
||||
};
|
||||
Loading…
Reference in a new issue