uboot/drivers/firmware/firmware-zynqmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xilinx Zynq MPSoC Firmware driver
   4 *
   5 * Copyright (C) 2018-2019 Xilinx, Inc.
   6 */
   7
   8#include <common.h>
   9#include <cpu_func.h>
  10#include <dm.h>
  11#include <dm/lists.h>
  12#include <log.h>
  13#include <zynqmp_firmware.h>
  14#include <asm/cache.h>
  15#include <asm/ptrace.h>
  16
  17#if defined(CONFIG_ZYNQMP_IPI)
  18#include <mailbox.h>
  19#include <asm/arch/sys_proto.h>
  20
  21#define PMUFW_PAYLOAD_ARG_CNT   8
  22
  23#define XST_PM_NO_ACCESS        2002L
  24#define XST_PM_ALREADY_CONFIGURED       2009L
  25
  26struct zynqmp_power {
  27        struct mbox_chan tx_chan;
  28        struct mbox_chan rx_chan;
  29} zynqmp_power = {};
  30
  31#define NODE_ID_LOCATION        5
  32
  33static unsigned int xpm_configobject[] = {
  34        /**********************************************************************/
  35        /* HEADER */
  36        2,      /* Number of remaining words in the header */
  37        1,      /* Number of sections included in config object */
  38        PM_CONFIG_OBJECT_TYPE_OVERLAY,  /* Type of Config object as overlay */
  39        /**********************************************************************/
  40        /* SLAVE SECTION */
  41
  42        PM_CONFIG_SLAVE_SECTION_ID,     /* Section ID */
  43        1,                              /* Number of slaves */
  44
  45        0, /* Node ID which will be changed below */
  46        PM_SLAVE_FLAG_IS_SHAREABLE,
  47        PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK |
  48        PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK |
  49        PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK, /* IPI Mask */
  50};
  51
  52static unsigned int xpm_configobject_close[] = {
  53        /**********************************************************************/
  54        /* HEADER */
  55        2,      /* Number of remaining words in the header */
  56        1,      /* Number of sections included in config object */
  57        PM_CONFIG_OBJECT_TYPE_OVERLAY,  /* Type of Config object as overlay */
  58        /**********************************************************************/
  59        /* SET CONFIG SECTION */
  60        PM_CONFIG_SET_CONFIG_SECTION_ID,
  61        0U,     /* Loading permission to Overlay config object */
  62};
  63
  64int zynqmp_pmufw_config_close(void)
  65{
  66        zynqmp_pmufw_load_config_object(xpm_configobject_close,
  67                                        sizeof(xpm_configobject_close));
  68        return 0;
  69}
  70
  71int zynqmp_pmufw_node(u32 id)
  72{
  73        static bool skip_config;
  74        int ret;
  75
  76        if (skip_config)
  77                return 0;
  78
  79        /* Record power domain id */
  80        xpm_configobject[NODE_ID_LOCATION] = id;
  81
  82        ret = zynqmp_pmufw_load_config_object(xpm_configobject,
  83                                              sizeof(xpm_configobject));
  84
  85        if (ret == XST_PM_NO_ACCESS && id == NODE_OCM_BANK_0)
  86                skip_config = true;
  87
  88        return 0;
  89}
  90
  91static int do_pm_probe(void)
  92{
  93        struct udevice *dev;
  94        int ret;
  95
  96        ret = uclass_get_device_by_driver(UCLASS_FIRMWARE,
  97                                          DM_DRIVER_GET(zynqmp_power),
  98                                          &dev);
  99        if (ret)
 100                debug("%s: Probing device failed: %d\n", __func__, ret);
 101
 102        return ret;
 103}
 104
 105static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
 106{
 107        struct zynqmp_ipi_msg msg;
 108        int ret;
 109        u32 buffer[PAYLOAD_ARG_CNT];
 110
 111        if (!res)
 112                res = buffer;
 113
 114        if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
 115            res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
 116                return -EINVAL;
 117
 118        if (!(zynqmp_power.tx_chan.dev) || !(zynqmp_power.rx_chan.dev)) {
 119                ret = do_pm_probe();
 120                if (ret)
 121                        return ret;
 122        }
 123
 124        debug("%s, Sending IPI message with ID: 0x%0x\n", __func__, req[0]);
 125        msg.buf = (u32 *)req;
 126        msg.len = req_len;
 127        ret = mbox_send(&zynqmp_power.tx_chan, &msg);
 128        if (ret) {
 129                debug("%s: Sending message failed\n", __func__);
 130                return ret;
 131        }
 132
 133        msg.buf = res;
 134        msg.len = res_maxlen;
 135        ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
 136        if (ret)
 137                debug("%s: Receiving message failed\n", __func__);
 138
 139        return ret;
 140}
 141
 142unsigned int zynqmp_firmware_version(void)
 143{
 144        int ret;
 145        u32 ret_payload[PAYLOAD_ARG_CNT];
 146        static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID;
 147
 148        /*
 149         * Get PMU version only once and later
 150         * just return stored values instead of
 151         * asking PMUFW again.
 152         **/
 153        if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) {
 154
 155                ret = xilinx_pm_request(PM_GET_API_VERSION, 0, 0, 0, 0,
 156                                        ret_payload);
 157                if (ret)
 158                        panic("PMUFW is not found - Please load it!\n");
 159
 160                pm_api_version = ret_payload[1];
 161                if (pm_api_version < ZYNQMP_PM_VERSION)
 162                        panic("PMUFW version error. Expected: v%d.%d\n",
 163                              ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
 164        }
 165
 166        return pm_api_version;
 167};
 168
 169int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value)
 170{
 171        int ret;
 172
 173        ret = xilinx_pm_request(PM_IOCTL, node, IOCTL_SET_GEM_CONFIG,
 174                                config, value, NULL);
 175        if (ret)
 176                printf("%s: node %d: set_gem_config %d failed\n",
 177                       __func__, node, config);
 178
 179        return ret;
 180}
 181
 182int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value)
 183{
 184        int ret;
 185
 186        ret = xilinx_pm_request(PM_IOCTL, node, IOCTL_SET_SD_CONFIG,
 187                                config, value, NULL);
 188        if (ret)
 189                printf("%s: node %d: set_sd_config %d failed\n",
 190                       __func__, node, config);
 191
 192        return ret;
 193}
 194
 195int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id)
 196{
 197        int ret;
 198        u32 *bit_mask;
 199        u32 ret_payload[PAYLOAD_ARG_CNT];
 200
 201        /* Input arguments validation */
 202        if (id >= 64 || (api_id != PM_IOCTL && api_id != PM_QUERY_DATA))
 203                return -EINVAL;
 204
 205        /* Check feature check API version */
 206        ret = xilinx_pm_request(PM_FEATURE_CHECK, PM_FEATURE_CHECK, 0, 0, 0,
 207                                ret_payload);
 208        if (ret)
 209                return ret;
 210
 211        /* Check if feature check version 2 is supported or not */
 212        if ((ret_payload[1] & FIRMWARE_VERSION_MASK) == PM_API_VERSION_2) {
 213                /*
 214                 * Call feature check for IOCTL/QUERY API to get IOCTL ID or
 215                 * QUERY ID feature status.
 216                 */
 217
 218                ret = xilinx_pm_request(PM_FEATURE_CHECK, api_id, 0, 0, 0,
 219                                        ret_payload);
 220                if (ret)
 221                        return ret;
 222
 223                bit_mask = &ret_payload[2];
 224                if ((bit_mask[(id / 32)] & BIT((id % 32))) == 0)
 225                        return -EOPNOTSUPP;
 226        } else {
 227                return -ENODATA;
 228        }
 229
 230        return 0;
 231}
 232
 233/**
 234 * Send a configuration object to the PMU firmware.
 235 *
 236 * @cfg_obj: Pointer to the configuration object
 237 * @size:    Size of @cfg_obj in bytes
 238 * Return:   0 on success otherwise negative errno. If the config object
 239 *           is not loadable returns positive errno XST_PM_NO_ACCESS(2002)
 240 */
 241int zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
 242{
 243        int err;
 244        u32 ret_payload[PAYLOAD_ARG_CNT];
 245
 246        if (IS_ENABLED(CONFIG_SPL_BUILD))
 247                printf("Loading new PMUFW cfg obj (%ld bytes)\n", size);
 248
 249        flush_dcache_range((ulong)cfg_obj, (ulong)(cfg_obj + size));
 250
 251        err = xilinx_pm_request(PM_SET_CONFIGURATION, (u32)(u64)cfg_obj, 0, 0,
 252                                0, ret_payload);
 253        if (err == XST_PM_NO_ACCESS) {
 254                if (((u32 *)cfg_obj)[NODE_ID_LOCATION] == NODE_OCM_BANK_0) {
 255                        printf("PMUFW:  No permission to change config object\n");
 256                        return err;
 257                }
 258                return -EACCES;
 259        }
 260
 261        if (err == XST_PM_ALREADY_CONFIGURED) {
 262                debug("PMUFW Node is already configured\n");
 263                return -ENODEV;
 264        }
 265
 266        if (err)
 267                printf("Cannot load PMUFW configuration object (%d)\n", err);
 268
 269        if (ret_payload[0])
 270                printf("PMUFW returned 0x%08x status!\n", ret_payload[0]);
 271
 272        if ((err || ret_payload[0]) && IS_ENABLED(CONFIG_SPL_BUILD))
 273                panic("PMUFW config object loading failed in EL3\n");
 274
 275        return 0;
 276}
 277
 278static int zynqmp_power_probe(struct udevice *dev)
 279{
 280        int ret;
 281
 282        debug("%s, (dev=%p)\n", __func__, dev);
 283
 284        ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
 285        if (ret) {
 286                debug("%s: Cannot find tx mailbox\n", __func__);
 287                return ret;
 288        }
 289
 290        ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
 291        if (ret) {
 292                debug("%s: Cannot find rx mailbox\n", __func__);
 293                return ret;
 294        }
 295
 296        ret = zynqmp_firmware_version();
 297        printf("PMUFW:\tv%d.%d\n",
 298               ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
 299               ret & ZYNQMP_PM_VERSION_MINOR_MASK);
 300
 301        if (IS_ENABLED(CONFIG_ARCH_ZYNQMP))
 302                zynqmp_pmufw_node(NODE_OCM_BANK_0);
 303
 304        return 0;
 305};
 306
 307static const struct udevice_id zynqmp_power_ids[] = {
 308        { .compatible = "xlnx,zynqmp-power" },
 309        { }
 310};
 311
 312U_BOOT_DRIVER(zynqmp_power) = {
 313        .name = "zynqmp_power",
 314        .id = UCLASS_FIRMWARE,
 315        .of_match = zynqmp_power_ids,
 316        .probe = zynqmp_power_probe,
 317};
 318#endif
 319
 320int __maybe_unused xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
 321                                     u32 arg3, u32 *ret_payload)
 322{
 323        debug("%s at EL%d, API ID: 0x%0x\n", __func__, current_el(), api_id);
 324
 325        if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
 326#if defined(CONFIG_ZYNQMP_IPI)
 327                /*
 328                 * Use fixed payload and arg size as the EL2 call. The firmware
 329                 * is capable to handle PMUFW_PAYLOAD_ARG_CNT bytes but the
 330                 * firmware API is limited by the SMC call size
 331                 */
 332                u32 regs[] = {api_id, arg0, arg1, arg2, arg3};
 333                int ret;
 334
 335                if (api_id == PM_FPGA_LOAD) {
 336                        /* Swap addr_hi/low because of incompatibility */
 337                        u32 temp = regs[1];
 338
 339                        regs[1] = regs[2];
 340                        regs[2] = temp;
 341                }
 342
 343                ret = ipi_req(regs, PAYLOAD_ARG_CNT, ret_payload,
 344                              PAYLOAD_ARG_CNT);
 345                if (ret)
 346                        return ret;
 347#else
 348                return -EPERM;
 349#endif
 350        } else {
 351                /*
 352                 * Added SIP service call Function Identifier
 353                 * Make sure to stay in x0 register
 354                 */
 355                struct pt_regs regs;
 356
 357                regs.regs[0] = PM_SIP_SVC | api_id;
 358                regs.regs[1] = ((u64)arg1 << 32) | arg0;
 359                regs.regs[2] = ((u64)arg3 << 32) | arg2;
 360
 361                smc_call(&regs);
 362
 363                if (ret_payload) {
 364                        ret_payload[0] = (u32)regs.regs[0];
 365                        ret_payload[1] = upper_32_bits(regs.regs[0]);
 366                        ret_payload[2] = (u32)regs.regs[1];
 367                        ret_payload[3] = upper_32_bits(regs.regs[1]);
 368                        ret_payload[4] = (u32)regs.regs[2];
 369                }
 370
 371        }
 372        return (ret_payload) ? ret_payload[0] : 0;
 373}
 374
 375static const struct udevice_id zynqmp_firmware_ids[] = {
 376        { .compatible = "xlnx,zynqmp-firmware" },
 377        { .compatible = "xlnx,versal-firmware"},
 378        { .compatible = "xlnx,versal-net-firmware"},
 379        { }
 380};
 381
 382static int zynqmp_firmware_bind(struct udevice *dev)
 383{
 384        int ret;
 385        struct udevice *child;
 386
 387        if ((IS_ENABLED(CONFIG_SPL_BUILD) &&
 388             IS_ENABLED(CONFIG_SPL_POWER_DOMAIN) &&
 389             IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN)) ||
 390             (!IS_ENABLED(CONFIG_SPL_BUILD) &&
 391              IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN))) {
 392                ret = device_bind_driver_to_node(dev, "zynqmp_power_domain",
 393                                                 "zynqmp_power_domain",
 394                                                 dev_ofnode(dev), &child);
 395                if (ret) {
 396                        printf("zynqmp power domain driver is not bound: %d\n", ret);
 397                        return ret;
 398                }
 399        }
 400
 401        return dm_scan_fdt_dev(dev);
 402}
 403
 404U_BOOT_DRIVER(zynqmp_firmware) = {
 405        .id = UCLASS_FIRMWARE,
 406        .name = "zynqmp_firmware",
 407        .of_match = zynqmp_firmware_ids,
 408        .bind = zynqmp_firmware_bind,
 409};
 410