Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61555831 authored by Lina Iyer's avatar Lina Iyer
Browse files

drivers: qcom: cmd-db: add Command DB driver



Command DB is a dynamic device database available in the shared memory.
The database houses the addresses and configuration information of the
hardened resource blocks. QTI drivers may query this database to find
the dynamic address of their resource for this SoC and send state
requests to those resources.

This is a snapshot as of 'commit b4a69c05b01172 ("drivers: mailbox:
rpmh-mailbox: rename file to match convention")'.

Change-Id: Iec7cc64146c5c2e214321b47d9068bae745034c9
Signed-off-by: default avatarLina Iyer <ilina@codeaurora.org>
parent 57a76e1e
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
Command DB
---------

Command DB is a database that provides a mapping between resource key and
the resource address for a system resource managed by RPMh. The data is
stored in a shared memory region and is loaded by the remote processor.

Some of the "Qualcomm Technologies Inc" SOC's have hardware accelerators
for controlling shared resources.  The HW accelerator associated to a given
resource could change between builds when accelerators are added/removed.
To remove dependencies between multiple images in a SoC, Command DB allows
drivers to query resource parameters based on predetermined key strings.

The devicetree representation of the command DB driver should be:

PROPERTIES:
- compatible:
	Usage: required
	Value type: <string>
	Definition: Should be "qcom,cmd-db"

- reg:
	Usage: required
	Value type: <prop-encoded-array>
	Definition: First element is the base address of shared memory
		Second element is the size of the shared memory region
		Points to the dictionary address that houses the command DB
		start address and the size of the command DB region

Example:

qcom,cmd-db@861e0000 {
	compatible = "qcom,cmd-db";
	reg = <0xc3f000c 0x8>;
}
+6 −0
Original line number Diff line number Diff line
@@ -298,3 +298,9 @@ config QCOM_BUS_CONFIG_RPMH
	  accelerators. It enables the translation of bandwidth requests
	  from logical nodes to hardware nodes controlled by the BCM (Bus
	  Clock Manager)

config QCOM_COMMAND_DB
	bool "Command DB"
	help
	  Command DB queries shared memory by key string for shared system
	  resources
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
obj-$(CONFIG_MSM_PIL)   +=      peripheral-loader.o
obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o

ifdef CONFIG_MSM_SUBSYSTEM_RESTART
       obj-y += subsystem_notif.o
+416 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 *
 * 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/module.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <soc/qcom/cmd-db.h>

#define RESOURCE_ID_LEN 8
#define NUM_PRIORITY  2
#define MAX_SLV_ID 8
#define CMD_DB_MAGIC 0x0C0330DBUL
#define SLAVE_ID_MASK 0x7
#define SLAVE_ID_SHIFT 16
#define CMD_DB_STANDALONE_MASK BIT(0)

struct entry_header {
	uint64_t res_id;
	u32 priority[NUM_PRIORITY];
	u32 addr;
	u16 len;
	u16 offset;
};

struct rsc_hdr {
	u16  slv_id;
	u16  header_offset;	/* Entry header offset from data  */
	u16  data_offset;	/* Entry offset for data location */
	u16  cnt;	/* Number of entries for HW type */
	u16  version;	/* MSB is Major and LSB is Minor version
			 * identifies the HW type of Aux Data
			 */
	u16 reserved[3];
};

struct cmd_db_header {
	u32 version;
	u32 magic_num;
	struct rsc_hdr header[MAX_SLV_ID];
	u32 check_sum;
	u32 reserved;
	u8 data[];
};

struct cmd_db_entry {
	const char resource_id[RESOURCE_ID_LEN + 1]; /* Unique id per entry */
	const u32 addr; /* TCS Addr Slave ID + Offset address */
	const u32 priority[NUM_PRIORITY]; /* Bitmask for DRV IDs */
	u32       len;                                 /* Aux data len */
	u16       version;
	u8        data[];
};

/* CMD DB QUERY TYPES */
enum cmd_db_query_type {
	CMD_DB_QUERY_RES_ID = 0,
	CMD_DB_QUERY_ADDRESS,
	CMD_DB_QUERY_INVALID,
	CMD_DB_QUERY_MAX = 0x7ffffff,
};

static void __iomem *start_addr;
static struct cmd_db_header *cmd_db_header;
static int cmd_db_status = -EPROBE_DEFER;

static u64 cmd_db_get_u64_id(const char *id)
{
	uint64_t rsc_id = 0;
	uint8_t *ch  = (uint8_t *)&rsc_id;
	int i;

	for (i = 0; ((i < sizeof(rsc_id)) && id[i]); i++)
		ch[i] = id[i];

	return rsc_id;
}

static int cmd_db_get_header(u64 query, struct entry_header *eh,
		struct rsc_hdr *rh, bool use_addr)
{
	struct rsc_hdr *rsc_hdr;
	int i, j;

	if (!cmd_db_header)
		return -EPROBE_DEFER;

	if (!eh || !rh)
		return -EINVAL;

	rsc_hdr = &cmd_db_header->header[0];

	for (i = 0; i < MAX_SLV_ID ; i++, rsc_hdr++) {
		struct entry_header *ent;

		if (!rsc_hdr->slv_id)
			break;

		ent = (struct entry_header *)(start_addr
				+ sizeof(*cmd_db_header)
				+ rsc_hdr->header_offset);

		for (j = 0; j < rsc_hdr->cnt; j++, ent++) {
			if (use_addr) {
				if (ent->addr == (u32)(query))
					break;
			} else if (ent->res_id == query)
				break;
		}

		if (j < rsc_hdr->cnt) {
			memcpy(eh, ent, sizeof(*ent));
			memcpy(rh, &cmd_db_header->header[i], sizeof(*rh));
			return 0;
		}
	}
	return -ENODEV;
}

static int cmd_db_get_header_by_addr(u32 addr,
		struct entry_header *ent_hdr,
		struct rsc_hdr *rsc_hdr)
{
	return cmd_db_get_header((u64)addr, ent_hdr, rsc_hdr, true);
}

static int cmd_db_get_header_by_rsc_id(const char *resource_id,
		struct entry_header *ent_hdr,
		struct rsc_hdr *rsc_hdr)
{
	u64 rsc_id = cmd_db_get_u64_id(resource_id);

	return cmd_db_get_header(rsc_id, ent_hdr, rsc_hdr, false);
}

u32 cmd_db_get_addr(const char *resource_id)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);

	return ret < 0 ? 0 : ent.addr;
}

bool cmd_db_get_priority(u32 addr, u8 drv_id)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	ret = cmd_db_get_header_by_addr(addr, &ent, &rsc_hdr);

	return ret < 0 ? false : (bool)(ent.priority[0] & (1 << drv_id));

}
int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	if (!data)
		return -EINVAL;

	ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);

	if (ret)
		return ret;

	if (ent.len < len)
		return -EINVAL;

	len = (ent.len < len) ? ent.len : len;

	memcpy_fromio(data,
			start_addr + sizeof(*cmd_db_header)
			+ rsc_hdr.data_offset + ent.offset,
			len);
	return len;
}

int cmd_db_get_aux_data_len(const char *resource_id)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);

	return ret < 0 ? 0 : ent.len;
}

u16 cmd_db_get_version(const char *resource_id)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
	return ret < 0 ? 0 : rsc_hdr.version;
}

int cmd_db_ready(void)
{
	return cmd_db_status;
}

int cmd_db_is_standalone(void)
{
	if (cmd_db_status < 0)
		return cmd_db_status;

	return !!(cmd_db_header->reserved & CMD_DB_STANDALONE_MASK);
}

int cmd_db_get_slave_id(const char *resource_id)
{
	int ret;
	struct entry_header ent;
	struct rsc_hdr rsc_hdr;

	ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
	return ret < 0 ? 0 : (ent.addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
}

static void *cmd_db_start(struct seq_file *m, loff_t *pos)
{
	struct cmd_db_header *hdr = m->private;
	int slv_idx, ent_idx;
	struct entry_header *ent;
	int total = 0;

	for (slv_idx = 0; slv_idx < MAX_SLV_ID; slv_idx++) {

		if (!hdr->header[slv_idx].cnt)
			continue;
		ent_idx = *pos - total;
		if (ent_idx < hdr->header[slv_idx].cnt)
			break;

		total += hdr->header[slv_idx].cnt;
	}

	if (slv_idx == MAX_SLV_ID)
		return NULL;

	ent = start_addr + hdr->header[slv_idx].header_offset + sizeof(*hdr);
	return &ent[ent_idx];

}

static void cmd_db_stop(struct seq_file *m, void *v)
{
}

static void *cmd_db_next(struct seq_file *m, void *v, loff_t *pos)
{
	(*pos)++;
	return cmd_db_start(m, pos);
}

static int cmd_db_seq_show(struct seq_file *m, void *v)
{
	struct entry_header *eh = v;
	struct cmd_db_header *hdr = m->private;
	char buf[9]  = {0};

	if (!eh)
		return 0;

	memcpy(buf, &eh->res_id, min(sizeof(eh->res_id), sizeof(buf)));

	seq_printf(m, "Address: 0x%05x, id: %s", eh->addr, buf);

	if (eh->len) {
		int slv_id = (eh->addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
		u8 aux[32] = {0};
		int len;
		int k;

		len = min_t(u32, eh->len, sizeof(aux));

		for (k = 0; k < MAX_SLV_ID; k++) {
			if (hdr->header[k].slv_id == slv_id)
				break;
		}

		if (k == MAX_SLV_ID)
			return -EINVAL;

		memcpy_fromio(aux, start_addr + hdr->header[k].data_offset
			+ eh->offset + sizeof(*cmd_db_header), len);

		seq_puts(m, ", aux data: ");

		for (k = 0; k < len; k++)
			seq_printf(m, "%02x ", aux[k]);

	}
	seq_puts(m, "\n");
	return 0;
}

static const struct seq_operations cmd_db_seq_ops = {
	.start = cmd_db_start,
	.stop = cmd_db_stop,
	.next = cmd_db_next,
	.show = cmd_db_seq_show,
};

static int cmd_db_file_open(struct inode *inode, struct file *file)
{
	int ret = seq_open(file, &cmd_db_seq_ops);
	struct seq_file *s = (struct seq_file *)(file->private_data);

	s->private = inode->i_private;
	return ret;
}

static const struct file_operations cmd_db_fops = {
	.owner = THIS_MODULE,
	.open = cmd_db_file_open,
	.read = seq_read,
	.release = seq_release,
	.llseek = no_llseek,
};

static int cmd_db_dev_probe(struct platform_device *pdev)
{
	struct resource res;
	void __iomem *dict;

	dict = of_iomap(pdev->dev.of_node, 0);
	if (!dict) {
		cmd_db_status = -ENOMEM;
		goto failed;
	}

	/*
	 * Read start address and size of the command DB address from
	 * shared dictionary location
	 */
	res.start = readl_relaxed(dict);
	res.end = res.start + readl_relaxed(dict + 0x4);
	res.flags = IORESOURCE_MEM;
	iounmap(dict);

	start_addr = devm_ioremap_resource(&pdev->dev, &res);

	cmd_db_header = devm_kzalloc(&pdev->dev,
			sizeof(*cmd_db_header), GFP_KERNEL);

	if (!cmd_db_header) {
		cmd_db_status = -ENOMEM;
		goto failed;
	}

	memcpy(cmd_db_header, start_addr, sizeof(*cmd_db_header));

	if (cmd_db_header->magic_num != CMD_DB_MAGIC) {
		pr_err("%s(): Invalid Magic\n", __func__);
		cmd_db_status = -EINVAL;
		goto failed;
	}
	cmd_db_status = 0;
	of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);

	if (!debugfs_create_file("cmd_db", 0444, NULL,
				cmd_db_header, &cmd_db_fops))
		pr_err("Couldn't create debugfs\n");

	if (cmd_db_is_standalone() == 1)
		pr_info("Command DB is initialized in standalone mode.\n");

failed:
	return cmd_db_status;
}

static const struct of_device_id cmd_db_match_table[] = {
	{.compatible = "qcom,cmd-db"},
	{},
};

static struct platform_driver cmd_db_dev_driver = {
	.probe = cmd_db_dev_probe,
	.driver = {
		.name = "cmd-db",
		.owner = THIS_MODULE,
		.of_match_table = cmd_db_match_table,
	},
};

int __init cmd_db_device_init(void)
{
	return platform_driver_register(&cmd_db_dev_driver);
}
arch_initcall(cmd_db_device_init);
+148 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 *
 * 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 COMMAND_DB_H
#define COMMAND_DB_H


enum cmd_db_hw_type {
	CMD_DB_HW_MIN = 3,
	CMD_DB_HW_ARC = CMD_DB_HW_MIN,
	CMD_DB_HW_VRM = 4,
	CMD_DB_HW_BCM = 5,
	CMD_DB_HW_MAX = CMD_DB_HW_BCM,
	CMD_DB_HW_ALL = 0xff,
};
#ifdef CONFIG_QCOM_COMMAND_DB
/**
 * cmd_db_get_addr() - Query command db for resource id address.
 *
 *  This is used to retrieve resource address based on resource
 *  id.
 *
 *  @resource_id : resource id to query for address
 *
 *  @return address on success or 0 on error otherwise
 */
u32 cmd_db_get_addr(const char *resource_id);

/**
 * cmd_db_get_priority() - Query command db for resource address priority
 * from command DB.
 *
 *  This is used to retrieve a command DB entry based resource address.
 *
 *  @addr : resource addr to query for priority.
 *  @drv_id : DRV ID to query resource for priority on.
 *  @type: HW type of ID being queried for faster lookups. Clients could
 *		pass in CMD_DB_HW_ALL if type field is unknown
 *
 *  @return true if priority bit is set for the DRV ID/address
 */
bool cmd_db_get_priority(u32 addr, u8 drv_id);

/**
 * cmd_db_get_aux_data() - Query command db for aux data. This is used to
 * retrieve a command DB entry based resource address.
 *
 *  @resource_id : Resource to retrieve AUX Data on.
 *  @data : Data buffer to copy returned aux data to. Returns size on NULL
 *  @len : Caller provides size of data buffer passed in.
 *
 *  returns size of data on success, errno on error
 */
int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len);

/**
 * cmd_db_get_aux_data_len - Get the length of the auxllary data stored in DB.
 *
 * @resource_id: Resource to retrieve AUX Data.
 *
 * returns size on success, errno on error
 */
int cmd_db_get_aux_data_len(const char *resource_id);

/**
 * cmd_db_get_version - Get the version of the command DB data
 *
 * @resource_id: Resource id to query the DB for version
 *
 * returns version on success, 0 on error.
 *	Major number in MSB, minor number in LSB
 */
u16 cmd_db_get_version(const char *resource_id);

/**
 * cmd_db_ready - Indicates if command DB is probed
 *
 * returns  0 on success , errno otherwise
 */
int cmd_db_ready(void);

/**
 * cmd_db_get_slave_id - Get the slave ID for a given resource address
 *
 * @resource_id: Resource id to query the DB for version
 *
 * return  cmd_db_hw_type enum  on success, errno on error
 */
int cmd_db_get_slave_id(const char *resource_id);

/**
 * cmd_db_is_standalone - Returns if the command DB is standalone
 *
 * return 1 if command DB is standalone, 0 if not, errno otherwise.
 */
int cmd_db_is_standalone(void);
#else

static inline u32 cmd_db_get_addr(const char *resource_id)
{
	return 0;
}

bool cmd_db_get_priority(u32 addr, u8 drv_id)
{
	return false;
}

int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len)
{
	return -ENODEV;
}

int cmd_db_get_aux_data_len(const char *resource_id)
{
	return -ENODEV;
}

u16 cmd_db_get_version(const char *resource_id)
{
	return 0;
}

int cmd_db_ready(void)
{
	return -ENODEV;
}

int cmd_db_get_slave_id(const char *resource_id)
{
	return -ENODEV;
}

int cmd_db_is_standalone(void)
{
	return -ENODEV;
}
#endif
#endif