1b4f8e52bSBjorn Andersson /* 2b4f8e52bSBjorn Andersson * Copyright (c) 2016-2017, Linaro Ltd 3b4f8e52bSBjorn Andersson * 4b4f8e52bSBjorn Andersson * This program is free software; you can redistribute it and/or modify 5b4f8e52bSBjorn Andersson * it under the terms of the GNU General Public License version 2 and 6b4f8e52bSBjorn Andersson * only version 2 as published by the Free Software Foundation. 7b4f8e52bSBjorn Andersson * 8b4f8e52bSBjorn Andersson * This program is distributed in the hope that it will be useful, 9b4f8e52bSBjorn Andersson * but WITHOUT ANY WARRANTY; without even the implied warranty of 10b4f8e52bSBjorn Andersson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11b4f8e52bSBjorn Andersson * GNU General Public License for more details. 12b4f8e52bSBjorn Andersson */ 13b4f8e52bSBjorn Andersson 14b4f8e52bSBjorn Andersson #include <linux/idr.h> 15b4f8e52bSBjorn Andersson #include <linux/interrupt.h> 16b4f8e52bSBjorn Andersson #include <linux/io.h> 17b4f8e52bSBjorn Andersson #include <linux/list.h> 18b4f8e52bSBjorn Andersson #include <linux/mfd/syscon.h> 19b4f8e52bSBjorn Andersson #include <linux/module.h> 20b4f8e52bSBjorn Andersson #include <linux/of.h> 21b4f8e52bSBjorn Andersson #include <linux/of_address.h> 22b4f8e52bSBjorn Andersson #include <linux/platform_device.h> 23b4f8e52bSBjorn Andersson #include <linux/regmap.h> 24b4f8e52bSBjorn Andersson #include <linux/rpmsg.h> 25b4f8e52bSBjorn Andersson #include <linux/slab.h> 26b4f8e52bSBjorn Andersson #include <linux/workqueue.h> 27b4f8e52bSBjorn Andersson #include <linux/mailbox_client.h> 28b4f8e52bSBjorn Andersson 29b4f8e52bSBjorn Andersson #include "rpmsg_internal.h" 30835764ddSBjorn Andersson #include "qcom_glink_native.h" 31b4f8e52bSBjorn Andersson 32b4f8e52bSBjorn Andersson #define RPM_TOC_SIZE 256 33b4f8e52bSBjorn Andersson #define RPM_TOC_MAGIC 0x67727430 /* grt0 */ 34b4f8e52bSBjorn Andersson #define RPM_TOC_MAX_ENTRIES ((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \ 35b4f8e52bSBjorn Andersson sizeof(struct rpm_toc_entry)) 36b4f8e52bSBjorn Andersson 37b4f8e52bSBjorn Andersson #define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ 38b4f8e52bSBjorn Andersson #define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ 39b4f8e52bSBjorn Andersson 40e45c5dc2SBjorn Andersson #define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) 41e45c5dc2SBjorn Andersson 42b4f8e52bSBjorn Andersson struct rpm_toc_entry { 43b4f8e52bSBjorn Andersson __le32 id; 44b4f8e52bSBjorn Andersson __le32 offset; 45b4f8e52bSBjorn Andersson __le32 size; 46b4f8e52bSBjorn Andersson } __packed; 47b4f8e52bSBjorn Andersson 48b4f8e52bSBjorn Andersson struct rpm_toc { 49b4f8e52bSBjorn Andersson __le32 magic; 50b4f8e52bSBjorn Andersson __le32 count; 51b4f8e52bSBjorn Andersson 52b4f8e52bSBjorn Andersson struct rpm_toc_entry entries[]; 53b4f8e52bSBjorn Andersson } __packed; 54b4f8e52bSBjorn Andersson 55b4f8e52bSBjorn Andersson struct glink_rpm_pipe { 56e45c5dc2SBjorn Andersson struct qcom_glink_pipe native; 57e45c5dc2SBjorn Andersson 58b4f8e52bSBjorn Andersson void __iomem *tail; 59b4f8e52bSBjorn Andersson void __iomem *head; 60b4f8e52bSBjorn Andersson 61b4f8e52bSBjorn Andersson void __iomem *fifo; 62b4f8e52bSBjorn Andersson }; 63b4f8e52bSBjorn Andersson 64e45c5dc2SBjorn Andersson static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) 65b4f8e52bSBjorn Andersson { 66e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 67b4f8e52bSBjorn Andersson unsigned int head; 68b4f8e52bSBjorn Andersson unsigned int tail; 69b4f8e52bSBjorn Andersson 70b4f8e52bSBjorn Andersson head = readl(pipe->head); 71b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 72b4f8e52bSBjorn Andersson 73b4f8e52bSBjorn Andersson if (head < tail) 74e45c5dc2SBjorn Andersson return pipe->native.length - tail + head; 75b4f8e52bSBjorn Andersson else 76b4f8e52bSBjorn Andersson return head - tail; 77b4f8e52bSBjorn Andersson } 78b4f8e52bSBjorn Andersson 79e45c5dc2SBjorn Andersson static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, 80b4f8e52bSBjorn Andersson void *data, size_t count) 81b4f8e52bSBjorn Andersson { 82e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 83b4f8e52bSBjorn Andersson unsigned int tail; 84b4f8e52bSBjorn Andersson size_t len; 85b4f8e52bSBjorn Andersson 86b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 87b4f8e52bSBjorn Andersson 88e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - tail); 89b4f8e52bSBjorn Andersson if (len) { 90b4f8e52bSBjorn Andersson __ioread32_copy(data, pipe->fifo + tail, 91b4f8e52bSBjorn Andersson len / sizeof(u32)); 92b4f8e52bSBjorn Andersson } 93b4f8e52bSBjorn Andersson 94b4f8e52bSBjorn Andersson if (len != count) { 95b4f8e52bSBjorn Andersson __ioread32_copy(data + len, pipe->fifo, 96b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 97b4f8e52bSBjorn Andersson } 98b4f8e52bSBjorn Andersson } 99b4f8e52bSBjorn Andersson 100e45c5dc2SBjorn Andersson static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, 101b4f8e52bSBjorn Andersson size_t count) 102b4f8e52bSBjorn Andersson { 103e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 104b4f8e52bSBjorn Andersson unsigned int tail; 105b4f8e52bSBjorn Andersson 106b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 107b4f8e52bSBjorn Andersson 108b4f8e52bSBjorn Andersson tail += count; 109e45c5dc2SBjorn Andersson if (tail >= pipe->native.length) 110e45c5dc2SBjorn Andersson tail -= pipe->native.length; 111b4f8e52bSBjorn Andersson 112b4f8e52bSBjorn Andersson writel(tail, pipe->tail); 113b4f8e52bSBjorn Andersson } 114b4f8e52bSBjorn Andersson 115e45c5dc2SBjorn Andersson static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) 116e45c5dc2SBjorn Andersson { 117e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 118b4f8e52bSBjorn Andersson unsigned int head; 119b4f8e52bSBjorn Andersson unsigned int tail; 120b4f8e52bSBjorn Andersson 121b4f8e52bSBjorn Andersson head = readl(pipe->head); 122b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 123b4f8e52bSBjorn Andersson 124b4f8e52bSBjorn Andersson if (tail <= head) 125e45c5dc2SBjorn Andersson return pipe->native.length - head + tail; 126b4f8e52bSBjorn Andersson else 127b4f8e52bSBjorn Andersson return tail - head; 128b4f8e52bSBjorn Andersson } 129b4f8e52bSBjorn Andersson 130e45c5dc2SBjorn Andersson static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, 131b4f8e52bSBjorn Andersson unsigned int head, 132b4f8e52bSBjorn Andersson const void *data, size_t count) 133b4f8e52bSBjorn Andersson { 134b4f8e52bSBjorn Andersson size_t len; 135b4f8e52bSBjorn Andersson 136e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - head); 137b4f8e52bSBjorn Andersson if (len) { 138b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo + head, data, 139b4f8e52bSBjorn Andersson len / sizeof(u32)); 140b4f8e52bSBjorn Andersson } 141b4f8e52bSBjorn Andersson 142b4f8e52bSBjorn Andersson if (len != count) { 143b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo, data + len, 144b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 145b4f8e52bSBjorn Andersson } 146b4f8e52bSBjorn Andersson 147b4f8e52bSBjorn Andersson head += count; 148e45c5dc2SBjorn Andersson if (head >= pipe->native.length) 149e45c5dc2SBjorn Andersson head -= pipe->native.length; 150b4f8e52bSBjorn Andersson 151b4f8e52bSBjorn Andersson return head; 152b4f8e52bSBjorn Andersson } 153b4f8e52bSBjorn Andersson 154e45c5dc2SBjorn Andersson static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, 155e45c5dc2SBjorn Andersson const void *hdr, size_t hlen, 156e45c5dc2SBjorn Andersson const void *data, size_t dlen) 157e45c5dc2SBjorn Andersson { 158e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 159e45c5dc2SBjorn Andersson unsigned int head; 160e45c5dc2SBjorn Andersson 161e45c5dc2SBjorn Andersson head = readl(pipe->head); 162e45c5dc2SBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); 163e45c5dc2SBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, data, dlen); 164e45c5dc2SBjorn Andersson writel(head, pipe->head); 165e45c5dc2SBjorn Andersson } 166e45c5dc2SBjorn Andersson 167b4f8e52bSBjorn Andersson static int glink_rpm_parse_toc(struct device *dev, 168b4f8e52bSBjorn Andersson void __iomem *msg_ram, 169b4f8e52bSBjorn Andersson size_t msg_ram_size, 170b4f8e52bSBjorn Andersson struct glink_rpm_pipe *rx, 171b4f8e52bSBjorn Andersson struct glink_rpm_pipe *tx) 172b4f8e52bSBjorn Andersson { 173b4f8e52bSBjorn Andersson struct rpm_toc *toc; 174b4f8e52bSBjorn Andersson int num_entries; 175b4f8e52bSBjorn Andersson unsigned int id; 176b4f8e52bSBjorn Andersson size_t offset; 177b4f8e52bSBjorn Andersson size_t size; 178b4f8e52bSBjorn Andersson void *buf; 179b4f8e52bSBjorn Andersson int i; 180b4f8e52bSBjorn Andersson 181b4f8e52bSBjorn Andersson buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL); 182b4f8e52bSBjorn Andersson if (!buf) 183b4f8e52bSBjorn Andersson return -ENOMEM; 184b4f8e52bSBjorn Andersson 185b4f8e52bSBjorn Andersson __ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE, 186b4f8e52bSBjorn Andersson RPM_TOC_SIZE / sizeof(u32)); 187b4f8e52bSBjorn Andersson 188b4f8e52bSBjorn Andersson toc = buf; 189b4f8e52bSBjorn Andersson 190b4f8e52bSBjorn Andersson if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) { 191b4f8e52bSBjorn Andersson dev_err(dev, "RPM TOC has invalid magic\n"); 192b4f8e52bSBjorn Andersson goto err_inval; 193b4f8e52bSBjorn Andersson } 194b4f8e52bSBjorn Andersson 195b4f8e52bSBjorn Andersson num_entries = le32_to_cpu(toc->count); 196b4f8e52bSBjorn Andersson if (num_entries > RPM_TOC_MAX_ENTRIES) { 197b4f8e52bSBjorn Andersson dev_err(dev, "Invalid number of toc entries\n"); 198b4f8e52bSBjorn Andersson goto err_inval; 199b4f8e52bSBjorn Andersson } 200b4f8e52bSBjorn Andersson 201b4f8e52bSBjorn Andersson for (i = 0; i < num_entries; i++) { 202b4f8e52bSBjorn Andersson id = le32_to_cpu(toc->entries[i].id); 203b4f8e52bSBjorn Andersson offset = le32_to_cpu(toc->entries[i].offset); 204b4f8e52bSBjorn Andersson size = le32_to_cpu(toc->entries[i].size); 205b4f8e52bSBjorn Andersson 206b4f8e52bSBjorn Andersson if (offset > msg_ram_size || offset + size > msg_ram_size) { 207b4f8e52bSBjorn Andersson dev_err(dev, "TOC entry with invalid size\n"); 208b4f8e52bSBjorn Andersson continue; 209b4f8e52bSBjorn Andersson } 210b4f8e52bSBjorn Andersson 211b4f8e52bSBjorn Andersson switch (id) { 212b4f8e52bSBjorn Andersson case RPM_RX_FIFO_ID: 213e45c5dc2SBjorn Andersson rx->native.length = size; 214b4f8e52bSBjorn Andersson 215b4f8e52bSBjorn Andersson rx->tail = msg_ram + offset; 216b4f8e52bSBjorn Andersson rx->head = msg_ram + offset + sizeof(u32); 217b4f8e52bSBjorn Andersson rx->fifo = msg_ram + offset + 2 * sizeof(u32); 218b4f8e52bSBjorn Andersson break; 219b4f8e52bSBjorn Andersson case RPM_TX_FIFO_ID: 220e45c5dc2SBjorn Andersson tx->native.length = size; 221b4f8e52bSBjorn Andersson 222b4f8e52bSBjorn Andersson tx->tail = msg_ram + offset; 223b4f8e52bSBjorn Andersson tx->head = msg_ram + offset + sizeof(u32); 224b4f8e52bSBjorn Andersson tx->fifo = msg_ram + offset + 2 * sizeof(u32); 225b4f8e52bSBjorn Andersson break; 226b4f8e52bSBjorn Andersson } 227b4f8e52bSBjorn Andersson } 228b4f8e52bSBjorn Andersson 229b4f8e52bSBjorn Andersson if (!rx->fifo || !tx->fifo) { 230b4f8e52bSBjorn Andersson dev_err(dev, "Unable to find rx and tx descriptors\n"); 231b4f8e52bSBjorn Andersson goto err_inval; 232b4f8e52bSBjorn Andersson } 233b4f8e52bSBjorn Andersson 234b4f8e52bSBjorn Andersson kfree(buf); 235b4f8e52bSBjorn Andersson return 0; 236b4f8e52bSBjorn Andersson 237b4f8e52bSBjorn Andersson err_inval: 238b4f8e52bSBjorn Andersson kfree(buf); 239b4f8e52bSBjorn Andersson return -EINVAL; 240b4f8e52bSBjorn Andersson } 241b4f8e52bSBjorn Andersson 2426799c434SBjorn Andersson static int glink_rpm_probe(struct platform_device *pdev) 2436799c434SBjorn Andersson { 2446799c434SBjorn Andersson struct qcom_glink *glink; 2456799c434SBjorn Andersson struct glink_rpm_pipe *rx_pipe; 2466799c434SBjorn Andersson struct glink_rpm_pipe *tx_pipe; 2476799c434SBjorn Andersson struct device_node *np; 2486799c434SBjorn Andersson void __iomem *msg_ram; 2496799c434SBjorn Andersson size_t msg_ram_size; 2506799c434SBjorn Andersson struct device *dev = &pdev->dev; 2516799c434SBjorn Andersson struct resource r; 2526799c434SBjorn Andersson int ret; 2536799c434SBjorn Andersson 2546799c434SBjorn Andersson rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); 2556799c434SBjorn Andersson tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); 2566799c434SBjorn Andersson if (!rx_pipe || !tx_pipe) 2576799c434SBjorn Andersson return -ENOMEM; 2586799c434SBjorn Andersson 259b4f8e52bSBjorn Andersson np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); 260b4f8e52bSBjorn Andersson ret = of_address_to_resource(np, 0, &r); 261b4f8e52bSBjorn Andersson of_node_put(np); 262b4f8e52bSBjorn Andersson if (ret) 263b4f8e52bSBjorn Andersson return ret; 264b4f8e52bSBjorn Andersson 265b4f8e52bSBjorn Andersson msg_ram = devm_ioremap(dev, r.start, resource_size(&r)); 266b4f8e52bSBjorn Andersson msg_ram_size = resource_size(&r); 267b4f8e52bSBjorn Andersson if (!msg_ram) 268b4f8e52bSBjorn Andersson return -ENOMEM; 269b4f8e52bSBjorn Andersson 270b4f8e52bSBjorn Andersson ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, 271e45c5dc2SBjorn Andersson rx_pipe, tx_pipe); 272b4f8e52bSBjorn Andersson if (ret) 273b4f8e52bSBjorn Andersson return ret; 274b4f8e52bSBjorn Andersson 275e45c5dc2SBjorn Andersson /* Pipe specific accessors */ 276e45c5dc2SBjorn Andersson rx_pipe->native.avail = glink_rpm_rx_avail; 277e45c5dc2SBjorn Andersson rx_pipe->native.peak = glink_rpm_rx_peak; 278e45c5dc2SBjorn Andersson rx_pipe->native.advance = glink_rpm_rx_advance; 279e45c5dc2SBjorn Andersson tx_pipe->native.avail = glink_rpm_tx_avail; 280e45c5dc2SBjorn Andersson tx_pipe->native.write = glink_rpm_tx_write; 281e45c5dc2SBjorn Andersson 282e45c5dc2SBjorn Andersson writel(0, tx_pipe->head); 283e45c5dc2SBjorn Andersson writel(0, rx_pipe->tail); 284b4f8e52bSBjorn Andersson 2856799c434SBjorn Andersson glink = qcom_glink_native_probe(&pdev->dev, &rx_pipe->native, 2866799c434SBjorn Andersson &tx_pipe->native); 2876799c434SBjorn Andersson if (IS_ERR(glink)) 2886799c434SBjorn Andersson return PTR_ERR(glink); 289b4f8e52bSBjorn Andersson 290b4f8e52bSBjorn Andersson platform_set_drvdata(pdev, glink); 291b4f8e52bSBjorn Andersson 292b4f8e52bSBjorn Andersson return 0; 293b4f8e52bSBjorn Andersson } 294b4f8e52bSBjorn Andersson 295b4f8e52bSBjorn Andersson static int glink_rpm_remove(struct platform_device *pdev) 296b4f8e52bSBjorn Andersson { 297d7101febSBjorn Andersson struct qcom_glink *glink = platform_get_drvdata(pdev); 298b4f8e52bSBjorn Andersson 299835764ddSBjorn Andersson qcom_glink_native_remove(glink); 300b4f8e52bSBjorn Andersson 301b4f8e52bSBjorn Andersson return 0; 302b4f8e52bSBjorn Andersson } 303b4f8e52bSBjorn Andersson 304b4f8e52bSBjorn Andersson static const struct of_device_id glink_rpm_of_match[] = { 305b4f8e52bSBjorn Andersson { .compatible = "qcom,glink-rpm" }, 306b4f8e52bSBjorn Andersson {} 307b4f8e52bSBjorn Andersson }; 308b4f8e52bSBjorn Andersson MODULE_DEVICE_TABLE(of, glink_rpm_of_match); 309b4f8e52bSBjorn Andersson 310b4f8e52bSBjorn Andersson static struct platform_driver glink_rpm_driver = { 311b4f8e52bSBjorn Andersson .probe = glink_rpm_probe, 312b4f8e52bSBjorn Andersson .remove = glink_rpm_remove, 313b4f8e52bSBjorn Andersson .driver = { 314b4f8e52bSBjorn Andersson .name = "qcom_glink_rpm", 315b4f8e52bSBjorn Andersson .of_match_table = glink_rpm_of_match, 316b4f8e52bSBjorn Andersson }, 317b4f8e52bSBjorn Andersson }; 318b4f8e52bSBjorn Andersson 319b4f8e52bSBjorn Andersson static int __init glink_rpm_init(void) 320b4f8e52bSBjorn Andersson { 321b4f8e52bSBjorn Andersson return platform_driver_register(&glink_rpm_driver); 322b4f8e52bSBjorn Andersson } 323b4f8e52bSBjorn Andersson subsys_initcall(glink_rpm_init); 324b4f8e52bSBjorn Andersson 325b4f8e52bSBjorn Andersson static void __exit glink_rpm_exit(void) 326b4f8e52bSBjorn Andersson { 327b4f8e52bSBjorn Andersson platform_driver_unregister(&glink_rpm_driver); 328b4f8e52bSBjorn Andersson } 329b4f8e52bSBjorn Andersson module_exit(glink_rpm_exit); 330b4f8e52bSBjorn Andersson 331b4f8e52bSBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 332b4f8e52bSBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK RPM driver"); 333b4f8e52bSBjorn Andersson MODULE_LICENSE("GPL v2"); 334