162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016, Linaro Ltd 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/of_address.h> 1062306a36Sopenharmony_ci#include <linux/of_irq.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/mailbox_client.h> 1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/rpmsg.h> 1762306a36Sopenharmony_ci#include <linux/idr.h> 1862306a36Sopenharmony_ci#include <linux/circ_buf.h> 1962306a36Sopenharmony_ci#include <linux/soc/qcom/smem.h> 2062306a36Sopenharmony_ci#include <linux/sizes.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/workqueue.h> 2462306a36Sopenharmony_ci#include <linux/list.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/rpmsg/qcom_glink.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "qcom_glink_native.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define FIFO_FULL_RESERVE 8 3162306a36Sopenharmony_ci#define FIFO_ALIGNMENT 8 3262306a36Sopenharmony_ci#define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 3562306a36Sopenharmony_ci#define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 3662306a36Sopenharmony_ci#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct qcom_glink_smem { 3962306a36Sopenharmony_ci struct device dev; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci int irq; 4262306a36Sopenharmony_ci struct qcom_glink *glink; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci struct mbox_client mbox_client; 4562306a36Sopenharmony_ci struct mbox_chan *mbox_chan; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci u32 remote_pid; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct glink_smem_pipe { 5162306a36Sopenharmony_ci struct qcom_glink_pipe native; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci __le32 *tail; 5462306a36Sopenharmony_ci __le32 *head; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci void *fifo; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci struct qcom_glink_smem *smem; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic size_t glink_smem_rx_avail(struct qcom_glink_pipe *np) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(np); 6662306a36Sopenharmony_ci struct qcom_glink_smem *smem = pipe->smem; 6762306a36Sopenharmony_ci size_t len; 6862306a36Sopenharmony_ci void *fifo; 6962306a36Sopenharmony_ci u32 head; 7062306a36Sopenharmony_ci u32 tail; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!pipe->fifo) { 7362306a36Sopenharmony_ci fifo = qcom_smem_get(smem->remote_pid, 7462306a36Sopenharmony_ci SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len); 7562306a36Sopenharmony_ci if (IS_ERR(fifo)) { 7662306a36Sopenharmony_ci pr_err("failed to acquire RX fifo handle: %ld\n", 7762306a36Sopenharmony_ci PTR_ERR(fifo)); 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pipe->fifo = fifo; 8262306a36Sopenharmony_ci pipe->native.length = len; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci head = le32_to_cpu(*pipe->head); 8662306a36Sopenharmony_ci tail = le32_to_cpu(*pipe->tail); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (head < tail) 8962306a36Sopenharmony_ci return pipe->native.length - tail + head; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci return head - tail; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void glink_smem_rx_peek(struct qcom_glink_pipe *np, 9562306a36Sopenharmony_ci void *data, unsigned int offset, size_t count) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(np); 9862306a36Sopenharmony_ci size_t len; 9962306a36Sopenharmony_ci u32 tail; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci tail = le32_to_cpu(*pipe->tail); 10262306a36Sopenharmony_ci tail += offset; 10362306a36Sopenharmony_ci if (tail >= pipe->native.length) 10462306a36Sopenharmony_ci tail -= pipe->native.length; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci len = min_t(size_t, count, pipe->native.length - tail); 10762306a36Sopenharmony_ci if (len) 10862306a36Sopenharmony_ci memcpy_fromio(data, pipe->fifo + tail, len); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (len != count) 11162306a36Sopenharmony_ci memcpy_fromio(data + len, pipe->fifo, (count - len)); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void glink_smem_rx_advance(struct qcom_glink_pipe *np, 11562306a36Sopenharmony_ci size_t count) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(np); 11862306a36Sopenharmony_ci u32 tail; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci tail = le32_to_cpu(*pipe->tail); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci tail += count; 12362306a36Sopenharmony_ci if (tail >= pipe->native.length) 12462306a36Sopenharmony_ci tail -= pipe->native.length; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci *pipe->tail = cpu_to_le32(tail); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic size_t glink_smem_tx_avail(struct qcom_glink_pipe *np) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(np); 13262306a36Sopenharmony_ci u32 head; 13362306a36Sopenharmony_ci u32 tail; 13462306a36Sopenharmony_ci u32 avail; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci head = le32_to_cpu(*pipe->head); 13762306a36Sopenharmony_ci tail = le32_to_cpu(*pipe->tail); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (tail <= head) 14062306a36Sopenharmony_ci avail = pipe->native.length - head + tail; 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci avail = tail - head; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)) 14562306a36Sopenharmony_ci avail = 0; 14662306a36Sopenharmony_ci else 14762306a36Sopenharmony_ci avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return avail; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe, 15362306a36Sopenharmony_ci unsigned int head, 15462306a36Sopenharmony_ci const void *data, size_t count) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci size_t len; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci len = min_t(size_t, count, pipe->native.length - head); 15962306a36Sopenharmony_ci if (len) 16062306a36Sopenharmony_ci memcpy(pipe->fifo + head, data, len); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (len != count) 16362306a36Sopenharmony_ci memcpy(pipe->fifo, data + len, count - len); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci head += count; 16662306a36Sopenharmony_ci if (head >= pipe->native.length) 16762306a36Sopenharmony_ci head -= pipe->native.length; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return head; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe, 17362306a36Sopenharmony_ci const void *hdr, size_t hlen, 17462306a36Sopenharmony_ci const void *data, size_t dlen) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe); 17762306a36Sopenharmony_ci unsigned int head; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci head = le32_to_cpu(*pipe->head); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci head = glink_smem_tx_write_one(pipe, head, hdr, hlen); 18262306a36Sopenharmony_ci head = glink_smem_tx_write_one(pipe, head, data, dlen); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Ensure head is always aligned to 8 bytes */ 18562306a36Sopenharmony_ci head = ALIGN(head, 8); 18662306a36Sopenharmony_ci if (head >= pipe->native.length) 18762306a36Sopenharmony_ci head -= pipe->native.length; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Ensure ordering of fifo and head update */ 19062306a36Sopenharmony_ci wmb(); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci *pipe->head = cpu_to_le32(head); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void glink_smem_tx_kick(struct qcom_glink_pipe *glink_pipe) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe); 19862306a36Sopenharmony_ci struct qcom_glink_smem *smem = pipe->smem; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mbox_send_message(smem->mbox_chan, NULL); 20162306a36Sopenharmony_ci mbox_client_txdone(smem->mbox_chan, 0); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic irqreturn_t qcom_glink_smem_intr(int irq, void *data) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct qcom_glink_smem *smem = data; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci qcom_glink_native_rx(smem->glink); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return IRQ_HANDLED; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void qcom_glink_smem_release(struct device *dev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct qcom_glink_smem *smem = container_of(dev, struct qcom_glink_smem, dev); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci kfree(smem); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistruct qcom_glink_smem *qcom_glink_smem_register(struct device *parent, 22162306a36Sopenharmony_ci struct device_node *node) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct glink_smem_pipe *rx_pipe; 22462306a36Sopenharmony_ci struct glink_smem_pipe *tx_pipe; 22562306a36Sopenharmony_ci struct qcom_glink_smem *smem; 22662306a36Sopenharmony_ci struct qcom_glink *glink; 22762306a36Sopenharmony_ci struct device *dev; 22862306a36Sopenharmony_ci u32 remote_pid; 22962306a36Sopenharmony_ci __le32 *descs; 23062306a36Sopenharmony_ci size_t size; 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci smem = kzalloc(sizeof(*smem), GFP_KERNEL); 23462306a36Sopenharmony_ci if (!smem) 23562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dev = &smem->dev; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci dev->parent = parent; 24062306a36Sopenharmony_ci dev->of_node = node; 24162306a36Sopenharmony_ci dev->release = qcom_glink_smem_release; 24262306a36Sopenharmony_ci dev_set_name(dev, "%s:%pOFn", dev_name(parent->parent), node); 24362306a36Sopenharmony_ci ret = device_register(dev); 24462306a36Sopenharmony_ci if (ret) { 24562306a36Sopenharmony_ci pr_err("failed to register glink edge\n"); 24662306a36Sopenharmony_ci put_device(dev); 24762306a36Sopenharmony_ci return ERR_PTR(ret); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "qcom,remote-pid", 25162306a36Sopenharmony_ci &remote_pid); 25262306a36Sopenharmony_ci if (ret) { 25362306a36Sopenharmony_ci dev_err(dev, "failed to parse qcom,remote-pid\n"); 25462306a36Sopenharmony_ci goto err_put_dev; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci smem->remote_pid = remote_pid; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL); 26062306a36Sopenharmony_ci tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL); 26162306a36Sopenharmony_ci if (!rx_pipe || !tx_pipe) { 26262306a36Sopenharmony_ci ret = -ENOMEM; 26362306a36Sopenharmony_ci goto err_put_dev; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = qcom_smem_alloc(remote_pid, 26762306a36Sopenharmony_ci SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32); 26862306a36Sopenharmony_ci if (ret && ret != -EEXIST) { 26962306a36Sopenharmony_ci dev_err(dev, "failed to allocate glink descriptors\n"); 27062306a36Sopenharmony_ci goto err_put_dev; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci descs = qcom_smem_get(remote_pid, 27462306a36Sopenharmony_ci SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size); 27562306a36Sopenharmony_ci if (IS_ERR(descs)) { 27662306a36Sopenharmony_ci dev_err(dev, "failed to acquire xprt descriptor\n"); 27762306a36Sopenharmony_ci ret = PTR_ERR(descs); 27862306a36Sopenharmony_ci goto err_put_dev; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (size != 32) { 28262306a36Sopenharmony_ci dev_err(dev, "glink descriptor of invalid size\n"); 28362306a36Sopenharmony_ci ret = -EINVAL; 28462306a36Sopenharmony_ci goto err_put_dev; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci tx_pipe->tail = &descs[0]; 28862306a36Sopenharmony_ci tx_pipe->head = &descs[1]; 28962306a36Sopenharmony_ci rx_pipe->tail = &descs[2]; 29062306a36Sopenharmony_ci rx_pipe->head = &descs[3]; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 29362306a36Sopenharmony_ci SZ_16K); 29462306a36Sopenharmony_ci if (ret && ret != -EEXIST) { 29562306a36Sopenharmony_ci dev_err(dev, "failed to allocate TX fifo\n"); 29662306a36Sopenharmony_ci goto err_put_dev; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, 30062306a36Sopenharmony_ci &tx_pipe->native.length); 30162306a36Sopenharmony_ci if (IS_ERR(tx_pipe->fifo)) { 30262306a36Sopenharmony_ci dev_err(dev, "failed to acquire TX fifo\n"); 30362306a36Sopenharmony_ci ret = PTR_ERR(tx_pipe->fifo); 30462306a36Sopenharmony_ci goto err_put_dev; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci smem->irq = of_irq_get(smem->dev.of_node, 0); 30862306a36Sopenharmony_ci ret = devm_request_irq(&smem->dev, smem->irq, qcom_glink_smem_intr, 30962306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_NO_AUTOEN, 31062306a36Sopenharmony_ci "glink-smem", smem); 31162306a36Sopenharmony_ci if (ret) { 31262306a36Sopenharmony_ci dev_err(&smem->dev, "failed to request IRQ\n"); 31362306a36Sopenharmony_ci goto err_put_dev; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci smem->mbox_client.dev = &smem->dev; 31762306a36Sopenharmony_ci smem->mbox_client.knows_txdone = true; 31862306a36Sopenharmony_ci smem->mbox_chan = mbox_request_channel(&smem->mbox_client, 0); 31962306a36Sopenharmony_ci if (IS_ERR(smem->mbox_chan)) { 32062306a36Sopenharmony_ci ret = dev_err_probe(&smem->dev, PTR_ERR(smem->mbox_chan), 32162306a36Sopenharmony_ci "failed to acquire IPC channel\n"); 32262306a36Sopenharmony_ci goto err_put_dev; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci rx_pipe->smem = smem; 32662306a36Sopenharmony_ci rx_pipe->native.avail = glink_smem_rx_avail; 32762306a36Sopenharmony_ci rx_pipe->native.peek = glink_smem_rx_peek; 32862306a36Sopenharmony_ci rx_pipe->native.advance = glink_smem_rx_advance; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci tx_pipe->smem = smem; 33162306a36Sopenharmony_ci tx_pipe->native.avail = glink_smem_tx_avail; 33262306a36Sopenharmony_ci tx_pipe->native.write = glink_smem_tx_write; 33362306a36Sopenharmony_ci tx_pipe->native.kick = glink_smem_tx_kick; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci *rx_pipe->tail = 0; 33662306a36Sopenharmony_ci *tx_pipe->head = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci glink = qcom_glink_native_probe(dev, 33962306a36Sopenharmony_ci GLINK_FEATURE_INTENT_REUSE, 34062306a36Sopenharmony_ci &rx_pipe->native, &tx_pipe->native, 34162306a36Sopenharmony_ci false); 34262306a36Sopenharmony_ci if (IS_ERR(glink)) { 34362306a36Sopenharmony_ci ret = PTR_ERR(glink); 34462306a36Sopenharmony_ci goto err_free_mbox; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci smem->glink = glink; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci enable_irq(smem->irq); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return smem; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cierr_free_mbox: 35462306a36Sopenharmony_ci mbox_free_channel(smem->mbox_chan); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cierr_put_dev: 35762306a36Sopenharmony_ci device_unregister(dev); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return ERR_PTR(ret); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_smem_register); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_civoid qcom_glink_smem_unregister(struct qcom_glink_smem *smem) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct qcom_glink *glink = smem->glink; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci disable_irq(smem->irq); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci qcom_glink_native_remove(glink); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mbox_free_channel(smem->mbox_chan); 37262306a36Sopenharmony_ci device_unregister(&smem->dev); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_smem_unregister); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); 37762306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm GLINK SMEM driver"); 37862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 379