Luca Ceresoli | c28a9cf | 2019-05-21 18:06:43 +0200 | [diff] [blame] | 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 | } |