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