blob: 43a53246abb332233a1d3c24749fc659bc572054 [file] [log] [blame]
Markus Mayer2f330ca2017-08-24 16:36:26 -07001/*
2 * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
3 *
4 * Copyright (c) 2017 Broadcom
5 *
6 * Released under the GPLv2 only.
7 * SPDX-License-Identifier: GPL-2.0
8 */
9
10/*
11 * This driver provides access to the DPFE interface of Broadcom STB SoCs.
12 * The firmware running on the DCPU inside the DDR PHY can provide current
13 * information about the system's RAM, for instance the DRAM refresh rate.
14 * This can be used as an indirect indicator for the DRAM's temperature.
15 * Slower refresh rate means cooler RAM, higher refresh rate means hotter
16 * RAM.
17 *
18 * Throughout the driver, we use readl_relaxed() and writel_relaxed(), which
19 * already contain the appropriate le32_to_cpu()/cpu_to_le32() calls.
20 *
21 * Note regarding the loading of the firmware image: we use be32_to_cpu()
22 * and le_32_to_cpu(), so we can support the following four cases:
23 * - LE kernel + LE firmware image (the most common case)
24 * - LE kernel + BE firmware image
25 * - BE kernel + LE firmware image
26 * - BE kernel + BE firmware image
27 *
28 * The DPCU always runs in big endian mode. The firwmare image, however, can
29 * be in either format. Also, communication between host CPU and DCPU is
30 * always in little endian.
31 */
32
33#include <linux/delay.h>
34#include <linux/firmware.h>
35#include <linux/io.h>
36#include <linux/module.h>
37#include <linux/of_address.h>
Markus Mayer58a84992019-04-02 16:01:01 -070038#include <linux/of_device.h>
Markus Mayer2f330ca2017-08-24 16:36:26 -070039#include <linux/platform_device.h>
40
41#define DRVNAME "brcmstb-dpfe"
Markus Mayer2f330ca2017-08-24 16:36:26 -070042
43/* DCPU register offsets */
44#define REG_DCPU_RESET 0x0
45#define REG_TO_DCPU_MBOX 0x10
46#define REG_TO_HOST_MBOX 0x14
47
Markus Mayerfee5f1e2018-02-13 12:40:40 -080048/* Macros to process offsets returned by the DCPU */
49#define DRAM_MSG_ADDR_OFFSET 0x0
50#define DRAM_MSG_TYPE_OFFSET 0x1c
51#define DRAM_MSG_ADDR_MASK ((1UL << DRAM_MSG_TYPE_OFFSET) - 1)
52#define DRAM_MSG_TYPE_MASK ((1UL << \
53 (BITS_PER_LONG - DRAM_MSG_TYPE_OFFSET)) - 1)
54
Markus Mayer2f330ca2017-08-24 16:36:26 -070055/* Message RAM */
Markus Mayerfee5f1e2018-02-13 12:40:40 -080056#define DCPU_MSG_RAM_START 0x100
57#define DCPU_MSG_RAM(x) (DCPU_MSG_RAM_START + (x) * sizeof(u32))
Markus Mayer2f330ca2017-08-24 16:36:26 -070058
59/* DRAM Info Offsets & Masks */
60#define DRAM_INFO_INTERVAL 0x0
61#define DRAM_INFO_MR4 0x4
62#define DRAM_INFO_ERROR 0x8
63#define DRAM_INFO_MR4_MASK 0xff
Markus Mayer78a6f5b2019-02-11 17:24:43 -080064#define DRAM_INFO_MR4_SHIFT 24 /* We need to look at byte 3 */
Markus Mayer2f330ca2017-08-24 16:36:26 -070065
66/* DRAM MR4 Offsets & Masks */
67#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */
68#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */
69#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */
70#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */
71#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */
72
73#define DRAM_MR4_REFRESH_MASK 0x7
74#define DRAM_MR4_SR_ABORT_MASK 0x1
75#define DRAM_MR4_PPRE_MASK 0x1
76#define DRAM_MR4_TH_OFFS_MASK 0x3
77#define DRAM_MR4_TUF_MASK 0x1
78
79/* DRAM Vendor Offsets & Masks */
80#define DRAM_VENDOR_MR5 0x0
81#define DRAM_VENDOR_MR6 0x4
82#define DRAM_VENDOR_MR7 0x8
83#define DRAM_VENDOR_MR8 0xc
84#define DRAM_VENDOR_ERROR 0x10
85#define DRAM_VENDOR_MASK 0xff
Markus Mayer78a6f5b2019-02-11 17:24:43 -080086#define DRAM_VENDOR_SHIFT 24 /* We need to look at byte 3 */
Markus Mayer2f330ca2017-08-24 16:36:26 -070087
88/* Reset register bits & masks */
89#define DCPU_RESET_SHIFT 0x0
90#define DCPU_RESET_MASK 0x1
91#define DCPU_CLK_DISABLE_SHIFT 0x2
92
93/* DCPU return codes */
94#define DCPU_RET_ERROR_BIT BIT(31)
95#define DCPU_RET_SUCCESS 0x1
96#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0))
97#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1))
98#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2))
99#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3))
100/* This error code is not firmware defined and only used in the driver. */
101#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4))
102
103/* Firmware magic */
104#define DPFE_BE_MAGIC 0xfe1010fe
105#define DPFE_LE_MAGIC 0xfe0101fe
106
107/* Error codes */
108#define ERR_INVALID_MAGIC -1
109#define ERR_INVALID_SIZE -2
110#define ERR_INVALID_CHKSUM -3
111
112/* Message types */
113#define DPFE_MSG_TYPE_COMMAND 1
114#define DPFE_MSG_TYPE_RESPONSE 2
115
Markus Mayer7ccd2ff2019-02-11 17:24:41 -0800116#define DELAY_LOOP_MAX 1000
Markus Mayer2f330ca2017-08-24 16:36:26 -0700117
118enum dpfe_msg_fields {
119 MSG_HEADER,
120 MSG_COMMAND,
121 MSG_ARG_COUNT,
122 MSG_ARG0,
123 MSG_CHKSUM,
124 MSG_FIELD_MAX /* Last entry */
125};
126
127enum dpfe_commands {
128 DPFE_CMD_GET_INFO,
129 DPFE_CMD_GET_REFRESH,
130 DPFE_CMD_GET_VENDOR,
131 DPFE_CMD_MAX /* Last entry */
132};
133
Markus Mayer2f330ca2017-08-24 16:36:26 -0700134/*
135 * Format of the binary firmware file:
136 *
137 * entry
138 * 0 header
139 * value: 0xfe0101fe <== little endian
140 * 0xfe1010fe <== big endian
141 * 1 sequence:
142 * [31:16] total segments on this build
143 * [15:0] this segment sequence.
144 * 2 FW version
145 * 3 IMEM byte size
146 * 4 DMEM byte size
147 * IMEM
148 * DMEM
149 * last checksum ==> sum of everything
150 */
151struct dpfe_firmware_header {
152 u32 magic;
153 u32 sequence;
154 u32 version;
155 u32 imem_size;
156 u32 dmem_size;
157};
158
159/* Things we only need during initialization. */
160struct init_data {
161 unsigned int dmem_len;
162 unsigned int imem_len;
163 unsigned int chksum;
164 bool is_big_endian;
165};
166
Markus Mayer58a84992019-04-02 16:01:01 -0700167/* API version and corresponding commands */
168struct dpfe_api {
169 int version;
170 const char *fw_name;
Markus Mayer5ef108b2019-04-02 16:01:02 -0700171 const struct attribute_group **sysfs_attrs;
Markus Mayer58a84992019-04-02 16:01:01 -0700172 u32 command[DPFE_CMD_MAX][MSG_FIELD_MAX];
173};
174
Markus Mayer2f330ca2017-08-24 16:36:26 -0700175/* Things we need for as long as we are active. */
176struct private_data {
177 void __iomem *regs;
178 void __iomem *dmem;
179 void __iomem *imem;
180 struct device *dev;
Markus Mayer58a84992019-04-02 16:01:01 -0700181 const struct dpfe_api *dpfe_api;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700182 struct mutex lock;
183};
184
185static const char *error_text[] = {
186 "Success", "Header code incorrect", "Unknown command or argument",
187 "Incorrect checksum", "Malformed command", "Timed out",
188};
189
Markus Mayer5ef108b2019-04-02 16:01:02 -0700190/*
191 * Forward declaration of our sysfs attribute functions, so we can declare the
192 * attribute data structures early.
193 */
194static ssize_t show_info(struct device *, struct device_attribute *, char *);
195static ssize_t show_refresh(struct device *, struct device_attribute *, char *);
196static ssize_t store_refresh(struct device *, struct device_attribute *,
197 const char *, size_t);
198static ssize_t show_vendor(struct device *, struct device_attribute *, char *);
199
200/*
201 * Declare our attributes early, so they can be referenced in the API data
202 * structure. We need to do this, because the attributes depend on the API
203 * version.
204 */
205static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
206static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
207static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
208
209/* API v2 sysfs attributes */
210static struct attribute *dpfe_v2_attrs[] = {
211 &dev_attr_dpfe_info.attr,
212 &dev_attr_dpfe_refresh.attr,
213 &dev_attr_dpfe_vendor.attr,
214 NULL
215};
216ATTRIBUTE_GROUPS(dpfe_v2);
217
Markus Mayer58a84992019-04-02 16:01:01 -0700218/* API v2 firmware commands */
219static const struct dpfe_api dpfe_api_v2 = {
220 .version = 2,
221 .fw_name = "dpfe.bin",
Markus Mayer5ef108b2019-04-02 16:01:02 -0700222 .sysfs_attrs = dpfe_v2_groups,
Markus Mayer58a84992019-04-02 16:01:01 -0700223 .command = {
224 [DPFE_CMD_GET_INFO] = {
225 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
226 [MSG_COMMAND] = 1,
227 [MSG_ARG_COUNT] = 1,
228 [MSG_ARG0] = 1,
229 [MSG_CHKSUM] = 4,
230 },
231 [DPFE_CMD_GET_REFRESH] = {
232 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
233 [MSG_COMMAND] = 2,
234 [MSG_ARG_COUNT] = 1,
235 [MSG_ARG0] = 1,
236 [MSG_CHKSUM] = 5,
237 },
238 [DPFE_CMD_GET_VENDOR] = {
239 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
240 [MSG_COMMAND] = 2,
241 [MSG_ARG_COUNT] = 1,
242 [MSG_ARG0] = 2,
243 [MSG_CHKSUM] = 6,
244 },
245 }
Markus Mayer2f330ca2017-08-24 16:36:26 -0700246};
247
Markus Mayerd56e7462017-10-02 16:13:46 -0700248static bool is_dcpu_enabled(void __iomem *regs)
249{
250 u32 val;
251
252 val = readl_relaxed(regs + REG_DCPU_RESET);
253
254 return !(val & DCPU_RESET_MASK);
255}
256
Markus Mayer2f330ca2017-08-24 16:36:26 -0700257static void __disable_dcpu(void __iomem *regs)
258{
259 u32 val;
260
Markus Mayerd56e7462017-10-02 16:13:46 -0700261 if (!is_dcpu_enabled(regs))
262 return;
263
264 /* Put DCPU in reset if it's running. */
Markus Mayer2f330ca2017-08-24 16:36:26 -0700265 val = readl_relaxed(regs + REG_DCPU_RESET);
Markus Mayerd56e7462017-10-02 16:13:46 -0700266 val |= (1 << DCPU_RESET_SHIFT);
267 writel_relaxed(val, regs + REG_DCPU_RESET);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700268}
269
270static void __enable_dcpu(void __iomem *regs)
271{
272 u32 val;
273
274 /* Clear mailbox registers. */
275 writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
276 writel_relaxed(0, regs + REG_TO_HOST_MBOX);
277
278 /* Disable DCPU clock gating */
279 val = readl_relaxed(regs + REG_DCPU_RESET);
280 val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
281 writel_relaxed(val, regs + REG_DCPU_RESET);
282
283 /* Take DCPU out of reset */
284 val = readl_relaxed(regs + REG_DCPU_RESET);
285 val &= ~(1 << DCPU_RESET_SHIFT);
286 writel_relaxed(val, regs + REG_DCPU_RESET);
287}
288
289static unsigned int get_msg_chksum(const u32 msg[])
290{
291 unsigned int sum = 0;
292 unsigned int i;
293
294 /* Don't include the last field in the checksum. */
295 for (i = 0; i < MSG_FIELD_MAX - 1; i++)
296 sum += msg[i];
297
298 return sum;
299}
300
Markus Mayerfee5f1e2018-02-13 12:40:40 -0800301static void __iomem *get_msg_ptr(struct private_data *priv, u32 response,
302 char *buf, ssize_t *size)
303{
304 unsigned int msg_type;
305 unsigned int offset;
306 void __iomem *ptr = NULL;
307
308 msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
309 offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
310
311 /*
312 * msg_type == 1: the offset is relative to the message RAM
313 * msg_type == 0: the offset is relative to the data RAM (this is the
314 * previous way of passing data)
315 * msg_type is anything else: there's critical hardware problem
316 */
317 switch (msg_type) {
318 case 1:
319 ptr = priv->regs + DCPU_MSG_RAM_START + offset;
320 break;
321 case 0:
322 ptr = priv->dmem + offset;
323 break;
324 default:
325 dev_emerg(priv->dev, "invalid message reply from DCPU: %#x\n",
326 response);
327 if (buf && size)
328 *size = sprintf(buf,
329 "FATAL: communication error with DCPU\n");
330 }
331
332 return ptr;
333}
334
Markus Mayer2f330ca2017-08-24 16:36:26 -0700335static int __send_command(struct private_data *priv, unsigned int cmd,
336 u32 result[])
337{
Markus Mayer58a84992019-04-02 16:01:01 -0700338 const u32 *msg = priv->dpfe_api->command[cmd];
Markus Mayer2f330ca2017-08-24 16:36:26 -0700339 void __iomem *regs = priv->regs;
340 unsigned int i, chksum;
341 int ret = 0;
342 u32 resp;
343
344 if (cmd >= DPFE_CMD_MAX)
345 return -1;
346
347 mutex_lock(&priv->lock);
348
Markus Mayera7c25752019-04-02 16:01:00 -0700349 /* Wait for DCPU to become ready */
350 for (i = 0; i < DELAY_LOOP_MAX; i++) {
351 resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
352 if (resp == 0)
353 break;
354 msleep(1);
355 }
356 if (resp != 0) {
357 mutex_unlock(&priv->lock);
358 return -ETIMEDOUT;
359 }
360
Markus Mayer2f330ca2017-08-24 16:36:26 -0700361 /* Write command and arguments to message area */
362 for (i = 0; i < MSG_FIELD_MAX; i++)
363 writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
364
365 /* Tell DCPU there is a command waiting */
366 writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
367
368 /* Wait for DCPU to process the command */
369 for (i = 0; i < DELAY_LOOP_MAX; i++) {
370 /* Read response code */
371 resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
372 if (resp > 0)
373 break;
Markus Mayer7ccd2ff2019-02-11 17:24:41 -0800374 msleep(1);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700375 }
376
377 if (i == DELAY_LOOP_MAX) {
378 resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
379 ret = -ffs(resp);
380 } else {
381 /* Read response data */
382 for (i = 0; i < MSG_FIELD_MAX; i++)
383 result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
384 }
385
386 /* Tell DCPU we are done */
387 writel_relaxed(0, regs + REG_TO_HOST_MBOX);
388
389 mutex_unlock(&priv->lock);
390
391 if (ret)
392 return ret;
393
394 /* Verify response */
395 chksum = get_msg_chksum(result);
396 if (chksum != result[MSG_CHKSUM])
397 resp = DCPU_RET_ERR_CHKSUM;
398
399 if (resp != DCPU_RET_SUCCESS) {
400 resp &= ~DCPU_RET_ERROR_BIT;
401 ret = -ffs(resp);
402 }
403
404 return ret;
405}
406
407/* Ensure that the firmware file loaded meets all the requirements. */
408static int __verify_firmware(struct init_data *init,
409 const struct firmware *fw)
410{
411 const struct dpfe_firmware_header *header = (void *)fw->data;
412 unsigned int dmem_size, imem_size, total_size;
413 bool is_big_endian = false;
414 const u32 *chksum_ptr;
415
416 if (header->magic == DPFE_BE_MAGIC)
417 is_big_endian = true;
418 else if (header->magic != DPFE_LE_MAGIC)
419 return ERR_INVALID_MAGIC;
420
421 if (is_big_endian) {
422 dmem_size = be32_to_cpu(header->dmem_size);
423 imem_size = be32_to_cpu(header->imem_size);
424 } else {
425 dmem_size = le32_to_cpu(header->dmem_size);
426 imem_size = le32_to_cpu(header->imem_size);
427 }
428
429 /* Data and instruction sections are 32 bit words. */
430 if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
431 return ERR_INVALID_SIZE;
432
433 /*
434 * The header + the data section + the instruction section + the
435 * checksum must be equal to the total firmware size.
436 */
437 total_size = dmem_size + imem_size + sizeof(*header) +
438 sizeof(*chksum_ptr);
439 if (total_size != fw->size)
440 return ERR_INVALID_SIZE;
441
442 /* The checksum comes at the very end. */
443 chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
444
445 init->is_big_endian = is_big_endian;
446 init->dmem_len = dmem_size;
447 init->imem_len = imem_size;
448 init->chksum = (is_big_endian)
449 ? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
450
451 return 0;
452}
453
454/* Verify checksum by reading back the firmware from co-processor RAM. */
455static int __verify_fw_checksum(struct init_data *init,
456 struct private_data *priv,
457 const struct dpfe_firmware_header *header,
458 u32 checksum)
459{
460 u32 magic, sequence, version, sum;
461 u32 __iomem *dmem = priv->dmem;
462 u32 __iomem *imem = priv->imem;
463 unsigned int i;
464
465 if (init->is_big_endian) {
466 magic = be32_to_cpu(header->magic);
467 sequence = be32_to_cpu(header->sequence);
468 version = be32_to_cpu(header->version);
469 } else {
470 magic = le32_to_cpu(header->magic);
471 sequence = le32_to_cpu(header->sequence);
472 version = le32_to_cpu(header->version);
473 }
474
475 sum = magic + sequence + version + init->dmem_len + init->imem_len;
476
477 for (i = 0; i < init->dmem_len / sizeof(u32); i++)
478 sum += readl_relaxed(dmem + i);
479
480 for (i = 0; i < init->imem_len / sizeof(u32); i++)
481 sum += readl_relaxed(imem + i);
482
483 return (sum == checksum) ? 0 : -1;
484}
485
486static int __write_firmware(u32 __iomem *mem, const u32 *fw,
487 unsigned int size, bool is_big_endian)
488{
489 unsigned int i;
490
491 /* Convert size to 32-bit words. */
492 size /= sizeof(u32);
493
494 /* It is recommended to clear the firmware area first. */
495 for (i = 0; i < size; i++)
496 writel_relaxed(0, mem + i);
497
498 /* Now copy it. */
499 if (is_big_endian) {
500 for (i = 0; i < size; i++)
501 writel_relaxed(be32_to_cpu(fw[i]), mem + i);
502 } else {
503 for (i = 0; i < size; i++)
504 writel_relaxed(le32_to_cpu(fw[i]), mem + i);
505 }
506
507 return 0;
508}
509
510static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
511 struct init_data *init)
512{
513 const struct dpfe_firmware_header *header;
514 unsigned int dmem_size, imem_size;
515 struct device *dev = &pdev->dev;
516 bool is_big_endian = false;
517 struct private_data *priv;
518 const struct firmware *fw;
519 const u32 *dmem, *imem;
520 const void *fw_blob;
521 int ret;
522
Markus Mayera56d3392017-10-02 16:13:47 -0700523 priv = platform_get_drvdata(pdev);
524
525 /*
526 * Skip downloading the firmware if the DCPU is already running and
527 * responding to commands.
528 */
529 if (is_dcpu_enabled(priv->regs)) {
530 u32 response[MSG_FIELD_MAX];
531
532 ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
533 if (!ret)
534 return 0;
535 }
536
Markus Mayer58a84992019-04-02 16:01:01 -0700537 /*
538 * If the firmware filename is NULL it means the boot firmware has to
539 * download the DCPU firmware for us. If that didn't work, we have to
540 * bail, since downloading it ourselves wouldn't work either.
541 */
542 if (!priv->dpfe_api->fw_name)
543 return -ENODEV;
544
545 ret = request_firmware(&fw, priv->dpfe_api->fw_name, dev);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700546 /* request_firmware() prints its own error messages. */
547 if (ret)
548 return ret;
549
Markus Mayer2f330ca2017-08-24 16:36:26 -0700550 ret = __verify_firmware(init, fw);
551 if (ret)
552 return -EFAULT;
553
554 __disable_dcpu(priv->regs);
555
556 is_big_endian = init->is_big_endian;
557 dmem_size = init->dmem_len;
558 imem_size = init->imem_len;
559
560 /* At the beginning of the firmware blob is a header. */
561 header = (struct dpfe_firmware_header *)fw->data;
562 /* Void pointer to the beginning of the actual firmware. */
563 fw_blob = fw->data + sizeof(*header);
564 /* IMEM comes right after the header. */
565 imem = fw_blob;
566 /* DMEM follows after IMEM. */
567 dmem = fw_blob + imem_size;
568
569 ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
570 if (ret)
571 return ret;
572 ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
573 if (ret)
574 return ret;
575
576 ret = __verify_fw_checksum(init, priv, header, init->chksum);
577 if (ret)
578 return ret;
579
580 __enable_dcpu(priv->regs);
581
582 return 0;
583}
584
585static ssize_t generic_show(unsigned int command, u32 response[],
Markus Mayer900c8f52019-02-11 17:24:42 -0800586 struct private_data *priv, char *buf)
Markus Mayer2f330ca2017-08-24 16:36:26 -0700587{
Markus Mayer2f330ca2017-08-24 16:36:26 -0700588 int ret;
589
Markus Mayer2f330ca2017-08-24 16:36:26 -0700590 if (!priv)
591 return sprintf(buf, "ERROR: driver private data not set\n");
592
593 ret = __send_command(priv, command, response);
594 if (ret < 0)
595 return sprintf(buf, "ERROR: %s\n", error_text[-ret]);
596
597 return 0;
598}
599
600static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
601 char *buf)
602{
603 u32 response[MSG_FIELD_MAX];
Markus Mayer900c8f52019-02-11 17:24:42 -0800604 struct private_data *priv;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700605 unsigned int info;
Markus Mayer9f2c4d92018-02-13 12:40:39 -0800606 ssize_t ret;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700607
Markus Mayer900c8f52019-02-11 17:24:42 -0800608 priv = dev_get_drvdata(dev);
609 ret = generic_show(DPFE_CMD_GET_INFO, response, priv, buf);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700610 if (ret)
611 return ret;
612
613 info = response[MSG_ARG0];
614
615 return sprintf(buf, "%u.%u.%u.%u\n",
616 (info >> 24) & 0xff,
617 (info >> 16) & 0xff,
618 (info >> 8) & 0xff,
619 info & 0xff);
620}
621
622static ssize_t show_refresh(struct device *dev,
623 struct device_attribute *devattr, char *buf)
624{
625 u32 response[MSG_FIELD_MAX];
626 void __iomem *info;
627 struct private_data *priv;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700628 u8 refresh, sr_abort, ppre, thermal_offs, tuf;
629 u32 mr4;
Markus Mayer9f2c4d92018-02-13 12:40:39 -0800630 ssize_t ret;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700631
Markus Mayer900c8f52019-02-11 17:24:42 -0800632 priv = dev_get_drvdata(dev);
633 ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700634 if (ret)
635 return ret;
636
Markus Mayerfee5f1e2018-02-13 12:40:40 -0800637 info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
638 if (!info)
639 return ret;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700640
Markus Mayer78a6f5b2019-02-11 17:24:43 -0800641 mr4 = (readl_relaxed(info + DRAM_INFO_MR4) >> DRAM_INFO_MR4_SHIFT) &
Markus Mayer1ffc0b52019-04-02 16:00:58 -0700642 DRAM_INFO_MR4_MASK;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700643
644 refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
645 sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
646 ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
647 thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
648 tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
649
650 return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
651 readl_relaxed(info + DRAM_INFO_INTERVAL),
652 refresh, sr_abort, ppre, thermal_offs, tuf,
653 readl_relaxed(info + DRAM_INFO_ERROR));
654}
655
656static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
657 const char *buf, size_t count)
658{
659 u32 response[MSG_FIELD_MAX];
660 struct private_data *priv;
661 void __iomem *info;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700662 unsigned long val;
663 int ret;
664
665 if (kstrtoul(buf, 0, &val) < 0)
666 return -EINVAL;
667
668 priv = dev_get_drvdata(dev);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700669 ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
670 if (ret)
671 return ret;
672
Markus Mayerfee5f1e2018-02-13 12:40:40 -0800673 info = get_msg_ptr(priv, response[MSG_ARG0], NULL, NULL);
674 if (!info)
675 return -EIO;
676
Markus Mayer2f330ca2017-08-24 16:36:26 -0700677 writel_relaxed(val, info + DRAM_INFO_INTERVAL);
678
679 return count;
680}
681
682static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
Markus Mayer1ffc0b52019-04-02 16:00:58 -0700683 char *buf)
Markus Mayer2f330ca2017-08-24 16:36:26 -0700684{
685 u32 response[MSG_FIELD_MAX];
686 struct private_data *priv;
687 void __iomem *info;
Markus Mayer9f2c4d92018-02-13 12:40:39 -0800688 ssize_t ret;
Markus Mayer78a6f5b2019-02-11 17:24:43 -0800689 u32 mr5, mr6, mr7, mr8, err;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700690
Markus Mayer900c8f52019-02-11 17:24:42 -0800691 priv = dev_get_drvdata(dev);
692 ret = generic_show(DPFE_CMD_GET_VENDOR, response, priv, buf);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700693 if (ret)
694 return ret;
695
Markus Mayerfee5f1e2018-02-13 12:40:40 -0800696 info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
697 if (!info)
698 return ret;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700699
Markus Mayer78a6f5b2019-02-11 17:24:43 -0800700 mr5 = (readl_relaxed(info + DRAM_VENDOR_MR5) >> DRAM_VENDOR_SHIFT) &
701 DRAM_VENDOR_MASK;
702 mr6 = (readl_relaxed(info + DRAM_VENDOR_MR6) >> DRAM_VENDOR_SHIFT) &
703 DRAM_VENDOR_MASK;
704 mr7 = (readl_relaxed(info + DRAM_VENDOR_MR7) >> DRAM_VENDOR_SHIFT) &
705 DRAM_VENDOR_MASK;
706 mr8 = (readl_relaxed(info + DRAM_VENDOR_MR8) >> DRAM_VENDOR_SHIFT) &
707 DRAM_VENDOR_MASK;
708 err = readl_relaxed(info + DRAM_VENDOR_ERROR) & DRAM_VENDOR_MASK;
709
710 return sprintf(buf, "%#x %#x %#x %#x %#x\n", mr5, mr6, mr7, mr8, err);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700711}
712
713static int brcmstb_dpfe_resume(struct platform_device *pdev)
714{
715 struct init_data init;
716
717 return brcmstb_dpfe_download_firmware(pdev, &init);
718}
719
Markus Mayer2f330ca2017-08-24 16:36:26 -0700720static int brcmstb_dpfe_probe(struct platform_device *pdev)
721{
722 struct device *dev = &pdev->dev;
723 struct private_data *priv;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700724 struct init_data init;
725 struct resource *res;
Markus Mayer2f330ca2017-08-24 16:36:26 -0700726 int ret;
727
728 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
729 if (!priv)
730 return -ENOMEM;
731
732 mutex_init(&priv->lock);
733 platform_set_drvdata(pdev, priv);
734
Markus Mayer2f330ca2017-08-24 16:36:26 -0700735 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
736 priv->regs = devm_ioremap_resource(dev, res);
737 if (IS_ERR(priv->regs)) {
738 dev_err(dev, "couldn't map DCPU registers\n");
739 return -ENODEV;
740 }
741
742 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
743 priv->dmem = devm_ioremap_resource(dev, res);
744 if (IS_ERR(priv->dmem)) {
745 dev_err(dev, "Couldn't map DCPU data memory\n");
746 return -ENOENT;
747 }
748
749 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
750 priv->imem = devm_ioremap_resource(dev, res);
751 if (IS_ERR(priv->imem)) {
752 dev_err(dev, "Couldn't map DCPU instruction memory\n");
753 return -ENOENT;
754 }
755
Markus Mayer58a84992019-04-02 16:01:01 -0700756 priv->dpfe_api = of_device_get_match_data(dev);
757 if (unlikely(!priv->dpfe_api)) {
758 /*
759 * It should be impossible to end up here, but to be safe we
760 * check anyway.
761 */
762 dev_err(dev, "Couldn't determine API\n");
763 return -ENOENT;
764 }
765
Markus Mayer2f330ca2017-08-24 16:36:26 -0700766 ret = brcmstb_dpfe_download_firmware(pdev, &init);
Markus Mayer6ca5d2ba2019-04-02 16:00:59 -0700767 if (ret) {
768 dev_err(dev, "Couldn't download firmware -- %d\n", ret);
Florian Fainellib1d09732018-03-27 16:40:38 -0700769 return ret;
Markus Mayer6ca5d2ba2019-04-02 16:00:59 -0700770 }
Markus Mayer2f330ca2017-08-24 16:36:26 -0700771
Markus Mayer5ef108b2019-04-02 16:01:02 -0700772 ret = sysfs_create_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
Florian Fainellib1d09732018-03-27 16:40:38 -0700773 if (!ret)
Markus Mayer58a84992019-04-02 16:01:01 -0700774 dev_info(dev, "registered with API v%d.\n",
775 priv->dpfe_api->version);
Markus Mayer2f330ca2017-08-24 16:36:26 -0700776
777 return ret;
778}
779
Florian Fainellib1d09732018-03-27 16:40:38 -0700780static int brcmstb_dpfe_remove(struct platform_device *pdev)
781{
Markus Mayer5ef108b2019-04-02 16:01:02 -0700782 struct private_data *priv = dev_get_drvdata(&pdev->dev);
783
784 sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
Florian Fainellib1d09732018-03-27 16:40:38 -0700785
786 return 0;
787}
788
Markus Mayer2f330ca2017-08-24 16:36:26 -0700789static const struct of_device_id brcmstb_dpfe_of_match[] = {
Markus Mayer58a84992019-04-02 16:01:01 -0700790 { .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v2 },
Markus Mayer2f330ca2017-08-24 16:36:26 -0700791 {}
792};
793MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
794
795static struct platform_driver brcmstb_dpfe_driver = {
796 .driver = {
797 .name = DRVNAME,
798 .of_match_table = brcmstb_dpfe_of_match,
799 },
800 .probe = brcmstb_dpfe_probe,
Florian Fainellib1d09732018-03-27 16:40:38 -0700801 .remove = brcmstb_dpfe_remove,
Markus Mayer2f330ca2017-08-24 16:36:26 -0700802 .resume = brcmstb_dpfe_resume,
803};
804
805module_platform_driver(brcmstb_dpfe_driver);
806
807MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
808MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
809MODULE_LICENSE("GPL");