]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
platform: tegra: nvadsp: ADSP OS load support
authorAjay Nandakumar <anandakumarm@nvidia.com>
Thu, 27 Feb 2014 13:33:43 +0000 (19:03 +0530)
committerBo Yan <byan@nvidia.com>
Tue, 22 Apr 2014 21:25:45 +0000 (14:25 -0700)
Adding support for ADSP OS loading with carveouts.

Bug 1351881

Change-Id: I371197a659421dba76de43630440445249a64813
Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com>
Reviewed-on: http://git-master/r/374838
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bo Yan <byan@nvidia.com>
Tested-by: Bo Yan <byan@nvidia.com>
drivers/platform/tegra/nvadsp/Makefile
drivers/platform/tegra/nvadsp/dev.c
drivers/platform/tegra/nvadsp/dev.h
drivers/platform/tegra/nvadsp/hwmailbox.c
drivers/platform/tegra/nvadsp/nvadsp_os.c [deleted file]
drivers/platform/tegra/nvadsp/os.c [new file with mode: 0644]
drivers/platform/tegra/nvadsp/os.h [moved from drivers/platform/tegra/nvadsp/nvadsp_os.h with 86% similarity]
include/linux/tegra_nvadsp.h
include/uapi/linux/tegra_nvadsp.h [deleted file]

index 455c43630a887b89e14d4dc0b199bb15b0e5fdb9..875915cac192840856c45c0d3d01983b8db53a75 100644 (file)
@@ -1,8 +1,8 @@
 GCOV_PROFILE := y
 
 ccflags-y := -Werror
-
-obj-y += dev.o nvadsp_os.o nvadsp_app.o \
+obj-y := nvadsp.o
+nvadsp-objs += dev.o os.o nvadsp_app.o \
         nvadsp_aram.o nvadsp_dram.o \
         nvadsp_cmsg_queue.o \
         nvadsp_shared_sema.o nvadsp_arb_sema.o \
index 1f660698f93a04d1398f0d36f8cd9e548ad48bf4..cab304393d1ff2a0ea0c869322849eef672c48cf 100644 (file)
 #include <linux/of_device.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/tegra_nvadsp.h>
+#include <linux/miscdevice.h>
 
 #include "dev.h"
+#include "os.h"
 
 status_t nvadsp_mbox_init(struct platform_device *pdev);
 
-static int nvadsp_open(struct inode *inode, struct file *filp)
-{
-       return 0;
-}
-
-static int nvadsp_release(struct inode *inode, struct file *filp)
-{
-       return 0;
-}
-
-static long nvadsp_ioctl(struct file *filp, unsigned int cmd,
-                        unsigned long arg)
-{
-       return 0;
-}
-
-static long nvadsp_compat_ioctl(struct file *filp, unsigned int cmd,
-                                unsigned long arg)
-{
-       return 0;
-}
-
-static const struct file_operations nvadsp_fops = {
-       .owner          = THIS_MODULE,
-       .open           = nvadsp_open,
-       .release        = nvadsp_release,
-       .unlocked_ioctl = nvadsp_ioctl,
-       .compat_ioctl   = nvadsp_compat_ioctl,
-};
-
 #ifdef CONFIG_PM_SLEEP
 static int nvadsp_suspend(struct device *dev)
 {
@@ -96,13 +70,14 @@ static const struct dev_pm_ops nvadsp_pm_ops = {
 static int nvadsp_probe(struct platform_device *pdev)
 {
        struct nvadsp_drv_data *drv_data;
-       struct resource *res = NULL;
        void __iomem *base = NULL;
        int ret = 0;
+       int iter;
+       struct device *dev = &pdev->dev;
 
-       dev_info(&pdev->dev, "in probe()...\n");
+       dev_info(dev, "in probe()...\n");
 
-       drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data),
+       drv_data = devm_kzalloc(dev, sizeof(*drv_data),
                                GFP_KERNEL);
        if (!drv_data) {
                dev_err(&pdev->dev, "Failed to allocate driver data");
@@ -110,29 +85,50 @@ static int nvadsp_probe(struct platform_device *pdev)
                goto err;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get AMISC resource\n");
-               ret = -EINVAL;
+       drv_data->base_regs =
+               devm_kzalloc(dev, sizeof(void *) * APE_MAX_REG,
+                                                       GFP_KERNEL);
+       if (!drv_data->base_regs) {
+               dev_err(dev, "Failed to allocate regs");
+               ret = -ENOMEM;
                goto err;
        }
 
-       base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(base)) {
-               dev_err(&pdev->dev, "Failed to remap AMISC resource\n");
-               ret = PTR_ERR(base);
-               goto err;
+       for (iter = 0; iter < APE_MAX_REG; iter++) {
+               struct resource *res = NULL;
+               res = platform_get_resource(pdev, IORESOURCE_MEM, iter);
+               if (!res) {
+                       dev_err(dev,
+                       "Failed to get resource with ID %d\n",
+                                                       iter);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               base = devm_ioremap_resource(dev, res);
+               if (IS_ERR(base)) {
+                       dev_err(dev,
+                               "Failed to remap AMISC resource\n");
+                       ret = PTR_ERR(base);
+                       goto err;
+               }
+               drv_data->base_regs[iter] = base;
+
+               adsp_add_load_mappings(res->start, base,
+                                               resource_size(res));
        }
-       drv_data->amisc_base = base;
 
        platform_set_drvdata(pdev, drv_data);
+       ret = nvadsp_os_probe(pdev);
+       if (ret)
+               goto err;
 
        ret = nvadsp_hwmbox_init(pdev);
        if (ret)
                goto err;
 
        ret = nvadsp_mbox_init(pdev);
- err:
+err:
        return ret;
 }
 
index b3318c9b86ab7b46ea2f4ea668b33a4de7c259c1..913ea9f297487e462dc20dba084f6a52fb2301fc 100644 (file)
 
 #include "hwmailbox.h"
 
-struct nvadsp_drv_data {
-       void __iomem *amisc_base;
+/*
+ * Note: These enums should be aligned to the regs mentioned in the
+ * device tree
+*/
+enum {
+       AMC,
+       AMISC,
+       APE_MAX_REG
+};
 
+struct nvadsp_drv_data {
+       void __iomem **base_regs;
        struct hwmbox_queue hwmbox_send_queue;
        int hwmbox_send_virq;
        int hwmbox_recv_virq;
index 36ff32e1ae3ae7644e9cffa183cf21ac881a2012..315789479963c98752dcf422bd7621e437a68d7e 100644 (file)
@@ -40,12 +40,12 @@ static atomic_t hwmbox_send_virq_count = ATOMIC_INIT(0);
 
 static inline u32 hwmbox_readl(u32 reg)
 {
-       return readl(nvadsp_drv_data->amisc_base + reg);
+       return readl(nvadsp_drv_data->base_regs[AMISC] + reg);
 }
 
 static inline void hwmbox_writel(u32 val, u32 reg)
 {
-       writel(val, nvadsp_drv_data->amisc_base + reg);
+       writel(val, nvadsp_drv_data->base_regs[AMISC] + reg);
 }
 
 static inline void hwmbox_enable_irq(int irq)
diff --git a/drivers/platform/tegra/nvadsp/nvadsp_os.c b/drivers/platform/tegra/nvadsp/nvadsp_os.c
deleted file mode 100644 (file)
index 8ed0427..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * nvadsp_os.c
- *
- * ADSP OS management
- *
- * Copyright (C) 2014 NVIDIA Corporation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/tegra_nvadsp.h>
-
-void nvadsp_adsp_init(void)
-{
-       return;
-}
-
-int nvadsp_os_load(void)
-{
-       return -ENOENT;
-}
-
-void nvadsp_os_start(void)
-{
-       return;
-}
-
-void nvadsp_os_stop(void)
-{
-       return;
-}
diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c
new file mode 100644 (file)
index 0000000..f6aaac6
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * os.c
+ *
+ * ADSP OS management
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Copyright (C) 2014 NVIDIA Corporation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/tegra_nvadsp.h>
+#include <linux/elf.h>
+
+#include "os.h"
+
+#define APE_FPGA_MISC_RST_DEVICES 0x702dc800 /*1882048512*/
+#define APE_RESET (1 << 6)
+
+#define AMC_EVP_RESET_VEC_0            0x700
+#define AMC_EVP_UNDEF_VEC_0            0x704
+#define AMC_EVP_SWI_VEC_0              0x708
+#define AMC_EVP_PREFETCH_ABORT_VEC_0   0x70c
+#define AMC_EVP_DATA_ABORT_VEC_0       0x710
+#define AMC_EVP_RSVD_VEC_0             0x714
+#define AMC_EVP_IRQ_VEC_0              0x718
+#define AMC_EVP_FIQ_VEC_0              0x71c
+#define AMC_EVP_RESET_ADDR_0           0x720
+#define AMC_EVP_UNDEF_ADDR_0           0x724
+#define AMC_EVP_SWI_ADDR_0             0x728
+#define AMC_EVP_PREFETCH_ABORT_ADDR_0  0x72c
+#define AMC_EVP_DATA_ABORT_ADDR_0      0x730
+#define AMC_EVP_RSVD_ADDR_0            0x734
+#define AMC_EVP_IRQ_ADDR_0             0x738
+#define AMC_EVP_FIQ_ADDR_0             0x73c
+
+#define AMC_EVP_SIZE (AMC_EVP_FIQ_ADDR_0 - AMC_EVP_RESET_VEC_0 + 4)
+
+#define NVADSP_ELF "adsp.elf"
+#define NVADSP_FIRMWARE NVADSP_ELF
+
+/* Maximum number of LOAD MAPPINGS supported */
+#define NM_LOAD_MAPPINGS 20
+
+struct nvadsp_os_data {
+       void __iomem *reset_reg;
+       struct platform_device *pdev;
+};
+
+static struct nvadsp_os_data priv;
+
+struct nvadsp_mappings {
+       phys_addr_t da;
+       void *va;
+       int len;
+};
+
+static struct nvadsp_mappings adsp_map[NM_LOAD_MAPPINGS];
+static int map_idx;
+
+int adsp_add_load_mappings(phys_addr_t pa, void *mapping, int len)
+{
+       if (map_idx >= NM_LOAD_MAPPINGS)
+               return -EINVAL;
+
+       adsp_map[map_idx].da = pa;
+       adsp_map[map_idx].va = mapping;
+       adsp_map[map_idx].len = len;
+       map_idx++;
+       return 0;
+}
+
+void *adsp_da_to_va_mappings(phys_addr_t da, int len)
+{
+       void *ptr = NULL;
+       int i;
+
+       for (i = 0; i < map_idx; i++) {
+               int offset = da - adsp_map[i].da;
+
+               /* try next carveout if da is too small */
+               if (offset < 0)
+                       continue;
+
+               /* try next carveout if da is too large */
+               if (offset + len > adsp_map[i].len)
+                       continue;
+
+               ptr = adsp_map[i].va + offset;
+               break;
+       }
+       return ptr;
+}
+
+int nvadsp_os_elf_load(const struct firmware *fw)
+{
+       struct device *dev = &priv.pdev->dev;
+       struct elf32_hdr *ehdr;
+       struct elf32_phdr *phdr;
+       int i, ret = 0;
+       const u8 *elf_data = fw->data;
+
+       ehdr = (struct elf32_hdr *)elf_data;
+       phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+       /* go through the available ELF segments */
+       for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+               u32 da = phdr->p_paddr;
+               u32 memsz = phdr->p_memsz;
+               u32 filesz = phdr->p_filesz;
+               u32 offset = phdr->p_offset;
+
+               if (phdr->p_type != PT_LOAD)
+                       continue;
+
+               dev_info(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+                                       phdr->p_type, da, memsz, filesz);
+
+               if (filesz > memsz) {
+                       dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+                                                       filesz, memsz);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (offset + filesz > fw->size) {
+                       dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
+                                       offset + filesz, fw->size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* put the segment where the remote processor expects it */
+               if (phdr->p_filesz)
+                       memcpy(adsp_da_to_va_mappings(da, filesz),
+                               elf_data + phdr->p_offset, filesz);
+       }
+
+       return ret;
+}
+
+int nvadsp_os_load(void)
+{
+       const struct firmware *fw;
+       int ret;
+       struct device *dev = &priv.pdev->dev;
+
+       ret = request_firmware(&fw, NVADSP_FIRMWARE, dev);
+       if (ret < 0) {
+               dev_info(dev,
+                       "reqest firmware for %s failed with %d\n",
+                                       NVADSP_FIRMWARE, ret);
+               return ret;
+       }
+
+       dev_info(dev, "Loading ADSP OS firmware %s\n",
+                                               NVADSP_FIRMWARE);
+       ret = nvadsp_os_elf_load(fw);
+       if (ret)
+               dev_info(dev, "failed to load %s\n", NVADSP_FIRMWARE);
+
+       return ret;
+}
+EXPORT_SYMBOL(nvadsp_os_load);
+
+int nvadsp_os_start(void)
+{
+       struct device *dev = &priv.pdev->dev;
+       dev_info(dev, "starting ADSP OS ....\n");
+       writel(APE_RESET, priv.reset_reg);
+       return 0;
+}
+EXPORT_SYMBOL(nvadsp_os_start);
+
+int nvadsp_os_probe(struct platform_device *pdev)
+{
+       struct nvadsp_platform_data *plat_data;
+       struct device *dev = &pdev->dev;
+       void *dram_va;
+
+       plat_data = pdev->dev.platform_data;
+       if (IS_ERR_OR_NULL(plat_data)) {
+               dev_info(dev, "carvout is NULL\n");
+               return PTR_ERR(plat_data);
+       }
+
+       dram_va = ioremap_nocache(plat_data->co_pa, plat_data->co_size);
+       if (!dram_va) {
+               dev_info(dev, "remap failed for addr %lx\n",
+                                       (long)plat_data->co_pa);
+               return -EINVAL;
+       }
+
+       adsp_add_load_mappings(plat_data->co_pa, dram_va,
+                                               plat_data->co_size);
+
+       priv.reset_reg = ioremap(APE_FPGA_MISC_RST_DEVICES, 1);
+       if (!priv.reset_reg) {
+               dev_info(dev, "unable to map reset addr\n");
+               return -EINVAL;
+       }
+
+       priv.pdev = pdev;
+
+       return 0;
+}
similarity index 86%
rename from drivers/platform/tegra/nvadsp/nvadsp_os.h
rename to drivers/platform/tegra/nvadsp/os.h
index 674bb339c8d0f2a6887b0099b7c848ae8d33eb41..87d4d31ccf596892bab18ae5b86a37003b1870a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * nvadsp_os.h
+ * os.h
  *
  * A header file containing data structures shared with ADSP OS
  *
@@ -17,4 +17,8 @@
  */
 #ifndef __TEGRA_NVADSP_OS_H
 #define __TEGRA_NVADSP_OS_H
+
+int nvadsp_os_probe(struct platform_device *);
+int adsp_add_load_mappings(phys_addr_t, void *, int);
+
 #endif /* __TEGRA_NVADSP_OS_H */
index f0300afdd816aa65b57de55f0a2d3f57a0c20897..e830b9837f360293f5bc1a70a10aae8ce8c8a838 100644 (file)
@@ -178,7 +178,7 @@ void nvadsp_aram_release(char *start, size_t size);
  */
 void nvadsp_adsp_init(void);
 int nvadsp_os_load(void);
-void nvadsp_os_start(void);
+int nvadsp_os_start(void);
 void nvadsp_os_stop(void);
 
 /*
diff --git a/include/uapi/linux/tegra_nvadsp.h b/include/uapi/linux/tegra_nvadsp.h
deleted file mode 100644 (file)
index fee54ca..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * include/uapi/linux/tegra_adsp.h
- *
- * a Header file for managing user interfaces of ADSP/APE
- *
- * Copyright (C) 2014 NVIDIA Corporation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-#ifndef _UAPI_LINUX_TEGRA_NVADSP_H
-#define _UAPI_LINUX_TEGRA_NVADSP_H
-
-/*
- * NVADSP IOCTLs
- */
-#define NVADSP_IOC_MAGIC       'a'
-
-#endif /* _UAPI_LINUX_TEGRA_NVADSP_H */