162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016-2017, Linaro Ltd 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/idr.h> 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/list.h> 1062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of_irq.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/rpmsg.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/workqueue.h> 2062306a36Sopenharmony_ci#include <linux/mailbox_client.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "rpmsg_internal.h" 2362306a36Sopenharmony_ci#include "qcom_glink_native.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define RPM_TOC_SIZE 256 2662306a36Sopenharmony_ci#define RPM_TOC_MAGIC 0x67727430 /* grt0 */ 2762306a36Sopenharmony_ci#define RPM_TOC_MAX_ENTRIES ((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \ 2862306a36Sopenharmony_ci sizeof(struct rpm_toc_entry)) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ 3162306a36Sopenharmony_ci#define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct rpm_toc_entry { 3662306a36Sopenharmony_ci __le32 id; 3762306a36Sopenharmony_ci __le32 offset; 3862306a36Sopenharmony_ci __le32 size; 3962306a36Sopenharmony_ci} __packed; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct rpm_toc { 4262306a36Sopenharmony_ci __le32 magic; 4362306a36Sopenharmony_ci __le32 count; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci struct rpm_toc_entry entries[]; 4662306a36Sopenharmony_ci} __packed; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct glink_rpm_pipe { 4962306a36Sopenharmony_ci struct qcom_glink_pipe native; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci void __iomem *tail; 5262306a36Sopenharmony_ci void __iomem *head; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci void __iomem *fifo; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct glink_rpm { 5862306a36Sopenharmony_ci struct qcom_glink *glink; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci int irq; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci struct mbox_client mbox_client; 6362306a36Sopenharmony_ci struct mbox_chan *mbox_chan; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci struct glink_rpm_pipe rx_pipe; 6662306a36Sopenharmony_ci struct glink_rpm_pipe tx_pipe; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 7262306a36Sopenharmony_ci unsigned int head; 7362306a36Sopenharmony_ci unsigned int tail; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci head = readl(pipe->head); 7662306a36Sopenharmony_ci tail = readl(pipe->tail); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (head < tail) 7962306a36Sopenharmony_ci return pipe->native.length - tail + head; 8062306a36Sopenharmony_ci else 8162306a36Sopenharmony_ci return head - tail; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void glink_rpm_rx_peek(struct qcom_glink_pipe *glink_pipe, 8562306a36Sopenharmony_ci void *data, unsigned int offset, size_t count) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 8862306a36Sopenharmony_ci unsigned int tail; 8962306a36Sopenharmony_ci size_t len; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci tail = readl(pipe->tail); 9262306a36Sopenharmony_ci tail += offset; 9362306a36Sopenharmony_ci if (tail >= pipe->native.length) 9462306a36Sopenharmony_ci tail -= pipe->native.length; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci len = min_t(size_t, count, pipe->native.length - tail); 9762306a36Sopenharmony_ci if (len) { 9862306a36Sopenharmony_ci __ioread32_copy(data, pipe->fifo + tail, 9962306a36Sopenharmony_ci len / sizeof(u32)); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (len != count) { 10362306a36Sopenharmony_ci __ioread32_copy(data + len, pipe->fifo, 10462306a36Sopenharmony_ci (count - len) / sizeof(u32)); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, 10962306a36Sopenharmony_ci size_t count) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 11262306a36Sopenharmony_ci unsigned int tail; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci tail = readl(pipe->tail); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci tail += count; 11762306a36Sopenharmony_ci if (tail >= pipe->native.length) 11862306a36Sopenharmony_ci tail -= pipe->native.length; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci writel(tail, pipe->tail); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 12662306a36Sopenharmony_ci unsigned int head; 12762306a36Sopenharmony_ci unsigned int tail; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci head = readl(pipe->head); 13062306a36Sopenharmony_ci tail = readl(pipe->tail); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (tail <= head) 13362306a36Sopenharmony_ci return pipe->native.length - head + tail; 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci return tail - head; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, 13962306a36Sopenharmony_ci unsigned int head, 14062306a36Sopenharmony_ci const void *data, size_t count) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci size_t len; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci len = min_t(size_t, count, pipe->native.length - head); 14562306a36Sopenharmony_ci if (len) { 14662306a36Sopenharmony_ci __iowrite32_copy(pipe->fifo + head, data, 14762306a36Sopenharmony_ci len / sizeof(u32)); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (len != count) { 15162306a36Sopenharmony_ci __iowrite32_copy(pipe->fifo, data + len, 15262306a36Sopenharmony_ci (count - len) / sizeof(u32)); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci head += count; 15662306a36Sopenharmony_ci if (head >= pipe->native.length) 15762306a36Sopenharmony_ci head -= pipe->native.length; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return head; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, 16362306a36Sopenharmony_ci const void *hdr, size_t hlen, 16462306a36Sopenharmony_ci const void *data, size_t dlen) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 16762306a36Sopenharmony_ci size_t tlen = hlen + dlen; 16862306a36Sopenharmony_ci size_t aligned_dlen; 16962306a36Sopenharmony_ci unsigned int head; 17062306a36Sopenharmony_ci char padding[8] = {0}; 17162306a36Sopenharmony_ci size_t pad; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Header length comes from glink native and is always 4 byte aligned */ 17462306a36Sopenharmony_ci if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n")) 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Move the unaligned tail of the message to the padding chunk, to 17962306a36Sopenharmony_ci * ensure word aligned accesses 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci aligned_dlen = ALIGN_DOWN(dlen, 4); 18262306a36Sopenharmony_ci if (aligned_dlen != dlen) 18362306a36Sopenharmony_ci memcpy(padding, data + aligned_dlen, dlen - aligned_dlen); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci head = readl(pipe->head); 18662306a36Sopenharmony_ci head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); 18762306a36Sopenharmony_ci head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4); 19062306a36Sopenharmony_ci if (pad) 19162306a36Sopenharmony_ci head = glink_rpm_tx_write_one(pipe, head, padding, pad); 19262306a36Sopenharmony_ci writel(head, pipe->head); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void glink_rpm_tx_kick(struct qcom_glink_pipe *glink_pipe) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); 19862306a36Sopenharmony_ci struct glink_rpm *rpm = container_of(pipe, struct glink_rpm, tx_pipe); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mbox_send_message(rpm->mbox_chan, NULL); 20162306a36Sopenharmony_ci mbox_client_txdone(rpm->mbox_chan, 0); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic irqreturn_t qcom_glink_rpm_intr(int irq, void *data) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct glink_rpm *rpm = data; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci qcom_glink_native_rx(rpm->glink); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return IRQ_HANDLED; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int glink_rpm_parse_toc(struct device *dev, 21462306a36Sopenharmony_ci void __iomem *msg_ram, 21562306a36Sopenharmony_ci size_t msg_ram_size, 21662306a36Sopenharmony_ci struct glink_rpm_pipe *rx, 21762306a36Sopenharmony_ci struct glink_rpm_pipe *tx) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct rpm_toc *toc; 22062306a36Sopenharmony_ci int num_entries; 22162306a36Sopenharmony_ci unsigned int id; 22262306a36Sopenharmony_ci size_t offset; 22362306a36Sopenharmony_ci size_t size; 22462306a36Sopenharmony_ci void *buf; 22562306a36Sopenharmony_ci int i; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL); 22862306a36Sopenharmony_ci if (!buf) 22962306a36Sopenharmony_ci return -ENOMEM; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci __ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE, 23262306a36Sopenharmony_ci RPM_TOC_SIZE / sizeof(u32)); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci toc = buf; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) { 23762306a36Sopenharmony_ci dev_err(dev, "RPM TOC has invalid magic\n"); 23862306a36Sopenharmony_ci goto err_inval; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci num_entries = le32_to_cpu(toc->count); 24262306a36Sopenharmony_ci if (num_entries > RPM_TOC_MAX_ENTRIES) { 24362306a36Sopenharmony_ci dev_err(dev, "Invalid number of toc entries\n"); 24462306a36Sopenharmony_ci goto err_inval; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci for (i = 0; i < num_entries; i++) { 24862306a36Sopenharmony_ci id = le32_to_cpu(toc->entries[i].id); 24962306a36Sopenharmony_ci offset = le32_to_cpu(toc->entries[i].offset); 25062306a36Sopenharmony_ci size = le32_to_cpu(toc->entries[i].size); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (offset > msg_ram_size || offset + size > msg_ram_size) { 25362306a36Sopenharmony_ci dev_err(dev, "TOC entry with invalid size\n"); 25462306a36Sopenharmony_ci continue; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (id) { 25862306a36Sopenharmony_ci case RPM_RX_FIFO_ID: 25962306a36Sopenharmony_ci rx->native.length = size; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci rx->tail = msg_ram + offset; 26262306a36Sopenharmony_ci rx->head = msg_ram + offset + sizeof(u32); 26362306a36Sopenharmony_ci rx->fifo = msg_ram + offset + 2 * sizeof(u32); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case RPM_TX_FIFO_ID: 26662306a36Sopenharmony_ci tx->native.length = size; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci tx->tail = msg_ram + offset; 26962306a36Sopenharmony_ci tx->head = msg_ram + offset + sizeof(u32); 27062306a36Sopenharmony_ci tx->fifo = msg_ram + offset + 2 * sizeof(u32); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!rx->fifo || !tx->fifo) { 27662306a36Sopenharmony_ci dev_err(dev, "Unable to find rx and tx descriptors\n"); 27762306a36Sopenharmony_ci goto err_inval; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci kfree(buf); 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cierr_inval: 28462306a36Sopenharmony_ci kfree(buf); 28562306a36Sopenharmony_ci return -EINVAL; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int glink_rpm_probe(struct platform_device *pdev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct qcom_glink *glink; 29162306a36Sopenharmony_ci struct glink_rpm *rpm; 29262306a36Sopenharmony_ci struct device_node *np; 29362306a36Sopenharmony_ci void __iomem *msg_ram; 29462306a36Sopenharmony_ci size_t msg_ram_size; 29562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 29662306a36Sopenharmony_ci struct resource r; 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); 30062306a36Sopenharmony_ci if (!rpm) 30162306a36Sopenharmony_ci return -ENOMEM; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); 30462306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &r); 30562306a36Sopenharmony_ci of_node_put(np); 30662306a36Sopenharmony_ci if (ret) 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci msg_ram = devm_ioremap(dev, r.start, resource_size(&r)); 31062306a36Sopenharmony_ci msg_ram_size = resource_size(&r); 31162306a36Sopenharmony_ci if (!msg_ram) 31262306a36Sopenharmony_ci return -ENOMEM; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, 31562306a36Sopenharmony_ci &rpm->rx_pipe, &rpm->tx_pipe); 31662306a36Sopenharmony_ci if (ret) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci rpm->irq = of_irq_get(dev->of_node, 0); 32062306a36Sopenharmony_ci ret = devm_request_irq(dev, rpm->irq, qcom_glink_rpm_intr, 32162306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_NO_AUTOEN, 32262306a36Sopenharmony_ci "glink-rpm", rpm); 32362306a36Sopenharmony_ci if (ret) { 32462306a36Sopenharmony_ci dev_err(dev, "failed to request IRQ\n"); 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci rpm->mbox_client.dev = dev; 32962306a36Sopenharmony_ci rpm->mbox_client.knows_txdone = true; 33062306a36Sopenharmony_ci rpm->mbox_chan = mbox_request_channel(&rpm->mbox_client, 0); 33162306a36Sopenharmony_ci if (IS_ERR(rpm->mbox_chan)) 33262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(rpm->mbox_chan), "failed to acquire IPC channel\n"); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Pipe specific accessors */ 33562306a36Sopenharmony_ci rpm->rx_pipe.native.avail = glink_rpm_rx_avail; 33662306a36Sopenharmony_ci rpm->rx_pipe.native.peek = glink_rpm_rx_peek; 33762306a36Sopenharmony_ci rpm->rx_pipe.native.advance = glink_rpm_rx_advance; 33862306a36Sopenharmony_ci rpm->tx_pipe.native.avail = glink_rpm_tx_avail; 33962306a36Sopenharmony_ci rpm->tx_pipe.native.write = glink_rpm_tx_write; 34062306a36Sopenharmony_ci rpm->tx_pipe.native.kick = glink_rpm_tx_kick; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci writel(0, rpm->tx_pipe.head); 34362306a36Sopenharmony_ci writel(0, rpm->rx_pipe.tail); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci glink = qcom_glink_native_probe(dev, 34662306a36Sopenharmony_ci 0, 34762306a36Sopenharmony_ci &rpm->rx_pipe.native, 34862306a36Sopenharmony_ci &rpm->tx_pipe.native, 34962306a36Sopenharmony_ci true); 35062306a36Sopenharmony_ci if (IS_ERR(glink)) { 35162306a36Sopenharmony_ci mbox_free_channel(rpm->mbox_chan); 35262306a36Sopenharmony_ci return PTR_ERR(glink); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rpm->glink = glink; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci platform_set_drvdata(pdev, rpm); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci enable_irq(rpm->irq); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void glink_rpm_remove(struct platform_device *pdev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct glink_rpm *rpm = platform_get_drvdata(pdev); 36762306a36Sopenharmony_ci struct qcom_glink *glink = rpm->glink; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci disable_irq(rpm->irq); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci qcom_glink_native_remove(glink); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci mbox_free_channel(rpm->mbox_chan); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic const struct of_device_id glink_rpm_of_match[] = { 37762306a36Sopenharmony_ci { .compatible = "qcom,glink-rpm" }, 37862306a36Sopenharmony_ci {} 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, glink_rpm_of_match); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic struct platform_driver glink_rpm_driver = { 38362306a36Sopenharmony_ci .probe = glink_rpm_probe, 38462306a36Sopenharmony_ci .remove_new = glink_rpm_remove, 38562306a36Sopenharmony_ci .driver = { 38662306a36Sopenharmony_ci .name = "qcom_glink_rpm", 38762306a36Sopenharmony_ci .of_match_table = glink_rpm_of_match, 38862306a36Sopenharmony_ci }, 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int __init glink_rpm_init(void) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci return platform_driver_register(&glink_rpm_driver); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_cisubsys_initcall(glink_rpm_init); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void __exit glink_rpm_exit(void) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci platform_driver_unregister(&glink_rpm_driver); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_cimodule_exit(glink_rpm_exit); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 40462306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm GLINK RPM driver"); 40562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 406