[ifxmips]
[openwrt.git] / package / lqtapi / src / mps / vmmc-module.c
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/slab.h>
4 #include <linux/mutex.h>
5
6 #include <asm/bitops.h>
7
8 #include "vmmc-module.h"
9
10 int vmmc_module_init(struct vmmc_module *module, size_t num_pins,
11         const struct vmmc_module_ops *ops)
12 {
13         module->pins = kcalloc(num_pins, sizeof(*module->pins), GFP_KERNEL);
14
15         if (!module->pins)
16                 return -ENOMEM;
17
18         module->num_pins = num_pins;
19         module->ops = ops;
20
21         mutex_init(&module->lock);
22         module->refcount = 0;
23
24         return 0;
25 }
26
27 int vmmc_module_sync(struct vmmc_module *module)
28 {
29         if (!test_and_clear_bit(VMMC_MODULE_FLAG_MODIFIED, &module->flags))
30                 return 0;
31
32         return module->ops->sync(module);
33 }
34
35 int vmmc_module_get_pin(struct vmmc_module *module)
36 {
37         size_t i = 0;
38         int ret = 0;
39
40         for (i = 0; i < module->num_pins; ++i) {
41                 if (!test_and_set_bit(VMMC_MODULE_FLAG_PIN_USED(i), &module->flags))
42                         break;
43         }
44         if (i == module->num_pins)
45                 ret = -EBUSY;
46         else
47                 ret = i;
48
49         return ret;
50 }
51
52 void vmmc_module_put_pin(struct vmmc_module *module, unsigned int pin)
53 {
54         module->pins[pin] = 0;
55         clear_bit(VMMC_MODULE_FLAG_PIN_USED(pin), &module->flags);
56 }
57
58 void vmmc_module_set_pin_input(struct vmmc_module *module, unsigned int pin,
59         struct vmmc_module *input)
60 {
61         if (input)
62                 module->pins[pin] = input->id;
63         else
64                 module->pins[pin] = 0;
65
66         set_bit(VMMC_MODULE_FLAG_MODIFIED, &module->flags);
67 }
68
69 static void vmmc_module_enable(struct vmmc_module *module)
70 {
71         mutex_lock(&module->lock);
72
73         if (++module->refcount == 1)
74                 module->ops->enable(module, true);
75
76         mutex_unlock(&module->lock);
77 }
78
79 static void vmmc_module_disable(struct vmmc_module *module)
80 {
81         mutex_lock(&module->lock);
82
83         if (module->refcount <= 0)
84                 printk(KERN_ERR "vmmc module: unbalanced disable\n");
85         else if (--module->refcount == 0)
86                 module->ops->enable(module, false);
87
88         mutex_unlock(&module->lock);
89 }
90
91
92 unsigned int vmmc_link_init(struct vmmc_link *link,
93         struct vmmc_module *a, struct vmmc_module *b)
94 {
95         link->pins[0] = vmmc_module_get_pin(a);
96         link->pins[1] = vmmc_module_get_pin(b);
97         link->modules[0] = a;
98         link->modules[1] = b;
99
100         return 0;
101 }
102
103 void vmmc_link_put(struct vmmc_link *link)
104 {
105         vmmc_link_disable(link);
106         vmmc_module_sync(link->modules[0]);
107         vmmc_module_sync(link->modules[1]);
108         vmmc_module_put_pin(link->modules[0], link->pins[0]);
109         vmmc_module_put_pin(link->modules[1], link->pins[1]);
110 }
111
112 void vmmc_link_enable(struct vmmc_link *link)
113 {
114         vmmc_module_set_pin_input(link->modules[0], link->pins[0],
115                 link->modules[1]);
116         vmmc_module_set_pin_input(link->modules[1], link->pins[1],
117                 link->modules[0]);
118
119         vmmc_module_enable(link->modules[0]);
120         vmmc_module_enable(link->modules[1]);
121 }
122
123 void vmmc_link_disable(struct vmmc_link *link)
124 {
125         vmmc_module_set_pin_input(link->modules[0], link->pins[0], NULL);
126         vmmc_module_set_pin_input(link->modules[1], link->pins[1], NULL);
127
128         vmmc_module_disable(link->modules[0]);
129         vmmc_module_disable(link->modules[1]);
130 }