aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mayer <mmayer@broadcom.com>2017-08-24 19:36:26 -0400
committerFlorian Fainelli <f.fainelli@gmail.com>2017-09-18 14:59:39 -0400
commit2f330caff5776239abb3e0337533886dbb21f6df (patch)
treeaf8ab12229586043d4f74e5eb92babb892884764
parent367f15d3130e0d6c6818a4ace2463914e8c78e2c (diff)
memory: brcmstb: Add driver for DPFE
This driver allows access to DRAM properties, such as the refresh rate, via the Broadcom STB DDR PHY Front End (DPFE). The refresh rate can be used as indirect indicator of the DRAM temperature. The driver also allows setting of the sampling interval. Signed-off-by: Markus Mayer <mmayer@broadcom.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/brcmstb_dpfe.c701
3 files changed, 710 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 2281af4b41b6..43ed9eb98c43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2963,6 +2963,14 @@ L: bcm-kernel-feedback-list@broadcom.com
2963S: Maintained 2963S: Maintained
2964F: drivers/mtd/nand/brcmnand/ 2964F: drivers/mtd/nand/brcmnand/
2965 2965
2966BROADCOM STB DPFE DRIVER
2967M: Markus Mayer <mmayer@broadcom.com>
2968M: bcm-kernel-feedback-list@broadcom.com
2969L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
2970S: Maintained
2971F: Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.txt
2972F: drivers/memory/brcmstb_dpfe.c
2973
2966BROADCOM SYSTEMPORT ETHERNET DRIVER 2974BROADCOM SYSTEMPORT ETHERNET DRIVER
2967M: Florian Fainelli <f.fainelli@gmail.com> 2975M: Florian Fainelli <f.fainelli@gmail.com>
2968L: netdev@vger.kernel.org 2976L: netdev@vger.kernel.org
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index e88097fbc085..9cb8b61ee5cc 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -8,6 +8,7 @@ endif
8obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o 8obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
9obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o 9obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
10obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o 10obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
11obj-$(CONFIG_ARCH_BRCMSTB) += brcmstb_dpfe.o
11obj-$(CONFIG_TI_AEMIF) += ti-aemif.o 12obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
12obj-$(CONFIG_TI_EMIF) += emif.o 13obj-$(CONFIG_TI_EMIF) += emif.o
13obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o 14obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c
new file mode 100644
index 000000000000..21242c401af5
--- /dev/null
+++ b/drivers/memory/brcmstb_dpfe.c
@@ -0,0 +1,701 @@
1/*
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>
38#include <linux/platform_device.h>
39
40#define DRVNAME "brcmstb-dpfe"
41#define FIRMWARE_NAME "dpfe.bin"
42
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
48/* Message RAM */
49#define DCPU_MSG_RAM(x) (0x100 + (x) * sizeof(u32))
50
51/* DRAM Info Offsets & Masks */
52#define DRAM_INFO_INTERVAL 0x0
53#define DRAM_INFO_MR4 0x4
54#define DRAM_INFO_ERROR 0x8
55#define DRAM_INFO_MR4_MASK 0xff
56
57/* DRAM MR4 Offsets & Masks */
58#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */
59#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */
60#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */
61#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */
62#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */
63
64#define DRAM_MR4_REFRESH_MASK 0x7
65#define DRAM_MR4_SR_ABORT_MASK 0x1
66#define DRAM_MR4_PPRE_MASK 0x1
67#define DRAM_MR4_TH_OFFS_MASK 0x3
68#define DRAM_MR4_TUF_MASK 0x1
69
70/* DRAM Vendor Offsets & Masks */
71#define DRAM_VENDOR_MR5 0x0
72#define DRAM_VENDOR_MR6 0x4
73#define DRAM_VENDOR_MR7 0x8
74#define DRAM_VENDOR_MR8 0xc
75#define DRAM_VENDOR_ERROR 0x10
76#define DRAM_VENDOR_MASK 0xff
77
78/* Reset register bits & masks */
79#define DCPU_RESET_SHIFT 0x0
80#define DCPU_RESET_MASK 0x1
81#define DCPU_CLK_DISABLE_SHIFT 0x2
82
83/* DCPU return codes */
84#define DCPU_RET_ERROR_BIT BIT(31)
85#define DCPU_RET_SUCCESS 0x1
86#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0))
87#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1))
88#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2))
89#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3))
90/* This error code is not firmware defined and only used in the driver. */
91#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4))
92
93/* Firmware magic */
94#define DPFE_BE_MAGIC 0xfe1010fe
95#define DPFE_LE_MAGIC 0xfe0101fe
96
97/* Error codes */
98#define ERR_INVALID_MAGIC -1
99#define ERR_INVALID_SIZE -2
100#define ERR_INVALID_CHKSUM -3
101
102/* Message types */
103#define DPFE_MSG_TYPE_COMMAND 1
104#define DPFE_MSG_TYPE_RESPONSE 2
105
106#define DELAY_LOOP_MAX 200000
107
108enum dpfe_msg_fields {
109 MSG_HEADER,
110 MSG_COMMAND,
111 MSG_ARG_COUNT,
112 MSG_ARG0,
113 MSG_CHKSUM,
114 MSG_FIELD_MAX /* Last entry */
115};
116
117enum dpfe_commands {
118 DPFE_CMD_GET_INFO,
119 DPFE_CMD_GET_REFRESH,
120 DPFE_CMD_GET_VENDOR,
121 DPFE_CMD_MAX /* Last entry */
122};
123
124struct dpfe_msg {
125 u32 header;
126 u32 command;
127 u32 arg_count;
128 u32 arg0;
129 u32 chksum; /* This is the sum of all other entries. */
130};
131
132/*
133 * Format of the binary firmware file:
134 *
135 * entry
136 * 0 header
137 * value: 0xfe0101fe <== little endian
138 * 0xfe1010fe <== big endian
139 * 1 sequence:
140 * [31:16] total segments on this build
141 * [15:0] this segment sequence.
142 * 2 FW version
143 * 3 IMEM byte size
144 * 4 DMEM byte size
145 * IMEM
146 * DMEM
147 * last checksum ==> sum of everything
148 */
149struct dpfe_firmware_header {
150 u32 magic;
151 u32 sequence;
152 u32 version;
153 u32 imem_size;
154 u32 dmem_size;
155};
156
157/* Things we only need during initialization. */
158struct init_data {
159 unsigned int dmem_len;
160 unsigned int imem_len;
161 unsigned int chksum;
162 bool is_big_endian;
163};
164
165/* Things we need for as long as we are active. */
166struct private_data {
167 void __iomem *regs;
168 void __iomem *dmem;
169 void __iomem *imem;
170 struct device *dev;
171 unsigned int index;
172 struct mutex lock;
173};
174
175static const char *error_text[] = {
176 "Success", "Header code incorrect", "Unknown command or argument",
177 "Incorrect checksum", "Malformed command", "Timed out",
178};
179
180/* List of supported firmware commands */
181static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = {
182 [DPFE_CMD_GET_INFO] = {
183 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
184 [MSG_COMMAND] = 1,
185 [MSG_ARG_COUNT] = 1,
186 [MSG_ARG0] = 1,
187 [MSG_CHKSUM] = 4,
188 },
189 [DPFE_CMD_GET_REFRESH] = {
190 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
191 [MSG_COMMAND] = 2,
192 [MSG_ARG_COUNT] = 1,
193 [MSG_ARG0] = 1,
194 [MSG_CHKSUM] = 5,
195 },
196 [DPFE_CMD_GET_VENDOR] = {
197 [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
198 [MSG_COMMAND] = 2,
199 [MSG_ARG_COUNT] = 1,
200 [MSG_ARG0] = 2,
201 [MSG_CHKSUM] = 6,
202 },
203};
204
205static void __disable_dcpu(void __iomem *regs)
206{
207 u32 val;
208
209 /* Check if DCPU is running */
210 val = readl_relaxed(regs + REG_DCPU_RESET);
211 if (!(val & DCPU_RESET_MASK)) {
212 /* Put DCPU in reset */
213 val |= (1 << DCPU_RESET_SHIFT);
214 writel_relaxed(val, regs + REG_DCPU_RESET);
215 }
216}
217
218static void __enable_dcpu(void __iomem *regs)
219{
220 u32 val;
221
222 /* Clear mailbox registers. */
223 writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
224 writel_relaxed(0, regs + REG_TO_HOST_MBOX);
225
226 /* Disable DCPU clock gating */
227 val = readl_relaxed(regs + REG_DCPU_RESET);
228 val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
229 writel_relaxed(val, regs + REG_DCPU_RESET);
230
231 /* Take DCPU out of reset */
232 val = readl_relaxed(regs + REG_DCPU_RESET);
233 val &= ~(1 << DCPU_RESET_SHIFT);
234 writel_relaxed(val, regs + REG_DCPU_RESET);
235}
236
237static unsigned int get_msg_chksum(const u32 msg[])
238{
239 unsigned int sum = 0;
240 unsigned int i;
241
242 /* Don't include the last field in the checksum. */
243 for (i = 0; i < MSG_FIELD_MAX - 1; i++)
244 sum += msg[i];
245
246 return sum;
247}
248
249static int __send_command(struct private_data *priv, unsigned int cmd,
250 u32 result[])
251{
252 const u32 *msg = dpfe_commands[cmd];
253 void __iomem *regs = priv->regs;
254 unsigned int i, chksum;
255 int ret = 0;
256 u32 resp;
257
258 if (cmd >= DPFE_CMD_MAX)
259 return -1;
260
261 mutex_lock(&priv->lock);
262
263 /* Write command and arguments to message area */
264 for (i = 0; i < MSG_FIELD_MAX; i++)
265 writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
266
267 /* Tell DCPU there is a command waiting */
268 writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
269
270 /* Wait for DCPU to process the command */
271 for (i = 0; i < DELAY_LOOP_MAX; i++) {
272 /* Read response code */
273 resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
274 if (resp > 0)
275 break;
276 udelay(5);
277 }
278
279 if (i == DELAY_LOOP_MAX) {
280 resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
281 ret = -ffs(resp);
282 } else {
283 /* Read response data */
284 for (i = 0; i < MSG_FIELD_MAX; i++)
285 result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
286 }
287
288 /* Tell DCPU we are done */
289 writel_relaxed(0, regs + REG_TO_HOST_MBOX);
290
291 mutex_unlock(&priv->lock);
292
293 if (ret)
294 return ret;
295
296 /* Verify response */
297 chksum = get_msg_chksum(result);
298 if (chksum != result[MSG_CHKSUM])
299 resp = DCPU_RET_ERR_CHKSUM;
300
301 if (resp != DCPU_RET_SUCCESS) {
302 resp &= ~DCPU_RET_ERROR_BIT;
303 ret = -ffs(resp);
304 }
305
306 return ret;
307}
308
309/* Ensure that the firmware file loaded meets all the requirements. */
310static int __verify_firmware(struct init_data *init,
311 const struct firmware *fw)
312{
313 const struct dpfe_firmware_header *header = (void *)fw->data;
314 unsigned int dmem_size, imem_size, total_size;
315 bool is_big_endian = false;
316 const u32 *chksum_ptr;
317
318 if (header->magic == DPFE_BE_MAGIC)
319 is_big_endian = true;
320 else if (header->magic != DPFE_LE_MAGIC)
321 return ERR_INVALID_MAGIC;
322
323 if (is_big_endian) {
324 dmem_size = be32_to_cpu(header->dmem_size);
325 imem_size = be32_to_cpu(header->imem_size);
326 } else {
327 dmem_size = le32_to_cpu(header->dmem_size);
328 imem_size = le32_to_cpu(header->imem_size);
329 }
330
331 /* Data and instruction sections are 32 bit words. */
332 if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
333 return ERR_INVALID_SIZE;
334
335 /*
336 * The header + the data section + the instruction section + the
337 * checksum must be equal to the total firmware size.
338 */
339 total_size = dmem_size + imem_size + sizeof(*header) +
340 sizeof(*chksum_ptr);
341 if (total_size != fw->size)
342 return ERR_INVALID_SIZE;
343
344 /* The checksum comes at the very end. */
345 chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
346
347 init->is_big_endian = is_big_endian;
348 init->dmem_len = dmem_size;
349 init->imem_len = imem_size;
350 init->chksum = (is_big_endian)
351 ? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
352
353 return 0;
354}
355
356/* Verify checksum by reading back the firmware from co-processor RAM. */
357static int __verify_fw_checksum(struct init_data *init,
358 struct private_data *priv,
359 const struct dpfe_firmware_header *header,
360 u32 checksum)
361{
362 u32 magic, sequence, version, sum;
363 u32 __iomem *dmem = priv->dmem;
364 u32 __iomem *imem = priv->imem;
365 unsigned int i;
366
367 if (init->is_big_endian) {
368 magic = be32_to_cpu(header->magic);
369 sequence = be32_to_cpu(header->sequence);
370 version = be32_to_cpu(header->version);
371 } else {
372 magic = le32_to_cpu(header->magic);
373 sequence = le32_to_cpu(header->sequence);
374 version = le32_to_cpu(header->version);
375 }
376
377 sum = magic + sequence + version + init->dmem_len + init->imem_len;
378
379 for (i = 0; i < init->dmem_len / sizeof(u32); i++)
380 sum += readl_relaxed(dmem + i);
381
382 for (i = 0; i < init->imem_len / sizeof(u32); i++)
383 sum += readl_relaxed(imem + i);
384
385 return (sum == checksum) ? 0 : -1;
386}
387
388static int __write_firmware(u32 __iomem *mem, const u32 *fw,
389 unsigned int size, bool is_big_endian)
390{
391 unsigned int i;
392
393 /* Convert size to 32-bit words. */
394 size /= sizeof(u32);
395
396 /* It is recommended to clear the firmware area first. */
397 for (i = 0; i < size; i++)
398 writel_relaxed(0, mem + i);
399
400 /* Now copy it. */
401 if (is_big_endian) {
402 for (i = 0; i < size; i++)
403 writel_relaxed(be32_to_cpu(fw[i]), mem + i);
404 } else {
405 for (i = 0; i < size; i++)
406 writel_relaxed(le32_to_cpu(fw[i]), mem + i);
407 }
408
409 return 0;
410}
411
412static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
413 struct init_data *init)
414{
415 const struct dpfe_firmware_header *header;
416 unsigned int dmem_size, imem_size;
417 struct device *dev = &pdev->dev;
418 bool is_big_endian = false;
419 struct private_data *priv;
420 const struct firmware *fw;
421 const u32 *dmem, *imem;
422 const void *fw_blob;
423 int ret;
424
425 ret = request_firmware(&fw, FIRMWARE_NAME, dev);
426 /* request_firmware() prints its own error messages. */
427 if (ret)
428 return ret;
429
430 priv = platform_get_drvdata(pdev);
431
432 ret = __verify_firmware(init, fw);
433 if (ret)
434 return -EFAULT;
435
436 __disable_dcpu(priv->regs);
437
438 is_big_endian = init->is_big_endian;
439 dmem_size = init->dmem_len;
440 imem_size = init->imem_len;
441
442 /* At the beginning of the firmware blob is a header. */
443 header = (struct dpfe_firmware_header *)fw->data;
444 /* Void pointer to the beginning of the actual firmware. */
445 fw_blob = fw->data + sizeof(*header);
446 /* IMEM comes right after the header. */
447 imem = fw_blob;
448 /* DMEM follows after IMEM. */
449 dmem = fw_blob + imem_size;
450
451 ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
452 if (ret)
453 return ret;
454 ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
455 if (ret)
456 return ret;
457
458 ret = __verify_fw_checksum(init, priv, header, init->chksum);
459 if (ret)
460 return ret;
461
462 __enable_dcpu(priv->regs);
463
464 return 0;
465}
466
467static ssize_t generic_show(unsigned int command, u32 response[],
468 struct device *dev, char *buf)
469{
470 struct private_data *priv;
471 int ret;
472
473 priv = dev_get_drvdata(dev);
474 if (!priv)
475 return sprintf(buf, "ERROR: driver private data not set\n");
476
477 ret = __send_command(priv, command, response);
478 if (ret < 0)
479 return sprintf(buf, "ERROR: %s\n", error_text[-ret]);
480
481 return 0;
482}
483
484static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
485 char *buf)
486{
487 u32 response[MSG_FIELD_MAX];
488 unsigned int info;
489 int ret;
490
491 ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf);
492 if (ret)
493 return ret;
494
495 info = response[MSG_ARG0];
496
497 return sprintf(buf, "%u.%u.%u.%u\n",
498 (info >> 24) & 0xff,
499 (info >> 16) & 0xff,
500 (info >> 8) & 0xff,
501 info & 0xff);
502}
503
504static ssize_t show_refresh(struct device *dev,
505 struct device_attribute *devattr, char *buf)
506{
507 u32 response[MSG_FIELD_MAX];
508 void __iomem *info;
509 struct private_data *priv;
510 unsigned int offset;
511 u8 refresh, sr_abort, ppre, thermal_offs, tuf;
512 u32 mr4;
513 int ret;
514
515 ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf);
516 if (ret)
517 return ret;
518
519 priv = dev_get_drvdata(dev);
520 offset = response[MSG_ARG0];
521 info = priv->dmem + offset;
522
523 mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK;
524
525 refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
526 sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
527 ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
528 thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
529 tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
530
531 return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
532 readl_relaxed(info + DRAM_INFO_INTERVAL),
533 refresh, sr_abort, ppre, thermal_offs, tuf,
534 readl_relaxed(info + DRAM_INFO_ERROR));
535}
536
537static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
538 const char *buf, size_t count)
539{
540 u32 response[MSG_FIELD_MAX];
541 struct private_data *priv;
542 void __iomem *info;
543 unsigned int offset;
544 unsigned long val;
545 int ret;
546
547 if (kstrtoul(buf, 0, &val) < 0)
548 return -EINVAL;
549
550 priv = dev_get_drvdata(dev);
551
552 ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
553 if (ret)
554 return ret;
555
556 offset = response[MSG_ARG0];
557 info = priv->dmem + offset;
558 writel_relaxed(val, info + DRAM_INFO_INTERVAL);
559
560 return count;
561}
562
563static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
564 char *buf)
565{
566 u32 response[MSG_FIELD_MAX];
567 struct private_data *priv;
568 void __iomem *info;
569 unsigned int offset;
570 int ret;
571
572 ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf);
573 if (ret)
574 return ret;
575
576 offset = response[MSG_ARG0];
577 priv = dev_get_drvdata(dev);
578 info = priv->dmem + offset;
579
580 return sprintf(buf, "%#x %#x %#x %#x %#x\n",
581 readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK,
582 readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK,
583 readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK,
584 readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK,
585 readl_relaxed(info + DRAM_VENDOR_ERROR));
586}
587
588static int brcmstb_dpfe_resume(struct platform_device *pdev)
589{
590 struct init_data init;
591
592 return brcmstb_dpfe_download_firmware(pdev, &init);
593}
594
595static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
596static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
597static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
598static struct attribute *dpfe_attrs[] = {
599 &dev_attr_dpfe_info.attr,
600 &dev_attr_dpfe_refresh.attr,
601 &dev_attr_dpfe_vendor.attr,
602 NULL
603};
604ATTRIBUTE_GROUPS(dpfe);
605
606static int brcmstb_dpfe_probe(struct platform_device *pdev)
607{
608 struct device *dev = &pdev->dev;
609 struct private_data *priv;
610 struct device *dpfe_dev;
611 struct init_data init;
612 struct resource *res;
613 u32 index;
614 int ret;
615
616 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
617 if (!priv)
618 return -ENOMEM;
619
620 mutex_init(&priv->lock);
621 platform_set_drvdata(pdev, priv);
622
623 /* Cell index is optional; default to 0 if not present. */
624 ret = of_property_read_u32(dev->of_node, "cell-index", &index);
625 if (ret)
626 index = 0;
627
628 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
629 priv->regs = devm_ioremap_resource(dev, res);
630 if (IS_ERR(priv->regs)) {
631 dev_err(dev, "couldn't map DCPU registers\n");
632 return -ENODEV;
633 }
634
635 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
636 priv->dmem = devm_ioremap_resource(dev, res);
637 if (IS_ERR(priv->dmem)) {
638 dev_err(dev, "Couldn't map DCPU data memory\n");
639 return -ENOENT;
640 }
641
642 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
643 priv->imem = devm_ioremap_resource(dev, res);
644 if (IS_ERR(priv->imem)) {
645 dev_err(dev, "Couldn't map DCPU instruction memory\n");
646 return -ENOENT;
647 }
648
649 ret = brcmstb_dpfe_download_firmware(pdev, &init);
650 if (ret)
651 goto err;
652
653 dpfe_dev = devm_kzalloc(dev, sizeof(*dpfe_dev), GFP_KERNEL);
654 if (!dpfe_dev) {
655 ret = -ENOMEM;
656 goto err;
657 }
658
659 priv->dev = dpfe_dev;
660 priv->index = index;
661
662 dpfe_dev->parent = dev;
663 dpfe_dev->groups = dpfe_groups;
664 dpfe_dev->of_node = dev->of_node;
665 dev_set_drvdata(dpfe_dev, priv);
666 dev_set_name(dpfe_dev, "dpfe%u", index);
667
668 ret = device_register(dpfe_dev);
669 if (ret)
670 goto err;
671
672 dev_info(dev, "registered.\n");
673
674 return 0;
675
676err:
677 dev_err(dev, "failed to initialize -- error %d\n", ret);
678
679 return ret;
680}
681
682static const struct of_device_id brcmstb_dpfe_of_match[] = {
683 { .compatible = "brcm,dpfe-cpu", },
684 {}
685};
686MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
687
688static struct platform_driver brcmstb_dpfe_driver = {
689 .driver = {
690 .name = DRVNAME,
691 .of_match_table = brcmstb_dpfe_of_match,
692 },
693 .probe = brcmstb_dpfe_probe,
694 .resume = brcmstb_dpfe_resume,
695};
696
697module_platform_driver(brcmstb_dpfe_driver);
698
699MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
700MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
701MODULE_LICENSE("GPL");