uboot/arch/arm/mach-zynqmp/pmu_ipc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Inter-Processor Communication with the Platform Management Unit (PMU)
   4 * firmware.
   5 *
   6 * (C) Copyright 2019 Luca Ceresoli
   7 * Luca Ceresoli <luca@lucaceresoli.net>
   8 */
   9
  10#include <common.h>
  11#include <asm/io.h>
  12#include <asm/arch/sys_proto.h>
  13
  14/* IPI bitmasks, register base and register offsets */
  15#define IPI_BIT_MASK_APU      0x00001
  16#define IPI_BIT_MASK_PMU0     0x10000
  17#define IPI_REG_BASE_APU      0xFF300000
  18#define IPI_REG_BASE_PMU0     0xFF330000
  19#define IPI_REG_OFFSET_TRIG   0x00
  20#define IPI_REG_OFFSET_OBR    0x04
  21
  22/* IPI mailbox buffer offsets */
  23#define IPI_BUF_BASE_APU               0xFF990400
  24#define IPI_BUF_OFFSET_TARGET_PMU      0x1C0
  25#define IPI_BUF_OFFSET_REQ             0x00
  26#define IPI_BUF_OFFSET_RESP            0x20
  27
  28#define PMUFW_PAYLOAD_ARG_CNT          8
  29
  30/* PMUFW commands */
  31#define PMUFW_CMD_SET_CONFIGURATION    2
  32
  33static void pmu_ipc_send_request(const u32 *req, size_t req_len)
  34{
  35        u32 *mbx = (u32 *)(IPI_BUF_BASE_APU +
  36                           IPI_BUF_OFFSET_TARGET_PMU +
  37                           IPI_BUF_OFFSET_REQ);
  38        size_t i;
  39
  40        for (i = 0; i < req_len; i++)
  41                writel(req[i], &mbx[i]);
  42}
  43
  44static void pmu_ipc_read_response(unsigned int *value, size_t count)
  45{
  46        u32 *mbx = (u32 *)(IPI_BUF_BASE_APU +
  47                           IPI_BUF_OFFSET_TARGET_PMU +
  48                           IPI_BUF_OFFSET_RESP);
  49        size_t i;
  50
  51        for (i = 0; i < count; i++)
  52                value[i] = readl(&mbx[i]);
  53}
  54
  55/**
  56 * Send request to PMU and get the response.
  57 *
  58 * @req:        Request buffer. Byte 0 is the API ID, other bytes are optional
  59 *              parameters.
  60 * @req_len:    Request length in number of 32-bit words.
  61 * @res:        Response buffer. Byte 0 is the error code, other bytes are
  62 *              optional parameters. Optional, if @res_maxlen==0 the parameters
  63 *              will not be read.
  64 * @res_maxlen: Space allocated for the response in number of 32-bit words.
  65 *
  66 * @return Error code returned by the PMU (i.e. the first word of the response)
  67 */
  68static int pmu_ipc_request(const u32 *req, size_t req_len,
  69                           u32 *res, size_t res_maxlen)
  70{
  71        u32 status;
  72
  73        if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
  74            res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
  75                return -EINVAL;
  76
  77        pmu_ipc_send_request(req, req_len);
  78
  79        /* Raise Inter-Processor Interrupt to PMU and wait for response */
  80        writel(IPI_BIT_MASK_PMU0, IPI_REG_BASE_APU + IPI_REG_OFFSET_TRIG);
  81        do {
  82                status = readl(IPI_REG_BASE_APU + IPI_REG_OFFSET_OBR);
  83        } while (status & IPI_BIT_MASK_PMU0);
  84
  85        pmu_ipc_read_response(res, res_maxlen);
  86
  87        return 0;
  88}
  89
  90/**
  91 * Send a configuration object to the PMU firmware.
  92 *
  93 * @cfg_obj: Pointer to the configuration object
  94 * @size:    Size of @cfg_obj in bytes
  95 */
  96void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
  97{
  98        const u32 request[] = {
  99                PMUFW_CMD_SET_CONFIGURATION,
 100                (u32)((u64)cfg_obj)
 101        };
 102        u32 response;
 103        int err;
 104
 105        printf("Loading PMUFW cfg obj (%ld bytes)\n", size);
 106
 107        err = pmu_ipc_request(request,  ARRAY_SIZE(request), &response, 1);
 108        if (err)
 109                panic("Cannot load PMUFW configuration object (%d)\n", err);
 110        if (response != 0)
 111                panic("PMUFW returned 0x%08x status!\n", response);
 112}
 113