1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
|
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <soc/qcom/cmd-db.h>
#define NUM_PRIORITY 2
#define MAX_SLV_ID 8
#define SLAVE_ID_MASK 0x7
#define SLAVE_ID_SHIFT 16
/**
* struct entry_header: header for each entry in cmddb
*
* @id: resource's identifier
* @priority: unused
* @addr: the address of the resource
* @len: length of the data
* @offset: offset from :@data_offset, start of the data
*/
struct entry_header {
u8 id[8];
__le32 priority[NUM_PRIORITY];
__le32 addr;
__le16 len;
__le16 offset;
};
/**
* struct rsc_hdr: resource header information
*
* @slv_id: id for the resource
* @header_offset: entry's header at offset from the end of the cmd_db_header
* @data_offset: entry's data at offset from the end of the cmd_db_header
* @cnt: number of entries for HW type
* @version: MSB is major, LSB is minor
* @reserved: reserved for future use.
*/
struct rsc_hdr {
__le16 slv_id;
__le16 header_offset;
__le16 data_offset;
__le16 cnt;
__le16 version;
__le16 reserved[3];
};
/**
* struct cmd_db_header: The DB header information
*
* @version: The cmd db version
* @magic: constant expected in the database
* @header: array of resources
* @checksum: checksum for the header. Unused.
* @reserved: reserved memory
* @data: driver specific data
*/
struct cmd_db_header {
__le32 version;
u8 magic[4];
struct rsc_hdr header[MAX_SLV_ID];
__le32 checksum;
__le32 reserved;
u8 data[];
};
/**
* DOC: Description of the Command DB database.
*
* At the start of the command DB memory is the cmd_db_header structure.
* The cmd_db_header holds the version, checksum, magic key as well as an
* array for header for each slave (depicted by the rsc_header). Each h/w
* based accelerator is a 'slave' (shared resource) and has slave id indicating
* the type of accelerator. The rsc_header is the header for such individual
* slaves of a given type. The entries for each of these slaves begin at the
* rsc_hdr.header_offset. In addition each slave could have auxiliary data
* that may be needed by the driver. The data for the slave starts at the
* entry_header.offset to the location pointed to by the rsc_hdr.data_offset.
*
* Drivers have a stringified key to a slave/resource. They can query the slave
* information and get the slave id and the auxiliary data and the length of the
* data. Using this information, they can format the request to be sent to the
* h/w accelerator and request a resource state.
*/
static const u8 CMD_DB_MAGIC[] = { 0xdb, 0x30, 0x03, 0x0c };
static bool cmd_db_magic_matches(const struct cmd_db_header *header)
{
const u8 *magic = header->magic;
return memcmp(magic, CMD_DB_MAGIC, ARRAY_SIZE(CMD_DB_MAGIC)) == 0;
}
static struct cmd_db_header *cmd_db_header;
static inline void *rsc_to_entry_header(struct rsc_hdr *hdr)
{
u16 offset = le16_to_cpu(hdr->header_offset);
return cmd_db_header->data + offset;
}
static inline void *
rsc_offset(struct rsc_hdr *hdr, struct entry_header *ent)
{
u16 offset = le16_to_cpu(hdr->data_offset);
u16 loffset = le16_to_cpu(ent->offset);
return cmd_db_header->data + offset + loffset;
}
/**
* cmd_db_ready - Indicates if command DB is available
*
* Return: 0 on success, errno otherwise
*/
int cmd_db_ready(void)
{
if (cmd_db_header == NULL)
return -EPROBE_DEFER;
else if (!cmd_db_magic_matches(cmd_db_header))
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(cmd_db_ready);
static int cmd_db_get_header(const char *id, struct entry_header *eh,
struct rsc_hdr *rh)
{
struct rsc_hdr *rsc_hdr;
struct entry_header *ent;
int ret, i, j;
u8 query[8];
ret = cmd_db_ready();
if (ret)
return ret;
if (!eh || !rh)
return -EINVAL;
/* Pad out query string to same length as in DB */
strncpy(query, id, sizeof(query));
for (i = 0; i < MAX_SLV_ID; i++) {
rsc_hdr = &cmd_db_header->header[i];
if (!rsc_hdr->slv_id)
break;
ent = rsc_to_entry_header(rsc_hdr);
for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) {
if (memcmp(ent->id, query, sizeof(ent->id)) == 0)
break;
}
if (j < le16_to_cpu(rsc_hdr->cnt)) {
memcpy(eh, ent, sizeof(*ent));
memcpy(rh, rsc_hdr, sizeof(*rh));
return 0;
}
}
return -ENODEV;
}
/**
* cmd_db_read_addr() - Query command db for resource id address.
*
* @id: resource id to query for address
*
* Return: resource address on success, 0 on error
*
* This is used to retrieve resource address based on resource
* id.
*/
u32 cmd_db_read_addr(const char *id)
{
int ret;
struct entry_header ent;
struct rsc_hdr rsc_hdr;
ret = cmd_db_get_header(id, &ent, &rsc_hdr);
return ret < 0 ? 0 : le32_to_cpu(ent.addr);
}
EXPORT_SYMBOL(cmd_db_read_addr);
/**
* cmd_db_read_aux_data() - Query command db for aux data.
*
* @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.
*
* Return: size of data on success, errno otherwise
*/
int cmd_db_read_aux_data(const char *id, u8 *data, size_t len)
{
int ret;
struct entry_header ent;
struct rsc_hdr rsc_hdr;
u16 ent_len;
if (!data)
return -EINVAL;
ret = cmd_db_get_header(id, &ent, &rsc_hdr);
if (ret)
return ret;
ent_len = le16_to_cpu(ent.len);
if (len < ent_len)
return -EINVAL;
len = min_t(u16, ent_len, len);
memcpy(data, rsc_offset(&rsc_hdr, &ent), len);
return len;
}
EXPORT_SYMBOL(cmd_db_read_aux_data);
/**
* cmd_db_read_aux_data_len - Get the length of the auxiliary data stored in DB.
*
* @id: Resource to retrieve AUX Data.
*
* Return: size on success, 0 on error
*/
size_t cmd_db_read_aux_data_len(const char *id)
{
int ret;
struct entry_header ent;
struct rsc_hdr rsc_hdr;
ret = cmd_db_get_header(id, &ent, &rsc_hdr);
return ret < 0 ? 0 : le16_to_cpu(ent.len);
}
EXPORT_SYMBOL(cmd_db_read_aux_data_len);
/**
* cmd_db_read_slave_id - Get the slave ID for a given resource address
*
* @id: Resource id to query the DB for version
*
* Return: cmd_db_hw_type enum on success, CMD_DB_HW_INVALID on error
*/
enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
{
int ret;
struct entry_header ent;
struct rsc_hdr rsc_hdr;
u32 addr;
ret = cmd_db_get_header(id, &ent, &rsc_hdr);
if (ret < 0)
return CMD_DB_HW_INVALID;
addr = le32_to_cpu(ent.addr);
return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
}
EXPORT_SYMBOL(cmd_db_read_slave_id);
static int cmd_db_dev_probe(struct platform_device *pdev)
{
struct reserved_mem *rmem;
int ret = 0;
rmem = of_reserved_mem_lookup(pdev->dev.of_node);
if (!rmem) {
dev_err(&pdev->dev, "failed to acquire memory region\n");
return -EINVAL;
}
cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WB);
if (IS_ERR_OR_NULL(cmd_db_header)) {
ret = PTR_ERR(cmd_db_header);
cmd_db_header = NULL;
return ret;
}
if (!cmd_db_magic_matches(cmd_db_header)) {
dev_err(&pdev->dev, "Invalid Command DB Magic\n");
return -EINVAL;
}
return 0;
}
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",
.of_match_table = cmd_db_match_table,
},
};
static int __init cmd_db_device_init(void)
{
return platform_driver_register(&cmd_db_dev_driver);
}
arch_initcall(cmd_db_device_init);
|