blob: f4f8977212abe2551bfb922ca7daed03c1e065d7 [file] [log] [blame]
Srinu Gorlecf8c6752018-01-19 18:36:13 +05301/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15#include <linux/bitops.h>
16#include <linux/clk.h>
17#include <linux/delay.h>
18#include <linux/interrupt.h>
19#include <linux/io.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/msm-bus.h>
23#include <linux/of.h>
24#include <linux/platform_device.h>
25#include <linux/regulator/consumer.h>
26#include <linux/slab.h>
27#include <linux/workqueue.h>
28#include "vmem.h"
29#include "vmem_debugfs.h"
30
31/* Registers */
32#define OCIMEM_BASE(v) ((uint8_t *)(v)->reg.base)
33#define OCIMEM_HW_VERSION(v) (OCIMEM_BASE(v) + 0x00)
34#define OCIMEM_HW_PROFILE(v) (OCIMEM_BASE(v) + 0x04)
35#define OCIMEM_GEN_CTL(v) (OCIMEM_BASE(v) + 0x08)
36#define OCIMEM_GEN_STAT(v) (OCIMEM_BASE(v) + 0x0C)
37#define OCIMEM_INTC_CLR(v) (OCIMEM_BASE(v) + 0x10)
38#define OCIMEM_INTC_MASK(v) (OCIMEM_BASE(v) + 0x14)
39#define OCIMEM_INTC_STAT(v) (OCIMEM_BASE(v) + 0x18)
40#define OCIMEM_OSW_STATUS(v) (OCIMEM_BASE(v) + 0x1C)
41#define OCIMEM_PSCGC_TIMERS(v) (OCIMEM_BASE(v) + 0x34)
42#define OCIMEM_PSCGC_STAT(v) (OCIMEM_BASE(v) + 0x38)
43#define OCIMEM_PSCGC_M0_M7_CTL(v) (OCIMEM_BASE(v) + 0x3C)
44#define OCIMEM_ERR_ADDRESS(v) (OCIMEM_BASE(v) + 0x60)
45#define OCIMEM_AXI_ERR_SYNDROME(v) (OCIMEM_BASE(v) + 0x64)
46#define OCIMEM_DEBUG_CTL(v) (OCIMEM_BASE(v) + 0x68)
47
48/*
49 * Helper macro to help out with masks and shifts for values packed into
50 * registers.
51 */
52#define DECLARE_TYPE(__type, __end, __start) \
53 static const unsigned int __type##_BITS = (__end) - (__start) + 1; \
54 static const unsigned int __type##_SHIFT = (__start); \
55 static const unsigned int __type##_MASK = GENMASK((__end), (__start)); \
56 static inline unsigned int __type(uint32_t val) \
57 { \
58 return (val & __type##_MASK) >> __type##_SHIFT; \
59 } \
60 static inline uint32_t __type##_UPDATE(unsigned int val) \
61 { \
62 return (val << __type##_SHIFT) & __type##_MASK; \
63 }
64
65/* Register masks */
66/* OCIMEM_PSCGC_M0_M7_CTL */
67DECLARE_TYPE(BANK0_STATE, 3, 0);
68DECLARE_TYPE(BANK1_STATE, 7, 4);
69DECLARE_TYPE(BANK2_STATE, 11, 8);
70DECLARE_TYPE(BANK3_STATE, 15, 12);
71/* OCIMEM_PSCGC_TIMERS */
72DECLARE_TYPE(TIMERS_WAKEUP, 3, 0);
73DECLARE_TYPE(TIMERS_SLEEP, 11, 8);
74/* OCIMEM_HW_VERSION */
75DECLARE_TYPE(VERSION_STEP, 15, 0);
76DECLARE_TYPE(VERSION_MINOR, 27, 16);
77DECLARE_TYPE(VERSION_MAJOR, 31, 28);
78/* OCIMEM_HW_PROFILE */
79DECLARE_TYPE(PROFILE_BANKS, 16, 12);
80/* OCIMEM_AXI_ERR_SYNDROME */
81DECLARE_TYPE(ERR_SYN_ATID, 14, 8);
82DECLARE_TYPE(ERR_SYN_AMID, 23, 16);
83DECLARE_TYPE(ERR_SYN_APID, 28, 24);
84DECLARE_TYPE(ERR_SYN_ABID, 31, 29);
85/* OCIMEM_INTC_MASK */
86DECLARE_TYPE(AXI_ERR_INT, 0, 0);
87
88/* Internal stuff */
89#define MAX_BANKS 4
90
91enum bank_state {
92 BANK_STATE_NORM_PASSTHRU = 0,
93 BANK_STATE_NORM_FORCE_CORE_ON = 2,
94 BANK_STATE_NORM_FORCE_PERIPH_ON = 1,
95 BANK_STATE_NORM_FORCE_ALL_ON = 3,
96 BANK_STATE_SLEEP_RET = 6,
97 BANK_STATE_SLEEP_RET_PERIPH_ON = 7,
98 BANK_STATE_SLEEP_NO_RET = 4,
99};
100
101struct vmem {
102 int irq;
103 int num_banks;
104 int bank_size;
105 struct {
106 struct resource *resource;
107 void __iomem *base;
108 } reg, mem;
109 struct regulator *vdd;
110 struct {
111 const char *name;
112 struct clk *clk;
113 } *clocks;
114 int num_clocks;
115 struct {
116 struct msm_bus_scale_pdata *pdata;
117 uint32_t priv;
118 } bus;
119 atomic_t alloc_count;
120 struct dentry *debugfs_root;
121};
122
123static struct vmem *vmem;
124
125static inline u32 __readl(void * __iomem addr)
126{
127 u32 value = 0;
128
129 pr_debug("read %pK ", addr);
130 value = readl_relaxed(addr);
131 pr_debug("-> %08x\n", value);
132
133 return value;
134}
135
136static inline void __writel(u32 val, void * __iomem addr)
137{
138 pr_debug("write %08x -> %pK\n", val, addr);
139 writel_relaxed(val, addr);
140 /*
141 * Commit all writes via a mem barrier, as subsequent __readl()
142 * will depend on the state that's set via __writel().
143 */
144 mb();
145}
146
147static inline void __wait_timer(struct vmem *v, bool wakeup)
148{
149 uint32_t ticks = 0;
150 unsigned int (*timer)(uint32_t) = wakeup ?
151 TIMERS_WAKEUP : TIMERS_SLEEP;
152
153 ticks = timer(__readl(OCIMEM_PSCGC_TIMERS(v)));
154
155 /* Sleep for `ticks` nanoseconds as per h/w spec */
156 ndelay(ticks);
157}
158
159static inline void __wait_wakeup(struct vmem *v)
160{
161 return __wait_timer(v, true);
162}
163
164static inline void __wait_sleep(struct vmem *v)
165{
166 return __wait_timer(v, false);
167}
168
169static inline int __power_on(struct vmem *v)
170{
171 int rc = 0, c = 0;
172
173 rc = msm_bus_scale_client_update_request(v->bus.priv, 1);
174 if (rc) {
175 pr_err("Failed to vote for buses (%d)\n", rc);
176 goto exit;
177 }
178 pr_debug("Voted for buses\n");
179
180 rc = regulator_enable(v->vdd);
181 if (rc) {
182 pr_err("Failed to power on gdsc (%d)", rc);
183 goto unvote_bus;
184 }
185 pr_debug("Enabled regulator vdd\n");
186
187 for (c = 0; c < v->num_clocks; ++c) {
188 rc = clk_prepare_enable(v->clocks[c].clk);
189 if (rc) {
190 pr_err("Failed to enable %s clock (%d)\n",
191 v->clocks[c].name, rc);
192 goto disable_clocks;
193 }
194
195 pr_debug("Enabled clock %s\n", v->clocks[c].name);
196 }
197
198 return 0;
199disable_clocks:
200 for (--c; c >= 0; c--)
201 clk_disable_unprepare(v->clocks[c].clk);
202 regulator_disable(v->vdd);
203unvote_bus:
204 msm_bus_scale_client_update_request(v->bus.priv, 0);
205exit:
206 return rc;
207}
208
209static inline int __power_off(struct vmem *v)
210{
211 int c = 0;
212
213 for (c = 0; c < v->num_clocks; ++c) {
214 clk_disable_unprepare(v->clocks[c].clk);
215 pr_debug("Disabled clock %s\n", v->clocks[c].name);
216 }
217
218 regulator_disable(v->vdd);
219 pr_debug("Disabled regulator vdd\n");
220
221 msm_bus_scale_client_update_request(v->bus.priv, 0);
222 pr_debug("Unvoted for buses\n");
223
224 return 0;
225}
226
227static inline enum bank_state __bank_get_state(struct vmem *v,
228 unsigned int bank)
229{
230 unsigned int (*func[MAX_BANKS])(uint32_t) = {
231 BANK0_STATE, BANK1_STATE, BANK2_STATE, BANK3_STATE
232 };
233
234 WARN_ON(bank >= ARRAY_SIZE(func));
235 return func[bank](__readl(OCIMEM_PSCGC_M0_M7_CTL(v)));
236}
237
238static inline void __bank_set_state(struct vmem *v, unsigned int bank,
239 enum bank_state state)
240{
241 uint32_t bank_state = 0;
242 struct {
243 uint32_t (*update)(unsigned int);
244 uint32_t mask;
245 } banks[MAX_BANKS] = {
246 {BANK0_STATE_UPDATE, BANK0_STATE_MASK},
247 {BANK1_STATE_UPDATE, BANK1_STATE_MASK},
248 {BANK2_STATE_UPDATE, BANK2_STATE_MASK},
249 {BANK3_STATE_UPDATE, BANK3_STATE_MASK},
250 };
251
252 WARN_ON(bank >= ARRAY_SIZE(banks));
253
254 bank_state = __readl(OCIMEM_PSCGC_M0_M7_CTL(v));
255 bank_state &= ~banks[bank].mask;
256 bank_state |= banks[bank].update(state);
257
258 __writel(bank_state, OCIMEM_PSCGC_M0_M7_CTL(v));
259}
260
261static inline void __toggle_interrupts(struct vmem *v, bool enable)
262{
263 uint32_t ints = __readl(OCIMEM_INTC_MASK(v)),
264 mask = AXI_ERR_INT_MASK,
265 update = AXI_ERR_INT_UPDATE(!enable);
266
267 ints &= ~mask;
268 ints |= update;
269
270 __writel(ints, OCIMEM_INTC_MASK(v));
271}
272
273static void __enable_interrupts(struct vmem *v)
274{
275 pr_debug("Enabling interrupts\n");
276 enable_irq(v->irq);
277 __toggle_interrupts(v, true);
278}
279
280static void __disable_interrupts(struct vmem *v)
281{
282 pr_debug("Disabling interrupts\n");
283 __toggle_interrupts(v, false);
284 disable_irq_nosync(v->irq);
285}
286
287/**
288 * vmem_allocate: - Allocates memory from VMEM. Allocations have a few
289 * restrictions: only allocations of the entire VMEM memory are allowed, and
290 * , as a result, only single outstanding allocations are allowed.
291 *
292 * @size: amount of bytes to allocate
293 * @addr: A pointer to phys_addr_t where the physical address of the memory
294 * allocated is stored.
295 *
296 * Return: 0 in case of successful allocation (i.e. *addr != NULL). -ENOTSUPP,
297 * if platform doesn't support VMEM. -EEXIST, if there are outstanding VMEM
298 * allocations. -ENOMEM, if platform can't support allocation of `size` bytes.
299 * -EAGAIN, if `size` does not allocate the entire VMEM region. -EIO in case of
300 * internal errors.
301 */
302int vmem_allocate(size_t size, phys_addr_t *addr)
303{
304 int rc = 0, c = 0;
305 resource_size_t max_size = 0;
306
307 if (!vmem) {
308 pr_err("No vmem, try rebooting your device\n");
309 rc = -ENOTSUPP;
310 goto exit;
311 }
312 if (!size) {
313 pr_err("%s Invalid size %ld\n", __func__, size);
314 rc = -EINVAL;
315 goto exit;
316 }
317
318 max_size = resource_size(vmem->mem.resource);
319
320 if (atomic_read(&vmem->alloc_count)) {
321 pr_err("Only single allocations allowed for vmem\n");
322 rc = -EEXIST;
323 goto exit;
324 } else if (size > max_size) {
325 pr_err("Out of memory, have max %pa\n", &max_size);
326 rc = -ENOMEM;
327 goto exit;
328 } else if (size != max_size) {
329 pr_err("Only support allocations of size %pa\n", &max_size);
330 rc = -EAGAIN;
331 goto exit;
332 }
333
334 rc = __power_on(vmem);
335 if (rc) {
336 pr_err("Failed power on (%d)\n", rc);
337 goto exit;
338 }
339
340 WARN_ON(vmem->num_banks != DIV_ROUND_UP(size, vmem->bank_size));
341
342 /* Turn on the necessary banks */
343 for (c = 0; c < vmem->num_banks; ++c) {
344 __bank_set_state(vmem, c, BANK_STATE_NORM_FORCE_CORE_ON);
345 __wait_wakeup(vmem);
346 }
347
348 /* Enable interrupts to detect faults */
349 __enable_interrupts(vmem);
350
351 atomic_inc(&vmem->alloc_count);
352 *addr = (phys_addr_t)vmem->mem.resource->start;
353 return 0;
354exit:
355 return rc;
356}
357
358/**
359 * vmem_free: - Frees the memory allocated via vmem_allocate. Undefined
360 * behaviour if to_free is a not a pointer returned via vmem_allocate
361 */
362void vmem_free(phys_addr_t to_free)
363{
364 int c = 0;
365
366 if (!to_free || !vmem)
367 return;
368
369 WARN_ON(atomic_read(&vmem->alloc_count) == 0);
370
371 for (c = 0; c < vmem->num_banks; ++c) {
372 enum bank_state curr_state = __bank_get_state(vmem, c);
373
374 if (curr_state != BANK_STATE_NORM_FORCE_CORE_ON) {
375 pr_warn("When freeing, expected bank state to be %d, was instead %d\n",
376 BANK_STATE_NORM_FORCE_CORE_ON,
377 curr_state);
378 }
379
380 __bank_set_state(vmem, c, BANK_STATE_SLEEP_NO_RET);
381 }
382
383 __disable_interrupts(vmem);
384 __power_off(vmem);
385 atomic_dec(&vmem->alloc_count);
386}
387
388struct vmem_interrupt_cookie {
389 struct vmem *vmem;
390 struct work_struct work;
391};
392
393static void __irq_helper(struct work_struct *work)
394{
395 struct vmem_interrupt_cookie *cookie = container_of(work,
396 struct vmem_interrupt_cookie, work);
397 struct vmem *v = cookie->vmem;
398 unsigned int stat, gen_stat, pscgc_stat, err_addr_abs,
399 err_addr_rel, err_syn;
400
401 stat = __readl(OCIMEM_INTC_STAT(v));
402 gen_stat = __readl(OCIMEM_GEN_CTL(v));
403 pscgc_stat = __readl(OCIMEM_PSCGC_STAT(v));
404
405 err_addr_abs = __readl(OCIMEM_ERR_ADDRESS(v));
406 err_addr_rel = v->mem.resource->start - err_addr_abs;
407
408 err_syn = __readl(OCIMEM_AXI_ERR_SYNDROME(v));
409
410 pr_crit("Detected a fault on VMEM:\n");
411 pr_cont("\tinterrupt status: %x\n", stat);
412 pr_cont("\tgeneral status: %x\n", gen_stat);
413 pr_cont("\tmemory status: %x\n", pscgc_stat);
414 pr_cont("\tfault address: %x (absolute), %x (relative)\n",
415 err_addr_abs, err_addr_rel);
416 pr_cont("\tfault bank: %x\n", err_addr_rel / v->bank_size);
417 pr_cont("\tfault core: %u (mid), %u (pid), %u (bid)\n",
418 ERR_SYN_AMID(err_syn), ERR_SYN_APID(err_syn),
419 ERR_SYN_ABID(err_syn));
420
421 /* Clear the interrupt */
422 __writel(0, OCIMEM_INTC_CLR(v));
423
424 __enable_interrupts(v);
425}
426
427static struct vmem_interrupt_cookie interrupt_cookie;
428
429static irqreturn_t __irq_handler(int irq, void *cookie)
430{
431 struct vmem *v = cookie;
432 irqreturn_t status = __readl(OCIMEM_INTC_STAT(vmem)) ?
433 IRQ_HANDLED : IRQ_NONE;
434
435 if (status != IRQ_NONE) {
436 /* Mask further interrupts while handling this one */
437 __disable_interrupts(v);
438
439 interrupt_cookie.vmem = v;
440 INIT_WORK(&interrupt_cookie.work, __irq_helper);
441 schedule_work(&interrupt_cookie.work);
442 }
443
444 return status;
445}
446
447static inline int __init_resources(struct vmem *v,
448 struct platform_device *pdev)
449{
450 int rc = 0, c = 0;
451
452 v->irq = platform_get_irq(pdev, 0);
453 if (v->irq < 0) {
454 rc = v->irq;
455 pr_err("Failed to get irq (%d)\n", rc);
456 v->irq = 0;
457 goto exit;
458 }
459
460 /* Registers and memory */
461 v->reg.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
462 "reg-base");
463 if (!v->reg.resource) {
464 pr_err("Failed to find register base\n");
465 rc = -ENOENT;
466 goto exit;
467 }
468
469 v->reg.base = devm_ioremap_resource(&pdev->dev, v->reg.resource);
470 if (IS_ERR_OR_NULL(v->reg.base)) {
471 rc = PTR_ERR(v->reg.base) ?: -EIO;
472 pr_err("Failed to map register base into kernel (%d)\n", rc);
473 v->reg.base = NULL;
474 goto exit;
475 }
476
477 pr_debug("Register range: %pa -> %pa\n", &v->reg.resource->start,
478 &v->reg.resource->end);
479
480 v->mem.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
481 "mem-base");
482 if (!v->mem.resource) {
483 pr_err("Failed to find memory base\n");
484 rc = -ENOENT;
485 goto exit;
486 }
487
488 v->mem.base = NULL;
489 pr_debug("Memory range: %pa -> %pa\n", &v->mem.resource->start,
490 &v->mem.resource->end);
491
492 /* Buses, Clocks & Regulators*/
493 v->num_clocks = of_property_count_strings(pdev->dev.of_node,
494 "clock-names");
495 if (v->num_clocks <= 0) {
496 pr_err("Can't find any clocks\n");
497 goto exit;
498 }
499
500 v->clocks = devm_kzalloc(&pdev->dev, sizeof(*v->clocks) * v->num_clocks,
501 GFP_KERNEL);
502 if (!v->clocks) {
503 rc = -ENOMEM;
504 goto exit;
505 }
506
507 for (c = 0; c < v->num_clocks; ++c) {
508 const char *name = NULL;
509 struct clk *temp = NULL;
510
511 of_property_read_string_index(pdev->dev.of_node, "clock-names",
512 c, &name);
513 temp = devm_clk_get(&pdev->dev, name);
514 if (IS_ERR_OR_NULL(temp)) {
515 rc = PTR_ERR(temp) ?: -ENOENT;
516 pr_err("Failed to find %s (%d)\n", name, rc);
517 goto exit;
518 }
519
520 v->clocks[c].clk = temp;
521 v->clocks[c].name = name;
522 }
523
524 v->vdd = devm_regulator_get(&pdev->dev, "vdd");
525 if (IS_ERR_OR_NULL(v->vdd)) {
526 rc = PTR_ERR(v->vdd) ?: -ENOENT;
527 pr_err("Failed to find regulator (vdd) (%d)\n", rc);
528 goto exit;
529 }
530
531 v->bus.pdata = msm_bus_cl_get_pdata(pdev);
532 if (IS_ERR_OR_NULL(v->bus.pdata)) {
533 rc = PTR_ERR(v->bus.pdata) ?: -ENOENT;
534 pr_err("Failed to find bus vectors (%d)\n", rc);
535 goto exit;
536 }
537
538 v->bus.priv = msm_bus_scale_register_client(v->bus.pdata);
539 if (!v->bus.priv) {
540 rc = -EBADHANDLE;
541 pr_err("Failed to register bus client\n");
542 goto free_pdata;
543 }
544
545 /* Misc. */
546 rc = of_property_read_u32(pdev->dev.of_node, "qcom,bank-size",
547 &v->bank_size);
548 if (rc || !v->bank_size) {
549 pr_err("Failed reading (or found invalid) qcom,bank-size in %s (%d)\n",
550 of_node_full_name(pdev->dev.of_node), rc);
551 rc = -ENOENT;
552 goto free_pdata;
553 }
554
555 v->num_banks = resource_size(v->mem.resource) / v->bank_size;
556
557 pr_debug("Found configuration with %d banks with size %d\n",
558 v->num_banks, v->bank_size);
559
560 return 0;
561free_pdata:
562 msm_bus_cl_clear_pdata(v->bus.pdata);
563exit:
564 return rc;
565}
566
567static inline void __uninit_resources(struct vmem *v,
568 struct platform_device *pdev)
569{
570 int c = 0;
571
572 msm_bus_cl_clear_pdata(v->bus.pdata);
573 v->bus.pdata = NULL;
574 v->bus.priv = 0;
575
576 for (c = 0; c < v->num_clocks; ++c) {
577 v->clocks[c].clk = NULL;
578 v->clocks[c].name = NULL;
579 }
580
581 v->vdd = NULL;
582}
583
584static int vmem_probe(struct platform_device *pdev)
585{
586 uint32_t version = 0, num_banks = 0, rc = 0;
587 struct vmem *v = NULL;
588
589 if (vmem) {
590 pr_err("Only one instance of %s allowed", pdev->name);
591 return -EEXIST;
592 }
593
594 v = devm_kzalloc(&pdev->dev, sizeof(*v), GFP_KERNEL);
595 if (!v)
596 return -ENOMEM;
597
598 rc = __init_resources(v, pdev);
599 if (rc) {
600 pr_err("Failed to read resources\n");
601 goto exit;
602 }
603
604 /*
605 * For now, only support up to 4 banks. It's unrealistic that VMEM has
606 * more banks than that (even in the future).
607 */
608 if (v->num_banks > MAX_BANKS) {
609 pr_err("Number of banks (%d) exceeds what's supported (%d)\n",
610 v->num_banks, MAX_BANKS);
611 rc = -ENOTSUPP;
612 goto exit;
613 }
614
615 /* Cross check the platform resources with what's available on chip */
616 rc = __power_on(v);
617 if (rc) {
618 pr_err("Failed to power on (%d)\n", rc);
619 goto exit;
620 }
621
622 version = __readl(OCIMEM_HW_VERSION(v));
623 pr_debug("v%d.%d.%d\n", VERSION_MAJOR(version), VERSION_MINOR(version),
624 VERSION_STEP(version));
625
626 num_banks = PROFILE_BANKS(__readl(OCIMEM_HW_PROFILE(v)));
627 pr_debug("Found %d banks on chip\n", num_banks);
628 if (v->num_banks != num_banks) {
629 pr_err("Platform configuration of %d banks differs from what's available on chip (%d)\n",
630 v->num_banks, num_banks);
631 rc = -EINVAL;
632 goto disable_clocks;
633 }
634
635 rc = devm_request_irq(&pdev->dev, v->irq, __irq_handler,
636 IRQF_TRIGGER_HIGH, "vmem", v);
637 if (rc) {
638 pr_err("Failed to setup irq (%d)\n", rc);
639 goto disable_clocks;
640 }
641
642 __disable_interrupts(v);
643
644 /* Everything good so far, set up the global context and debug hooks */
645 pr_info("Up and running with %d banks of memory from %pR\n",
646 v->num_banks, &v->mem.resource);
647 v->debugfs_root = vmem_debugfs_init(pdev);
648 platform_set_drvdata(pdev, v);
649 vmem = v;
650
651disable_clocks:
652 __power_off(v);
653exit:
654 return rc;
655}
656
657static int vmem_remove(struct platform_device *pdev)
658{
659 struct vmem *v = platform_get_drvdata(pdev);
660
661 WARN_ON(v != vmem);
662
663 __uninit_resources(v, pdev);
664 vmem_debugfs_deinit(v->debugfs_root);
665 vmem = NULL;
666
667 return 0;
668}
669
670static const struct of_device_id vmem_of_match[] = {
671 {.compatible = "qcom,msm-vmem"},
672 {}
673};
674
675MODULE_DEVICE_TABLE(of, vmem_of_match);
676
677static struct platform_driver vmem_driver = {
678 .probe = vmem_probe,
679 .remove = vmem_remove,
680 .driver = {
681 .name = "msm_vidc_vmem",
682 .owner = THIS_MODULE,
683 .of_match_table = vmem_of_match,
684 },
685};
686
687static int __init vmem_init(void)
688{
689 return platform_driver_register(&vmem_driver);
690}
691
692static void __exit vmem_exit(void)
693{
694 platform_driver_unregister(&vmem_driver);
695}
696
697module_init(vmem_init);
698module_exit(vmem_exit);