1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. 4 * Copyright (C) 2019-2022 Linaro Limited. 5 */ 6 7#define LOG_CATEGORY UCLASS_SCMI_AGENT 8 9#include <common.h> 10#include <cpu_func.h> 11#include <dm.h> 12#include <dm/device_compat.h> 13#include <errno.h> 14#include <scmi_agent.h> 15#include <asm/cache.h> 16#include <asm/system.h> 17#include <dm/ofnode.h> 18#include <linux/compat.h> 19#include <linux/io.h> 20#include <linux/ioport.h> 21 22#include "smt.h" 23 24/** 25 * Get shared memory configuration defined by the referred DT phandle 26 * Return with a errno compliant value. 27 */ 28int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) 29{ 30 int ret; 31 struct ofnode_phandle_args args; 32 struct resource resource; 33 34 ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args); 35 if (ret) 36 return ret; 37 38 ret = ofnode_read_resource(args.node, 0, &resource); 39 if (ret) 40 return ret; 41 42 smt->size = resource_size(&resource); 43 if (smt->size < sizeof(struct scmi_smt_header)) { 44 dev_err(dev, "Shared memory buffer too small\n"); 45 return -EINVAL; 46 } 47 48 smt->buf = devm_ioremap(dev, resource.start, smt->size); 49 if (!smt->buf) 50 return -ENOMEM; 51 52#ifdef CONFIG_ARM 53 if (dcache_status()) 54 mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), 55 ALIGN(smt->size, MMU_SECTION_SIZE), 56 DCACHE_OFF); 57 58#endif 59 60 return 0; 61} 62 63/** 64 * Write SCMI message @msg into a SMT shared buffer @smt. 65 * Return 0 on success and with a negative errno in case of error. 66 */ 67int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt, 68 struct scmi_msg *msg) 69{ 70 struct scmi_smt_header *hdr = (void *)smt->buf; 71 72 if ((!msg->in_msg && msg->in_msg_sz) || 73 (!msg->out_msg && msg->out_msg_sz)) 74 return -EINVAL; 75 76 if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) { 77 dev_dbg(dev, "Channel busy\n"); 78 return -EBUSY; 79 } 80 81 if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) || 82 smt->size < (sizeof(*hdr) + msg->out_msg_sz)) { 83 dev_dbg(dev, "Buffer too small\n"); 84 return -ETOOSMALL; 85 } 86 87 /* Load message in shared memory */ 88 hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 89 hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header); 90 hdr->msg_header = SMT_HEADER_TOKEN(0) | 91 SMT_HEADER_MESSAGE_TYPE(0) | 92 SMT_HEADER_PROTOCOL_ID(msg->protocol_id) | 93 SMT_HEADER_MESSAGE_ID(msg->message_id); 94 95 memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz); 96 97 return 0; 98} 99 100/** 101 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg. 102 * Return 0 on success and with a negative errno in case of error. 103 */ 104int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt, 105 struct scmi_msg *msg) 106{ 107 struct scmi_smt_header *hdr = (void *)smt->buf; 108 109 if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) { 110 dev_err(dev, "Channel unexpectedly busy\n"); 111 return -EBUSY; 112 } 113 114 if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) { 115 dev_err(dev, "Channel error reported, reset channel\n"); 116 return -ECOMM; 117 } 118 119 if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) { 120 dev_err(dev, "Buffer to small\n"); 121 return -ETOOSMALL; 122 } 123 124 /* Get the data */ 125 msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header); 126 memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz); 127 128 return 0; 129} 130 131/** 132 * Clear SMT flags in shared buffer to allow further message exchange 133 */ 134void scmi_clear_smt_channel(struct scmi_smt *smt) 135{ 136 struct scmi_smt_header *hdr = (void *)smt->buf; 137 138 hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; 139} 140 141/** 142 * Write SCMI message @msg into a SMT_MSG shared buffer @smt. 143 * Return 0 on success and with a negative errno in case of error. 144 */ 145int scmi_msg_to_smt_msg(struct udevice *dev, struct scmi_smt *smt, 146 struct scmi_msg *msg, size_t *buf_size) 147{ 148 struct scmi_smt_msg_header *hdr = (void *)smt->buf; 149 150 if ((!msg->in_msg && msg->in_msg_sz) || 151 (!msg->out_msg && msg->out_msg_sz)) 152 return -EINVAL; 153 154 if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) || 155 smt->size < (sizeof(*hdr) + msg->out_msg_sz)) { 156 dev_dbg(dev, "Buffer too small\n"); 157 return -ETOOSMALL; 158 } 159 160 *buf_size = msg->in_msg_sz + sizeof(hdr->msg_header); 161 162 hdr->msg_header = SMT_HEADER_TOKEN(0) | 163 SMT_HEADER_MESSAGE_TYPE(0) | 164 SMT_HEADER_PROTOCOL_ID(msg->protocol_id) | 165 SMT_HEADER_MESSAGE_ID(msg->message_id); 166 167 memcpy(hdr->msg_payload, msg->in_msg, msg->in_msg_sz); 168 169 return 0; 170} 171 172/** 173 * Read SCMI message from a SMT shared buffer @smt and copy it into @msg. 174 * Return 0 on success and with a negative errno in case of error. 175 */ 176int scmi_msg_from_smt_msg(struct udevice *dev, struct scmi_smt *smt, 177 struct scmi_msg *msg, size_t buf_size) 178{ 179 struct scmi_smt_msg_header *hdr = (void *)smt->buf; 180 181 if (buf_size > msg->out_msg_sz + sizeof(hdr->msg_header)) { 182 dev_err(dev, "Buffer to small\n"); 183 return -ETOOSMALL; 184 } 185 186 msg->out_msg_sz = buf_size - sizeof(hdr->msg_header); 187 memcpy(msg->out_msg, hdr->msg_payload, msg->out_msg_sz); 188 189 return 0; 190} 191