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> 14f424d1cbSBjorn Andersson #include <linux/of_irq.h> 15b4f8e52bSBjorn Andersson #include <linux/platform_device.h> 16b4f8e52bSBjorn Andersson #include <linux/regmap.h> 17b4f8e52bSBjorn Andersson #include <linux/rpmsg.h> 18b4f8e52bSBjorn Andersson #include <linux/slab.h> 19b4f8e52bSBjorn Andersson #include <linux/workqueue.h> 20b4f8e52bSBjorn Andersson #include <linux/mailbox_client.h> 21b4f8e52bSBjorn Andersson 22b4f8e52bSBjorn Andersson #include "rpmsg_internal.h" 23835764ddSBjorn Andersson #include "qcom_glink_native.h" 24b4f8e52bSBjorn Andersson 25b4f8e52bSBjorn Andersson #define RPM_TOC_SIZE 256 26b4f8e52bSBjorn Andersson #define RPM_TOC_MAGIC 0x67727430 /* grt0 */ 27b4f8e52bSBjorn Andersson #define RPM_TOC_MAX_ENTRIES ((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \ 28b4f8e52bSBjorn Andersson sizeof(struct rpm_toc_entry)) 29b4f8e52bSBjorn Andersson 30b4f8e52bSBjorn Andersson #define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ 31b4f8e52bSBjorn Andersson #define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ 32b4f8e52bSBjorn Andersson 33e45c5dc2SBjorn Andersson #define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) 34e45c5dc2SBjorn Andersson 35b4f8e52bSBjorn Andersson struct rpm_toc_entry { 36b4f8e52bSBjorn Andersson __le32 id; 37b4f8e52bSBjorn Andersson __le32 offset; 38b4f8e52bSBjorn Andersson __le32 size; 39b4f8e52bSBjorn Andersson } __packed; 40b4f8e52bSBjorn Andersson 41b4f8e52bSBjorn Andersson struct rpm_toc { 42b4f8e52bSBjorn Andersson __le32 magic; 43b4f8e52bSBjorn Andersson __le32 count; 44b4f8e52bSBjorn Andersson 45b4f8e52bSBjorn Andersson struct rpm_toc_entry entries[]; 46b4f8e52bSBjorn Andersson } __packed; 47b4f8e52bSBjorn Andersson 48b4f8e52bSBjorn Andersson struct glink_rpm_pipe { 49e45c5dc2SBjorn Andersson struct qcom_glink_pipe native; 50e45c5dc2SBjorn Andersson 51b4f8e52bSBjorn Andersson void __iomem *tail; 52b4f8e52bSBjorn Andersson void __iomem *head; 53b4f8e52bSBjorn Andersson 54b4f8e52bSBjorn Andersson void __iomem *fifo; 55b4f8e52bSBjorn Andersson }; 56b4f8e52bSBjorn Andersson 57178c3af4SBjorn Andersson struct glink_rpm { 58178c3af4SBjorn Andersson struct qcom_glink *glink; 59178c3af4SBjorn Andersson 60f424d1cbSBjorn Andersson int irq; 61f424d1cbSBjorn Andersson 62f424d1cbSBjorn Andersson struct mbox_client mbox_client; 63f424d1cbSBjorn Andersson struct mbox_chan *mbox_chan; 64f424d1cbSBjorn Andersson 65178c3af4SBjorn Andersson struct glink_rpm_pipe rx_pipe; 66178c3af4SBjorn Andersson struct glink_rpm_pipe tx_pipe; 67178c3af4SBjorn Andersson }; 68178c3af4SBjorn Andersson 69e45c5dc2SBjorn Andersson static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) 70b4f8e52bSBjorn Andersson { 71e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 72b4f8e52bSBjorn Andersson unsigned int head; 73b4f8e52bSBjorn Andersson unsigned int tail; 74b4f8e52bSBjorn Andersson 75b4f8e52bSBjorn Andersson head = readl(pipe->head); 76b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 77b4f8e52bSBjorn Andersson 78b4f8e52bSBjorn Andersson if (head < tail) 79e45c5dc2SBjorn Andersson return pipe->native.length - tail + head; 80b4f8e52bSBjorn Andersson else 81b4f8e52bSBjorn Andersson return head - tail; 82b4f8e52bSBjorn Andersson } 83b4f8e52bSBjorn Andersson 84*a8f500c6SBjorn Andersson static void glink_rpm_rx_peek(struct qcom_glink_pipe *glink_pipe, 85b88eee97SBjorn Andersson void *data, unsigned int offset, size_t count) 86b4f8e52bSBjorn Andersson { 87e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 88b4f8e52bSBjorn Andersson unsigned int tail; 89b4f8e52bSBjorn Andersson size_t len; 90b4f8e52bSBjorn Andersson 91b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 92b88eee97SBjorn Andersson tail += offset; 93b88eee97SBjorn Andersson if (tail >= pipe->native.length) 94b88eee97SBjorn Andersson tail -= pipe->native.length; 95b4f8e52bSBjorn Andersson 96e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - tail); 97b4f8e52bSBjorn Andersson if (len) { 98b4f8e52bSBjorn Andersson __ioread32_copy(data, pipe->fifo + tail, 99b4f8e52bSBjorn Andersson len / sizeof(u32)); 100b4f8e52bSBjorn Andersson } 101b4f8e52bSBjorn Andersson 102b4f8e52bSBjorn Andersson if (len != count) { 103b4f8e52bSBjorn Andersson __ioread32_copy(data + len, pipe->fifo, 104b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 105b4f8e52bSBjorn Andersson } 106b4f8e52bSBjorn Andersson } 107b4f8e52bSBjorn Andersson 108e45c5dc2SBjorn Andersson static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, 109b4f8e52bSBjorn Andersson size_t count) 110b4f8e52bSBjorn Andersson { 111e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 112b4f8e52bSBjorn Andersson unsigned int tail; 113b4f8e52bSBjorn Andersson 114b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 115b4f8e52bSBjorn Andersson 116b4f8e52bSBjorn Andersson tail += count; 117e45c5dc2SBjorn Andersson if (tail >= pipe->native.length) 118e45c5dc2SBjorn Andersson tail -= pipe->native.length; 119b4f8e52bSBjorn Andersson 120b4f8e52bSBjorn Andersson writel(tail, pipe->tail); 121b4f8e52bSBjorn Andersson } 122b4f8e52bSBjorn Andersson 123e45c5dc2SBjorn Andersson static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) 124e45c5dc2SBjorn Andersson { 125e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 126b4f8e52bSBjorn Andersson unsigned int head; 127b4f8e52bSBjorn Andersson unsigned int tail; 128b4f8e52bSBjorn Andersson 129b4f8e52bSBjorn Andersson head = readl(pipe->head); 130b4f8e52bSBjorn Andersson tail = readl(pipe->tail); 131b4f8e52bSBjorn Andersson 132b4f8e52bSBjorn Andersson if (tail <= head) 133e45c5dc2SBjorn Andersson return pipe->native.length - head + tail; 134b4f8e52bSBjorn Andersson else 135b4f8e52bSBjorn Andersson return tail - head; 136b4f8e52bSBjorn Andersson } 137b4f8e52bSBjorn Andersson 138e45c5dc2SBjorn Andersson static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, 139b4f8e52bSBjorn Andersson unsigned int head, 140b4f8e52bSBjorn Andersson const void *data, size_t count) 141b4f8e52bSBjorn Andersson { 142b4f8e52bSBjorn Andersson size_t len; 143b4f8e52bSBjorn Andersson 144e45c5dc2SBjorn Andersson len = min_t(size_t, count, pipe->native.length - head); 145b4f8e52bSBjorn Andersson if (len) { 146b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo + head, data, 147b4f8e52bSBjorn Andersson len / sizeof(u32)); 148b4f8e52bSBjorn Andersson } 149b4f8e52bSBjorn Andersson 150b4f8e52bSBjorn Andersson if (len != count) { 151b4f8e52bSBjorn Andersson __iowrite32_copy(pipe->fifo, data + len, 152b4f8e52bSBjorn Andersson (count - len) / sizeof(u32)); 153b4f8e52bSBjorn Andersson } 154b4f8e52bSBjorn Andersson 155b4f8e52bSBjorn Andersson head += count; 156e45c5dc2SBjorn Andersson if (head >= pipe->native.length) 157e45c5dc2SBjorn Andersson head -= pipe->native.length; 158b4f8e52bSBjorn Andersson 159b4f8e52bSBjorn Andersson return head; 160b4f8e52bSBjorn Andersson } 161b4f8e52bSBjorn Andersson 162e45c5dc2SBjorn Andersson static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, 163e45c5dc2SBjorn Andersson const void *hdr, size_t hlen, 164e45c5dc2SBjorn Andersson const void *data, size_t dlen) 165e45c5dc2SBjorn Andersson { 166e45c5dc2SBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 1677339859dSBjorn Andersson size_t tlen = hlen + dlen; 1687339859dSBjorn Andersson size_t aligned_dlen; 169e45c5dc2SBjorn Andersson unsigned int head; 1707339859dSBjorn Andersson char padding[8] = {0}; 1717339859dSBjorn Andersson size_t pad; 1727339859dSBjorn Andersson 1737339859dSBjorn Andersson /* Header length comes from glink native and is always 4 byte aligned */ 1747339859dSBjorn Andersson if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n")) 1757339859dSBjorn Andersson return; 1767339859dSBjorn Andersson 1777339859dSBjorn Andersson /* 1787339859dSBjorn Andersson * Move the unaligned tail of the message to the padding chunk, to 1797339859dSBjorn Andersson * ensure word aligned accesses 1807339859dSBjorn Andersson */ 1817339859dSBjorn Andersson aligned_dlen = ALIGN_DOWN(dlen, 4); 1827339859dSBjorn Andersson if (aligned_dlen != dlen) 1837339859dSBjorn Andersson memcpy(padding, data + aligned_dlen, dlen - aligned_dlen); 184e45c5dc2SBjorn Andersson 185e45c5dc2SBjorn Andersson head = readl(pipe->head); 186e45c5dc2SBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); 1877339859dSBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen); 1887339859dSBjorn Andersson 1897339859dSBjorn Andersson pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4); 1907339859dSBjorn Andersson if (pad) 1917339859dSBjorn Andersson head = glink_rpm_tx_write_one(pipe, head, padding, pad); 192e45c5dc2SBjorn Andersson writel(head, pipe->head); 193e45c5dc2SBjorn Andersson } 194e45c5dc2SBjorn Andersson 195f424d1cbSBjorn Andersson static void glink_rpm_tx_kick(struct qcom_glink_pipe *glink_pipe) 196f424d1cbSBjorn Andersson { 197f424d1cbSBjorn Andersson struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 198f424d1cbSBjorn Andersson struct glink_rpm *rpm = container_of(pipe, struct glink_rpm, tx_pipe); 199f424d1cbSBjorn Andersson 200f424d1cbSBjorn Andersson mbox_send_message(rpm->mbox_chan, NULL); 201f424d1cbSBjorn Andersson mbox_client_txdone(rpm->mbox_chan, 0); 202f424d1cbSBjorn Andersson } 203f424d1cbSBjorn Andersson 204f424d1cbSBjorn Andersson static irqreturn_t qcom_glink_rpm_intr(int irq, void *data) 205f424d1cbSBjorn Andersson { 206f424d1cbSBjorn Andersson struct glink_rpm *rpm = data; 207f424d1cbSBjorn Andersson 208f424d1cbSBjorn Andersson qcom_glink_native_rx(rpm->glink); 209f424d1cbSBjorn Andersson 210f424d1cbSBjorn Andersson return IRQ_HANDLED; 211f424d1cbSBjorn Andersson } 212f424d1cbSBjorn Andersson 213b4f8e52bSBjorn Andersson static int glink_rpm_parse_toc(struct device *dev, 214b4f8e52bSBjorn Andersson void __iomem *msg_ram, 215b4f8e52bSBjorn Andersson size_t msg_ram_size, 216b4f8e52bSBjorn Andersson struct glink_rpm_pipe *rx, 217b4f8e52bSBjorn Andersson struct glink_rpm_pipe *tx) 218b4f8e52bSBjorn Andersson { 219b4f8e52bSBjorn Andersson struct rpm_toc *toc; 220b4f8e52bSBjorn Andersson int num_entries; 221b4f8e52bSBjorn Andersson unsigned int id; 222b4f8e52bSBjorn Andersson size_t offset; 223b4f8e52bSBjorn Andersson size_t size; 224b4f8e52bSBjorn Andersson void *buf; 225b4f8e52bSBjorn Andersson int i; 226b4f8e52bSBjorn Andersson 227b4f8e52bSBjorn Andersson buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL); 228b4f8e52bSBjorn Andersson if (!buf) 229b4f8e52bSBjorn Andersson return -ENOMEM; 230b4f8e52bSBjorn Andersson 231b4f8e52bSBjorn Andersson __ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE, 232b4f8e52bSBjorn Andersson RPM_TOC_SIZE / sizeof(u32)); 233b4f8e52bSBjorn Andersson 234b4f8e52bSBjorn Andersson toc = buf; 235b4f8e52bSBjorn Andersson 236b4f8e52bSBjorn Andersson if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) { 237b4f8e52bSBjorn Andersson dev_err(dev, "RPM TOC has invalid magic\n"); 238b4f8e52bSBjorn Andersson goto err_inval; 239b4f8e52bSBjorn Andersson } 240b4f8e52bSBjorn Andersson 241b4f8e52bSBjorn Andersson num_entries = le32_to_cpu(toc->count); 242b4f8e52bSBjorn Andersson if (num_entries > RPM_TOC_MAX_ENTRIES) { 243b4f8e52bSBjorn Andersson dev_err(dev, "Invalid number of toc entries\n"); 244b4f8e52bSBjorn Andersson goto err_inval; 245b4f8e52bSBjorn Andersson } 246b4f8e52bSBjorn Andersson 247b4f8e52bSBjorn Andersson for (i = 0; i < num_entries; i++) { 248b4f8e52bSBjorn Andersson id = le32_to_cpu(toc->entries[i].id); 249b4f8e52bSBjorn Andersson offset = le32_to_cpu(toc->entries[i].offset); 250b4f8e52bSBjorn Andersson size = le32_to_cpu(toc->entries[i].size); 251b4f8e52bSBjorn Andersson 252b4f8e52bSBjorn Andersson if (offset > msg_ram_size || offset + size > msg_ram_size) { 253b4f8e52bSBjorn Andersson dev_err(dev, "TOC entry with invalid size\n"); 254b4f8e52bSBjorn Andersson continue; 255b4f8e52bSBjorn Andersson } 256b4f8e52bSBjorn Andersson 257b4f8e52bSBjorn Andersson switch (id) { 258b4f8e52bSBjorn Andersson case RPM_RX_FIFO_ID: 259e45c5dc2SBjorn Andersson rx->native.length = size; 260b4f8e52bSBjorn Andersson 261b4f8e52bSBjorn Andersson rx->tail = msg_ram + offset; 262b4f8e52bSBjorn Andersson rx->head = msg_ram + offset + sizeof(u32); 263b4f8e52bSBjorn Andersson rx->fifo = msg_ram + offset + 2 * sizeof(u32); 264b4f8e52bSBjorn Andersson break; 265b4f8e52bSBjorn Andersson case RPM_TX_FIFO_ID: 266e45c5dc2SBjorn Andersson tx->native.length = size; 267b4f8e52bSBjorn Andersson 268b4f8e52bSBjorn Andersson tx->tail = msg_ram + offset; 269b4f8e52bSBjorn Andersson tx->head = msg_ram + offset + sizeof(u32); 270b4f8e52bSBjorn Andersson tx->fifo = msg_ram + offset + 2 * sizeof(u32); 271b4f8e52bSBjorn Andersson break; 272b4f8e52bSBjorn Andersson } 273b4f8e52bSBjorn Andersson } 274b4f8e52bSBjorn Andersson 275b4f8e52bSBjorn Andersson if (!rx->fifo || !tx->fifo) { 276b4f8e52bSBjorn Andersson dev_err(dev, "Unable to find rx and tx descriptors\n"); 277b4f8e52bSBjorn Andersson goto err_inval; 278b4f8e52bSBjorn Andersson } 279b4f8e52bSBjorn Andersson 280b4f8e52bSBjorn Andersson kfree(buf); 281b4f8e52bSBjorn Andersson return 0; 282b4f8e52bSBjorn Andersson 283b4f8e52bSBjorn Andersson err_inval: 284b4f8e52bSBjorn Andersson kfree(buf); 285b4f8e52bSBjorn Andersson return -EINVAL; 286b4f8e52bSBjorn Andersson } 287b4f8e52bSBjorn Andersson 2886799c434SBjorn Andersson static int glink_rpm_probe(struct platform_device *pdev) 2896799c434SBjorn Andersson { 2906799c434SBjorn Andersson struct qcom_glink *glink; 291178c3af4SBjorn Andersson struct glink_rpm *rpm; 2926799c434SBjorn Andersson struct device_node *np; 2936799c434SBjorn Andersson void __iomem *msg_ram; 2946799c434SBjorn Andersson size_t msg_ram_size; 2956799c434SBjorn Andersson struct device *dev = &pdev->dev; 2966799c434SBjorn Andersson struct resource r; 2976799c434SBjorn Andersson int ret; 2986799c434SBjorn Andersson 299178c3af4SBjorn Andersson rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); 300178c3af4SBjorn Andersson if (!rpm) 3016799c434SBjorn Andersson return -ENOMEM; 3026799c434SBjorn Andersson 303b4f8e52bSBjorn Andersson np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); 304b4f8e52bSBjorn Andersson ret = of_address_to_resource(np, 0, &r); 305b4f8e52bSBjorn Andersson of_node_put(np); 306b4f8e52bSBjorn Andersson if (ret) 307b4f8e52bSBjorn Andersson return ret; 308b4f8e52bSBjorn Andersson 309b4f8e52bSBjorn Andersson msg_ram = devm_ioremap(dev, r.start, resource_size(&r)); 310b4f8e52bSBjorn Andersson msg_ram_size = resource_size(&r); 311b4f8e52bSBjorn Andersson if (!msg_ram) 312b4f8e52bSBjorn Andersson return -ENOMEM; 313b4f8e52bSBjorn Andersson 314b4f8e52bSBjorn Andersson ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, 315178c3af4SBjorn Andersson &rpm->rx_pipe, &rpm->tx_pipe); 316b4f8e52bSBjorn Andersson if (ret) 317b4f8e52bSBjorn Andersson return ret; 318b4f8e52bSBjorn Andersson 319f424d1cbSBjorn Andersson rpm->irq = of_irq_get(dev->of_node, 0); 320f424d1cbSBjorn Andersson ret = devm_request_irq(dev, rpm->irq, qcom_glink_rpm_intr, 321f424d1cbSBjorn Andersson IRQF_NO_SUSPEND | IRQF_NO_AUTOEN, 322f424d1cbSBjorn Andersson "glink-rpm", rpm); 323f424d1cbSBjorn Andersson if (ret) { 324f424d1cbSBjorn Andersson dev_err(dev, "failed to request IRQ\n"); 325f424d1cbSBjorn Andersson return ret; 326f424d1cbSBjorn Andersson } 327f424d1cbSBjorn Andersson 328f424d1cbSBjorn Andersson rpm->mbox_client.dev = dev; 329f424d1cbSBjorn Andersson rpm->mbox_client.knows_txdone = true; 330f424d1cbSBjorn Andersson rpm->mbox_chan = mbox_request_channel(&rpm->mbox_client, 0); 331f424d1cbSBjorn Andersson if (IS_ERR(rpm->mbox_chan)) 332f424d1cbSBjorn Andersson return dev_err_probe(dev, PTR_ERR(rpm->mbox_chan), "failed to acquire IPC channel\n"); 333f424d1cbSBjorn Andersson 334e45c5dc2SBjorn Andersson /* Pipe specific accessors */ 335178c3af4SBjorn Andersson rpm->rx_pipe.native.avail = glink_rpm_rx_avail; 336*a8f500c6SBjorn Andersson rpm->rx_pipe.native.peek = glink_rpm_rx_peek; 337178c3af4SBjorn Andersson rpm->rx_pipe.native.advance = glink_rpm_rx_advance; 338178c3af4SBjorn Andersson rpm->tx_pipe.native.avail = glink_rpm_tx_avail; 339178c3af4SBjorn Andersson rpm->tx_pipe.native.write = glink_rpm_tx_write; 340f424d1cbSBjorn Andersson rpm->tx_pipe.native.kick = glink_rpm_tx_kick; 341e45c5dc2SBjorn Andersson 342178c3af4SBjorn Andersson writel(0, rpm->tx_pipe.head); 343178c3af4SBjorn Andersson writel(0, rpm->rx_pipe.tail); 344b4f8e52bSBjorn Andersson 345178c3af4SBjorn Andersson glink = qcom_glink_native_probe(dev, 346d31ad615SSricharan R 0, 347178c3af4SBjorn Andersson &rpm->rx_pipe.native, 348178c3af4SBjorn Andersson &rpm->tx_pipe.native, 349933b45daSSricharan R true); 350f424d1cbSBjorn Andersson if (IS_ERR(glink)) { 351f424d1cbSBjorn Andersson mbox_free_channel(rpm->mbox_chan); 3526799c434SBjorn Andersson return PTR_ERR(glink); 353f424d1cbSBjorn Andersson } 354b4f8e52bSBjorn Andersson 355178c3af4SBjorn Andersson rpm->glink = glink; 356178c3af4SBjorn Andersson 357178c3af4SBjorn Andersson platform_set_drvdata(pdev, rpm); 358b4f8e52bSBjorn Andersson 359f424d1cbSBjorn Andersson enable_irq(rpm->irq); 360f424d1cbSBjorn Andersson 361b4f8e52bSBjorn Andersson return 0; 362b4f8e52bSBjorn Andersson } 363b4f8e52bSBjorn Andersson 364b4f8e52bSBjorn Andersson static int glink_rpm_remove(struct platform_device *pdev) 365b4f8e52bSBjorn Andersson { 366178c3af4SBjorn Andersson struct glink_rpm *rpm = platform_get_drvdata(pdev); 367178c3af4SBjorn Andersson struct qcom_glink *glink = rpm->glink; 368b4f8e52bSBjorn Andersson 369f424d1cbSBjorn Andersson disable_irq(rpm->irq); 370f424d1cbSBjorn Andersson 371835764ddSBjorn Andersson qcom_glink_native_remove(glink); 372b4f8e52bSBjorn Andersson 373f424d1cbSBjorn Andersson mbox_free_channel(rpm->mbox_chan); 374f424d1cbSBjorn Andersson 375b4f8e52bSBjorn Andersson return 0; 376b4f8e52bSBjorn Andersson } 377b4f8e52bSBjorn Andersson 378b4f8e52bSBjorn Andersson static const struct of_device_id glink_rpm_of_match[] = { 379b4f8e52bSBjorn Andersson { .compatible = "qcom,glink-rpm" }, 380b4f8e52bSBjorn Andersson {} 381b4f8e52bSBjorn Andersson }; 382b4f8e52bSBjorn Andersson MODULE_DEVICE_TABLE(of, glink_rpm_of_match); 383b4f8e52bSBjorn Andersson 384b4f8e52bSBjorn Andersson static struct platform_driver glink_rpm_driver = { 385b4f8e52bSBjorn Andersson .probe = glink_rpm_probe, 386b4f8e52bSBjorn Andersson .remove = glink_rpm_remove, 387b4f8e52bSBjorn Andersson .driver = { 388b4f8e52bSBjorn Andersson .name = "qcom_glink_rpm", 389b4f8e52bSBjorn Andersson .of_match_table = glink_rpm_of_match, 390b4f8e52bSBjorn Andersson }, 391b4f8e52bSBjorn Andersson }; 392b4f8e52bSBjorn Andersson 393b4f8e52bSBjorn Andersson static int __init glink_rpm_init(void) 394b4f8e52bSBjorn Andersson { 395b4f8e52bSBjorn Andersson return platform_driver_register(&glink_rpm_driver); 396b4f8e52bSBjorn Andersson } 397b4f8e52bSBjorn Andersson subsys_initcall(glink_rpm_init); 398b4f8e52bSBjorn Andersson 399b4f8e52bSBjorn Andersson static void __exit glink_rpm_exit(void) 400b4f8e52bSBjorn Andersson { 401b4f8e52bSBjorn Andersson platform_driver_unregister(&glink_rpm_driver); 402b4f8e52bSBjorn Andersson } 403b4f8e52bSBjorn Andersson module_exit(glink_rpm_exit); 404b4f8e52bSBjorn Andersson 405b4f8e52bSBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 406b4f8e52bSBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK RPM driver"); 407b4f8e52bSBjorn Andersson MODULE_LICENSE("GPL v2"); 408