SmartAudio/lichee/linux-4.9/drivers/char/oases/pmem.c

213 lines
4.7 KiB
C
Executable File

/*
* Sysfs for OASES framework
*
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
*
* You should have received a copy of license along with this program;
* if not, ask for it from Baidu, Inc.
*
*/
#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kmsg_dump.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/version.h>
#include "pmem.h"
#include "util.h"
int oases_boot_reason = -1;
struct oases_pmem_data {
phys_addr_t phys_addr;
unsigned phys_size;
void *virt_addr;
};
static struct oases_pmem *g_oases_pmem = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
static void oases_dump(struct kmsg_dumper *dump, enum kmsg_dump_reason reason,
const char *s1, unsigned long l1, const char *s2, unsigned long l2)
#else
static void oases_dump(struct kmsg_dumper *dump,
enum kmsg_dump_reason reason)
#endif
{
if (reason != KMSG_DUMP_PANIC)
return;
if (g_oases_pmem) {
struct oases_pmem tmp;
memset(&tmp, 0, sizeof(tmp));
memcpy(tmp.magic, OASES_PMEM_MAGIC, 16);
tmp.boot |= OASES_KDUMP_PANIC;
memcpy_toio(g_oases_pmem, &tmp, sizeof(tmp));
}
}
static struct kmsg_dumper oases_kmsg_dump = {
.dump = oases_dump,
};
static int pmem_load_dt(struct device *dev, struct device_node *of)
{
int rc;
struct oases_pmem_data *pdata;
u32 val;
if (!of) {
oases_debug("OASES DT not configured\n");
return -EINVAL;
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL) {
oases_debug("devm_kzalloc failed\n");
return -ENOMEM;
}
rc = of_property_read_u32(of, "oases,pmem_phys", &val);
if (rc) {
oases_debug("oases,pmem_phys not configured\n");
goto fail_of;
}
pdata->phys_addr = val;
rc = of_property_read_u32(of, "oases,pmem_size", &val);
if (rc) {
oases_debug("oases,pmem_size not configured\n");
goto fail_of;
}
pdata->phys_size = val;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
rc = dev_set_drvdata(dev, pdata);
if (rc) {
oases_debug("dev_set_drvdata failed\n");
goto fail_drvdata;
}
#else
dev_set_drvdata(dev, pdata);
#endif
return 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
fail_drvdata:
#endif
fail_of:
if (pdata)
kfree(pdata);
return -EINVAL;
}
static int pmem_config(struct oases_pmem_data *pdata)
{
struct oases_pmem tmp, *p;
pdata->virt_addr = ioremap(pdata->phys_addr, pdata->phys_size);
if (!pdata->virt_addr) {
oases_debug("pmem ioremap failed\n");
return -EINVAL;
}
p = pdata->virt_addr;
memcpy_fromio(&tmp, p, sizeof(*p));
if (!memcmp(tmp.magic, OASES_PMEM_MAGIC, 16)) {
oases_debug("pmem magic match\n");
oases_boot_reason = tmp.boot;
} else {
/* treat cold boot as a normal *hot* reboot */
oases_boot_reason = 0;
}
/* reset pmem */
memset(&tmp, 0, sizeof(tmp));
memcpy(tmp.magic, OASES_PMEM_MAGIC, 16);
memcpy_toio(p, &tmp, sizeof(*p));
return 0;
}
static int oases_pmem_probe(struct platform_device *pdev)
{
int rc;
struct device *dev = &pdev->dev;
struct device_node *of = pdev->dev.of_node;
struct oases_pmem_data *pdata;
rc = pmem_load_dt(dev, of);
if (rc) {
oases_error("pmem_load_dt() failed\n");
goto fail_load_dt;
}
pdata = dev_get_drvdata(dev);
rc = pmem_config(pdata);
if (rc) {
oases_error("pmem_config() failed\n");
goto fail_config;
}
/* for oases_dump() */
g_oases_pmem = pdata->virt_addr;
rc = kmsg_dump_register(&oases_kmsg_dump);
if (rc) {
oases_error("kmsg_dump_register failed\n");
goto fail_reg;
}
oases_info("oases_pmem_probe success\n");
return 0;
fail_reg:
iounmap(pdata->virt_addr);
fail_config:
dev_set_drvdata(dev, NULL);
kfree(pdata);
fail_load_dt:
return rc;
}
static int oases_pmem_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct oases_pmem_data *pdata;
oases_info("oases_pmem_remove\n");
g_oases_pmem = NULL;
kmsg_dump_unregister(&oases_kmsg_dump);
pdata = dev_get_drvdata(dev);
dev_set_drvdata(dev, NULL);
iounmap(pdata->virt_addr);
kfree(pdata);
return 0;
}
static const struct of_device_id oases_pmem_dt[] = {
{ .compatible = "oases_pmem" },
{ },
};
static struct platform_driver oases_pmem_driver = {
.probe = oases_pmem_probe,
.remove = oases_pmem_remove,
.driver = {
.name = "oases_pmem",
.of_match_table = oases_pmem_dt,
},
};
static int __init oases_pmem_init(void)
{
return platform_driver_register(&oases_pmem_driver);
}
static void __exit oases_pmem_exit(void)
{
platform_driver_unregister(&oases_pmem_driver);
}
module_init(oases_pmem_init);
module_exit(oases_pmem_exit);
MODULE_AUTHOR("Baidu, Inc.");
MODULE_DESCRIPTION("OASES - Open Adaptive Security Extensions");