blob: a0c14ec159b4a3f2942fe9b6c57dcdd8a7000936 [file] [log] [blame]
/*
* Implementation of the GPS EMI driver.
*
* Copyright (C) 2014 Mediatek
* Authors:
* Heiping <Heiping.Lei@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/******************************************************************************
* Dependency
******************************************************************************/
#ifdef CONFIG_MTK_GPS_EMI
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/version.h>
#include <asm/memblock.h>
#include <mach/emi_mpu.h>
#include "gps.h"
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt
/******************************************************************************
* Definition
******************************************************************************/
/* device name and major number */
#define GPSEMI_DEVNAME "gps_emi"
#define IOCTL_MNL_IMAGE_FILE_TO_MEM 1
#define IOCTL_MNL_NVRAM_FILE_TO_MEM 2
#define IOCTL_MNL_NVRAM_MEM_TO_FILE 3
/******************************************************************************
* Debug configuration
******************************************************************************/
#define GPS_DBG_NONE(fmt, arg...) do {} while (0)
#define GPS_DBG pr_err
#define GPS_TRC GPS_DBG_NONE
#define GPS_VER pr_err
#define GPS_ERR pr_err
/******************************************************************************
* structure & enumeration
******************************************************************************/
/*---------------------------------------------------------------------------*/
struct gps_emi_dev {
struct class *cls;
struct device *dev;
dev_t devno;
struct cdev chdev;
};
/*typedef unsigned char UINT8, *PUINT8, **PPUINT8;*/
/******************************************************************************
* local variables
******************************************************************************/
phys_addr_t gGpsEmiPhyBase;
UINT8 __iomem *pGpsEmibaseaddr;
struct gps_emi_dev *devobj;
#define EMI_MPU_PROTECTION_IS_READY 1
void mtk_wcn_consys_gps_memory_reserve(void)
{
#if 0
#ifdef MTK_WCN_ARM64
gGpsEmiPhyBase = arm64_memblock_steal(SZ_1M, SZ_1M);
#else
gGpsEmiPhyBase = arm_memblock_steal(SZ_1M, SZ_1M);
#endif
#else
gGpsEmiPhyBase = gConEmiPhyBase + SZ_1M;
#endif
if (gGpsEmiPhyBase)
GPS_DBG("memblock done: 0x%zx\n", (size_t)gGpsEmiPhyBase);
else
GPS_DBG("memblock fail\n");
}
INT32 mtk_wcn_consys_gps_emi_init(void)
{
INT32 iRet = -1;
mtk_wcn_consys_gps_memory_reserve();
if (gGpsEmiPhyBase) {
#if CONSYS_EMI_MPU_SETTING
/*set MPU for EMI share Memory*/
GPS_DBG("setting MPU for EMI share memory\n");
#if EMI_MPU_PROTECTION_IS_READY
emi_mpu_set_region_protection(gGpsEmiPhyBase,
gGpsEmiPhyBase + SZ_1M - 1,
20,
SET_ACCESS_PERMISSON(FORBIDDEN, FORBIDDEN,
FORBIDDEN, FORBIDDEN, FORBIDDEN, NO_PROTECTION,
FORBIDDEN, NO_PROTECTION));
#endif
#endif
GPS_DBG("get consys start phy address(0x%zx)\n",
(size_t)gGpsEmiPhyBase);
#if 0
/*consys to ap emi remapping register:10001310,
*cal remapping address
*/
addrPhy = (gGpsEmiPhyBase & 0xFFF00000) >> 20;
/*enable consys to ap emi remapping bit12*/
addrPhy -= 0x400;/*Gavin ??*/
addrPhy = addrPhy | 0x1000;
CONSYS_REG_WRITE(conn_reg.topckgen_base +
CONSYS_EMI_MAPPING_OFFSET, CONSYS_REG_READ
(conn_reg.topckgen_base + CONSYS_EMI_MAPPING_OFFSET)
| addrPhy);
GPS_DBG("GPS_EMI_MAPPING dump(0x%08x)\n",
CONSYS_REG_READ(conn_reg.topckgen_base
+ CONSYS_EMI_MAPPING_OFFSET));
#endif
pGpsEmibaseaddr = ioremap_nocache(gGpsEmiPhyBase, SZ_1M);
if (pGpsEmibaseaddr != NULL) {
UINT8 *pFullPatchName = "/system/etc/firmware/MNL.bin";
firmware *pPatch = NULL;
GPS_DBG("EMI mapping OK(0x%p)\n", pGpsEmibaseaddr);
memset_io(pGpsEmibaseaddr, 0, SZ_1M);
if ((pFullPatchName != NULL)
&& (wmt_dev_patch_get(pFullPatchName, &pPatch,
0/*BCNT_PATCH_BUF_HEADROOM*/) == 0)) {
if (pPatch != NULL) {
/*get full name patch success*/
GPS_DBG("get full patch name(%s) ",
pFullPatchName);
GPS_DBG("buf(0x%p) ", (pPatch)->data);
GPS_DBG("size(%ld)\n", (pPatch)->size);
GPS_DBG("AF get patch, pPatch(0x%p)\n",
pPatch);
}
}
if (pPatch != NULL) {
if ((pPatch)->size <= SZ_1M) {
memcpy(pGpsEmibaseaddr,
(pPatch)->data, (pPatch)->size);
iRet = 1;
}
}
} else {
GPS_DBG("EMI mapping fail\n");
}
} else {
GPS_DBG("gps emi memory address gGpsEmiPhyBase invalid\n");
}
return iRet;
}
/*---------------------------------------------------------------------------*/
long gps_emi_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int retval = 0;
GPS_DBG("cmd (%d),arg(%ld)\n", cmd, arg);
switch (cmd) {
case IOCTL_MNL_IMAGE_FILE_TO_MEM:
retval = mtk_wcn_consys_gps_emi_init();
GPS_DBG("IOCTL_MNL_IMAGE_FILE_TO_MEM\n");
break;
case IOCTL_MNL_NVRAM_FILE_TO_MEM:
GPS_DBG("IOCTL_MNL_NVRAM_FILE_TO_MEM\n");
break;
case IOCTL_MNL_NVRAM_MEM_TO_FILE:
GPS_DBG("IOCTL_MNL_NVRAM_MEM_TO_FILE\n");
break;
default:
GPS_DBG("unknown cmd (%d)\n", cmd);
retval = 0;
break;
}
return retval;
}
/******************************************************************************/
/*****************************************************************************/
static int gps_emi_open(struct inode *inode, struct file *file)
{
GPS_TRC();
return nonseekable_open(inode, file);
}
/*****************************************************************************/
/*****************************************************************************/
static int gps_emi_release(struct inode *inode, struct file *file)
{
GPS_TRC();
return 0;
}
/******************************************************************************/
static ssize_t gps_emi_read(struct file *file,
char __user *buf, size_t count, loff_t *ppos)
{
ssize_t ret = 0;
GPS_TRC();
return ret;
}
/******************************************************************************/
static ssize_t gps_emi_write(struct file *file,
const char __user *buf, size_t count,
loff_t *ppos)
{
ssize_t ret = 0;
GPS_TRC();
return ret;
}
/*****************************************************************************/
/* Kernel interface */
static const struct file_operations gps_emi_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = gps_emi_unlocked_ioctl,
.open = gps_emi_open,
.read = gps_emi_read,
.write = gps_emi_write,
.release = gps_emi_release,
};
/*****************************************************************************/
static int gps_emi_probe(struct platform_device *dev)
{
int ret = 0, err = 0;
devobj = kzalloc(sizeof(*devobj), GFP_KERNEL);
if (devobj == NULL) {
err = -ENOMEM;
ret = -ENOMEM;
goto err_out;
}
GPS_DBG("Registering chardev\n");
ret = alloc_chrdev_region(&devobj->devno, 0, 1, GPSEMI_DEVNAME);
if (ret) {
GPS_ERR("alloc_chrdev_region fail: %d\n", ret);
kfree(devobj);
err = -ENOMEM;
goto err_out;
} else {
GPS_DBG("major: %d, minor: %d\n",
MAJOR(devobj->devno), MINOR(devobj->devno));
}
cdev_init(&devobj->chdev, &gps_emi_fops);
devobj->chdev.owner = THIS_MODULE;
err = cdev_add(&devobj->chdev, devobj->devno, 1);
if (err) {
GPS_ERR("cdev_add fail: %d\n", err);
kfree(devobj);
goto err_out;
}
devobj->cls = class_create(THIS_MODULE, "gpsemi");
if (IS_ERR(devobj->cls)) {
GPS_ERR("Unable to create class, err = %d\n",
(int)PTR_ERR(devobj->cls));
kfree(devobj);
goto err_out;
}
devobj->dev = device_create(devobj->cls, NULL,
devobj->devno, devobj, "gps_emi");
GPS_DBG("Done\n");
return 0;
err_out:
if (err == 0)
cdev_del(&devobj->chdev);
if (ret == 0)
unregister_chrdev_region(devobj->devno, 1);
return -1;
}
/*****************************************************************************/
static int gps_emi_remove(struct platform_device *dev)
{
if (!devobj) {
GPS_ERR("null pointer: %p\n", devobj);
return -1;
}
GPS_DBG("Unregistering chardev\n");
cdev_del(&devobj->chdev);
unregister_chrdev_region(devobj->devno, 1);
device_destroy(devobj->cls, devobj->devno);
class_destroy(devobj->cls);
kfree(devobj);
GPS_DBG("Done\n");
return 0;
}
/*****************************************************************************/
#ifdef CONFIG_PM
/*****************************************************************************/
static int gps_emi_suspend(struct platform_device *dev, pm_message_t state)
{
GPS_DBG("dev = %p, event = %u,", dev, state.event);
if (state.event == PM_EVENT_SUSPEND)
GPS_DBG("Receive PM_EVENT_SUSPEND!!\n");
return 0;
}
/*****************************************************************************/
static int gps_emi_resume(struct platform_device *dev)
{
GPS_DBG("");
return 0;
}
/*****************************************************************************/
#endif /* CONFIG_PM */
/*****************************************************************************/
#ifdef CONFIG_OF
static const struct of_device_id apgps_of_ids[] = {
{ .compatible = "mediatek,gps_emi-v1", },
{}
};
#endif
static struct platform_driver gps_emi_driver = {
.probe = gps_emi_probe,
.remove = gps_emi_remove,
#if defined(CONFIG_PM)
.suspend = gps_emi_suspend,
.resume = gps_emi_resume,
#endif
.driver = {
.name = GPSEMI_DEVNAME,
.bus = &platform_bus_type,
#ifdef CONFIG_OF
.of_match_table = apgps_of_ids,
#endif
},
};
/*****************************************************************************/
static int __init gps_emi_mod_init(void)
{
int ret = 0;
GPS_TRC();
ret = platform_driver_register(&gps_emi_driver);
return ret;
}
/*****************************************************************************/
static void __exit gps_emi_mod_exit(void)
{
GPS_TRC();
platform_driver_unregister(&gps_emi_driver);
}
/*****************************************************************************/
module_init(gps_emi_mod_init);
module_exit(gps_emi_mod_exit);
/*****************************************************************************/
MODULE_AUTHOR("Heiping Lei <Heiping.Lei@mediatek.com>");
MODULE_DESCRIPTION("GPS EMI Driver");
MODULE_LICENSE("GPL");
#endif