xref: /openbmc/linux/drivers/memory/brcmstb_dpfe.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1714c29cfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22f330cafSMarkus Mayer /*
32f330cafSMarkus Mayer  * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
42f330cafSMarkus Mayer  *
52f330cafSMarkus Mayer  * Copyright (c) 2017 Broadcom
62f330cafSMarkus Mayer  */
72f330cafSMarkus Mayer 
82f330cafSMarkus Mayer /*
92f330cafSMarkus Mayer  * This driver provides access to the DPFE interface of Broadcom STB SoCs.
102f330cafSMarkus Mayer  * The firmware running on the DCPU inside the DDR PHY can provide current
112f330cafSMarkus Mayer  * information about the system's RAM, for instance the DRAM refresh rate.
122f330cafSMarkus Mayer  * This can be used as an indirect indicator for the DRAM's temperature.
132f330cafSMarkus Mayer  * Slower refresh rate means cooler RAM, higher refresh rate means hotter
142f330cafSMarkus Mayer  * RAM.
152f330cafSMarkus Mayer  *
162f330cafSMarkus Mayer  * Throughout the driver, we use readl_relaxed() and writel_relaxed(), which
172f330cafSMarkus Mayer  * already contain the appropriate le32_to_cpu()/cpu_to_le32() calls.
182f330cafSMarkus Mayer  *
192f330cafSMarkus Mayer  * Note regarding the loading of the firmware image: we use be32_to_cpu()
202f330cafSMarkus Mayer  * and le_32_to_cpu(), so we can support the following four cases:
212f330cafSMarkus Mayer  *     - LE kernel + LE firmware image (the most common case)
222f330cafSMarkus Mayer  *     - LE kernel + BE firmware image
232f330cafSMarkus Mayer  *     - BE kernel + LE firmware image
242f330cafSMarkus Mayer  *     - BE kernel + BE firmware image
252f330cafSMarkus Mayer  *
26f7fa245aSKrzysztof Kozlowski  * The DPCU always runs in big endian mode. The firmware image, however, can
272f330cafSMarkus Mayer  * be in either format. Also, communication between host CPU and DCPU is
282f330cafSMarkus Mayer  * always in little endian.
292f330cafSMarkus Mayer  */
302f330cafSMarkus Mayer 
312f330cafSMarkus Mayer #include <linux/delay.h>
322f330cafSMarkus Mayer #include <linux/firmware.h>
332f330cafSMarkus Mayer #include <linux/io.h>
342f330cafSMarkus Mayer #include <linux/module.h>
35*0b483871SRob Herring #include <linux/of.h>
362f330cafSMarkus Mayer #include <linux/platform_device.h>
372f330cafSMarkus Mayer 
382f330cafSMarkus Mayer #define DRVNAME			"brcmstb-dpfe"
392f330cafSMarkus Mayer 
402f330cafSMarkus Mayer /* DCPU register offsets */
412f330cafSMarkus Mayer #define REG_DCPU_RESET		0x0
422f330cafSMarkus Mayer #define REG_TO_DCPU_MBOX	0x10
432f330cafSMarkus Mayer #define REG_TO_HOST_MBOX	0x14
442f330cafSMarkus Mayer 
45fee5f1efSMarkus Mayer /* Macros to process offsets returned by the DCPU */
46fee5f1efSMarkus Mayer #define DRAM_MSG_ADDR_OFFSET	0x0
47fee5f1efSMarkus Mayer #define DRAM_MSG_TYPE_OFFSET	0x1c
48fee5f1efSMarkus Mayer #define DRAM_MSG_ADDR_MASK	((1UL << DRAM_MSG_TYPE_OFFSET) - 1)
49fee5f1efSMarkus Mayer #define DRAM_MSG_TYPE_MASK	((1UL << \
50fee5f1efSMarkus Mayer 				 (BITS_PER_LONG - DRAM_MSG_TYPE_OFFSET)) - 1)
51fee5f1efSMarkus Mayer 
522f330cafSMarkus Mayer /* Message RAM */
53fee5f1efSMarkus Mayer #define DCPU_MSG_RAM_START	0x100
54fee5f1efSMarkus Mayer #define DCPU_MSG_RAM(x)		(DCPU_MSG_RAM_START + (x) * sizeof(u32))
552f330cafSMarkus Mayer 
562f330cafSMarkus Mayer /* DRAM Info Offsets & Masks */
572f330cafSMarkus Mayer #define DRAM_INFO_INTERVAL	0x0
582f330cafSMarkus Mayer #define DRAM_INFO_MR4		0x4
592f330cafSMarkus Mayer #define DRAM_INFO_ERROR		0x8
602f330cafSMarkus Mayer #define DRAM_INFO_MR4_MASK	0xff
6178a6f5beSMarkus Mayer #define DRAM_INFO_MR4_SHIFT	24	/* We need to look at byte 3 */
622f330cafSMarkus Mayer 
632f330cafSMarkus Mayer /* DRAM MR4 Offsets & Masks */
642f330cafSMarkus Mayer #define DRAM_MR4_REFRESH	0x0	/* Refresh rate */
652f330cafSMarkus Mayer #define DRAM_MR4_SR_ABORT	0x3	/* Self Refresh Abort */
662f330cafSMarkus Mayer #define DRAM_MR4_PPRE		0x4	/* Post-package repair entry/exit */
672f330cafSMarkus Mayer #define DRAM_MR4_TH_OFFS	0x5	/* Thermal Offset; vendor specific */
682f330cafSMarkus Mayer #define DRAM_MR4_TUF		0x7	/* Temperature Update Flag */
692f330cafSMarkus Mayer 
702f330cafSMarkus Mayer #define DRAM_MR4_REFRESH_MASK	0x7
712f330cafSMarkus Mayer #define DRAM_MR4_SR_ABORT_MASK	0x1
722f330cafSMarkus Mayer #define DRAM_MR4_PPRE_MASK	0x1
732f330cafSMarkus Mayer #define DRAM_MR4_TH_OFFS_MASK	0x3
742f330cafSMarkus Mayer #define DRAM_MR4_TUF_MASK	0x1
752f330cafSMarkus Mayer 
76e3b74723SMarkus Mayer /* DRAM Vendor Offsets & Masks (API v2) */
772f330cafSMarkus Mayer #define DRAM_VENDOR_MR5		0x0
782f330cafSMarkus Mayer #define DRAM_VENDOR_MR6		0x4
792f330cafSMarkus Mayer #define DRAM_VENDOR_MR7		0x8
802f330cafSMarkus Mayer #define DRAM_VENDOR_MR8		0xc
812f330cafSMarkus Mayer #define DRAM_VENDOR_ERROR	0x10
822f330cafSMarkus Mayer #define DRAM_VENDOR_MASK	0xff
8378a6f5beSMarkus Mayer #define DRAM_VENDOR_SHIFT	24	/* We need to look at byte 3 */
842f330cafSMarkus Mayer 
85e3b74723SMarkus Mayer /* DRAM Information Offsets & Masks (API v3) */
86e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MR4	0x0
87e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MR5	0x4
88e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MR6	0x8
89e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MR7	0xc
90e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MR8	0x10
91e3b74723SMarkus Mayer #define DRAM_DDR_INFO_ERROR	0x14
92e3b74723SMarkus Mayer #define DRAM_DDR_INFO_MASK	0xff
932f330cafSMarkus Mayer 
942f330cafSMarkus Mayer /* Reset register bits & masks */
952f330cafSMarkus Mayer #define DCPU_RESET_SHIFT	0x0
962f330cafSMarkus Mayer #define DCPU_RESET_MASK		0x1
972f330cafSMarkus Mayer #define DCPU_CLK_DISABLE_SHIFT	0x2
982f330cafSMarkus Mayer 
992f330cafSMarkus Mayer /* DCPU return codes */
1002f330cafSMarkus Mayer #define DCPU_RET_ERROR_BIT	BIT(31)
1012f330cafSMarkus Mayer #define DCPU_RET_SUCCESS	0x1
1022f330cafSMarkus Mayer #define DCPU_RET_ERR_HEADER	(DCPU_RET_ERROR_BIT | BIT(0))
1032f330cafSMarkus Mayer #define DCPU_RET_ERR_INVAL	(DCPU_RET_ERROR_BIT | BIT(1))
1042f330cafSMarkus Mayer #define DCPU_RET_ERR_CHKSUM	(DCPU_RET_ERROR_BIT | BIT(2))
1052f330cafSMarkus Mayer #define DCPU_RET_ERR_COMMAND	(DCPU_RET_ERROR_BIT | BIT(3))
1062f330cafSMarkus Mayer /* This error code is not firmware defined and only used in the driver. */
1072f330cafSMarkus Mayer #define DCPU_RET_ERR_TIMEDOUT	(DCPU_RET_ERROR_BIT | BIT(4))
1082f330cafSMarkus Mayer 
1092f330cafSMarkus Mayer /* Firmware magic */
1102f330cafSMarkus Mayer #define DPFE_BE_MAGIC		0xfe1010fe
1112f330cafSMarkus Mayer #define DPFE_LE_MAGIC		0xfe0101fe
1122f330cafSMarkus Mayer 
1132f330cafSMarkus Mayer /* Error codes */
1142f330cafSMarkus Mayer #define ERR_INVALID_MAGIC	-1
1152f330cafSMarkus Mayer #define ERR_INVALID_SIZE	-2
1162f330cafSMarkus Mayer #define ERR_INVALID_CHKSUM	-3
1172f330cafSMarkus Mayer 
1182f330cafSMarkus Mayer /* Message types */
1192f330cafSMarkus Mayer #define DPFE_MSG_TYPE_COMMAND	1
1202f330cafSMarkus Mayer #define DPFE_MSG_TYPE_RESPONSE	2
1212f330cafSMarkus Mayer 
1227ccd2ffcSMarkus Mayer #define DELAY_LOOP_MAX		1000
1232f330cafSMarkus Mayer 
1242f330cafSMarkus Mayer enum dpfe_msg_fields {
1252f330cafSMarkus Mayer 	MSG_HEADER,
1262f330cafSMarkus Mayer 	MSG_COMMAND,
1272f330cafSMarkus Mayer 	MSG_ARG_COUNT,
1282f330cafSMarkus Mayer 	MSG_ARG0,
129e3b74723SMarkus Mayer 	MSG_FIELD_MAX	= 16 /* Max number of arguments */
1302f330cafSMarkus Mayer };
1312f330cafSMarkus Mayer 
1322f330cafSMarkus Mayer enum dpfe_commands {
1332f330cafSMarkus Mayer 	DPFE_CMD_GET_INFO,
1342f330cafSMarkus Mayer 	DPFE_CMD_GET_REFRESH,
1352f330cafSMarkus Mayer 	DPFE_CMD_GET_VENDOR,
1362f330cafSMarkus Mayer 	DPFE_CMD_MAX /* Last entry */
1372f330cafSMarkus Mayer };
1382f330cafSMarkus Mayer 
1392f330cafSMarkus Mayer /*
1402f330cafSMarkus Mayer  * Format of the binary firmware file:
1412f330cafSMarkus Mayer  *
1422f330cafSMarkus Mayer  *   entry
1432f330cafSMarkus Mayer  *      0    header
1442f330cafSMarkus Mayer  *              value:  0xfe0101fe  <== little endian
1452f330cafSMarkus Mayer  *                      0xfe1010fe  <== big endian
1462f330cafSMarkus Mayer  *      1    sequence:
1472f330cafSMarkus Mayer  *              [31:16] total segments on this build
1482f330cafSMarkus Mayer  *              [15:0]  this segment sequence.
1492f330cafSMarkus Mayer  *      2    FW version
1502f330cafSMarkus Mayer  *      3    IMEM byte size
1512f330cafSMarkus Mayer  *      4    DMEM byte size
1522f330cafSMarkus Mayer  *           IMEM
1532f330cafSMarkus Mayer  *           DMEM
1542f330cafSMarkus Mayer  *      last checksum ==> sum of everything
1552f330cafSMarkus Mayer  */
1562f330cafSMarkus Mayer struct dpfe_firmware_header {
1572f330cafSMarkus Mayer 	u32 magic;
1582f330cafSMarkus Mayer 	u32 sequence;
1592f330cafSMarkus Mayer 	u32 version;
1602f330cafSMarkus Mayer 	u32 imem_size;
1612f330cafSMarkus Mayer 	u32 dmem_size;
1622f330cafSMarkus Mayer };
1632f330cafSMarkus Mayer 
1642f330cafSMarkus Mayer /* Things we only need during initialization. */
1652f330cafSMarkus Mayer struct init_data {
1662f330cafSMarkus Mayer 	unsigned int dmem_len;
1672f330cafSMarkus Mayer 	unsigned int imem_len;
1682f330cafSMarkus Mayer 	unsigned int chksum;
1692f330cafSMarkus Mayer 	bool is_big_endian;
1702f330cafSMarkus Mayer };
1712f330cafSMarkus Mayer 
17258a8499fSMarkus Mayer /* API version and corresponding commands */
17358a8499fSMarkus Mayer struct dpfe_api {
17458a8499fSMarkus Mayer 	int version;
17558a8499fSMarkus Mayer 	const char *fw_name;
1765ef108b4SMarkus Mayer 	const struct attribute_group **sysfs_attrs;
17758a8499fSMarkus Mayer 	u32 command[DPFE_CMD_MAX][MSG_FIELD_MAX];
17858a8499fSMarkus Mayer };
17958a8499fSMarkus Mayer 
1802f330cafSMarkus Mayer /* Things we need for as long as we are active. */
181abf94566SMarkus Mayer struct brcmstb_dpfe_priv {
1822f330cafSMarkus Mayer 	void __iomem *regs;
1832f330cafSMarkus Mayer 	void __iomem *dmem;
1842f330cafSMarkus Mayer 	void __iomem *imem;
1852f330cafSMarkus Mayer 	struct device *dev;
18658a8499fSMarkus Mayer 	const struct dpfe_api *dpfe_api;
1872f330cafSMarkus Mayer 	struct mutex lock;
1882f330cafSMarkus Mayer };
1892f330cafSMarkus Mayer 
1905ef108b4SMarkus Mayer /*
1915ef108b4SMarkus Mayer  * Forward declaration of our sysfs attribute functions, so we can declare the
1925ef108b4SMarkus Mayer  * attribute data structures early.
1935ef108b4SMarkus Mayer  */
1945ef108b4SMarkus Mayer static ssize_t show_info(struct device *, struct device_attribute *, char *);
1955ef108b4SMarkus Mayer static ssize_t show_refresh(struct device *, struct device_attribute *, char *);
1965ef108b4SMarkus Mayer static ssize_t store_refresh(struct device *, struct device_attribute *,
1975ef108b4SMarkus Mayer 			  const char *, size_t);
1985ef108b4SMarkus Mayer static ssize_t show_vendor(struct device *, struct device_attribute *, char *);
199e3b74723SMarkus Mayer static ssize_t show_dram(struct device *, struct device_attribute *, char *);
2005ef108b4SMarkus Mayer 
2015ef108b4SMarkus Mayer /*
2025ef108b4SMarkus Mayer  * Declare our attributes early, so they can be referenced in the API data
2035ef108b4SMarkus Mayer  * structure. We need to do this, because the attributes depend on the API
2045ef108b4SMarkus Mayer  * version.
2055ef108b4SMarkus Mayer  */
2065ef108b4SMarkus Mayer static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
2075ef108b4SMarkus Mayer static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
2085ef108b4SMarkus Mayer static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
209e3b74723SMarkus Mayer static DEVICE_ATTR(dpfe_dram, 0444, show_dram, NULL);
2105ef108b4SMarkus Mayer 
2115ef108b4SMarkus Mayer /* API v2 sysfs attributes */
2125ef108b4SMarkus Mayer static struct attribute *dpfe_v2_attrs[] = {
2135ef108b4SMarkus Mayer 	&dev_attr_dpfe_info.attr,
2145ef108b4SMarkus Mayer 	&dev_attr_dpfe_refresh.attr,
2155ef108b4SMarkus Mayer 	&dev_attr_dpfe_vendor.attr,
2165ef108b4SMarkus Mayer 	NULL
2175ef108b4SMarkus Mayer };
2185ef108b4SMarkus Mayer ATTRIBUTE_GROUPS(dpfe_v2);
2195ef108b4SMarkus Mayer 
220e3b74723SMarkus Mayer /* API v3 sysfs attributes */
221e3b74723SMarkus Mayer static struct attribute *dpfe_v3_attrs[] = {
222e3b74723SMarkus Mayer 	&dev_attr_dpfe_info.attr,
223e3b74723SMarkus Mayer 	&dev_attr_dpfe_dram.attr,
224e3b74723SMarkus Mayer 	NULL
225e3b74723SMarkus Mayer };
226e3b74723SMarkus Mayer ATTRIBUTE_GROUPS(dpfe_v3);
227e3b74723SMarkus Mayer 
228b61d3e87SFlorian Fainelli /*
229b61d3e87SFlorian Fainelli  * Old API v2 firmware commands, as defined in the rev 0.61 specification, we
230b61d3e87SFlorian Fainelli  * use a version set to 1 to denote that it is not compatible with the new API
231b61d3e87SFlorian Fainelli  * v2 and onwards.
232b61d3e87SFlorian Fainelli  */
233b61d3e87SFlorian Fainelli static const struct dpfe_api dpfe_api_old_v2 = {
234b61d3e87SFlorian Fainelli 	.version = 1,
23558a8499fSMarkus Mayer 	.fw_name = "dpfe.bin",
2365ef108b4SMarkus Mayer 	.sysfs_attrs = dpfe_v2_groups,
23758a8499fSMarkus Mayer 	.command = {
2382f330cafSMarkus Mayer 		[DPFE_CMD_GET_INFO] = {
2392f330cafSMarkus Mayer 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2402f330cafSMarkus Mayer 			[MSG_COMMAND] = 1,
2412f330cafSMarkus Mayer 			[MSG_ARG_COUNT] = 1,
2422f330cafSMarkus Mayer 			[MSG_ARG0] = 1,
2432f330cafSMarkus Mayer 		},
2442f330cafSMarkus Mayer 		[DPFE_CMD_GET_REFRESH] = {
2452f330cafSMarkus Mayer 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2462f330cafSMarkus Mayer 			[MSG_COMMAND] = 2,
2472f330cafSMarkus Mayer 			[MSG_ARG_COUNT] = 1,
2482f330cafSMarkus Mayer 			[MSG_ARG0] = 1,
2492f330cafSMarkus Mayer 		},
2502f330cafSMarkus Mayer 		[DPFE_CMD_GET_VENDOR] = {
2512f330cafSMarkus Mayer 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2522f330cafSMarkus Mayer 			[MSG_COMMAND] = 2,
2532f330cafSMarkus Mayer 			[MSG_ARG_COUNT] = 1,
2542f330cafSMarkus Mayer 			[MSG_ARG0] = 2,
2552f330cafSMarkus Mayer 		},
25658a8499fSMarkus Mayer 	}
2572f330cafSMarkus Mayer };
2582f330cafSMarkus Mayer 
259b61d3e87SFlorian Fainelli /*
260b61d3e87SFlorian Fainelli  * API v2 firmware commands, as defined in the rev 0.8 specification, named new
261b61d3e87SFlorian Fainelli  * v2 here
262b61d3e87SFlorian Fainelli  */
263b61d3e87SFlorian Fainelli static const struct dpfe_api dpfe_api_new_v2 = {
264b61d3e87SFlorian Fainelli 	.version = 2,
265b61d3e87SFlorian Fainelli 	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
266b61d3e87SFlorian Fainelli 	.sysfs_attrs = dpfe_v2_groups,
267b61d3e87SFlorian Fainelli 	.command = {
268b61d3e87SFlorian Fainelli 		[DPFE_CMD_GET_INFO] = {
269b61d3e87SFlorian Fainelli 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
270b61d3e87SFlorian Fainelli 			[MSG_COMMAND] = 0x101,
271b61d3e87SFlorian Fainelli 		},
272b61d3e87SFlorian Fainelli 		[DPFE_CMD_GET_REFRESH] = {
273b61d3e87SFlorian Fainelli 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
274b61d3e87SFlorian Fainelli 			[MSG_COMMAND] = 0x201,
275b61d3e87SFlorian Fainelli 		},
276b61d3e87SFlorian Fainelli 		[DPFE_CMD_GET_VENDOR] = {
277b61d3e87SFlorian Fainelli 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
278b61d3e87SFlorian Fainelli 			[MSG_COMMAND] = 0x202,
279b61d3e87SFlorian Fainelli 		},
280b61d3e87SFlorian Fainelli 	}
281b61d3e87SFlorian Fainelli };
282b61d3e87SFlorian Fainelli 
283e3b74723SMarkus Mayer /* API v3 firmware commands */
284e3b74723SMarkus Mayer static const struct dpfe_api dpfe_api_v3 = {
285e3b74723SMarkus Mayer 	.version = 3,
286e3b74723SMarkus Mayer 	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
287e3b74723SMarkus Mayer 	.sysfs_attrs = dpfe_v3_groups,
288e3b74723SMarkus Mayer 	.command = {
289e3b74723SMarkus Mayer 		[DPFE_CMD_GET_INFO] = {
290e3b74723SMarkus Mayer 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
291e3b74723SMarkus Mayer 			[MSG_COMMAND] = 0x0101,
292e3b74723SMarkus Mayer 			[MSG_ARG_COUNT] = 1,
293e3b74723SMarkus Mayer 			[MSG_ARG0] = 1,
294e3b74723SMarkus Mayer 		},
295e3b74723SMarkus Mayer 		[DPFE_CMD_GET_REFRESH] = {
296e3b74723SMarkus Mayer 			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
297e3b74723SMarkus Mayer 			[MSG_COMMAND] = 0x0202,
298e3b74723SMarkus Mayer 			[MSG_ARG_COUNT] = 0,
299e3b74723SMarkus Mayer 		},
300e3b74723SMarkus Mayer 		/* There's no GET_VENDOR command in API v3. */
301e3b74723SMarkus Mayer 	},
3022f330cafSMarkus Mayer };
3032f330cafSMarkus Mayer 
get_error_text(unsigned int i)304f42ae4bbSMarkus Mayer static const char *get_error_text(unsigned int i)
305f42ae4bbSMarkus Mayer {
306f42ae4bbSMarkus Mayer 	static const char * const error_text[] = {
307f42ae4bbSMarkus Mayer 		"Success", "Header code incorrect",
308f42ae4bbSMarkus Mayer 		"Unknown command or argument", "Incorrect checksum",
309f42ae4bbSMarkus Mayer 		"Malformed command", "Timed out", "Unknown error",
310f42ae4bbSMarkus Mayer 	};
311f42ae4bbSMarkus Mayer 
312f42ae4bbSMarkus Mayer 	if (unlikely(i >= ARRAY_SIZE(error_text)))
313f42ae4bbSMarkus Mayer 		i = ARRAY_SIZE(error_text) - 1;
314f42ae4bbSMarkus Mayer 
315f42ae4bbSMarkus Mayer 	return error_text[i];
316f42ae4bbSMarkus Mayer }
317f42ae4bbSMarkus Mayer 
is_dcpu_enabled(struct brcmstb_dpfe_priv * priv)31875d316e7SMarkus Mayer static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv)
319d56e746fSMarkus Mayer {
320d56e746fSMarkus Mayer 	u32 val;
321d56e746fSMarkus Mayer 
32275d316e7SMarkus Mayer 	mutex_lock(&priv->lock);
32375d316e7SMarkus Mayer 	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
32475d316e7SMarkus Mayer 	mutex_unlock(&priv->lock);
325d56e746fSMarkus Mayer 
326d56e746fSMarkus Mayer 	return !(val & DCPU_RESET_MASK);
327d56e746fSMarkus Mayer }
328d56e746fSMarkus Mayer 
__disable_dcpu(struct brcmstb_dpfe_priv * priv)32975d316e7SMarkus Mayer static void __disable_dcpu(struct brcmstb_dpfe_priv *priv)
3302f330cafSMarkus Mayer {
3312f330cafSMarkus Mayer 	u32 val;
3322f330cafSMarkus Mayer 
33375d316e7SMarkus Mayer 	if (!is_dcpu_enabled(priv))
334d56e746fSMarkus Mayer 		return;
335d56e746fSMarkus Mayer 
33675d316e7SMarkus Mayer 	mutex_lock(&priv->lock);
33775d316e7SMarkus Mayer 
338d56e746fSMarkus Mayer 	/* Put DCPU in reset if it's running. */
33975d316e7SMarkus Mayer 	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
3402f330cafSMarkus Mayer 	val |= (1 << DCPU_RESET_SHIFT);
34175d316e7SMarkus Mayer 	writel_relaxed(val, priv->regs + REG_DCPU_RESET);
34275d316e7SMarkus Mayer 
34375d316e7SMarkus Mayer 	mutex_unlock(&priv->lock);
3442f330cafSMarkus Mayer }
3452f330cafSMarkus Mayer 
__enable_dcpu(struct brcmstb_dpfe_priv * priv)34675d316e7SMarkus Mayer static void __enable_dcpu(struct brcmstb_dpfe_priv *priv)
3472f330cafSMarkus Mayer {
34875d316e7SMarkus Mayer 	void __iomem *regs = priv->regs;
3492f330cafSMarkus Mayer 	u32 val;
3502f330cafSMarkus Mayer 
35175d316e7SMarkus Mayer 	mutex_lock(&priv->lock);
35275d316e7SMarkus Mayer 
3532f330cafSMarkus Mayer 	/* Clear mailbox registers. */
3542f330cafSMarkus Mayer 	writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
3552f330cafSMarkus Mayer 	writel_relaxed(0, regs + REG_TO_HOST_MBOX);
3562f330cafSMarkus Mayer 
3572f330cafSMarkus Mayer 	/* Disable DCPU clock gating */
3582f330cafSMarkus Mayer 	val = readl_relaxed(regs + REG_DCPU_RESET);
3592f330cafSMarkus Mayer 	val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
3602f330cafSMarkus Mayer 	writel_relaxed(val, regs + REG_DCPU_RESET);
3612f330cafSMarkus Mayer 
3622f330cafSMarkus Mayer 	/* Take DCPU out of reset */
3632f330cafSMarkus Mayer 	val = readl_relaxed(regs + REG_DCPU_RESET);
3642f330cafSMarkus Mayer 	val &= ~(1 << DCPU_RESET_SHIFT);
3652f330cafSMarkus Mayer 	writel_relaxed(val, regs + REG_DCPU_RESET);
36675d316e7SMarkus Mayer 
36775d316e7SMarkus Mayer 	mutex_unlock(&priv->lock);
3682f330cafSMarkus Mayer }
3692f330cafSMarkus Mayer 
get_msg_chksum(const u32 msg[],unsigned int max)370e3b74723SMarkus Mayer static unsigned int get_msg_chksum(const u32 msg[], unsigned int max)
3712f330cafSMarkus Mayer {
3722f330cafSMarkus Mayer 	unsigned int sum = 0;
3732f330cafSMarkus Mayer 	unsigned int i;
3742f330cafSMarkus Mayer 
3752f330cafSMarkus Mayer 	/* Don't include the last field in the checksum. */
376e3b74723SMarkus Mayer 	for (i = 0; i < max; i++)
3772f330cafSMarkus Mayer 		sum += msg[i];
3782f330cafSMarkus Mayer 
3792f330cafSMarkus Mayer 	return sum;
3802f330cafSMarkus Mayer }
3812f330cafSMarkus Mayer 
get_msg_ptr(struct brcmstb_dpfe_priv * priv,u32 response,char * buf,ssize_t * size)382abf94566SMarkus Mayer static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
383fee5f1efSMarkus Mayer 				 char *buf, ssize_t *size)
384fee5f1efSMarkus Mayer {
385fee5f1efSMarkus Mayer 	unsigned int msg_type;
386fee5f1efSMarkus Mayer 	unsigned int offset;
387fee5f1efSMarkus Mayer 	void __iomem *ptr = NULL;
388fee5f1efSMarkus Mayer 
389e3b74723SMarkus Mayer 	/* There is no need to use this function for API v3 or later. */
390bf77f3f4SKrzysztof Kozlowski 	if (unlikely(priv->dpfe_api->version >= 3))
391e3b74723SMarkus Mayer 		return NULL;
392e3b74723SMarkus Mayer 
393fee5f1efSMarkus Mayer 	msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
394fee5f1efSMarkus Mayer 	offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
395fee5f1efSMarkus Mayer 
396fee5f1efSMarkus Mayer 	/*
397fee5f1efSMarkus Mayer 	 * msg_type == 1: the offset is relative to the message RAM
398fee5f1efSMarkus Mayer 	 * msg_type == 0: the offset is relative to the data RAM (this is the
399fee5f1efSMarkus Mayer 	 *                previous way of passing data)
400fee5f1efSMarkus Mayer 	 * msg_type is anything else: there's critical hardware problem
401fee5f1efSMarkus Mayer 	 */
402fee5f1efSMarkus Mayer 	switch (msg_type) {
403fee5f1efSMarkus Mayer 	case 1:
404fee5f1efSMarkus Mayer 		ptr = priv->regs + DCPU_MSG_RAM_START + offset;
405fee5f1efSMarkus Mayer 		break;
406fee5f1efSMarkus Mayer 	case 0:
407fee5f1efSMarkus Mayer 		ptr = priv->dmem + offset;
408fee5f1efSMarkus Mayer 		break;
409fee5f1efSMarkus Mayer 	default:
410fee5f1efSMarkus Mayer 		dev_emerg(priv->dev, "invalid message reply from DCPU: %#x\n",
411fee5f1efSMarkus Mayer 			response);
412fee5f1efSMarkus Mayer 		if (buf && size)
413fee5f1efSMarkus Mayer 			*size = sprintf(buf,
414fee5f1efSMarkus Mayer 				"FATAL: communication error with DCPU\n");
415fee5f1efSMarkus Mayer 	}
416fee5f1efSMarkus Mayer 
417fee5f1efSMarkus Mayer 	return ptr;
418fee5f1efSMarkus Mayer }
419fee5f1efSMarkus Mayer 
__finalize_command(struct brcmstb_dpfe_priv * priv)420abf94566SMarkus Mayer static void __finalize_command(struct brcmstb_dpfe_priv *priv)
421e3b74723SMarkus Mayer {
422e3b74723SMarkus Mayer 	unsigned int release_mbox;
423e3b74723SMarkus Mayer 
424e3b74723SMarkus Mayer 	/*
425e3b74723SMarkus Mayer 	 * It depends on the API version which MBOX register we have to write to
426e29ed0d1SJason Wang 	 * signal we are done.
427e3b74723SMarkus Mayer 	 */
428b61d3e87SFlorian Fainelli 	release_mbox = (priv->dpfe_api->version < 2)
429e3b74723SMarkus Mayer 			? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX;
430e3b74723SMarkus Mayer 	writel_relaxed(0, priv->regs + release_mbox);
431e3b74723SMarkus Mayer }
432e3b74723SMarkus Mayer 
__send_command(struct brcmstb_dpfe_priv * priv,unsigned int cmd,u32 result[])433abf94566SMarkus Mayer static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd,
4342f330cafSMarkus Mayer 			  u32 result[])
4352f330cafSMarkus Mayer {
4362f330cafSMarkus Mayer 	void __iomem *regs = priv->regs;
437e3b74723SMarkus Mayer 	unsigned int i, chksum, chksum_idx;
4381d9e93faSKrzysztof Kozlowski 	const u32 *msg;
4392f330cafSMarkus Mayer 	int ret = 0;
4402f330cafSMarkus Mayer 	u32 resp;
4412f330cafSMarkus Mayer 
4422f330cafSMarkus Mayer 	if (cmd >= DPFE_CMD_MAX)
4432f330cafSMarkus Mayer 		return -1;
4442f330cafSMarkus Mayer 
4451d9e93faSKrzysztof Kozlowski 	msg = priv->dpfe_api->command[cmd];
4461d9e93faSKrzysztof Kozlowski 
4472f330cafSMarkus Mayer 	mutex_lock(&priv->lock);
4482f330cafSMarkus Mayer 
449a7c25759SMarkus Mayer 	/* Wait for DCPU to become ready */
450a7c25759SMarkus Mayer 	for (i = 0; i < DELAY_LOOP_MAX; i++) {
451a7c25759SMarkus Mayer 		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
452a7c25759SMarkus Mayer 		if (resp == 0)
453a7c25759SMarkus Mayer 			break;
454a7c25759SMarkus Mayer 		msleep(1);
455a7c25759SMarkus Mayer 	}
456a7c25759SMarkus Mayer 	if (resp != 0) {
457a7c25759SMarkus Mayer 		mutex_unlock(&priv->lock);
458f42ae4bbSMarkus Mayer 		return -ffs(DCPU_RET_ERR_TIMEDOUT);
459a7c25759SMarkus Mayer 	}
460a7c25759SMarkus Mayer 
4615d06f53dSFlorian Fainelli 	/* Compute checksum over the message */
4625d06f53dSFlorian Fainelli 	chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
4635d06f53dSFlorian Fainelli 	chksum = get_msg_chksum(msg, chksum_idx);
4645d06f53dSFlorian Fainelli 
4652f330cafSMarkus Mayer 	/* Write command and arguments to message area */
4665d06f53dSFlorian Fainelli 	for (i = 0; i < MSG_FIELD_MAX; i++) {
4675d06f53dSFlorian Fainelli 		if (i == chksum_idx)
4685d06f53dSFlorian Fainelli 			writel_relaxed(chksum, regs + DCPU_MSG_RAM(i));
4695d06f53dSFlorian Fainelli 		else
4702f330cafSMarkus Mayer 			writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
4715d06f53dSFlorian Fainelli 	}
4722f330cafSMarkus Mayer 
4732f330cafSMarkus Mayer 	/* Tell DCPU there is a command waiting */
4742f330cafSMarkus Mayer 	writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
4752f330cafSMarkus Mayer 
4762f330cafSMarkus Mayer 	/* Wait for DCPU to process the command */
4772f330cafSMarkus Mayer 	for (i = 0; i < DELAY_LOOP_MAX; i++) {
4782f330cafSMarkus Mayer 		/* Read response code */
4792f330cafSMarkus Mayer 		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
4802f330cafSMarkus Mayer 		if (resp > 0)
4812f330cafSMarkus Mayer 			break;
4827ccd2ffcSMarkus Mayer 		msleep(1);
4832f330cafSMarkus Mayer 	}
4842f330cafSMarkus Mayer 
4852f330cafSMarkus Mayer 	if (i == DELAY_LOOP_MAX) {
4862f330cafSMarkus Mayer 		resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
4872f330cafSMarkus Mayer 		ret = -ffs(resp);
4882f330cafSMarkus Mayer 	} else {
4892f330cafSMarkus Mayer 		/* Read response data */
4902f330cafSMarkus Mayer 		for (i = 0; i < MSG_FIELD_MAX; i++)
4912f330cafSMarkus Mayer 			result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
492e3b74723SMarkus Mayer 		chksum_idx = result[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
4932f330cafSMarkus Mayer 	}
4942f330cafSMarkus Mayer 
4952f330cafSMarkus Mayer 	/* Tell DCPU we are done */
496e3b74723SMarkus Mayer 	__finalize_command(priv);
4972f330cafSMarkus Mayer 
4982f330cafSMarkus Mayer 	mutex_unlock(&priv->lock);
4992f330cafSMarkus Mayer 
5002f330cafSMarkus Mayer 	if (ret)
5012f330cafSMarkus Mayer 		return ret;
5022f330cafSMarkus Mayer 
5032f330cafSMarkus Mayer 	/* Verify response */
504e3b74723SMarkus Mayer 	chksum = get_msg_chksum(result, chksum_idx);
505e3b74723SMarkus Mayer 	if (chksum != result[chksum_idx])
5062f330cafSMarkus Mayer 		resp = DCPU_RET_ERR_CHKSUM;
5072f330cafSMarkus Mayer 
5082f330cafSMarkus Mayer 	if (resp != DCPU_RET_SUCCESS) {
5092f330cafSMarkus Mayer 		resp &= ~DCPU_RET_ERROR_BIT;
5102f330cafSMarkus Mayer 		ret = -ffs(resp);
5112f330cafSMarkus Mayer 	}
5122f330cafSMarkus Mayer 
5132f330cafSMarkus Mayer 	return ret;
5142f330cafSMarkus Mayer }
5152f330cafSMarkus Mayer 
5162f330cafSMarkus Mayer /* Ensure that the firmware file loaded meets all the requirements. */
__verify_firmware(struct init_data * init,const struct firmware * fw)5172f330cafSMarkus Mayer static int __verify_firmware(struct init_data *init,
5182f330cafSMarkus Mayer 			     const struct firmware *fw)
5192f330cafSMarkus Mayer {
5202f330cafSMarkus Mayer 	const struct dpfe_firmware_header *header = (void *)fw->data;
5212f330cafSMarkus Mayer 	unsigned int dmem_size, imem_size, total_size;
5222f330cafSMarkus Mayer 	bool is_big_endian = false;
5232f330cafSMarkus Mayer 	const u32 *chksum_ptr;
5242f330cafSMarkus Mayer 
5252f330cafSMarkus Mayer 	if (header->magic == DPFE_BE_MAGIC)
5262f330cafSMarkus Mayer 		is_big_endian = true;
5272f330cafSMarkus Mayer 	else if (header->magic != DPFE_LE_MAGIC)
5282f330cafSMarkus Mayer 		return ERR_INVALID_MAGIC;
5292f330cafSMarkus Mayer 
5302f330cafSMarkus Mayer 	if (is_big_endian) {
5312f330cafSMarkus Mayer 		dmem_size = be32_to_cpu(header->dmem_size);
5322f330cafSMarkus Mayer 		imem_size = be32_to_cpu(header->imem_size);
5332f330cafSMarkus Mayer 	} else {
5342f330cafSMarkus Mayer 		dmem_size = le32_to_cpu(header->dmem_size);
5352f330cafSMarkus Mayer 		imem_size = le32_to_cpu(header->imem_size);
5362f330cafSMarkus Mayer 	}
5372f330cafSMarkus Mayer 
5382f330cafSMarkus Mayer 	/* Data and instruction sections are 32 bit words. */
5392f330cafSMarkus Mayer 	if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
5402f330cafSMarkus Mayer 		return ERR_INVALID_SIZE;
5412f330cafSMarkus Mayer 
5422f330cafSMarkus Mayer 	/*
5432f330cafSMarkus Mayer 	 * The header + the data section + the instruction section + the
5442f330cafSMarkus Mayer 	 * checksum must be equal to the total firmware size.
5452f330cafSMarkus Mayer 	 */
5462f330cafSMarkus Mayer 	total_size = dmem_size + imem_size + sizeof(*header) +
5472f330cafSMarkus Mayer 		sizeof(*chksum_ptr);
5482f330cafSMarkus Mayer 	if (total_size != fw->size)
5492f330cafSMarkus Mayer 		return ERR_INVALID_SIZE;
5502f330cafSMarkus Mayer 
5512f330cafSMarkus Mayer 	/* The checksum comes at the very end. */
5522f330cafSMarkus Mayer 	chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
5532f330cafSMarkus Mayer 
5542f330cafSMarkus Mayer 	init->is_big_endian = is_big_endian;
5552f330cafSMarkus Mayer 	init->dmem_len = dmem_size;
5562f330cafSMarkus Mayer 	init->imem_len = imem_size;
5572f330cafSMarkus Mayer 	init->chksum = (is_big_endian)
5582f330cafSMarkus Mayer 		? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
5592f330cafSMarkus Mayer 
5602f330cafSMarkus Mayer 	return 0;
5612f330cafSMarkus Mayer }
5622f330cafSMarkus Mayer 
5632f330cafSMarkus Mayer /* Verify checksum by reading back the firmware from co-processor RAM. */
__verify_fw_checksum(struct init_data * init,struct brcmstb_dpfe_priv * priv,const struct dpfe_firmware_header * header,u32 checksum)5642f330cafSMarkus Mayer static int __verify_fw_checksum(struct init_data *init,
565abf94566SMarkus Mayer 				struct brcmstb_dpfe_priv *priv,
5662f330cafSMarkus Mayer 				const struct dpfe_firmware_header *header,
5672f330cafSMarkus Mayer 				u32 checksum)
5682f330cafSMarkus Mayer {
5692f330cafSMarkus Mayer 	u32 magic, sequence, version, sum;
5702f330cafSMarkus Mayer 	u32 __iomem *dmem = priv->dmem;
5712f330cafSMarkus Mayer 	u32 __iomem *imem = priv->imem;
5722f330cafSMarkus Mayer 	unsigned int i;
5732f330cafSMarkus Mayer 
5742f330cafSMarkus Mayer 	if (init->is_big_endian) {
5752f330cafSMarkus Mayer 		magic = be32_to_cpu(header->magic);
5762f330cafSMarkus Mayer 		sequence = be32_to_cpu(header->sequence);
5772f330cafSMarkus Mayer 		version = be32_to_cpu(header->version);
5782f330cafSMarkus Mayer 	} else {
5792f330cafSMarkus Mayer 		magic = le32_to_cpu(header->magic);
5802f330cafSMarkus Mayer 		sequence = le32_to_cpu(header->sequence);
5812f330cafSMarkus Mayer 		version = le32_to_cpu(header->version);
5822f330cafSMarkus Mayer 	}
5832f330cafSMarkus Mayer 
5842f330cafSMarkus Mayer 	sum = magic + sequence + version + init->dmem_len + init->imem_len;
5852f330cafSMarkus Mayer 
5862f330cafSMarkus Mayer 	for (i = 0; i < init->dmem_len / sizeof(u32); i++)
5872f330cafSMarkus Mayer 		sum += readl_relaxed(dmem + i);
5882f330cafSMarkus Mayer 
5892f330cafSMarkus Mayer 	for (i = 0; i < init->imem_len / sizeof(u32); i++)
5902f330cafSMarkus Mayer 		sum += readl_relaxed(imem + i);
5912f330cafSMarkus Mayer 
5922f330cafSMarkus Mayer 	return (sum == checksum) ? 0 : -1;
5932f330cafSMarkus Mayer }
5942f330cafSMarkus Mayer 
__write_firmware(u32 __iomem * mem,const u32 * fw,unsigned int size,bool is_big_endian)5952f330cafSMarkus Mayer static int __write_firmware(u32 __iomem *mem, const u32 *fw,
5962f330cafSMarkus Mayer 			    unsigned int size, bool is_big_endian)
5972f330cafSMarkus Mayer {
5982f330cafSMarkus Mayer 	unsigned int i;
5992f330cafSMarkus Mayer 
6002f330cafSMarkus Mayer 	/* Convert size to 32-bit words. */
6012f330cafSMarkus Mayer 	size /= sizeof(u32);
6022f330cafSMarkus Mayer 
6032f330cafSMarkus Mayer 	/* It is recommended to clear the firmware area first. */
6042f330cafSMarkus Mayer 	for (i = 0; i < size; i++)
6052f330cafSMarkus Mayer 		writel_relaxed(0, mem + i);
6062f330cafSMarkus Mayer 
6072f330cafSMarkus Mayer 	/* Now copy it. */
6082f330cafSMarkus Mayer 	if (is_big_endian) {
6092f330cafSMarkus Mayer 		for (i = 0; i < size; i++)
6102f330cafSMarkus Mayer 			writel_relaxed(be32_to_cpu(fw[i]), mem + i);
6112f330cafSMarkus Mayer 	} else {
6122f330cafSMarkus Mayer 		for (i = 0; i < size; i++)
6132f330cafSMarkus Mayer 			writel_relaxed(le32_to_cpu(fw[i]), mem + i);
6142f330cafSMarkus Mayer 	}
6152f330cafSMarkus Mayer 
6162f330cafSMarkus Mayer 	return 0;
6172f330cafSMarkus Mayer }
6182f330cafSMarkus Mayer 
brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv * priv)619ac2ea9cfSMarkus Mayer static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv)
6202f330cafSMarkus Mayer {
6212f330cafSMarkus Mayer 	const struct dpfe_firmware_header *header;
6222f330cafSMarkus Mayer 	unsigned int dmem_size, imem_size;
623ac2ea9cfSMarkus Mayer 	struct device *dev = priv->dev;
6242f330cafSMarkus Mayer 	bool is_big_endian = false;
6252f330cafSMarkus Mayer 	const struct firmware *fw;
6262f330cafSMarkus Mayer 	const u32 *dmem, *imem;
6276ef972b1SMarkus Mayer 	struct init_data init;
6282f330cafSMarkus Mayer 	const void *fw_blob;
6292f330cafSMarkus Mayer 	int ret;
6302f330cafSMarkus Mayer 
631a56d339eSMarkus Mayer 	/*
632a56d339eSMarkus Mayer 	 * Skip downloading the firmware if the DCPU is already running and
633a56d339eSMarkus Mayer 	 * responding to commands.
634a56d339eSMarkus Mayer 	 */
63575d316e7SMarkus Mayer 	if (is_dcpu_enabled(priv)) {
636a56d339eSMarkus Mayer 		u32 response[MSG_FIELD_MAX];
637a56d339eSMarkus Mayer 
638a56d339eSMarkus Mayer 		ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
639a56d339eSMarkus Mayer 		if (!ret)
640a56d339eSMarkus Mayer 			return 0;
641a56d339eSMarkus Mayer 	}
642a56d339eSMarkus Mayer 
64358a8499fSMarkus Mayer 	/*
64458a8499fSMarkus Mayer 	 * If the firmware filename is NULL it means the boot firmware has to
64558a8499fSMarkus Mayer 	 * download the DCPU firmware for us. If that didn't work, we have to
64658a8499fSMarkus Mayer 	 * bail, since downloading it ourselves wouldn't work either.
64758a8499fSMarkus Mayer 	 */
64858a8499fSMarkus Mayer 	if (!priv->dpfe_api->fw_name)
64958a8499fSMarkus Mayer 		return -ENODEV;
65058a8499fSMarkus Mayer 
651242fb2f1SMarkus Mayer 	ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev);
652242fb2f1SMarkus Mayer 	/*
653242fb2f1SMarkus Mayer 	 * Defer the firmware download if the firmware file couldn't be found.
654242fb2f1SMarkus Mayer 	 * The root file system may not be available yet.
655242fb2f1SMarkus Mayer 	 */
6562f330cafSMarkus Mayer 	if (ret)
657242fb2f1SMarkus Mayer 		return (ret == -ENOENT) ? -EPROBE_DEFER : ret;
6582f330cafSMarkus Mayer 
6596ef972b1SMarkus Mayer 	ret = __verify_firmware(&init, fw);
6604da1edcfSAlex Dewar 	if (ret) {
6614da1edcfSAlex Dewar 		ret = -EFAULT;
6624da1edcfSAlex Dewar 		goto release_fw;
6634da1edcfSAlex Dewar 	}
6642f330cafSMarkus Mayer 
66575d316e7SMarkus Mayer 	__disable_dcpu(priv);
6662f330cafSMarkus Mayer 
6676ef972b1SMarkus Mayer 	is_big_endian = init.is_big_endian;
6686ef972b1SMarkus Mayer 	dmem_size = init.dmem_len;
6696ef972b1SMarkus Mayer 	imem_size = init.imem_len;
6702f330cafSMarkus Mayer 
6712f330cafSMarkus Mayer 	/* At the beginning of the firmware blob is a header. */
6722f330cafSMarkus Mayer 	header = (struct dpfe_firmware_header *)fw->data;
6732f330cafSMarkus Mayer 	/* Void pointer to the beginning of the actual firmware. */
6742f330cafSMarkus Mayer 	fw_blob = fw->data + sizeof(*header);
6752f330cafSMarkus Mayer 	/* IMEM comes right after the header. */
6762f330cafSMarkus Mayer 	imem = fw_blob;
6772f330cafSMarkus Mayer 	/* DMEM follows after IMEM. */
6782f330cafSMarkus Mayer 	dmem = fw_blob + imem_size;
6792f330cafSMarkus Mayer 
6802f330cafSMarkus Mayer 	ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
6812f330cafSMarkus Mayer 	if (ret)
6824da1edcfSAlex Dewar 		goto release_fw;
6832f330cafSMarkus Mayer 	ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
6842f330cafSMarkus Mayer 	if (ret)
6854da1edcfSAlex Dewar 		goto release_fw;
6862f330cafSMarkus Mayer 
6876ef972b1SMarkus Mayer 	ret = __verify_fw_checksum(&init, priv, header, init.chksum);
6882f330cafSMarkus Mayer 	if (ret)
6894da1edcfSAlex Dewar 		goto release_fw;
6902f330cafSMarkus Mayer 
69175d316e7SMarkus Mayer 	__enable_dcpu(priv);
6922f330cafSMarkus Mayer 
6934da1edcfSAlex Dewar release_fw:
6944da1edcfSAlex Dewar 	release_firmware(fw);
6954da1edcfSAlex Dewar 	return ret;
6962f330cafSMarkus Mayer }
6972f330cafSMarkus Mayer 
generic_show(unsigned int command,u32 response[],struct brcmstb_dpfe_priv * priv,char * buf)6982f330cafSMarkus Mayer static ssize_t generic_show(unsigned int command, u32 response[],
699abf94566SMarkus Mayer 			    struct brcmstb_dpfe_priv *priv, char *buf)
7002f330cafSMarkus Mayer {
7012f330cafSMarkus Mayer 	int ret;
7022f330cafSMarkus Mayer 
7032f330cafSMarkus Mayer 	if (!priv)
7042f330cafSMarkus Mayer 		return sprintf(buf, "ERROR: driver private data not set\n");
7052f330cafSMarkus Mayer 
7062f330cafSMarkus Mayer 	ret = __send_command(priv, command, response);
7072f330cafSMarkus Mayer 	if (ret < 0)
708f42ae4bbSMarkus Mayer 		return sprintf(buf, "ERROR: %s\n", get_error_text(-ret));
7092f330cafSMarkus Mayer 
7102f330cafSMarkus Mayer 	return 0;
7112f330cafSMarkus Mayer }
7122f330cafSMarkus Mayer 
show_info(struct device * dev,struct device_attribute * devattr,char * buf)7132f330cafSMarkus Mayer static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
7142f330cafSMarkus Mayer 			 char *buf)
7152f330cafSMarkus Mayer {
7162f330cafSMarkus Mayer 	u32 response[MSG_FIELD_MAX];
717abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
7182f330cafSMarkus Mayer 	unsigned int info;
7199f2c4d95SMarkus Mayer 	ssize_t ret;
7202f330cafSMarkus Mayer 
721900c8f57SMarkus Mayer 	priv = dev_get_drvdata(dev);
722900c8f57SMarkus Mayer 	ret = generic_show(DPFE_CMD_GET_INFO, response, priv, buf);
7232f330cafSMarkus Mayer 	if (ret)
7242f330cafSMarkus Mayer 		return ret;
7252f330cafSMarkus Mayer 
7262f330cafSMarkus Mayer 	info = response[MSG_ARG0];
7272f330cafSMarkus Mayer 
7282f330cafSMarkus Mayer 	return sprintf(buf, "%u.%u.%u.%u\n",
7292f330cafSMarkus Mayer 		       (info >> 24) & 0xff,
7302f330cafSMarkus Mayer 		       (info >> 16) & 0xff,
7312f330cafSMarkus Mayer 		       (info >> 8) & 0xff,
7322f330cafSMarkus Mayer 		       info & 0xff);
7332f330cafSMarkus Mayer }
7342f330cafSMarkus Mayer 
show_refresh(struct device * dev,struct device_attribute * devattr,char * buf)7352f330cafSMarkus Mayer static ssize_t show_refresh(struct device *dev,
7362f330cafSMarkus Mayer 			    struct device_attribute *devattr, char *buf)
7372f330cafSMarkus Mayer {
7382f330cafSMarkus Mayer 	u32 response[MSG_FIELD_MAX];
7392f330cafSMarkus Mayer 	void __iomem *info;
740abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
7412f330cafSMarkus Mayer 	u8 refresh, sr_abort, ppre, thermal_offs, tuf;
7422f330cafSMarkus Mayer 	u32 mr4;
7439f2c4d95SMarkus Mayer 	ssize_t ret;
7442f330cafSMarkus Mayer 
745900c8f57SMarkus Mayer 	priv = dev_get_drvdata(dev);
746900c8f57SMarkus Mayer 	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
7472f330cafSMarkus Mayer 	if (ret)
7482f330cafSMarkus Mayer 		return ret;
7492f330cafSMarkus Mayer 
750fee5f1efSMarkus Mayer 	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
751fee5f1efSMarkus Mayer 	if (!info)
752fee5f1efSMarkus Mayer 		return ret;
7532f330cafSMarkus Mayer 
75478a6f5beSMarkus Mayer 	mr4 = (readl_relaxed(info + DRAM_INFO_MR4) >> DRAM_INFO_MR4_SHIFT) &
75578a6f5beSMarkus Mayer 	       DRAM_INFO_MR4_MASK;
7562f330cafSMarkus Mayer 
7572f330cafSMarkus Mayer 	refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
7582f330cafSMarkus Mayer 	sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
7592f330cafSMarkus Mayer 	ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
7602f330cafSMarkus Mayer 	thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
7612f330cafSMarkus Mayer 	tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
7622f330cafSMarkus Mayer 
7632f330cafSMarkus Mayer 	return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
7642f330cafSMarkus Mayer 		       readl_relaxed(info + DRAM_INFO_INTERVAL),
7652f330cafSMarkus Mayer 		       refresh, sr_abort, ppre, thermal_offs, tuf,
7662f330cafSMarkus Mayer 		       readl_relaxed(info + DRAM_INFO_ERROR));
7672f330cafSMarkus Mayer }
7682f330cafSMarkus Mayer 
store_refresh(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)7692f330cafSMarkus Mayer static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
7702f330cafSMarkus Mayer 			  const char *buf, size_t count)
7712f330cafSMarkus Mayer {
7722f330cafSMarkus Mayer 	u32 response[MSG_FIELD_MAX];
773abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
7742f330cafSMarkus Mayer 	void __iomem *info;
7752f330cafSMarkus Mayer 	unsigned long val;
7762f330cafSMarkus Mayer 	int ret;
7772f330cafSMarkus Mayer 
7782f330cafSMarkus Mayer 	if (kstrtoul(buf, 0, &val) < 0)
7792f330cafSMarkus Mayer 		return -EINVAL;
7802f330cafSMarkus Mayer 
7812f330cafSMarkus Mayer 	priv = dev_get_drvdata(dev);
7822f330cafSMarkus Mayer 	ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
7832f330cafSMarkus Mayer 	if (ret)
7842f330cafSMarkus Mayer 		return ret;
7852f330cafSMarkus Mayer 
786fee5f1efSMarkus Mayer 	info = get_msg_ptr(priv, response[MSG_ARG0], NULL, NULL);
787fee5f1efSMarkus Mayer 	if (!info)
788fee5f1efSMarkus Mayer 		return -EIO;
789fee5f1efSMarkus Mayer 
7902f330cafSMarkus Mayer 	writel_relaxed(val, info + DRAM_INFO_INTERVAL);
7912f330cafSMarkus Mayer 
7922f330cafSMarkus Mayer 	return count;
7932f330cafSMarkus Mayer }
7942f330cafSMarkus Mayer 
show_vendor(struct device * dev,struct device_attribute * devattr,char * buf)7952f330cafSMarkus Mayer static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
7962f330cafSMarkus Mayer 			   char *buf)
7972f330cafSMarkus Mayer {
7982f330cafSMarkus Mayer 	u32 response[MSG_FIELD_MAX];
799abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
8002f330cafSMarkus Mayer 	void __iomem *info;
8019f2c4d95SMarkus Mayer 	ssize_t ret;
80278a6f5beSMarkus Mayer 	u32 mr5, mr6, mr7, mr8, err;
8032f330cafSMarkus Mayer 
8042f330cafSMarkus Mayer 	priv = dev_get_drvdata(dev);
805900c8f57SMarkus Mayer 	ret = generic_show(DPFE_CMD_GET_VENDOR, response, priv, buf);
8062f330cafSMarkus Mayer 	if (ret)
8072f330cafSMarkus Mayer 		return ret;
808fee5f1efSMarkus Mayer 
809fee5f1efSMarkus Mayer 	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
810fee5f1efSMarkus Mayer 	if (!info)
811fee5f1efSMarkus Mayer 		return ret;
8122f330cafSMarkus Mayer 
81378a6f5beSMarkus Mayer 	mr5 = (readl_relaxed(info + DRAM_VENDOR_MR5) >> DRAM_VENDOR_SHIFT) &
81478a6f5beSMarkus Mayer 		DRAM_VENDOR_MASK;
81578a6f5beSMarkus Mayer 	mr6 = (readl_relaxed(info + DRAM_VENDOR_MR6) >> DRAM_VENDOR_SHIFT) &
81678a6f5beSMarkus Mayer 		DRAM_VENDOR_MASK;
81778a6f5beSMarkus Mayer 	mr7 = (readl_relaxed(info + DRAM_VENDOR_MR7) >> DRAM_VENDOR_SHIFT) &
81878a6f5beSMarkus Mayer 		DRAM_VENDOR_MASK;
81978a6f5beSMarkus Mayer 	mr8 = (readl_relaxed(info + DRAM_VENDOR_MR8) >> DRAM_VENDOR_SHIFT) &
82078a6f5beSMarkus Mayer 		DRAM_VENDOR_MASK;
82178a6f5beSMarkus Mayer 	err = readl_relaxed(info + DRAM_VENDOR_ERROR) & DRAM_VENDOR_MASK;
82278a6f5beSMarkus Mayer 
82378a6f5beSMarkus Mayer 	return sprintf(buf, "%#x %#x %#x %#x %#x\n", mr5, mr6, mr7, mr8, err);
8242f330cafSMarkus Mayer }
8252f330cafSMarkus Mayer 
show_dram(struct device * dev,struct device_attribute * devattr,char * buf)826e3b74723SMarkus Mayer static ssize_t show_dram(struct device *dev, struct device_attribute *devattr,
827e3b74723SMarkus Mayer 			 char *buf)
828e3b74723SMarkus Mayer {
829e3b74723SMarkus Mayer 	u32 response[MSG_FIELD_MAX];
830abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
831e3b74723SMarkus Mayer 	ssize_t ret;
832e3b74723SMarkus Mayer 	u32 mr4, mr5, mr6, mr7, mr8, err;
833e3b74723SMarkus Mayer 
834e3b74723SMarkus Mayer 	priv = dev_get_drvdata(dev);
835e3b74723SMarkus Mayer 	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
836e3b74723SMarkus Mayer 	if (ret)
837e3b74723SMarkus Mayer 		return ret;
838e3b74723SMarkus Mayer 
839e3b74723SMarkus Mayer 	mr4 = response[MSG_ARG0 + 0] & DRAM_INFO_MR4_MASK;
840e3b74723SMarkus Mayer 	mr5 = response[MSG_ARG0 + 1] & DRAM_DDR_INFO_MASK;
841e3b74723SMarkus Mayer 	mr6 = response[MSG_ARG0 + 2] & DRAM_DDR_INFO_MASK;
842e3b74723SMarkus Mayer 	mr7 = response[MSG_ARG0 + 3] & DRAM_DDR_INFO_MASK;
843e3b74723SMarkus Mayer 	mr8 = response[MSG_ARG0 + 4] & DRAM_DDR_INFO_MASK;
844e3b74723SMarkus Mayer 	err = response[MSG_ARG0 + 5] & DRAM_DDR_INFO_MASK;
845e3b74723SMarkus Mayer 
846e3b74723SMarkus Mayer 	return sprintf(buf, "%#x %#x %#x %#x %#x %#x\n", mr4, mr5, mr6, mr7,
847e3b74723SMarkus Mayer 			mr8, err);
8482f330cafSMarkus Mayer }
8492f330cafSMarkus Mayer 
brcmstb_dpfe_resume(struct platform_device * pdev)8502f330cafSMarkus Mayer static int brcmstb_dpfe_resume(struct platform_device *pdev)
8512f330cafSMarkus Mayer {
852ac2ea9cfSMarkus Mayer 	struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev);
853ac2ea9cfSMarkus Mayer 
854ac2ea9cfSMarkus Mayer 	return brcmstb_dpfe_download_firmware(priv);
8552f330cafSMarkus Mayer }
8562f330cafSMarkus Mayer 
brcmstb_dpfe_probe(struct platform_device * pdev)8572f330cafSMarkus Mayer static int brcmstb_dpfe_probe(struct platform_device *pdev)
8582f330cafSMarkus Mayer {
8592f330cafSMarkus Mayer 	struct device *dev = &pdev->dev;
860abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv;
8612f330cafSMarkus Mayer 	int ret;
8622f330cafSMarkus Mayer 
8632f330cafSMarkus Mayer 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
8642f330cafSMarkus Mayer 	if (!priv)
8652f330cafSMarkus Mayer 		return -ENOMEM;
8662f330cafSMarkus Mayer 
86756ece3faSMarkus Mayer 	priv->dev = dev;
86856ece3faSMarkus Mayer 
8692f330cafSMarkus Mayer 	mutex_init(&priv->lock);
8702f330cafSMarkus Mayer 	platform_set_drvdata(pdev, priv);
8712f330cafSMarkus Mayer 
872ef231fefSKrzysztof Kozlowski 	priv->regs = devm_platform_ioremap_resource_byname(pdev, "dpfe-cpu");
8732f330cafSMarkus Mayer 	if (IS_ERR(priv->regs)) {
8742f330cafSMarkus Mayer 		dev_err(dev, "couldn't map DCPU registers\n");
8752f330cafSMarkus Mayer 		return -ENODEV;
8762f330cafSMarkus Mayer 	}
8772f330cafSMarkus Mayer 
878ef231fefSKrzysztof Kozlowski 	priv->dmem = devm_platform_ioremap_resource_byname(pdev, "dpfe-dmem");
8792f330cafSMarkus Mayer 	if (IS_ERR(priv->dmem)) {
8802f330cafSMarkus Mayer 		dev_err(dev, "Couldn't map DCPU data memory\n");
8812f330cafSMarkus Mayer 		return -ENOENT;
8822f330cafSMarkus Mayer 	}
8832f330cafSMarkus Mayer 
884ef231fefSKrzysztof Kozlowski 	priv->imem = devm_platform_ioremap_resource_byname(pdev, "dpfe-imem");
8852f330cafSMarkus Mayer 	if (IS_ERR(priv->imem)) {
8862f330cafSMarkus Mayer 		dev_err(dev, "Couldn't map DCPU instruction memory\n");
8872f330cafSMarkus Mayer 		return -ENOENT;
8882f330cafSMarkus Mayer 	}
8892f330cafSMarkus Mayer 
89058a8499fSMarkus Mayer 	priv->dpfe_api = of_device_get_match_data(dev);
89158a8499fSMarkus Mayer 	if (unlikely(!priv->dpfe_api)) {
89258a8499fSMarkus Mayer 		/*
89358a8499fSMarkus Mayer 		 * It should be impossible to end up here, but to be safe we
89458a8499fSMarkus Mayer 		 * check anyway.
89558a8499fSMarkus Mayer 		 */
89658a8499fSMarkus Mayer 		dev_err(dev, "Couldn't determine API\n");
89758a8499fSMarkus Mayer 		return -ENOENT;
89858a8499fSMarkus Mayer 	}
8992f330cafSMarkus Mayer 
900ac2ea9cfSMarkus Mayer 	ret = brcmstb_dpfe_download_firmware(priv);
90174ca0d83SKrzysztof Kozlowski 	if (ret)
90274ca0d83SKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "Couldn't download firmware\n");
9032f330cafSMarkus Mayer 
9045ef108b4SMarkus Mayer 	ret = sysfs_create_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
905b1d0973eSFlorian Fainelli 	if (!ret)
90658a8499fSMarkus Mayer 		dev_info(dev, "registered with API v%d.\n",
90758a8499fSMarkus Mayer 			 priv->dpfe_api->version);
9082f330cafSMarkus Mayer 
9092f330cafSMarkus Mayer 	return ret;
9102f330cafSMarkus Mayer }
9112f330cafSMarkus Mayer 
brcmstb_dpfe_remove(struct platform_device * pdev)912b1d0973eSFlorian Fainelli static int brcmstb_dpfe_remove(struct platform_device *pdev)
913b1d0973eSFlorian Fainelli {
914abf94566SMarkus Mayer 	struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev);
9155ef108b4SMarkus Mayer 
9165ef108b4SMarkus Mayer 	sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
917b1d0973eSFlorian Fainelli 
918b1d0973eSFlorian Fainelli 	return 0;
919b1d0973eSFlorian Fainelli }
920b1d0973eSFlorian Fainelli 
9212f330cafSMarkus Mayer static const struct of_device_id brcmstb_dpfe_of_match[] = {
922e3b74723SMarkus Mayer 	/* Use legacy API v2 for a select number of chips */
923b61d3e87SFlorian Fainelli 	{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 },
924b61d3e87SFlorian Fainelli 	{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 },
925b61d3e87SFlorian Fainelli 	{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 },
926b61d3e87SFlorian Fainelli 	{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 },
927e3b74723SMarkus Mayer 	/* API v3 is the default going forward */
928e3b74723SMarkus Mayer 	{ .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 },
9292f330cafSMarkus Mayer 	{}
9302f330cafSMarkus Mayer };
9312f330cafSMarkus Mayer MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
9322f330cafSMarkus Mayer 
9332f330cafSMarkus Mayer static struct platform_driver brcmstb_dpfe_driver = {
9342f330cafSMarkus Mayer 	.driver	= {
9352f330cafSMarkus Mayer 		.name = DRVNAME,
9362f330cafSMarkus Mayer 		.of_match_table = brcmstb_dpfe_of_match,
9372f330cafSMarkus Mayer 	},
9382f330cafSMarkus Mayer 	.probe = brcmstb_dpfe_probe,
939b1d0973eSFlorian Fainelli 	.remove	= brcmstb_dpfe_remove,
9402f330cafSMarkus Mayer 	.resume = brcmstb_dpfe_resume,
9412f330cafSMarkus Mayer };
9422f330cafSMarkus Mayer 
9432f330cafSMarkus Mayer module_platform_driver(brcmstb_dpfe_driver);
9442f330cafSMarkus Mayer 
9452f330cafSMarkus Mayer MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
9462f330cafSMarkus Mayer MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
9472f330cafSMarkus Mayer MODULE_LICENSE("GPL");
948