blob: c900647b35bd0474f442768b6db349ec4cd54e9d [file] [log] [blame]
/*
*
* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/fmem.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include "tmem.h"
#include <asm/mach/map.h>
struct fmem_data fmem_data;
enum fmem_state fmem_state;
static spinlock_t fmem_state_lock;
void *fmem_map_virtual_area(int cacheability)
{
unsigned long addr;
const struct mem_type *type;
int ret;
addr = (unsigned long) fmem_data.area->addr;
type = get_mem_type(cacheability);
ret = ioremap_page_range(addr, addr + fmem_data.size,
fmem_data.phys, __pgprot(type->prot_pte));
if (ret)
return ERR_PTR(ret);
fmem_data.virt = fmem_data.area->addr;
return fmem_data.virt;
}
void fmem_unmap_virtual_area(void)
{
unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
fmem_data.virt = NULL;
}
static int fmem_probe(struct platform_device *pdev)
{
struct fmem_platform_data *pdata = pdev->dev.platform_data;
fmem_data.phys = pdata->phys + pdata->reserved_size_low;
fmem_data.size = pdata->size - pdata->reserved_size_low -
pdata->reserved_size_high;
fmem_data.reserved_size_low = pdata->reserved_size_low;
fmem_data.reserved_size_high = pdata->reserved_size_high;
if (!fmem_data.size)
return -ENODEV;
fmem_data.area = get_vm_area(fmem_data.size, VM_IOREMAP);
if (!fmem_data.area)
return -ENOMEM;
if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
remove_vm_area(fmem_data.area->addr);
return -ENOMEM;
}
pr_info("fmem phys %lx virt %p size %lx\n",
fmem_data.phys, fmem_data.virt, fmem_data.size);
spin_lock_init(&fmem_state_lock);
return 0;
}
static int fmem_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver fmem_driver = {
.probe = fmem_probe,
.remove = fmem_remove,
.driver = { .name = "fmem" }
};
#ifdef CONFIG_SYSFS
static ssize_t fmem_state_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
if (fmem_state == FMEM_T_STATE)
return snprintf(buf, 3, "t\n");
else if (fmem_state == FMEM_C_STATE)
return snprintf(buf, 3, "c\n");
else if (fmem_state == FMEM_UNINITIALIZED)
return snprintf(buf, 15, "uninitialized\n");
return snprintf(buf, 3, "?\n");
}
static ssize_t fmem_state_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret = -EINVAL;
if (!strncmp(buf, "t", 1))
ret = fmem_set_state(FMEM_T_STATE);
else if (!strncmp(buf, "c", 1))
ret = fmem_set_state(FMEM_C_STATE);
if (ret)
return ret;
return 1;
}
static struct kobj_attribute fmem_state_attr = {
.attr = { .name = "state", .mode = 0644 },
.show = fmem_state_show,
.store = fmem_state_store,
};
static struct attribute *fmem_attrs[] = {
&fmem_state_attr.attr,
NULL,
};
static struct attribute_group fmem_attr_group = {
.attrs = fmem_attrs,
.name = "fmem",
};
static int fmem_create_sysfs(void)
{
int ret = 0;
ret = sysfs_create_group(mm_kobj, &fmem_attr_group);
if (ret)
pr_err("fmem: can't create sysfs\n");
return ret;
}
#endif
static int __init fmem_init(void)
{
return platform_driver_register(&fmem_driver);
}
static void __exit fmem_exit(void)
{
platform_driver_unregister(&fmem_driver);
}
struct fmem_data *fmem_get_info(void)
{
return &fmem_data;
}
EXPORT_SYMBOL(fmem_get_info);
void lock_fmem_state(void)
{
spin_lock(&fmem_state_lock);
}
void unlock_fmem_state(void)
{
spin_unlock(&fmem_state_lock);
}
int fmem_set_state(enum fmem_state new_state)
{
int ret = 0;
int create_sysfs = 0;
lock_fmem_state();
if (fmem_state == new_state)
goto out;
if (fmem_state == FMEM_UNINITIALIZED) {
if (new_state == FMEM_T_STATE) {
tmem_enable();
create_sysfs = 1;
goto out_set;
}
if (new_state == FMEM_C_STATE) {
ret = -EINVAL;
goto out;
}
}
if (new_state == FMEM_T_STATE) {
void *v;
v = fmem_map_virtual_area(MT_DEVICE_CACHED);
if (IS_ERR_OR_NULL(v)) {
ret = PTR_ERR(v);
goto out;
}
tmem_enable();
} else {
tmem_disable();
fmem_unmap_virtual_area();
}
out_set:
fmem_state = new_state;
out:
unlock_fmem_state();
#ifdef CONFIG_SYSFS
if (create_sysfs)
fmem_create_sysfs();
#endif
return ret;
}
EXPORT_SYMBOL(fmem_set_state);
arch_initcall(fmem_init);
module_exit(fmem_exit);