blob: 4d5816f9ab3a363eee82f4e22ef25e2715b8ff9e [file] [log] [blame]
/*
* 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 mloader_sysfs_addr(struct device *dev,
struct device_attribute *attr,
char *buf);
static ssize_t mloader_sysfs_finalize(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
static DEVICE_ATTR(addr, S_IRUSR|S_IRGRP, mloader_sysfs_addr, NULL);
static DEVICE_ATTR(finalize, S_IWUSR, NULL, mloader_sysfs_finalize);
static unsigned int bootargs_memmap_modem_start;
static unsigned int bootargs_memmap_modem_total_size;
static unsigned int mloader_helper_shm_total_size;
module_param_named(shm_total_size, mloader_helper_shm_total_size, uint, 0600);
MODULE_PARM_DESC(shm_total_size, "Total Size of SHM shared memory");
static int __init bootargs_memmap(char *str)
{
char *start_val_str;
bootargs_memmap_modem_total_size = simple_strtoul(str, NULL, 0);
start_val_str = strchr(str, '$');
if (start_val_str == NULL)
return -EINVAL;
bootargs_memmap_modem_start = simple_strtoul(start_val_str + 1, NULL, 0);
return 1;
}
__setup("memmap=", bootargs_memmap);
static int __init bootargs_shm_total_size(char *str)
{
mloader_helper_shm_total_size = simple_strtoul(str, NULL, 0);
return 1;
}
__setup(" mloader_helper.shm_total_size=", bootargs_shm_total_size);
static int __exit 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);
return 0;
}
static struct platform_driver mloader_driver = {
.driver = {
.name = "mloader_helper",
},
.remove = __exit_p(mloader_remove),
};
struct mloader_helper {
struct work_struct work;
struct platform_device *pdev;
};
static void mloader_clean_up(struct work_struct *work)
{
struct mloader_helper *m = container_of(work,
struct mloader_helper,
work);
/* Remove this module */
platform_device_unregister(m->pdev);
platform_driver_unregister(&mloader_driver);
kfree(m);
}
static ssize_t mloader_sysfs_addr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "0x%x 0x%x 0x%x\n",
bootargs_memmap_modem_start,
bootargs_memmap_modem_total_size,
mloader_helper_shm_total_size);
}
static ssize_t mloader_sysfs_finalize(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct mloader_helper *m;
m = kmalloc(sizeof(struct mloader_helper), GFP_KERNEL);
m->pdev = container_of(dev,
struct platform_device,
dev);
INIT_WORK(&m->work, 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 mloader_release(struct device *dev)
{
/* Nothing to release */
}
static int __init mloader_probe(struct platform_device *pdev)
{
int ret = 0;
pdev->dev.release = 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;
}
return 0;
}
static int __init mloader_init(void)
{
/*
* mLoader_helper 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(&mloader_driver, mloader_probe);
}
module_init(mloader_init);
static void __exit mloader_exit(void)
{
platform_driver_unregister(&mloader_driver);
}
module_exit(mloader_exit);
MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
MODULE_LICENSE("GPL");