blob: 252bd21086815a7ce2cf57bf79fb82f1555076b3 [file] [log] [blame]
Lina Iyer1c5ab792017-02-01 13:34:54 -07001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <linux/io.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20#include <linux/of_platform.h>
21#include <linux/kernel.h>
Mahesh Sivasubramanianc44e3802017-04-28 09:56:01 -060022#include <linux/debugfs.h>
23#include <linux/fs.h>
24#include <linux/seq_file.h>
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -060025#include <soc/qcom/cmd-db.h>
26
27#define RESOURCE_ID_LEN 8
28#define NUM_PRIORITY 2
29#define MAX_SLV_ID 8
30#define CMD_DB_MAGIC 0x0C0330DBUL
31#define SLAVE_ID_MASK 0x7
32#define SLAVE_ID_SHIFT 16
Lina Iyer1c5ab792017-02-01 13:34:54 -070033#define CMD_DB_STANDALONE_MASK BIT(0)
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -060034
35struct entry_header {
36 uint64_t res_id;
37 u32 priority[NUM_PRIORITY];
38 u32 addr;
39 u16 len;
40 u16 offset;
41};
42
43struct rsc_hdr {
44 u16 slv_id;
45 u16 header_offset; /* Entry header offset from data */
46 u16 data_offset; /* Entry offset for data location */
47 u16 cnt; /* Number of entries for HW type */
48 u16 version; /* MSB is Major and LSB is Minor version
49 * identifies the HW type of Aux Data
50 */
51 u16 reserved[3];
52};
53
54struct cmd_db_header {
55 u32 version;
56 u32 magic_num;
57 struct rsc_hdr header[MAX_SLV_ID];
58 u32 check_sum;
59 u32 reserved;
60 u8 data[];
61};
62
63struct cmd_db_entry {
64 const char resource_id[RESOURCE_ID_LEN + 1]; /* Unique id per entry */
65 const u32 addr; /* TCS Addr Slave ID + Offset address */
66 const u32 priority[NUM_PRIORITY]; /* Bitmask for DRV IDs */
67 u32 len; /* Aux data len */
68 u16 version;
69 u8 data[];
70};
71
72/* CMD DB QUERY TYPES */
73enum cmd_db_query_type {
74 CMD_DB_QUERY_RES_ID = 0,
75 CMD_DB_QUERY_ADDRESS,
76 CMD_DB_QUERY_INVALID,
77 CMD_DB_QUERY_MAX = 0x7ffffff,
78};
79
80static void __iomem *start_addr;
81static struct cmd_db_header *cmd_db_header;
82static int cmd_db_status = -EPROBE_DEFER;
83
84static u64 cmd_db_get_u64_id(const char *id)
85{
86 uint64_t rsc_id = 0;
87 uint8_t *ch = (uint8_t *)&rsc_id;
88 int i;
89
90 for (i = 0; ((i < sizeof(rsc_id)) && id[i]); i++)
91 ch[i] = id[i];
92
93 return rsc_id;
94}
95
96static int cmd_db_get_header(u64 query, struct entry_header *eh,
97 struct rsc_hdr *rh, bool use_addr)
98{
99 struct rsc_hdr *rsc_hdr;
100 int i, j;
101
102 if (!cmd_db_header)
103 return -EPROBE_DEFER;
104
105 if (!eh || !rh)
106 return -EINVAL;
107
108 rsc_hdr = &cmd_db_header->header[0];
109
110 for (i = 0; i < MAX_SLV_ID ; i++, rsc_hdr++) {
111 struct entry_header *ent;
112
113 if (!rsc_hdr->slv_id)
114 break;
115
116 ent = (struct entry_header *)(start_addr
117 + sizeof(*cmd_db_header)
118 + rsc_hdr->header_offset);
119
120 for (j = 0; j < rsc_hdr->cnt; j++, ent++) {
121 if (use_addr) {
122 if (ent->addr == (u32)(query))
123 break;
124 } else if (ent->res_id == query)
125 break;
126 }
127
128 if (j < rsc_hdr->cnt) {
129 memcpy(eh, ent, sizeof(*ent));
130 memcpy(rh, &cmd_db_header->header[i], sizeof(*rh));
131 return 0;
132 }
133 }
134 return -ENODEV;
135}
136
137static int cmd_db_get_header_by_addr(u32 addr,
138 struct entry_header *ent_hdr,
139 struct rsc_hdr *rsc_hdr)
140{
141 return cmd_db_get_header((u64)addr, ent_hdr, rsc_hdr, true);
142}
143
144static int cmd_db_get_header_by_rsc_id(const char *resource_id,
145 struct entry_header *ent_hdr,
146 struct rsc_hdr *rsc_hdr)
147{
148 u64 rsc_id = cmd_db_get_u64_id(resource_id);
149
150 return cmd_db_get_header(rsc_id, ent_hdr, rsc_hdr, false);
151}
152
153u32 cmd_db_get_addr(const char *resource_id)
154{
155 int ret;
156 struct entry_header ent;
157 struct rsc_hdr rsc_hdr;
158
159 ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
160
161 return ret < 0 ? 0 : ent.addr;
162}
163
164bool cmd_db_get_priority(u32 addr, u8 drv_id)
165{
166 int ret;
167 struct entry_header ent;
168 struct rsc_hdr rsc_hdr;
169
170 ret = cmd_db_get_header_by_addr(addr, &ent, &rsc_hdr);
171
172 return ret < 0 ? false : (bool)(ent.priority[0] & (1 << drv_id));
173
174}
175int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len)
176{
177 int ret;
178 struct entry_header ent;
179 struct rsc_hdr rsc_hdr;
180
181 if (!data)
182 return -EINVAL;
183
184 ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
185
186 if (ret)
187 return ret;
188
189 if (ent.len < len)
190 return -EINVAL;
191
192 len = (ent.len < len) ? ent.len : len;
193
194 memcpy_fromio(data,
195 start_addr + sizeof(*cmd_db_header)
196 + rsc_hdr.data_offset + ent.offset,
197 len);
198 return len;
199}
200
201int cmd_db_get_aux_data_len(const char *resource_id)
202{
203 int ret;
204 struct entry_header ent;
205 struct rsc_hdr rsc_hdr;
206
207 ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
208
209 return ret < 0 ? 0 : ent.len;
210}
211
212u16 cmd_db_get_version(const char *resource_id)
213{
214 int ret;
215 struct entry_header ent;
216 struct rsc_hdr rsc_hdr;
217
218 ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
219 return ret < 0 ? 0 : rsc_hdr.version;
220}
221
222int cmd_db_ready(void)
223{
224 return cmd_db_status;
225}
226
Lina Iyer1c5ab792017-02-01 13:34:54 -0700227int cmd_db_is_standalone(void)
228{
229 if (cmd_db_status < 0)
230 return cmd_db_status;
231
232 return !!(cmd_db_header->reserved & CMD_DB_STANDALONE_MASK);
233}
234
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600235int cmd_db_get_slave_id(const char *resource_id)
236{
237 int ret;
238 struct entry_header ent;
239 struct rsc_hdr rsc_hdr;
240
241 ret = cmd_db_get_header_by_rsc_id(resource_id, &ent, &rsc_hdr);
242 return ret < 0 ? 0 : (ent.addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
243}
244
Mahesh Sivasubramanianc44e3802017-04-28 09:56:01 -0600245static void *cmd_db_start(struct seq_file *m, loff_t *pos)
246{
247 struct cmd_db_header *hdr = m->private;
248 int slv_idx, ent_idx;
249 struct entry_header *ent;
250 int total = 0;
251
252 for (slv_idx = 0; slv_idx < MAX_SLV_ID; slv_idx++) {
253
254 if (!hdr->header[slv_idx].cnt)
255 continue;
256 ent_idx = *pos - total;
257 if (ent_idx < hdr->header[slv_idx].cnt)
258 break;
259
260 total += hdr->header[slv_idx].cnt;
261 }
262
263 if (slv_idx == MAX_SLV_ID)
264 return NULL;
265
266 ent = start_addr + hdr->header[slv_idx].header_offset + sizeof(*hdr);
267 return &ent[ent_idx];
268
269}
270
271static void cmd_db_stop(struct seq_file *m, void *v)
272{
273}
274
275static void *cmd_db_next(struct seq_file *m, void *v, loff_t *pos)
276{
277 (*pos)++;
278 return cmd_db_start(m, pos);
279}
280
281static int cmd_db_seq_show(struct seq_file *m, void *v)
282{
283 struct entry_header *eh = v;
284 struct cmd_db_header *hdr = m->private;
285 char buf[9] = {0};
286
287 if (!eh)
288 return 0;
289
290 memcpy(buf, &eh->res_id, min(sizeof(eh->res_id), sizeof(buf)));
291
292 seq_printf(m, "Address: 0x%05x, id: %s", eh->addr, buf);
293
294 if (eh->len) {
295 int slv_id = (eh->addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
296 u8 aux[32] = {0};
297 int len;
298 int k;
299
300 len = min_t(u32, eh->len, sizeof(aux));
301
302 for (k = 0; k < MAX_SLV_ID; k++) {
303 if (hdr->header[k].slv_id == slv_id)
304 break;
305 }
306
307 if (k == MAX_SLV_ID)
308 return -EINVAL;
309
310 memcpy_fromio(aux, start_addr + hdr->header[k].data_offset
311 + eh->offset + sizeof(*cmd_db_header), len);
312
313 seq_puts(m, ", aux data: ");
314
315 for (k = 0; k < len; k++)
316 seq_printf(m, "%02x ", aux[k]);
317
318 }
319 seq_puts(m, "\n");
320 return 0;
321}
322
323static const struct seq_operations cmd_db_seq_ops = {
324 .start = cmd_db_start,
325 .stop = cmd_db_stop,
326 .next = cmd_db_next,
327 .show = cmd_db_seq_show,
328};
329
330static int cmd_db_file_open(struct inode *inode, struct file *file)
331{
332 int ret = seq_open(file, &cmd_db_seq_ops);
333 struct seq_file *s = (struct seq_file *)(file->private_data);
334
335 s->private = inode->i_private;
336 return ret;
337}
338
339static const struct file_operations cmd_db_fops = {
340 .owner = THIS_MODULE,
341 .open = cmd_db_file_open,
342 .read = seq_read,
343 .release = seq_release,
344 .llseek = no_llseek,
345};
346
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600347static int cmd_db_dev_probe(struct platform_device *pdev)
348{
Mahesh Sivasubramaniand65a35e2017-04-28 11:18:13 -0600349 struct resource res;
350 void __iomem *dict;
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600351
Mahesh Sivasubramaniand65a35e2017-04-28 11:18:13 -0600352 dict = of_iomap(pdev->dev.of_node, 0);
353 if (!dict) {
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600354 cmd_db_status = -ENOMEM;
355 goto failed;
356 }
357
Mahesh Sivasubramaniand65a35e2017-04-28 11:18:13 -0600358 /*
359 * Read start address and size of the command DB address from
360 * shared dictionary location
361 */
362 res.start = readl_relaxed(dict);
363 res.end = res.start + readl_relaxed(dict + 0x4);
364 res.flags = IORESOURCE_MEM;
365 iounmap(dict);
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600366
Mahesh Sivasubramaniand65a35e2017-04-28 11:18:13 -0600367 start_addr = devm_ioremap_resource(&pdev->dev, &res);
368
369 cmd_db_header = devm_kzalloc(&pdev->dev,
370 sizeof(*cmd_db_header), GFP_KERNEL);
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600371
372 if (!cmd_db_header) {
373 cmd_db_status = -ENOMEM;
374 goto failed;
375 }
376
377 memcpy(cmd_db_header, start_addr, sizeof(*cmd_db_header));
378
379 if (cmd_db_header->magic_num != CMD_DB_MAGIC) {
380 pr_err("%s(): Invalid Magic\n", __func__);
381 cmd_db_status = -EINVAL;
382 goto failed;
383 }
384 cmd_db_status = 0;
385 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
Lina Iyer1c5ab792017-02-01 13:34:54 -0700386
Mahesh Sivasubramanianc44e3802017-04-28 09:56:01 -0600387 if (!debugfs_create_file("cmd_db", 0444, NULL,
388 cmd_db_header, &cmd_db_fops))
389 pr_err("Couldn't create debugfs\n");
390
Lina Iyer1c5ab792017-02-01 13:34:54 -0700391 if (cmd_db_is_standalone() == 1)
392 pr_info("Command DB is initialized in standalone mode.\n");
393
Mahesh Sivasubramaniancb649522016-08-19 14:04:44 -0600394failed:
395 return cmd_db_status;
396}
397
398static const struct of_device_id cmd_db_match_table[] = {
399 {.compatible = "qcom,cmd-db"},
400 {},
401};
402
403static struct platform_driver cmd_db_dev_driver = {
404 .probe = cmd_db_dev_probe,
405 .driver = {
406 .name = "cmd-db",
407 .owner = THIS_MODULE,
408 .of_match_table = cmd_db_match_table,
409 },
410};
411
412int __init cmd_db_device_init(void)
413{
414 return platform_driver_register(&cmd_db_dev_driver);
415}
416arch_initcall(cmd_db_device_init);