From mboxrd@z Thu Jan 1 00:00:00 1970 From: code@mmayer.net (Markus Mayer) Date: Tue, 13 Jun 2017 15:37:10 -0700 Subject: [PATCH v2 2/2] soc: brcmstb: Add driver for DPFE In-Reply-To: <20170613223710.31719-1-code@mmayer.net> References: <20170613223710.31719-1-code@mmayer.net> Message-ID: <20170613223710.31719-3-code@mmayer.net> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org From: Markus Mayer This driver allows access to DRAM properties, such as the refresh rate, via the Broadcom STB DDR PHY Front End (DPFE). The refresh rate can be used as indirect indicator of the DRAM temperature. The driver also allows setting of the sampling interval. Signed-off-by: Markus Mayer --- MAINTAINERS | 8 + drivers/soc/bcm/brcmstb/Makefile | 2 +- drivers/soc/bcm/brcmstb/dpfe.c | 689 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/bcm/brcmstb/dpfe.c diff --git a/MAINTAINERS b/MAINTAINERS index f7d568b..04f0be6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2864,6 +2864,14 @@ S: Maintained F: Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt F: drivers/cpufreq/brcmstb* +BROADCOM STB SOC DPFE DRIVER +M: Markus Mayer +M: bcm-kernel-feedback-list at broadcom.com +L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/soc/bcm/brcm,dpfe-cpu.txt +F: drivers/misc/brcmstb-dpfe.c + BROADCOM SPECIFIC AMBA DRIVER (BCMA) M: Rafa? Mi?ecki L: linux-wireless at vger.kernel.org diff --git a/drivers/soc/bcm/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile index 9120b27..3ec87fe 100644 --- a/drivers/soc/bcm/brcmstb/Makefile +++ b/drivers/soc/bcm/brcmstb/Makefile @@ -1 +1 @@ -obj-y += common.o biuctrl.o +obj-y += common.o biuctrl.o dpfe.o diff --git a/drivers/soc/bcm/brcmstb/dpfe.c b/drivers/soc/bcm/brcmstb/dpfe.c new file mode 100644 index 0000000..c2504ca --- /dev/null +++ b/drivers/soc/bcm/brcmstb/dpfe.c @@ -0,0 +1,689 @@ +/* + * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs + * + * Copyright (c) 2017 Broadcom + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * This driver provides access to the DPFE interface of Broadcom STB SoCs. + * The firmware running on the DCPU inside the DDR PHY can provide current + * information about the system's RAM, for instance the DRAM refresh rate. + * This can be used as an indirect indicator for the DRAM's temperature. + * Slower refresh rate means cooler RAM, higher refresh rate means hotter + * RAM. + * + * Throughout the driver, we use readl_relaxed() and writel_relaxed(), which + * already contain the appropriate le32_to_cpu()/cpu_to_le32() calls. + */ + +#include +#include +#include +#include +#include +#include + +#define DRVNAME "brcmstb-dpfe" +#define FIRMWARE_NAME "dpfe.bin" + +/* DCPU register offsets */ +#define REG_DCPU_RESET 0x0 +#define REG_TO_DCPU_MBOX 0x10 +#define REG_TO_HOST_MBOX 0x14 + +/* Message RAM */ +#define DCPU_MSG_RAM(x) (0x100 + (x) * sizeof(u32)) + +/* DRAM Info Offsets & Masks */ +#define DRAM_INFO_INTERVAL 0x0 +#define DRAM_INFO_MR4 0x4 +#define DRAM_INFO_ERROR 0x8 +#define DRAM_INFO_MR4_MASK 0xff + +/* DRAM MR4 Offsets & Masks */ +#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */ +#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */ +#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */ +#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */ +#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */ + +#define DRAM_MR4_REFRESH_MASK 0x7 +#define DRAM_MR4_SR_ABORT_MASK 0x1 +#define DRAM_MR4_PPRE_MASK 0x1 +#define DRAM_MR4_TH_OFFS_MASK 0x3 +#define DRAM_MR4_TUF_MASK 0x1 + +/* DRAM Vendor Offsets & Masks */ +#define DRAM_VENDOR_MR5 0x0 +#define DRAM_VENDOR_MR6 0x4 +#define DRAM_VENDOR_MR7 0x8 +#define DRAM_VENDOR_MR8 0xc +#define DRAM_VENDOR_ERROR 0x10 +#define DRAM_VENDOR_MASK 0xff + +/* Reset register bits & masks */ +#define DCPU_RESET_SHIFT 0x0 +#define DCPU_RESET_MASK 0x1 +#define DCPU_CLK_DISABLE_SHIFT 0x2 + +/* DCPU return codes */ +#define DCPU_RET_ERROR_BIT BIT(31) +#define DCPU_RET_SUCCESS 0x1 +#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0)) +#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1)) +#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2)) +#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3)) +/* This error code is not firmware defined and only used in the driver. */ +#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4)) + +/* Firmware magic */ +#define DPFE_BE_MAGIC 0xfe1010fe +#define DPFE_LE_MAGIC 0xfe0101fe + +/* Error codes */ +#define ERR_INVALID_MAGIC -1 +#define ERR_INVALID_SIZE -2 +#define ERR_INVALID_CHKSUM -3 + +/* Message types */ +#define DPFE_MSG_TYPE_COMMAND 1 +#define DPFE_MSG_TYPE_RESPONSE 2 + +#define DELAY_LOOP_MAX 200000 + +enum dpfe_msg_fields { + MSG_HEADER, + MSG_COMMAND, + MSG_ARG_COUNT, + MSG_ARG0, + MSG_CHKSUM, + MSG_FIELD_MAX /* Last entry */ +}; + +enum dpfe_commands { + DPFE_CMD_GET_INFO, + DPFE_CMD_GET_REFRESH, + DPFE_CMD_GET_VENDOR, + DPFE_CMD_MAX /* Last entry */ +}; + +struct dpfe_msg { + u32 header; + u32 command; + u32 arg_count; + u32 arg0; + u32 chksum; /* This is the sum of all other entries. */ +}; + +/* + * Format of the binary firmware file: + * + * entry + * 0 header + * value: 0xfe0101fe <== little endian + * 0xfe1010fe <== big endian + * 1 sequence: + * [31:16] total segments on this build + * [15:0] this segment sequence. + * 2 FW version + * 3 IMEM byte size + * 4 DMEM byte size + * IMEM + * DMEM + * last checksum ==> sum of everything + */ +struct dpfe_firmware_header { + u32 magic; + u32 sequence; + u32 version; + u32 imem_size; + u32 dmem_size; +}; + +/* Things we only need during initialization. */ +struct init_data { + unsigned int dmem_len; + unsigned int imem_len; + unsigned int chksum; + bool is_big_endian; +}; + +/* Things we need for as long as we are active. */ +struct private_data { + void __iomem *regs; + void __iomem *dmem; + void __iomem *imem; + struct device *dev; + unsigned int index; + struct mutex lock; +}; + +static const char *error_text[] = { + "Success", "Header code incorrect", "Unknown command or argument", + "Incorrect checksum", "Malformed command", "Timed out", +}; + +/* List of supported firmware commands */ +static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = { + [DPFE_CMD_GET_INFO] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 1, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 1, + [MSG_CHKSUM] = 4, + }, + [DPFE_CMD_GET_REFRESH] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 2, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 1, + [MSG_CHKSUM] = 5, + }, + [DPFE_CMD_GET_VENDOR] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 2, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 2, + [MSG_CHKSUM] = 6, + }, +}; + +static void __disable_dcpu(void __iomem *regs) +{ + u32 val; + + /* Check if DCPU is running */ + val = readl_relaxed(regs + REG_DCPU_RESET); + if (!(val & DCPU_RESET_MASK)) { + /* Put DCPU in reset */ + val |= (1 << DCPU_RESET_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); + } +} + +static void __enable_dcpu(void __iomem *regs) +{ + u32 val; + + /* Clear mailbox registers. */ + writel_relaxed(0, regs + REG_TO_DCPU_MBOX); + writel_relaxed(0, regs + REG_TO_HOST_MBOX); + + /* Disable DCPU clock gating */ + val = readl_relaxed(regs + REG_DCPU_RESET); + val &= ~(1 << DCPU_CLK_DISABLE_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); + + /* Take DCPU out of reset */ + val = readl_relaxed(regs + REG_DCPU_RESET); + val &= ~(1 << DCPU_RESET_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); +} + +static unsigned int get_msg_chksum(const u32 msg[]) +{ + unsigned int sum = 0; + unsigned int i; + + /* Don't include the last field in the checksum. */ + for (i = 0; i < MSG_FIELD_MAX - 1; i++) + sum += msg[i]; + + return sum; +} + +static int __send_command(struct private_data *priv, unsigned int cmd, + u32 result[]) +{ + const u32 *msg = dpfe_commands[cmd]; + void __iomem *regs = priv->regs; + unsigned int i, chksum; + int ret = 0; + u32 resp; + + if (cmd >= DPFE_CMD_MAX) + return -1; + + mutex_lock(&priv->lock); + + /* Write command and arguments to message area */ + for (i = 0; i < MSG_FIELD_MAX; i++) + writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); + + /* Tell DCPU there is a command waiting */ + writel_relaxed(1, regs + REG_TO_DCPU_MBOX); + + /* Wait for DCPU to process the command */ + for (i = 0; i < DELAY_LOOP_MAX; i++) { + /* Read response code */ + resp = readl_relaxed(regs + REG_TO_HOST_MBOX); + if (resp > 0) + break; + udelay(5); + } + + if (i == DELAY_LOOP_MAX) { + resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT); + ret = -ffs(resp); + } else { + /* Read response data */ + for (i = 0; i < MSG_FIELD_MAX; i++) + result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i)); + } + + /* Tell DCPU we are done */ + writel_relaxed(0, regs + REG_TO_HOST_MBOX); + + mutex_unlock(&priv->lock); + + if (ret) + return ret; + + /* Verify response */ + chksum = get_msg_chksum(result); + if (chksum != result[MSG_CHKSUM]) + resp = DCPU_RET_ERR_CHKSUM; + + if (resp != DCPU_RET_SUCCESS) { + resp &= ~DCPU_RET_ERROR_BIT; + ret = -ffs(resp); + } + + return ret; +} + +/* Ensure that the firmware file loaded meets all the requirements. */ +static int __verify_firmware(struct init_data *init, + const struct firmware *fw) +{ + const struct dpfe_firmware_header *header = (void *)fw->data; + unsigned int dmem_size, imem_size, total_size; + bool is_big_endian = false; + const u32 *chksum_ptr; + + if (header->magic == DPFE_BE_MAGIC) + is_big_endian = true; + else if (header->magic != DPFE_LE_MAGIC) + return ERR_INVALID_MAGIC; + + if (is_big_endian) { + dmem_size = be32_to_cpu(header->dmem_size); + imem_size = be32_to_cpu(header->imem_size); + } else { + dmem_size = header->dmem_size; + imem_size = header->imem_size; + } + + /* Data and instruction sections are 32 bit words. */ + if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0) + return ERR_INVALID_SIZE; + + /* + * The header + the data section + the instruction section + the + * checksum must be equal to the total firmware size. + */ + total_size = dmem_size + imem_size + sizeof(*header) + + sizeof(*chksum_ptr); + if (total_size != fw->size) + return ERR_INVALID_SIZE; + + /* The checksum comes at the very end. */ + chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size; + + init->is_big_endian = is_big_endian; + init->dmem_len = dmem_size; + init->imem_len = imem_size; + init->chksum = (is_big_endian) ? be32_to_cpu(*chksum_ptr) : *chksum_ptr; + + return 0; +} + +/* Verify checksum by reading back the firmware from co-processor RAM. */ +static int __verify_fw_checksum(struct init_data *init, + struct private_data *priv, + const struct dpfe_firmware_header *header, + u32 checksum) +{ + u32 magic, sequence, version, sum; + u32 __iomem *dmem = priv->dmem; + u32 __iomem *imem = priv->imem; + unsigned int i; + + if (init->is_big_endian) { + magic = be32_to_cpu(header->magic); + sequence = be32_to_cpu(header->sequence); + version = be32_to_cpu(header->version); + } else { + magic = header->magic; + sequence = header->sequence; + version = header->version; + } + + sum = magic + sequence + version + init->dmem_len + init->imem_len; + + for (i = 0; i < init->dmem_len / sizeof(u32); i++) + sum += readl_relaxed(dmem + i); + + for (i = 0; i < init->imem_len / sizeof(u32); i++) + sum += readl_relaxed(imem + i); + + return (sum == checksum) ? 0 : -1; +} + +static int __write_firmware(u32 __iomem *mem, const u32 *fw, + unsigned int size, bool is_big_endian) +{ + unsigned int i; + + /* Convert size to 32-bit words. */ + size /= sizeof(u32); + + /* It is recommended to clear the firmware area first. */ + for (i = 0; i < size; i++) + writel_relaxed(0, mem + i); + + /* Now copy it. */ + if (is_big_endian) { + for (i = 0; i < size; i++) + writel_relaxed(be32_to_cpu(fw[i]), mem + i); + } else { + for (i = 0; i < size; i++) + writel_relaxed(fw[i], mem + i); + } + + return 0; +} + +static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, + struct init_data *init) +{ + const struct dpfe_firmware_header *header; + unsigned int dmem_size, imem_size; + struct device *dev = &pdev->dev; + bool is_big_endian = false; + struct private_data *priv; + const struct firmware *fw; + const u32 *dmem, *imem; + const void *fw_blob; + int ret; + + ret = request_firmware(&fw, FIRMWARE_NAME, dev); + /* request_firmware() prints its own error messages. */ + if (ret) + return ret; + + priv = platform_get_drvdata(pdev); + + ret = __verify_firmware(init, fw); + if (ret) + return -EFAULT; + + __disable_dcpu(priv->regs); + + is_big_endian = init->is_big_endian; + dmem_size = init->dmem_len; + imem_size = init->imem_len; + + /* At the beginning of the firmware blob is a header. */ + header = (struct dpfe_firmware_header *)fw->data; + /* Void pointer to the beginning of the actual firmware. */ + fw_blob = fw->data + sizeof(*header); + /* IMEM comes right after the header. */ + imem = fw_blob; + /* DMEM follows after IMEM. */ + dmem = fw_blob + imem_size; + + ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian); + if (ret) + return ret; + ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian); + if (ret) + return ret; + + ret = __verify_fw_checksum(init, priv, header, init->chksum); + if (ret) + return ret; + + __enable_dcpu(priv->regs); + + return 0; +} + +static ssize_t generic_show(unsigned int command, u32 response[], + struct device *dev, char *buf) +{ + struct private_data *priv; + int ret; + + priv = dev_get_drvdata(dev); + if (!priv) + return sprintf(buf, "ERROR: driver private data not set\n"); + + ret = __send_command(priv, command, response); + if (ret < 0) + return sprintf(buf, "ERROR: %s\n", error_text[-ret]); + + return 0; +} + +static ssize_t show_info(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + u32 response[MSG_FIELD_MAX]; + unsigned int info; + int ret; + + ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf); + if (ret) + return ret; + + info = response[MSG_ARG0]; + + return sprintf(buf, "%u.%u.%u.%u\n", + (info >> 24) & 0xff, + (info >> 16) & 0xff, + (info >> 8) & 0xff, + info & 0xff); +} + +static ssize_t show_refresh(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 response[MSG_FIELD_MAX]; + void __iomem *info; + struct private_data *priv; + unsigned int offset; + u8 refresh, sr_abort, ppre, thermal_offs, tuf; + u32 mr4; + int ret; + + ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf); + if (ret) + return ret; + + priv = dev_get_drvdata(dev); + offset = response[MSG_ARG0]; + info = priv->dmem + offset; + + mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK; + + refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK; + sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK; + ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK; + thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK; + tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK; + + return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n", + readl_relaxed(info + DRAM_INFO_INTERVAL), + refresh, sr_abort, ppre, thermal_offs, tuf, + readl_relaxed(info + DRAM_INFO_ERROR)); +} + +static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 response[MSG_FIELD_MAX]; + struct private_data *priv; + void __iomem *info; + unsigned int offset; + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + priv = dev_get_drvdata(dev); + + ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response); + if (ret) + return ret; + + offset = response[MSG_ARG0]; + info = priv->dmem + offset; + writel_relaxed(val, info + DRAM_INFO_INTERVAL); + + return count; +} + +static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + u32 response[MSG_FIELD_MAX]; + struct private_data *priv; + void __iomem *info; + unsigned int offset; + int ret; + + ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf); + if (ret) + return ret; + + offset = response[MSG_ARG0]; + priv = dev_get_drvdata(dev); + info = priv->dmem + offset; + + return sprintf(buf, "%#x %#x %#x %#x %#x\n", + readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_ERROR)); +} + +static int brcmstb_dpfe_resume(struct platform_device *pdev) +{ + struct init_data init; + + return brcmstb_dpfe_download_firmware(pdev, &init); +} + +static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL); +static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh); +static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL); +static struct attribute *dpfe_attrs[] = { + &dev_attr_dpfe_info.attr, + &dev_attr_dpfe_refresh.attr, + &dev_attr_dpfe_vendor.attr, + NULL +}; +ATTRIBUTE_GROUPS(dpfe); + +static int brcmstb_dpfe_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct private_data *priv; + struct device *dpfe_dev; + struct init_data init; + struct resource *res; + u32 index; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + /* Cell index is optional; default to 0 if not present. */ + ret = of_property_read_u32(dev->of_node, "cell-index", &index); + if (ret) + index = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu"); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "couldn't map DCPU registers\n"); + return -ENODEV; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem"); + priv->dmem = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->dmem)) { + dev_err(dev, "Couldn't map DCPU data memory\n"); + return -ENOENT; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem"); + priv->imem = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->imem)) { + dev_err(dev, "Couldn't map DCPU instruction memory\n"); + return -ENOENT; + } + + ret = brcmstb_dpfe_download_firmware(pdev, &init); + if (ret) + goto err; + + dpfe_dev = devm_kzalloc(dev, sizeof(*dpfe_dev), GFP_KERNEL); + if (!dpfe_dev) { + ret = -ENOMEM; + goto err; + } + + priv->dev = dpfe_dev; + priv->index = index; + + dpfe_dev->parent = dev; + dpfe_dev->groups = dpfe_groups; + dpfe_dev->of_node = dev->of_node; + dev_set_drvdata(dpfe_dev, priv); + dev_set_name(dpfe_dev, "dpfe%u", index); + + ret = device_register(dpfe_dev); + if (ret) + goto err; + + dev_info(dev, "registered.\n"); + + return 0; + +err: + dev_err(dev, "failed to initialize -- error %d\n", ret); + + return ret; +} + +static const struct of_device_id brcmstb_dpfe_of_match[] = { + { .compatible = "brcm,dpfe-cpu", }, + {} +}; +MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match); + +static struct platform_driver brcmstb_dpfe_driver = { + .driver = { + .name = DRVNAME, + .of_match_table = brcmstb_dpfe_of_match, + }, + .probe = brcmstb_dpfe_probe, + .resume = brcmstb_dpfe_resume, +}; + +module_platform_driver(brcmstb_dpfe_driver); + +MODULE_AUTHOR("Markus Mayer "); +MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver"); +MODULE_LICENSE("GPL"); -- 2.7.4 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Markus Mayer Subject: [PATCH v2 2/2] soc: brcmstb: Add driver for DPFE Date: Tue, 13 Jun 2017 15:37:10 -0700 Message-ID: <20170613223710.31719-3-code@mmayer.net> References: <20170613223710.31719-1-code@mmayer.net> Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: <20170613223710.31719-1-code@mmayer.net> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.infradead.org To: Rob Herring , Mark Rutland , Florian Fainelli , Gregory Fong Cc: Device Tree List , Broadcom Kernel List , ARM Kernel List , Markus Mayer , Linux Kernel Mailing List List-Id: devicetree@vger.kernel.org RnJvbTogTWFya3VzIE1heWVyIDxtbWF5ZXJAYnJvYWRjb20uY29tPgoKVGhpcyBkcml2ZXIgYWxs b3dzIGFjY2VzcyB0byBEUkFNIHByb3BlcnRpZXMsIHN1Y2ggYXMgdGhlIHJlZnJlc2ggcmF0ZSwK dmlhIHRoZSBCcm9hZGNvbSBTVEIgRERSIFBIWSBGcm9udCBFbmQgKERQRkUpLiBUaGUgcmVmcmVz aCByYXRlIGNhbiBiZQp1c2VkIGFzIGluZGlyZWN0IGluZGljYXRvciBvZiB0aGUgRFJBTSB0ZW1w ZXJhdHVyZS4KClRoZSBkcml2ZXIgYWxzbyBhbGxvd3Mgc2V0dGluZyBvZiB0aGUgc2FtcGxpbmcg aW50ZXJ2YWwuCgpTaWduZWQtb2ZmLWJ5OiBNYXJrdXMgTWF5ZXIgPG1tYXllckBicm9hZGNvbS5j b20+Ci0tLQogTUFJTlRBSU5FUlMgICAgICAgICAgICAgICAgICAgICAgfCAgIDggKwogZHJpdmVy cy9zb2MvYmNtL2JyY21zdGIvTWFrZWZpbGUgfCAgIDIgKy0KIGRyaXZlcnMvc29jL2JjbS9icmNt c3RiL2RwZmUuYyAgIHwgNjg5ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KwogMyBmaWxlcyBjaGFuZ2VkLCA2OTggaW5zZXJ0aW9ucygrKSwgMSBkZWxldGlvbigtKQogY3Jl YXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvc29jL2JjbS9icmNtc3RiL2RwZmUuYwoKZGlmZiAtLWdp dCBhL01BSU5UQUlORVJTIGIvTUFJTlRBSU5FUlMKaW5kZXggZjdkNTY4Yi4uMDRmMGJlNiAxMDA2 NDQKLS0tIGEvTUFJTlRBSU5FUlMKKysrIGIvTUFJTlRBSU5FUlMKQEAgLTI4NjQsNiArMjg2NCwx NCBAQCBTOglNYWludGFpbmVkCiBGOglEb2N1bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3Mv Y3B1ZnJlcS9icmNtLHN0Yi1hdnMtY3B1LWZyZXEudHh0CiBGOglkcml2ZXJzL2NwdWZyZXEvYnJj bXN0YioKIAorQlJPQURDT00gU1RCIFNPQyBEUEZFIERSSVZFUgorTToJTWFya3VzIE1heWVyIDxt bWF5ZXJAYnJvYWRjb20uY29tPgorTToJYmNtLWtlcm5lbC1mZWVkYmFjay1saXN0QGJyb2FkY29t LmNvbQorTDoJbGludXgtYXJtLWtlcm5lbEBsaXN0cy5pbmZyYWRlYWQub3JnIChtb2RlcmF0ZWQg Zm9yIG5vbi1zdWJzY3JpYmVycykKK1M6CU1haW50YWluZWQKK0Y6CURvY3VtZW50YXRpb24vZGV2 aWNldHJlZS9iaW5kaW5ncy9zb2MvYmNtL2JyY20sZHBmZS1jcHUudHh0CitGOglkcml2ZXJzL21p c2MvYnJjbXN0Yi1kcGZlLmMKKwogQlJPQURDT00gU1BFQ0lGSUMgQU1CQSBEUklWRVIgKEJDTUEp CiBNOglSYWZhxYIgTWnFgmVja2kgPHphamVjNUBnbWFpbC5jb20+CiBMOglsaW51eC13aXJlbGVz c0B2Z2VyLmtlcm5lbC5vcmcKZGlmZiAtLWdpdCBhL2RyaXZlcnMvc29jL2JjbS9icmNtc3RiL01h a2VmaWxlIGIvZHJpdmVycy9zb2MvYmNtL2JyY21zdGIvTWFrZWZpbGUKaW5kZXggOTEyMGIyNy4u M2VjODdmZSAxMDA2NDQKLS0tIGEvZHJpdmVycy9zb2MvYmNtL2JyY21zdGIvTWFrZWZpbGUKKysr IGIvZHJpdmVycy9zb2MvYmNtL2JyY21zdGIvTWFrZWZpbGUKQEAgLTEgKzEgQEAKLW9iai15CQkJ CSs9IGNvbW1vbi5vIGJpdWN0cmwubworb2JqLXkJCQkJKz0gY29tbW9uLm8gYml1Y3RybC5vIGRw ZmUubwpkaWZmIC0tZ2l0IGEvZHJpdmVycy9zb2MvYmNtL2JyY21zdGIvZHBmZS5jIGIvZHJpdmVy cy9zb2MvYmNtL2JyY21zdGIvZHBmZS5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAw MDAuLmMyNTA0Y2EKLS0tIC9kZXYvbnVsbAorKysgYi9kcml2ZXJzL3NvYy9iY20vYnJjbXN0Yi9k cGZlLmMKQEAgLTAsMCArMSw2ODkgQEAKKy8qCisgKiBERFIgUEhZIEZyb250IEVuZCAoRFBGRSkg ZHJpdmVyIGZvciBCcm9hZGNvbSBzZXQgdG9wIGJveCBTb0NzCisgKgorICogQ29weXJpZ2h0IChj KSAyMDE3IEJyb2FkY29tCisgKgorICogUmVsZWFzZWQgdW5kZXIgdGhlIEdQTHYyIG9ubHkuCisg KiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMAorICovCisKKy8qCisgKiBUaGlzIGRy aXZlciBwcm92aWRlcyBhY2Nlc3MgdG8gdGhlIERQRkUgaW50ZXJmYWNlIG9mIEJyb2FkY29tIFNU QiBTb0NzLgorICogVGhlIGZpcm13YXJlIHJ1bm5pbmcgb24gdGhlIERDUFUgaW5zaWRlIHRoZSBE RFIgUEhZIGNhbiBwcm92aWRlIGN1cnJlbnQKKyAqIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzeXN0 ZW0ncyBSQU0sIGZvciBpbnN0YW5jZSB0aGUgRFJBTSByZWZyZXNoIHJhdGUuCisgKiBUaGlzIGNh biBiZSB1c2VkIGFzIGFuIGluZGlyZWN0IGluZGljYXRvciBmb3IgdGhlIERSQU0ncyB0ZW1wZXJh dHVyZS4KKyAqIFNsb3dlciByZWZyZXNoIHJhdGUgbWVhbnMgY29vbGVyIFJBTSwgaGlnaGVyIHJl ZnJlc2ggcmF0ZSBtZWFucyBob3R0ZXIKKyAqIFJBTS4KKyAqCisgKiBUaHJvdWdob3V0IHRoZSBk cml2ZXIsIHdlIHVzZSByZWFkbF9yZWxheGVkKCkgYW5kIHdyaXRlbF9yZWxheGVkKCksIHdoaWNo CisgKiBhbHJlYWR5IGNvbnRhaW4gdGhlIGFwcHJvcHJpYXRlIGxlMzJfdG9fY3B1KCkvY3B1X3Rv X2xlMzIoKSBjYWxscy4KKyAqLworCisjaW5jbHVkZSA8bGludXgvZGVsYXkuaD4KKyNpbmNsdWRl IDxsaW51eC9maXJtd2FyZS5oPgorI2luY2x1ZGUgPGxpbnV4L2lvLmg+CisjaW5jbHVkZSA8bGlu dXgvbW9kdWxlLmg+CisjaW5jbHVkZSA8bGludXgvb2ZfYWRkcmVzcy5oPgorI2luY2x1ZGUgPGxp bnV4L3BsYXRmb3JtX2RldmljZS5oPgorCisjZGVmaW5lIERSVk5BTUUJCQkiYnJjbXN0Yi1kcGZl IgorI2RlZmluZSBGSVJNV0FSRV9OQU1FCQkiZHBmZS5iaW4iCisKKy8qIERDUFUgcmVnaXN0ZXIg b2Zmc2V0cyAqLworI2RlZmluZSBSRUdfRENQVV9SRVNFVAkJMHgwCisjZGVmaW5lIFJFR19UT19E Q1BVX01CT1gJMHgxMAorI2RlZmluZSBSRUdfVE9fSE9TVF9NQk9YCTB4MTQKKworLyogTWVzc2Fn ZSBSQU0gKi8KKyNkZWZpbmUgRENQVV9NU0dfUkFNKHgpCQkoMHgxMDAgKyAoeCkgKiBzaXplb2Yo dTMyKSkKKworLyogRFJBTSBJbmZvIE9mZnNldHMgJiBNYXNrcyAqLworI2RlZmluZSBEUkFNX0lO Rk9fSU5URVJWQUwJMHgwCisjZGVmaW5lIERSQU1fSU5GT19NUjQJCTB4NAorI2RlZmluZSBEUkFN X0lORk9fRVJST1IJCTB4OAorI2RlZmluZSBEUkFNX0lORk9fTVI0X01BU0sJMHhmZgorCisvKiBE UkFNIE1SNCBPZmZzZXRzICYgTWFza3MgKi8KKyNkZWZpbmUgRFJBTV9NUjRfUkVGUkVTSAkweDAJ LyogUmVmcmVzaCByYXRlICovCisjZGVmaW5lIERSQU1fTVI0X1NSX0FCT1JUCTB4MwkvKiBTZWxm IFJlZnJlc2ggQWJvcnQgKi8KKyNkZWZpbmUgRFJBTV9NUjRfUFBSRQkJMHg0CS8qIFBvc3QtcGFj a2FnZSByZXBhaXIgZW50cnkvZXhpdCAqLworI2RlZmluZSBEUkFNX01SNF9USF9PRkZTCTB4NQkv KiBUaGVybWFsIE9mZnNldDsgdmVuZG9yIHNwZWNpZmljICovCisjZGVmaW5lIERSQU1fTVI0X1RV RgkJMHg3CS8qIFRlbXBlcmF0dXJlIFVwZGF0ZSBGbGFnICovCisKKyNkZWZpbmUgRFJBTV9NUjRf UkVGUkVTSF9NQVNLCTB4NworI2RlZmluZSBEUkFNX01SNF9TUl9BQk9SVF9NQVNLCTB4MQorI2Rl ZmluZSBEUkFNX01SNF9QUFJFX01BU0sJMHgxCisjZGVmaW5lIERSQU1fTVI0X1RIX09GRlNfTUFT SwkweDMKKyNkZWZpbmUgRFJBTV9NUjRfVFVGX01BU0sJMHgxCisKKy8qIERSQU0gVmVuZG9yIE9m ZnNldHMgJiBNYXNrcyAqLworI2RlZmluZSBEUkFNX1ZFTkRPUl9NUjUJCTB4MAorI2RlZmluZSBE UkFNX1ZFTkRPUl9NUjYJCTB4NAorI2RlZmluZSBEUkFNX1ZFTkRPUl9NUjcJCTB4OAorI2RlZmlu ZSBEUkFNX1ZFTkRPUl9NUjgJCTB4YworI2RlZmluZSBEUkFNX1ZFTkRPUl9FUlJPUgkweDEwCisj ZGVmaW5lIERSQU1fVkVORE9SX01BU0sJMHhmZgorCisvKiBSZXNldCByZWdpc3RlciBiaXRzICYg bWFza3MgKi8KKyNkZWZpbmUgRENQVV9SRVNFVF9TSElGVAkweDAKKyNkZWZpbmUgRENQVV9SRVNF VF9NQVNLCQkweDEKKyNkZWZpbmUgRENQVV9DTEtfRElTQUJMRV9TSElGVAkweDIKKworLyogRENQ VSByZXR1cm4gY29kZXMgKi8KKyNkZWZpbmUgRENQVV9SRVRfRVJST1JfQklUCUJJVCgzMSkKKyNk ZWZpbmUgRENQVV9SRVRfU1VDQ0VTUwkweDEKKyNkZWZpbmUgRENQVV9SRVRfRVJSX0hFQURFUgko RENQVV9SRVRfRVJST1JfQklUIHwgQklUKDApKQorI2RlZmluZSBEQ1BVX1JFVF9FUlJfSU5WQUwJ KERDUFVfUkVUX0VSUk9SX0JJVCB8IEJJVCgxKSkKKyNkZWZpbmUgRENQVV9SRVRfRVJSX0NIS1NV TQkoRENQVV9SRVRfRVJST1JfQklUIHwgQklUKDIpKQorI2RlZmluZSBEQ1BVX1JFVF9FUlJfQ09N TUFORAkoRENQVV9SRVRfRVJST1JfQklUIHwgQklUKDMpKQorLyogVGhpcyBlcnJvciBjb2RlIGlz IG5vdCBmaXJtd2FyZSBkZWZpbmVkIGFuZCBvbmx5IHVzZWQgaW4gdGhlIGRyaXZlci4gKi8KKyNk ZWZpbmUgRENQVV9SRVRfRVJSX1RJTUVET1VUCShEQ1BVX1JFVF9FUlJPUl9CSVQgfCBCSVQoNCkp CisKKy8qIEZpcm13YXJlIG1hZ2ljICovCisjZGVmaW5lIERQRkVfQkVfTUFHSUMJCTB4ZmUxMDEw ZmUKKyNkZWZpbmUgRFBGRV9MRV9NQUdJQwkJMHhmZTAxMDFmZQorCisvKiBFcnJvciBjb2RlcyAq LworI2RlZmluZSBFUlJfSU5WQUxJRF9NQUdJQwktMQorI2RlZmluZSBFUlJfSU5WQUxJRF9TSVpF CS0yCisjZGVmaW5lIEVSUl9JTlZBTElEX0NIS1NVTQktMworCisvKiBNZXNzYWdlIHR5cGVzICov CisjZGVmaW5lIERQRkVfTVNHX1RZUEVfQ09NTUFORAkxCisjZGVmaW5lIERQRkVfTVNHX1RZUEVf UkVTUE9OU0UJMgorCisjZGVmaW5lIERFTEFZX0xPT1BfTUFYCQkyMDAwMDAKKworZW51bSBkcGZl X21zZ19maWVsZHMgeworCU1TR19IRUFERVIsCisJTVNHX0NPTU1BTkQsCisJTVNHX0FSR19DT1VO VCwKKwlNU0dfQVJHMCwKKwlNU0dfQ0hLU1VNLAorCU1TR19GSUVMRF9NQVggLyogTGFzdCBlbnRy eSAqLworfTsKKworZW51bSBkcGZlX2NvbW1hbmRzIHsKKwlEUEZFX0NNRF9HRVRfSU5GTywKKwlE UEZFX0NNRF9HRVRfUkVGUkVTSCwKKwlEUEZFX0NNRF9HRVRfVkVORE9SLAorCURQRkVfQ01EX01B WCAvKiBMYXN0IGVudHJ5ICovCit9OworCitzdHJ1Y3QgZHBmZV9tc2cgeworCXUzMiBoZWFkZXI7 CisJdTMyIGNvbW1hbmQ7CisJdTMyIGFyZ19jb3VudDsKKwl1MzIgYXJnMDsKKwl1MzIgY2hrc3Vt OyAvKiBUaGlzIGlzIHRoZSBzdW0gb2YgYWxsIG90aGVyIGVudHJpZXMuICovCit9OworCisvKgor ICogRm9ybWF0IG9mIHRoZSBiaW5hcnkgZmlybXdhcmUgZmlsZToKKyAqCisgKiAgIGVudHJ5Cisg KiAgICAgIDAgICAgaGVhZGVyCisgKiAgICAgICAgICAgICAgdmFsdWU6ICAweGZlMDEwMWZlICA8 PT0gbGl0dGxlIGVuZGlhbgorICogICAgICAgICAgICAgICAgICAgICAgMHhmZTEwMTBmZSAgPD09 IGJpZyBlbmRpYW4KKyAqICAgICAgMSAgICBzZXF1ZW5jZToKKyAqICAgICAgICAgICAgICBbMzE6 MTZdIHRvdGFsIHNlZ21lbnRzIG9uIHRoaXMgYnVpbGQKKyAqICAgICAgICAgICAgICBbMTU6MF0g IHRoaXMgc2VnbWVudCBzZXF1ZW5jZS4KKyAqICAgICAgMiAgICBGVyB2ZXJzaW9uCisgKiAgICAg IDMgICAgSU1FTSBieXRlIHNpemUKKyAqICAgICAgNCAgICBETUVNIGJ5dGUgc2l6ZQorICogICAg ICAgICAgIElNRU0KKyAqICAgICAgICAgICBETUVNCisgKiAgICAgIGxhc3QgY2hlY2tzdW0gPT0+ IHN1bSBvZiBldmVyeXRoaW5nCisgKi8KK3N0cnVjdCBkcGZlX2Zpcm13YXJlX2hlYWRlciB7CisJ dTMyIG1hZ2ljOworCXUzMiBzZXF1ZW5jZTsKKwl1MzIgdmVyc2lvbjsKKwl1MzIgaW1lbV9zaXpl OworCXUzMiBkbWVtX3NpemU7Cit9OworCisvKiBUaGluZ3Mgd2Ugb25seSBuZWVkIGR1cmluZyBp bml0aWFsaXphdGlvbi4gKi8KK3N0cnVjdCBpbml0X2RhdGEgeworCXVuc2lnbmVkIGludCBkbWVt X2xlbjsKKwl1bnNpZ25lZCBpbnQgaW1lbV9sZW47CisJdW5zaWduZWQgaW50IGNoa3N1bTsKKwli b29sIGlzX2JpZ19lbmRpYW47Cit9OworCisvKiBUaGluZ3Mgd2UgbmVlZCBmb3IgYXMgbG9uZyBh cyB3ZSBhcmUgYWN0aXZlLiAqLworc3RydWN0IHByaXZhdGVfZGF0YSB7CisJdm9pZCBfX2lvbWVt ICpyZWdzOworCXZvaWQgX19pb21lbSAqZG1lbTsKKwl2b2lkIF9faW9tZW0gKmltZW07CisJc3Ry dWN0IGRldmljZSAqZGV2OworCXVuc2lnbmVkIGludCBpbmRleDsKKwlzdHJ1Y3QgbXV0ZXggbG9j azsKK307CisKK3N0YXRpYyBjb25zdCBjaGFyICplcnJvcl90ZXh0W10gPSB7CisJIlN1Y2Nlc3Mi LCAiSGVhZGVyIGNvZGUgaW5jb3JyZWN0IiwgIlVua25vd24gY29tbWFuZCBvciBhcmd1bWVudCIs CisJIkluY29ycmVjdCBjaGVja3N1bSIsICJNYWxmb3JtZWQgY29tbWFuZCIsICJUaW1lZCBvdXQi LAorfTsKKworLyogTGlzdCBvZiBzdXBwb3J0ZWQgZmlybXdhcmUgY29tbWFuZHMgKi8KK3N0YXRp YyBjb25zdCB1MzIgZHBmZV9jb21tYW5kc1tEUEZFX0NNRF9NQVhdW01TR19GSUVMRF9NQVhdID0g eworCVtEUEZFX0NNRF9HRVRfSU5GT10gPSB7CisJCVtNU0dfSEVBREVSXSA9IERQRkVfTVNHX1RZ UEVfQ09NTUFORCwKKwkJW01TR19DT01NQU5EXSA9IDEsCisJCVtNU0dfQVJHX0NPVU5UXSA9IDEs CisJCVtNU0dfQVJHMF0gPSAxLAorCQlbTVNHX0NIS1NVTV0gPSA0LAorCX0sCisJW0RQRkVfQ01E X0dFVF9SRUZSRVNIXSA9IHsKKwkJW01TR19IRUFERVJdID0gRFBGRV9NU0dfVFlQRV9DT01NQU5E LAorCQlbTVNHX0NPTU1BTkRdID0gMiwKKwkJW01TR19BUkdfQ09VTlRdID0gMSwKKwkJW01TR19B UkcwXSA9IDEsCisJCVtNU0dfQ0hLU1VNXSA9IDUsCisJfSwKKwlbRFBGRV9DTURfR0VUX1ZFTkRP Ul0gPSB7CisJCVtNU0dfSEVBREVSXSA9IERQRkVfTVNHX1RZUEVfQ09NTUFORCwKKwkJW01TR19D T01NQU5EXSA9IDIsCisJCVtNU0dfQVJHX0NPVU5UXSA9IDEsCisJCVtNU0dfQVJHMF0gPSAyLAor CQlbTVNHX0NIS1NVTV0gPSA2LAorCX0sCit9OworCitzdGF0aWMgdm9pZCBfX2Rpc2FibGVfZGNw dSh2b2lkIF9faW9tZW0gKnJlZ3MpCit7CisJdTMyIHZhbDsKKworCS8qIENoZWNrIGlmIERDUFUg aXMgcnVubmluZyAqLworCXZhbCA9IHJlYWRsX3JlbGF4ZWQocmVncyArIFJFR19EQ1BVX1JFU0VU KTsKKwlpZiAoISh2YWwgJiBEQ1BVX1JFU0VUX01BU0spKSB7CisJCS8qIFB1dCBEQ1BVIGluIHJl c2V0ICovCisJCXZhbCB8PSAoMSA8PCBEQ1BVX1JFU0VUX1NISUZUKTsKKwkJd3JpdGVsX3JlbGF4 ZWQodmFsLCByZWdzICsgUkVHX0RDUFVfUkVTRVQpOworCX0KK30KKworc3RhdGljIHZvaWQgX19l bmFibGVfZGNwdSh2b2lkIF9faW9tZW0gKnJlZ3MpCit7CisJdTMyIHZhbDsKKworCS8qIENsZWFy IG1haWxib3ggcmVnaXN0ZXJzLiAqLworCXdyaXRlbF9yZWxheGVkKDAsIHJlZ3MgKyBSRUdfVE9f RENQVV9NQk9YKTsKKwl3cml0ZWxfcmVsYXhlZCgwLCByZWdzICsgUkVHX1RPX0hPU1RfTUJPWCk7 CisKKwkvKiBEaXNhYmxlIERDUFUgY2xvY2sgZ2F0aW5nICovCisJdmFsID0gcmVhZGxfcmVsYXhl ZChyZWdzICsgUkVHX0RDUFVfUkVTRVQpOworCXZhbCAmPSB+KDEgPDwgRENQVV9DTEtfRElTQUJM RV9TSElGVCk7CisJd3JpdGVsX3JlbGF4ZWQodmFsLCByZWdzICsgUkVHX0RDUFVfUkVTRVQpOwor CisJLyogVGFrZSBEQ1BVIG91dCBvZiByZXNldCAqLworCXZhbCA9IHJlYWRsX3JlbGF4ZWQocmVn cyArIFJFR19EQ1BVX1JFU0VUKTsKKwl2YWwgJj0gfigxIDw8IERDUFVfUkVTRVRfU0hJRlQpOwor CXdyaXRlbF9yZWxheGVkKHZhbCwgcmVncyArIFJFR19EQ1BVX1JFU0VUKTsKK30KKworc3RhdGlj IHVuc2lnbmVkIGludCBnZXRfbXNnX2Noa3N1bShjb25zdCB1MzIgbXNnW10pCit7CisJdW5zaWdu ZWQgaW50IHN1bSA9IDA7CisJdW5zaWduZWQgaW50IGk7CisKKwkvKiBEb24ndCBpbmNsdWRlIHRo ZSBsYXN0IGZpZWxkIGluIHRoZSBjaGVja3N1bS4gKi8KKwlmb3IgKGkgPSAwOyBpIDwgTVNHX0ZJ RUxEX01BWCAtIDE7IGkrKykKKwkJc3VtICs9IG1zZ1tpXTsKKworCXJldHVybiBzdW07Cit9CisK K3N0YXRpYyBpbnQgX19zZW5kX2NvbW1hbmQoc3RydWN0IHByaXZhdGVfZGF0YSAqcHJpdiwgdW5z aWduZWQgaW50IGNtZCwKKwkJCSAgdTMyIHJlc3VsdFtdKQoreworCWNvbnN0IHUzMiAqbXNnID0g ZHBmZV9jb21tYW5kc1tjbWRdOworCXZvaWQgX19pb21lbSAqcmVncyA9IHByaXYtPnJlZ3M7CisJ dW5zaWduZWQgaW50IGksIGNoa3N1bTsKKwlpbnQgcmV0ID0gMDsKKwl1MzIgcmVzcDsKKworCWlm IChjbWQgPj0gRFBGRV9DTURfTUFYKQorCQlyZXR1cm4gLTE7CisKKwltdXRleF9sb2NrKCZwcml2 LT5sb2NrKTsKKworCS8qIFdyaXRlIGNvbW1hbmQgYW5kIGFyZ3VtZW50cyB0byBtZXNzYWdlIGFy ZWEgKi8KKwlmb3IgKGkgPSAwOyBpIDwgTVNHX0ZJRUxEX01BWDsgaSsrKQorCQl3cml0ZWxfcmVs YXhlZChtc2dbaV0sIHJlZ3MgKyBEQ1BVX01TR19SQU0oaSkpOworCisJLyogVGVsbCBEQ1BVIHRo ZXJlIGlzIGEgY29tbWFuZCB3YWl0aW5nICovCisJd3JpdGVsX3JlbGF4ZWQoMSwgcmVncyArIFJF R19UT19EQ1BVX01CT1gpOworCisJLyogV2FpdCBmb3IgRENQVSB0byBwcm9jZXNzIHRoZSBjb21t YW5kICovCisJZm9yIChpID0gMDsgaSA8IERFTEFZX0xPT1BfTUFYOyBpKyspIHsKKwkJLyogUmVh ZCByZXNwb25zZSBjb2RlICovCisJCXJlc3AgPSByZWFkbF9yZWxheGVkKHJlZ3MgKyBSRUdfVE9f SE9TVF9NQk9YKTsKKwkJaWYgKHJlc3AgPiAwKQorCQkJYnJlYWs7CisJCXVkZWxheSg1KTsKKwl9 CisKKwlpZiAoaSA9PSBERUxBWV9MT09QX01BWCkgeworCQlyZXNwID0gKERDUFVfUkVUX0VSUl9U SU1FRE9VVCAmIH5EQ1BVX1JFVF9FUlJPUl9CSVQpOworCQlyZXQgPSAtZmZzKHJlc3ApOworCX0g ZWxzZSB7CisJCS8qIFJlYWQgcmVzcG9uc2UgZGF0YSAqLworCQlmb3IgKGkgPSAwOyBpIDwgTVNH X0ZJRUxEX01BWDsgaSsrKQorCQkJcmVzdWx0W2ldID0gcmVhZGxfcmVsYXhlZChyZWdzICsgRENQ VV9NU0dfUkFNKGkpKTsKKwl9CisKKwkvKiBUZWxsIERDUFUgd2UgYXJlIGRvbmUgKi8KKwl3cml0 ZWxfcmVsYXhlZCgwLCByZWdzICsgUkVHX1RPX0hPU1RfTUJPWCk7CisKKwltdXRleF91bmxvY2so JnByaXYtPmxvY2spOworCisJaWYgKHJldCkKKwkJcmV0dXJuIHJldDsKKworCS8qIFZlcmlmeSBy ZXNwb25zZSAqLworCWNoa3N1bSA9IGdldF9tc2dfY2hrc3VtKHJlc3VsdCk7CisJaWYgKGNoa3N1 bSAhPSByZXN1bHRbTVNHX0NIS1NVTV0pCisJCXJlc3AgPSBEQ1BVX1JFVF9FUlJfQ0hLU1VNOwor CisJaWYgKHJlc3AgIT0gRENQVV9SRVRfU1VDQ0VTUykgeworCQlyZXNwICY9IH5EQ1BVX1JFVF9F UlJPUl9CSVQ7CisJCXJldCA9IC1mZnMocmVzcCk7CisJfQorCisJcmV0dXJuIHJldDsKK30KKwor LyogRW5zdXJlIHRoYXQgdGhlIGZpcm13YXJlIGZpbGUgbG9hZGVkIG1lZXRzIGFsbCB0aGUgcmVx dWlyZW1lbnRzLiAqLworc3RhdGljIGludCBfX3ZlcmlmeV9maXJtd2FyZShzdHJ1Y3QgaW5pdF9k YXRhICppbml0LAorCQkJICAgICBjb25zdCBzdHJ1Y3QgZmlybXdhcmUgKmZ3KQoreworCWNvbnN0 IHN0cnVjdCBkcGZlX2Zpcm13YXJlX2hlYWRlciAqaGVhZGVyID0gKHZvaWQgKilmdy0+ZGF0YTsK Kwl1bnNpZ25lZCBpbnQgZG1lbV9zaXplLCBpbWVtX3NpemUsIHRvdGFsX3NpemU7CisJYm9vbCBp c19iaWdfZW5kaWFuID0gZmFsc2U7CisJY29uc3QgdTMyICpjaGtzdW1fcHRyOworCisJaWYgKGhl YWRlci0+bWFnaWMgPT0gRFBGRV9CRV9NQUdJQykKKwkJaXNfYmlnX2VuZGlhbiA9IHRydWU7CisJ ZWxzZSBpZiAoaGVhZGVyLT5tYWdpYyAhPSBEUEZFX0xFX01BR0lDKQorCQlyZXR1cm4gRVJSX0lO VkFMSURfTUFHSUM7CisKKwlpZiAoaXNfYmlnX2VuZGlhbikgeworCQlkbWVtX3NpemUgPSBiZTMy X3RvX2NwdShoZWFkZXItPmRtZW1fc2l6ZSk7CisJCWltZW1fc2l6ZSA9IGJlMzJfdG9fY3B1KGhl YWRlci0+aW1lbV9zaXplKTsKKwl9IGVsc2UgeworCQlkbWVtX3NpemUgPSBoZWFkZXItPmRtZW1f c2l6ZTsKKwkJaW1lbV9zaXplID0gaGVhZGVyLT5pbWVtX3NpemU7CisJfQorCisJLyogRGF0YSBh bmQgaW5zdHJ1Y3Rpb24gc2VjdGlvbnMgYXJlIDMyIGJpdCB3b3Jkcy4gKi8KKwlpZiAoKGRtZW1f c2l6ZSAlIHNpemVvZih1MzIpKSAhPSAwIHx8IChpbWVtX3NpemUgJSBzaXplb2YodTMyKSkgIT0g MCkKKwkJcmV0dXJuIEVSUl9JTlZBTElEX1NJWkU7CisKKwkvKgorCSAqIFRoZSBoZWFkZXIgKyB0 aGUgZGF0YSBzZWN0aW9uICsgdGhlIGluc3RydWN0aW9uIHNlY3Rpb24gKyB0aGUKKwkgKiBjaGVj a3N1bSBtdXN0IGJlIGVxdWFsIHRvIHRoZSB0b3RhbCBmaXJtd2FyZSBzaXplLgorCSAqLworCXRv dGFsX3NpemUgPSBkbWVtX3NpemUgKyBpbWVtX3NpemUgKyBzaXplb2YoKmhlYWRlcikgKworCQlz aXplb2YoKmNoa3N1bV9wdHIpOworCWlmICh0b3RhbF9zaXplICE9IGZ3LT5zaXplKQorCQlyZXR1 cm4gRVJSX0lOVkFMSURfU0laRTsKKworCS8qIFRoZSBjaGVja3N1bSBjb21lcyBhdCB0aGUgdmVy eSBlbmQuICovCisJY2hrc3VtX3B0ciA9ICh2b2lkICopZnctPmRhdGEgKyBzaXplb2YoKmhlYWRl cikgKyBkbWVtX3NpemUgKyBpbWVtX3NpemU7CisKKwlpbml0LT5pc19iaWdfZW5kaWFuID0gaXNf YmlnX2VuZGlhbjsKKwlpbml0LT5kbWVtX2xlbiA9IGRtZW1fc2l6ZTsKKwlpbml0LT5pbWVtX2xl biA9IGltZW1fc2l6ZTsKKwlpbml0LT5jaGtzdW0gPSAoaXNfYmlnX2VuZGlhbikgPyBiZTMyX3Rv X2NwdSgqY2hrc3VtX3B0cikgOiAqY2hrc3VtX3B0cjsKKworCXJldHVybiAwOworfQorCisvKiBW ZXJpZnkgY2hlY2tzdW0gYnkgcmVhZGluZyBiYWNrIHRoZSBmaXJtd2FyZSBmcm9tIGNvLXByb2Nl c3NvciBSQU0uICovCitzdGF0aWMgaW50IF9fdmVyaWZ5X2Z3X2NoZWNrc3VtKHN0cnVjdCBpbml0 X2RhdGEgKmluaXQsCisJCQkJc3RydWN0IHByaXZhdGVfZGF0YSAqcHJpdiwKKwkJCQljb25zdCBz dHJ1Y3QgZHBmZV9maXJtd2FyZV9oZWFkZXIgKmhlYWRlciwKKwkJCQl1MzIgY2hlY2tzdW0pCit7 CisJdTMyIG1hZ2ljLCBzZXF1ZW5jZSwgdmVyc2lvbiwgc3VtOworCXUzMiBfX2lvbWVtICpkbWVt ID0gcHJpdi0+ZG1lbTsKKwl1MzIgX19pb21lbSAqaW1lbSA9IHByaXYtPmltZW07CisJdW5zaWdu ZWQgaW50IGk7CisKKwlpZiAoaW5pdC0+aXNfYmlnX2VuZGlhbikgeworCQltYWdpYyA9IGJlMzJf dG9fY3B1KGhlYWRlci0+bWFnaWMpOworCQlzZXF1ZW5jZSA9IGJlMzJfdG9fY3B1KGhlYWRlci0+ c2VxdWVuY2UpOworCQl2ZXJzaW9uID0gYmUzMl90b19jcHUoaGVhZGVyLT52ZXJzaW9uKTsKKwl9 IGVsc2UgeworCQltYWdpYyA9IGhlYWRlci0+bWFnaWM7CisJCXNlcXVlbmNlID0gaGVhZGVyLT5z ZXF1ZW5jZTsKKwkJdmVyc2lvbiA9IGhlYWRlci0+dmVyc2lvbjsKKwl9CisKKwlzdW0gPSBtYWdp YyArIHNlcXVlbmNlICsgdmVyc2lvbiArIGluaXQtPmRtZW1fbGVuICsgaW5pdC0+aW1lbV9sZW47 CisKKwlmb3IgKGkgPSAwOyBpIDwgaW5pdC0+ZG1lbV9sZW4gLyBzaXplb2YodTMyKTsgaSsrKQor CQlzdW0gKz0gcmVhZGxfcmVsYXhlZChkbWVtICsgaSk7CisKKwlmb3IgKGkgPSAwOyBpIDwgaW5p dC0+aW1lbV9sZW4gLyBzaXplb2YodTMyKTsgaSsrKQorCQlzdW0gKz0gcmVhZGxfcmVsYXhlZChp bWVtICsgaSk7CisKKwlyZXR1cm4gKHN1bSA9PSBjaGVja3N1bSkgPyAwIDogLTE7Cit9CisKK3N0 YXRpYyBpbnQgX193cml0ZV9maXJtd2FyZSh1MzIgX19pb21lbSAqbWVtLCBjb25zdCB1MzIgKmZ3 LAorCQkJICAgIHVuc2lnbmVkIGludCBzaXplLCBib29sIGlzX2JpZ19lbmRpYW4pCit7CisJdW5z aWduZWQgaW50IGk7CisKKwkvKiBDb252ZXJ0IHNpemUgdG8gMzItYml0IHdvcmRzLiAqLworCXNp emUgLz0gc2l6ZW9mKHUzMik7CisKKwkvKiBJdCBpcyByZWNvbW1lbmRlZCB0byBjbGVhciB0aGUg ZmlybXdhcmUgYXJlYSBmaXJzdC4gKi8KKwlmb3IgKGkgPSAwOyBpIDwgc2l6ZTsgaSsrKQorCQl3 cml0ZWxfcmVsYXhlZCgwLCBtZW0gKyBpKTsKKworCS8qIE5vdyBjb3B5IGl0LiAqLworCWlmIChp c19iaWdfZW5kaWFuKSB7CisJCWZvciAoaSA9IDA7IGkgPCBzaXplOyBpKyspCisJCQl3cml0ZWxf cmVsYXhlZChiZTMyX3RvX2NwdShmd1tpXSksIG1lbSArIGkpOworCX0gZWxzZSB7CisJCWZvciAo aSA9IDA7IGkgPCBzaXplOyBpKyspCisJCQl3cml0ZWxfcmVsYXhlZChmd1tpXSwgbWVtICsgaSk7 CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgYnJjbXN0Yl9kcGZlX2Rvd25sb2Fk X2Zpcm13YXJlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYsCisJCQkJCSAgc3RydWN0IGlu aXRfZGF0YSAqaW5pdCkKK3sKKwljb25zdCBzdHJ1Y3QgZHBmZV9maXJtd2FyZV9oZWFkZXIgKmhl YWRlcjsKKwl1bnNpZ25lZCBpbnQgZG1lbV9zaXplLCBpbWVtX3NpemU7CisJc3RydWN0IGRldmlj ZSAqZGV2ID0gJnBkZXYtPmRldjsKKwlib29sIGlzX2JpZ19lbmRpYW4gPSBmYWxzZTsKKwlzdHJ1 Y3QgcHJpdmF0ZV9kYXRhICpwcml2OworCWNvbnN0IHN0cnVjdCBmaXJtd2FyZSAqZnc7CisJY29u c3QgdTMyICpkbWVtLCAqaW1lbTsKKwljb25zdCB2b2lkICpmd19ibG9iOworCWludCByZXQ7CisK KwlyZXQgPSByZXF1ZXN0X2Zpcm13YXJlKCZmdywgRklSTVdBUkVfTkFNRSwgZGV2KTsKKwkvKiBy ZXF1ZXN0X2Zpcm13YXJlKCkgcHJpbnRzIGl0cyBvd24gZXJyb3IgbWVzc2FnZXMuICovCisJaWYg KHJldCkKKwkJcmV0dXJuIHJldDsKKworCXByaXYgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2 KTsKKworCXJldCA9IF9fdmVyaWZ5X2Zpcm13YXJlKGluaXQsIGZ3KTsKKwlpZiAocmV0KQorCQly ZXR1cm4gLUVGQVVMVDsKKworCV9fZGlzYWJsZV9kY3B1KHByaXYtPnJlZ3MpOworCisJaXNfYmln X2VuZGlhbiA9IGluaXQtPmlzX2JpZ19lbmRpYW47CisJZG1lbV9zaXplID0gaW5pdC0+ZG1lbV9s ZW47CisJaW1lbV9zaXplID0gaW5pdC0+aW1lbV9sZW47CisKKwkvKiBBdCB0aGUgYmVnaW5uaW5n IG9mIHRoZSBmaXJtd2FyZSBibG9iIGlzIGEgaGVhZGVyLiAqLworCWhlYWRlciA9IChzdHJ1Y3Qg ZHBmZV9maXJtd2FyZV9oZWFkZXIgKilmdy0+ZGF0YTsKKwkvKiBWb2lkIHBvaW50ZXIgdG8gdGhl IGJlZ2lubmluZyBvZiB0aGUgYWN0dWFsIGZpcm13YXJlLiAqLworCWZ3X2Jsb2IgPSBmdy0+ZGF0 YSArIHNpemVvZigqaGVhZGVyKTsKKwkvKiBJTUVNIGNvbWVzIHJpZ2h0IGFmdGVyIHRoZSBoZWFk ZXIuICovCisJaW1lbSA9IGZ3X2Jsb2I7CisJLyogRE1FTSBmb2xsb3dzIGFmdGVyIElNRU0uICov CisJZG1lbSA9IGZ3X2Jsb2IgKyBpbWVtX3NpemU7CisKKwlyZXQgPSBfX3dyaXRlX2Zpcm13YXJl KHByaXYtPmRtZW0sIGRtZW0sIGRtZW1fc2l6ZSwgaXNfYmlnX2VuZGlhbik7CisJaWYgKHJldCkK KwkJcmV0dXJuIHJldDsKKwlyZXQgPSBfX3dyaXRlX2Zpcm13YXJlKHByaXYtPmltZW0sIGltZW0s IGltZW1fc2l6ZSwgaXNfYmlnX2VuZGlhbik7CisJaWYgKHJldCkKKwkJcmV0dXJuIHJldDsKKwor CXJldCA9IF9fdmVyaWZ5X2Z3X2NoZWNrc3VtKGluaXQsIHByaXYsIGhlYWRlciwgaW5pdC0+Y2hr c3VtKTsKKwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCisJX19lbmFibGVfZGNwdShwcml2LT5y ZWdzKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgc3NpemVfdCBnZW5lcmljX3Nob3codW5z aWduZWQgaW50IGNvbW1hbmQsIHUzMiByZXNwb25zZVtdLAorCQkJICAgIHN0cnVjdCBkZXZpY2Ug KmRldiwgY2hhciAqYnVmKQoreworCXN0cnVjdCBwcml2YXRlX2RhdGEgKnByaXY7CisJaW50IHJl dDsKKworCXByaXYgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlpZiAoIXByaXYpCisJCXJldHVy biBzcHJpbnRmKGJ1ZiwgIkVSUk9SOiBkcml2ZXIgcHJpdmF0ZSBkYXRhIG5vdCBzZXRcbiIpOwor CisJcmV0ID0gX19zZW5kX2NvbW1hbmQocHJpdiwgY29tbWFuZCwgcmVzcG9uc2UpOworCWlmIChy ZXQgPCAwKQorCQlyZXR1cm4gc3ByaW50ZihidWYsICJFUlJPUjogJXNcbiIsIGVycm9yX3RleHRb LXJldF0pOworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBzc2l6ZV90IHNob3dfaW5mbyhzdHJ1 Y3QgZGV2aWNlICpkZXYsIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICpkZXZhdHRyLAorCQkJIGNo YXIgKmJ1ZikKK3sKKwl1MzIgcmVzcG9uc2VbTVNHX0ZJRUxEX01BWF07CisJdW5zaWduZWQgaW50 IGluZm87CisJaW50IHJldDsKKworCXJldCA9IGdlbmVyaWNfc2hvdyhEUEZFX0NNRF9HRVRfSU5G TywgcmVzcG9uc2UsIGRldiwgYnVmKTsKKwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCisJaW5m byA9IHJlc3BvbnNlW01TR19BUkcwXTsKKworCXJldHVybiBzcHJpbnRmKGJ1ZiwgIiV1LiV1LiV1 LiV1XG4iLAorCQkgICAgICAgKGluZm8gPj4gMjQpICYgMHhmZiwKKwkJICAgICAgIChpbmZvID4+ IDE2KSAmIDB4ZmYsCisJCSAgICAgICAoaW5mbyA+PiA4KSAmIDB4ZmYsCisJCSAgICAgICBpbmZv ICYgMHhmZik7Cit9CisKK3N0YXRpYyBzc2l6ZV90IHNob3dfcmVmcmVzaChzdHJ1Y3QgZGV2aWNl ICpkZXYsCisJCQkgICAgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmRldmF0dHIsIGNoYXIgKmJ1 ZikKK3sKKwl1MzIgcmVzcG9uc2VbTVNHX0ZJRUxEX01BWF07CisJdm9pZCBfX2lvbWVtICppbmZv OworCXN0cnVjdCBwcml2YXRlX2RhdGEgKnByaXY7CisJdW5zaWduZWQgaW50IG9mZnNldDsKKwl1 OCByZWZyZXNoLCBzcl9hYm9ydCwgcHByZSwgdGhlcm1hbF9vZmZzLCB0dWY7CisJdTMyIG1yNDsK KwlpbnQgcmV0OworCisJcmV0ID0gZ2VuZXJpY19zaG93KERQRkVfQ01EX0dFVF9SRUZSRVNILCBy ZXNwb25zZSwgZGV2LCBidWYpOworCWlmIChyZXQpCisJCXJldHVybiByZXQ7CisKKwlwcml2ID0g ZGV2X2dldF9kcnZkYXRhKGRldik7CisJb2Zmc2V0ID0gcmVzcG9uc2VbTVNHX0FSRzBdOworCWlu Zm8gPSBwcml2LT5kbWVtICsgb2Zmc2V0OworCisJbXI0ID0gcmVhZGxfcmVsYXhlZChpbmZvICsg RFJBTV9JTkZPX01SNCkgJiBEUkFNX0lORk9fTVI0X01BU0s7CisKKwlyZWZyZXNoID0gKG1yNCA+ PiBEUkFNX01SNF9SRUZSRVNIKSAmIERSQU1fTVI0X1JFRlJFU0hfTUFTSzsKKwlzcl9hYm9ydCA9 IChtcjQgPj4gRFJBTV9NUjRfU1JfQUJPUlQpICYgRFJBTV9NUjRfU1JfQUJPUlRfTUFTSzsKKwlw cHJlID0gKG1yNCA+PiBEUkFNX01SNF9QUFJFKSAmIERSQU1fTVI0X1BQUkVfTUFTSzsKKwl0aGVy bWFsX29mZnMgPSAobXI0ID4+IERSQU1fTVI0X1RIX09GRlMpICYgRFJBTV9NUjRfVEhfT0ZGU19N QVNLOworCXR1ZiA9IChtcjQgPj4gRFJBTV9NUjRfVFVGKSAmIERSQU1fTVI0X1RVRl9NQVNLOwor CisJcmV0dXJuIHNwcmludGYoYnVmLCAiJSN4ICUjeCAlI3ggJSN4ICUjeCAlI3ggJSN4XG4iLAor CQkgICAgICAgcmVhZGxfcmVsYXhlZChpbmZvICsgRFJBTV9JTkZPX0lOVEVSVkFMKSwKKwkJICAg ICAgIHJlZnJlc2gsIHNyX2Fib3J0LCBwcHJlLCB0aGVybWFsX29mZnMsIHR1ZiwKKwkJICAgICAg IHJlYWRsX3JlbGF4ZWQoaW5mbyArIERSQU1fSU5GT19FUlJPUikpOworfQorCitzdGF0aWMgc3Np emVfdCBzdG9yZV9yZWZyZXNoKHN0cnVjdCBkZXZpY2UgKmRldiwgc3RydWN0IGRldmljZV9hdHRy aWJ1dGUgKmF0dHIsCisJCQkgIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQoreworCXUz MiByZXNwb25zZVtNU0dfRklFTERfTUFYXTsKKwlzdHJ1Y3QgcHJpdmF0ZV9kYXRhICpwcml2Owor CXZvaWQgX19pb21lbSAqaW5mbzsKKwl1bnNpZ25lZCBpbnQgb2Zmc2V0OworCXVuc2lnbmVkIGxv bmcgdmFsOworCWludCByZXQ7CisKKwlpZiAoa3N0cnRvdWwoYnVmLCAwLCAmdmFsKSA8IDApCisJ CXJldHVybiAtRUlOVkFMOworCisJcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCisJcmV0 ID0gX19zZW5kX2NvbW1hbmQocHJpdiwgRFBGRV9DTURfR0VUX1JFRlJFU0gsIHJlc3BvbnNlKTsK KwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCisJb2Zmc2V0ID0gcmVzcG9uc2VbTVNHX0FSRzBd OworCWluZm8gPSBwcml2LT5kbWVtICsgb2Zmc2V0OworCXdyaXRlbF9yZWxheGVkKHZhbCwgaW5m byArIERSQU1fSU5GT19JTlRFUlZBTCk7CisKKwlyZXR1cm4gY291bnQ7Cit9CisKK3N0YXRpYyBz c2l6ZV90IHNob3dfdmVuZG9yKHN0cnVjdCBkZXZpY2UgKmRldiwgc3RydWN0IGRldmljZV9hdHRy aWJ1dGUgKmRldmF0dHIsCisJCQkgY2hhciAqYnVmKQoreworCXUzMiByZXNwb25zZVtNU0dfRklF TERfTUFYXTsKKwlzdHJ1Y3QgcHJpdmF0ZV9kYXRhICpwcml2OworCXZvaWQgX19pb21lbSAqaW5m bzsKKwl1bnNpZ25lZCBpbnQgb2Zmc2V0OworCWludCByZXQ7CisKKwlyZXQgPSBnZW5lcmljX3No b3coRFBGRV9DTURfR0VUX1ZFTkRPUiwgcmVzcG9uc2UsIGRldiwgYnVmKTsKKwlpZiAocmV0KQor CQlyZXR1cm4gcmV0OworCisJb2Zmc2V0ID0gcmVzcG9uc2VbTVNHX0FSRzBdOworCXByaXYgPSBk ZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlpbmZvID0gcHJpdi0+ZG1lbSArIG9mZnNldDsKKworCXJl dHVybiBzcHJpbnRmKGJ1ZiwgIiUjeCAlI3ggJSN4ICUjeCAlI3hcbiIsCisJCSAgICAgICByZWFk bF9yZWxheGVkKGluZm8gKyBEUkFNX1ZFTkRPUl9NUjUpICYgRFJBTV9WRU5ET1JfTUFTSywKKwkJ ICAgICAgIHJlYWRsX3JlbGF4ZWQoaW5mbyArIERSQU1fVkVORE9SX01SNikgJiBEUkFNX1ZFTkRP Ul9NQVNLLAorCQkgICAgICAgcmVhZGxfcmVsYXhlZChpbmZvICsgRFJBTV9WRU5ET1JfTVI3KSAm IERSQU1fVkVORE9SX01BU0ssCisJCSAgICAgICByZWFkbF9yZWxheGVkKGluZm8gKyBEUkFNX1ZF TkRPUl9NUjgpICYgRFJBTV9WRU5ET1JfTUFTSywKKwkJICAgICAgIHJlYWRsX3JlbGF4ZWQoaW5m byArIERSQU1fVkVORE9SX0VSUk9SKSk7Cit9CisKK3N0YXRpYyBpbnQgYnJjbXN0Yl9kcGZlX3Jl c3VtZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBpbml0X2RhdGEg aW5pdDsKKworCXJldHVybiBicmNtc3RiX2RwZmVfZG93bmxvYWRfZmlybXdhcmUocGRldiwgJmlu aXQpOworfQorCitzdGF0aWMgREVWSUNFX0FUVFIoZHBmZV9pbmZvLCAwNDQ0LCBzaG93X2luZm8s IE5VTEwpOworc3RhdGljIERFVklDRV9BVFRSKGRwZmVfcmVmcmVzaCwgMDY0NCwgc2hvd19yZWZy ZXNoLCBzdG9yZV9yZWZyZXNoKTsKK3N0YXRpYyBERVZJQ0VfQVRUUihkcGZlX3ZlbmRvciwgMDQ0 NCwgc2hvd192ZW5kb3IsIE5VTEwpOworc3RhdGljIHN0cnVjdCBhdHRyaWJ1dGUgKmRwZmVfYXR0 cnNbXSA9IHsKKwkmZGV2X2F0dHJfZHBmZV9pbmZvLmF0dHIsCisJJmRldl9hdHRyX2RwZmVfcmVm cmVzaC5hdHRyLAorCSZkZXZfYXR0cl9kcGZlX3ZlbmRvci5hdHRyLAorCU5VTEwKK307CitBVFRS SUJVVEVfR1JPVVBTKGRwZmUpOworCitzdGF0aWMgaW50IGJyY21zdGJfZHBmZV9wcm9iZShzdHJ1 Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBkZXZpY2UgKmRldiA9ICZwZGV2 LT5kZXY7CisJc3RydWN0IHByaXZhdGVfZGF0YSAqcHJpdjsKKwlzdHJ1Y3QgZGV2aWNlICpkcGZl X2RldjsKKwlzdHJ1Y3QgaW5pdF9kYXRhIGluaXQ7CisJc3RydWN0IHJlc291cmNlICpyZXM7CisJ dTMyIGluZGV4OworCWludCByZXQ7CisKKwlwcml2ID0gZGV2bV9remFsbG9jKGRldiwgc2l6ZW9m KCpwcml2KSwgR0ZQX0tFUk5FTCk7CisJaWYgKCFwcml2KQorCQlyZXR1cm4gLUVOT01FTTsKKwor CW11dGV4X2luaXQoJnByaXYtPmxvY2spOworCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIHBy aXYpOworCisJLyogQ2VsbCBpbmRleCBpcyBvcHRpb25hbDsgZGVmYXVsdCB0byAwIGlmIG5vdCBw cmVzZW50LiAqLworCXJldCA9IG9mX3Byb3BlcnR5X3JlYWRfdTMyKGRldi0+b2Zfbm9kZSwgImNl bGwtaW5kZXgiLCAmaW5kZXgpOworCWlmIChyZXQpCisJCWluZGV4ID0gMDsKKworCXJlcyA9IHBs YXRmb3JtX2dldF9yZXNvdXJjZV9ieW5hbWUocGRldiwgSU9SRVNPVVJDRV9NRU0sICJkcGZlLWNw dSIpOworCXByaXYtPnJlZ3MgPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCByZXMpOworCWlm IChJU19FUlIocHJpdi0+cmVncykpIHsKKwkJZGV2X2VycihkZXYsICJjb3VsZG4ndCBtYXAgRENQ VSByZWdpc3RlcnNcbiIpOworCQlyZXR1cm4gLUVOT0RFVjsKKwl9CisKKwlyZXMgPSBwbGF0Zm9y bV9nZXRfcmVzb3VyY2VfYnluYW1lKHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAiZHBmZS1kbWVtIik7 CisJcHJpdi0+ZG1lbSA9IGRldm1faW9yZW1hcF9yZXNvdXJjZShkZXYsIHJlcyk7CisJaWYgKElT X0VSUihwcml2LT5kbWVtKSkgeworCQlkZXZfZXJyKGRldiwgIkNvdWxkbid0IG1hcCBEQ1BVIGRh dGEgbWVtb3J5XG4iKTsKKwkJcmV0dXJuIC1FTk9FTlQ7CisJfQorCisJcmVzID0gcGxhdGZvcm1f Z2V0X3Jlc291cmNlX2J5bmFtZShwZGV2LCBJT1JFU09VUkNFX01FTSwgImRwZmUtaW1lbSIpOwor CXByaXYtPmltZW0gPSBkZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCByZXMpOworCWlmIChJU19F UlIocHJpdi0+aW1lbSkpIHsKKwkJZGV2X2VycihkZXYsICJDb3VsZG4ndCBtYXAgRENQVSBpbnN0 cnVjdGlvbiBtZW1vcnlcbiIpOworCQlyZXR1cm4gLUVOT0VOVDsKKwl9CisKKwlyZXQgPSBicmNt c3RiX2RwZmVfZG93bmxvYWRfZmlybXdhcmUocGRldiwgJmluaXQpOworCWlmIChyZXQpCisJCWdv dG8gZXJyOworCisJZHBmZV9kZXYgPSBkZXZtX2t6YWxsb2MoZGV2LCBzaXplb2YoKmRwZmVfZGV2 KSwgR0ZQX0tFUk5FTCk7CisJaWYgKCFkcGZlX2RldikgeworCQlyZXQgPSAtRU5PTUVNOworCQln b3RvIGVycjsKKwl9CisKKwlwcml2LT5kZXYgPSBkcGZlX2RldjsKKwlwcml2LT5pbmRleCA9IGlu ZGV4OworCisJZHBmZV9kZXYtPnBhcmVudCA9IGRldjsKKwlkcGZlX2Rldi0+Z3JvdXBzID0gZHBm ZV9ncm91cHM7CisJZHBmZV9kZXYtPm9mX25vZGUgPSBkZXYtPm9mX25vZGU7CisJZGV2X3NldF9k cnZkYXRhKGRwZmVfZGV2LCBwcml2KTsKKwlkZXZfc2V0X25hbWUoZHBmZV9kZXYsICJkcGZlJXUi LCBpbmRleCk7CisKKwlyZXQgPSBkZXZpY2VfcmVnaXN0ZXIoZHBmZV9kZXYpOworCWlmIChyZXQp CisJCWdvdG8gZXJyOworCisJZGV2X2luZm8oZGV2LCAicmVnaXN0ZXJlZC5cbiIpOworCisJcmV0 dXJuIDA7CisKK2VycjoKKwlkZXZfZXJyKGRldiwgImZhaWxlZCB0byBpbml0aWFsaXplIC0tIGVy cm9yICVkXG4iLCByZXQpOworCisJcmV0dXJuIHJldDsKK30KKworc3RhdGljIGNvbnN0IHN0cnVj dCBvZl9kZXZpY2VfaWQgYnJjbXN0Yl9kcGZlX29mX21hdGNoW10gPSB7CisJeyAuY29tcGF0aWJs ZSA9ICJicmNtLGRwZmUtY3B1IiwgfSwKKwl7fQorfTsKK01PRFVMRV9ERVZJQ0VfVEFCTEUob2Ys IGJyY21zdGJfZHBmZV9vZl9tYXRjaCk7CisKK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVy IGJyY21zdGJfZHBmZV9kcml2ZXIgPSB7CisJLmRyaXZlcgk9IHsKKwkJLm5hbWUgPSBEUlZOQU1F LAorCQkub2ZfbWF0Y2hfdGFibGUgPSBicmNtc3RiX2RwZmVfb2ZfbWF0Y2gsCisJfSwKKwkucHJv YmUgPSBicmNtc3RiX2RwZmVfcHJvYmUsCisJLnJlc3VtZSA9IGJyY21zdGJfZHBmZV9yZXN1bWUs Cit9OworCittb2R1bGVfcGxhdGZvcm1fZHJpdmVyKGJyY21zdGJfZHBmZV9kcml2ZXIpOworCitN T0RVTEVfQVVUSE9SKCJNYXJrdXMgTWF5ZXIgPG1tYXllckBicm9hZGNvbS5jb20+Iik7CitNT0RV TEVfREVTQ1JJUFRJT04oIkJSQ01TVEIgRERSIFBIWSBGcm9udCBFbmQgRHJpdmVyIik7CitNT0RV TEVfTElDRU5TRSgiR1BMIik7Ci0tIAoyLjcuNAoKCl9fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fCmxpbnV4LWFybS1rZXJuZWwgbWFpbGluZyBsaXN0CmxpbnV4 LWFybS1rZXJuZWxAbGlzdHMuaW5mcmFkZWFkLm9yZwpodHRwOi8vbGlzdHMuaW5mcmFkZWFkLm9y Zy9tYWlsbWFuL2xpc3RpbmZvL2xpbnV4LWFybS1rZXJuZWwK From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753930AbdFMWp5 (ORCPT ); Tue, 13 Jun 2017 18:45:57 -0400 Received: from smtp-out-no.shaw.ca ([64.59.134.12]:56801 "EHLO smtp-out-no.shaw.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753870AbdFMWpy (ORCPT ); Tue, 13 Jun 2017 18:45:54 -0400 X-Greylist: delayed 487 seconds by postgrey-1.27 at vger.kernel.org; Tue, 13 Jun 2017 18:45:44 EDT X-Authority-Analysis: v=2.2 cv=dZbw5Tfe c=1 sm=1 tr=0 a=k5HOQ6ZN7M0zyjl8M7O1NA==:117 a=k5HOQ6ZN7M0zyjl8M7O1NA==:17 a=IkcTkHD0fZMA:10 a=LWSFodeU3zMA:10 a=Q-fNiiVtAAAA:8 a=JfrnYn6hAAAA:8 a=pGLkceISAAAA:8 a=VwQbUJbxAAAA:8 a=XdqdjIA2AOjW5yAAf9MA:9 a=eEZXcbWszHXFlvcU:21 a=AXxNbyMwbLUZLCIS:21 a=QEXdDO2ut3YA:10 a=Fp8MccfUoT0GBdDC_Lng:22 a=1CNFftbPRP8L7MoqJWF3:22 a=6kGIvZw6iX1k4Y-7sg4_:22 a=AjGcO6oz07-iQ99wixmX:22 From: Markus Mayer To: Rob Herring , Mark Rutland , Florian Fainelli , Gregory Fong Cc: Markus Mayer , Broadcom Kernel List , Device Tree List , ARM Kernel List , Linux Kernel Mailing List Subject: [PATCH v2 2/2] soc: brcmstb: Add driver for DPFE Date: Tue, 13 Jun 2017 15:37:10 -0700 Message-Id: <20170613223710.31719-3-code@mmayer.net> X-Mailer: git-send-email 2.13.0 In-Reply-To: <20170613223710.31719-1-code@mmayer.net> References: <20170613223710.31719-1-code@mmayer.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-CMAE-Envelope: MS4wfEpfetG4U5ZAbGrc9lqJIH2oZC7lWR9qfP+HCDseH10yahJcIoS+pRIXaeLOaiA2QxCuDAuVjYBp/W04RnmO0VGbLJXKRcu+HQ83O5WdDdHttUBvvD/Z 5sDUpmxIKg8/MlT++Ch2aNX7NULttCDL/4a6pcudl7ZuIpBJdHXQTbUNKYOlhQMk+zPEx4r3AXrokwJEH1h/q5SrjG/IU2IgisHuYVmEMi8EgimlSdWcOYPH 5x0xLvJ8MlVy8y6VGKDaX6ugze92XXuj8O7C8FAVUaadP+kRv5PbvK2PGD39TrIkbCHNqqTwel7nv6stnMhMhTa/mQUiABhSBH/K88gxiCQjEYiWKIW4jKxH 1eT3NR08OnzdytpBW6nMmbQ7UV7yxQ8bvN1elwg+YvORqxQHhwcDRVqLkEpfTvPP9TLyjW8VkTIEErxtBTz1JUll7v9EJ0WkR5JQxLs5N57xVhduJbPNjOUC oiOCMOk0LvmLEYnM Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Markus Mayer This driver allows access to DRAM properties, such as the refresh rate, via the Broadcom STB DDR PHY Front End (DPFE). The refresh rate can be used as indirect indicator of the DRAM temperature. The driver also allows setting of the sampling interval. Signed-off-by: Markus Mayer --- MAINTAINERS | 8 + drivers/soc/bcm/brcmstb/Makefile | 2 +- drivers/soc/bcm/brcmstb/dpfe.c | 689 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/bcm/brcmstb/dpfe.c diff --git a/MAINTAINERS b/MAINTAINERS index f7d568b..04f0be6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2864,6 +2864,14 @@ S: Maintained F: Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt F: drivers/cpufreq/brcmstb* +BROADCOM STB SOC DPFE DRIVER +M: Markus Mayer +M: bcm-kernel-feedback-list@broadcom.com +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/soc/bcm/brcm,dpfe-cpu.txt +F: drivers/misc/brcmstb-dpfe.c + BROADCOM SPECIFIC AMBA DRIVER (BCMA) M: Rafał Miłecki L: linux-wireless@vger.kernel.org diff --git a/drivers/soc/bcm/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile index 9120b27..3ec87fe 100644 --- a/drivers/soc/bcm/brcmstb/Makefile +++ b/drivers/soc/bcm/brcmstb/Makefile @@ -1 +1 @@ -obj-y += common.o biuctrl.o +obj-y += common.o biuctrl.o dpfe.o diff --git a/drivers/soc/bcm/brcmstb/dpfe.c b/drivers/soc/bcm/brcmstb/dpfe.c new file mode 100644 index 0000000..c2504ca --- /dev/null +++ b/drivers/soc/bcm/brcmstb/dpfe.c @@ -0,0 +1,689 @@ +/* + * DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs + * + * Copyright (c) 2017 Broadcom + * + * Released under the GPLv2 only. + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * This driver provides access to the DPFE interface of Broadcom STB SoCs. + * The firmware running on the DCPU inside the DDR PHY can provide current + * information about the system's RAM, for instance the DRAM refresh rate. + * This can be used as an indirect indicator for the DRAM's temperature. + * Slower refresh rate means cooler RAM, higher refresh rate means hotter + * RAM. + * + * Throughout the driver, we use readl_relaxed() and writel_relaxed(), which + * already contain the appropriate le32_to_cpu()/cpu_to_le32() calls. + */ + +#include +#include +#include +#include +#include +#include + +#define DRVNAME "brcmstb-dpfe" +#define FIRMWARE_NAME "dpfe.bin" + +/* DCPU register offsets */ +#define REG_DCPU_RESET 0x0 +#define REG_TO_DCPU_MBOX 0x10 +#define REG_TO_HOST_MBOX 0x14 + +/* Message RAM */ +#define DCPU_MSG_RAM(x) (0x100 + (x) * sizeof(u32)) + +/* DRAM Info Offsets & Masks */ +#define DRAM_INFO_INTERVAL 0x0 +#define DRAM_INFO_MR4 0x4 +#define DRAM_INFO_ERROR 0x8 +#define DRAM_INFO_MR4_MASK 0xff + +/* DRAM MR4 Offsets & Masks */ +#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */ +#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */ +#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */ +#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */ +#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */ + +#define DRAM_MR4_REFRESH_MASK 0x7 +#define DRAM_MR4_SR_ABORT_MASK 0x1 +#define DRAM_MR4_PPRE_MASK 0x1 +#define DRAM_MR4_TH_OFFS_MASK 0x3 +#define DRAM_MR4_TUF_MASK 0x1 + +/* DRAM Vendor Offsets & Masks */ +#define DRAM_VENDOR_MR5 0x0 +#define DRAM_VENDOR_MR6 0x4 +#define DRAM_VENDOR_MR7 0x8 +#define DRAM_VENDOR_MR8 0xc +#define DRAM_VENDOR_ERROR 0x10 +#define DRAM_VENDOR_MASK 0xff + +/* Reset register bits & masks */ +#define DCPU_RESET_SHIFT 0x0 +#define DCPU_RESET_MASK 0x1 +#define DCPU_CLK_DISABLE_SHIFT 0x2 + +/* DCPU return codes */ +#define DCPU_RET_ERROR_BIT BIT(31) +#define DCPU_RET_SUCCESS 0x1 +#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0)) +#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1)) +#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2)) +#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3)) +/* This error code is not firmware defined and only used in the driver. */ +#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4)) + +/* Firmware magic */ +#define DPFE_BE_MAGIC 0xfe1010fe +#define DPFE_LE_MAGIC 0xfe0101fe + +/* Error codes */ +#define ERR_INVALID_MAGIC -1 +#define ERR_INVALID_SIZE -2 +#define ERR_INVALID_CHKSUM -3 + +/* Message types */ +#define DPFE_MSG_TYPE_COMMAND 1 +#define DPFE_MSG_TYPE_RESPONSE 2 + +#define DELAY_LOOP_MAX 200000 + +enum dpfe_msg_fields { + MSG_HEADER, + MSG_COMMAND, + MSG_ARG_COUNT, + MSG_ARG0, + MSG_CHKSUM, + MSG_FIELD_MAX /* Last entry */ +}; + +enum dpfe_commands { + DPFE_CMD_GET_INFO, + DPFE_CMD_GET_REFRESH, + DPFE_CMD_GET_VENDOR, + DPFE_CMD_MAX /* Last entry */ +}; + +struct dpfe_msg { + u32 header; + u32 command; + u32 arg_count; + u32 arg0; + u32 chksum; /* This is the sum of all other entries. */ +}; + +/* + * Format of the binary firmware file: + * + * entry + * 0 header + * value: 0xfe0101fe <== little endian + * 0xfe1010fe <== big endian + * 1 sequence: + * [31:16] total segments on this build + * [15:0] this segment sequence. + * 2 FW version + * 3 IMEM byte size + * 4 DMEM byte size + * IMEM + * DMEM + * last checksum ==> sum of everything + */ +struct dpfe_firmware_header { + u32 magic; + u32 sequence; + u32 version; + u32 imem_size; + u32 dmem_size; +}; + +/* Things we only need during initialization. */ +struct init_data { + unsigned int dmem_len; + unsigned int imem_len; + unsigned int chksum; + bool is_big_endian; +}; + +/* Things we need for as long as we are active. */ +struct private_data { + void __iomem *regs; + void __iomem *dmem; + void __iomem *imem; + struct device *dev; + unsigned int index; + struct mutex lock; +}; + +static const char *error_text[] = { + "Success", "Header code incorrect", "Unknown command or argument", + "Incorrect checksum", "Malformed command", "Timed out", +}; + +/* List of supported firmware commands */ +static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = { + [DPFE_CMD_GET_INFO] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 1, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 1, + [MSG_CHKSUM] = 4, + }, + [DPFE_CMD_GET_REFRESH] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 2, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 1, + [MSG_CHKSUM] = 5, + }, + [DPFE_CMD_GET_VENDOR] = { + [MSG_HEADER] = DPFE_MSG_TYPE_COMMAND, + [MSG_COMMAND] = 2, + [MSG_ARG_COUNT] = 1, + [MSG_ARG0] = 2, + [MSG_CHKSUM] = 6, + }, +}; + +static void __disable_dcpu(void __iomem *regs) +{ + u32 val; + + /* Check if DCPU is running */ + val = readl_relaxed(regs + REG_DCPU_RESET); + if (!(val & DCPU_RESET_MASK)) { + /* Put DCPU in reset */ + val |= (1 << DCPU_RESET_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); + } +} + +static void __enable_dcpu(void __iomem *regs) +{ + u32 val; + + /* Clear mailbox registers. */ + writel_relaxed(0, regs + REG_TO_DCPU_MBOX); + writel_relaxed(0, regs + REG_TO_HOST_MBOX); + + /* Disable DCPU clock gating */ + val = readl_relaxed(regs + REG_DCPU_RESET); + val &= ~(1 << DCPU_CLK_DISABLE_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); + + /* Take DCPU out of reset */ + val = readl_relaxed(regs + REG_DCPU_RESET); + val &= ~(1 << DCPU_RESET_SHIFT); + writel_relaxed(val, regs + REG_DCPU_RESET); +} + +static unsigned int get_msg_chksum(const u32 msg[]) +{ + unsigned int sum = 0; + unsigned int i; + + /* Don't include the last field in the checksum. */ + for (i = 0; i < MSG_FIELD_MAX - 1; i++) + sum += msg[i]; + + return sum; +} + +static int __send_command(struct private_data *priv, unsigned int cmd, + u32 result[]) +{ + const u32 *msg = dpfe_commands[cmd]; + void __iomem *regs = priv->regs; + unsigned int i, chksum; + int ret = 0; + u32 resp; + + if (cmd >= DPFE_CMD_MAX) + return -1; + + mutex_lock(&priv->lock); + + /* Write command and arguments to message area */ + for (i = 0; i < MSG_FIELD_MAX; i++) + writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i)); + + /* Tell DCPU there is a command waiting */ + writel_relaxed(1, regs + REG_TO_DCPU_MBOX); + + /* Wait for DCPU to process the command */ + for (i = 0; i < DELAY_LOOP_MAX; i++) { + /* Read response code */ + resp = readl_relaxed(regs + REG_TO_HOST_MBOX); + if (resp > 0) + break; + udelay(5); + } + + if (i == DELAY_LOOP_MAX) { + resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT); + ret = -ffs(resp); + } else { + /* Read response data */ + for (i = 0; i < MSG_FIELD_MAX; i++) + result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i)); + } + + /* Tell DCPU we are done */ + writel_relaxed(0, regs + REG_TO_HOST_MBOX); + + mutex_unlock(&priv->lock); + + if (ret) + return ret; + + /* Verify response */ + chksum = get_msg_chksum(result); + if (chksum != result[MSG_CHKSUM]) + resp = DCPU_RET_ERR_CHKSUM; + + if (resp != DCPU_RET_SUCCESS) { + resp &= ~DCPU_RET_ERROR_BIT; + ret = -ffs(resp); + } + + return ret; +} + +/* Ensure that the firmware file loaded meets all the requirements. */ +static int __verify_firmware(struct init_data *init, + const struct firmware *fw) +{ + const struct dpfe_firmware_header *header = (void *)fw->data; + unsigned int dmem_size, imem_size, total_size; + bool is_big_endian = false; + const u32 *chksum_ptr; + + if (header->magic == DPFE_BE_MAGIC) + is_big_endian = true; + else if (header->magic != DPFE_LE_MAGIC) + return ERR_INVALID_MAGIC; + + if (is_big_endian) { + dmem_size = be32_to_cpu(header->dmem_size); + imem_size = be32_to_cpu(header->imem_size); + } else { + dmem_size = header->dmem_size; + imem_size = header->imem_size; + } + + /* Data and instruction sections are 32 bit words. */ + if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0) + return ERR_INVALID_SIZE; + + /* + * The header + the data section + the instruction section + the + * checksum must be equal to the total firmware size. + */ + total_size = dmem_size + imem_size + sizeof(*header) + + sizeof(*chksum_ptr); + if (total_size != fw->size) + return ERR_INVALID_SIZE; + + /* The checksum comes at the very end. */ + chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size; + + init->is_big_endian = is_big_endian; + init->dmem_len = dmem_size; + init->imem_len = imem_size; + init->chksum = (is_big_endian) ? be32_to_cpu(*chksum_ptr) : *chksum_ptr; + + return 0; +} + +/* Verify checksum by reading back the firmware from co-processor RAM. */ +static int __verify_fw_checksum(struct init_data *init, + struct private_data *priv, + const struct dpfe_firmware_header *header, + u32 checksum) +{ + u32 magic, sequence, version, sum; + u32 __iomem *dmem = priv->dmem; + u32 __iomem *imem = priv->imem; + unsigned int i; + + if (init->is_big_endian) { + magic = be32_to_cpu(header->magic); + sequence = be32_to_cpu(header->sequence); + version = be32_to_cpu(header->version); + } else { + magic = header->magic; + sequence = header->sequence; + version = header->version; + } + + sum = magic + sequence + version + init->dmem_len + init->imem_len; + + for (i = 0; i < init->dmem_len / sizeof(u32); i++) + sum += readl_relaxed(dmem + i); + + for (i = 0; i < init->imem_len / sizeof(u32); i++) + sum += readl_relaxed(imem + i); + + return (sum == checksum) ? 0 : -1; +} + +static int __write_firmware(u32 __iomem *mem, const u32 *fw, + unsigned int size, bool is_big_endian) +{ + unsigned int i; + + /* Convert size to 32-bit words. */ + size /= sizeof(u32); + + /* It is recommended to clear the firmware area first. */ + for (i = 0; i < size; i++) + writel_relaxed(0, mem + i); + + /* Now copy it. */ + if (is_big_endian) { + for (i = 0; i < size; i++) + writel_relaxed(be32_to_cpu(fw[i]), mem + i); + } else { + for (i = 0; i < size; i++) + writel_relaxed(fw[i], mem + i); + } + + return 0; +} + +static int brcmstb_dpfe_download_firmware(struct platform_device *pdev, + struct init_data *init) +{ + const struct dpfe_firmware_header *header; + unsigned int dmem_size, imem_size; + struct device *dev = &pdev->dev; + bool is_big_endian = false; + struct private_data *priv; + const struct firmware *fw; + const u32 *dmem, *imem; + const void *fw_blob; + int ret; + + ret = request_firmware(&fw, FIRMWARE_NAME, dev); + /* request_firmware() prints its own error messages. */ + if (ret) + return ret; + + priv = platform_get_drvdata(pdev); + + ret = __verify_firmware(init, fw); + if (ret) + return -EFAULT; + + __disable_dcpu(priv->regs); + + is_big_endian = init->is_big_endian; + dmem_size = init->dmem_len; + imem_size = init->imem_len; + + /* At the beginning of the firmware blob is a header. */ + header = (struct dpfe_firmware_header *)fw->data; + /* Void pointer to the beginning of the actual firmware. */ + fw_blob = fw->data + sizeof(*header); + /* IMEM comes right after the header. */ + imem = fw_blob; + /* DMEM follows after IMEM. */ + dmem = fw_blob + imem_size; + + ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian); + if (ret) + return ret; + ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian); + if (ret) + return ret; + + ret = __verify_fw_checksum(init, priv, header, init->chksum); + if (ret) + return ret; + + __enable_dcpu(priv->regs); + + return 0; +} + +static ssize_t generic_show(unsigned int command, u32 response[], + struct device *dev, char *buf) +{ + struct private_data *priv; + int ret; + + priv = dev_get_drvdata(dev); + if (!priv) + return sprintf(buf, "ERROR: driver private data not set\n"); + + ret = __send_command(priv, command, response); + if (ret < 0) + return sprintf(buf, "ERROR: %s\n", error_text[-ret]); + + return 0; +} + +static ssize_t show_info(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + u32 response[MSG_FIELD_MAX]; + unsigned int info; + int ret; + + ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf); + if (ret) + return ret; + + info = response[MSG_ARG0]; + + return sprintf(buf, "%u.%u.%u.%u\n", + (info >> 24) & 0xff, + (info >> 16) & 0xff, + (info >> 8) & 0xff, + info & 0xff); +} + +static ssize_t show_refresh(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 response[MSG_FIELD_MAX]; + void __iomem *info; + struct private_data *priv; + unsigned int offset; + u8 refresh, sr_abort, ppre, thermal_offs, tuf; + u32 mr4; + int ret; + + ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf); + if (ret) + return ret; + + priv = dev_get_drvdata(dev); + offset = response[MSG_ARG0]; + info = priv->dmem + offset; + + mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK; + + refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK; + sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK; + ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK; + thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK; + tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK; + + return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n", + readl_relaxed(info + DRAM_INFO_INTERVAL), + refresh, sr_abort, ppre, thermal_offs, tuf, + readl_relaxed(info + DRAM_INFO_ERROR)); +} + +static ssize_t store_refresh(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 response[MSG_FIELD_MAX]; + struct private_data *priv; + void __iomem *info; + unsigned int offset; + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + priv = dev_get_drvdata(dev); + + ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response); + if (ret) + return ret; + + offset = response[MSG_ARG0]; + info = priv->dmem + offset; + writel_relaxed(val, info + DRAM_INFO_INTERVAL); + + return count; +} + +static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + u32 response[MSG_FIELD_MAX]; + struct private_data *priv; + void __iomem *info; + unsigned int offset; + int ret; + + ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf); + if (ret) + return ret; + + offset = response[MSG_ARG0]; + priv = dev_get_drvdata(dev); + info = priv->dmem + offset; + + return sprintf(buf, "%#x %#x %#x %#x %#x\n", + readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK, + readl_relaxed(info + DRAM_VENDOR_ERROR)); +} + +static int brcmstb_dpfe_resume(struct platform_device *pdev) +{ + struct init_data init; + + return brcmstb_dpfe_download_firmware(pdev, &init); +} + +static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL); +static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh); +static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL); +static struct attribute *dpfe_attrs[] = { + &dev_attr_dpfe_info.attr, + &dev_attr_dpfe_refresh.attr, + &dev_attr_dpfe_vendor.attr, + NULL +}; +ATTRIBUTE_GROUPS(dpfe); + +static int brcmstb_dpfe_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct private_data *priv; + struct device *dpfe_dev; + struct init_data init; + struct resource *res; + u32 index; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + /* Cell index is optional; default to 0 if not present. */ + ret = of_property_read_u32(dev->of_node, "cell-index", &index); + if (ret) + index = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu"); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "couldn't map DCPU registers\n"); + return -ENODEV; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem"); + priv->dmem = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->dmem)) { + dev_err(dev, "Couldn't map DCPU data memory\n"); + return -ENOENT; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem"); + priv->imem = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->imem)) { + dev_err(dev, "Couldn't map DCPU instruction memory\n"); + return -ENOENT; + } + + ret = brcmstb_dpfe_download_firmware(pdev, &init); + if (ret) + goto err; + + dpfe_dev = devm_kzalloc(dev, sizeof(*dpfe_dev), GFP_KERNEL); + if (!dpfe_dev) { + ret = -ENOMEM; + goto err; + } + + priv->dev = dpfe_dev; + priv->index = index; + + dpfe_dev->parent = dev; + dpfe_dev->groups = dpfe_groups; + dpfe_dev->of_node = dev->of_node; + dev_set_drvdata(dpfe_dev, priv); + dev_set_name(dpfe_dev, "dpfe%u", index); + + ret = device_register(dpfe_dev); + if (ret) + goto err; + + dev_info(dev, "registered.\n"); + + return 0; + +err: + dev_err(dev, "failed to initialize -- error %d\n", ret); + + return ret; +} + +static const struct of_device_id brcmstb_dpfe_of_match[] = { + { .compatible = "brcm,dpfe-cpu", }, + {} +}; +MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match); + +static struct platform_driver brcmstb_dpfe_driver = { + .driver = { + .name = DRVNAME, + .of_match_table = brcmstb_dpfe_of_match, + }, + .probe = brcmstb_dpfe_probe, + .resume = brcmstb_dpfe_resume, +}; + +module_platform_driver(brcmstb_dpfe_driver); + +MODULE_AUTHOR("Markus Mayer "); +MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver"); +MODULE_LICENSE("GPL"); -- 2.7.4