diff options
author | Terje Bergstrom <tbergstrom@nvidia.com> | 2012-02-10 12:12:20 +0200 |
---|---|---|
committer | Varun Wadekar <vwadekar@nvidia.com> | 2012-06-26 11:31:42 +0530 |
commit | 1ef7013cccbb1f5d58dce0db8e4a43ce988214d1 (patch) | |
tree | 92444feccfac50bc5bd9ced31c51d3ffb5146ace /drivers/video | |
parent | 4e4a20dcbc049eec4309f86e6174ce4ca91df509 (diff) |
video: tegra: host: Add TSEC firmware upload
Add TSEC firmware upload.
Bug 839970
Change-Id: I8fca9ad6d33dcf7832c1b67d6d6444e39e4e2991
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-on: http://git-master/r/83123
Reviewed-by: Mark Stadler <mastadler@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Reviewed-by: Ken Adams <kadams@nvidia.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/tegra/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/host/t114/t114.c | 8 | ||||
-rw-r--r-- | drivers/video/tegra/host/tsec/Makefile | 8 | ||||
-rw-r--r-- | drivers/video/tegra/host/tsec/hw_tsec.h | 184 | ||||
-rw-r--r-- | drivers/video/tegra/host/tsec/tsec.c | 313 | ||||
-rw-r--r-- | drivers/video/tegra/host/tsec/tsec.h | 81 |
6 files changed, 594 insertions, 1 deletions
diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile index 358ef503dc04..305078c0327a 100644 --- a/drivers/video/tegra/host/Makefile +++ b/drivers/video/tegra/host/Makefile @@ -22,4 +22,5 @@ obj-$(CONFIG_TEGRA_GRHOST) += isp/ obj-$(CONFIG_TEGRA_GRHOST) += vi/ obj-$(CONFIG_TEGRA_GRHOST) += t114/ obj-$(CONFIG_TEGRA_GRHOST) += msenc/ +obj-$(CONFIG_TEGRA_GRHOST) += tsec/ obj-$(CONFIG_TEGRA_GRHOST) += nvhost.o diff --git a/drivers/video/tegra/host/t114/t114.c b/drivers/video/tegra/host/t114/t114.c index e329737b1acf..6e815adae4ea 100644 --- a/drivers/video/tegra/host/t114/t114.c +++ b/drivers/video/tegra/host/t114/t114.c @@ -33,6 +33,7 @@ #include "gr3d/gr3d_t114.h" #include "gr3d/scale3d.h" #include "msenc/msenc.h" +#include "tsec/tsec.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) @@ -143,9 +144,12 @@ static struct nvhost_device devices[] = { .waitbases = BIT(NVWAITBASE_TSEC), .class = NV_TSEC_CLASS_ID, .exclusive = true, + .init = nvhost_tsec_init, + .deinit = nvhost_tsec_deinit, + .clocks = {{"tsec", UINT_MAX}, {"emc", HOST_EMC_FLOOR} }, NVHOST_MODULE_NO_POWERGATE_IDS, NVHOST_DEFAULT_CLOCKGATE_DELAY, - .moduleid = NVHOST_MODULE_NONE, + .moduleid = NVHOST_MODULE_TSEC, } }; static inline int t114_nvhost_hwctx_handler_init( @@ -182,6 +186,8 @@ static int t114_channel_init(struct nvhost_channel *ch, int nvhost_init_t114_channel_support(struct nvhost_master *host) { int result = nvhost_init_t20_channel_support(host); + /* We're using 8 out of 9 channels supported by hw */ + host->nb_channels = NV_HOST1X_CHANNELS_T114-1; host->op.channel.init = t114_channel_init; return result; diff --git a/drivers/video/tegra/host/tsec/Makefile b/drivers/video/tegra/host/tsec/Makefile new file mode 100644 index 000000000000..5454fd5b1c8c --- /dev/null +++ b/drivers/video/tegra/host/tsec/Makefile @@ -0,0 +1,8 @@ +GCOV_PROFILE := y + +EXTRA_CFLAGS += -Idrivers/video/tegra/host + +nvhost-tsec-objs = \ + tsec.o + +obj-$(CONFIG_TEGRA_GRHOST) += nvhost-tsec.o diff --git a/drivers/video/tegra/host/tsec/hw_tsec.h b/drivers/video/tegra/host/tsec/hw_tsec.h new file mode 100644 index 000000000000..ac4a3e481adf --- /dev/null +++ b/drivers/video/tegra/host/tsec/hw_tsec.h @@ -0,0 +1,184 @@ +/* + * drivers/video/tegra/host/tsec/hw_tsec.h + * + * Tegra TSEC Module Support + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __NVHOST_HW_TSEC_H__ +#define __NVHOST_HW_TSEC_H__ + +#include <linux/nvhost.h> + +static inline u32 tsec_irqmset_r(void) +{ + return 0x1010; +} + +static inline u32 tsec_irqmset_wdtmr_set_f(void) +{ + return 0x2; +} + +static inline u32 tsec_irqmset_ext_f(u32 v) +{ + return (v & 0xff) << 8; +} + +static inline u32 tsec_irqmset_halt_set_f(void) +{ + return 0x10; +} + +static inline u32 tsec_irqmset_exterr_set_f(void) +{ + return 0x20; +} + +static inline u32 tsec_irqmset_swgen0_set_f(void) +{ + return 0x40; +} +static inline u32 tsec_irqmset_swgen1_set_f(void) +{ + return 0x80; +} + +static inline u32 tsec_irqdest_r(void) +{ + return 0x101c; +} + +static inline u32 tsec_irqdest_host_halt_host_f(void) +{ + return 0x10; +} +static inline u32 tsec_irqdest_host_ext_f(u32 v) +{ + return (v & 0xff) << 8; +} + +static inline u32 tsec_irqdest_host_exterr_host_f(void) +{ + return 0x20; +} + +static inline u32 tsec_irqdest_host_swgen0_host_f(void) +{ + return 0x40; +} + +static inline u32 tsec_irqdest_host_swgen1_host_f(void) +{ + return 0x80; +} + +static inline u32 tsec_itfen_r(void) +{ + return 0x1048; +} + +static inline u32 tsec_itfen_ctxen_enable_f(void) +{ + return 0x1; +} + +static inline u32 tsec_itfen_mthden_enable_f(void) +{ + return 0x2; +} + +static inline u32 tsec_idlestate_r(void) +{ + return 0x104c; +} + +static inline u32 tsec_cpuctl_r(void) +{ + return 0x1100; +} +static inline u32 tsec_cpuctl_startcpu_true_f(void) +{ + return 0x2; +} + +static inline u32 tsec_bootvec_r(void) +{ + return 0x1104; +} + +static inline u32 tsec_bootvec_vec_f(u32 v) +{ + return (v & 0xffffffff) << 0; +} + +static inline u32 tsec_dmactl_r(void) +{ + return 0x110c; +} + +static inline u32 tsec_dmatrfbase_r(void) +{ + return 0x1110; +} + +static inline u32 tsec_dmatrfmoffs_r(void) +{ + return 0x1114; +} + +static inline u32 tsec_dmatrfmoffs_offs_f(u32 v) +{ + return (v & 0xffff) << 0; +} + +static inline u32 tsec_dmatrfcmd_r(void) +{ + return 0x1118; +} + +static inline u32 tsec_dmatrfcmd_idle_v(u32 r) +{ + return (r >> 1) & 0x1; +} + +static inline u32 tsec_dmatrfcmd_idle_true_v(void) +{ + return 0x00000001; +} + +static inline u32 tsec_dmatrfcmd_size_256b_f(void) +{ + return 0x600; +} + +static inline u32 tsec_dmatrfcmd_imem_true_f(void) +{ + return 0x10; +} + +static inline u32 tsec_dmatrffboffs_r(void) +{ + return 0x111c; +} + +static inline u32 tsec_dmatrffboffs_offs_f(u32 v) +{ + return (v & 0xffffffff) << 0; +} + +#endif diff --git a/drivers/video/tegra/host/tsec/tsec.c b/drivers/video/tegra/host/tsec/tsec.c new file mode 100644 index 000000000000..7f1193b9947b --- /dev/null +++ b/drivers/video/tegra/host/tsec/tsec.c @@ -0,0 +1,313 @@ +/* + * drivers/video/tegra/host/tsec/tsec.c + * + * Tegra TSEC Module Support + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/slab.h> /* for kzalloc */ +#include <linux/firmware.h> +#include <asm/byteorder.h> /* for parsing ucode image wrt endianness */ +#include <linux/delay.h> /* for udelay */ +#include <mach/nvmap.h> +#include "dev.h" +#include "tsec.h" +#include "hw_tsec.h" + +#define TSEC_IDLE_TIMEOUT_DEFAULT 10000 /* 10 milliseconds */ +#define TSEC_IDLE_CHECK_PERIOD 10 /* 10 usec */ +#define TSEC_FIRMWARE_NAME "nvhost_tsec.fw" + +#define get_tsec(ndev) ((struct tsec *)(ndev)->dev.platform_data) +#define set_tsec(ndev, f) ((ndev)->dev.platform_data = f) + +static int tsec_dma_wait_idle(struct nvhost_device *dev, u32 *timeout) +{ + if (!*timeout) + *timeout = TSEC_IDLE_TIMEOUT_DEFAULT; + + do { + u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout); + u32 dmatrfcmd = nvhost_device_readl(dev, tsec_dmatrfcmd_r()); + u32 idle_v = tsec_dmatrfcmd_idle_v(dmatrfcmd); + + if (tsec_dmatrfcmd_idle_true_v() == idle_v) + return 0; + + udelay(TSEC_IDLE_CHECK_PERIOD); + *timeout -= check; + } while (*timeout); + + dev_err(&dev->dev, "dma idle timeout"); + + return -1; +} + +static int tsec_dma_pa_to_internal_256b(struct nvhost_device *dev, + phys_addr_t pa, u32 internal_offset, bool imem) +{ + u32 cmd = tsec_dmatrfcmd_size_256b_f(); + u32 pa_offset = tsec_dmatrffboffs_offs_f(pa); + u32 i_offset = tsec_dmatrfmoffs_offs_f(internal_offset); + u32 timeout = 0; /* default*/ + + if (imem) + cmd |= tsec_dmatrfcmd_imem_true_f(); + + nvhost_device_writel(dev, tsec_dmatrfmoffs_r(), i_offset); + nvhost_device_writel(dev, tsec_dmatrffboffs_r(), pa_offset); + nvhost_device_writel(dev, tsec_dmatrfcmd_r(), cmd); + + return tsec_dma_wait_idle(dev, &timeout); + +} + +static int tsec_wait_idle(struct nvhost_device *dev, u32 *timeout) +{ + if (!*timeout) + *timeout = TSEC_IDLE_TIMEOUT_DEFAULT; + + do { + u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout); + u32 w = nvhost_device_readl(dev, tsec_idlestate_r()); + + if (!w) + return 0; + udelay(TSEC_IDLE_CHECK_PERIOD); + *timeout -= check; + } while (*timeout); + + return -1; +} + +int tsec_boot(struct nvhost_device *dev) +{ + u32 timeout; + u32 offset; + int err = 0; + struct tsec *m = get_tsec(dev); + + nvhost_device_writel(dev, tsec_dmactl_r(), 0); + nvhost_device_writel(dev, tsec_dmatrfbase_r(), + (m->pa + m->os.bin_data_offset) >> 8); + + for (offset = 0; offset < m->os.data_size; offset += 256) + tsec_dma_pa_to_internal_256b(dev, + m->os.data_offset + offset, + offset, false); + + tsec_dma_pa_to_internal_256b(dev, m->os.code_offset, 0, true); + + /* setup tsec interrupts and enable interface */ + nvhost_device_writel(dev, tsec_irqmset_r(), + (tsec_irqmset_ext_f(0xff) | + tsec_irqmset_swgen1_set_f() | + tsec_irqmset_swgen0_set_f() | + tsec_irqmset_exterr_set_f() | + tsec_irqmset_halt_set_f() | + tsec_irqmset_wdtmr_set_f())); + nvhost_device_writel(dev, tsec_irqdest_r(), + (tsec_irqdest_host_ext_f(0xff) | + tsec_irqdest_host_swgen1_host_f() | + tsec_irqdest_host_swgen0_host_f() | + tsec_irqdest_host_exterr_host_f() | + tsec_irqdest_host_halt_host_f())); + nvhost_device_writel(dev, tsec_itfen_r(), + (tsec_itfen_mthden_enable_f() | + tsec_itfen_ctxen_enable_f())); + + /* boot tsec */ + nvhost_device_writel(dev, tsec_bootvec_r(), tsec_bootvec_vec_f(0)); + nvhost_device_writel(dev, tsec_cpuctl_r(), + tsec_cpuctl_startcpu_true_f()); + + timeout = 0; /* default */ + + err = tsec_wait_idle(dev, &timeout); + if (err != 0) { + dev_err(&dev->dev, "boot failed due to timeout"); + return err; + } + + return 0; +} + +static int tsec_setup_ucode_image(struct nvhost_device *dev, + u32 *ucode_ptr, + const struct firmware *ucode_fw) +{ + struct tsec *m = get_tsec(dev); + /* image data is little endian. */ + struct tsec_ucode_v1 ucode; + int w; + + /* copy the whole thing taking into account endianness */ + for (w = 0; w < ucode_fw->size / sizeof(u32); w++) + ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]); + + ucode.bin_header = (struct tsec_ucode_bin_header_v1 *)ucode_ptr; + /* endian problems would show up right here */ + if (ucode.bin_header->bin_magic != 0x10de) { + dev_err(&dev->dev, + "failed to get firmware magic"); + return -EINVAL; + } + if (ucode.bin_header->bin_ver != 1) { + dev_err(&dev->dev, + "unsupported firmware version"); + return -ENOENT; + } + /* shouldn't be bigger than what firmware thinks */ + if (ucode.bin_header->bin_size > ucode_fw->size) { + dev_err(&dev->dev, + "ucode image size inconsistency"); + return -EINVAL; + } + + dev_dbg(&dev->dev, + "ucode bin header: magic:0x%x ver:%d size:%d", + ucode.bin_header->bin_magic, + ucode.bin_header->bin_ver, + ucode.bin_header->bin_size); + dev_dbg(&dev->dev, + "ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d", + ucode.bin_header->os_bin_header_offset, + ucode.bin_header->os_bin_data_offset, + ucode.bin_header->os_bin_size); + ucode.os_header = (struct tsec_ucode_os_header_v1 *) + (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset); + + dev_dbg(&dev->dev, + "os ucode header: os code (offset,size): 0x%x, 0x%x", + ucode.os_header->os_code_offset, + ucode.os_header->os_code_size); + dev_dbg(&dev->dev, + "os ucode header: os data (offset,size): 0x%x, 0x%x", + ucode.os_header->os_data_offset, + ucode.os_header->os_data_size); + dev_dbg(&dev->dev, + "os ucode header: num apps: %d", + ucode.os_header->num_apps); + + m->os.size = ucode.bin_header->os_bin_size; + m->os.bin_data_offset = ucode.bin_header->os_bin_data_offset; + m->os.code_offset = ucode.os_header->os_code_offset; + m->os.data_offset = ucode.os_header->os_data_offset; + m->os.data_size = ucode.os_header->os_data_size; + + return 0; +} + +int tsec_read_ucode(struct nvhost_device *dev, const char *fw_name) +{ + struct tsec *m = get_tsec(dev); + const struct firmware *ucode_fw; + void *ucode_ptr; + int err; + + err = request_firmware(&ucode_fw, fw_name, &dev->dev); + if (err) { + dev_err(&dev->dev, "failed to get tsec firmware\n"); + return err; + } + + /* allocate pages for ucode */ + m->mem_r = nvmap_alloc(nvhost_get_host(dev)->nvmap, + roundup(ucode_fw->size, PAGE_SIZE), + PAGE_SIZE, NVMAP_HANDLE_UNCACHEABLE); + if (IS_ERR_OR_NULL(m->mem_r)) { + dev_err(&dev->dev, "nvmap alloc failed"); + err = -ENOMEM; + goto clean_up; + } + + ucode_ptr = nvmap_mmap(m->mem_r); + if (!ucode_ptr) { + dev_err(&dev->dev, "nvmap mmap failed"); + err = -ENOMEM; + goto clean_up; + } + + err = tsec_setup_ucode_image(dev, ucode_ptr, ucode_fw); + if (err) { + dev_err(&dev->dev, "failed to parse firmware image\n"); + return err; + } + + m->valid = true; + + nvmap_munmap(m->mem_r, ucode_ptr); + + clean_up: + release_firmware(ucode_fw); + return err; +} + +void nvhost_tsec_init(struct nvhost_device *dev) +{ + int err = 0; + struct tsec *m; + + m = kzalloc(sizeof(struct tsec), GFP_KERNEL); + if (!m) { + dev_err(&dev->dev, "couldn't alloc ucode"); + return; + } + set_tsec(dev, m); + + err = tsec_read_ucode(dev, TSEC_FIRMWARE_NAME); + + if (err || !m->valid) { + kfree(m); + dev_err(&dev->dev, "ucode not valid"); + return; + } + + m->pa = nvmap_pin(nvhost_get_host(dev)->nvmap, m->mem_r); + if (m->pa == -EINVAL || m->pa == -EINTR) { + dev_err(&dev->dev, "nvmap pin failed for ucode"); + err = -EINVAL; + kfree(m); + goto clean_up; + } + + nvhost_module_busy(dev); + tsec_boot(dev); + nvhost_module_idle(dev); + return; + + clean_up: + dev_err(&dev->dev, "failed"); + nvmap_unpin(nvhost_get_host(dev)->nvmap, m->mem_r); +} + +void nvhost_tsec_deinit(struct nvhost_device *dev) +{ + struct tsec *m = get_tsec(dev); + + /* unpin, free ucode memory */ + if (m->mem_r) { + nvmap_unpin(nvhost_get_host(dev)->nvmap, m->mem_r); + nvmap_free(nvhost_get_host(dev)->nvmap, m->mem_r); + m->mem_r = 0; + } +} + +void nvhost_tsec_finalize_poweron(struct nvhost_device *dev) +{ + tsec_boot(dev); +} diff --git a/drivers/video/tegra/host/tsec/tsec.h b/drivers/video/tegra/host/tsec/tsec.h new file mode 100644 index 000000000000..8afc4467ad24 --- /dev/null +++ b/drivers/video/tegra/host/tsec/tsec.h @@ -0,0 +1,81 @@ +/* + * drivers/video/tegra/host/tsec/tsec.h + * + * Tegra TSEC Module Support + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __NVHOST_TSEC_H__ +#define __NVHOST_TSEC_H__ + +#include <linux/nvhost.h> + +void nvhost_tsec_finalize_poweron(struct nvhost_device *dev); +void nvhost_tsec_init(struct nvhost_device *dev); +void nvhost_tsec_deinit(struct nvhost_device *dev); + +struct tsec { + bool valid; + u32 size; + struct nvmap_handle_ref *mem_r; + + struct { + u32 bin_data_offset; + u32 data_offset; + u32 data_size; + u32 code_offset; + u32 size; + } os; + + phys_addr_t pa; +}; + +struct tsec_ucode_bin_header_v1 { + u32 bin_magic; /* 0x10de */ + u32 bin_ver; /* cya, versioning of bin format (1) */ + u32 bin_size; /* entire image size including this header */ + u32 os_bin_header_offset; + u32 os_bin_data_offset; + u32 os_bin_size; +}; + +struct tsec_ucode_os_code_header_v1 { + u32 offset; + u32 size; +}; + +struct tsec_ucode_os_header_v1 { + u32 os_code_offset; + u32 os_code_size; + u32 os_data_offset; + u32 os_data_size; + u32 num_apps; + struct tsec_ucode_os_code_header_v1 *app_code; + struct tsec_ucode_os_code_header_v1 *app_data; + u32 *os_ovl_offset; + u32 *of_ovl_size; +}; + +struct tsec_ucode_v1 { + struct tsec_ucode_bin_header_v1 *bin_header; + struct tsec_ucode_os_header_v1 *os_header; + struct nvmap_handle_ref *mem; + phys_addr_t pa; + bool valid; +}; + +#endif |