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