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