qcom_glink_rpm.c (8073B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2016-2017, Linaro Ltd 4 */ 5 6#include <linux/idr.h> 7#include <linux/interrupt.h> 8#include <linux/io.h> 9#include <linux/list.h> 10#include <linux/mfd/syscon.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/of_address.h> 14#include <linux/platform_device.h> 15#include <linux/regmap.h> 16#include <linux/rpmsg.h> 17#include <linux/slab.h> 18#include <linux/workqueue.h> 19#include <linux/mailbox_client.h> 20 21#include "rpmsg_internal.h" 22#include "qcom_glink_native.h" 23 24#define RPM_TOC_SIZE 256 25#define RPM_TOC_MAGIC 0x67727430 /* grt0 */ 26#define RPM_TOC_MAX_ENTRIES ((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \ 27 sizeof(struct rpm_toc_entry)) 28 29#define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ 30#define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ 31 32#define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) 33 34struct rpm_toc_entry { 35 __le32 id; 36 __le32 offset; 37 __le32 size; 38} __packed; 39 40struct rpm_toc { 41 __le32 magic; 42 __le32 count; 43 44 struct rpm_toc_entry entries[]; 45} __packed; 46 47struct glink_rpm_pipe { 48 struct qcom_glink_pipe native; 49 50 void __iomem *tail; 51 void __iomem *head; 52 53 void __iomem *fifo; 54}; 55 56static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) 57{ 58 struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 59 unsigned int head; 60 unsigned int tail; 61 62 head = readl(pipe->head); 63 tail = readl(pipe->tail); 64 65 if (head < tail) 66 return pipe->native.length - tail + head; 67 else 68 return head - tail; 69} 70 71static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, 72 void *data, unsigned int offset, size_t count) 73{ 74 struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 75 unsigned int tail; 76 size_t len; 77 78 tail = readl(pipe->tail); 79 tail += offset; 80 if (tail >= pipe->native.length) 81 tail -= pipe->native.length; 82 83 len = min_t(size_t, count, pipe->native.length - tail); 84 if (len) { 85 __ioread32_copy(data, pipe->fifo + tail, 86 len / sizeof(u32)); 87 } 88 89 if (len != count) { 90 __ioread32_copy(data + len, pipe->fifo, 91 (count - len) / sizeof(u32)); 92 } 93} 94 95static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, 96 size_t count) 97{ 98 struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 99 unsigned int tail; 100 101 tail = readl(pipe->tail); 102 103 tail += count; 104 if (tail >= pipe->native.length) 105 tail -= pipe->native.length; 106 107 writel(tail, pipe->tail); 108} 109 110static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) 111{ 112 struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 113 unsigned int head; 114 unsigned int tail; 115 116 head = readl(pipe->head); 117 tail = readl(pipe->tail); 118 119 if (tail <= head) 120 return pipe->native.length - head + tail; 121 else 122 return tail - head; 123} 124 125static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, 126 unsigned int head, 127 const void *data, size_t count) 128{ 129 size_t len; 130 131 len = min_t(size_t, count, pipe->native.length - head); 132 if (len) { 133 __iowrite32_copy(pipe->fifo + head, data, 134 len / sizeof(u32)); 135 } 136 137 if (len != count) { 138 __iowrite32_copy(pipe->fifo, data + len, 139 (count - len) / sizeof(u32)); 140 } 141 142 head += count; 143 if (head >= pipe->native.length) 144 head -= pipe->native.length; 145 146 return head; 147} 148 149static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, 150 const void *hdr, size_t hlen, 151 const void *data, size_t dlen) 152{ 153 struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 154 size_t tlen = hlen + dlen; 155 size_t aligned_dlen; 156 unsigned int head; 157 char padding[8] = {0}; 158 size_t pad; 159 160 /* Header length comes from glink native and is always 4 byte aligned */ 161 if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n")) 162 return; 163 164 /* 165 * Move the unaligned tail of the message to the padding chunk, to 166 * ensure word aligned accesses 167 */ 168 aligned_dlen = ALIGN_DOWN(dlen, 4); 169 if (aligned_dlen != dlen) 170 memcpy(padding, data + aligned_dlen, dlen - aligned_dlen); 171 172 head = readl(pipe->head); 173 head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); 174 head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen); 175 176 pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4); 177 if (pad) 178 head = glink_rpm_tx_write_one(pipe, head, padding, pad); 179 writel(head, pipe->head); 180} 181 182static int glink_rpm_parse_toc(struct device *dev, 183 void __iomem *msg_ram, 184 size_t msg_ram_size, 185 struct glink_rpm_pipe *rx, 186 struct glink_rpm_pipe *tx) 187{ 188 struct rpm_toc *toc; 189 int num_entries; 190 unsigned int id; 191 size_t offset; 192 size_t size; 193 void *buf; 194 int i; 195 196 buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL); 197 if (!buf) 198 return -ENOMEM; 199 200 __ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE, 201 RPM_TOC_SIZE / sizeof(u32)); 202 203 toc = buf; 204 205 if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) { 206 dev_err(dev, "RPM TOC has invalid magic\n"); 207 goto err_inval; 208 } 209 210 num_entries = le32_to_cpu(toc->count); 211 if (num_entries > RPM_TOC_MAX_ENTRIES) { 212 dev_err(dev, "Invalid number of toc entries\n"); 213 goto err_inval; 214 } 215 216 for (i = 0; i < num_entries; i++) { 217 id = le32_to_cpu(toc->entries[i].id); 218 offset = le32_to_cpu(toc->entries[i].offset); 219 size = le32_to_cpu(toc->entries[i].size); 220 221 if (offset > msg_ram_size || offset + size > msg_ram_size) { 222 dev_err(dev, "TOC entry with invalid size\n"); 223 continue; 224 } 225 226 switch (id) { 227 case RPM_RX_FIFO_ID: 228 rx->native.length = size; 229 230 rx->tail = msg_ram + offset; 231 rx->head = msg_ram + offset + sizeof(u32); 232 rx->fifo = msg_ram + offset + 2 * sizeof(u32); 233 break; 234 case RPM_TX_FIFO_ID: 235 tx->native.length = size; 236 237 tx->tail = msg_ram + offset; 238 tx->head = msg_ram + offset + sizeof(u32); 239 tx->fifo = msg_ram + offset + 2 * sizeof(u32); 240 break; 241 } 242 } 243 244 if (!rx->fifo || !tx->fifo) { 245 dev_err(dev, "Unable to find rx and tx descriptors\n"); 246 goto err_inval; 247 } 248 249 kfree(buf); 250 return 0; 251 252err_inval: 253 kfree(buf); 254 return -EINVAL; 255} 256 257static int glink_rpm_probe(struct platform_device *pdev) 258{ 259 struct qcom_glink *glink; 260 struct glink_rpm_pipe *rx_pipe; 261 struct glink_rpm_pipe *tx_pipe; 262 struct device_node *np; 263 void __iomem *msg_ram; 264 size_t msg_ram_size; 265 struct device *dev = &pdev->dev; 266 struct resource r; 267 int ret; 268 269 rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); 270 tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); 271 if (!rx_pipe || !tx_pipe) 272 return -ENOMEM; 273 274 np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); 275 ret = of_address_to_resource(np, 0, &r); 276 of_node_put(np); 277 if (ret) 278 return ret; 279 280 msg_ram = devm_ioremap(dev, r.start, resource_size(&r)); 281 msg_ram_size = resource_size(&r); 282 if (!msg_ram) 283 return -ENOMEM; 284 285 ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, 286 rx_pipe, tx_pipe); 287 if (ret) 288 return ret; 289 290 /* Pipe specific accessors */ 291 rx_pipe->native.avail = glink_rpm_rx_avail; 292 rx_pipe->native.peak = glink_rpm_rx_peak; 293 rx_pipe->native.advance = glink_rpm_rx_advance; 294 tx_pipe->native.avail = glink_rpm_tx_avail; 295 tx_pipe->native.write = glink_rpm_tx_write; 296 297 writel(0, tx_pipe->head); 298 writel(0, rx_pipe->tail); 299 300 glink = qcom_glink_native_probe(&pdev->dev, 301 0, 302 &rx_pipe->native, 303 &tx_pipe->native, 304 true); 305 if (IS_ERR(glink)) 306 return PTR_ERR(glink); 307 308 platform_set_drvdata(pdev, glink); 309 310 return 0; 311} 312 313static int glink_rpm_remove(struct platform_device *pdev) 314{ 315 struct qcom_glink *glink = platform_get_drvdata(pdev); 316 317 qcom_glink_native_remove(glink); 318 319 return 0; 320} 321 322static const struct of_device_id glink_rpm_of_match[] = { 323 { .compatible = "qcom,glink-rpm" }, 324 {} 325}; 326MODULE_DEVICE_TABLE(of, glink_rpm_of_match); 327 328static struct platform_driver glink_rpm_driver = { 329 .probe = glink_rpm_probe, 330 .remove = glink_rpm_remove, 331 .driver = { 332 .name = "qcom_glink_rpm", 333 .of_match_table = glink_rpm_of_match, 334 }, 335}; 336 337static int __init glink_rpm_init(void) 338{ 339 return platform_driver_register(&glink_rpm_driver); 340} 341subsys_initcall(glink_rpm_init); 342 343static void __exit glink_rpm_exit(void) 344{ 345 platform_driver_unregister(&glink_rpm_driver); 346} 347module_exit(glink_rpm_exit); 348 349MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 350MODULE_DESCRIPTION("Qualcomm GLINK RPM driver"); 351MODULE_LICENSE("GPL v2");