]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - drivers/video/tegra/host/tsec/tsec.c
video: tegra: host: Use host1x_readl/writel
[linux-3.10.git] / drivers / video / tegra / host / tsec / tsec.c
index c3beafc5c527471fbc918111fad62655838b14d7..9316ab5bc9cc198f48c732d0324cebb9c6034748 100644 (file)
@@ -3,7 +3,7 @@
  *
  * 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)
 {
@@ -155,7 +92,7 @@ static int tsec_dma_wait_idle(struct platform_device *dev, u32 *timeout)
 
        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)
@@ -181,9 +118,9 @@ static int tsec_dma_pa_to_internal_256b(struct platform_device *dev,
        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);
 
@@ -196,7 +133,7 @@ static int tsec_wait_idle(struct platform_device *dev, u32 *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;
@@ -207,6 +144,41 @@ static int tsec_wait_idle(struct platform_device *dev, u32 *timeout)
        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;
@@ -214,9 +186,15 @@ int tsec_boot(struct platform_device *dev)
        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,
@@ -229,9 +207,9 @@ int tsec_boot(struct platform_device *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 */
@@ -243,7 +221,7 @@ int tsec_boot(struct platform_device *dev)
        }
 
        /* 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() |
@@ -251,11 +229,16 @@ int tsec_boot(struct platform_device *dev)
                                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,
@@ -347,43 +330,34 @@ int tsec_read_ucode(struct platform_device *dev, const char *fw_name)
        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;
@@ -393,17 +367,18 @@ int tsec_read_ucode(struct platform_device *dev, const char *fw_name)
        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;
@@ -412,16 +387,17 @@ void nvhost_tsec_init(struct platform_device *dev)
        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);
@@ -434,103 +410,140 @@ void nvhost_tsec_init(struct platform_device *dev)
 
        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
        }
 };