travis: Move keystone 3 (k3) boards into the k2 job
[platform/kernel/u-boot.git] / arch / arm / mach-zynqmp / pmu_ipc.c
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
33 static 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
44 static 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  */
68 static 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  */
96 void 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 }