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