summaryrefslogtreecommitdiff
path: root/arch/arm/mach-ux500/mloader-db5500.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-ux500/mloader-db5500.c')
-rw-r--r--arch/arm/mach-ux500/mloader-db5500.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/arch/arm/mach-ux500/mloader-db5500.c b/arch/arm/mach-ux500/mloader-db5500.c
new file mode 100644
index 00000000000..bc3a57af28b
--- /dev/null
+++ b/arch/arm/mach-ux500/mloader-db5500.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors: Jonas Aaberg <jonas.aberg@stericsson.com>
+ * Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
+ * for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+static ssize_t db5500_mloader_sysfs_addr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t db5500_mloader_sysfs_finalize(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t db5500_mloader_sysfs_itpmode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static DEVICE_ATTR(addr, S_IRUSR|S_IRGRP, db5500_mloader_sysfs_addr, NULL);
+static DEVICE_ATTR(finalize, S_IWUSR, NULL, db5500_mloader_sysfs_finalize);
+static DEVICE_ATTR(is_itpmode, S_IRUSR|S_IRGRP, db5500_mloader_sysfs_itpmode, NULL);
+
+static unsigned int db5500_bootargs_memmap_modem_start;
+static unsigned int db5500_bootargs_memmap_modem_total_size;
+static unsigned int db5500_mloader_itpmode;
+static unsigned int db5500_mloader_shm_total_size;
+module_param_named(shm_total_size, db5500_mloader_shm_total_size, uint, 0600);
+MODULE_PARM_DESC(shm_total_size, "Total Size of SHM shared memory");
+
+static int __init db5500_bootargs_modem_memmap(char *p)
+{
+ db5500_bootargs_memmap_modem_total_size = memparse(p, &p);
+ if (*p == '@')
+ db5500_bootargs_memmap_modem_start = memparse(p + 1, &p);
+
+ return 0;
+}
+early_param("mem_modem", db5500_bootargs_modem_memmap);
+
+static int __init db5500_bootargs_shm_total_size(char *str)
+{
+ int ret;
+ ret = strict_strtoul(str, 0, &db5500_mloader_shm_total_size);
+ if (ret < 0)
+ return -EINVAL;
+ return 1;
+}
+early_param("mloader.shm_total_size", db5500_bootargs_shm_total_size);
+
+static int __init db5500_bootargs_itpmode(char *p)
+{
+ int ret;
+ int count = 3;
+ if (!memcmp(p, "itp", count))
+ db5500_mloader_itpmode = true;
+ else
+ db5500_mloader_itpmode = false;
+ return 1;
+}
+early_param("modem_boot_type", db5500_bootargs_itpmode);
+
+static int __exit db5500_mloader_remove(struct platform_device *pdev)
+{
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr);
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_finalize.attr);
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_is_itpmode.attr);
+
+ return 0;
+}
+
+
+static struct platform_driver db5500_mloader_driver = {
+ .driver = {
+ .name = "db5500_mloader",
+ },
+ .remove = __exit_p(db5500_mloader_remove),
+};
+
+struct db5500_mloader {
+ struct work_struct work;
+ struct platform_device *pdev;
+};
+
+static void db5500_mloader_clean_up(struct work_struct *work)
+{
+ struct db5500_mloader *m = container_of(work,
+ struct db5500_mloader,
+ work);
+
+ /* Remove this module */
+ platform_device_unregister(m->pdev);
+
+ platform_driver_unregister(&db5500_mloader_driver);
+ kfree(m);
+
+}
+
+static ssize_t db5500_mloader_sysfs_addr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%x 0x%x 0x%x\n",
+ db5500_bootargs_memmap_modem_start,
+ db5500_bootargs_memmap_modem_total_size,
+ db5500_mloader_shm_total_size);
+}
+
+static ssize_t db5500_mloader_sysfs_itpmode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%x\n",
+ db5500_mloader_itpmode);
+}
+
+static ssize_t db5500_mloader_sysfs_finalize(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct db5500_mloader *m;
+
+ m = kmalloc(sizeof(struct db5500_mloader), GFP_KERNEL);
+
+ m->pdev = container_of(dev,
+ struct platform_device,
+ dev);
+
+ INIT_WORK(&m->work, db5500_mloader_clean_up);
+
+ /* The module can not remove itself while being in a sysfs function,
+ * it has to use a workqueue.
+ */
+ schedule_work(&m->work);
+
+ return count;
+}
+
+static void db5500_mloader_release(struct device *dev)
+{
+ /* Nothing to release */
+}
+
+static int __init db5500_mloader_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ pdev->dev.release = db5500_mloader_release;
+
+ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_addr.attr);
+ if (ret)
+ return ret;
+ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_finalize.attr);
+
+ if (ret) {
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr);
+ return ret;
+ }
+ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_is_itpmode.attr);
+ if (ret) {
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_finalize.attr);
+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr);
+ return ret;
+ }
+ return 0;
+
+}
+
+static int __init db5500_mloader_init(void)
+{
+/*
+ * mloader for Fairbanks. It exports the physical
+ * address where the modem side ELF should be located in a sysfs
+ * file to make it available for a user space utility.
+ * When the mLoader utility has picked up these settings, this module is no
+ * longer needed and can be removed by writing to sysfs finalize.
+ *
+ * The modem side should be loaded via mmap'ed /dev/mem
+ *
+ */
+
+ return platform_driver_probe(&db5500_mloader_driver,
+ db5500_mloader_probe);
+}
+module_init(db5500_mloader_init);
+
+
+static void __exit mloader_exit(void)
+{
+ platform_driver_unregister(&db5500_mloader_driver);
+}
+module_exit(mloader_exit);
+
+MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
+MODULE_LICENSE("GPL");