aboutsummaryrefslogtreecommitdiffstats
path: root/package/lqtapi/src/mps/vmmc-module.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/lqtapi/src/mps/vmmc-module.c')
-rw-r--r--package/lqtapi/src/mps/vmmc-module.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/package/lqtapi/src/mps/vmmc-module.c b/package/lqtapi/src/mps/vmmc-module.c
new file mode 100644
index 0000000000..181f0c6b7c
--- /dev/null
+++ b/package/lqtapi/src/mps/vmmc-module.c
@@ -0,0 +1,130 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <asm/bitops.h>
+
+#include "vmmc-module.h"
+
+int vmmc_module_init(struct vmmc_module *module, size_t num_pins,
+ const struct vmmc_module_ops *ops)
+{
+ module->pins = kcalloc(num_pins, sizeof(*module->pins), GFP_KERNEL);
+
+ if (!module->pins)
+ return -ENOMEM;
+
+ module->num_pins = num_pins;
+ module->ops = ops;
+
+ mutex_init(&module->lock);
+ module->refcount = 0;
+
+ return 0;
+}
+
+int vmmc_module_sync(struct vmmc_module *module)
+{
+ if (!test_and_clear_bit(VMMC_MODULE_FLAG_MODIFIED, &module->flags))
+ return 0;
+
+ return module->ops->sync(module);
+}
+
+int vmmc_module_get_pin(struct vmmc_module *module)
+{
+ size_t i = 0;
+ int ret = 0;
+
+ for (i = 0; i < module->num_pins; ++i) {
+ if (!test_and_set_bit(VMMC_MODULE_FLAG_PIN_USED(i), &module->flags))
+ break;
+ }
+ if (i == module->num_pins)
+ ret = -EBUSY;
+ else
+ ret = i;
+
+ return ret;
+}
+
+void vmmc_module_put_pin(struct vmmc_module *module, unsigned int pin)
+{
+ module->pins[pin] = 0;
+ clear_bit(VMMC_MODULE_FLAG_PIN_USED(pin), &module->flags);
+}
+
+void vmmc_module_set_pin_input(struct vmmc_module *module, unsigned int pin,
+ struct vmmc_module *input)
+{
+ if (input)
+ module->pins[pin] = input->id;
+ else
+ module->pins[pin] = 0;
+
+ set_bit(VMMC_MODULE_FLAG_MODIFIED, &module->flags);
+}
+
+static void vmmc_module_enable(struct vmmc_module *module)
+{
+ mutex_lock(&module->lock);
+
+ if (++module->refcount == 1)
+ module->ops->enable(module, true);
+
+ mutex_unlock(&module->lock);
+}
+
+static void vmmc_module_disable(struct vmmc_module *module)
+{
+ mutex_lock(&module->lock);
+
+ if (module->refcount <= 0)
+ printk(KERN_ERR "vmmc module: unbalanced disable\n");
+ else if (--module->refcount == 0)
+ module->ops->enable(module, false);
+
+ mutex_unlock(&module->lock);
+}
+
+
+unsigned int vmmc_link_init(struct vmmc_link *link,
+ struct vmmc_module *a, struct vmmc_module *b)
+{
+ link->pins[0] = vmmc_module_get_pin(a);
+ link->pins[1] = vmmc_module_get_pin(b);
+ link->modules[0] = a;
+ link->modules[1] = b;
+
+ return 0;
+}
+
+void vmmc_link_put(struct vmmc_link *link)
+{
+ vmmc_link_disable(link);
+ vmmc_module_sync(link->modules[0]);
+ vmmc_module_sync(link->modules[1]);
+ vmmc_module_put_pin(link->modules[0], link->pins[0]);
+ vmmc_module_put_pin(link->modules[1], link->pins[1]);
+}
+
+void vmmc_link_enable(struct vmmc_link *link)
+{
+ vmmc_module_set_pin_input(link->modules[0], link->pins[0],
+ link->modules[1]);
+ vmmc_module_set_pin_input(link->modules[1], link->pins[1],
+ link->modules[0]);
+
+ vmmc_module_enable(link->modules[0]);
+ vmmc_module_enable(link->modules[1]);
+}
+
+void vmmc_link_disable(struct vmmc_link *link)
+{
+ vmmc_module_set_pin_input(link->modules[0], link->pins[0], NULL);
+ vmmc_module_set_pin_input(link->modules[1], link->pins[1], NULL);
+
+ vmmc_module_disable(link->modules[0]);
+ vmmc_module_disable(link->modules[1]);
+}