blob: 7c54073afb0a62c952575ab23486cfcdd4c4e307 [file] [log] [blame]
Mao Jinlong99337ac2018-04-09 14:36:26 +08001/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
Satyajit Desai765e7ef2016-11-09 14:27:45 -08002 *
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#include <linux/module.h>
13#include <linux/bitops.h>
14#include <linux/cdev.h>
15#include <linux/delay.h>
16#include <linux/io.h>
17#include <linux/iopoll.h>
18#include <linux/fs.h>
19#include <linux/of.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/uaccess.h>
23#include <soc/qcom/memory_dump.h>
24#include <soc/qcom/scm.h>
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -070025#include <dt-bindings/soc/qcom,dcc_v2.h>
Satyajit Desai765e7ef2016-11-09 14:27:45 -080026
27#define TIMEOUT_US (100)
28
29#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
30#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
31#define BVAL(val, n) ((val & BIT(n)) >> n)
32
33#define dcc_writel(drvdata, val, off) \
34 __raw_writel((val), drvdata->base + off)
35#define dcc_readl(drvdata, off) \
36 __raw_readl(drvdata->base + off)
37
38#define dcc_sram_writel(drvdata, val, off) \
39 __raw_writel((val), drvdata->ram_base + off)
40#define dcc_sram_readl(drvdata, off) \
41 __raw_readl(drvdata->ram_base + off)
42
43#define HLOS_LIST_START 1
44
45/* DCC registers */
46#define DCC_HW_VERSION (0x00)
47#define DCC_HW_INFO (0x04)
48#define DCC_EXEC_CTRL (0x08)
49#define DCC_STATUS (0x0C)
50#define DCC_CFG (0x10)
51#define DCC_FDA_CURR (0x14)
52#define DCC_LLA_CURR (0x18)
53#define DCC_LL_LOCK(m) (0x1C + 0x80 * (m + HLOS_LIST_START))
54#define DCC_LL_CFG(m) (0x20 + 0x80 * (m + HLOS_LIST_START))
55#define DCC_LL_BASE(m) (0x24 + 0x80 * (m + HLOS_LIST_START))
56#define DCC_FD_BASE(m) (0x28 + 0x80 * (m + HLOS_LIST_START))
57#define DCC_LL_TIMEOUT(m) (0x2c + 0x80 * (m + HLOS_LIST_START))
58#define DCC_LL_INT_ENABLE(m) (0x30 + 0x80 * (m + HLOS_LIST_START))
59#define DCC_LL_INT_STATUS(m) (0x34 + 0x80 * (m + HLOS_LIST_START))
60#define DCC_FDA_CAPTURED(m) (0x38 + 0x80 * (m + HLOS_LIST_START))
61#define DCC_LLA_CAPTURED(m) (0x3C + 0x80 * (m + HLOS_LIST_START))
62#define DCC_LL_CRC_CAPTURED(m) (0x40 + 0x80 * (m + HLOS_LIST_START))
63#define DCC_LL_SW_TRIGGER(m) (0x44 + 0x80 * (m + HLOS_LIST_START))
64#define DCC_LL_BUS_ACCESS_STATUS(m) (0x48 + 0x80 * (m + HLOS_LIST_START))
65
66#define DCC_REG_DUMP_MAGIC_V2 (0x42445953)
67#define DCC_REG_DUMP_VER (1)
68
69#define MAX_DCC_OFFSET (0xFF * 4)
70#define MAX_DCC_LEN 0x7F
71#define MAX_LOOP_CNT 0xFF
72
73#define DCC_ADDR_DESCRIPTOR 0x00
74#define DCC_LOOP_DESCRIPTOR (BIT(30))
75#define DCC_RD_MOD_WR_DESCRIPTOR (BIT(31))
76#define DCC_LINK_DESCRIPTOR (BIT(31) | BIT(30))
77
Satyajit Desai33073622017-04-17 16:45:06 -070078#define DCC_READ_IND 0x00
79#define DCC_WRITE_IND (BIT(28))
80
Satyajit Desaie95613c2017-05-01 17:04:47 -070081#define DCC_AHB_IND 0x00
82#define DCC_APB_IND BIT(29)
83
Satyajit Desai765e7ef2016-11-09 14:27:45 -080084#define DCC_MAX_LINK_LIST 5
85#define DCC_INVALID_LINK_LIST 0xFF
86
87enum dcc_func_type {
88 DCC_FUNC_TYPE_CAPTURE,
89 DCC_FUNC_TYPE_CRC,
90};
91
92static const char * const str_dcc_func_type[] = {
93 [DCC_FUNC_TYPE_CAPTURE] = "cap",
94 [DCC_FUNC_TYPE_CRC] = "crc",
95};
96
97enum dcc_data_sink {
98 DCC_DATA_SINK_SRAM,
99 DCC_DATA_SINK_ATB
100};
101
Satyajit Desai33073622017-04-17 16:45:06 -0700102enum dcc_descriptor_type {
103 DCC_ADDR_TYPE,
104 DCC_LOOP_TYPE,
105 DCC_READ_WRITE_TYPE,
106 DCC_WRITE_TYPE
107};
108
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800109static const char * const str_dcc_data_sink[] = {
110 [DCC_DATA_SINK_SRAM] = "sram",
111 [DCC_DATA_SINK_ATB] = "atb",
112};
113
114struct rpm_trig_req {
115 uint32_t enable;
116 uint32_t reserved;
117};
118
119struct dcc_config_entry {
Satyajit Desai33073622017-04-17 16:45:06 -0700120 uint32_t base;
121 uint32_t offset;
122 uint32_t len;
123 uint32_t index;
124 uint32_t loop_cnt;
125 uint32_t write_val;
126 uint32_t mask;
Satyajit Desaie95613c2017-05-01 17:04:47 -0700127 bool apb_bus;
Satyajit Desai33073622017-04-17 16:45:06 -0700128 enum dcc_descriptor_type desc_type;
129 struct list_head list;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800130};
131
132struct dcc_drvdata {
133 void __iomem *base;
134 uint32_t reg_size;
135 struct device *dev;
136 struct mutex mutex;
137 void __iomem *ram_base;
138 uint32_t ram_size;
Satyajit Desai3d86e1b2017-04-13 17:11:09 -0700139 uint32_t ram_offset;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800140 enum dcc_data_sink data_sink;
141 enum dcc_func_type func_type[DCC_MAX_LINK_LIST];
142 uint32_t ram_cfg;
143 uint32_t ram_start;
144 bool enable[DCC_MAX_LINK_LIST];
145 bool configured[DCC_MAX_LINK_LIST];
146 bool interrupt_disable;
147 char *sram_node;
148 struct cdev sram_dev;
149 struct class *sram_class;
150 struct list_head cfg_head[DCC_MAX_LINK_LIST];
151 uint32_t nr_config[DCC_MAX_LINK_LIST];
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800152 uint8_t curr_list;
Satyajit Desaife1311b2017-05-19 16:02:35 -0700153 uint8_t cti_trig;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800154};
155
156static bool dcc_ready(struct dcc_drvdata *drvdata)
157{
158 uint32_t val;
159
160 /* poll until DCC ready */
161 if (!readl_poll_timeout((drvdata->base + DCC_STATUS), val,
162 (BMVAL(val, 0, 1) == 0), 1, TIMEOUT_US))
163 return true;
164
165 return false;
166}
167
168static int dcc_read_status(struct dcc_drvdata *drvdata)
169{
170 int curr_list;
171 uint32_t bus_status;
172
173 for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
174 if (!drvdata->enable[curr_list])
175 continue;
176
177 bus_status = dcc_readl(drvdata,
178 DCC_LL_BUS_ACCESS_STATUS(curr_list));
179
180 if (bus_status) {
181
182 dev_err(drvdata->dev,
183 "Read access error for list %d err: 0x%x",
184 curr_list, bus_status);
185
186 dcc_writel(drvdata, 0x3,
187 DCC_LL_BUS_ACCESS_STATUS(curr_list));
188 return -ENODATA;
189 }
190 }
191
192 return 0;
193}
194
195static int dcc_sw_trigger(struct dcc_drvdata *drvdata)
196{
197 int ret = 0;
198 int curr_list;
199
200 mutex_lock(&drvdata->mutex);
201
202 if (!dcc_ready(drvdata)) {
Satyajit Desai33073622017-04-17 16:45:06 -0700203 dev_err(drvdata->dev, "DCC is not ready\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800204 ret = -EBUSY;
205 goto err;
206 }
207
208 for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
209 if (!drvdata->enable[curr_list])
210 continue;
211
212 dcc_writel(drvdata, 1, DCC_LL_SW_TRIGGER(curr_list));
213 }
214
215 if (!dcc_ready(drvdata)) {
216 dev_err(drvdata->dev,
217 "DCC is busy after receiving sw tigger.\n");
218 ret = -EBUSY;
219 goto err;
220 }
221
222 ret = dcc_read_status(drvdata);
223
224err:
225 mutex_unlock(&drvdata->mutex);
226 return ret;
227}
228
229static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
230{
231 int ret = 0;
232 uint32_t sram_offset = drvdata->ram_cfg * 4;
233 uint32_t prev_addr, addr;
234 uint32_t prev_off = 0, off;
235 uint32_t loop_off = 0;
236 uint32_t link;
237 uint32_t pos, total_len = 0, loop_len = 0;
Satyajit Desai33073622017-04-17 16:45:06 -0700238 uint32_t loop, loop_cnt = 0;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800239 bool loop_start = false;
240 struct dcc_config_entry *entry;
241
242 prev_addr = 0;
243 addr = 0;
244 link = 0;
245
246 list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) {
Satyajit Desai33073622017-04-17 16:45:06 -0700247 switch (entry->desc_type) {
248 case DCC_READ_WRITE_TYPE:
249 {
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800250 if (link) {
251 /* write new offset = 1 to continue
252 * processing the list
253 */
254 link |= ((0x1 << 8) & BM(8, 14));
255 dcc_sram_writel(drvdata, link, sram_offset);
256 sram_offset += 4;
257 /* Reset link and prev_off */
258 addr = 0x00;
259 link = 0;
260 prev_off = 0;
261 prev_addr = addr;
262 }
263
264 addr = DCC_RD_MOD_WR_DESCRIPTOR;
265 dcc_sram_writel(drvdata, addr, sram_offset);
266 sram_offset += 4;
267
268 dcc_sram_writel(drvdata, entry->mask, sram_offset);
269 sram_offset += 4;
270
Satyajit Desai33073622017-04-17 16:45:06 -0700271 dcc_sram_writel(drvdata, entry->write_val, sram_offset);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800272 sram_offset += 4;
Satyajit Desai33073622017-04-17 16:45:06 -0700273 addr = 0;
274 break;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800275 }
276
Satyajit Desai33073622017-04-17 16:45:06 -0700277 case DCC_LOOP_TYPE:
278 {
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800279 /* Check if we need to write link of prev entry */
280 if (link) {
281 dcc_sram_writel(drvdata, link, sram_offset);
282 sram_offset += 4;
283 }
284
285 if (loop_start) {
286 loop = (sram_offset - loop_off) / 4;
287 loop |= (loop_cnt << 13) & BM(13, 27);
288 loop |= DCC_LOOP_DESCRIPTOR;
289 total_len += (total_len - loop_len) * loop_cnt;
290
291 dcc_sram_writel(drvdata, loop, sram_offset);
292 sram_offset += 4;
293
294 loop_start = false;
295 loop_len = 0;
296 loop_off = 0;
297 } else {
298 loop_start = true;
299 loop_cnt = entry->loop_cnt - 1;
300 loop_len = total_len;
301 loop_off = sram_offset;
302 }
303
304 /* Reset link and prev_off */
305 addr = 0x00;
306 link = 0;
307 prev_off = 0;
308 prev_addr = addr;
309
Satyajit Desai33073622017-04-17 16:45:06 -0700310 break;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800311 }
312
Satyajit Desai33073622017-04-17 16:45:06 -0700313 case DCC_WRITE_TYPE:
314 {
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800315 if (link) {
Satyajit Desai33073622017-04-17 16:45:06 -0700316 /* write new offset = 1 to continue
317 * processing the list
318 */
319 link |= ((0x1 << 8) & BM(8, 14));
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800320 dcc_sram_writel(drvdata, link, sram_offset);
321 sram_offset += 4;
Satyajit Desai33073622017-04-17 16:45:06 -0700322 /* Reset link and prev_off */
323 addr = 0x00;
324 prev_off = 0;
325 prev_addr = addr;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800326 }
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800327
Satyajit Desai33073622017-04-17 16:45:06 -0700328 off = entry->offset/4;
329 /* write new offset-length pair to correct position */
330 link |= ((off & BM(0, 7)) | BIT(15) |
331 ((entry->len << 8) & BM(8, 14)));
332 link |= DCC_LINK_DESCRIPTOR;
333
334 /* Address type */
335 addr = (entry->base >> 4) & BM(0, 27);
Satyajit Desaie95613c2017-05-01 17:04:47 -0700336 if (entry->apb_bus)
337 addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND
338 | DCC_APB_IND;
339 else
340 addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND
341 | DCC_AHB_IND;
Satyajit Desai33073622017-04-17 16:45:06 -0700342
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800343 dcc_sram_writel(drvdata, addr, sram_offset);
Satyajit Desai33073622017-04-17 16:45:06 -0700344 sram_offset += 4;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800345
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800346 dcc_sram_writel(drvdata, link, sram_offset);
Satyajit Desai33073622017-04-17 16:45:06 -0700347 sram_offset += 4;
348
349 dcc_sram_writel(drvdata, entry->write_val, sram_offset);
350 sram_offset += 4;
351 addr = 0x00;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800352 link = 0;
Satyajit Desai33073622017-04-17 16:45:06 -0700353 break;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800354 }
355
Satyajit Desai33073622017-04-17 16:45:06 -0700356 default:
357 {
358 /* Address type */
359 addr = (entry->base >> 4) & BM(0, 27);
Satyajit Desaie95613c2017-05-01 17:04:47 -0700360 if (entry->apb_bus)
361 addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND
362 | DCC_APB_IND;
363 else
364 addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND
365 | DCC_AHB_IND;
Satyajit Desai33073622017-04-17 16:45:06 -0700366
367 off = entry->offset/4;
368 total_len += entry->len * 4;
369
370 if (!prev_addr || prev_addr != addr || prev_off > off) {
371 /* Check if we need to write prev link entry */
372 if (link) {
373 dcc_sram_writel(drvdata,
374 link, sram_offset);
375 sram_offset += 4;
376 }
377 dev_dbg(drvdata->dev,
378 "DCC: sram address 0x%x\n",
379 sram_offset);
380
381 /* Write address */
382 dcc_sram_writel(drvdata, addr, sram_offset);
383 sram_offset += 4;
384
385 /* Reset link and prev_off */
386 link = 0;
387 prev_off = 0;
388 }
389
390 if ((off - prev_off) > 0xFF ||
391 entry->len > MAX_DCC_LEN) {
392 dev_err(drvdata->dev,
393 "DCC: Progamming error Base: 0x%x, offset 0x%x\n",
394 entry->base, entry->offset);
395 ret = -EINVAL;
396 goto err;
397 }
398
399 if (link) {
400 /*
401 * link already has one offset-length so new
402 * offset-length needs to be placed at
403 * bits [29:15]
404 */
405 pos = 15;
406
407 /* Clear bits [31:16] */
408 link &= BM(0, 14);
409 } else {
410 /*
411 * link is empty, so new offset-length needs
412 * to be placed at bits [15:0]
413 */
414 pos = 0;
415 link = 1 << 15;
416 }
417
418 /* write new offset-length pair to correct position */
419 link |= (((off-prev_off) & BM(0, 7)) |
420 ((entry->len << 8) & BM(8, 14))) << pos;
421
422 link |= DCC_LINK_DESCRIPTOR;
423
424 if (pos) {
425 dcc_sram_writel(drvdata, link, sram_offset);
426 sram_offset += 4;
427 link = 0;
428 }
429
Satyajit Desai6d001d52017-06-16 15:16:16 -0700430 prev_off = off + entry->len - 1;
Satyajit Desai33073622017-04-17 16:45:06 -0700431 prev_addr = addr;
432 }
433 }
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800434 }
435
436 if (link) {
437 dcc_sram_writel(drvdata, link, sram_offset);
438 sram_offset += 4;
439 }
440
441 if (loop_start) {
442 dev_err(drvdata->dev,
Satyajit Desai33073622017-04-17 16:45:06 -0700443 "DCC: Progamming error: Loop unterminated\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800444 ret = -EINVAL;
445 goto err;
446 }
447
448 /* Handling special case of list ending with a rd_mod_wr */
449 if (addr == DCC_RD_MOD_WR_DESCRIPTOR) {
450 addr = (0xC105E) & BM(0, 27);
451 addr |= DCC_ADDR_DESCRIPTOR;
452
453 dcc_sram_writel(drvdata, addr, sram_offset);
454 sram_offset += 4;
455 }
456
457 /* Setting zero to indicate end of the list */
458 link = DCC_LINK_DESCRIPTOR;
459 dcc_sram_writel(drvdata, link, sram_offset);
460 sram_offset += 4;
461
462 /* Update ram_cfg and check if the data will overstep */
463 if (drvdata->data_sink == DCC_DATA_SINK_SRAM &&
464 drvdata->func_type[curr_list] == DCC_FUNC_TYPE_CAPTURE) {
465 drvdata->ram_cfg = (sram_offset + total_len) / 4;
466
467 if (sram_offset + total_len > drvdata->ram_size) {
468 sram_offset += total_len;
469 goto overstep;
470 }
471 } else {
472 drvdata->ram_cfg = sram_offset / 4;
473
474 if (sram_offset > drvdata->ram_size)
475 goto overstep;
476 }
477
478 drvdata->ram_start = sram_offset/4;
479 return 0;
480overstep:
481 ret = -EINVAL;
482 memset_io(drvdata->ram_base, 0, drvdata->ram_size);
483 dev_err(drvdata->dev, "DCC SRAM oversteps, 0x%x (0x%x)\n",
484 sram_offset, drvdata->ram_size);
485err:
486 return ret;
487}
488
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800489static void __dcc_first_crc(struct dcc_drvdata *drvdata)
490{
491 int i;
492
493 /*
494 * Need to send 2 triggers to DCC. First trigger sets CRC error status
495 * bit. So need second trigger to reset this bit.
496 */
497 for (i = 0; i < 2; i++) {
498 if (!dcc_ready(drvdata))
Satyajit Desai33073622017-04-17 16:45:06 -0700499 dev_err(drvdata->dev, "DCC is not ready\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800500
501 dcc_writel(drvdata, 1,
502 DCC_LL_SW_TRIGGER(drvdata->curr_list));
503 }
504
505 /* Clear CRC error interrupt */
506 dcc_writel(drvdata, BIT(1),
507 DCC_LL_INT_STATUS(drvdata->curr_list));
508}
509
510static int dcc_valid_list(struct dcc_drvdata *drvdata, int curr_list)
511{
512 uint32_t lock_reg;
513
514 if (list_empty(&drvdata->cfg_head[curr_list]))
515 return -EINVAL;
516
517 if (drvdata->enable[curr_list]) {
Satyajit Desai33073622017-04-17 16:45:06 -0700518 dev_err(drvdata->dev, "DCC is already enabled\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800519 return -EINVAL;
520 }
521
522 lock_reg = dcc_readl(drvdata, DCC_LL_LOCK(curr_list));
523 if (lock_reg & 0x1) {
Satyajit Desai33073622017-04-17 16:45:06 -0700524 dev_err(drvdata->dev, "DCC is already enabled\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800525 return -EINVAL;
526 }
527
528 dev_err(drvdata->dev, "DCC list passed %d\n", curr_list);
529 return 0;
530}
531
532static int dcc_enable(struct dcc_drvdata *drvdata)
533{
534 int ret = 0;
535 int list;
536 uint32_t ram_cfg_base;
537
538 mutex_lock(&drvdata->mutex);
539
Satyajit Desai8b8ba6f2017-10-24 15:41:05 -0700540 memset_io(drvdata->ram_base, 0xDE, drvdata->ram_size);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800541
542 for (list = 0; list < DCC_MAX_LINK_LIST; list++) {
543
544 if (dcc_valid_list(drvdata, list))
545 continue;
546
547 /* 1. Take ownership of the list */
548 dcc_writel(drvdata, BIT(0), DCC_LL_LOCK(list));
549
550 /* 2. Program linked-list in the SRAM */
551 ram_cfg_base = drvdata->ram_cfg;
552 ret = __dcc_ll_cfg(drvdata, list);
553 if (ret) {
554 dev_info(drvdata->dev, "DCC ram programming failed\n");
555 goto err;
556 }
557
Saranya Chidura09e31a62017-10-12 16:59:08 +0530558 /* 3. program DCC_RAM_CFG reg */
559 dcc_writel(drvdata, ram_cfg_base +
560 drvdata->ram_offset/4, DCC_LL_BASE(list));
561 dcc_writel(drvdata, drvdata->ram_start +
562 drvdata->ram_offset/4, DCC_FD_BASE(list));
563 dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800564
Satyajit Desaidc56a3f2017-05-19 12:19:15 -0700565 /* 4. Configure trigger, data sink and function type */
Satyajit Desaife1311b2017-05-19 16:02:35 -0700566 dcc_writel(drvdata, BIT(9) | ((drvdata->cti_trig << 8) |
567 (drvdata->data_sink << 4) |
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800568 (drvdata->func_type[list])), DCC_LL_CFG(list));
569
570 /* 5. Clears interrupt status register */
571 dcc_writel(drvdata, 0, DCC_LL_INT_ENABLE(list));
572 dcc_writel(drvdata, (BIT(0) | BIT(1) | BIT(2)),
573 DCC_LL_INT_STATUS(list));
574
575 dev_info(drvdata->dev, "All values written to enable");
576 /* Make sure all config is written in sram */
577 mb();
578
579 drvdata->enable[list] = 1;
580
581 if (drvdata->func_type[list] == DCC_FUNC_TYPE_CRC) {
582 __dcc_first_crc(drvdata);
583
584 /* Enable CRC error interrupt */
585 if (!drvdata->interrupt_disable)
586 dcc_writel(drvdata, BIT(1),
587 DCC_LL_INT_ENABLE(list));
588 }
589 }
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800590
591err:
592 mutex_unlock(&drvdata->mutex);
593 return ret;
594}
595
596static void dcc_disable(struct dcc_drvdata *drvdata)
597{
598 int curr_list;
599
600 mutex_lock(&drvdata->mutex);
601
602 if (!dcc_ready(drvdata))
Satyajit Desai33073622017-04-17 16:45:06 -0700603 dev_err(drvdata->dev, "DCC is not ready Disabling DCC...\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800604
605 for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
606 if (!drvdata->enable[curr_list])
607 continue;
608
609 dcc_writel(drvdata, 0, DCC_LL_LOCK(curr_list));
610 drvdata->enable[curr_list] = 0;
611 }
612 drvdata->ram_cfg = 0;
613 drvdata->ram_start = 0;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800614
615 mutex_unlock(&drvdata->mutex);
616}
617
618static ssize_t dcc_curr_list(struct device *dev,
619 struct device_attribute *attr,
620 const char *buf, size_t size)
621{
622 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
623 unsigned long val;
624 uint32_t lock_reg;
625
626 if (kstrtoul(buf, 16, &val))
627 return -EINVAL;
628
629 if (val >= DCC_MAX_LINK_LIST)
630 return -EINVAL;
631
632 mutex_lock(&drvdata->mutex);
633 lock_reg = dcc_readl(drvdata, DCC_LL_LOCK(val));
634 if (lock_reg & 0x1) {
Satyajit Desai33073622017-04-17 16:45:06 -0700635 dev_err(drvdata->dev, "DCC linked list is already configured\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800636 mutex_unlock(&drvdata->mutex);
637 return -EINVAL;
638 }
639 drvdata->curr_list = val;
640 mutex_unlock(&drvdata->mutex);
641
642 return size;
643}
644static DEVICE_ATTR(curr_list, 0200,
645 NULL, dcc_curr_list);
646
647static ssize_t dcc_show_func_type(struct device *dev,
648 struct device_attribute *attr, char *buf)
649{
650 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
651 ssize_t len = 0;
652 unsigned int i;
653
654 for (i = 0; i < DCC_MAX_LINK_LIST; i++)
655 len += scnprintf(buf + len, PAGE_SIZE - len, "%u :%s\n",
656 i, str_dcc_func_type[drvdata->func_type[i]]);
657
658 return len;
659}
660
661static ssize_t dcc_store_func_type(struct device *dev,
662 struct device_attribute *attr,
663 const char *buf, size_t size)
664{
665 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
666 char str[10] = "";
667 int ret;
668
669 if (strlen(buf) >= 10)
670 return -EINVAL;
671 if (sscanf(buf, "%s", str) != 1)
672 return -EINVAL;
673
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700674 mutex_lock(&drvdata->mutex);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800675 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
676 dev_err(dev,
677 "Select link list to program using curr_list\n");
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700678 ret = -EINVAL;
679 goto out;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800680 }
681
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800682 if (drvdata->enable[drvdata->curr_list]) {
683 ret = -EBUSY;
684 goto out;
685 }
686
687 if (!strcmp(str, str_dcc_func_type[DCC_FUNC_TYPE_CAPTURE]))
688 drvdata->func_type[drvdata->curr_list] =
689 DCC_FUNC_TYPE_CAPTURE;
690 else if (!strcmp(str, str_dcc_func_type[DCC_FUNC_TYPE_CRC]))
691 drvdata->func_type[drvdata->curr_list] =
692 DCC_FUNC_TYPE_CRC;
693 else {
694 ret = -EINVAL;
695 goto out;
696 }
697
698 ret = size;
699out:
700 mutex_unlock(&drvdata->mutex);
701 return ret;
702}
703static DEVICE_ATTR(func_type, 0644,
704 dcc_show_func_type, dcc_store_func_type);
705
706static ssize_t dcc_show_data_sink(struct device *dev,
707 struct device_attribute *attr, char *buf)
708{
709 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
710
711 return scnprintf(buf, PAGE_SIZE, "%s\n",
712 str_dcc_data_sink[drvdata->data_sink]);
713}
714
715static ssize_t dcc_store_data_sink(struct device *dev,
716 struct device_attribute *attr,
717 const char *buf, size_t size)
718{
719 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
720 char str[10] = "";
721 int ret;
722
723 if (strlen(buf) >= 10)
724 return -EINVAL;
725 if (sscanf(buf, "%s", str) != 1)
726 return -EINVAL;
727
728 mutex_lock(&drvdata->mutex);
729 if (drvdata->enable[drvdata->curr_list]) {
730 ret = -EBUSY;
731 goto out;
732 }
733
734 if (!strcmp(str, str_dcc_data_sink[DCC_DATA_SINK_SRAM]))
735 drvdata->data_sink = DCC_DATA_SINK_SRAM;
736 else if (!strcmp(str, str_dcc_data_sink[DCC_DATA_SINK_ATB]))
737 drvdata->data_sink = DCC_DATA_SINK_ATB;
738 else {
739 ret = -EINVAL;
740 goto out;
741 }
742
743 ret = size;
744out:
745 mutex_unlock(&drvdata->mutex);
746 return ret;
747}
748static DEVICE_ATTR(data_sink, 0644,
749 dcc_show_data_sink, dcc_store_data_sink);
750
751static ssize_t dcc_store_trigger(struct device *dev,
752 struct device_attribute *attr,
753 const char *buf, size_t size)
754{
755 int ret = 0;
756 unsigned long val;
757 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
758
759 if (kstrtoul(buf, 16, &val))
760 return -EINVAL;
761 if (val != 1)
762 return -EINVAL;
763
764 ret = dcc_sw_trigger(drvdata);
765 if (!ret)
766 ret = size;
767
768 return ret;
769}
770static DEVICE_ATTR(trigger, 0200, NULL, dcc_store_trigger);
771
772static ssize_t dcc_show_enable(struct device *dev,
773 struct device_attribute *attr, char *buf)
774{
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700775 int ret;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800776 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
777
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700778 mutex_lock(&drvdata->mutex);
779 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
780 dev_err(dev, "Select link list to program using curr_list\n");
781 ret = -EINVAL;
782 goto err;
783 }
784
785 ret = scnprintf(buf, PAGE_SIZE, "%u\n",
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800786 (unsigned int)drvdata->enable[drvdata->curr_list]);
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700787err:
788 mutex_unlock(&drvdata->mutex);
789 return ret;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800790}
791
792static ssize_t dcc_store_enable(struct device *dev,
793 struct device_attribute *attr,
794 const char *buf, size_t size)
795{
796 int ret = 0;
797 unsigned long val;
798 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
799
800 if (kstrtoul(buf, 16, &val))
801 return -EINVAL;
802
803 if (val)
804 ret = dcc_enable(drvdata);
805 else
806 dcc_disable(drvdata);
807
808 if (!ret)
809 ret = size;
810
811 return ret;
812
813}
814static DEVICE_ATTR(enable, 0644, dcc_show_enable,
815 dcc_store_enable);
816
817static ssize_t dcc_show_config(struct device *dev,
818 struct device_attribute *attr, char *buf)
819{
820 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
821 struct dcc_config_entry *entry;
822 char local_buf[64];
823 int len = 0, count = 0;
824
825 buf[0] = '\0';
826
827 mutex_lock(&drvdata->mutex);
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700828 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
829 dev_err(dev, "Select link list to program using curr_list\n");
830 count = -EINVAL;
831 goto err;
832 }
833
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800834 list_for_each_entry(entry,
835 &drvdata->cfg_head[drvdata->curr_list], list) {
Satyajit Desai33073622017-04-17 16:45:06 -0700836 switch (entry->desc_type) {
837 case DCC_READ_WRITE_TYPE:
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800838 len = snprintf(local_buf, 64,
839 "Index: 0x%x, mask: 0x%x, val: 0x%x\n",
840 entry->index, entry->mask,
Satyajit Desai33073622017-04-17 16:45:06 -0700841 entry->write_val);
842 break;
843 case DCC_LOOP_TYPE:
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800844 len = snprintf(local_buf, 64, "Index: 0x%x, Loop: %d\n",
845 entry->index, entry->loop_cnt);
Satyajit Desai33073622017-04-17 16:45:06 -0700846 break;
847 case DCC_WRITE_TYPE:
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800848 len = snprintf(local_buf, 64,
Satyajit Desaie95613c2017-05-01 17:04:47 -0700849 "Write Index: 0x%x, Base: 0x%x, Offset: 0x%x, len: 0x%x APB: %d\n",
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800850 entry->index, entry->base,
Satyajit Desaie95613c2017-05-01 17:04:47 -0700851 entry->offset, entry->len,
852 entry->apb_bus);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800853 break;
Satyajit Desai33073622017-04-17 16:45:06 -0700854 default:
855 len = snprintf(local_buf, 64,
Satyajit Desaie95613c2017-05-01 17:04:47 -0700856 "Read Index: 0x%x, Base: 0x%x, Offset: 0x%x, len: 0x%x APB: %d\n",
Satyajit Desai33073622017-04-17 16:45:06 -0700857 entry->index, entry->base,
Satyajit Desaie95613c2017-05-01 17:04:47 -0700858 entry->offset, entry->len,
859 entry->apb_bus);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800860 }
861
Satyajit Desai33073622017-04-17 16:45:06 -0700862 if ((count + len) > PAGE_SIZE) {
863 dev_err(dev, "DCC: Couldn't write complete config\n");
864 break;
865 }
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800866 strlcat(buf, local_buf, PAGE_SIZE);
867 count += len;
868 }
869
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700870err:
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800871 mutex_unlock(&drvdata->mutex);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800872 return count;
873}
874
875static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr,
Satyajit Desaie95613c2017-05-01 17:04:47 -0700876 unsigned int len, int apb_bus)
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800877{
878 int ret;
879 struct dcc_config_entry *entry, *pentry;
880 unsigned int base, offset;
881
882 mutex_lock(&drvdata->mutex);
883
Satyajit Desaic3b82b32017-10-30 15:53:11 -0700884 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
885 dev_err(drvdata->dev, "Select link list to program using curr_list\n");
886 ret = -EINVAL;
887 goto err;
888 }
889
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800890 if (!len) {
Satyajit Desai33073622017-04-17 16:45:06 -0700891 dev_err(drvdata->dev, "DCC: Invalid length\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800892 ret = -EINVAL;
893 goto err;
894 }
895
896 base = addr & BM(4, 31);
897
898 if (!list_empty(&drvdata->cfg_head[drvdata->curr_list])) {
899 pentry = list_last_entry(&drvdata->cfg_head[drvdata->curr_list],
900 struct dcc_config_entry, list);
901
902 if (addr >= (pentry->base + pentry->offset) &&
903 addr <= (pentry->base + pentry->offset + MAX_DCC_OFFSET)) {
904
905 /* Re-use base address from last entry */
906 base = pentry->base;
907
908 /*
909 * Check if new address is contiguous to last entry's
910 * addresses. If yes then we can re-use last entry and
911 * just need to update its length.
912 */
913 if ((pentry->len * 4 + pentry->base + pentry->offset)
914 == addr) {
915 len += pentry->len;
916
917 /*
918 * Check if last entry can hold additional new
919 * length. If yes then we don't need to create
920 * a new entry else we need to add a new entry
921 * with same base but updated offset.
922 */
923 if (len > MAX_DCC_LEN)
924 pentry->len = MAX_DCC_LEN;
925 else
926 pentry->len = len;
927
928 /*
929 * Update start addr and len for remaining
930 * addresses, which will be part of new
931 * entry.
932 */
933 addr = pentry->base + pentry->offset +
934 pentry->len * 4;
935 len -= pentry->len;
936 }
937 }
938 }
939
940 offset = addr - base;
941
942 while (len) {
943 entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
944 if (!entry) {
945 ret = -ENOMEM;
946 goto err;
947 }
948
949 entry->base = base;
950 entry->offset = offset;
951 entry->len = min_t(uint32_t, len, MAX_DCC_LEN);
952 entry->index = drvdata->nr_config[drvdata->curr_list]++;
Satyajit Desai33073622017-04-17 16:45:06 -0700953 entry->desc_type = DCC_ADDR_TYPE;
Satyajit Desaie95613c2017-05-01 17:04:47 -0700954 entry->apb_bus = apb_bus;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800955 INIT_LIST_HEAD(&entry->list);
956 list_add_tail(&entry->list,
957 &drvdata->cfg_head[drvdata->curr_list]);
958
959 len -= entry->len;
960 offset += MAX_DCC_LEN * 4;
961 }
962
963 mutex_unlock(&drvdata->mutex);
964 return 0;
965err:
966 mutex_unlock(&drvdata->mutex);
967 return ret;
968}
969
970static ssize_t dcc_store_config(struct device *dev,
971 struct device_attribute *attr,
972 const char *buf, size_t size)
973{
Satyajit Desaie95613c2017-05-01 17:04:47 -0700974 int ret, len, apb_bus;
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800975 unsigned int base;
976 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
977 int nval;
978
Satyajit Desaie95613c2017-05-01 17:04:47 -0700979 nval = sscanf(buf, "%x %i %d", &base, &len, &apb_bus);
980 if (nval <= 0 || nval > 3)
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800981 return -EINVAL;
982
Satyajit Desaie95613c2017-05-01 17:04:47 -0700983 if (nval == 1) {
984 len = 1;
985 apb_bus = 0;
986 } else if (nval == 2) {
987 apb_bus = 0;
988 } else {
989 apb_bus = 1;
990 }
991
992 ret = dcc_config_add(drvdata, base, len, apb_bus);
Satyajit Desai765e7ef2016-11-09 14:27:45 -0800993 if (ret)
994 return ret;
995
996 return size;
997
998}
999static DEVICE_ATTR(config, 0644, dcc_show_config,
1000 dcc_store_config);
1001
1002static void dcc_config_reset(struct dcc_drvdata *drvdata)
1003{
1004 struct dcc_config_entry *entry, *temp;
1005 int curr_list;
1006
1007 mutex_lock(&drvdata->mutex);
1008
1009 for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
1010
1011 list_for_each_entry_safe(entry, temp,
1012 &drvdata->cfg_head[curr_list], list) {
1013 list_del(&entry->list);
1014 devm_kfree(drvdata->dev, entry);
1015 drvdata->nr_config[curr_list]--;
1016 }
1017 }
1018 drvdata->ram_start = 0;
1019 drvdata->ram_cfg = 0;
1020 mutex_unlock(&drvdata->mutex);
1021}
1022
1023static ssize_t dcc_store_config_reset(struct device *dev,
1024 struct device_attribute *attr,
1025 const char *buf, size_t size)
1026{
1027 unsigned long val;
1028 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1029
1030 if (kstrtoul(buf, 16, &val))
1031 return -EINVAL;
1032
1033 if (val)
1034 dcc_config_reset(drvdata);
1035
1036 return size;
1037}
1038static DEVICE_ATTR(config_reset, 0200, NULL, dcc_store_config_reset);
1039
1040static ssize_t dcc_show_crc_error(struct device *dev,
1041 struct device_attribute *attr, char *buf)
1042{
1043 int ret;
1044 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1045
1046 mutex_lock(&drvdata->mutex);
Satyajit Desaic3b82b32017-10-30 15:53:11 -07001047 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1048 dev_err(dev, "Select link list to program using curr_list\n");
1049 ret = -EINVAL;
1050 goto err;
1051 }
1052
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001053 if (!drvdata->enable[drvdata->curr_list]) {
1054 ret = -EINVAL;
1055 goto err;
1056 }
1057
1058 ret = scnprintf(buf, PAGE_SIZE, "%u\n",
1059 (unsigned int)BVAL(dcc_readl(
1060 drvdata, DCC_LL_INT_STATUS(drvdata->curr_list)), 1));
1061err:
1062 mutex_unlock(&drvdata->mutex);
1063 return ret;
1064}
1065static DEVICE_ATTR(crc_error, 0444, dcc_show_crc_error, NULL);
1066
1067static ssize_t dcc_show_ready(struct device *dev,
1068 struct device_attribute *attr, char *buf)
1069{
1070 int ret;
1071 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1072
1073 mutex_lock(&drvdata->mutex);
Satyajit Desaic3b82b32017-10-30 15:53:11 -07001074
1075 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1076 dev_err(dev, "Select link list to program using curr_list\n");
1077 ret = -EINVAL;
1078 goto err;
1079 }
1080
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001081 if (!drvdata->enable[drvdata->curr_list]) {
1082 ret = -EINVAL;
1083 goto err;
1084 }
1085
1086 ret = scnprintf(buf, PAGE_SIZE, "%u\n",
1087 (unsigned int)BVAL(dcc_readl(drvdata, DCC_STATUS), 1));
1088err:
1089 mutex_unlock(&drvdata->mutex);
1090 return ret;
1091}
1092static DEVICE_ATTR(ready, 0444, dcc_show_ready, NULL);
1093
1094static ssize_t dcc_show_interrupt_disable(struct device *dev,
1095 struct device_attribute *attr,
1096 char *buf)
1097{
1098 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1099
1100 return scnprintf(buf, PAGE_SIZE, "%u\n",
1101 (unsigned int)drvdata->interrupt_disable);
1102}
1103
1104static ssize_t dcc_store_interrupt_disable(struct device *dev,
1105 struct device_attribute *attr,
1106 const char *buf, size_t size)
1107{
1108 unsigned long val;
1109 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1110
1111 if (kstrtoul(buf, 16, &val))
1112 return -EINVAL;
1113
1114 mutex_lock(&drvdata->mutex);
1115 drvdata->interrupt_disable = (val ? 1:0);
1116 mutex_unlock(&drvdata->mutex);
1117 return size;
1118}
1119static DEVICE_ATTR(interrupt_disable, 0644,
1120 dcc_show_interrupt_disable, dcc_store_interrupt_disable);
1121
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001122static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt)
1123{
1124 struct dcc_config_entry *entry;
1125
1126 entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
1127 if (!entry)
1128 return -ENOMEM;
1129
1130 entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT);
1131 entry->index = drvdata->nr_config[drvdata->curr_list]++;
1132 entry->desc_type = DCC_LOOP_TYPE;
1133 INIT_LIST_HEAD(&entry->list);
1134 list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
1135
1136 return 0;
1137}
1138
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001139static ssize_t dcc_store_loop(struct device *dev,
1140 struct device_attribute *attr,
1141 const char *buf, size_t size)
1142{
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001143 int ret;
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001144 unsigned long loop_cnt;
1145 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001146
1147 mutex_lock(&drvdata->mutex);
1148
Satyajit Desai9e1ffcb2017-05-23 12:40:08 -07001149 if (kstrtoul(buf, 16, &loop_cnt)) {
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001150 ret = -EINVAL;
Satyajit Desai9e1ffcb2017-05-23 12:40:08 -07001151 goto err;
1152 }
1153
1154 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1155 dev_err(dev, "Select link list to program using curr_list\n");
1156 ret = -EINVAL;
1157 goto err;
1158 }
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001159
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001160 ret = dcc_add_loop(drvdata, loop_cnt);
1161 if (ret)
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001162 goto err;
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001163
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001164 mutex_unlock(&drvdata->mutex);
1165 return size;
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001166err:
1167 mutex_unlock(&drvdata->mutex);
1168 return ret;
1169}
1170static DEVICE_ATTR(loop, 0200, NULL, dcc_store_loop);
1171
1172static ssize_t dcc_rd_mod_wr(struct device *dev,
1173 struct device_attribute *attr,
1174 const char *buf, size_t size)
1175{
1176 int ret = size;
1177 int nval;
1178 unsigned int mask, val;
1179 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1180 struct dcc_config_entry *entry;
1181
1182 mutex_lock(&drvdata->mutex);
1183
1184 nval = sscanf(buf, "%x %x", &mask, &val);
1185
1186 if (nval <= 1 || nval > 2) {
1187 ret = -EINVAL;
1188 goto err;
1189 }
1190
Satyajit Desaic3b82b32017-10-30 15:53:11 -07001191 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1192 dev_err(dev, "Select link list to program using curr_list\n");
1193 ret = -EINVAL;
1194 goto err;
1195 }
1196
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001197 if (list_empty(&drvdata->cfg_head[drvdata->curr_list])) {
Satyajit Desai33073622017-04-17 16:45:06 -07001198 dev_err(drvdata->dev, "DCC: No read address programmed\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001199 ret = -EPERM;
1200 goto err;
1201 }
1202
1203 entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
1204 if (!entry) {
1205 ret = -ENOMEM;
1206 goto err;
1207 }
1208
Satyajit Desai33073622017-04-17 16:45:06 -07001209 entry->desc_type = DCC_READ_WRITE_TYPE;
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001210 entry->mask = mask;
Satyajit Desai33073622017-04-17 16:45:06 -07001211 entry->write_val = val;
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001212 entry->index = drvdata->nr_config[drvdata->curr_list]++;
1213 INIT_LIST_HEAD(&entry->list);
1214 list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
1215err:
1216 mutex_unlock(&drvdata->mutex);
1217 return ret;
1218}
1219static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr);
1220
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001221static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr,
1222 unsigned int write_val, int apb_bus)
1223{
1224 struct dcc_config_entry *entry;
1225
1226 entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
1227 if (!entry)
1228 return -ENOMEM;
1229
1230 entry->desc_type = DCC_WRITE_TYPE;
1231 entry->base = addr & BM(4, 31);
1232 entry->offset = addr - entry->base;
1233 entry->write_val = write_val;
1234 entry->index = drvdata->nr_config[drvdata->curr_list]++;
1235 entry->len = 1;
1236 entry->apb_bus = apb_bus;
1237 INIT_LIST_HEAD(&entry->list);
1238 list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
1239
1240 return 0;
1241}
1242
Satyajit Desai33073622017-04-17 16:45:06 -07001243static ssize_t dcc_write(struct device *dev,
1244 struct device_attribute *attr,
1245 const char *buf, size_t size)
1246{
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001247 int ret;
Satyajit Desai33073622017-04-17 16:45:06 -07001248 int nval;
1249 unsigned int addr, write_val;
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001250 int apb_bus = 0;
Satyajit Desai33073622017-04-17 16:45:06 -07001251 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
Satyajit Desai33073622017-04-17 16:45:06 -07001252
1253 mutex_lock(&drvdata->mutex);
1254
Satyajit Desaie95613c2017-05-01 17:04:47 -07001255 nval = sscanf(buf, "%x %x %d", &addr, &write_val, &apb_bus);
Satyajit Desai33073622017-04-17 16:45:06 -07001256
Satyajit Desai9e1ffcb2017-05-23 12:40:08 -07001257 if (nval <= 1 || nval > 3) {
1258 ret = -EINVAL;
1259 goto err;
Satyajit Desai33073622017-04-17 16:45:06 -07001260 }
1261
Satyajit Desai9e1ffcb2017-05-23 12:40:08 -07001262 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1263 dev_err(dev, "Select link list to program using curr_list\n");
Satyajit Desai33073622017-04-17 16:45:06 -07001264 ret = -EINVAL;
1265 goto err;
1266 }
1267
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001268 if (nval == 3 && apb_bus != 0)
1269 apb_bus = 1;
1270
1271 ret = dcc_add_write(drvdata, addr, write_val, apb_bus);
1272 if (ret)
Satyajit Desai33073622017-04-17 16:45:06 -07001273 goto err;
Satyajit Desai33073622017-04-17 16:45:06 -07001274
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001275 mutex_unlock(&drvdata->mutex);
1276 return size;
Satyajit Desai33073622017-04-17 16:45:06 -07001277err:
1278 mutex_unlock(&drvdata->mutex);
1279 return ret;
1280}
1281static DEVICE_ATTR(config_write, 0200, NULL, dcc_write);
1282
Satyajit Desaife1311b2017-05-19 16:02:35 -07001283static ssize_t dcc_show_cti_trig(struct device *dev,
1284 struct device_attribute *attr, char *buf)
1285{
1286 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1287
1288 return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->cti_trig);
1289}
1290
1291static ssize_t dcc_store_cti_trig(struct device *dev,
1292 struct device_attribute *attr,
1293 const char *buf, size_t size)
1294{
1295 unsigned long val;
1296 int ret = 0;
1297 struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
1298
1299 if (kstrtoul(buf, 16, &val))
1300 return -EINVAL;
1301
1302 mutex_lock(&drvdata->mutex);
1303
Satyajit Desaic3b82b32017-10-30 15:53:11 -07001304 if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
1305 dev_err(dev, "Select link list to program using curr_list\n");
1306 ret = -EINVAL;
1307 goto out;
1308 }
1309
Satyajit Desaife1311b2017-05-19 16:02:35 -07001310 if (drvdata->enable[drvdata->curr_list]) {
1311 ret = -EBUSY;
1312 goto out;
1313 }
1314
1315 if (val)
1316 drvdata->cti_trig = 1;
1317 else
1318 drvdata->cti_trig = 0;
1319out:
1320 mutex_unlock(&drvdata->mutex);
1321 return ret;
1322}
1323static DEVICE_ATTR(cti_trig, 0644,
1324 dcc_show_cti_trig, dcc_store_cti_trig);
1325
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001326static const struct device_attribute *dcc_attrs[] = {
1327 &dev_attr_func_type,
1328 &dev_attr_data_sink,
1329 &dev_attr_trigger,
1330 &dev_attr_enable,
1331 &dev_attr_config,
1332 &dev_attr_config_reset,
1333 &dev_attr_ready,
1334 &dev_attr_crc_error,
1335 &dev_attr_interrupt_disable,
1336 &dev_attr_loop,
1337 &dev_attr_rd_mod_wr,
1338 &dev_attr_curr_list,
Satyajit Desai33073622017-04-17 16:45:06 -07001339 &dev_attr_config_write,
Satyajit Desaife1311b2017-05-19 16:02:35 -07001340 &dev_attr_cti_trig,
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001341 NULL,
1342};
1343
1344static int dcc_create_files(struct device *dev,
1345 const struct device_attribute **attrs)
1346{
1347 int ret = 0, i;
1348
1349 for (i = 0; attrs[i] != NULL; i++) {
1350 ret = device_create_file(dev, attrs[i]);
1351 if (ret) {
Satyajit Desai33073622017-04-17 16:45:06 -07001352 dev_err(dev, "DCC: Couldn't create sysfs attribute: %s\n",
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001353 attrs[i]->attr.name);
1354 break;
1355 }
1356 }
1357 return ret;
1358}
1359
1360static int dcc_sram_open(struct inode *inode, struct file *file)
1361{
1362 struct dcc_drvdata *drvdata = container_of(inode->i_cdev,
1363 struct dcc_drvdata,
1364 sram_dev);
1365 file->private_data = drvdata;
1366
1367 return 0;
1368}
1369
1370static ssize_t dcc_sram_read(struct file *file, char __user *data,
1371 size_t len, loff_t *ppos)
1372{
1373 unsigned char *buf;
1374 struct dcc_drvdata *drvdata = file->private_data;
1375
1376 /* EOF check */
1377 if (drvdata->ram_size <= *ppos)
1378 return 0;
1379
1380 if ((*ppos + len) > drvdata->ram_size)
1381 len = (drvdata->ram_size - *ppos);
1382
1383 buf = kzalloc(len, GFP_KERNEL);
1384 if (!buf)
1385 return -ENOMEM;
1386
1387 memcpy_fromio(buf, (drvdata->ram_base + *ppos), len);
1388
1389 if (copy_to_user(data, buf, len)) {
1390 dev_err(drvdata->dev,
Satyajit Desai33073622017-04-17 16:45:06 -07001391 "DCC: Couldn't copy all data to user\n");
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001392 kfree(buf);
1393 return -EFAULT;
1394 }
1395
1396 *ppos += len;
1397
1398 kfree(buf);
1399
1400 return len;
1401}
1402
1403static const struct file_operations dcc_sram_fops = {
1404 .owner = THIS_MODULE,
1405 .open = dcc_sram_open,
1406 .read = dcc_sram_read,
1407 .llseek = no_llseek,
1408};
1409
1410static int dcc_sram_dev_register(struct dcc_drvdata *drvdata)
1411{
1412 int ret;
1413 struct device *device;
1414 dev_t dev;
1415
1416 ret = alloc_chrdev_region(&dev, 0, 1, drvdata->sram_node);
1417 if (ret)
1418 goto err_alloc;
1419
1420 cdev_init(&drvdata->sram_dev, &dcc_sram_fops);
1421
1422 drvdata->sram_dev.owner = THIS_MODULE;
1423 ret = cdev_add(&drvdata->sram_dev, dev, 1);
1424 if (ret)
1425 goto err_cdev_add;
1426
1427 drvdata->sram_class = class_create(THIS_MODULE,
1428 drvdata->sram_node);
1429 if (IS_ERR(drvdata->sram_class)) {
1430 ret = PTR_ERR(drvdata->sram_class);
1431 goto err_class_create;
1432 }
1433
1434 device = device_create(drvdata->sram_class, NULL,
1435 drvdata->sram_dev.dev, drvdata,
1436 drvdata->sram_node);
1437 if (IS_ERR(device)) {
1438 ret = PTR_ERR(device);
1439 goto err_dev_create;
1440 }
1441
1442 return 0;
1443err_dev_create:
1444 class_destroy(drvdata->sram_class);
1445err_class_create:
1446 cdev_del(&drvdata->sram_dev);
1447err_cdev_add:
1448 unregister_chrdev_region(drvdata->sram_dev.dev, 1);
1449err_alloc:
1450 return ret;
1451}
1452
1453static void dcc_sram_dev_deregister(struct dcc_drvdata *drvdata)
1454{
1455 device_destroy(drvdata->sram_class, drvdata->sram_dev.dev);
1456 class_destroy(drvdata->sram_class);
1457 cdev_del(&drvdata->sram_dev);
1458 unregister_chrdev_region(drvdata->sram_dev.dev, 1);
1459}
1460
1461static int dcc_sram_dev_init(struct dcc_drvdata *drvdata)
1462{
1463 int ret = 0;
1464 size_t node_size;
1465 char *node_name = "dcc_sram";
1466 struct device *dev = drvdata->dev;
1467
1468 node_size = strlen(node_name) + 1;
1469
1470 drvdata->sram_node = devm_kzalloc(dev, node_size, GFP_KERNEL);
1471 if (!drvdata->sram_node)
1472 return -ENOMEM;
1473
1474 strlcpy(drvdata->sram_node, node_name, node_size);
1475 ret = dcc_sram_dev_register(drvdata);
1476 if (ret)
1477 dev_err(drvdata->dev, "DCC: sram node not registered.\n");
1478
1479 return ret;
1480}
1481
1482static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata)
1483{
1484 dcc_sram_dev_deregister(drvdata);
1485}
1486
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001487static void dcc_configure_list(struct dcc_drvdata *drvdata,
1488 struct device_node *np)
1489{
1490 int ret, i;
1491 const __be32 *prop;
1492 uint32_t len, entry, val1, val2, apb_bus;
1493 uint32_t curr_link_list;
1494
1495 ret = of_property_read_u32(np, "qcom,curr-link-list",
1496 &curr_link_list);
1497 if (ret)
1498 return;
1499
1500 if (curr_link_list >= DCC_MAX_LINK_LIST) {
1501 dev_err(drvdata->dev, "List configuration failed");
1502 return;
1503 }
1504 drvdata->curr_list = curr_link_list;
1505
1506 prop = of_get_property(np, "qcom,link-list", &len);
1507 if (prop) {
1508 len /= sizeof(__be32);
1509 i = 0;
1510 while (i < len) {
1511 entry = be32_to_cpu(prop[i++]);
1512 val1 = be32_to_cpu(prop[i++]);
1513 val2 = be32_to_cpu(prop[i++]);
1514 apb_bus = be32_to_cpu(prop[i++]);
1515
1516 switch (entry) {
1517 case DCC_READ:
1518 ret = dcc_config_add(drvdata, val1,
1519 val2, apb_bus);
1520 break;
1521 case DCC_WRITE:
1522 ret = dcc_add_write(drvdata, val1,
1523 val2, apb_bus);
1524 break;
1525 case DCC_LOOP:
1526 ret = dcc_add_loop(drvdata, val1);
1527 break;
1528 default:
1529 ret = -EINVAL;
1530 }
1531
1532 if (ret) {
1533 dev_err(drvdata->dev,
1534 "DCC init time config failed err:%d\n",
1535 ret);
1536 break;
1537 }
1538 }
1539
1540 if (!ret)
1541 dcc_enable(drvdata);
1542 }
1543}
1544
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001545static int dcc_probe(struct platform_device *pdev)
1546{
1547 int ret, i;
1548 struct device *dev = &pdev->dev;
1549 struct dcc_drvdata *drvdata;
1550 struct resource *res;
1551 const char *data_sink;
1552
1553 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
1554 if (!drvdata)
1555 return -ENOMEM;
1556
1557 drvdata->dev = &pdev->dev;
1558 platform_set_drvdata(pdev, drvdata);
1559
1560 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dcc-base");
1561 if (!res)
1562 return -EINVAL;
1563
1564 drvdata->reg_size = resource_size(res);
1565 drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
1566 if (!drvdata->base)
1567 return -ENOMEM;
1568
1569 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
1570 "dcc-ram-base");
1571 if (!res)
1572 return -EINVAL;
1573
1574 drvdata->ram_size = resource_size(res);
1575 drvdata->ram_base = devm_ioremap(dev, res->start, resource_size(res));
1576 if (!drvdata->ram_base)
1577 return -ENOMEM;
1578
Satyajit Desai3d86e1b2017-04-13 17:11:09 -07001579 ret = of_property_read_u32(pdev->dev.of_node, "dcc-ram-offset",
1580 &drvdata->ram_offset);
1581 if (ret)
1582 return -EINVAL;
1583
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001584 mutex_init(&drvdata->mutex);
1585
1586 for (i = 0; i < DCC_MAX_LINK_LIST; i++) {
1587 INIT_LIST_HEAD(&drvdata->cfg_head[i]);
1588 drvdata->nr_config[i] = 0;
1589 }
1590
1591 memset_io(drvdata->ram_base, 0, drvdata->ram_size);
1592
1593 drvdata->data_sink = DCC_DATA_SINK_SRAM;
1594 ret = of_property_read_string(pdev->dev.of_node, "qcom,data-sink",
1595 &data_sink);
1596 if (!ret) {
1597 for (i = 0; i < ARRAY_SIZE(str_dcc_data_sink); i++)
1598 if (!strcmp(data_sink, str_dcc_data_sink[i])) {
1599 drvdata->data_sink = i;
1600 break;
1601 }
1602
1603 if (i == ARRAY_SIZE(str_dcc_data_sink)) {
Satyajit Desai33073622017-04-17 16:45:06 -07001604 dev_err(dev, "Unknown sink type for DCC Using '%s' as data sink\n",
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001605 str_dcc_data_sink[drvdata->data_sink]);
1606 }
1607 }
1608
1609 drvdata->curr_list = DCC_INVALID_LINK_LIST;
1610
1611 ret = dcc_sram_dev_init(drvdata);
1612 if (ret)
1613 goto err;
1614
1615 ret = dcc_create_files(dev, dcc_attrs);
1616 if (ret)
1617 goto err;
1618
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001619 dcc_configure_list(drvdata, pdev->dev.of_node);
1620
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001621 return 0;
1622err:
1623 return ret;
1624}
1625
1626static int dcc_remove(struct platform_device *pdev)
1627{
1628 struct dcc_drvdata *drvdata = platform_get_drvdata(pdev);
1629
1630 dcc_sram_dev_exit(drvdata);
1631
1632 dcc_config_reset(drvdata);
1633
1634 return 0;
1635}
1636
1637static const struct of_device_id msm_dcc_match[] = {
Mao Jinlong99337ac2018-04-09 14:36:26 +08001638 { .compatible = "qcom,dcc-v2"},
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001639 {}
1640};
1641
1642static struct platform_driver dcc_driver = {
1643 .probe = dcc_probe,
1644 .remove = dcc_remove,
1645 .driver = {
1646 .name = "msm-dcc",
1647 .owner = THIS_MODULE,
1648 .of_match_table = msm_dcc_match,
1649 },
1650};
1651
1652static int __init dcc_init(void)
1653{
1654 return platform_driver_register(&dcc_driver);
1655}
Satyajit Desaiaf71a5a2017-09-29 14:30:43 -07001656pure_initcall(dcc_init);
Satyajit Desai765e7ef2016-11-09 14:27:45 -08001657
1658MODULE_LICENSE("GPL v2");
1659MODULE_DESCRIPTION("MSM data capture and compare engine");