Merge tag 'qcom-soc-for-3.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / arch / arm / mach-bcm / bcm_kona_smc.c
index 5e31e91..a55a7ec 100644 (file)
 
 #include "bcm_kona_smc.h"
 
-struct secure_bridge_data {
-       void __iomem *bounce;           /* virtual address */
-       u32 __iomem buffer_addr;        /* physical address */
-       int initialized;
-} bridge_data;
+static u32             bcm_smc_buffer_phys;    /* physical address */
+static void __iomem    *bcm_smc_buffer;        /* virtual address */
 
 struct bcm_kona_smc_data {
        unsigned service_id;
@@ -33,6 +30,7 @@ struct bcm_kona_smc_data {
        unsigned arg1;
        unsigned arg2;
        unsigned arg3;
+       unsigned result;
 };
 
 static const struct of_device_id bcm_kona_smc_ids[] __initconst = {
@@ -41,59 +39,125 @@ static const struct of_device_id bcm_kona_smc_ids[] __initconst = {
        {},
 };
 
-/* Map in the bounce area */
+/* Map in the args buffer area */
 int __init bcm_kona_smc_init(void)
 {
        struct device_node *node;
+       const __be32 *prop_val;
+       u64 prop_size = 0;
+       unsigned long buffer_size;
+       u32 buffer_phys;
 
        /* Read buffer addr and size from the device tree node */
        node = of_find_matching_node(NULL, bcm_kona_smc_ids);
        if (!node)
                return -ENODEV;
 
-       /* Don't care about size or flags of the DT node */
-       bridge_data.buffer_addr =
-               be32_to_cpu(*of_get_address(node, 0, NULL, NULL));
-       BUG_ON(!bridge_data.buffer_addr);
+       prop_val = of_get_address(node, 0, &prop_size, NULL);
+       if (!prop_val)
+               return -EINVAL;
 
-       bridge_data.bounce = of_iomap(node, 0);
-       BUG_ON(!bridge_data.bounce);
+       /* We assume space for four 32-bit arguments */
+       if (prop_size < 4 * sizeof(u32) || prop_size > (u64)ULONG_MAX)
+               return -EINVAL;
+       buffer_size = (unsigned long)prop_size;
 
-       bridge_data.initialized = 1;
+       buffer_phys = be32_to_cpup(prop_val);
+       if (!buffer_phys)
+               return -EINVAL;
+
+       bcm_smc_buffer = ioremap(buffer_phys, buffer_size);
+       if (!bcm_smc_buffer)
+               return -ENOMEM;
+       bcm_smc_buffer_phys = buffer_phys;
 
        pr_info("Kona Secure API initialized\n");
 
        return 0;
 }
 
+/*
+ * int bcm_kona_do_smc(u32 service_id, u32 buffer_addr)
+ *
+ * Only core 0 can run the secure monitor code.  If an "smc" request
+ * is initiated on a different core it must be redirected to core 0
+ * for execution.  We rely on the caller to handle this.
+ *
+ * Each "smc" request supplies a service id and the address of a
+ * buffer containing parameters related to the service to be
+ * performed.  A flags value defines the behavior of the level 2
+ * cache and interrupt handling while the secure monitor executes.
+ *
+ * Parameters to the "smc" request are passed in r4-r6 as follows:
+ *     r4      service id
+ *     r5      flags (SEC_ROM_*)
+ *     r6      physical address of buffer with other parameters
+ *
+ * Execution of an "smc" request produces two distinct results.
+ *
+ * First, the secure monitor call itself (regardless of the specific
+ * service request) can succeed, or can produce an error.  When an
+ * "smc" request completes this value is found in r12; it should
+ * always be SEC_EXIT_NORMAL.
+ *
+ * In addition, the particular service performed produces a result.
+ * The values that should be expected depend on the service.  We
+ * therefore return this value to the caller, so it can handle the
+ * request result appropriately.  This result value is found in r0
+ * when the "smc" request completes.
+ */
+static int bcm_kona_do_smc(u32 service_id, u32 buffer_phys)
+{
+       register u32 ip asm("ip");      /* Also called r12 */
+       register u32 r0 asm("r0");
+       register u32 r4 asm("r4");
+       register u32 r5 asm("r5");
+       register u32 r6 asm("r6");
+
+       r4 = service_id;
+       r5 = 0x3;               /* Keep IRQ and FIQ off in SM */
+       r6 = buffer_phys;
+
+       asm volatile (
+               /* Make sure we got the registers we want */
+               __asmeq("%0", "ip")
+               __asmeq("%1", "r0")
+               __asmeq("%2", "r4")
+               __asmeq("%3", "r5")
+               __asmeq("%4", "r6")
+#ifdef REQUIRES_SEC
+               ".arch_extension sec\n"
+#endif
+               "       smc    #0\n"
+               : "=r" (ip), "=r" (r0)
+               : "r" (r4), "r" (r5), "r" (r6)
+               : "r1", "r2", "r3", "r7", "lr");
+
+       BUG_ON(ip != SEC_EXIT_NORMAL);
+
+       return r0;
+}
+
 /* __bcm_kona_smc() should only run on CPU 0, with pre-emption disabled */
 static void __bcm_kona_smc(void *info)
 {
        struct bcm_kona_smc_data *data = info;
-       u32 *args = bridge_data.bounce;
-       int rc = 0;
+       u32 *args = bcm_smc_buffer;
 
-       /* Must run on CPU 0 */
        BUG_ON(smp_processor_id() != 0);
+       BUG_ON(!args);
 
-       /* Check map in the bounce area */
-       BUG_ON(!bridge_data.initialized);
-
-       /* Copy one 32 bit word into the bounce area */
-       args[0] = data->arg0;
-       args[1] = data->arg1;
-       args[2] = data->arg2;
-       args[3] = data->arg3;
+       /* Copy the four 32 bit argument values into the bounce area */
+       writel_relaxed(data->arg0, args++);
+       writel_relaxed(data->arg1, args++);
+       writel_relaxed(data->arg2, args++);
+       writel(data->arg3, args);
 
        /* Flush caches for input data passed to Secure Monitor */
-       if (data->service_id != SSAPI_BRCM_START_VC_CORE)
-               flush_cache_all();
-
-       /* Trap into Secure Monitor */
-       rc = bcm_kona_smc_asm(data->service_id, bridge_data.buffer_addr);
+       flush_cache_all();
 
-       if (rc != SEC_ROM_RET_OK)
-               pr_err("Secure Monitor call failed (0x%x)!\n", rc);
+       /* Trap into Secure Monitor and record the request result */
+       data->result = bcm_kona_do_smc(data->service_id, bcm_smc_buffer_phys);
 }
 
 unsigned bcm_kona_smc(unsigned service_id, unsigned arg0, unsigned arg1,
@@ -106,17 +170,13 @@ unsigned bcm_kona_smc(unsigned service_id, unsigned arg0, unsigned arg1,
        data.arg1 = arg1;
        data.arg2 = arg2;
        data.arg3 = arg3;
+       data.result = 0;
 
        /*
         * Due to a limitation of the secure monitor, we must use the SMP
         * infrastructure to forward all secure monitor calls to Core 0.
         */
-       if (get_cpu() != 0)
-               smp_call_function_single(0, __bcm_kona_smc, (void *)&data, 1);
-       else
-               __bcm_kona_smc(&data);
+       smp_call_function_single(0, __bcm_kona_smc, &data, 1);
 
-       put_cpu();
-
-       return 0;
+       return data.result;
 }