Qualcomm ARM Based Driver Updates for v4.15 Part 2
* Add Qualcomm Remote Filesystem Memory driver * Add OF linkage for RMTFS -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZ7G5TAAoJEFKiBbHx2RXVhYgP/23hFR610Nb/00Zdukt8pRAv 2Q54WO8oWl0CKjY6ohkHhOCwHFIp6Td/DHcg01yw1jTD7gnRJEFZleMX4/SJOWjX eIui9+sztCI3MpQbWOqCGmfSVGwlCmivto2fyyJfqQaKUTYfs4CtqSGFw5t01TH2 /fnvP1z06Qa3mPk38La8vmxGdQCF7O97O0RceWxFNyiOEhksLZUOktkC/DEfupzU DjKbcgDIr7GGok7mdP26mRMGLcqtkIV+ZdQsIyx6JTMm7tC7Yq6jNn7e2iwMTDnt aYJQ3iecWMoa9XV9+jG4OzR0aMl1IzFy75BewJIsSKOMt6DhlN/wxF7HNpnxnIMZ HmtXgrqDM4k8yIyG7Gp5C152N6vK5cNXGuJSfJliw+cMtGC2ikBu/aQkCU9H5+gh wa0pcYKxdBdImhJfj+bC5SmoJ3ERUKH3neH90aOTfsUZilGVAwrJ72eGT8zchbB1 R9oHlez4RIGsM1gx0DyK8stqYdYrrelML43qsBsbC9OiwgCJY9Y5ObykMjO+s3Oa zSxn1f7b4LxgdnQnzRgcJIMAKlR6zv/vUI7+TGRWe3lrcMJAEqx2dmxI6k+2yatG ijRAyS2SBaK4aVb5Qk1dBUGRD9isEyD6aqyr8TAl1kLLYm5x4XKqLXtmC0NLU1ee 3Kw/ohbcBRlPlaZzWs9v =E/Ae -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-4.15-2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/agross/linux into next/drivers Pull "Qualcomm ARM Based Driver Updates for v4.15 Part 2" from Andy Gross: * Add Qualcomm Remote Filesystem Memory driver * Add OF linkage for RMTFS * tag 'qcom-drivers-for-4.15-2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/agross/linux: soc: qcom: Remote filesystem memory driver dt-binding: soc: qcom: Add binding for rmtfs memory of: reserved_mem: Accessor for acquiring reserved_mem of/platform: Generalize /reserved-memory handling
This commit is contained in:
Коммит
ae1d77fce7
|
@ -0,0 +1,51 @@
|
|||
Qualcomm Remote File System Memory binding
|
||||
|
||||
This binding describes the Qualcomm remote filesystem memory, which serves the
|
||||
purpose of describing the shared memory region used for remote processors to
|
||||
access block device data using the Remote Filesystem protocol.
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: must be:
|
||||
"qcom,rmtfs-mem"
|
||||
|
||||
- reg:
|
||||
Usage: required for static allocation
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: must specify base address and size of the memory region,
|
||||
as described in reserved-memory.txt
|
||||
|
||||
- size:
|
||||
Usage: required for dynamic allocation
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: must specify a size of the memory region, as described in
|
||||
reserved-memory.txt
|
||||
|
||||
- qcom,client-id:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: identifier of the client to use this region for buffers.
|
||||
|
||||
- qcom,vmid:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: vmid of the remote processor, to set up memory protection.
|
||||
|
||||
= EXAMPLE
|
||||
The following example shows the remote filesystem memory setup for APQ8016,
|
||||
with the rmtfs region for the Hexagon DSP (id #1) located at 0x86700000.
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
rmtfs@86700000 {
|
||||
compatible = "qcom,rmtfs-mem";
|
||||
reg = <0x0 0x86700000 0x0 0xe0000>;
|
||||
no-map;
|
||||
|
||||
qcom,client-id = <1>;
|
||||
};
|
||||
};
|
|
@ -397,3 +397,29 @@ void of_reserved_mem_device_release(struct device *dev)
|
|||
rmem->ops->device_release(rmem, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reserved_mem_device_release);
|
||||
|
||||
/**
|
||||
* of_reserved_mem_lookup() - acquire reserved_mem from a device node
|
||||
* @np: node pointer of the desired reserved-memory region
|
||||
*
|
||||
* This function allows drivers to acquire a reference to the reserved_mem
|
||||
* struct based on a device node handle.
|
||||
*
|
||||
* Returns a reserved_mem reference, or NULL on error.
|
||||
*/
|
||||
struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
|
||||
{
|
||||
const char *name;
|
||||
int i;
|
||||
|
||||
if (!np->full_name)
|
||||
return NULL;
|
||||
|
||||
name = kbasename(np->full_name);
|
||||
for (i = 0; i < reserved_mem_count; i++)
|
||||
if (!strcmp(reserved_mem[i].name, name))
|
||||
return &reserved_mem[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_reserved_mem_lookup);
|
||||
|
|
|
@ -497,6 +497,12 @@ int of_platform_default_populate(struct device_node *root,
|
|||
EXPORT_SYMBOL_GPL(of_platform_default_populate);
|
||||
|
||||
#ifndef CONFIG_PPC
|
||||
static const struct of_device_id reserved_mem_matches[] = {
|
||||
{ .compatible = "qcom,rmtfs-mem" },
|
||||
{ .compatible = "ramoops" },
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init of_platform_default_populate_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
@ -505,15 +511,12 @@ static int __init of_platform_default_populate_init(void)
|
|||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Handle ramoops explicitly, since it is inside /reserved-memory,
|
||||
* which lacks a "compatible" property.
|
||||
* Handle certain compatibles explicitly, since we don't want to create
|
||||
* platform_devices for every node in /reserved-memory with a
|
||||
* "compatible",
|
||||
*/
|
||||
node = of_find_node_by_path("/reserved-memory");
|
||||
if (node) {
|
||||
node = of_find_compatible_node(node, NULL, "ramoops");
|
||||
if (node)
|
||||
of_platform_device_create(node, NULL, NULL);
|
||||
}
|
||||
for_each_matching_node(node, reserved_mem_matches)
|
||||
of_platform_device_create(node, NULL, NULL);
|
||||
|
||||
/* Populate everything else. */
|
||||
of_platform_default_populate(NULL, NULL, NULL);
|
||||
|
|
|
@ -35,6 +35,17 @@ config QCOM_PM
|
|||
modes. It interface with various system drivers to put the cores in
|
||||
low power modes.
|
||||
|
||||
config QCOM_RMTFS_MEM
|
||||
tristate "Qualcomm Remote Filesystem memory driver"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
The Qualcomm remote filesystem memory driver is used for allocating
|
||||
and exposing regions of shared memory with remote processors for the
|
||||
purpose of exchanging sector-data between the remote filesystem
|
||||
service and its clients.
|
||||
|
||||
Say y here if you intend to boot the modem remoteproc.
|
||||
|
||||
config QCOM_SMEM
|
||||
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
||||
depends on ARCH_QCOM
|
||||
|
|
|
@ -2,6 +2,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
|
|||
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
|
||||
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
|
||||
obj-$(CONFIG_QCOM_PM) += spm.o
|
||||
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
|
||||
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
|
||||
obj-$(CONFIG_QCOM_SMEM) += smem.o
|
||||
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
|
||||
#define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
|
||||
|
||||
static dev_t qcom_rmtfs_mem_major;
|
||||
|
||||
struct qcom_rmtfs_mem {
|
||||
struct device dev;
|
||||
struct cdev cdev;
|
||||
|
||||
void *base;
|
||||
phys_addr_t addr;
|
||||
phys_addr_t size;
|
||||
|
||||
unsigned int client_id;
|
||||
};
|
||||
|
||||
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static DEVICE_ATTR(phys_addr, 0400, qcom_rmtfs_mem_show, NULL);
|
||||
static DEVICE_ATTR(size, 0400, qcom_rmtfs_mem_show, NULL);
|
||||
static DEVICE_ATTR(client_id, 0400, qcom_rmtfs_mem_show, NULL);
|
||||
|
||||
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
|
||||
struct qcom_rmtfs_mem,
|
||||
dev);
|
||||
|
||||
if (attr == &dev_attr_phys_addr)
|
||||
return sprintf(buf, "%pa\n", &rmtfs_mem->addr);
|
||||
if (attr == &dev_attr_size)
|
||||
return sprintf(buf, "%pa\n", &rmtfs_mem->size);
|
||||
if (attr == &dev_attr_client_id)
|
||||
return sprintf(buf, "%d\n", rmtfs_mem->client_id);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct attribute *qcom_rmtfs_mem_attrs[] = {
|
||||
&dev_attr_phys_addr.attr,
|
||||
&dev_attr_size.attr,
|
||||
&dev_attr_client_id.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(qcom_rmtfs_mem);
|
||||
|
||||
static int qcom_rmtfs_mem_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = container_of(inode->i_cdev,
|
||||
struct qcom_rmtfs_mem,
|
||||
cdev);
|
||||
|
||||
get_device(&rmtfs_mem->dev);
|
||||
filp->private_data = rmtfs_mem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static ssize_t qcom_rmtfs_mem_read(struct file *filp,
|
||||
char __user *buf, size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
|
||||
|
||||
if (*f_pos >= rmtfs_mem->size)
|
||||
return 0;
|
||||
|
||||
if (*f_pos + count >= rmtfs_mem->size)
|
||||
count = rmtfs_mem->size - *f_pos;
|
||||
|
||||
if (copy_to_user(buf, rmtfs_mem->base + *f_pos, count))
|
||||
return -EFAULT;
|
||||
|
||||
*f_pos += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t qcom_rmtfs_mem_write(struct file *filp,
|
||||
const char __user *buf, size_t count,
|
||||
loff_t *f_pos)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
|
||||
|
||||
if (*f_pos >= rmtfs_mem->size)
|
||||
return 0;
|
||||
|
||||
if (*f_pos + count >= rmtfs_mem->size)
|
||||
count = rmtfs_mem->size - *f_pos;
|
||||
|
||||
if (copy_from_user(rmtfs_mem->base + *f_pos, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
*f_pos += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
|
||||
|
||||
put_device(&rmtfs_mem->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations qcom_rmtfs_mem_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = qcom_rmtfs_mem_open,
|
||||
.read = qcom_rmtfs_mem_read,
|
||||
.write = qcom_rmtfs_mem_write,
|
||||
.release = qcom_rmtfs_mem_release,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static void qcom_rmtfs_mem_release_device(struct device *dev)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
|
||||
struct qcom_rmtfs_mem,
|
||||
dev);
|
||||
|
||||
kfree(rmtfs_mem);
|
||||
}
|
||||
|
||||
static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct reserved_mem *rmem;
|
||||
struct qcom_rmtfs_mem *rmtfs_mem;
|
||||
u32 client_id;
|
||||
int ret;
|
||||
|
||||
rmem = of_reserved_mem_lookup(node);
|
||||
if (!rmem) {
|
||||
dev_err(&pdev->dev, "failed to acquire memory region\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,client-id", &client_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to parse \"qcom,client-id\"\n");
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
rmtfs_mem = kzalloc(sizeof(*rmtfs_mem), GFP_KERNEL);
|
||||
if (!rmtfs_mem)
|
||||
return -ENOMEM;
|
||||
|
||||
rmtfs_mem->addr = rmem->base;
|
||||
rmtfs_mem->client_id = client_id;
|
||||
rmtfs_mem->size = rmem->size;
|
||||
|
||||
device_initialize(&rmtfs_mem->dev);
|
||||
rmtfs_mem->dev.parent = &pdev->dev;
|
||||
rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
|
||||
|
||||
rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
|
||||
rmtfs_mem->size, MEMREMAP_WC);
|
||||
if (IS_ERR(rmtfs_mem->base)) {
|
||||
dev_err(&pdev->dev, "failed to remap rmtfs_mem region\n");
|
||||
ret = PTR_ERR(rmtfs_mem->base);
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
cdev_init(&rmtfs_mem->cdev, &qcom_rmtfs_mem_fops);
|
||||
rmtfs_mem->cdev.owner = THIS_MODULE;
|
||||
|
||||
dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id);
|
||||
rmtfs_mem->dev.id = client_id;
|
||||
rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id);
|
||||
|
||||
ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add cdev: %d\n", ret);
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, rmtfs_mem);
|
||||
|
||||
return 0;
|
||||
|
||||
remove_cdev:
|
||||
cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
|
||||
put_device:
|
||||
put_device(&rmtfs_mem->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
|
||||
put_device(&rmtfs_mem->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_rmtfs_mem_of_match[] = {
|
||||
{ .compatible = "qcom,rmtfs-mem" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match);
|
||||
|
||||
static struct platform_driver qcom_rmtfs_mem_driver = {
|
||||
.probe = qcom_rmtfs_mem_probe,
|
||||
.remove = qcom_rmtfs_mem_remove,
|
||||
.driver = {
|
||||
.name = "qcom_rmtfs_mem",
|
||||
.of_match_table = qcom_rmtfs_mem_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int qcom_rmtfs_mem_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0,
|
||||
QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem");
|
||||
if (ret < 0) {
|
||||
pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_driver_register(&qcom_rmtfs_mem_driver);
|
||||
if (ret < 0) {
|
||||
pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
|
||||
unregister_chrdev_region(qcom_rmtfs_mem_major,
|
||||
QCOM_RMTFS_MEM_DEV_MAX);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(qcom_rmtfs_mem_init);
|
||||
|
||||
static void qcom_rmtfs_mem_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qcom_rmtfs_mem_driver);
|
||||
unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX);
|
||||
}
|
||||
module_exit(qcom_rmtfs_mem_exit);
|
|
@ -44,6 +44,7 @@ int early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
|
|||
void fdt_init_reserved_mem(void);
|
||||
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size);
|
||||
struct reserved_mem *of_reserved_mem_lookup(struct device_node *np);
|
||||
#else
|
||||
static inline int of_reserved_mem_device_init_by_idx(struct device *dev,
|
||||
struct device_node *np, int idx)
|
||||
|
@ -55,6 +56,10 @@ static inline void of_reserved_mem_device_release(struct device *pdev) { }
|
|||
static inline void fdt_init_reserved_mem(void) { }
|
||||
static inline void fdt_reserved_mem_save_node(unsigned long node,
|
||||
const char *uname, phys_addr_t base, phys_addr_t size) { }
|
||||
static inline struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче