*
* Tegra TSEC Module Support
*
- * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2013, 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,
#include <linux/slab.h> /* for kzalloc */
#include <linux/firmware.h>
#include <linux/module.h>
-#include <mach/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk/tegra.h>
#include <asm/byteorder.h> /* for parsing ucode image wrt endianness */
#include <linux/delay.h> /* for udelay */
#include <linux/scatterlist.h>
#include <linux/stop_machine.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <mach/pm_domains.h>
+
#include "dev.h"
#include "tsec.h"
#include "hw_tsec.h"
#include "chip_support.h"
#include "nvhost_memmgr.h"
#include "nvhost_intr.h"
+#include "t114/t114.h"
+#include "t148/t148.h"
+#include "t124/t124.h"
#define TSEC_IDLE_TIMEOUT_DEFAULT 10000 /* 10 milliseconds */
#define TSEC_IDLE_CHECK_PERIOD 10 /* 10 usec */
#define TSEC_KEY_LENGTH 16
#define TSEC_RESERVE 256
#define TSEC_KEY_OFFSET (TSEC_RESERVE - TSEC_KEY_LENGTH)
-#define TSEC_HOST1X_STATUS_OFFSET (TSEC_KEY_OFFSET - 4)
#define TSEC_OS_START_OFFSET 256
/* The key value in ascii hex */
static u8 otf_key[TSEC_KEY_LENGTH];
-/* Pointer to this device */
-struct platform_device *tsec;
-
-static u32 *host1x_status_offset(struct tsec *m)
-{
- return (u32 *)
- &(m->mapped[m->os.reserved_offset + TSEC_HOST1X_STATUS_OFFSET]);
-}
-
-static u32 tsec_get_host1x_state(void)
-{
- struct tsec *m = get_tsec(tsec);
- u32 ret;
-
- /* We have dedicated memory byte */
- ret = *host1x_status_offset(m);
- rmb();
- return ret;
-}
-
-static void tsec_set_host1x_state(int state)
-{
- struct tsec *m = get_tsec(tsec);
-
- *host1x_status_offset(m) = state;
- wmb();
-}
-
-static int stop_machine_fn(void *priv)
-{
- int timeout = 10000;
-
- tsec_set_host1x_state(tsec_host1x_access_granted);
-
- while (tsec_get_host1x_state() != tsec_host1x_release_access
- && timeout)
- timeout--;
-
- if (!timeout)
- pr_err("TSEC didn't release access");
-
- tsec_set_host1x_state(tsec_host1x_none);
-
- return 0;
-}
-
-static void disable_tsec_irq(struct platform_device *pdev)
-{
- nvhost_intr_disable_general_irq(&nvhost_get_host(pdev)->intr, 20);
-}
-
-static void enable_tsec_irq(struct platform_device *pdev)
-{
- /* Clear interrupt */
- nvhost_device_writel(pdev, tsec_irqsclr_r(), 0xffffff);
- nvhost_device_writel(pdev, tsec_thi_int_status_r(), 0x1);
- nvhost_intr_enable_general_irq(&nvhost_get_host(pdev)->intr, 20,
- nvhost_tsec_isr, nvhost_tsec_isr_thread);
-}
-
-void nvhost_tsec_isr(void)
-{
- disable_tsec_irq(tsec);
-}
-
-void nvhost_tsec_isr_thread(void)
-{
- if (tsec_get_host1x_state() == tsec_host1x_request_access)
- stop_machine(stop_machine_fn, NULL, NULL);
- enable_tsec_irq(tsec);
-}
-
/* caller is responsible for freeing */
static char *tsec_get_fw_name(struct platform_device *dev)
{
do {
u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout);
- u32 dmatrfcmd = nvhost_device_readl(dev, tsec_dmatrfcmd_r());
+ u32 dmatrfcmd = host1x_readl(dev, tsec_dmatrfcmd_r());
u32 idle_v = tsec_dmatrfcmd_idle_v(dmatrfcmd);
if (tsec_dmatrfcmd_idle_true_v() == idle_v)
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);
+ host1x_writel(dev, tsec_dmatrfmoffs_r(), i_offset);
+ host1x_writel(dev, tsec_dmatrffboffs_r(), pa_offset);
+ host1x_writel(dev, tsec_dmatrfcmd_r(), cmd);
return tsec_dma_wait_idle(dev, &timeout);
do {
u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout);
- u32 w = nvhost_device_readl(dev, tsec_idlestate_r());
+ u32 w = host1x_readl(dev, tsec_idlestate_r());
if (!w)
return 0;
return -1;
}
+static int tsec_load_kfuse(struct platform_device *pdev)
+{
+ u32 val;
+ u32 timeout;
+
+ val = host1x_readl(pdev, tsec_tegra_ctl_r());
+ val &= ~tsec_tegra_ctl_tkfi_kfuse_m();
+ host1x_writel(pdev, tsec_tegra_ctl_r(), val);
+
+ val = host1x_readl(pdev, tsec_scp_ctl_pkey_r());
+ val |= tsec_scp_ctl_pkey_request_reload_s();
+ host1x_writel(pdev, tsec_scp_ctl_pkey_r(), val);
+
+ timeout = TSEC_IDLE_TIMEOUT_DEFAULT;
+
+ do {
+ u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, timeout);
+ u32 w = host1x_readl(pdev, tsec_scp_ctl_pkey_r());
+
+ if (w & tsec_scp_ctl_pkey_loaded_m())
+ break;
+ udelay(TSEC_IDLE_CHECK_PERIOD);
+ timeout -= check;
+ } while (timeout);
+
+ val = host1x_readl(pdev, tsec_tegra_ctl_r());
+ val |= tsec_tegra_ctl_tkfi_kfuse_m();
+ host1x_writel(pdev, tsec_tegra_ctl_r(), val);
+
+ if (timeout)
+ return 0;
+ else
+ return -1;
+}
+
int tsec_boot(struct platform_device *dev)
{
u32 timeout;
int err = 0;
struct tsec *m = get_tsec(dev);
- nvhost_device_writel(dev, tsec_dmactl_r(), 0);
- nvhost_device_writel(dev, tsec_dmatrfbase_r(),
- (sg_dma_address(m->pa->sgl) + m->os.bin_data_offset) >> 8);
+ if (!m || !m->valid)
+ return -ENOMEDIUM;
+
+ if (m->is_booted)
+ return 0;
+
+ host1x_writel(dev, tsec_dmactl_r(), 0);
+ host1x_writel(dev, tsec_dmatrfbase_r(),
+ (m->dma_addr + m->os.bin_data_offset) >> 8);
for (offset = 0; offset < m->os.data_size; offset += 256)
tsec_dma_pa_to_internal_256b(dev,
/* boot tsec */
- nvhost_device_writel(dev, tsec_bootvec_r(),
+ host1x_writel(dev, tsec_bootvec_r(),
tsec_bootvec_vec_f(TSEC_OS_START_OFFSET));
- nvhost_device_writel(dev, tsec_cpuctl_r(),
+ host1x_writel(dev, tsec_cpuctl_r(),
tsec_cpuctl_startcpu_true_f());
timeout = 0; /* default */
}
/* setup tsec interrupts and enable interface */
- nvhost_device_writel(dev, tsec_irqmset_r(),
+ host1x_writel(dev, tsec_irqmset_r(),
(tsec_irqmset_ext_f(0xff) |
tsec_irqmset_swgen1_set_f() |
tsec_irqmset_swgen0_set_f() |
tsec_irqmset_halt_set_f() |
tsec_irqmset_wdtmr_set_f()));
- nvhost_device_writel(dev, tsec_itfen_r(),
+ host1x_writel(dev, tsec_itfen_r(),
(tsec_itfen_mthden_enable_f() |
tsec_itfen_ctxen_enable_f()));
- return 0;
+ err = tsec_load_kfuse(dev);
+ if (err)
+ return err;
+ m->is_booted = true;
+
+ return err;
}
static int tsec_setup_ucode_image(struct platform_device *dev,
struct tsec *m = get_tsec(dev);
const struct firmware *ucode_fw;
int err;
+ DEFINE_DMA_ATTRS(attrs);
+
+ m->dma_addr = 0;
+ m->mapped = NULL;
ucode_fw = nvhost_client_request_firmware(dev, fw_name);
- if (IS_ERR_OR_NULL(ucode_fw)) {
+ if (!ucode_fw) {
dev_err(&dev->dev, "failed to get tsec firmware\n");
err = -ENOENT;
return err;
}
- /* allocate pages for ucode */
- m->mem_r = mem_op().alloc(nvhost_get_host(dev)->memmgr,
- roundup(ucode_fw->size+256, PAGE_SIZE),
- PAGE_SIZE, mem_mgr_flag_uncacheable);
- if (IS_ERR_OR_NULL(m->mem_r)) {
- dev_err(&dev->dev, "nvmap alloc failed");
- err = -ENOMEM;
- goto clean_up;
- }
-
- m->pa = mem_op().pin(nvhost_get_host(dev)->memmgr, m->mem_r);
- if (IS_ERR_OR_NULL(m->pa)) {
- dev_err(&dev->dev, "nvmap pin failed for ucode");
- err = PTR_ERR(m->pa);
- m->pa = 0;
- goto clean_up;
- }
+ m->size = ucode_fw->size;
+ dma_set_attr(DMA_ATTR_READ_ONLY, &attrs);
- m->mapped = mem_op().mmap(m->mem_r);
- if (IS_ERR_OR_NULL(m->mapped)) {
- dev_err(&dev->dev, "nvmap mmap failed");
+ m->mapped = dma_alloc_attrs(&dev->dev,
+ m->size, &m->dma_addr,
+ GFP_KERNEL, &attrs);
+ if (!m->mapped) {
+ dev_err(&dev->dev, "dma memory allocation failed");
err = -ENOMEM;
goto clean_up;
}
- err = tsec_setup_ucode_image(dev, (u32 *)m->mapped, ucode_fw);
+ err = tsec_setup_ucode_image(dev, m->mapped, ucode_fw);
if (err) {
dev_err(&dev->dev, "failed to parse firmware image\n");
- return err;
+ goto clean_up;
}
m->valid = true;
return 0;
clean_up:
- if (m->mapped)
- mem_op().munmap(m->mem_r, m->mapped);
- if (m->pa)
- mem_op().unpin(nvhost_get_host(dev)->memmgr, m->mem_r, m->pa);
- if (m->mem_r)
- mem_op().put(nvhost_get_host(dev)->memmgr, m->mem_r);
+ if (m->mapped) {
+ dma_free_attrs(&dev->dev,
+ m->size, m->mapped,
+ m->dma_addr, &attrs);
+ m->mapped = NULL;
+ m->dma_addr = 0;
+ }
release_firmware(ucode_fw);
return err;
}
-void nvhost_tsec_init(struct platform_device *dev)
+int nvhost_tsec_init(struct platform_device *dev)
{
int err = 0;
struct tsec *m;
fw_name = tsec_get_fw_name(dev);
if (!fw_name) {
dev_err(&dev->dev, "couldn't determine firmware name");
- return;
+ return -EINVAL;
}
m = kzalloc(sizeof(struct tsec), GFP_KERNEL);
if (!m) {
dev_err(&dev->dev, "couldn't alloc ucode");
kfree(fw_name);
- return;
+ return -ENOMEM;
}
set_tsec(dev, m);
+ m->is_booted = false;
err = tsec_read_ucode(dev, fw_name);
kfree(fw_name);
nvhost_module_busy(dev);
- tsec_boot(dev);
- enable_tsec_irq(dev);
+ err = tsec_boot(dev);
+ if (err)
+ goto clean_up;
+
nvhost_module_idle(dev);
- return;
+ return 0;
- clean_up:
+clean_up:
dev_err(&dev->dev, "failed");
- mem_op().unpin(nvhost_get_host(dev)->memmgr, m->mem_r, m->pa);
+ return err;
}
void nvhost_tsec_deinit(struct platform_device *dev)
{
struct tsec *m = get_tsec(dev);
- disable_tsec_irq(dev);
+ DEFINE_DMA_ATTRS(attrs);
+ dma_set_attr(DMA_ATTR_READ_ONLY, &attrs);
- /* unpin, free ucode memory */
- if (m->mem_r) {
- mem_op().munmap(m->mem_r, m->mapped);
- mem_op().unpin(nvhost_get_host(dev)->memmgr, m->mem_r, m->pa);
- mem_op().put(nvhost_get_host(dev)->memmgr, m->mem_r);
- m->mem_r = 0;
+ if (m->mapped) {
+ dma_free_attrs(&dev->dev,
+ m->size, m->mapped,
+ m->dma_addr, &attrs);
+ m->mapped = NULL;
+ m->dma_addr = 0;
}
+
+ kfree(m);
+ set_tsec(dev, NULL);
}
-void nvhost_tsec_finalize_poweron(struct platform_device *dev)
+int nvhost_tsec_finalize_poweron(struct platform_device *dev)
{
- tsec_boot(dev);
+ struct nvhost_device_data *pdata = platform_get_drvdata(dev);
+
+ tegra_periph_reset_assert(pdata->clk[0]);
+ udelay(10);
+ tegra_periph_reset_deassert(pdata->clk[0]);
+
+ return tsec_boot(dev);
}
+int nvhost_tsec_prepare_poweroff(struct platform_device *dev)
+{
+ struct tsec *m = get_tsec(dev);
+ if (m)
+ m->is_booted = false;
+
+ return 0;
+}
+
+
+static struct of_device_id tegra_tsec_of_match[] = {
+#ifdef TEGRA_11X_OR_HIGHER_CONFIG
+ { .compatible = "nvidia,tegra114-tsec",
+ .data = (struct nvhost_device_data *)&t11_tsec_info },
+#endif
+#ifdef TEGRA_14X_OR_HIGHER_CONFIG
+ { .compatible = "nvidia,tegra148-tsec",
+ .data = (struct nvhost_device_data *)&t14_tsec_info },
+#endif
+#ifdef TEGRA_12X_OR_HIGHER_CONFIG
+ { .compatible = "nvidia,tegra124-tsec",
+ .data = (struct nvhost_device_data *)&t124_tsec_info },
+#endif
+ { },
+};
+
static int tsec_probe(struct platform_device *dev)
{
int err;
- struct nvhost_device_data *pdata =
- (struct nvhost_device_data *)dev->dev.platform_data;
+ struct nvhost_device_data *pdata = NULL;
- pdata->pdev = dev;
- pdata->init = nvhost_tsec_init;
- pdata->deinit = nvhost_tsec_deinit;
+ if (dev->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_device(tegra_tsec_of_match, &dev->dev);
+ if (match)
+ pdata = (struct nvhost_device_data *)match->data;
+ } else
+ pdata = (struct nvhost_device_data *)dev->dev.platform_data;
+
+ WARN_ON(!pdata);
+ if (!pdata) {
+ dev_info(&dev->dev, "no platform data\n");
+ return -ENODATA;
+ }
+ pdata->pdev = dev;
+ mutex_init(&pdata->lock);
platform_set_drvdata(dev, pdata);
err = nvhost_client_device_get_resources(dev);
if (err)
return err;
- tsec = dev;
+ nvhost_module_init(dev);
- err = nvhost_client_device_init(dev);
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+ pdata->pd.name = "tsec";
+ err = nvhost_module_add_domain(&pdata->pd, dev);
if (err)
return err;
+#endif
- nvhost_module_busy(to_platform_device(dev->dev.parent));
+ err = nvhost_client_device_init(dev);
- /* Reset TSEC at boot-up. Otherwise it starts sending interrupts. */
- clk_enable(pdata->clk[0]);
- tegra_periph_reset_assert(pdata->clk[0]);
- udelay(10);
- tegra_periph_reset_deassert(pdata->clk[0]);
- clk_disable(pdata->clk[0]);
- nvhost_module_idle(to_platform_device(dev->dev.parent));
return err;
}
static int __exit tsec_remove(struct platform_device *dev)
{
- struct nvhost_master *host = nvhost_get_host(dev);
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_put(&dev->dev);
+ pm_runtime_disable(&dev->dev);
+#else
+ nvhost_module_disable_clk(&dev->dev);
+#endif
- /* Add clean-up */
- host->intr.generic_isr[20] = NULL;
- host->intr.generic_isr_thread[20] = NULL;
return 0;
}
-#ifdef CONFIG_PM
-static int tsec_suspend(struct platform_device *dev, pm_message_t state)
-{
- return nvhost_client_device_suspend(dev);
-}
-
-static int tsec_resume(struct platform_device *dev)
-{
- dev_info(&dev->dev, "resuming\n");
- return 0;
-}
-#endif
-
static struct platform_driver tsec_driver = {
.probe = tsec_probe,
.remove = __exit_p(tsec_remove),
-#ifdef CONFIG_PM
- .suspend = tsec_suspend,
- .resume = tsec_resume,
-#endif
.driver = {
.owner = THIS_MODULE,
.name = "tsec",
+#ifdef CONFIG_PM
+ .pm = &nvhost_module_pm_ops,
+#endif
+#ifdef CONFIG_OF
+ .of_match_table = tegra_tsec_of_match,
+#endif
}
};