From 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 22 Oct 2024 10:36:11 +0000
Subject: [PATCH] 修改4g拨号为QMI,需要在系统里后台执行quectel-CM

---
 kernel/drivers/memory/brcmstb_dpfe.c |  472 ++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 339 insertions(+), 133 deletions(-)

diff --git a/kernel/drivers/memory/brcmstb_dpfe.c b/kernel/drivers/memory/brcmstb_dpfe.c
index 04599ec..2daae2e 100644
--- a/kernel/drivers/memory/brcmstb_dpfe.c
+++ b/kernel/drivers/memory/brcmstb_dpfe.c
@@ -1,10 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
  *
  * Copyright (c) 2017 Broadcom
- *
- * Released under the GPLv2 only.
- * SPDX-License-Identifier: GPL-2.0
  */
 
 /*
@@ -25,7 +23,7 @@
  *     - BE kernel + LE firmware image
  *     - BE kernel + BE firmware image
  *
- * The DPCU always runs in big endian mode. The firwmare image, however, can
+ * The DPCU always runs in big endian mode. The firmware image, however, can
  * be in either format. Also, communication between host CPU and DCPU is
  * always in little endian.
  */
@@ -35,10 +33,10 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 
 #define DRVNAME			"brcmstb-dpfe"
-#define FIRMWARE_NAME		"dpfe.bin"
 
 /* DCPU register offsets */
 #define REG_DCPU_RESET		0x0
@@ -61,6 +59,7 @@
 #define DRAM_INFO_MR4		0x4
 #define DRAM_INFO_ERROR		0x8
 #define DRAM_INFO_MR4_MASK	0xff
+#define DRAM_INFO_MR4_SHIFT	24	/* We need to look at byte 3 */
 
 /* DRAM MR4 Offsets & Masks */
 #define DRAM_MR4_REFRESH	0x0	/* Refresh rate */
@@ -75,13 +74,23 @@
 #define DRAM_MR4_TH_OFFS_MASK	0x3
 #define DRAM_MR4_TUF_MASK	0x1
 
-/* DRAM Vendor Offsets & Masks */
+/* DRAM Vendor Offsets & Masks (API v2) */
 #define DRAM_VENDOR_MR5		0x0
 #define DRAM_VENDOR_MR6		0x4
 #define DRAM_VENDOR_MR7		0x8
 #define DRAM_VENDOR_MR8		0xc
 #define DRAM_VENDOR_ERROR	0x10
 #define DRAM_VENDOR_MASK	0xff
+#define DRAM_VENDOR_SHIFT	24	/* We need to look at byte 3 */
+
+/* DRAM Information Offsets & Masks (API v3) */
+#define DRAM_DDR_INFO_MR4	0x0
+#define DRAM_DDR_INFO_MR5	0x4
+#define DRAM_DDR_INFO_MR6	0x8
+#define DRAM_DDR_INFO_MR7	0xc
+#define DRAM_DDR_INFO_MR8	0x10
+#define DRAM_DDR_INFO_ERROR	0x14
+#define DRAM_DDR_INFO_MASK	0xff
 
 /* Reset register bits & masks */
 #define DCPU_RESET_SHIFT	0x0
@@ -111,15 +120,14 @@
 #define DPFE_MSG_TYPE_COMMAND	1
 #define DPFE_MSG_TYPE_RESPONSE	2
 
-#define DELAY_LOOP_MAX		200000
+#define DELAY_LOOP_MAX		1000
 
 enum dpfe_msg_fields {
 	MSG_HEADER,
 	MSG_COMMAND,
 	MSG_ARG_COUNT,
 	MSG_ARG0,
-	MSG_CHKSUM,
-	MSG_FIELD_MAX /* Last entry */
+	MSG_FIELD_MAX	= 16 /* Max number of arguments */
 };
 
 enum dpfe_commands {
@@ -127,14 +135,6 @@
 	DPFE_CMD_GET_REFRESH,
 	DPFE_CMD_GET_VENDOR,
 	DPFE_CMD_MAX /* Last entry */
-};
-
-struct dpfe_msg {
-	u32 header;
-	u32 command;
-	u32 arg_count;
-	u32 arg0;
-	u32 chksum; /* This is the sum of all other entries. */
 };
 
 /*
@@ -170,70 +170,186 @@
 	bool is_big_endian;
 };
 
+/* API version and corresponding commands */
+struct dpfe_api {
+	int version;
+	const char *fw_name;
+	const struct attribute_group **sysfs_attrs;
+	u32 command[DPFE_CMD_MAX][MSG_FIELD_MAX];
+};
+
 /* Things we need for as long as we are active. */
-struct private_data {
+struct brcmstb_dpfe_priv {
 	void __iomem *regs;
 	void __iomem *dmem;
 	void __iomem *imem;
 	struct device *dev;
+	const struct dpfe_api *dpfe_api;
 	struct mutex lock;
 };
 
-static const char *error_text[] = {
-	"Success", "Header code incorrect", "Unknown command or argument",
-	"Incorrect checksum", "Malformed command", "Timed out",
+/*
+ * Forward declaration of our sysfs attribute functions, so we can declare the
+ * attribute data structures early.
+ */
+static ssize_t show_info(struct device *, struct device_attribute *, char *);
+static ssize_t show_refresh(struct device *, struct device_attribute *, char *);
+static ssize_t store_refresh(struct device *, struct device_attribute *,
+			  const char *, size_t);
+static ssize_t show_vendor(struct device *, struct device_attribute *, char *);
+static ssize_t show_dram(struct device *, struct device_attribute *, char *);
+
+/*
+ * Declare our attributes early, so they can be referenced in the API data
+ * structure. We need to do this, because the attributes depend on the API
+ * version.
+ */
+static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
+static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
+static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
+static DEVICE_ATTR(dpfe_dram, 0444, show_dram, NULL);
+
+/* API v2 sysfs attributes */
+static struct attribute *dpfe_v2_attrs[] = {
+	&dev_attr_dpfe_info.attr,
+	&dev_attr_dpfe_refresh.attr,
+	&dev_attr_dpfe_vendor.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(dpfe_v2);
+
+/* API v3 sysfs attributes */
+static struct attribute *dpfe_v3_attrs[] = {
+	&dev_attr_dpfe_info.attr,
+	&dev_attr_dpfe_dram.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(dpfe_v3);
+
+/*
+ * Old API v2 firmware commands, as defined in the rev 0.61 specification, we
+ * use a version set to 1 to denote that it is not compatible with the new API
+ * v2 and onwards.
+ */
+static const struct dpfe_api dpfe_api_old_v2 = {
+	.version = 1,
+	.fw_name = "dpfe.bin",
+	.sysfs_attrs = dpfe_v2_groups,
+	.command = {
+		[DPFE_CMD_GET_INFO] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 1,
+			[MSG_ARG_COUNT] = 1,
+			[MSG_ARG0] = 1,
+		},
+		[DPFE_CMD_GET_REFRESH] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 2,
+			[MSG_ARG_COUNT] = 1,
+			[MSG_ARG0] = 1,
+		},
+		[DPFE_CMD_GET_VENDOR] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 2,
+			[MSG_ARG_COUNT] = 1,
+			[MSG_ARG0] = 2,
+		},
+	}
 };
 
-/* List of supported firmware commands */
-static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = {
-	[DPFE_CMD_GET_INFO] = {
-		[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
-		[MSG_COMMAND] = 1,
-		[MSG_ARG_COUNT] = 1,
-		[MSG_ARG0] = 1,
-		[MSG_CHKSUM] = 4,
-	},
-	[DPFE_CMD_GET_REFRESH] = {
-		[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
-		[MSG_COMMAND] = 2,
-		[MSG_ARG_COUNT] = 1,
-		[MSG_ARG0] = 1,
-		[MSG_CHKSUM] = 5,
-	},
-	[DPFE_CMD_GET_VENDOR] = {
-		[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
-		[MSG_COMMAND] = 2,
-		[MSG_ARG_COUNT] = 1,
-		[MSG_ARG0] = 2,
-		[MSG_CHKSUM] = 6,
+/*
+ * API v2 firmware commands, as defined in the rev 0.8 specification, named new
+ * v2 here
+ */
+static const struct dpfe_api dpfe_api_new_v2 = {
+	.version = 2,
+	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
+	.sysfs_attrs = dpfe_v2_groups,
+	.command = {
+		[DPFE_CMD_GET_INFO] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 0x101,
+		},
+		[DPFE_CMD_GET_REFRESH] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 0x201,
+		},
+		[DPFE_CMD_GET_VENDOR] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 0x202,
+		},
+	}
+};
+
+/* API v3 firmware commands */
+static const struct dpfe_api dpfe_api_v3 = {
+	.version = 3,
+	.fw_name = NULL, /* We expect the firmware to have been downloaded! */
+	.sysfs_attrs = dpfe_v3_groups,
+	.command = {
+		[DPFE_CMD_GET_INFO] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 0x0101,
+			[MSG_ARG_COUNT] = 1,
+			[MSG_ARG0] = 1,
+		},
+		[DPFE_CMD_GET_REFRESH] = {
+			[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
+			[MSG_COMMAND] = 0x0202,
+			[MSG_ARG_COUNT] = 0,
+		},
+		/* There's no GET_VENDOR command in API v3. */
 	},
 };
 
-static bool is_dcpu_enabled(void __iomem *regs)
+static const char *get_error_text(unsigned int i)
+{
+	static const char * const error_text[] = {
+		"Success", "Header code incorrect",
+		"Unknown command or argument", "Incorrect checksum",
+		"Malformed command", "Timed out", "Unknown error",
+	};
+
+	if (unlikely(i >= ARRAY_SIZE(error_text)))
+		i = ARRAY_SIZE(error_text) - 1;
+
+	return error_text[i];
+}
+
+static bool is_dcpu_enabled(struct brcmstb_dpfe_priv *priv)
 {
 	u32 val;
 
-	val = readl_relaxed(regs + REG_DCPU_RESET);
+	mutex_lock(&priv->lock);
+	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
+	mutex_unlock(&priv->lock);
 
 	return !(val & DCPU_RESET_MASK);
 }
 
-static void __disable_dcpu(void __iomem *regs)
+static void __disable_dcpu(struct brcmstb_dpfe_priv *priv)
 {
 	u32 val;
 
-	if (!is_dcpu_enabled(regs))
+	if (!is_dcpu_enabled(priv))
 		return;
 
+	mutex_lock(&priv->lock);
+
 	/* Put DCPU in reset if it's running. */
-	val = readl_relaxed(regs + REG_DCPU_RESET);
+	val = readl_relaxed(priv->regs + REG_DCPU_RESET);
 	val |= (1 << DCPU_RESET_SHIFT);
-	writel_relaxed(val, regs + REG_DCPU_RESET);
+	writel_relaxed(val, priv->regs + REG_DCPU_RESET);
+
+	mutex_unlock(&priv->lock);
 }
 
-static void __enable_dcpu(void __iomem *regs)
+static void __enable_dcpu(struct brcmstb_dpfe_priv *priv)
 {
+	void __iomem *regs = priv->regs;
 	u32 val;
+
+	mutex_lock(&priv->lock);
 
 	/* Clear mailbox registers. */
 	writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
@@ -248,26 +364,32 @@
 	val = readl_relaxed(regs + REG_DCPU_RESET);
 	val &= ~(1 << DCPU_RESET_SHIFT);
 	writel_relaxed(val, regs + REG_DCPU_RESET);
+
+	mutex_unlock(&priv->lock);
 }
 
-static unsigned int get_msg_chksum(const u32 msg[])
+static unsigned int get_msg_chksum(const u32 msg[], unsigned int max)
 {
 	unsigned int sum = 0;
 	unsigned int i;
 
 	/* Don't include the last field in the checksum. */
-	for (i = 0; i < MSG_FIELD_MAX - 1; i++)
+	for (i = 0; i < max; i++)
 		sum += msg[i];
 
 	return sum;
 }
 
-static void __iomem *get_msg_ptr(struct private_data *priv, u32 response,
+static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
 				 char *buf, ssize_t *size)
 {
 	unsigned int msg_type;
 	unsigned int offset;
 	void __iomem *ptr = NULL;
+
+	/* There is no need to use this function for API v3 or later. */
+	if (unlikely(priv->dpfe_api->version >= 3))
+		return NULL;
 
 	msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
 	offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
@@ -296,23 +418,58 @@
 	return ptr;
 }
 
-static int __send_command(struct private_data *priv, unsigned int cmd,
+static void __finalize_command(struct brcmstb_dpfe_priv *priv)
+{
+	unsigned int release_mbox;
+
+	/*
+	 * It depends on the API version which MBOX register we have to write to
+	 * to signal we are done.
+	 */
+	release_mbox = (priv->dpfe_api->version < 2)
+			? REG_TO_HOST_MBOX : REG_TO_DCPU_MBOX;
+	writel_relaxed(0, priv->regs + release_mbox);
+}
+
+static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd,
 			  u32 result[])
 {
-	const u32 *msg = dpfe_commands[cmd];
 	void __iomem *regs = priv->regs;
-	unsigned int i, chksum;
+	unsigned int i, chksum, chksum_idx;
+	const u32 *msg;
 	int ret = 0;
 	u32 resp;
 
 	if (cmd >= DPFE_CMD_MAX)
 		return -1;
 
+	msg = priv->dpfe_api->command[cmd];
+
 	mutex_lock(&priv->lock);
 
+	/* Wait for DCPU to become ready */
+	for (i = 0; i < DELAY_LOOP_MAX; i++) {
+		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
+		if (resp == 0)
+			break;
+		msleep(1);
+	}
+	if (resp != 0) {
+		mutex_unlock(&priv->lock);
+		return -ffs(DCPU_RET_ERR_TIMEDOUT);
+	}
+
+	/* Compute checksum over the message */
+	chksum_idx = msg[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
+	chksum = get_msg_chksum(msg, chksum_idx);
+
 	/* Write command and arguments to message area */
-	for (i = 0; i < MSG_FIELD_MAX; i++)
-		writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
+	for (i = 0; i < MSG_FIELD_MAX; i++) {
+		if (i == chksum_idx)
+			writel_relaxed(chksum, regs + DCPU_MSG_RAM(i));
+		else
+			writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
+	}
 
 	/* Tell DCPU there is a command waiting */
 	writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
@@ -323,7 +480,7 @@
 		resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
 		if (resp > 0)
 			break;
-		udelay(5);
+		msleep(1);
 	}
 
 	if (i == DELAY_LOOP_MAX) {
@@ -333,10 +490,11 @@
 		/* Read response data */
 		for (i = 0; i < MSG_FIELD_MAX; i++)
 			result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
+		chksum_idx = result[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
 	}
 
 	/* Tell DCPU we are done */
-	writel_relaxed(0, regs + REG_TO_HOST_MBOX);
+	__finalize_command(priv);
 
 	mutex_unlock(&priv->lock);
 
@@ -344,8 +502,8 @@
 		return ret;
 
 	/* Verify response */
-	chksum = get_msg_chksum(result);
-	if (chksum != result[MSG_CHKSUM])
+	chksum = get_msg_chksum(result, chksum_idx);
+	if (chksum != result[chksum_idx])
 		resp = DCPU_RET_ERR_CHKSUM;
 
 	if (resp != DCPU_RET_SUCCESS) {
@@ -405,7 +563,7 @@
 
 /* Verify checksum by reading back the firmware from co-processor RAM. */
 static int __verify_fw_checksum(struct init_data *init,
-				struct private_data *priv,
+				struct brcmstb_dpfe_priv *priv,
 				const struct dpfe_firmware_header *header,
 				u32 checksum)
 {
@@ -459,26 +617,23 @@
 	return 0;
 }
 
-static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
-					  struct init_data *init)
+static int brcmstb_dpfe_download_firmware(struct brcmstb_dpfe_priv *priv)
 {
 	const struct dpfe_firmware_header *header;
 	unsigned int dmem_size, imem_size;
-	struct device *dev = &pdev->dev;
+	struct device *dev = priv->dev;
 	bool is_big_endian = false;
-	struct private_data *priv;
 	const struct firmware *fw;
 	const u32 *dmem, *imem;
+	struct init_data init;
 	const void *fw_blob;
 	int ret;
-
-	priv = platform_get_drvdata(pdev);
 
 	/*
 	 * Skip downloading the firmware if the DCPU is already running and
 	 * responding to commands.
 	 */
-	if (is_dcpu_enabled(priv->regs)) {
+	if (is_dcpu_enabled(priv)) {
 		u32 response[MSG_FIELD_MAX];
 
 		ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
@@ -486,20 +641,33 @@
 			return 0;
 	}
 
-	ret = request_firmware(&fw, FIRMWARE_NAME, dev);
-	/* request_firmware() prints its own error messages. */
+	/*
+	 * If the firmware filename is NULL it means the boot firmware has to
+	 * download the DCPU firmware for us. If that didn't work, we have to
+	 * bail, since downloading it ourselves wouldn't work either.
+	 */
+	if (!priv->dpfe_api->fw_name)
+		return -ENODEV;
+
+	ret = firmware_request_nowarn(&fw, priv->dpfe_api->fw_name, dev);
+	/*
+	 * Defer the firmware download if the firmware file couldn't be found.
+	 * The root file system may not be available yet.
+	 */
 	if (ret)
-		return ret;
+		return (ret == -ENOENT) ? -EPROBE_DEFER : ret;
 
-	ret = __verify_firmware(init, fw);
-	if (ret)
-		return -EFAULT;
+	ret = __verify_firmware(&init, fw);
+	if (ret) {
+		ret = -EFAULT;
+		goto release_fw;
+	}
 
-	__disable_dcpu(priv->regs);
+	__disable_dcpu(priv);
 
-	is_big_endian = init->is_big_endian;
-	dmem_size = init->dmem_len;
-	imem_size = init->imem_len;
+	is_big_endian = init.is_big_endian;
+	dmem_size = init.dmem_len;
+	imem_size = init.imem_len;
 
 	/* At the beginning of the firmware blob is a header. */
 	header = (struct dpfe_firmware_header *)fw->data;
@@ -512,33 +680,33 @@
 
 	ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
 	if (ret)
-		return ret;
+		goto release_fw;
 	ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
 	if (ret)
-		return ret;
+		goto release_fw;
 
-	ret = __verify_fw_checksum(init, priv, header, init->chksum);
+	ret = __verify_fw_checksum(&init, priv, header, init.chksum);
 	if (ret)
-		return ret;
+		goto release_fw;
 
-	__enable_dcpu(priv->regs);
+	__enable_dcpu(priv);
 
-	return 0;
+release_fw:
+	release_firmware(fw);
+	return ret;
 }
 
 static ssize_t generic_show(unsigned int command, u32 response[],
-			    struct device *dev, char *buf)
+			    struct brcmstb_dpfe_priv *priv, char *buf)
 {
-	struct private_data *priv;
 	int ret;
 
-	priv = dev_get_drvdata(dev);
 	if (!priv)
 		return sprintf(buf, "ERROR: driver private data not set\n");
 
 	ret = __send_command(priv, command, response);
 	if (ret < 0)
-		return sprintf(buf, "ERROR: %s\n", error_text[-ret]);
+		return sprintf(buf, "ERROR: %s\n", get_error_text(-ret));
 
 	return 0;
 }
@@ -547,10 +715,12 @@
 			 char *buf)
 {
 	u32 response[MSG_FIELD_MAX];
+	struct brcmstb_dpfe_priv *priv;
 	unsigned int info;
 	ssize_t ret;
 
-	ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf);
+	priv = dev_get_drvdata(dev);
+	ret = generic_show(DPFE_CMD_GET_INFO, response, priv, buf);
 	if (ret)
 		return ret;
 
@@ -568,22 +738,22 @@
 {
 	u32 response[MSG_FIELD_MAX];
 	void __iomem *info;
-	struct private_data *priv;
+	struct brcmstb_dpfe_priv *priv;
 	u8 refresh, sr_abort, ppre, thermal_offs, tuf;
 	u32 mr4;
 	ssize_t ret;
 
-	ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf);
+	priv = dev_get_drvdata(dev);
+	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
 	if (ret)
 		return ret;
-
-	priv = dev_get_drvdata(dev);
 
 	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
 	if (!info)
 		return ret;
 
-	mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK;
+	mr4 = (readl_relaxed(info + DRAM_INFO_MR4) >> DRAM_INFO_MR4_SHIFT) &
+	       DRAM_INFO_MR4_MASK;
 
 	refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
 	sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
@@ -601,7 +771,7 @@
 			  const char *buf, size_t count)
 {
 	u32 response[MSG_FIELD_MAX];
-	struct private_data *priv;
+	struct brcmstb_dpfe_priv *priv;
 	void __iomem *info;
 	unsigned long val;
 	int ret;
@@ -610,7 +780,6 @@
 		return -EINVAL;
 
 	priv = dev_get_drvdata(dev);
-
 	ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
 	if (ret)
 		return ret;
@@ -625,61 +794,79 @@
 }
 
 static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
-			 char *buf)
+			   char *buf)
 {
 	u32 response[MSG_FIELD_MAX];
-	struct private_data *priv;
+	struct brcmstb_dpfe_priv *priv;
 	void __iomem *info;
 	ssize_t ret;
-
-	ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf);
-	if (ret)
-		return ret;
+	u32 mr5, mr6, mr7, mr8, err;
 
 	priv = dev_get_drvdata(dev);
+	ret = generic_show(DPFE_CMD_GET_VENDOR, response, priv, buf);
+	if (ret)
+		return ret;
 
 	info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
 	if (!info)
 		return ret;
 
-	return sprintf(buf, "%#x %#x %#x %#x %#x\n",
-		       readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK,
-		       readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK,
-		       readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK,
-		       readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK,
-		       readl_relaxed(info + DRAM_VENDOR_ERROR) &
-				     DRAM_VENDOR_MASK);
+	mr5 = (readl_relaxed(info + DRAM_VENDOR_MR5) >> DRAM_VENDOR_SHIFT) &
+		DRAM_VENDOR_MASK;
+	mr6 = (readl_relaxed(info + DRAM_VENDOR_MR6) >> DRAM_VENDOR_SHIFT) &
+		DRAM_VENDOR_MASK;
+	mr7 = (readl_relaxed(info + DRAM_VENDOR_MR7) >> DRAM_VENDOR_SHIFT) &
+		DRAM_VENDOR_MASK;
+	mr8 = (readl_relaxed(info + DRAM_VENDOR_MR8) >> DRAM_VENDOR_SHIFT) &
+		DRAM_VENDOR_MASK;
+	err = readl_relaxed(info + DRAM_VENDOR_ERROR) & DRAM_VENDOR_MASK;
+
+	return sprintf(buf, "%#x %#x %#x %#x %#x\n", mr5, mr6, mr7, mr8, err);
+}
+
+static ssize_t show_dram(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	u32 response[MSG_FIELD_MAX];
+	struct brcmstb_dpfe_priv *priv;
+	ssize_t ret;
+	u32 mr4, mr5, mr6, mr7, mr8, err;
+
+	priv = dev_get_drvdata(dev);
+	ret = generic_show(DPFE_CMD_GET_REFRESH, response, priv, buf);
+	if (ret)
+		return ret;
+
+	mr4 = response[MSG_ARG0 + 0] & DRAM_INFO_MR4_MASK;
+	mr5 = response[MSG_ARG0 + 1] & DRAM_DDR_INFO_MASK;
+	mr6 = response[MSG_ARG0 + 2] & DRAM_DDR_INFO_MASK;
+	mr7 = response[MSG_ARG0 + 3] & DRAM_DDR_INFO_MASK;
+	mr8 = response[MSG_ARG0 + 4] & DRAM_DDR_INFO_MASK;
+	err = response[MSG_ARG0 + 5] & DRAM_DDR_INFO_MASK;
+
+	return sprintf(buf, "%#x %#x %#x %#x %#x %#x\n", mr4, mr5, mr6, mr7,
+			mr8, err);
 }
 
 static int brcmstb_dpfe_resume(struct platform_device *pdev)
 {
-	struct init_data init;
+	struct brcmstb_dpfe_priv *priv = platform_get_drvdata(pdev);
 
-	return brcmstb_dpfe_download_firmware(pdev, &init);
+	return brcmstb_dpfe_download_firmware(priv);
 }
-
-static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
-static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
-static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
-static struct attribute *dpfe_attrs[] = {
-	&dev_attr_dpfe_info.attr,
-	&dev_attr_dpfe_refresh.attr,
-	&dev_attr_dpfe_vendor.attr,
-	NULL
-};
-ATTRIBUTE_GROUPS(dpfe);
 
 static int brcmstb_dpfe_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct private_data *priv;
-	struct init_data init;
+	struct brcmstb_dpfe_priv *priv;
 	struct resource *res;
 	int ret;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
+
+	priv->dev = dev;
 
 	mutex_init(&priv->lock);
 	platform_set_drvdata(pdev, priv);
@@ -705,26 +892,45 @@
 		return -ENOENT;
 	}
 
-	ret = brcmstb_dpfe_download_firmware(pdev, &init);
-	if (ret)
-		return ret;
+	priv->dpfe_api = of_device_get_match_data(dev);
+	if (unlikely(!priv->dpfe_api)) {
+		/*
+		 * It should be impossible to end up here, but to be safe we
+		 * check anyway.
+		 */
+		dev_err(dev, "Couldn't determine API\n");
+		return -ENOENT;
+	}
 
-	ret = sysfs_create_groups(&pdev->dev.kobj, dpfe_groups);
+	ret = brcmstb_dpfe_download_firmware(priv);
+	if (ret)
+		return dev_err_probe(dev, ret, "Couldn't download firmware\n");
+
+	ret = sysfs_create_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
 	if (!ret)
-		dev_info(dev, "registered.\n");
+		dev_info(dev, "registered with API v%d.\n",
+			 priv->dpfe_api->version);
 
 	return ret;
 }
 
 static int brcmstb_dpfe_remove(struct platform_device *pdev)
 {
-	sysfs_remove_groups(&pdev->dev.kobj, dpfe_groups);
+	struct brcmstb_dpfe_priv *priv = dev_get_drvdata(&pdev->dev);
+
+	sysfs_remove_groups(&pdev->dev.kobj, priv->dpfe_api->sysfs_attrs);
 
 	return 0;
 }
 
 static const struct of_device_id brcmstb_dpfe_of_match[] = {
-	{ .compatible = "brcm,dpfe-cpu", },
+	/* Use legacy API v2 for a select number of chips */
+	{ .compatible = "brcm,bcm7268-dpfe-cpu", .data = &dpfe_api_old_v2 },
+	{ .compatible = "brcm,bcm7271-dpfe-cpu", .data = &dpfe_api_old_v2 },
+	{ .compatible = "brcm,bcm7278-dpfe-cpu", .data = &dpfe_api_old_v2 },
+	{ .compatible = "brcm,bcm7211-dpfe-cpu", .data = &dpfe_api_new_v2 },
+	/* API v3 is the default going forward */
+	{ .compatible = "brcm,dpfe-cpu", .data = &dpfe_api_v3 },
 	{}
 };
 MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);

--
Gitblit v1.6.2