18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Broadcom
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * This driver provides access to the DPFE interface of Broadcom STB SoCs.
108c2ecf20Sopenharmony_ci * The firmware running on the DCPU inside the DDR PHY can provide current
118c2ecf20Sopenharmony_ci * information about the system's RAM, for instance the DRAM refresh rate.
128c2ecf20Sopenharmony_ci * This can be used as an indirect indicator for the DRAM's temperature.
138c2ecf20Sopenharmony_ci * Slower refresh rate means cooler RAM, higher refresh rate means hotter
148c2ecf20Sopenharmony_ci * RAM.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Throughout the driver, we use readl_relaxed() and writel_relaxed(), which
178c2ecf20Sopenharmony_ci * already contain the appropriate le32_to_cpu()/cpu_to_le32() calls.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Note regarding the loading of the firmware image: we use be32_to_cpu()
208c2ecf20Sopenharmony_ci * and le_32_to_cpu(), so we can support the following four cases:
218c2ecf20Sopenharmony_ci *     - LE kernel + LE firmware image (the most common case)
228c2ecf20Sopenharmony_ci *     - LE kernel + BE firmware image
238c2ecf20Sopenharmony_ci *     - BE kernel + LE firmware image
248c2ecf20Sopenharmony_ci *     - BE kernel + BE firmware image
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * The DPCU always runs in big endian mode. The firmware image, however, can
278c2ecf20Sopenharmony_ci * be in either format. Also, communication between host CPU and DCPU is
288c2ecf20Sopenharmony_ci * always in little endian.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <linux/firmware.h>
338c2ecf20Sopenharmony_ci#include <linux/io.h>
348c2ecf20Sopenharmony_ci#include <linux/module.h>
358c2ecf20Sopenharmony_ci#include <linux/of_address.h>
368c2ecf20Sopenharmony_ci#include <linux/of_device.h>
378c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define DRVNAME			"brcmstb-dpfe"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* DCPU register offsets */
428c2ecf20Sopenharmony_ci#define REG_DCPU_RESET		0x0
438c2ecf20Sopenharmony_ci#define REG_TO_DCPU_MBOX	0x10
448c2ecf20Sopenharmony_ci#define REG_TO_HOST_MBOX	0x14
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Macros to process offsets returned by the DCPU */
478c2ecf20Sopenharmony_ci#define DRAM_MSG_ADDR_OFFSET	0x0
488c2ecf20Sopenharmony_ci#define DRAM_MSG_TYPE_OFFSET	0x1c
498c2ecf20Sopenharmony_ci#define DRAM_MSG_ADDR_MASK	((1UL << DRAM_MSG_TYPE_OFFSET) - 1)
508c2ecf20Sopenharmony_ci#define DRAM_MSG_TYPE_MASK	((1UL << \
518c2ecf20Sopenharmony_ci				 (BITS_PER_LONG - DRAM_MSG_TYPE_OFFSET)) - 1)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Message RAM */
548c2ecf20Sopenharmony_ci#define DCPU_MSG_RAM_START	0x100
558c2ecf20Sopenharmony_ci#define DCPU_MSG_RAM(x)		(DCPU_MSG_RAM_START + (x) * sizeof(u32))
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* DRAM Info Offsets & Masks */
588c2ecf20Sopenharmony_ci#define DRAM_INFO_INTERVAL	0x0
598c2ecf20Sopenharmony_ci#define DRAM_INFO_MR4		0x4
608c2ecf20Sopenharmony_ci#define DRAM_INFO_ERROR		0x8
618c2ecf20Sopenharmony_ci#define DRAM_INFO_MR4_MASK	0xff
628c2ecf20Sopenharmony_ci#define DRAM_INFO_MR4_SHIFT	24	/* We need to look at byte 3 */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* DRAM MR4 Offsets & Masks */
658c2ecf20Sopenharmony_ci#define DRAM_MR4_REFRESH	0x0	/* Refresh rate */
668c2ecf20Sopenharmony_ci#define DRAM_MR4_SR_ABORT	0x3	/* Self Refresh Abort */
678c2ecf20Sopenharmony_ci#define DRAM_MR4_PPRE		0x4	/* Post-package repair entry/exit */
688c2ecf20Sopenharmony_ci#define DRAM_MR4_TH_OFFS	0x5	/* Thermal Offset; vendor specific */
698c2ecf20Sopenharmony_ci#define DRAM_MR4_TUF		0x7	/* Temperature Update Flag */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define DRAM_MR4_REFRESH_MASK	0x7
728c2ecf20Sopenharmony_ci#define DRAM_MR4_SR_ABORT_MASK	0x1
738c2ecf20Sopenharmony_ci#define DRAM_MR4_PPRE_MASK	0x1
748c2ecf20Sopenharmony_ci#define DRAM_MR4_TH_OFFS_MASK	0x3
758c2ecf20Sopenharmony_ci#define DRAM_MR4_TUF_MASK	0x1
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* DRAM Vendor Offsets & Masks (API v2) */
788c2ecf20Sopenharmony_ci#define DRAM_VENDOR_MR5		0x0
798c2ecf20Sopenharmony_ci#define DRAM_VENDOR_MR6		0x4
808c2ecf20Sopenharmony_ci#define DRAM_VENDOR_MR7		0x8
818c2ecf20Sopenharmony_ci#define DRAM_VENDOR_MR8		0xc
828c2ecf20Sopenharmony_ci#define DRAM_VENDOR_ERROR	0x10
838c2ecf20Sopenharmony_ci#define DRAM_VENDOR_MASK	0xff
848c2ecf20Sopenharmony_ci#define DRAM_VENDOR_SHIFT	24	/* We need to look at byte 3 */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* DRAM Information Offsets & Masks (API v3) */
878c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MR4	0x0
888c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MR5	0x4
898c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MR6	0x8
908c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MR7	0xc
918c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MR8	0x10
928c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_ERROR	0x14
938c2ecf20Sopenharmony_ci#define DRAM_DDR_INFO_MASK	0xff
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Reset register bits & masks */
968c2ecf20Sopenharmony_ci#define DCPU_RESET_SHIFT	0x0
978c2ecf20Sopenharmony_ci#define DCPU_RESET_MASK		0x1
988c2ecf20Sopenharmony_ci#define DCPU_CLK_DISABLE_SHIFT	0x2
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* DCPU return codes */
1018c2ecf20Sopenharmony_ci#define DCPU_RET_ERROR_BIT	BIT(31)
1028c2ecf20Sopenharmony_ci#define DCPU_RET_SUCCESS	0x1
1038c2ecf20Sopenharmony_ci#define DCPU_RET_ERR_HEADER	(DCPU_RET_ERROR_BIT | BIT(0))
1048c2ecf20Sopenharmony_ci#define DCPU_RET_ERR_INVAL	(DCPU_RET_ERROR_BIT | BIT(1))
1058c2ecf20Sopenharmony_ci#define DCPU_RET_ERR_CHKSUM	(DCPU_RET_ERROR_BIT | BIT(2))
1068c2ecf20Sopenharmony_ci#define DCPU_RET_ERR_COMMAND	(DCPU_RET_ERROR_BIT | BIT(3))
1078c2ecf20Sopenharmony_ci/* This error code is not firmware defined and only used in the driver. */
1088c2ecf20Sopenharmony_ci#define DCPU_RET_ERR_TIMEDOUT	(DCPU_RET_ERROR_BIT | BIT(4))
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* Firmware magic */
1118c2ecf20Sopenharmony_ci#define DPFE_BE_MAGIC		0xfe1010fe
1128c2ecf20Sopenharmony_ci#define DPFE_LE_MAGIC		0xfe0101fe
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Error codes */
1158c2ecf20Sopenharmony_ci#define ERR_INVALID_MAGIC	-1
1168c2ecf20Sopenharmony_ci#define ERR_INVALID_SIZE	-2
1178c2ecf20Sopenharmony_ci#define ERR_INVALID_CHKSUM	-3
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* Message types */
1208c2ecf20Sopenharmony_ci#define DPFE_MSG_TYPE_COMMAND	1
1218c2ecf20Sopenharmony_ci#define DPFE_MSG_TYPE_RESPONSE	2
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define DELAY_LOOP_MAX		1000
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cienum dpfe_msg_fields {
1268c2ecf20Sopenharmony_ci	MSG_HEADER,
1278c2ecf20Sopenharmony_ci	MSG_COMMAND,
1288c2ecf20Sopenharmony_ci	MSG_ARG_COUNT,
1298c2ecf20Sopenharmony_ci	MSG_ARG0,
1308c2ecf20Sopenharmony_ci	MSG_FIELD_MAX	= 16 /* Max number of arguments */
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cienum dpfe_commands {
1348c2ecf20Sopenharmony_ci	DPFE_CMD_GET_INFO,
1358c2ecf20Sopenharmony_ci	DPFE_CMD_GET_REFRESH,
1368c2ecf20Sopenharmony_ci	DPFE_CMD_GET_VENDOR,
1378c2ecf20Sopenharmony_ci	DPFE_CMD_MAX /* Last entry */
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Format of the binary firmware file:
1428c2ecf20Sopenharmony_ci *
1438c2ecf20Sopenharmony_ci *   entry
1448c2ecf20Sopenharmony_ci *      0    header
1458c2ecf20Sopenharmony_ci *              value:  0xfe0101fe  <== little endian
1468c2ecf20Sopenharmony_ci *                      0xfe1010fe  <== big endian
1478c2ecf20Sopenharmony_ci *      1    sequence:
1488c2ecf20Sopenharmony_ci *              [31:16] total segments on this build
1498c2ecf20Sopenharmony_ci *              [15:0]  this segment sequence.
1508c2ecf20Sopenharmony_ci *      2    FW version
1518c2ecf20Sopenharmony_ci *      3    IMEM byte size
1528c2ecf20Sopenharmony_ci *      4    DMEM byte size
1538c2ecf20Sopenharmony_ci *           IMEM
1548c2ecf20Sopenharmony_ci *           DMEM
1558c2ecf20Sopenharmony_ci *      last checksum ==> sum of everything
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistruct dpfe_firmware_header {
1588c2ecf20Sopenharmony_ci	u32 magic;
1598c2ecf20Sopenharmony_ci	u32 sequence;
1608c2ecf20Sopenharmony_ci	u32 version;
1618c2ecf20Sopenharmony_ci	u32 imem_size;
1628c2ecf20Sopenharmony_ci	u32 dmem_size;
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/* Things we only need during initialization. */
1668c2ecf20Sopenharmony_cistruct init_data {
1678c2ecf20Sopenharmony_ci	unsigned int dmem_len;
1688c2ecf20Sopenharmony_ci	unsigned int imem_len;
1698c2ecf20Sopenharmony_ci	unsigned int chksum;
1708c2ecf20Sopenharmony_ci	bool is_big_endian;
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/* API version and corresponding commands */
1748c2ecf20Sopenharmony_cistruct dpfe_api {
1758c2ecf20Sopenharmony_ci	int version;
1768c2ecf20Sopenharmony_ci	const char *fw_name;
1778c2ecf20Sopenharmony_ci	const struct attribute_group **sysfs_attrs;
1788c2ecf20Sopenharmony_ci	u32 command[DPFE_CMD_MAX][MSG_FIELD_MAX];
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* Things we need for as long as we are active. */
1828c2ecf20Sopenharmony_cistruct brcmstb_dpfe_priv {
1838c2ecf20Sopenharmony_ci	void __iomem *regs;
1848c2ecf20Sopenharmony_ci	void __iomem *dmem;
1858c2ecf20Sopenharmony_ci	void __iomem *imem;
1868c2ecf20Sopenharmony_ci	struct device *dev;
1878c2ecf20Sopenharmony_ci	const struct dpfe_api *dpfe_api;
1888c2ecf20Sopenharmony_ci	struct mutex lock;
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/*
1928c2ecf20Sopenharmony_ci * Forward declaration of our sysfs attribute functions, so we can declare the
1938c2ecf20Sopenharmony_ci * attribute data structures early.
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic ssize_t show_info(struct device *, struct device_attribute *, char *);
1968c2ecf20Sopenharmony_cistatic ssize_t show_refresh(struct device *, struct device_attribute *, char *);
1978c2ecf20Sopenharmony_cistatic ssize_t store_refresh(struct device *, struct device_attribute *,
1988c2ecf20Sopenharmony_ci			  const char *, size_t);
1998c2ecf20Sopenharmony_cistatic ssize_t show_vendor(struct device *, struct device_attribute *, char *);
2008c2ecf20Sopenharmony_cistatic ssize_t show_dram(struct device *, struct device_attribute *, char *);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/*
2038c2ecf20Sopenharmony_ci * Declare our attributes early, so they can be referenced in the API data
2048c2ecf20Sopenharmony_ci * structure. We need to do this, because the attributes depend on the API
2058c2ecf20Sopenharmony_ci * version.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
2088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
2098c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
2108c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dpfe_dram, 0444, show_dram, NULL);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/* API v2 sysfs attributes */
2138c2ecf20Sopenharmony_cistatic struct attribute *dpfe_v2_attrs[] = {
2148c2ecf20Sopenharmony_ci	&dev_attr_dpfe_info.attr,
2158c2ecf20Sopenharmony_ci	&dev_attr_dpfe_refresh.attr,
2168c2ecf20Sopenharmony_ci	&dev_attr_dpfe_vendor.attr,
2178c2ecf20Sopenharmony_ci	NULL
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(dpfe_v2);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* API v3 sysfs attributes */
2228c2ecf20Sopenharmony_cistatic struct attribute *dpfe_v3_attrs[] = {
2238c2ecf20Sopenharmony_ci	&dev_attr_dpfe_info.attr,
2248c2ecf20Sopenharmony_ci	&dev_attr_dpfe_dram.attr,
2258c2ecf20Sopenharmony_ci	NULL
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(dpfe_v3);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/*
2308c2ecf20Sopenharmony_ci * Old API v2 firmware commands, as defined in the rev 0.61 specification, we
2318c2ecf20Sopenharmony_ci * use a version set to 1 to denote that it is not compatible with the new API
2328c2ecf20Sopenharmony_ci * v2 and onwards.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic const struct dpfe_api dpfe_api_old_v2 = {
2358c2ecf20Sopenharmony_ci	.version = 1,
2368c2ecf20Sopenharmony_ci	.fw_name = "dpfe.bin",
2378c2ecf20Sopenharmony_ci	.sysfs_attrs = dpfe_v2_groups,
2388c2ecf20Sopenharmony_ci	.command = {
2398c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_INFO] = {
2408c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2418c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 1,
2428c2ecf20Sopenharmony_ci			[MSG_ARG_COUNT] = 1,
2438c2ecf20Sopenharmony_ci			[MSG_ARG0] = 1,
2448c2ecf20Sopenharmony_ci		},
2458c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_REFRESH] = {
2468c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2478c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 2,
2488c2ecf20Sopenharmony_ci			[MSG_ARG_COUNT] = 1,
2498c2ecf20Sopenharmony_ci			[MSG_ARG0] = 1,
2508c2ecf20Sopenharmony_ci		},
2518c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_VENDOR] = {
2528c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2538c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 2,
2548c2ecf20Sopenharmony_ci			[MSG_ARG_COUNT] = 1,
2558c2ecf20Sopenharmony_ci			[MSG_ARG0] = 2,
2568c2ecf20Sopenharmony_ci		},
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/*
2618c2ecf20Sopenharmony_ci * API v2 firmware commands, as defined in the rev 0.8 specification, named new
2628c2ecf20Sopenharmony_ci * v2 here
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic const struct dpfe_api dpfe_api_new_v2 = {
2658c2ecf20Sopenharmony_ci	.version = 2,
2668c2ecf20Sopenharmony_ci	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
2678c2ecf20Sopenharmony_ci	.sysfs_attrs = dpfe_v2_groups,
2688c2ecf20Sopenharmony_ci	.command = {
2698c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_INFO] = {
2708c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2718c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 0x101,
2728c2ecf20Sopenharmony_ci		},
2738c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_REFRESH] = {
2748c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2758c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 0x201,
2768c2ecf20Sopenharmony_ci		},
2778c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_VENDOR] = {
2788c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2798c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 0x202,
2808c2ecf20Sopenharmony_ci		},
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/* API v3 firmware commands */
2858c2ecf20Sopenharmony_cistatic const struct dpfe_api dpfe_api_v3 = {
2868c2ecf20Sopenharmony_ci	.version = 3,
2878c2ecf20Sopenharmony_ci	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
2888c2ecf20Sopenharmony_ci	.sysfs_attrs = dpfe_v3_groups,
2898c2ecf20Sopenharmony_ci	.command = {
2908c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_INFO] = {
2918c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2928c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 0x0101,
2938c2ecf20Sopenharmony_ci			[MSG_ARG_COUNT] = 1,
2948c2ecf20Sopenharmony_ci			[MSG_ARG0] = 1,
2958c2ecf20Sopenharmony_ci		},
2968c2ecf20Sopenharmony_ci		[DPFE_CMD_GET_REFRESH] = {
2978c2ecf20Sopenharmony_ci			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
2988c2ecf20Sopenharmony_ci			[MSG_COMMAND] = 0x0202,
2998c2ecf20Sopenharmony_ci			[MSG_ARG_COUNT] = 0,
3008c2ecf20Sopenharmony_ci		},
3018c2ecf20Sopenharmony_ci		/* There's no GET_VENDOR command in API v3. */
3028c2ecf20Sopenharmony_ci	},
3038c2ecf20Sopenharmony_ci};
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic const char *get_error_text(unsigned int i)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	static const char * const error_text[] = {
3088c2ecf20Sopenharmony_ci		"Success", "Header code incorrect",
3098c2ecf20Sopenharmony_ci		"Unknown command or argument", "Incorrect checksum",
3108c2ecf20Sopenharmony_ci		"Malformed command", "Timed out", "Unknown error",
3118c2ecf20Sopenharmony_ci	};
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (unlikely(i >= ARRAY_SIZE(error_text)))
3148c2ecf20Sopenharmony_ci		i = ARRAY_SIZE(error_text) - 1;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return error_text[i];
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	u32 val;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
3248c2ecf20Sopenharmony_ci	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
3258c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return !(val & DCPU_RESET_MASK);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void __disable_dcpu(struct brcmstb_dpfe_priv *priv)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 val;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!is_dcpu_enabled(priv))
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* Put DCPU in reset if it's running. */
3408c2ecf20Sopenharmony_ci	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
3418c2ecf20Sopenharmony_ci	val |= (1 << DCPU_RESET_SHIFT);
3428c2ecf20Sopenharmony_ci	writel_relaxed(val, priv->regs + REG_DCPU_RESET);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void __enable_dcpu(struct brcmstb_dpfe_priv *priv)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs;
3508c2ecf20Sopenharmony_ci	u32 val;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* Clear mailbox registers. */
3558c2ecf20Sopenharmony_ci	writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
3568c2ecf20Sopenharmony_ci	writel_relaxed(0, regs + REG_TO_HOST_MBOX);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Disable DCPU clock gating */
3598c2ecf20Sopenharmony_ci	val = readl_relaxed(regs + REG_DCPU_RESET);
3608c2ecf20Sopenharmony_ci	val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
3618c2ecf20Sopenharmony_ci	writel_relaxed(val, regs + REG_DCPU_RESET);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Take DCPU out of reset */
3648c2ecf20Sopenharmony_ci	val = readl_relaxed(regs + REG_DCPU_RESET);
3658c2ecf20Sopenharmony_ci	val &= ~(1 << DCPU_RESET_SHIFT);
3668c2ecf20Sopenharmony_ci	writel_relaxed(val, regs + REG_DCPU_RESET);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic unsigned int get_msg_chksum(const u32 msg[], unsigned int max)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	unsigned int sum = 0;
3748c2ecf20Sopenharmony_ci	unsigned int i;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Don't include the last field in the checksum. */
3778c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++)
3788c2ecf20Sopenharmony_ci		sum += msg[i];
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return sum;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
3848c2ecf20Sopenharmony_ci				 char *buf, ssize_t *size)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	unsigned int msg_type;
3878c2ecf20Sopenharmony_ci	unsigned int offset;
3888c2ecf20Sopenharmony_ci	void __iomem *ptr = NULL;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* There is no need to use this function for API v3 or later. */
3918c2ecf20Sopenharmony_ci	if (unlikely(priv->dpfe_api->version >= 3))
3928c2ecf20Sopenharmony_ci		return NULL;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
3958c2ecf20Sopenharmony_ci	offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/*
3988c2ecf20Sopenharmony_ci	 * msg_type == 1: the offset is relative to the message RAM
3998c2ecf20Sopenharmony_ci	 * msg_type == 0: the offset is relative to the data RAM (this is the
4008c2ecf20Sopenharmony_ci	 *                previous way of passing data)
4018c2ecf20Sopenharmony_ci	 * msg_type is anything else: there's critical hardware problem
4028c2ecf20Sopenharmony_ci	 */
4038c2ecf20Sopenharmony_ci	switch (msg_type) {
4048c2ecf20Sopenharmony_ci	case 1:
4058c2ecf20Sopenharmony_ci		ptr = priv->regs + DCPU_MSG_RAM_START + offset;
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci	case 0:
4088c2ecf20Sopenharmony_ci		ptr = priv->dmem + offset;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	default:
4118c2ecf20Sopenharmony_ci		dev_emerg(priv->dev, "invalid message reply from DCPU: %#x\n",
4128c2ecf20Sopenharmony_ci			response);
4138c2ecf20Sopenharmony_ci		if (buf && size)
4148c2ecf20Sopenharmony_ci			*size = sprintf(buf,
4158c2ecf20Sopenharmony_ci				"FATAL: communication error with DCPU\n");
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return ptr;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic void __finalize_command(struct brcmstb_dpfe_priv *priv)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	unsigned int release_mbox;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/*
4268c2ecf20Sopenharmony_ci	 * It depends on the API version which MBOX register we have to write to
4278c2ecf20Sopenharmony_ci	 * to signal we are done.
4288c2ecf20Sopenharmony_ci	 */
4298c2ecf20Sopenharmony_ci	release_mbox = (priv->dpfe_api->version < 2)
4308c2ecf20Sopenharmony_ci			? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX;
4318c2ecf20Sopenharmony_ci	writel_relaxed(0, priv->regs + release_mbox);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd,
4358c2ecf20Sopenharmony_ci			  u32 result[])
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs;
4388c2ecf20Sopenharmony_ci	unsigned int i, chksum, chksum_idx;
4398c2ecf20Sopenharmony_ci	const u32 *msg;
4408c2ecf20Sopenharmony_ci	int ret = 0;
4418c2ecf20Sopenharmony_ci	u32 resp;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (cmd >= DPFE_CMD_MAX)
4448c2ecf20Sopenharmony_ci		return -1;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	msg = priv->dpfe_api->command[cmd];
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* Wait for DCPU to become ready */
4518c2ecf20Sopenharmony_ci	for (i = 0; i < DELAY_LOOP_MAX; i++) {
4528c2ecf20Sopenharmony_ci		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
4538c2ecf20Sopenharmony_ci		if (resp == 0)
4548c2ecf20Sopenharmony_ci			break;
4558c2ecf20Sopenharmony_ci		msleep(1);
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	if (resp != 0) {
4588c2ecf20Sopenharmony_ci		mutex_unlock(&priv->lock);
4598c2ecf20Sopenharmony_ci		return -ffs(DCPU_RET_ERR_TIMEDOUT);
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* Compute checksum over the message */
4638c2ecf20Sopenharmony_ci	chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
4648c2ecf20Sopenharmony_ci	chksum = get_msg_chksum(msg, chksum_idx);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* Write command and arguments to message area */
4678c2ecf20Sopenharmony_ci	for (i = 0; i < MSG_FIELD_MAX; i++) {
4688c2ecf20Sopenharmony_ci		if (i == chksum_idx)
4698c2ecf20Sopenharmony_ci			writel_relaxed(chksum, regs + DCPU_MSG_RAM(i));
4708c2ecf20Sopenharmony_ci		else
4718c2ecf20Sopenharmony_ci			writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Tell DCPU there is a command waiting */
4758c2ecf20Sopenharmony_ci	writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* Wait for DCPU to process the command */
4788c2ecf20Sopenharmony_ci	for (i = 0; i < DELAY_LOOP_MAX; i++) {
4798c2ecf20Sopenharmony_ci		/* Read response code */
4808c2ecf20Sopenharmony_ci		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
4818c2ecf20Sopenharmony_ci		if (resp > 0)
4828c2ecf20Sopenharmony_ci			break;
4838c2ecf20Sopenharmony_ci		msleep(1);
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (i == DELAY_LOOP_MAX) {
4878c2ecf20Sopenharmony_ci		resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
4888c2ecf20Sopenharmony_ci		ret = -ffs(resp);
4898c2ecf20Sopenharmony_ci	} else {
4908c2ecf20Sopenharmony_ci		/* Read response data */
4918c2ecf20Sopenharmony_ci		for (i = 0; i < MSG_FIELD_MAX; i++)
4928c2ecf20Sopenharmony_ci			result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
4938c2ecf20Sopenharmony_ci		chksum_idx = result[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Tell DCPU we are done */
4978c2ecf20Sopenharmony_ci	__finalize_command(priv);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (ret)
5028c2ecf20Sopenharmony_ci		return ret;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Verify response */
5058c2ecf20Sopenharmony_ci	chksum = get_msg_chksum(result, chksum_idx);
5068c2ecf20Sopenharmony_ci	if (chksum != result[chksum_idx])
5078c2ecf20Sopenharmony_ci		resp = DCPU_RET_ERR_CHKSUM;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (resp != DCPU_RET_SUCCESS) {
5108c2ecf20Sopenharmony_ci		resp &= ~DCPU_RET_ERROR_BIT;
5118c2ecf20Sopenharmony_ci		ret = -ffs(resp);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return ret;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci/* Ensure that the firmware file loaded meets all the requirements. */
5188c2ecf20Sopenharmony_cistatic int __verify_firmware(struct init_data *init,
5198c2ecf20Sopenharmony_ci			     const struct firmware *fw)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	const struct dpfe_firmware_header *header = (void *)fw->data;
5228c2ecf20Sopenharmony_ci	unsigned int dmem_size, imem_size, total_size;
5238c2ecf20Sopenharmony_ci	bool is_big_endian = false;
5248c2ecf20Sopenharmony_ci	const u32 *chksum_ptr;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (header->magic == DPFE_BE_MAGIC)
5278c2ecf20Sopenharmony_ci		is_big_endian = true;
5288c2ecf20Sopenharmony_ci	else if (header->magic != DPFE_LE_MAGIC)
5298c2ecf20Sopenharmony_ci		return ERR_INVALID_MAGIC;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (is_big_endian) {
5328c2ecf20Sopenharmony_ci		dmem_size = be32_to_cpu(header->dmem_size);
5338c2ecf20Sopenharmony_ci		imem_size = be32_to_cpu(header->imem_size);
5348c2ecf20Sopenharmony_ci	} else {
5358c2ecf20Sopenharmony_ci		dmem_size = le32_to_cpu(header->dmem_size);
5368c2ecf20Sopenharmony_ci		imem_size = le32_to_cpu(header->imem_size);
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	/* Data and instruction sections are 32 bit words. */
5408c2ecf20Sopenharmony_ci	if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
5418c2ecf20Sopenharmony_ci		return ERR_INVALID_SIZE;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/*
5448c2ecf20Sopenharmony_ci	 * The header + the data section + the instruction section + the
5458c2ecf20Sopenharmony_ci	 * checksum must be equal to the total firmware size.
5468c2ecf20Sopenharmony_ci	 */
5478c2ecf20Sopenharmony_ci	total_size = dmem_size + imem_size + sizeof(*header) +
5488c2ecf20Sopenharmony_ci		sizeof(*chksum_ptr);
5498c2ecf20Sopenharmony_ci	if (total_size != fw->size)
5508c2ecf20Sopenharmony_ci		return ERR_INVALID_SIZE;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* The checksum comes at the very end. */
5538c2ecf20Sopenharmony_ci	chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	init->is_big_endian = is_big_endian;
5568c2ecf20Sopenharmony_ci	init->dmem_len = dmem_size;
5578c2ecf20Sopenharmony_ci	init->imem_len = imem_size;
5588c2ecf20Sopenharmony_ci	init->chksum = (is_big_endian)
5598c2ecf20Sopenharmony_ci		? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return 0;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci/* Verify checksum by reading back the firmware from co-processor RAM. */
5658c2ecf20Sopenharmony_cistatic int __verify_fw_checksum(struct init_data *init,
5668c2ecf20Sopenharmony_ci				struct brcmstb_dpfe_priv *priv,
5678c2ecf20Sopenharmony_ci				const struct dpfe_firmware_header *header,
5688c2ecf20Sopenharmony_ci				u32 checksum)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	u32 magic, sequence, version, sum;
5718c2ecf20Sopenharmony_ci	u32 __iomem *dmem = priv->dmem;
5728c2ecf20Sopenharmony_ci	u32 __iomem *imem = priv->imem;
5738c2ecf20Sopenharmony_ci	unsigned int i;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (init->is_big_endian) {
5768c2ecf20Sopenharmony_ci		magic = be32_to_cpu(header->magic);
5778c2ecf20Sopenharmony_ci		sequence = be32_to_cpu(header->sequence);
5788c2ecf20Sopenharmony_ci		version = be32_to_cpu(header->version);
5798c2ecf20Sopenharmony_ci	} else {
5808c2ecf20Sopenharmony_ci		magic = le32_to_cpu(header->magic);
5818c2ecf20Sopenharmony_ci		sequence = le32_to_cpu(header->sequence);
5828c2ecf20Sopenharmony_ci		version = le32_to_cpu(header->version);
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	sum = magic + sequence + version + init->dmem_len + init->imem_len;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	for (i = 0; i < init->dmem_len / sizeof(u32); i++)
5888c2ecf20Sopenharmony_ci		sum += readl_relaxed(dmem + i);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	for (i = 0; i < init->imem_len / sizeof(u32); i++)
5918c2ecf20Sopenharmony_ci		sum += readl_relaxed(imem + i);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return (sum == checksum) ? 0 : -1;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int __write_firmware(u32 __iomem *mem, const u32 *fw,
5978c2ecf20Sopenharmony_ci			    unsigned int size, bool is_big_endian)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	unsigned int i;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Convert size to 32-bit words. */
6028c2ecf20Sopenharmony_ci	size /= sizeof(u32);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* It is recommended to clear the firmware area first. */
6058c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++)
6068c2ecf20Sopenharmony_ci		writel_relaxed(0, mem + i);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* Now copy it. */
6098c2ecf20Sopenharmony_ci	if (is_big_endian) {
6108c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++)
6118c2ecf20Sopenharmony_ci			writel_relaxed(be32_to_cpu(fw[i]), mem + i);
6128c2ecf20Sopenharmony_ci	} else {
6138c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++)
6148c2ecf20Sopenharmony_ci			writel_relaxed(le32_to_cpu(fw[i]), mem + i);
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return 0;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	const struct dpfe_firmware_header *header;
6238c2ecf20Sopenharmony_ci	unsigned int dmem_size, imem_size;
6248c2ecf20Sopenharmony_ci	struct device *dev = priv->dev;
6258c2ecf20Sopenharmony_ci	bool is_big_endian = false;
6268c2ecf20Sopenharmony_ci	const struct firmware *fw;
6278c2ecf20Sopenharmony_ci	const u32 *dmem, *imem;
6288c2ecf20Sopenharmony_ci	struct init_data init;
6298c2ecf20Sopenharmony_ci	const void *fw_blob;
6308c2ecf20Sopenharmony_ci	int ret;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/*
6338c2ecf20Sopenharmony_ci	 * Skip downloading the firmware if the DCPU is already running and
6348c2ecf20Sopenharmony_ci	 * responding to commands.
6358c2ecf20Sopenharmony_ci	 */
6368c2ecf20Sopenharmony_ci	if (is_dcpu_enabled(priv)) {
6378c2ecf20Sopenharmony_ci		u32 response[MSG_FIELD_MAX];
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
6408c2ecf20Sopenharmony_ci		if (!ret)
6418c2ecf20Sopenharmony_ci			return 0;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/*
6458c2ecf20Sopenharmony_ci	 * If the firmware filename is NULL it means the boot firmware has to
6468c2ecf20Sopenharmony_ci	 * download the DCPU firmware for us. If that didn't work, we have to
6478c2ecf20Sopenharmony_ci	 * bail, since downloading it ourselves wouldn't work either.
6488c2ecf20Sopenharmony_ci	 */
6498c2ecf20Sopenharmony_ci	if (!priv->dpfe_api->fw_name)
6508c2ecf20Sopenharmony_ci		return -ENODEV;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev);
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * Defer the firmware download if the firmware file couldn't be found.
6558c2ecf20Sopenharmony_ci	 * The root file system may not be available yet.
6568c2ecf20Sopenharmony_ci	 */
6578c2ecf20Sopenharmony_ci	if (ret)
6588c2ecf20Sopenharmony_ci		return (ret == -ENOENT) ? -EPROBE_DEFER : ret;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	ret = __verify_firmware(&init, fw);
6618c2ecf20Sopenharmony_ci	if (ret) {
6628c2ecf20Sopenharmony_ci		ret = -EFAULT;
6638c2ecf20Sopenharmony_ci		goto release_fw;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	__disable_dcpu(priv);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	is_big_endian = init.is_big_endian;
6698c2ecf20Sopenharmony_ci	dmem_size = init.dmem_len;
6708c2ecf20Sopenharmony_ci	imem_size = init.imem_len;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* At the beginning of the firmware blob is a header. */
6738c2ecf20Sopenharmony_ci	header = (struct dpfe_firmware_header *)fw->data;
6748c2ecf20Sopenharmony_ci	/* Void pointer to the beginning of the actual firmware. */
6758c2ecf20Sopenharmony_ci	fw_blob = fw->data + sizeof(*header);
6768c2ecf20Sopenharmony_ci	/* IMEM comes right after the header. */
6778c2ecf20Sopenharmony_ci	imem = fw_blob;
6788c2ecf20Sopenharmony_ci	/* DMEM follows after IMEM. */
6798c2ecf20Sopenharmony_ci	dmem = fw_blob + imem_size;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
6828c2ecf20Sopenharmony_ci	if (ret)
6838c2ecf20Sopenharmony_ci		goto release_fw;
6848c2ecf20Sopenharmony_ci	ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
6858c2ecf20Sopenharmony_ci	if (ret)
6868c2ecf20Sopenharmony_ci		goto release_fw;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	ret = __verify_fw_checksum(&init, priv, header, init.chksum);
6898c2ecf20Sopenharmony_ci	if (ret)
6908c2ecf20Sopenharmony_ci		goto release_fw;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	__enable_dcpu(priv);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cirelease_fw:
6958c2ecf20Sopenharmony_ci	release_firmware(fw);
6968c2ecf20Sopenharmony_ci	return ret;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic ssize_t generic_show(unsigned int command, u32 response[],
7008c2ecf20Sopenharmony_ci			    struct brcmstb_dpfe_priv *priv, char *buf)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	int ret;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (!priv)
7058c2ecf20Sopenharmony_ci		return sprintf(buf, "ERROR: driver private data not set\n");
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	ret = __send_command(priv, command, response);
7088c2ecf20Sopenharmony_ci	if (ret < 0)
7098c2ecf20Sopenharmony_ci		return sprintf(buf, "ERROR: %s\n", get_error_text(-ret));
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic ssize_t show_info(struct device *dev, struct device_attribute *devattr,
7158c2ecf20Sopenharmony_ci			 char *buf)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	u32 response[MSG_FIELD_MAX];
7188c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
7198c2ecf20Sopenharmony_ci	unsigned int info;
7208c2ecf20Sopenharmony_ci	ssize_t ret;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(dev);
7238c2ecf20Sopenharmony_ci	ret = generic_show(DPFE_CMD_GET_INFO, response, priv, buf);
7248c2ecf20Sopenharmony_ci	if (ret)
7258c2ecf20Sopenharmony_ci		return ret;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	info = response[MSG_ARG0];
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	return sprintf(buf, "%u.%u.%u.%u\n",
7308c2ecf20Sopenharmony_ci		       (info >> 24) & 0xff,
7318c2ecf20Sopenharmony_ci		       (info >> 16) & 0xff,
7328c2ecf20Sopenharmony_ci		       (info >> 8) & 0xff,
7338c2ecf20Sopenharmony_ci		       info & 0xff);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic ssize_t show_refresh(struct device *dev,
7378c2ecf20Sopenharmony_ci			    struct device_attribute *devattr, char *buf)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	u32 response[MSG_FIELD_MAX];
7408c2ecf20Sopenharmony_ci	void __iomem *info;
7418c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
7428c2ecf20Sopenharmony_ci	u8 refresh, sr_abort, ppre, thermal_offs, tuf;
7438c2ecf20Sopenharmony_ci	u32 mr4;
7448c2ecf20Sopenharmony_ci	ssize_t ret;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(dev);
7478c2ecf20Sopenharmony_ci	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
7488c2ecf20Sopenharmony_ci	if (ret)
7498c2ecf20Sopenharmony_ci		return ret;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
7528c2ecf20Sopenharmony_ci	if (!info)
7538c2ecf20Sopenharmony_ci		return ret;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	mr4 = (readl_relaxed(info + DRAM_INFO_MR4) >> DRAM_INFO_MR4_SHIFT) &
7568c2ecf20Sopenharmony_ci	       DRAM_INFO_MR4_MASK;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
7598c2ecf20Sopenharmony_ci	sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
7608c2ecf20Sopenharmony_ci	ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
7618c2ecf20Sopenharmony_ci	thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
7628c2ecf20Sopenharmony_ci	tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
7658c2ecf20Sopenharmony_ci		       readl_relaxed(info + DRAM_INFO_INTERVAL),
7668c2ecf20Sopenharmony_ci		       refresh, sr_abort, ppre, thermal_offs, tuf,
7678c2ecf20Sopenharmony_ci		       readl_relaxed(info + DRAM_INFO_ERROR));
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
7718c2ecf20Sopenharmony_ci			  const char *buf, size_t count)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	u32 response[MSG_FIELD_MAX];
7748c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
7758c2ecf20Sopenharmony_ci	void __iomem *info;
7768c2ecf20Sopenharmony_ci	unsigned long val;
7778c2ecf20Sopenharmony_ci	int ret;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &val) < 0)
7808c2ecf20Sopenharmony_ci		return -EINVAL;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(dev);
7838c2ecf20Sopenharmony_ci	ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
7848c2ecf20Sopenharmony_ci	if (ret)
7858c2ecf20Sopenharmony_ci		return ret;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	info = get_msg_ptr(priv, response[MSG_ARG0], NULL, NULL);
7888c2ecf20Sopenharmony_ci	if (!info)
7898c2ecf20Sopenharmony_ci		return -EIO;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	writel_relaxed(val, info + DRAM_INFO_INTERVAL);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	return count;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
7978c2ecf20Sopenharmony_ci			   char *buf)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	u32 response[MSG_FIELD_MAX];
8008c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
8018c2ecf20Sopenharmony_ci	void __iomem *info;
8028c2ecf20Sopenharmony_ci	ssize_t ret;
8038c2ecf20Sopenharmony_ci	u32 mr5, mr6, mr7, mr8, err;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(dev);
8068c2ecf20Sopenharmony_ci	ret = generic_show(DPFE_CMD_GET_VENDOR, response, priv, buf);
8078c2ecf20Sopenharmony_ci	if (ret)
8088c2ecf20Sopenharmony_ci		return ret;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
8118c2ecf20Sopenharmony_ci	if (!info)
8128c2ecf20Sopenharmony_ci		return ret;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	mr5 = (readl_relaxed(info + DRAM_VENDOR_MR5) >> DRAM_VENDOR_SHIFT) &
8158c2ecf20Sopenharmony_ci		DRAM_VENDOR_MASK;
8168c2ecf20Sopenharmony_ci	mr6 = (readl_relaxed(info + DRAM_VENDOR_MR6) >> DRAM_VENDOR_SHIFT) &
8178c2ecf20Sopenharmony_ci		DRAM_VENDOR_MASK;
8188c2ecf20Sopenharmony_ci	mr7 = (readl_relaxed(info + DRAM_VENDOR_MR7) >> DRAM_VENDOR_SHIFT) &
8198c2ecf20Sopenharmony_ci		DRAM_VENDOR_MASK;
8208c2ecf20Sopenharmony_ci	mr8 = (readl_relaxed(info + DRAM_VENDOR_MR8) >> DRAM_VENDOR_SHIFT) &
8218c2ecf20Sopenharmony_ci		DRAM_VENDOR_MASK;
8228c2ecf20Sopenharmony_ci	err = readl_relaxed(info + DRAM_VENDOR_ERROR) & DRAM_VENDOR_MASK;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	return sprintf(buf, "%#x %#x %#x %#x %#x\n", mr5, mr6, mr7, mr8, err);
8258c2ecf20Sopenharmony_ci}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_cistatic ssize_t show_dram(struct device *dev, struct device_attribute *devattr,
8288c2ecf20Sopenharmony_ci			 char *buf)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	u32 response[MSG_FIELD_MAX];
8318c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
8328c2ecf20Sopenharmony_ci	ssize_t ret;
8338c2ecf20Sopenharmony_ci	u32 mr4, mr5, mr6, mr7, mr8, err;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(dev);
8368c2ecf20Sopenharmony_ci	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
8378c2ecf20Sopenharmony_ci	if (ret)
8388c2ecf20Sopenharmony_ci		return ret;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	mr4 = response[MSG_ARG0 + 0] & DRAM_INFO_MR4_MASK;
8418c2ecf20Sopenharmony_ci	mr5 = response[MSG_ARG0 + 1] & DRAM_DDR_INFO_MASK;
8428c2ecf20Sopenharmony_ci	mr6 = response[MSG_ARG0 + 2] & DRAM_DDR_INFO_MASK;
8438c2ecf20Sopenharmony_ci	mr7 = response[MSG_ARG0 + 3] & DRAM_DDR_INFO_MASK;
8448c2ecf20Sopenharmony_ci	mr8 = response[MSG_ARG0 + 4] & DRAM_DDR_INFO_MASK;
8458c2ecf20Sopenharmony_ci	err = response[MSG_ARG0 + 5] & DRAM_DDR_INFO_MASK;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	return sprintf(buf, "%#x %#x %#x %#x %#x %#x\n", mr4, mr5, mr6, mr7,
8488c2ecf20Sopenharmony_ci			mr8, err);
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic int brcmstb_dpfe_resume(struct platform_device *pdev)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return brcmstb_dpfe_download_firmware(priv);
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic int brcmstb_dpfe_probe(struct platform_device *pdev)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
8618c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv;
8628c2ecf20Sopenharmony_ci	struct resource *res;
8638c2ecf20Sopenharmony_ci	int ret;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
8668c2ecf20Sopenharmony_ci	if (!priv)
8678c2ecf20Sopenharmony_ci		return -ENOMEM;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	priv->dev = dev;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	mutex_init(&priv->lock);
8728c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
8758c2ecf20Sopenharmony_ci	priv->regs = devm_ioremap_resource(dev, res);
8768c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regs)) {
8778c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't map DCPU registers\n");
8788c2ecf20Sopenharmony_ci		return -ENODEV;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
8828c2ecf20Sopenharmony_ci	priv->dmem = devm_ioremap_resource(dev, res);
8838c2ecf20Sopenharmony_ci	if (IS_ERR(priv->dmem)) {
8848c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't map DCPU data memory\n");
8858c2ecf20Sopenharmony_ci		return -ENOENT;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
8898c2ecf20Sopenharmony_ci	priv->imem = devm_ioremap_resource(dev, res);
8908c2ecf20Sopenharmony_ci	if (IS_ERR(priv->imem)) {
8918c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't map DCPU instruction memory\n");
8928c2ecf20Sopenharmony_ci		return -ENOENT;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	priv->dpfe_api = of_device_get_match_data(dev);
8968c2ecf20Sopenharmony_ci	if (unlikely(!priv->dpfe_api)) {
8978c2ecf20Sopenharmony_ci		/*
8988c2ecf20Sopenharmony_ci		 * It should be impossible to end up here, but to be safe we
8998c2ecf20Sopenharmony_ci		 * check anyway.
9008c2ecf20Sopenharmony_ci		 */
9018c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't determine API\n");
9028c2ecf20Sopenharmony_ci		return -ENOENT;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	ret = brcmstb_dpfe_download_firmware(priv);
9068c2ecf20Sopenharmony_ci	if (ret)
9078c2ecf20Sopenharmony_ci		return dev_err_probe(dev, ret, "Couldn't download firmware\n");
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	ret = sysfs_create_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
9108c2ecf20Sopenharmony_ci	if (!ret)
9118c2ecf20Sopenharmony_ci		dev_info(dev, "registered with API v%d.\n",
9128c2ecf20Sopenharmony_ci			 priv->dpfe_api->version);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return ret;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic int brcmstb_dpfe_remove(struct platform_device *pdev)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	return 0;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_cistatic const struct of_device_id brcmstb_dpfe_of_match[] = {
9278c2ecf20Sopenharmony_ci	/* Use legacy API v2 for a select number of chips */
9288c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 },
9298c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 },
9308c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 },
9318c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 },
9328c2ecf20Sopenharmony_ci	/* API v3 is the default going forward */
9338c2ecf20Sopenharmony_ci	{ .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 },
9348c2ecf20Sopenharmony_ci	{}
9358c2ecf20Sopenharmony_ci};
9368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_cistatic struct platform_driver brcmstb_dpfe_driver = {
9398c2ecf20Sopenharmony_ci	.driver	= {
9408c2ecf20Sopenharmony_ci		.name = DRVNAME,
9418c2ecf20Sopenharmony_ci		.of_match_table = brcmstb_dpfe_of_match,
9428c2ecf20Sopenharmony_ci	},
9438c2ecf20Sopenharmony_ci	.probe = brcmstb_dpfe_probe,
9448c2ecf20Sopenharmony_ci	.remove	= brcmstb_dpfe_remove,
9458c2ecf20Sopenharmony_ci	.resume = brcmstb_dpfe_resume,
9468c2ecf20Sopenharmony_ci};
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cimodule_platform_driver(brcmstb_dpfe_driver);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
9518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
9528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
953