31bc2728d315ed4ea9a486eb3ef1b749c13af58a
[linux-3.10.git] / drivers / video / tegra / host / tsec / tsec.c
1 /*
2  * drivers/video/tegra/host/tsec/tsec.c
3  *
4  * Tegra TSEC Module Support
5  *
6  * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <linux/slab.h>         /* for kzalloc */
22 #include <linux/firmware.h>
23 #include <linux/module.h>
24 #include <linux/pm_runtime.h>
25 #include <mach/clk.h>
26 #include <asm/byteorder.h>      /* for parsing ucode image wrt endianness */
27 #include <linux/delay.h>        /* for udelay */
28 #include <linux/scatterlist.h>
29 #include <linux/stop_machine.h>
30 #include <linux/of.h>
31 #include <linux/of_device.h>
32 #include <linux/of_platform.h>
33
34 #include <mach/pm_domains.h>
35
36 #include "dev.h"
37 #include "tsec.h"
38 #include "hw_tsec.h"
39 #include "bus_client.h"
40 #include "nvhost_acm.h"
41 #include "chip_support.h"
42 #include "nvhost_memmgr.h"
43 #include "nvhost_intr.h"
44 #include "t114/t114.h"
45 #include "t148/t148.h"
46 #include "t124/t124.h"
47
48 #define TSEC_IDLE_TIMEOUT_DEFAULT       10000   /* 10 milliseconds */
49 #define TSEC_IDLE_CHECK_PERIOD          10      /* 10 usec */
50 #define TSEC_KEY_LENGTH                 16
51 #define TSEC_RESERVE                    256
52 #define TSEC_KEY_OFFSET                 (TSEC_RESERVE - TSEC_KEY_LENGTH)
53 #define TSEC_HOST1X_STATUS_OFFSET       (TSEC_KEY_OFFSET - 4)
54
55 #define TSEC_OS_START_OFFSET    256
56
57 #define get_tsec(ndev) ((struct tsec *)(ndev)->dev.platform_data)
58 #define set_tsec(ndev, f) ((ndev)->dev.platform_data = f)
59
60 /* The key value in ascii hex */
61 static u8 otf_key[TSEC_KEY_LENGTH];
62
63 /* Pointer to this device */
64 struct platform_device *tsec;
65
66 static u32 *host1x_status_offset(struct tsec *m)
67 {
68         return (u32 *)
69                 &(m->mapped[m->os.reserved_offset + TSEC_HOST1X_STATUS_OFFSET]);
70 }
71
72 static u32 tsec_get_host1x_state(void)
73 {
74         struct tsec *m = get_tsec(tsec);
75         u32 ret;
76
77         /* We have dedicated memory byte  */
78         ret = *host1x_status_offset(m);
79         rmb();
80         return ret;
81 }
82
83 static void tsec_set_host1x_state(int state)
84 {
85         struct tsec *m = get_tsec(tsec);
86
87         *host1x_status_offset(m) = state;
88         wmb();
89 }
90
91 static int stop_machine_fn(void *priv)
92 {
93         int timeout = 10000;
94
95         tsec_set_host1x_state(tsec_host1x_access_granted);
96
97         while (tsec_get_host1x_state() != tsec_host1x_release_access
98                         && timeout)
99                 timeout--;
100
101         if (!timeout)
102                 pr_err("TSEC didn't release access");
103
104         tsec_set_host1x_state(tsec_host1x_none);
105
106         return 0;
107 }
108
109 static void disable_tsec_irq(struct platform_device *pdev)
110 {
111         nvhost_intr_disable_general_irq(&nvhost_get_host(pdev)->intr, 20);
112 }
113
114 static void enable_tsec_irq(struct platform_device *pdev)
115 {
116         /* Clear interrupt */
117         nvhost_device_writel(pdev, tsec_irqsclr_r(), 0xffffff);
118         nvhost_device_writel(pdev, tsec_thi_int_status_r(), 0x1);
119         nvhost_intr_enable_general_irq(&nvhost_get_host(pdev)->intr, 20,
120                         nvhost_tsec_isr, nvhost_tsec_isr_thread);
121 }
122
123 void nvhost_tsec_isr(void)
124 {
125         disable_tsec_irq(tsec);
126 }
127
128 void nvhost_tsec_isr_thread(void)
129 {
130         if (tsec_get_host1x_state() == tsec_host1x_request_access)
131                 stop_machine(stop_machine_fn, NULL, NULL);
132         enable_tsec_irq(tsec);
133 }
134
135 /* caller is responsible for freeing */
136 static char *tsec_get_fw_name(struct platform_device *dev)
137 {
138         char *fw_name;
139         u8 maj, min;
140         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
141
142         /* note size here is a little over...*/
143         fw_name = kzalloc(32, GFP_KERNEL);
144         if (!fw_name)
145                 return NULL;
146
147         decode_tsec_ver(pdata->version, &maj, &min);
148         if (maj == 1) {
149                 /* there are no minor versions so far for maj==1 */
150                 sprintf(fw_name, "nvhost_tsec.fw");
151         } else {
152                 kfree(fw_name);
153                 return NULL;
154         }
155
156         dev_info(&dev->dev, "fw name:%s\n", fw_name);
157
158         return fw_name;
159 }
160
161 static int tsec_dma_wait_idle(struct platform_device *dev, u32 *timeout)
162 {
163         if (!*timeout)
164                 *timeout = TSEC_IDLE_TIMEOUT_DEFAULT;
165
166         do {
167                 u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout);
168                 u32 dmatrfcmd = nvhost_device_readl(dev, tsec_dmatrfcmd_r());
169                 u32 idle_v = tsec_dmatrfcmd_idle_v(dmatrfcmd);
170
171                 if (tsec_dmatrfcmd_idle_true_v() == idle_v)
172                         return 0;
173
174                 udelay(TSEC_IDLE_CHECK_PERIOD);
175                 *timeout -= check;
176         } while (*timeout);
177
178         dev_err(&dev->dev, "dma idle timeout");
179
180         return -1;
181 }
182
183 static int tsec_dma_pa_to_internal_256b(struct platform_device *dev,
184                 u32 offset, u32 internal_offset, bool imem)
185 {
186         u32 cmd = tsec_dmatrfcmd_size_256b_f();
187         u32 pa_offset =  tsec_dmatrffboffs_offs_f(offset);
188         u32 i_offset = tsec_dmatrfmoffs_offs_f(internal_offset);
189         u32 timeout = 0; /* default*/
190
191         if (imem)
192                 cmd |= tsec_dmatrfcmd_imem_true_f();
193
194         nvhost_device_writel(dev, tsec_dmatrfmoffs_r(), i_offset);
195         nvhost_device_writel(dev, tsec_dmatrffboffs_r(), pa_offset);
196         nvhost_device_writel(dev, tsec_dmatrfcmd_r(), cmd);
197
198         return tsec_dma_wait_idle(dev, &timeout);
199
200 }
201
202 static int tsec_wait_idle(struct platform_device *dev, u32 *timeout)
203 {
204         if (!*timeout)
205                 *timeout = TSEC_IDLE_TIMEOUT_DEFAULT;
206
207         do {
208                 u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, *timeout);
209                 u32 w = nvhost_device_readl(dev, tsec_idlestate_r());
210
211                 if (!w)
212                         return 0;
213                 udelay(TSEC_IDLE_CHECK_PERIOD);
214                 *timeout -= check;
215         } while (*timeout);
216
217         return -1;
218 }
219
220 static int tsec_load_kfuse(struct platform_device *pdev)
221 {
222         u32 val;
223         u32 timeout;
224
225         val = nvhost_device_readl(pdev, tsec_tegra_ctl_r());
226         val &= ~tsec_tegra_ctl_tkfi_kfuse_m();
227         nvhost_device_writel(pdev, tsec_tegra_ctl_r(), val);
228
229         val = nvhost_device_readl(pdev, tsec_scp_ctl_pkey_r());
230         val |= tsec_scp_ctl_pkey_request_reload_s();
231         nvhost_device_writel(pdev, tsec_scp_ctl_pkey_r(), val);
232
233         timeout = TSEC_IDLE_TIMEOUT_DEFAULT;
234
235         do {
236                 u32 check = min_t(u32, TSEC_IDLE_CHECK_PERIOD, timeout);
237                 u32 w = nvhost_device_readl(pdev, tsec_scp_ctl_pkey_r());
238
239                 if (w & tsec_scp_ctl_pkey_loaded_m())
240                         break;
241                 udelay(TSEC_IDLE_CHECK_PERIOD);
242                 timeout -= check;
243         } while (timeout);
244
245         val = nvhost_device_readl(pdev, tsec_tegra_ctl_r());
246         val |= tsec_tegra_ctl_tkfi_kfuse_m();
247         nvhost_device_writel(pdev, tsec_tegra_ctl_r(), val);
248
249         if (timeout)
250                 return 0;
251         else
252                 return -1;
253 }
254
255 int tsec_boot(struct platform_device *dev)
256 {
257         u32 timeout;
258         u32 offset;
259         int err = 0;
260         struct tsec *m = get_tsec(dev);
261
262         nvhost_device_writel(dev, tsec_dmactl_r(), 0);
263         nvhost_device_writel(dev, tsec_dmatrfbase_r(),
264                 (sg_dma_address(m->pa->sgl) + m->os.bin_data_offset) >> 8);
265
266         for (offset = 0; offset < m->os.data_size; offset += 256)
267                 tsec_dma_pa_to_internal_256b(dev,
268                                            m->os.data_offset + offset,
269                                            offset, false);
270
271         tsec_dma_pa_to_internal_256b(dev,
272                                      m->os.code_offset+TSEC_OS_START_OFFSET,
273                                      TSEC_OS_START_OFFSET, true);
274
275
276         /* boot tsec */
277         nvhost_device_writel(dev, tsec_bootvec_r(),
278                              tsec_bootvec_vec_f(TSEC_OS_START_OFFSET));
279         nvhost_device_writel(dev, tsec_cpuctl_r(),
280                         tsec_cpuctl_startcpu_true_f());
281
282         timeout = 0; /* default */
283
284         err = tsec_wait_idle(dev, &timeout);
285         if (err != 0) {
286                 dev_err(&dev->dev, "boot failed due to timeout");
287                 return err;
288         }
289
290         /* setup tsec interrupts and enable interface */
291         nvhost_device_writel(dev, tsec_irqmset_r(),
292                         (tsec_irqmset_ext_f(0xff) |
293                                 tsec_irqmset_swgen1_set_f() |
294                                 tsec_irqmset_swgen0_set_f() |
295                                 tsec_irqmset_exterr_set_f() |
296                                 tsec_irqmset_halt_set_f()   |
297                                 tsec_irqmset_wdtmr_set_f()));
298
299         nvhost_device_writel(dev, tsec_itfen_r(),
300                         (tsec_itfen_mthden_enable_f() |
301                                 tsec_itfen_ctxen_enable_f()));
302
303         return tsec_load_kfuse(dev);
304 }
305
306 static int tsec_setup_ucode_image(struct platform_device *dev,
307                 u32 *ucode_ptr,
308                 const struct firmware *ucode_fw)
309 {
310         struct tsec *m = get_tsec(dev);
311         /* image data is little endian. */
312         struct tsec_ucode_v1 ucode;
313         int w;
314         u32 reserved_offset;
315         u32 tsec_key_offset;
316
317         /* copy the whole thing taking into account endianness */
318         for (w = 0; w < ucode_fw->size / sizeof(u32); w++)
319                 ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
320
321         ucode.bin_header = (struct tsec_ucode_bin_header_v1 *)ucode_ptr;
322         /* endian problems would show up right here */
323         if (ucode.bin_header->bin_magic != 0x10de) {
324                 dev_err(&dev->dev,
325                            "failed to get firmware magic");
326                 return -EINVAL;
327         }
328         if (ucode.bin_header->bin_ver != 1) {
329                 dev_err(&dev->dev,
330                            "unsupported firmware version");
331                 return -ENOENT;
332         }
333         /* shouldn't be bigger than what firmware thinks */
334         if (ucode.bin_header->bin_size > ucode_fw->size) {
335                 dev_err(&dev->dev,
336                            "ucode image size inconsistency");
337                 return -EINVAL;
338         }
339
340         dev_dbg(&dev->dev,
341                 "ucode bin header: magic:0x%x ver:%d size:%d\n",
342                 ucode.bin_header->bin_magic,
343                 ucode.bin_header->bin_ver,
344                 ucode.bin_header->bin_size);
345         dev_dbg(&dev->dev,
346                 "ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d\n",
347                 ucode.bin_header->os_bin_header_offset,
348                 ucode.bin_header->os_bin_data_offset,
349                 ucode.bin_header->os_bin_size);
350         ucode.os_header = (struct tsec_ucode_os_header_v1 *)
351                 (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset);
352
353         dev_dbg(&dev->dev,
354                 "os ucode header: os code (offset,size): 0x%x, 0x%x\n",
355                 ucode.os_header->os_code_offset,
356                 ucode.os_header->os_code_size);
357         dev_dbg(&dev->dev,
358                 "os ucode header: os data (offset,size): 0x%x, 0x%x\n",
359                 ucode.os_header->os_data_offset,
360                 ucode.os_header->os_data_size);
361         dev_dbg(&dev->dev,
362                 "os ucode header: num apps: %d\n",
363                 ucode.os_header->num_apps);
364
365         /* make space for reserved area - we need 20 bytes, but we move 256
366          * bytes because firmware needs to be 256 byte aligned */
367         reserved_offset = ucode.bin_header->os_bin_data_offset;
368         memmove(((void *)ucode_ptr) + reserved_offset + TSEC_RESERVE,
369                         ((void *)ucode_ptr) + reserved_offset,
370                         ucode.bin_header->os_bin_size);
371         ucode.bin_header->os_bin_data_offset += TSEC_RESERVE;
372
373         /*  clear 256 bytes before ucode os code */
374         memset(((void *)ucode_ptr) + reserved_offset, 0, TSEC_RESERVE);
375
376         /* Copy key to be the 16 bytes before the firmware */
377         tsec_key_offset = reserved_offset + TSEC_KEY_OFFSET;
378         memcpy(((void *)ucode_ptr) + tsec_key_offset, otf_key, TSEC_KEY_LENGTH);
379
380         m->os.size = ucode.bin_header->os_bin_size;
381         m->os.reserved_offset = reserved_offset;
382         m->os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
383         m->os.code_offset = ucode.os_header->os_code_offset;
384         m->os.data_offset = ucode.os_header->os_data_offset;
385         m->os.data_size   = ucode.os_header->os_data_size;
386
387         return 0;
388 }
389
390 int tsec_read_ucode(struct platform_device *dev, const char *fw_name)
391 {
392         struct tsec *m = get_tsec(dev);
393         const struct firmware *ucode_fw;
394         int err;
395
396         ucode_fw = nvhost_client_request_firmware(dev, fw_name);
397         if (!ucode_fw) {
398                 dev_err(&dev->dev, "failed to get tsec firmware\n");
399                 err = -ENOENT;
400                 return err;
401         }
402
403         /* allocate pages for ucode */
404         m->mem_r = nvhost_memmgr_alloc(nvhost_get_host(dev)->memmgr,
405                              roundup(ucode_fw->size+256, PAGE_SIZE),
406                              PAGE_SIZE, mem_mgr_flag_uncacheable, 0);
407         if (IS_ERR(m->mem_r)) {
408                 dev_err(&dev->dev, "nvmap alloc failed");
409                 err = PTR_ERR(m->mem_r);
410                 m->mem_r = NULL;
411                 goto clean_up;
412         }
413
414         m->pa = nvhost_memmgr_pin(nvhost_get_host(dev)->memmgr, m->mem_r,
415                         &dev->dev);
416         if (IS_ERR(m->pa)) {
417                 dev_err(&dev->dev, "nvmap pin failed for ucode");
418                 err = PTR_ERR(m->pa);
419                 m->pa = 0;
420                 goto clean_up;
421         }
422
423         m->mapped = nvhost_memmgr_mmap(m->mem_r);
424         if (!m->mapped) {
425                 dev_err(&dev->dev, "nvmap mmap failed");
426                 err = -ENOMEM;
427                 goto clean_up;
428         }
429
430         err = tsec_setup_ucode_image(dev, (u32 *)m->mapped, ucode_fw);
431         if (err) {
432                 dev_err(&dev->dev, "failed to parse firmware image\n");
433                 return err;
434         }
435
436         m->valid = true;
437
438         release_firmware(ucode_fw);
439
440         return 0;
441
442 clean_up:
443         if (m->mapped) {
444                 nvhost_memmgr_munmap(m->mem_r, m->mapped);
445                 m->mapped = NULL;
446         }
447         if (m->pa) {
448                 nvhost_memmgr_unpin(nvhost_get_host(dev)->memmgr, m->mem_r,
449                                 &dev->dev, m->pa);
450                 m->pa = NULL;
451         }
452         if (m->mem_r) {
453                 nvhost_memmgr_put(nvhost_get_host(dev)->memmgr, m->mem_r);
454                 m->mem_r = NULL;
455         }
456         release_firmware(ucode_fw);
457         return err;
458 }
459
460 int nvhost_tsec_init(struct platform_device *dev)
461 {
462         int err = 0;
463         struct tsec *m;
464         char *fw_name;
465
466         fw_name = tsec_get_fw_name(dev);
467         if (!fw_name) {
468                 dev_err(&dev->dev, "couldn't determine firmware name");
469                 return -EINVAL;
470         }
471
472         m = kzalloc(sizeof(struct tsec), GFP_KERNEL);
473         if (!m) {
474                 dev_err(&dev->dev, "couldn't alloc ucode");
475                 kfree(fw_name);
476                 return -ENOMEM;
477         }
478         set_tsec(dev, m);
479
480         err = tsec_read_ucode(dev, fw_name);
481         kfree(fw_name);
482         fw_name = 0;
483
484         if (err || !m->valid) {
485                 dev_err(&dev->dev, "ucode not valid");
486                 goto clean_up;
487         }
488
489         nvhost_module_busy(dev);
490
491         err = tsec_boot(dev);
492         if (err)
493                 goto clean_up;
494
495         enable_tsec_irq(dev);
496         nvhost_module_idle(dev);
497         return 0;
498
499 clean_up:
500         dev_err(&dev->dev, "failed");
501         return err;
502 }
503
504 void nvhost_tsec_deinit(struct platform_device *dev)
505 {
506         struct tsec *m = get_tsec(dev);
507
508         disable_tsec_irq(dev);
509
510         /* unpin, free ucode memory */
511         if (m->mem_r) {
512                 if (m->mapped)
513                         nvhost_memmgr_munmap(m->mem_r, m->mapped);
514                 if (m->pa)
515                         nvhost_memmgr_unpin(nvhost_get_host(dev)->memmgr,
516                                         m->mem_r, &dev->dev, m->pa);
517                 if (m->mem_r)
518                         nvhost_memmgr_put(nvhost_get_host(dev)->memmgr,
519                                         m->mem_r);
520                 m->mem_r = 0;
521         }
522         kfree(m);
523         set_tsec(dev, NULL);
524 }
525
526 void nvhost_tsec_finalize_poweron(struct platform_device *dev)
527 {
528         tsec_boot(dev);
529 }
530
531 static struct of_device_id tegra_tsec_of_match[] = {
532 #ifdef TEGRA_11X_OR_HIGHER_CONFIG
533         { .compatible = "nvidia,tegra114-tsec",
534                 .data = (struct nvhost_device_data *)&t11_tsec_info },
535 #endif
536 #ifdef TEGRA_14X_OR_HIGHER_CONFIG
537         { .compatible = "nvidia,tegra148-tsec",
538                 .data = (struct nvhost_device_data *)&t14_tsec_info },
539 #endif
540 #ifdef TEGRA_12X_OR_HIGHER_CONFIG
541         { .compatible = "nvidia,tegra124-tsec",
542                 .data = (struct nvhost_device_data *)&t124_tsec_info },
543 #endif
544         { },
545 };
546
547 static int tsec_probe(struct platform_device *dev)
548 {
549         int err;
550         struct nvhost_device_data *pdata = NULL;
551
552         if (dev->dev.of_node) {
553                 const struct of_device_id *match;
554
555                 match = of_match_device(tegra_tsec_of_match, &dev->dev);
556                 if (match)
557                         pdata = (struct nvhost_device_data *)match->data;
558         } else
559                 pdata = (struct nvhost_device_data *)dev->dev.platform_data;
560
561         WARN_ON(!pdata);
562         if (!pdata) {
563                 dev_info(&dev->dev, "no platform data\n");
564                 return -ENODATA;
565         }
566
567         pdata->pdev = dev;
568         pdata->init = nvhost_tsec_init;
569         pdata->deinit = nvhost_tsec_deinit;
570
571         mutex_init(&pdata->lock);
572
573         platform_set_drvdata(dev, pdata);
574         nvhost_module_init(dev);
575
576         err = nvhost_client_device_get_resources(dev);
577         if (err)
578                 return err;
579
580         tsec = dev;
581
582         tegra_pd_add_device(&dev->dev);
583         if (pdata->clockgate_delay) {
584                 pm_runtime_set_autosuspend_delay(&dev->dev,
585                         pdata->clockgate_delay);
586                 pm_runtime_use_autosuspend(&dev->dev);
587         }
588         pm_runtime_enable(&dev->dev);
589
590         pm_runtime_get_sync(&dev->dev);
591
592         err = nvhost_client_device_init(dev);
593         if (err)
594                 return err;
595
596         /* Reset TSEC at boot-up. Otherwise it starts sending interrupts. */
597         tegra_periph_reset_assert(pdata->clk[0]);
598         udelay(10);
599         tegra_periph_reset_deassert(pdata->clk[0]);
600
601         if (pdata->clockgate_delay)
602                 pm_runtime_put_sync_autosuspend(&dev->dev);
603         else
604                 pm_runtime_put(&dev->dev);
605
606         return err;
607 }
608
609 static int __exit tsec_remove(struct platform_device *dev)
610 {
611         struct nvhost_master *host = nvhost_get_host(dev);
612
613         /* Add clean-up */
614         host->intr.generic_isr[20] = NULL;
615         host->intr.generic_isr_thread[20] = NULL;
616         return 0;
617 }
618
619 #ifdef CONFIG_PM
620 static const struct dev_pm_ops tsec_pm_ops = {
621         .suspend = nvhost_client_device_suspend,
622         .resume = nvhost_client_device_resume,
623 #ifdef CONFIG_PM_RUNTIME
624         .runtime_suspend = nvhost_module_disable_clk,
625         .runtime_resume = nvhost_module_enable_clk,
626 #endif
627 };
628 #endif
629
630 static struct platform_driver tsec_driver = {
631         .probe = tsec_probe,
632         .remove = __exit_p(tsec_remove),
633         .driver = {
634                 .owner = THIS_MODULE,
635                 .name = "tsec",
636 #ifdef CONFIG_PM
637                 .pm = &tsec_pm_ops,
638 #endif
639 #ifdef CONFIG_OF
640                 .of_match_table = tegra_tsec_of_match,
641 #endif
642         }
643 };
644
645 static int __init tsec_key_setup(char *line)
646 {
647         int i;
648         u8 tmp[] = {0,0,0};
649         pr_debug("tsec otf key: %s\n", line);
650
651         if (strlen(line) != TSEC_KEY_LENGTH*2) {
652                 pr_warn("invalid tsec key: %s\n", line);
653                 return 0;
654         }
655
656         for (i = 0; i < TSEC_KEY_LENGTH; i++) {
657                 int err;
658                 memcpy(tmp, &line[i*2], 2);
659                 err = kstrtou8(tmp, 16, &otf_key[i]);
660                 if (err) {
661                         pr_warn("cannot read tsec otf key: %d", err);
662                         break;
663                 }
664         }
665         return 0;
666 }
667 __setup("otf_key=", tsec_key_setup);
668
669 static int __init tsec_init(void)
670 {
671         return platform_driver_register(&tsec_driver);
672 }
673
674 static void __exit tsec_exit(void)
675 {
676         platform_driver_unregister(&tsec_driver);
677 }
678
679 module_init(tsec_init);
680 module_exit(tsec_exit);