mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
rpmsg updates for v4.14
This extends the Qualcomm GLINK implementation to support the additional features used for communicating with modem and DSP coprocessors in modern Qualcomm platforms. In addition to this there's support for placing virtio RPMSG buffers in non-System RAM. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZswd9AAoJEAsfOT8Nma3FSigQALyq2wNtf60tkTKW0f2K8cM6 JpnEKAKqO1t5ZkokEevT05PLT4rqXuidrDVOBJ3VQ7LtSqJOiTjGyN9YM8t+WBVG ts0Yg69+wUxBATg9iM2JiUsKn8gH8swUAtf16dmkL/Ca8BJKOHGYTUjADfwuFrEg EAGcLZnlwz736xdo9qZu/Ke9rItz6CDOD3AphbpJbcv7Wj2F/mEuqMZjfLk+1Mo8 C3wXx9jIzW1siNtT0OY9raUNV5gLaGS2zZ+DYwrwjND7Eh0TqeLxyJ1OS7f2kOZ5 P6zcrYw02WbDVW7LUzVegj5PejZm/wdFZ5hSvishEfZZVT+wGHBK34mfObe0moJZ Wp8jFSXKqECIEU3x/2DtPAHi+mittDpQ3FCkElWKJRD6B3HTySHjvM0lOor+zYKB gRX4QUR6jwkSRAbV6GP24Z8SL9kU6TXOSoOQvuzuKhB30JzADJPuQnGZR0SftYiH YJG/RT9FklbljWP9hIgey3QPNHPoL3IzNBU2iyEjdraabmNaFEb++wpLdpCZdnGf 1HMLrMfUmd3QKRtXvT+5Q6CjnfzwFk1ii1naN3Ky9rar3WRtij5bzo8eveR8i9ag DtWA88N9mnpSUao14o5vKN830q3/fkwW6HyT0nCd59Wq94cMNfiMdFPVXvO0xVAF mjP4JXHbqNhdJySWR4Du =PVGN -----END PGP SIGNATURE----- Merge tag 'rpmsg-v4.14' of git://github.com/andersson/remoteproc Pull rpmsg updates from Bjorn Andersson: "This extends the Qualcomm GLINK implementation to support the additional features used for communicating with modem and DSP coprocessors in modern Qualcomm platforms. In addition to this there's support for placing virtio RPMSG buffers in non-System RAM" * tag 'rpmsg-v4.14' of git://github.com/andersson/remoteproc: (29 commits) rpmsg: glink: initialize ret to zero to ensure error status check is correct rpmsg: glink: fix null pointer dereference on a null intent dt-bindings: soc: qcom: Extend GLINK to cover SMEM remoteproc: qcom: adsp: Allow defining GLINK edge rpmsg: glink: Export symbols from common code rpmsg: glink: Release idr lock before returning on error rpmsg: glink: Handle remote rx done command rpmsg: glink: Request for intents when unavailable rpmsg: glink: Use the intents passed by remote rpmsg: glink: Receive and store the remote intent buffers rpmsg: glink: Add announce_create ops and preallocate intents rpmsg: glink: Add rx done command rpmsg: glink: Make RX FIFO peak accessor to take an offset rpmsg: glink: Use the local intents when receiving data rpmsg: glink: Add support for TX intents rpmsg: glink: Fix idr_lock from mutex to spinlock rpmsg: glink: Add support for transport version negotiation rpmsg: glink: Introduce glink smem based transport rpmsg: glink: Do a mbox_free_channel in remove rpmsg: glink: Return -EAGAIN when there is no FIFO space ...
This commit is contained in:
commit
7151202b64
@ -63,9 +63,10 @@ on the Qualcomm ADSP Hexagon core.
|
||||
|
||||
|
||||
= SUBNODES
|
||||
The adsp node may have an subnode named "smd-edge" that describes the SMD edge,
|
||||
channels and devices related to the ADSP. See ../soc/qcom/qcom,smd.txt for
|
||||
details on how to describe the SMD edge.
|
||||
The adsp node may have an subnode named either "smd-edge" or "glink-edge" that
|
||||
describes the communication edge, channels and devices related to the ADSP.
|
||||
See ../soc/qcom/qcom,smd.txt and ../soc/qcom/qcom,glink.txt for details on how
|
||||
to describe these.
|
||||
|
||||
|
||||
= EXAMPLE
|
||||
|
@ -90,6 +90,11 @@ the memory regions used by the Hexagon firmware. Each sub-node must contain:
|
||||
Value type: <phandle>
|
||||
Definition: reference to the reserved-memory for the region
|
||||
|
||||
The Hexagon node may also have an subnode named either "smd-edge" or
|
||||
"glink-edge" that describes the communication edge, channels and devices
|
||||
related to the Hexagon. See ../soc/qcom/qcom,smd.txt and
|
||||
../soc/qcom/qcom,glink.txt for details on how to describe these.
|
||||
|
||||
= EXAMPLE
|
||||
The following example describes the resources needed to boot control the
|
||||
Hexagon, as it is found on MSM8974 boards.
|
||||
|
@ -1,11 +1,12 @@
|
||||
Qualcomm RPM GLINK binding
|
||||
Qualcomm GLINK edge binding
|
||||
|
||||
This binding describes the Qualcomm RPM GLINK, a fifo based mechanism for
|
||||
communication with the Resource Power Management system on various Qualcomm
|
||||
platforms.
|
||||
This binding describes a Qualcomm GLINK edge, a fifo based mechanism for
|
||||
communication between subsystem-pairs on various Qualcomm platforms. Two types
|
||||
of edges can be described by the binding; the GLINK RPM edge and a SMEM based
|
||||
edge.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Usage: required for glink-rpm
|
||||
Value type: <stringlist>
|
||||
Definition: must be "qcom,glink-rpm"
|
||||
|
||||
@ -16,7 +17,7 @@ platforms.
|
||||
signal this processor about communication related events
|
||||
|
||||
- qcom,rpm-msg-ram:
|
||||
Usage: required
|
||||
Usage: required for glink-rpm
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: handle to RPM message memory resource
|
||||
|
||||
|
@ -92,6 +92,7 @@ config QCOM_ADSP_PIL
|
||||
depends on OF && ARCH_QCOM
|
||||
depends on QCOM_SMEM
|
||||
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
|
||||
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
|
||||
select MFD_SYSCON
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_RPROC_COMMON
|
||||
|
@ -72,6 +72,7 @@ struct qcom_adsp {
|
||||
void *mem_region;
|
||||
size_t mem_size;
|
||||
|
||||
struct qcom_rproc_glink glink_subdev;
|
||||
struct qcom_rproc_subdev smd_subdev;
|
||||
struct qcom_rproc_ssr ssr_subdev;
|
||||
};
|
||||
@ -400,6 +401,7 @@ static int adsp_probe(struct platform_device *pdev)
|
||||
goto free_rproc;
|
||||
}
|
||||
|
||||
qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
|
||||
qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
|
||||
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
|
||||
|
||||
@ -422,6 +424,7 @@ static int adsp_remove(struct platform_device *pdev)
|
||||
qcom_smem_state_put(adsp->state);
|
||||
rproc_del(adsp->rproc);
|
||||
|
||||
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
|
||||
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
|
||||
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
|
||||
rproc_free(adsp->rproc);
|
||||
|
@ -20,11 +20,13 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/rpmsg/qcom_glink.h>
|
||||
#include <linux/rpmsg/qcom_smd.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
#include "qcom_common.h"
|
||||
|
||||
#define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
|
||||
#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
|
||||
#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
|
||||
|
||||
@ -49,6 +51,53 @@ struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table);
|
||||
|
||||
static int glink_subdev_probe(struct rproc_subdev *subdev)
|
||||
{
|
||||
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
|
||||
|
||||
glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
|
||||
|
||||
return IS_ERR(glink->edge) ? PTR_ERR(glink->edge) : 0;
|
||||
}
|
||||
|
||||
static void glink_subdev_remove(struct rproc_subdev *subdev)
|
||||
{
|
||||
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
|
||||
|
||||
qcom_glink_smem_unregister(glink->edge);
|
||||
glink->edge = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
|
||||
* @rproc: rproc handle to parent the subdevice
|
||||
* @glink: reference to a GLINK subdev context
|
||||
*/
|
||||
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
|
||||
glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
|
||||
if (!glink->node)
|
||||
return;
|
||||
|
||||
glink->dev = dev;
|
||||
rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
|
||||
|
||||
/**
|
||||
* qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
|
||||
* @rproc: rproc handle
|
||||
* @glink: reference to a GLINK subdev context
|
||||
*/
|
||||
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
|
||||
{
|
||||
rproc_remove_subdev(rproc, &glink->subdev);
|
||||
of_node_put(glink->node);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
|
||||
|
||||
static int smd_subdev_probe(struct rproc_subdev *subdev)
|
||||
{
|
||||
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
|
||||
|
@ -4,6 +4,14 @@
|
||||
#include <linux/remoteproc.h>
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
struct qcom_rproc_glink {
|
||||
struct rproc_subdev subdev;
|
||||
|
||||
struct device *dev;
|
||||
struct device_node *node;
|
||||
struct qcom_glink *edge;
|
||||
};
|
||||
|
||||
struct qcom_rproc_subdev {
|
||||
struct rproc_subdev subdev;
|
||||
|
||||
@ -22,6 +30,9 @@ struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
|
||||
const struct firmware *fw,
|
||||
int *tablesz);
|
||||
|
||||
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
|
||||
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
|
||||
|
||||
void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
|
||||
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
|
||||
|
||||
|
@ -13,9 +13,13 @@ config RPMSG_CHAR
|
||||
in /dev. They make it possible for user-space programs to send and
|
||||
receive rpmsg packets.
|
||||
|
||||
config RPMSG_QCOM_GLINK_NATIVE
|
||||
tristate
|
||||
select RPMSG
|
||||
|
||||
config RPMSG_QCOM_GLINK_RPM
|
||||
tristate "Qualcomm RPM Glink driver"
|
||||
select RPMSG
|
||||
select RPMSG_QCOM_GLINK_NATIVE
|
||||
depends on HAS_IOMEM
|
||||
depends on MAILBOX
|
||||
help
|
||||
@ -23,6 +27,16 @@ config RPMSG_QCOM_GLINK_RPM
|
||||
which serves as a channel for communication with the RPM in GLINK
|
||||
enabled systems.
|
||||
|
||||
config RPMSG_QCOM_GLINK_SMEM
|
||||
tristate "Qualcomm SMEM Glink driver"
|
||||
select RPMSG_QCOM_GLINK_NATIVE
|
||||
depends on MAILBOX
|
||||
depends on QCOM_SMEM
|
||||
help
|
||||
Say y here to enable support for the GLINK SMEM communication driver,
|
||||
which provides support for using the GLINK communication protocol
|
||||
over SMEM.
|
||||
|
||||
config RPMSG_QCOM_SMD
|
||||
tristate "Qualcomm Shared Memory Driver (SMD)"
|
||||
depends on QCOM_SMEM
|
||||
|
@ -1,5 +1,7 @@
|
||||
obj-$(CONFIG_RPMSG) += rpmsg_core.o
|
||||
obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o
|
||||
obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o
|
||||
obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o
|
||||
|
1612
drivers/rpmsg/qcom_glink_native.c
Normal file
1612
drivers/rpmsg/qcom_glink_native.c
Normal file
File diff suppressed because it is too large
Load Diff
45
drivers/rpmsg/qcom_glink_native.h
Normal file
45
drivers/rpmsg/qcom_glink_native.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Linaro Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __QCOM_GLINK_NATIVE_H__
|
||||
#define __QCOM_GLINK_NATIVE_H__
|
||||
|
||||
#define GLINK_FEATURE_INTENT_REUSE BIT(0)
|
||||
#define GLINK_FEATURE_MIGRATION BIT(1)
|
||||
#define GLINK_FEATURE_TRACER_PKT BIT(2)
|
||||
|
||||
struct qcom_glink_pipe {
|
||||
size_t length;
|
||||
|
||||
size_t (*avail)(struct qcom_glink_pipe *glink_pipe);
|
||||
|
||||
void (*peak)(struct qcom_glink_pipe *glink_pipe, void *data,
|
||||
unsigned int offset, size_t count);
|
||||
void (*advance)(struct qcom_glink_pipe *glink_pipe, size_t count);
|
||||
|
||||
void (*write)(struct qcom_glink_pipe *glink_pipe,
|
||||
const void *hdr, size_t hlen,
|
||||
const void *data, size_t dlen);
|
||||
};
|
||||
|
||||
struct qcom_glink;
|
||||
|
||||
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);
|
||||
void qcom_glink_native_remove(struct qcom_glink *glink);
|
||||
|
||||
void qcom_glink_native_unregister(struct qcom_glink *glink);
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
316
drivers/rpmsg/qcom_glink_smem.c
Normal file
316
drivers/rpmsg/qcom_glink_smem.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Linaro Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/soc/qcom/smem.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rpmsg.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 SMEM_GLINK_NATIVE_XPRT_FIFO_0 479
|
||||
#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480
|
||||
|
||||
struct glink_smem_pipe {
|
||||
struct qcom_glink_pipe native;
|
||||
|
||||
__le32 *tail;
|
||||
__le32 *head;
|
||||
|
||||
void *fifo;
|
||||
|
||||
int remote_pid;
|
||||
};
|
||||
|
||||
#define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native)
|
||||
|
||||
static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_pipe(np);
|
||||
size_t len;
|
||||
void *fifo;
|
||||
u32 head;
|
||||
u32 tail;
|
||||
|
||||
if (!pipe->fifo) {
|
||||
fifo = qcom_smem_get(pipe->remote_pid,
|
||||
SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len);
|
||||
if (IS_ERR(fifo)) {
|
||||
pr_err("failed to acquire RX fifo handle: %ld\n",
|
||||
PTR_ERR(fifo));
|
||||
return 0;
|
||||
}
|
||||
|
||||
pipe->fifo = fifo;
|
||||
pipe->native.length = len;
|
||||
}
|
||||
|
||||
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_smem_rx_peak(struct qcom_glink_pipe *np,
|
||||
void *data, unsigned int offset, size_t count)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_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) {
|
||||
__ioread32_copy(data, pipe->fifo + tail,
|
||||
len / sizeof(u32));
|
||||
}
|
||||
|
||||
if (len != count) {
|
||||
__ioread32_copy(data + len, pipe->fifo,
|
||||
(count - len) / sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
|
||||
size_t count)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_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_smem_tx_avail(struct qcom_glink_pipe *np)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_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_smem_tx_write_one(struct glink_smem_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_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
|
||||
const void *hdr, size_t hlen,
|
||||
const void *data, size_t dlen)
|
||||
{
|
||||
struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe);
|
||||
unsigned int head;
|
||||
|
||||
head = le32_to_cpu(*pipe->head);
|
||||
|
||||
head = glink_smem_tx_write_one(pipe, head, hdr, hlen);
|
||||
head = glink_smem_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;
|
||||
|
||||
*pipe->head = cpu_to_le32(head);
|
||||
}
|
||||
|
||||
static void qcom_glink_smem_release(struct device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
struct qcom_glink *qcom_glink_smem_register(struct device *parent,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct glink_smem_pipe *rx_pipe;
|
||||
struct glink_smem_pipe *tx_pipe;
|
||||
struct qcom_glink *glink;
|
||||
struct device *dev;
|
||||
u32 remote_pid;
|
||||
__le32 *descs;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->parent = parent;
|
||||
dev->of_node = node;
|
||||
dev->release = qcom_glink_smem_release;
|
||||
dev_set_name(dev, "%s:%s", node->parent->name, node->name);
|
||||
ret = device_register(dev);
|
||||
if (ret) {
|
||||
pr_err("failed to register glink edge\n");
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = qcom_smem_alloc(remote_pid,
|
||||
SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32);
|
||||
if (ret && ret != -EEXIST) {
|
||||
dev_err(dev, "failed to allocate glink descriptors\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
descs = qcom_smem_get(remote_pid,
|
||||
SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size);
|
||||
if (IS_ERR(descs)) {
|
||||
dev_err(dev, "failed to acquire xprt descriptor\n");
|
||||
ret = PTR_ERR(descs);
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
if (size != 32) {
|
||||
dev_err(dev, "glink descriptor of invalid size\n");
|
||||
ret = -EINVAL;
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
tx_pipe->tail = &descs[0];
|
||||
tx_pipe->head = &descs[1];
|
||||
rx_pipe->tail = &descs[2];
|
||||
rx_pipe->head = &descs[3];
|
||||
|
||||
ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
|
||||
SZ_16K);
|
||||
if (ret && ret != -EEXIST) {
|
||||
dev_err(dev, "failed to allocate TX fifo\n");
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
|
||||
&tx_pipe->native.length);
|
||||
if (IS_ERR(tx_pipe->fifo)) {
|
||||
dev_err(dev, "failed to acquire TX fifo\n");
|
||||
ret = PTR_ERR(tx_pipe->fifo);
|
||||
goto err_put_dev;
|
||||
}
|
||||
|
||||
rx_pipe->native.avail = glink_smem_rx_avail;
|
||||
rx_pipe->native.peak = glink_smem_rx_peak;
|
||||
rx_pipe->native.advance = glink_smem_rx_advance;
|
||||
rx_pipe->remote_pid = remote_pid;
|
||||
|
||||
tx_pipe->native.avail = glink_smem_tx_avail;
|
||||
tx_pipe->native.write = glink_smem_tx_write;
|
||||
tx_pipe->remote_pid = remote_pid;
|
||||
|
||||
*rx_pipe->tail = 0;
|
||||
*tx_pipe->head = 0;
|
||||
|
||||
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_put_dev;
|
||||
}
|
||||
|
||||
return glink;
|
||||
|
||||
err_put_dev:
|
||||
put_device(dev);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_smem_register);
|
||||
|
||||
void qcom_glink_smem_unregister(struct qcom_glink *glink)
|
||||
{
|
||||
qcom_glink_native_remove(glink);
|
||||
qcom_glink_native_unregister(glink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister);
|
||||
|
||||
MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
|
||||
MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1368,6 +1368,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
|
||||
|
||||
edge->dev.parent = parent;
|
||||
edge->dev.release = qcom_smd_edge_release;
|
||||
edge->dev.of_node = node;
|
||||
edge->dev.groups = qcom_smd_edge_groups;
|
||||
dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
|
||||
ret = device_register(&edge->dev);
|
||||
|
@ -45,6 +45,7 @@
|
||||
* @rbufs: kernel address of rx buffers
|
||||
* @sbufs: kernel address of tx buffers
|
||||
* @num_bufs: total number of buffers for rx and tx
|
||||
* @buf_size: size of one rx or tx buffer
|
||||
* @last_sbuf: index of last tx buffer used
|
||||
* @bufs_dma: dma base addr of the buffers
|
||||
* @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders.
|
||||
@ -65,6 +66,7 @@ struct virtproc_info {
|
||||
struct virtqueue *rvq, *svq;
|
||||
void *rbufs, *sbufs;
|
||||
unsigned int num_bufs;
|
||||
unsigned int buf_size;
|
||||
int last_sbuf;
|
||||
dma_addr_t bufs_dma;
|
||||
struct mutex tx_lock;
|
||||
@ -158,7 +160,7 @@ struct virtio_rpmsg_channel {
|
||||
* processor.
|
||||
*/
|
||||
#define MAX_RPMSG_NUM_BUFS (512)
|
||||
#define RPMSG_BUF_SIZE (512)
|
||||
#define MAX_RPMSG_BUF_SIZE (512)
|
||||
|
||||
/*
|
||||
* Local addresses are dynamically allocated on-demand.
|
||||
@ -192,6 +194,28 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
|
||||
.trysend_offchannel = virtio_rpmsg_trysend_offchannel,
|
||||
};
|
||||
|
||||
/**
|
||||
* rpmsg_sg_init - initialize scatterlist according to cpu address location
|
||||
* @sg: scatterlist to fill
|
||||
* @cpu_addr: virtual address of the buffer
|
||||
* @len: buffer length
|
||||
*
|
||||
* An internal function filling scatterlist according to virtual address
|
||||
* location (in vmalloc or in kernel).
|
||||
*/
|
||||
static void
|
||||
rpmsg_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len)
|
||||
{
|
||||
if (is_vmalloc_addr(cpu_addr)) {
|
||||
sg_init_table(sg, 1);
|
||||
sg_set_page(sg, vmalloc_to_page(cpu_addr), len,
|
||||
offset_in_page(cpu_addr));
|
||||
} else {
|
||||
WARN_ON(!virt_addr_valid(cpu_addr));
|
||||
sg_init_one(sg, cpu_addr, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __ept_release() - deallocate an rpmsg endpoint
|
||||
* @kref: the ept's reference count
|
||||
@ -435,7 +459,7 @@ static void *get_a_tx_buf(struct virtproc_info *vrp)
|
||||
* (half of our buffers are used for sending messages)
|
||||
*/
|
||||
if (vrp->last_sbuf < vrp->num_bufs / 2)
|
||||
ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++;
|
||||
ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++;
|
||||
/* or recycle a used one */
|
||||
else
|
||||
ret = virtqueue_get_buf(vrp->svq, &len);
|
||||
@ -561,7 +585,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
|
||||
* messaging), or to improve the buffer allocator, to support
|
||||
* variable-length buffer sizes.
|
||||
*/
|
||||
if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) {
|
||||
if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) {
|
||||
dev_err(dev, "message is too big (%d)\n", len);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
@ -610,7 +634,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
|
||||
msg, sizeof(*msg) + msg->len, true);
|
||||
#endif
|
||||
|
||||
sg_init_one(&sg, msg, sizeof(*msg) + len);
|
||||
rpmsg_sg_init(&sg, msg, sizeof(*msg) + len);
|
||||
|
||||
mutex_lock(&vrp->tx_lock);
|
||||
|
||||
@ -632,7 +656,6 @@ out:
|
||||
mutex_unlock(&vrp->tx_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
|
||||
|
||||
static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
|
||||
{
|
||||
@ -702,7 +725,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
|
||||
* We currently use fixed-sized buffers, so trivially sanitize
|
||||
* the reported payload length.
|
||||
*/
|
||||
if (len > RPMSG_BUF_SIZE ||
|
||||
if (len > vrp->buf_size ||
|
||||
msg->len > (len - sizeof(struct rpmsg_hdr))) {
|
||||
dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
|
||||
return -EINVAL;
|
||||
@ -735,7 +758,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
|
||||
dev_warn(dev, "msg received with no recipient\n");
|
||||
|
||||
/* publish the real size of the buffer */
|
||||
sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
|
||||
rpmsg_sg_init(&sg, msg, vrp->buf_size);
|
||||
|
||||
/* add the buffer back to the remote processor's virtqueue */
|
||||
err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
|
||||
@ -892,7 +915,9 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
||||
else
|
||||
vrp->num_bufs = MAX_RPMSG_NUM_BUFS;
|
||||
|
||||
total_buf_space = vrp->num_bufs * RPMSG_BUF_SIZE;
|
||||
vrp->buf_size = MAX_RPMSG_BUF_SIZE;
|
||||
|
||||
total_buf_space = vrp->num_bufs * vrp->buf_size;
|
||||
|
||||
/* allocate coherent memory for the buffers */
|
||||
bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,
|
||||
@ -915,9 +940,9 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
||||
/* set up the receive buffers */
|
||||
for (i = 0; i < vrp->num_bufs / 2; i++) {
|
||||
struct scatterlist sg;
|
||||
void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE;
|
||||
void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
|
||||
|
||||
sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE);
|
||||
rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);
|
||||
|
||||
err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
|
||||
GFP_KERNEL);
|
||||
@ -982,7 +1007,7 @@ static int rpmsg_remove_device(struct device *dev, void *data)
|
||||
static void rpmsg_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtproc_info *vrp = vdev->priv;
|
||||
size_t total_buf_space = vrp->num_bufs * RPMSG_BUF_SIZE;
|
||||
size_t total_buf_space = vrp->num_bufs * vrp->buf_size;
|
||||
int ret;
|
||||
|
||||
vdev->config->reset(vdev);
|
||||
|
27
include/linux/rpmsg/qcom_glink.h
Normal file
27
include/linux/rpmsg/qcom_glink.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _LINUX_RPMSG_QCOM_GLINK_H
|
||||
#define _LINUX_RPMSG_QCOM_GLINK_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
struct qcom_glink;
|
||||
|
||||
#if IS_ENABLED(CONFIG_RPMSG_QCOM_GLINK_SMEM)
|
||||
|
||||
struct qcom_glink *qcom_glink_smem_register(struct device *parent,
|
||||
struct device_node *node);
|
||||
void qcom_glink_smem_unregister(struct qcom_glink *glink);
|
||||
|
||||
#else
|
||||
|
||||
static inline struct qcom_glink *
|
||||
qcom_glink_smem_register(struct device *parent,
|
||||
struct device_node *node)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void qcom_glink_smem_unregister(struct qcom_glink *glink) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user