Add samsung specific changes
This commit is contained in:
@@ -31,6 +31,16 @@ config RPMSG_NS
|
||||
channel that probes the associated RPMsg device on remote endpoint
|
||||
service announcement.
|
||||
|
||||
config MSM_RPM_SMD
|
||||
tristate "RPM driver using SMD protocol"
|
||||
select RPMSG
|
||||
help
|
||||
RPM is the dedicated hardware engine for managing shared SoC
|
||||
resources. This config adds driver support for using SMD as a
|
||||
transport layer communication with RPM hardware. It also selects
|
||||
the MSM_MPM config that programs the MPM module to monitor interrupts
|
||||
during sleep modes.
|
||||
|
||||
config RPMSG_MTK_SCP
|
||||
tristate "MediaTek SCP"
|
||||
depends on MTK_SCP
|
||||
@@ -44,6 +54,15 @@ config RPMSG_QCOM_GLINK
|
||||
tristate
|
||||
select RPMSG
|
||||
|
||||
config RPMSG_QCOM_GLINK_DEBUG
|
||||
bool "Qualcomm Technologies, Inc. Glink driver debug support"
|
||||
depends on RPMSG_QCOM_GLINK
|
||||
help
|
||||
Say y here to enable GLINK debugging features. This currently includes
|
||||
an assert in the intent timeout case to catch issues with unresponsive
|
||||
remote processors. Future debug enhancements can be switched on and off
|
||||
with this config.
|
||||
|
||||
config RPMSG_QCOM_GLINK_RPM
|
||||
tristate "Qualcomm RPM Glink driver"
|
||||
select RPMSG_QCOM_GLINK
|
||||
@@ -64,6 +83,27 @@ config RPMSG_QCOM_GLINK_SMEM
|
||||
which provides support for using the GLINK communication protocol
|
||||
over SMEM.
|
||||
|
||||
config RPMSG_QCOM_GLINK_SPSS
|
||||
tristate "QTI SPSS Glink driver"
|
||||
select RPMSG_QCOM_GLINK_NATIVE
|
||||
depends on MAILBOX
|
||||
depends on QCOM_SMEM
|
||||
help
|
||||
Say y here to enable support for the GLINK SPSS communication driver,
|
||||
which provides support for using the GLINK communication protocol
|
||||
over SMEM. This protocol maps the smem and then shares the mapped
|
||||
region with the remote proc by writing the smem descriptor location
|
||||
and size into shared registers.
|
||||
|
||||
config QCOM_GLINK_PKT
|
||||
tristate "Enable device interface for GLINK packet channels"
|
||||
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SLATECOM
|
||||
help
|
||||
G-link packet driver provides the interface for the userspace
|
||||
clients to communicate over G-Link via device nodes.
|
||||
This enable the userspace clients to read and write to
|
||||
some glink packets channel.
|
||||
|
||||
config RPMSG_QCOM_SMD
|
||||
tristate "Qualcomm Shared Memory Driver (SMD)"
|
||||
depends on MAILBOX
|
||||
|
@@ -5,8 +5,12 @@ obj-$(CONFIG_RPMSG_CTRL) += rpmsg_ctrl.o
|
||||
obj-$(CONFIG_RPMSG_NS) += rpmsg_ns.o
|
||||
obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o
|
||||
qcom_glink-objs := qcom_glink_native.o qcom_glink_ssr.o
|
||||
qcom_glink-objs += qcom_glink_memshare.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK) += qcom_glink.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_SPSS) += qcom_glink_spss.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o
|
||||
obj-$(CONFIG_QCOM_GLINK_PKT) += glink_pkt.o
|
||||
obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o
|
||||
obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o
|
||||
|
1361
drivers/rpmsg/glink_pkt.c
Normal file
1361
drivers/rpmsg/glink_pkt.c
Normal file
File diff suppressed because it is too large
Load Diff
95
drivers/rpmsg/qcom_glink_memshare.c
Normal file
95
drivers/rpmsg/qcom_glink_memshare.c
Normal file
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct qcom_glink_mem_entry {
|
||||
struct device *dev;
|
||||
void *va;
|
||||
dma_addr_t dma;
|
||||
size_t len;
|
||||
u32 da;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(qcom_glink_mem_entry_lock);
|
||||
static LIST_HEAD(qcom_glink_mem_entries);
|
||||
|
||||
struct qcom_glink_mem_entry *
|
||||
qcom_glink_mem_entry_init(struct device *dev, void *va, dma_addr_t dma, size_t len, u32 da)
|
||||
{
|
||||
struct qcom_glink_mem_entry *mem = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
|
||||
if (!mem)
|
||||
return mem;
|
||||
|
||||
mem->dev = dev;
|
||||
mem->va = va;
|
||||
mem->dma = dma;
|
||||
mem->da = da;
|
||||
mem->len = len;
|
||||
INIT_LIST_HEAD(&mem->node);
|
||||
|
||||
spin_lock_irqsave(&qcom_glink_mem_entry_lock, flags);
|
||||
list_add_tail(&mem->node, &qcom_glink_mem_entries);
|
||||
spin_unlock_irqrestore(&qcom_glink_mem_entry_lock, flags);
|
||||
|
||||
return mem;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_mem_entry_init);
|
||||
|
||||
void qcom_glink_mem_entry_free(struct qcom_glink_mem_entry *mem)
|
||||
{
|
||||
struct qcom_glink_mem_entry *entry, *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&qcom_glink_mem_entry_lock, flags);
|
||||
list_for_each_entry_safe(entry, tmp, &qcom_glink_mem_entries, node) {
|
||||
if (entry == mem) {
|
||||
list_del(&mem->node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&qcom_glink_mem_entry_lock, flags);
|
||||
|
||||
kfree(mem);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_mem_entry_free);
|
||||
|
||||
void *qcom_glink_prepare_da_for_cpu(u64 da, size_t len)
|
||||
{
|
||||
struct qcom_glink_mem_entry *mem;
|
||||
unsigned long flags;
|
||||
void *ptr = NULL;
|
||||
|
||||
spin_lock_irqsave(&qcom_glink_mem_entry_lock, flags);
|
||||
list_for_each_entry(mem, &qcom_glink_mem_entries, node) {
|
||||
int offset = da - mem->da;
|
||||
|
||||
if (!mem->va)
|
||||
continue;
|
||||
|
||||
if (offset < 0)
|
||||
continue;
|
||||
|
||||
if (offset + len > mem->len)
|
||||
continue;
|
||||
|
||||
ptr = mem->va + offset;
|
||||
dma_sync_single_for_cpu(mem->dev, da, len, DMA_FROM_DEVICE);
|
||||
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&qcom_glink_mem_entry_lock, flags);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_prepare_da_for_cpu);
|
File diff suppressed because it is too large
Load Diff
@@ -7,10 +7,25 @@
|
||||
#define __QCOM_GLINK_NATIVE_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/rpmsg.h>
|
||||
|
||||
#define GLINK_FEATURE_INTENT_REUSE BIT(0)
|
||||
#define GLINK_FEATURE_MIGRATION BIT(1)
|
||||
#define GLINK_FEATURE_TRACER_PKT BIT(2)
|
||||
#define GLINK_FEATURE_ZERO_COPY BIT(3)
|
||||
#define GLINK_FEATURE_ZERO_COPY_POOLS BIT(4)
|
||||
|
||||
/**
|
||||
* rpmsg rx callback return definitions
|
||||
* @RPMSG_HANDLED: rpmsg user is done processing data, framework can free the
|
||||
* resources related to the buffer
|
||||
* @RPMSG_DEFER: rpmsg user is not done processing data, framework will hold
|
||||
* onto resources related to the buffer until rpmsg_rx_done is
|
||||
* called. User should check their endpoint to see if rx_done
|
||||
* is a supported operation.
|
||||
*/
|
||||
#define RPMSG_HANDLED 0
|
||||
#define RPMSG_DEFER 1
|
||||
|
||||
struct qcom_glink_pipe {
|
||||
size_t length;
|
||||
@@ -29,13 +44,27 @@ struct qcom_glink_pipe {
|
||||
|
||||
struct device;
|
||||
struct qcom_glink;
|
||||
extern const struct dev_pm_ops glink_native_pm_ops;
|
||||
|
||||
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
|
||||
unsigned long features,
|
||||
struct qcom_glink_pipe *rx,
|
||||
struct qcom_glink_pipe *tx,
|
||||
bool intentless);
|
||||
int qcom_glink_native_start(struct qcom_glink *glink);
|
||||
void qcom_glink_native_remove(struct qcom_glink *glink);
|
||||
void qcom_glink_native_rx(struct qcom_glink *glink);
|
||||
|
||||
/* These operations are temporarily exposing signal interfaces */
|
||||
int qcom_glink_get_signals(struct rpmsg_endpoint *ept);
|
||||
int qcom_glink_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear);
|
||||
int qcom_glink_register_signals_cb(struct rpmsg_endpoint *ept,
|
||||
int (*signals_cb)(struct rpmsg_device *dev, void *priv, u32 old, u32 new));
|
||||
|
||||
/* These operations are temporarily exposing deferred freeing interfaces */
|
||||
bool qcom_glink_rx_done_supported(struct rpmsg_endpoint *ept);
|
||||
int qcom_glink_rx_done(struct rpmsg_endpoint *ept, void *data);
|
||||
|
||||
void *qcom_glink_prepare_da_for_cpu(u64 da, size_t len);
|
||||
|
||||
#endif
|
||||
|
@@ -358,7 +358,9 @@ static int glink_rpm_probe(struct platform_device *pdev)
|
||||
|
||||
enable_irq(rpm->irq);
|
||||
|
||||
return 0;
|
||||
ret = qcom_glink_native_start(glink);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void glink_rpm_remove(struct platform_device *pdev)
|
||||
@@ -385,6 +387,7 @@ static struct platform_driver glink_rpm_driver = {
|
||||
.driver = {
|
||||
.name = "qcom_glink_rpm",
|
||||
.of_match_table = glink_rpm_of_match,
|
||||
.pm = &glink_native_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
|
||||
#include <linux/rpmsg/qcom_glink.h>
|
||||
|
||||
@@ -39,6 +40,7 @@ struct qcom_glink_smem {
|
||||
struct device dev;
|
||||
|
||||
int irq;
|
||||
char irqname[32];
|
||||
struct qcom_glink *glink;
|
||||
|
||||
struct mbox_client mbox_client;
|
||||
@@ -46,6 +48,12 @@ struct qcom_glink_smem {
|
||||
|
||||
u32 remote_pid;
|
||||
};
|
||||
/* Define IPC Logging Macros */
|
||||
#define GLINK_SMEM_IPC_LOG_PAGE_CNT 32
|
||||
static void *glink_ilctxt;
|
||||
|
||||
#define GLINK_SMEM_INFO(x, ...) \
|
||||
ipc_log_string(glink_ilctxt, "[%s]: "x, __func__, ##__VA_ARGS__)
|
||||
|
||||
struct glink_smem_pipe {
|
||||
struct qcom_glink_pipe native;
|
||||
@@ -86,29 +94,53 @@ static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
|
||||
if (head < tail)
|
||||
return pipe->native.length - tail + head;
|
||||
len = pipe->native.length - tail + head;
|
||||
else
|
||||
return head - tail;
|
||||
len = head - tail;
|
||||
|
||||
if (WARN_ON_ONCE(len > pipe->native.length))
|
||||
len = 0;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void glink_smem_rx_peek(struct qcom_glink_pipe *np,
|
||||
void *data, unsigned int offset, size_t count)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_pipe(np);
|
||||
unsigned char *bytedata = (unsigned char *)data;
|
||||
struct qcom_glink_smem *smem = pipe->smem;
|
||||
size_t len;
|
||||
u32 tail;
|
||||
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
|
||||
if (WARN_ON_ONCE(tail > pipe->native.length))
|
||||
return;
|
||||
|
||||
tail += offset;
|
||||
if (tail >= pipe->native.length)
|
||||
tail -= pipe->native.length;
|
||||
|
||||
/* Update the tail pointer and add a memory barrier to ensure
|
||||
* consistent read/write between the APPS and the remote.
|
||||
* This prevents the APPS from reading stale data from the FIFO.
|
||||
*/
|
||||
mb();
|
||||
|
||||
len = min_t(size_t, count, pipe->native.length - tail);
|
||||
if (len)
|
||||
memcpy_fromio(data, pipe->fifo + tail, len);
|
||||
|
||||
if (len != count)
|
||||
memcpy_fromio(data + len, pipe->fifo, (count - len));
|
||||
|
||||
if (count == 1)
|
||||
GLINK_SMEM_INFO("RX: remote-pid=%d, head=0x%x, tail=0x%x, [%02x]\n",
|
||||
smem->remote_pid, le32_to_cpu(*pipe->head), tail, bytedata[0]);
|
||||
if (count > 1)
|
||||
GLINK_SMEM_INFO("RX: remote-pid=%d, head=0x%x, tail=0x%x, [%02x %02x]\n",
|
||||
smem->remote_pid, le32_to_cpu(*pipe->head), tail, bytedata[1], bytedata[0]);
|
||||
}
|
||||
|
||||
static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
|
||||
@@ -121,7 +153,7 @@ static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
|
||||
|
||||
tail += count;
|
||||
if (tail >= pipe->native.length)
|
||||
tail -= pipe->native.length;
|
||||
tail %= pipe->native.length;
|
||||
|
||||
*pipe->tail = cpu_to_le32(tail);
|
||||
}
|
||||
@@ -146,6 +178,9 @@ static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np)
|
||||
else
|
||||
avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE;
|
||||
|
||||
if (WARN_ON_ONCE(avail > pipe->native.length))
|
||||
avail = 0;
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
@@ -155,6 +190,9 @@ static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe,
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (WARN_ON_ONCE(head > pipe->native.length))
|
||||
return head;
|
||||
|
||||
len = min_t(size_t, count, pipe->native.length - head);
|
||||
if (len)
|
||||
memcpy(pipe->fifo + head, data, len);
|
||||
@@ -174,6 +212,7 @@ static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
|
||||
const void *data, size_t dlen)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe);
|
||||
struct qcom_glink_smem *smem = pipe->smem;
|
||||
unsigned int head;
|
||||
|
||||
head = le32_to_cpu(*pipe->head);
|
||||
@@ -189,6 +228,8 @@ static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
|
||||
/* Ensure ordering of fifo and head update */
|
||||
wmb();
|
||||
|
||||
GLINK_SMEM_INFO("TX: remote-pid=%d, head=0x%x, tail=0x%x\n",
|
||||
smem->remote_pid, head, le32_to_cpu(*pipe->tail));
|
||||
*pipe->head = cpu_to_le32(head);
|
||||
}
|
||||
|
||||
@@ -304,10 +345,11 @@ struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
scnprintf(smem->irqname, 32, "glink-native-%u", remote_pid);
|
||||
smem->irq = of_irq_get(smem->dev.of_node, 0);
|
||||
ret = devm_request_irq(&smem->dev, smem->irq, qcom_glink_smem_intr,
|
||||
IRQF_NO_SUSPEND | IRQF_NO_AUTOEN,
|
||||
"glink-smem", smem);
|
||||
smem->irqname, smem);
|
||||
if (ret) {
|
||||
dev_err(&smem->dev, "failed to request IRQ\n");
|
||||
goto err_put_dev;
|
||||
@@ -336,7 +378,7 @@ struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
|
||||
*tx_pipe->head = 0;
|
||||
|
||||
glink = qcom_glink_native_probe(dev,
|
||||
GLINK_FEATURE_INTENT_REUSE,
|
||||
GLINK_FEATURE_INTENT_REUSE | GLINK_FEATURE_ZERO_COPY,
|
||||
&rx_pipe->native, &tx_pipe->native,
|
||||
false);
|
||||
if (IS_ERR(glink)) {
|
||||
@@ -344,6 +386,9 @@ struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
|
||||
goto err_free_mbox;
|
||||
}
|
||||
|
||||
if (!glink_ilctxt)
|
||||
glink_ilctxt = ipc_log_context_create(GLINK_SMEM_IPC_LOG_PAGE_CNT,
|
||||
"glink_smem", 0);
|
||||
smem->glink = glink;
|
||||
|
||||
enable_irq(smem->irq);
|
||||
@@ -360,9 +405,19 @@ err_put_dev:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_smem_register);
|
||||
|
||||
int qcom_glink_smem_start(struct qcom_glink_smem *smem)
|
||||
{
|
||||
return qcom_glink_native_start(smem->glink);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_glink_smem_start);
|
||||
|
||||
void qcom_glink_smem_unregister(struct qcom_glink_smem *smem)
|
||||
{
|
||||
struct qcom_glink *glink = smem->glink;
|
||||
struct qcom_glink *glink;
|
||||
|
||||
if (!smem)
|
||||
return;
|
||||
glink = smem->glink;
|
||||
|
||||
disable_irq(smem->irq);
|
||||
|
||||
|
431
drivers/rpmsg/qcom_glink_spss.c
Normal file
431
drivers/rpmsg/qcom_glink_spss.c
Normal file
@@ -0,0 +1,431 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, 2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/soc/qcom/smem.h>
|
||||
#include <linux/rpmsg/qcom_glink.h>
|
||||
|
||||
#include "qcom_glink_native.h"
|
||||
|
||||
#define FIFO_FULL_RESERVE 8
|
||||
#define FIFO_ALIGNMENT 8
|
||||
#define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */
|
||||
|
||||
#define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478
|
||||
#define SPSS_TX_FIFO_SIZE SZ_2K
|
||||
#define SPSS_RX_FIFO_SIZE SZ_2K
|
||||
|
||||
struct glink_spss_cfg {
|
||||
__le32 tx_tail;
|
||||
__le32 tx_head;
|
||||
__le32 tx_fifo_size;
|
||||
__le32 rx_tail;
|
||||
__le32 rx_head;
|
||||
__le32 rx_fifo_size;
|
||||
};
|
||||
|
||||
struct glink_spss_pipe {
|
||||
struct qcom_glink_pipe native;
|
||||
|
||||
__le32 *tail;
|
||||
__le32 *head;
|
||||
|
||||
void *fifo;
|
||||
|
||||
struct qcom_glink_spss *spss;
|
||||
};
|
||||
|
||||
struct qcom_glink_spss {
|
||||
struct device dev;
|
||||
|
||||
int irq;
|
||||
char irqname[32];
|
||||
struct qcom_glink *glink;
|
||||
|
||||
struct mbox_client mbox_client;
|
||||
struct mbox_chan *mbox_chan;
|
||||
|
||||
struct glink_spss_pipe *tx_pipe;
|
||||
struct glink_spss_pipe *rx_pipe;
|
||||
|
||||
u32 remote_pid;
|
||||
};
|
||||
|
||||
#define to_spss_pipe(p) container_of(p, struct glink_spss_pipe, native)
|
||||
|
||||
static void glink_spss_reset(struct glink_spss_pipe *np)
|
||||
{
|
||||
*np->head = cpu_to_le32(0);
|
||||
*np->tail = cpu_to_le32(0);
|
||||
}
|
||||
|
||||
static size_t glink_spss_rx_avail(struct qcom_glink_pipe *np)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(np);
|
||||
u32 head;
|
||||
u32 tail;
|
||||
|
||||
head = le32_to_cpu(*pipe->head);
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
|
||||
if (head < tail)
|
||||
return pipe->native.length - tail + head;
|
||||
else
|
||||
return head - tail;
|
||||
}
|
||||
|
||||
static void glink_spss_rx_peek(struct qcom_glink_pipe *np,
|
||||
void *data, unsigned int offset, size_t count)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(np);
|
||||
size_t len;
|
||||
u32 tail;
|
||||
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
tail += offset;
|
||||
if (tail >= pipe->native.length)
|
||||
tail -= pipe->native.length;
|
||||
|
||||
len = min_t(size_t, count, pipe->native.length - tail);
|
||||
if (len)
|
||||
memcpy_fromio(data, pipe->fifo + tail, len);
|
||||
|
||||
if (len != count)
|
||||
memcpy_fromio(data + len, pipe->fifo, count - len);
|
||||
}
|
||||
|
||||
static void glink_spss_rx_advance(struct qcom_glink_pipe *np,
|
||||
size_t count)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(np);
|
||||
u32 tail;
|
||||
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
|
||||
tail += count;
|
||||
if (tail >= pipe->native.length)
|
||||
tail -= pipe->native.length;
|
||||
|
||||
*pipe->tail = cpu_to_le32(tail);
|
||||
}
|
||||
|
||||
static size_t glink_spss_tx_avail(struct qcom_glink_pipe *np)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(np);
|
||||
u32 head;
|
||||
u32 tail;
|
||||
u32 avail;
|
||||
|
||||
head = le32_to_cpu(*pipe->head);
|
||||
tail = le32_to_cpu(*pipe->tail);
|
||||
|
||||
if (tail <= head)
|
||||
avail = pipe->native.length - head + tail;
|
||||
else
|
||||
avail = tail - head;
|
||||
|
||||
if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE))
|
||||
avail = 0;
|
||||
else
|
||||
avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE;
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
static unsigned int glink_spss_tx_write_one(struct glink_spss_pipe *pipe,
|
||||
unsigned int head,
|
||||
const void *data, size_t count)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = min_t(size_t, count, pipe->native.length - head);
|
||||
if (len)
|
||||
memcpy(pipe->fifo + head, data, len);
|
||||
|
||||
if (len != count)
|
||||
memcpy(pipe->fifo, data + len, count - len);
|
||||
|
||||
head += count;
|
||||
if (head >= pipe->native.length)
|
||||
head -= pipe->native.length;
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
static void glink_spss_tx_write(struct qcom_glink_pipe *glink_pipe,
|
||||
const void *hdr, size_t hlen,
|
||||
const void *data, size_t dlen)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(glink_pipe);
|
||||
unsigned int head;
|
||||
|
||||
head = le32_to_cpu(*pipe->head);
|
||||
|
||||
head = glink_spss_tx_write_one(pipe, head, hdr, hlen);
|
||||
head = glink_spss_tx_write_one(pipe, head, data, dlen);
|
||||
|
||||
/* Ensure head is always aligned to 8 bytes */
|
||||
head = ALIGN(head, 8);
|
||||
if (head >= pipe->native.length)
|
||||
head -= pipe->native.length;
|
||||
|
||||
/* Ensure ordering of fifo and head update */
|
||||
wmb();
|
||||
|
||||
*pipe->head = cpu_to_le32(head);
|
||||
}
|
||||
|
||||
static void glink_spss_tx_kick(struct qcom_glink_pipe *glink_pipe)
|
||||
{
|
||||
struct glink_spss_pipe *pipe = to_spss_pipe(glink_pipe);
|
||||
struct qcom_glink_spss *spss = pipe->spss;
|
||||
|
||||
mbox_send_message(spss->mbox_chan, NULL);
|
||||
mbox_client_txdone(spss->mbox_chan, 0);
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_glink_spss_intr(int irq, void *data)
|
||||
{
|
||||
struct qcom_glink_spss *spss = data;
|
||||
|
||||
qcom_glink_native_rx(spss->glink);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void qcom_glink_spss_release(struct device *dev)
|
||||
{
|
||||
struct qcom_glink_spss *spss = container_of(dev, struct qcom_glink_spss, dev);
|
||||
|
||||
kfree(spss);
|
||||
}
|
||||
|
||||
static int glink_spss_advertise_cfg(struct device *dev,
|
||||
u32 size, phys_addr_t addr)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
__le64 __iomem *spss_addr;
|
||||
__le32 __iomem *spss_size;
|
||||
struct resource addr_r;
|
||||
struct resource size_r;
|
||||
int addr_idx;
|
||||
int size_idx;
|
||||
|
||||
addr_idx = of_property_match_string(np, "reg-names", "qcom,spss-addr");
|
||||
size_idx = of_property_match_string(np, "reg-names", "qcom,spss-size");
|
||||
if (addr_idx < 0 || size_idx < 0) {
|
||||
dev_err(dev, "failed to find location registers\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_address_to_resource(np, addr_idx, &addr_r))
|
||||
return -ENOMEM;
|
||||
spss_addr = ioremap(addr_r.start, resource_size(&addr_r));
|
||||
if (IS_ERR_OR_NULL(spss_addr)) {
|
||||
dev_err(dev, "failed to map spss addr resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (of_address_to_resource(np, size_idx, &size_r)) {
|
||||
iounmap(spss_addr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
spss_size = ioremap(size_r.start, resource_size(&size_r));
|
||||
if (IS_ERR_OR_NULL(spss_size)) {
|
||||
iounmap(spss_addr);
|
||||
dev_err(dev, "failed to map spss size resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
writeq_relaxed(addr, spss_addr);
|
||||
writel_relaxed(size, spss_size);
|
||||
iounmap(spss_addr);
|
||||
iounmap(spss_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct qcom_glink_spss *qcom_glink_spss_register(struct device *parent,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct glink_spss_pipe *rx_pipe;
|
||||
struct glink_spss_pipe *tx_pipe;
|
||||
struct qcom_glink_spss *spss;
|
||||
struct glink_spss_cfg *cfg;
|
||||
struct qcom_glink *glink;
|
||||
struct device *dev;
|
||||
u32 remote_pid;
|
||||
size_t tx_size;
|
||||
size_t rx_size;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
|
||||
spss = kzalloc(sizeof(*spss), GFP_KERNEL);
|
||||
if (!spss)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = &spss->dev;
|
||||
|
||||
dev->parent = parent;
|
||||
dev->of_node = node;
|
||||
dev->release = qcom_glink_spss_release;
|
||||
dev_set_name(dev, "%s:%pOFn", dev_name(parent->parent), node);
|
||||
ret = device_register(dev);
|
||||
if (ret) {
|
||||
pr_err("failed to register glink edge\n");
|
||||
put_device(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "qcom,remote-pid",
|
||||
&remote_pid);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to parse qcom,remote-pid\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
spss->remote_pid = remote_pid;
|
||||
|
||||
rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL);
|
||||
tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL);
|
||||
if (!rx_pipe || !tx_pipe) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
tx_size = SPSS_TX_FIFO_SIZE;
|
||||
rx_size = SPSS_RX_FIFO_SIZE;
|
||||
size = tx_size + rx_size + sizeof(*cfg);
|
||||
ret = qcom_smem_alloc(remote_pid,
|
||||
SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, size);
|
||||
if (ret && ret != -EEXIST) {
|
||||
dev_err(dev, "failed to allocate glink descriptors\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
cfg = qcom_smem_get(remote_pid,
|
||||
SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size);
|
||||
if (IS_ERR(cfg)) {
|
||||
dev_err(dev, "failed to acquire xprt descriptor\n");
|
||||
ret = PTR_ERR(cfg);
|
||||
goto err_put_dev;
|
||||
}
|
||||
if (size != tx_size + rx_size + sizeof(*cfg)) {
|
||||
dev_err(dev, "glink descriptor of invalid size\n");
|
||||
ret = -EINVAL;
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
scnprintf(spss->irqname, 32, "glink-native-%u", remote_pid);
|
||||
spss->irq = of_irq_get(spss->dev.of_node, 0);
|
||||
ret = devm_request_irq(&spss->dev, spss->irq, qcom_glink_spss_intr,
|
||||
IRQF_NO_SUSPEND | IRQF_NO_AUTOEN,
|
||||
spss->irqname, spss);
|
||||
if (ret) {
|
||||
dev_err(&spss->dev, "failed to request IRQ\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
spss->mbox_client.dev = &spss->dev;
|
||||
spss->mbox_client.knows_txdone = true;
|
||||
spss->mbox_chan = mbox_request_channel(&spss->mbox_client, 0);
|
||||
if (IS_ERR(spss->mbox_chan)) {
|
||||
ret = dev_err_probe(&spss->dev, PTR_ERR(spss->mbox_chan),
|
||||
"failed to acquire IPC channel\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
cfg->tx_fifo_size = cpu_to_le32(tx_size);
|
||||
cfg->rx_fifo_size = cpu_to_le32(rx_size);
|
||||
|
||||
tx_pipe->spss = spss;
|
||||
spss->tx_pipe = tx_pipe;
|
||||
tx_pipe->tail = &cfg->tx_tail;
|
||||
tx_pipe->head = &cfg->tx_head;
|
||||
tx_pipe->native.length = tx_size;
|
||||
tx_pipe->fifo = (u8 *)cfg + sizeof(*cfg);
|
||||
|
||||
rx_pipe->spss = spss;
|
||||
spss->rx_pipe = rx_pipe;
|
||||
rx_pipe->tail = &cfg->rx_tail;
|
||||
rx_pipe->head = &cfg->rx_head;
|
||||
rx_pipe->native.length = rx_size;
|
||||
rx_pipe->fifo = (u8 *)cfg + sizeof(*cfg) + tx_size;
|
||||
|
||||
rx_pipe->native.avail = glink_spss_rx_avail;
|
||||
rx_pipe->native.peek = glink_spss_rx_peek;
|
||||
rx_pipe->native.advance = glink_spss_rx_advance;
|
||||
|
||||
tx_pipe->native.avail = glink_spss_tx_avail;
|
||||
tx_pipe->native.write = glink_spss_tx_write;
|
||||
tx_pipe->native.kick = glink_spss_tx_kick;
|
||||
|
||||
*rx_pipe->tail = 0;
|
||||
*tx_pipe->head = 0;
|
||||
|
||||
ret = glink_spss_advertise_cfg(dev, size, qcom_smem_virt_to_phys(cfg));
|
||||
if (ret)
|
||||
goto err_free_mbox;
|
||||
|
||||
glink = qcom_glink_native_probe(dev,
|
||||
GLINK_FEATURE_INTENT_REUSE,
|
||||
&rx_pipe->native, &tx_pipe->native,
|
||||
false);
|
||||
if (IS_ERR(glink)) {
|
||||
ret = PTR_ERR(glink);
|
||||
goto err_free_mbox;
|
||||
}
|
||||
|
||||
spss->glink = glink;
|
||||
|
||||
enable_irq(spss->irq);
|
||||
|
||||
ret = qcom_glink_native_start(glink);
|
||||
if (ret)
|
||||
goto err_free_mbox;
|
||||
|
||||
return spss;
|
||||
|
||||
err_free_mbox:
|
||||
mbox_free_channel(spss->mbox_chan);
|
||||
|
||||
err_put_dev:
|
||||
put_device(dev);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_spss_register);
|
||||
|
||||
void qcom_glink_spss_unregister(struct qcom_glink_spss *spss)
|
||||
{
|
||||
if (!spss)
|
||||
return;
|
||||
|
||||
disable_irq(spss->irq);
|
||||
|
||||
qcom_glink_native_remove(spss->glink);
|
||||
|
||||
mbox_free_channel(spss->mbox_chan);
|
||||
|
||||
glink_spss_reset(spss->tx_pipe);
|
||||
glink_spss_reset(spss->rx_pipe);
|
||||
|
||||
device_unregister(&spss->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_spss_unregister);
|
||||
|
||||
MODULE_DESCRIPTION("QTI GLINK SPSS driver");
|
||||
MODULE_LICENSE("GPL");
|
1701
drivers/rpmsg/rpm-smd.c
Normal file
1701
drivers/rpmsg/rpm-smd.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user