534 lines
14 KiB
C
534 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 MediaTek Inc.
|
|
* Copyright (c) 2021 Eric Lin <tesheng.lin@mediatek.com>
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/io.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/delay.h>
|
|
|
|
|
|
#define FAKE_ENG_EN 0x0
|
|
#define FAKE_ENG_RST 0x4
|
|
#define FAKE_ENG_DONE 0x8
|
|
#define FAKE_ENG_CON0 0xc
|
|
#define FAKE_ENG_CON1 0x10
|
|
#define FAKE_ENG_CON2 0x14
|
|
#define FAKE_ENG_CON3 0x18
|
|
#define FAKE_ENG_START_ADDR 0x1c
|
|
#define FAKE_ENG_START_ADDR_2ND 0x20
|
|
#define FAKE_ENG_ADDR 0x24
|
|
#define FAKE_ENG_INIT_PAT0 0x28
|
|
#define FAKE_ENG_INIT_PAT1 0x2c
|
|
#define FAKE_ENG_INIT_PAT2 0x30
|
|
#define FAKE_ENG_INIT_PAT3 0x34
|
|
#define FAKE_ENG_INIT_PAT4 0x38
|
|
#define FAKE_ENG_INIT_PAT5 0x3c
|
|
#define FAKE_ENG_INIT_PAT6 0x40
|
|
#define FAKE_ENG_INIT_PAT7 0x44
|
|
#define FAKE_ENG_CMP_RESULT 0x48
|
|
#define FAKE_ENG_FREEZE_RESULT 0xb0
|
|
#define FAKE_ENG_IDLE 0xb4
|
|
#define FAKE_ENG_HASH 0xb8
|
|
#define FAKE_ENG_START_ADDR_RD 0xbc
|
|
#define FAKE_ENG_START_ADDR_RD_2ND 0xc0
|
|
#define FAKE_ENG_ADDR_RD 0xc4
|
|
|
|
#define SIZE 256
|
|
|
|
struct fake_eng_set {
|
|
unsigned int chn_number;
|
|
unsigned int rd_dis;
|
|
unsigned int wr_dis;
|
|
unsigned int loop_en;
|
|
unsigned int cross_rk_en;
|
|
unsigned int data_cmp_en;
|
|
unsigned int pat_mode;
|
|
unsigned int rd_wr_joint_en;
|
|
unsigned int rd_amount;
|
|
unsigned int wr_amount;
|
|
unsigned int slow_down;
|
|
unsigned int slow_down_grp;
|
|
unsigned int burst_len;
|
|
unsigned int burst_size;
|
|
unsigned int aw_slc;
|
|
unsigned int ar_slc;
|
|
unsigned int grp_aomunt;
|
|
unsigned int start_addr_wr;
|
|
unsigned int start_addr_rd;
|
|
unsigned int start_addr_wr_2nd;
|
|
unsigned int start_addr_rd_2nd;
|
|
unsigned int start_addr_wr_extend;
|
|
unsigned int start_addr_rd_extend;
|
|
unsigned int start_addr_wr_2nd_extend;
|
|
unsigned int start_addr_rd_2nd_extend;
|
|
unsigned int addr_offset1;
|
|
unsigned int addr_offset2;
|
|
unsigned int init_pat0;
|
|
unsigned int init_pat1;
|
|
unsigned int init_pat2;
|
|
unsigned int init_pat3;
|
|
unsigned int init_pat4;
|
|
unsigned int init_pat5;
|
|
unsigned int init_pat6;
|
|
unsigned int init_pat7;
|
|
unsigned int freeze_en;
|
|
};
|
|
|
|
struct emi_fake_eng {
|
|
void __iomem **fake_eng_base;
|
|
void **k_addr;
|
|
unsigned int emi_fake_eng_cnt;
|
|
unsigned int bitmap;
|
|
struct fake_eng_set feng_arg;
|
|
};
|
|
|
|
/* global pointer for sysfs operations*/
|
|
static struct emi_fake_eng *fakeng;
|
|
|
|
static int fake_eng_init(unsigned int chn_id)
|
|
{
|
|
unsigned long phy_addr;
|
|
u32 val;
|
|
|
|
/* Basic setting for fake engine */
|
|
fakeng->feng_arg.chn_number = 0;
|
|
fakeng->feng_arg.rd_dis = 1;
|
|
fakeng->feng_arg.wr_dis = 1;
|
|
fakeng->feng_arg.loop_en = 0;
|
|
fakeng->feng_arg.cross_rk_en = 0;
|
|
fakeng->feng_arg.data_cmp_en = 0;
|
|
fakeng->feng_arg.pat_mode = 5;
|
|
fakeng->feng_arg.rd_wr_joint_en = 0;
|
|
fakeng->feng_arg.rd_amount = 30;
|
|
fakeng->feng_arg.wr_amount = 30;
|
|
fakeng->feng_arg.slow_down = 0;
|
|
fakeng->feng_arg.slow_down_grp = 0;
|
|
fakeng->feng_arg.burst_len = 7;
|
|
fakeng->feng_arg.burst_size = 4;
|
|
fakeng->feng_arg.aw_slc = 0;
|
|
fakeng->feng_arg.ar_slc = 0;
|
|
fakeng->feng_arg.grp_aomunt = 15;
|
|
fakeng->feng_arg.start_addr_wr = 0x40000000;
|
|
fakeng->feng_arg.start_addr_rd = 0x80000000;
|
|
fakeng->feng_arg.start_addr_wr_2nd = 0x40000000;
|
|
fakeng->feng_arg.start_addr_rd_2nd = 0x80000000;
|
|
fakeng->feng_arg.start_addr_wr_extend = 0x00000000;
|
|
fakeng->feng_arg.start_addr_rd_extend = 0x00000000;
|
|
fakeng->feng_arg.start_addr_wr_2nd_extend = 0x0;
|
|
fakeng->feng_arg.start_addr_rd_2nd_extend = 0x0;
|
|
fakeng->feng_arg.addr_offset1 = 0x60;
|
|
fakeng->feng_arg.addr_offset2 = 0;
|
|
fakeng->feng_arg.init_pat0 = 0x0000ffff;
|
|
fakeng->feng_arg.init_pat1 = 0x0000ffff;
|
|
fakeng->feng_arg.init_pat2 = 0x0000ffff;
|
|
fakeng->feng_arg.init_pat3 = 0x0000ffff;
|
|
fakeng->feng_arg.init_pat4 = 0xffffffff;
|
|
fakeng->feng_arg.init_pat5 = 0xffffffff;
|
|
fakeng->feng_arg.init_pat6 = 0xffffffff;
|
|
fakeng->feng_arg.init_pat7 = 0xffffffff;
|
|
fakeng->feng_arg.freeze_en = 0;
|
|
|
|
fakeng->feng_arg.chn_number = chn_id;
|
|
|
|
fakeng->k_addr[chn_id] = kzalloc(PAGE_SIZE * SIZE, GFP_KERNEL | GFP_DMA);
|
|
if (!fakeng->k_addr[chn_id])
|
|
return -ENOMEM;
|
|
|
|
phy_addr = virt_to_phys(fakeng->k_addr[chn_id]);
|
|
fakeng->feng_arg.start_addr_wr = phy_addr;
|
|
fakeng->feng_arg.start_addr_rd = phy_addr;
|
|
fakeng->feng_arg.start_addr_wr_2nd = phy_addr;
|
|
fakeng->feng_arg.start_addr_rd_2nd = phy_addr;
|
|
|
|
fakeng->feng_arg.start_addr_wr_extend = phy_addr >> 32;
|
|
fakeng->feng_arg.start_addr_rd_extend = phy_addr >> 32;
|
|
fakeng->feng_arg.start_addr_wr_2nd_extend = phy_addr >> 32;
|
|
fakeng->feng_arg.start_addr_rd_2nd_extend = phy_addr >> 32;
|
|
|
|
/* Disable fake engine*/
|
|
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
|
|
/* Reset fake engine*/
|
|
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
|
|
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
|
|
|
|
/* FAKE_ENG_CON0 Mode enable control */
|
|
writel((fakeng->feng_arg.rd_wr_joint_en << 17) |
|
|
(fakeng->feng_arg.pat_mode << 13) |
|
|
(fakeng->feng_arg.data_cmp_en << 4) |
|
|
(fakeng->feng_arg.cross_rk_en << 3) |
|
|
(fakeng->feng_arg.loop_en << 2) |
|
|
(fakeng->feng_arg.wr_dis << 1) |
|
|
(fakeng->feng_arg.rd_dis << 0),
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
|
|
/* FAKE_ENG_CON1 Command control */
|
|
writel((fakeng->feng_arg.slow_down_grp << 20) |
|
|
(fakeng->feng_arg.slow_down << 10) |
|
|
(fakeng->feng_arg.wr_amount << 5) |
|
|
(fakeng->feng_arg.rd_amount << 0),
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
|
|
|
|
/* FAKE_ENG_CON2 AXI protocal */
|
|
writel((fakeng->feng_arg.ar_slc << 19) |
|
|
(fakeng->feng_arg.aw_slc << 14) |
|
|
(fakeng->feng_arg.burst_size << 4) |
|
|
(fakeng->feng_arg.burst_len << 0),
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON2);
|
|
|
|
/* FAKE_ENG_CON3 Command control */
|
|
writel(fakeng->feng_arg.grp_aomunt,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON3);
|
|
|
|
/* FAKE_EN_START_ADDR */
|
|
writel(fakeng->feng_arg.start_addr_wr,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR);
|
|
|
|
/* FAKE_EN_START_ADDR_RD */
|
|
writel(fakeng->feng_arg.start_addr_rd,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_RD);
|
|
|
|
/* FAKE_EN_START_ADDR_2ND */
|
|
writel(fakeng->feng_arg.start_addr_wr_2nd,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_2ND);
|
|
|
|
/* FAKE_EN_START_ADDR_RD_2ND */
|
|
writel(fakeng->feng_arg.start_addr_rd_2nd,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_RD_2ND);
|
|
|
|
/* FAKE_ENG_ADDR */
|
|
writel((fakeng->feng_arg.addr_offset2 << 18) |
|
|
(fakeng->feng_arg.addr_offset1 << 8) |
|
|
(fakeng->feng_arg.start_addr_wr_2nd_extend << 4) |
|
|
(fakeng->feng_arg.start_addr_wr_extend << 0),
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_ADDR);
|
|
|
|
/* FAKE_ENG_ADDR_RD */
|
|
writel((fakeng->feng_arg.start_addr_rd_2nd_extend << 4) |
|
|
(fakeng->feng_arg.start_addr_rd_extend << 0),
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_ADDR_RD);
|
|
|
|
/* INIT_PAT0 */
|
|
writel(fakeng->feng_arg.init_pat0,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT0);
|
|
|
|
/* INIT_PAT1 */
|
|
writel(fakeng->feng_arg.init_pat1,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT1);
|
|
|
|
/* INIT_PAT2 */
|
|
writel(fakeng->feng_arg.init_pat2,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT2);
|
|
|
|
/* INIT_PAT3 */
|
|
writel(fakeng->feng_arg.init_pat3,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT3);
|
|
|
|
/* INIT_PAT4 */
|
|
writel(fakeng->feng_arg.init_pat4,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT4);
|
|
|
|
/* INIT_PAT5 */
|
|
writel(fakeng->feng_arg.init_pat5,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT5);
|
|
|
|
/* INIT_PAT6 */
|
|
writel(fakeng->feng_arg.init_pat6,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT6);
|
|
|
|
/* INIT_PAT7 */
|
|
writel(fakeng->feng_arg.init_pat7,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT7);
|
|
|
|
/* compare result freeze */
|
|
writel(fakeng->feng_arg.freeze_en,
|
|
fakeng->fake_eng_base[chn_id] + FAKE_ENG_FREEZE_RESULT);
|
|
|
|
/* HASH */
|
|
val = (0x0 << 24) | (0x1 << 20) | (0x1 << 16) | (0x2 << 12) |
|
|
(0x1 << 8) | (fakeng->feng_arg.chn_number << 4) | 0x1;
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_HASH);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void emi_fake_eng_stop(unsigned int chn_id)
|
|
{
|
|
unsigned int val;
|
|
|
|
if (!(fakeng->bitmap & (0x1 << chn_id))) {
|
|
pr_info("%s: fake eng:%d no start, do nothing here\n", __func__, chn_id);
|
|
return;
|
|
}
|
|
|
|
/* set loop_en = 0 */
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
val &= ~(0x1 << 2);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
/* wait for loop mode disable*/
|
|
udelay(5);
|
|
|
|
/* Wait fake engine done */
|
|
while (1) {
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_DONE);
|
|
pr_debug_ratelimited("%s: fake eng %d, val: %d\n",
|
|
__func__, chn_id, val);
|
|
|
|
if (val)
|
|
break;
|
|
}
|
|
|
|
/* Disable fake engine */
|
|
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
|
|
/* Reset fake engine */
|
|
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
|
|
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
|
|
|
|
/* free k_addr */
|
|
kfree(fakeng->k_addr[chn_id]);
|
|
|
|
/* Clear chn_id */
|
|
fakeng->bitmap &= ~(0x1 << chn_id);
|
|
|
|
pr_info("%s: disable fake eng:%d done, fake eng bitmap:0x%x\n",
|
|
__func__, chn_id, fakeng->bitmap);
|
|
}
|
|
|
|
static void emi_fake_eng_start(unsigned int chn_id,
|
|
unsigned int wr_en,
|
|
unsigned int rd_en,
|
|
unsigned int latency)
|
|
{
|
|
u32 loop_en = 1;
|
|
u32 slow_down, slow_down_grp, val;
|
|
int ret;
|
|
|
|
if (fakeng->bitmap & (0x1 << chn_id)) {
|
|
pr_info("%s:Please disable fake eng:%d first\n", __func__, chn_id);
|
|
return;
|
|
}
|
|
|
|
/* set chn_id bitmap */
|
|
fakeng->bitmap |= (0x1 << chn_id);
|
|
|
|
/* Fake engine basic setting */
|
|
ret = fake_eng_init(chn_id);
|
|
if (ret) {
|
|
pr_info("%s: Cannot allocate memory, stop trigger fake engine\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* set slow_down */
|
|
slow_down = latency;
|
|
|
|
/* Start to incease BW, */
|
|
slow_down_grp = slow_down;
|
|
|
|
if (rd_en) {
|
|
/* set rd_dis = 0 to enable read command*/
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
val &= ~(0x1 << 0);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
}
|
|
|
|
if (wr_en) {
|
|
/* set wr_dis = 0 to enable write command*/
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
val &= ~(0x1 << 1);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
}
|
|
|
|
/* set slow_down = 0 */
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
|
|
val &= ~(0xfffff << 10);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
|
|
|
|
/* set slow_down */
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
|
|
val |= (slow_down_grp << 20) | (slow_down << 10);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
|
|
|
|
/* set loop_en = 1 */
|
|
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
val |= (loop_en << 2);
|
|
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
|
|
|
|
/* Enable fake engine */
|
|
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
|
|
|
|
pr_info("%s: chn_id:%d, wr_en:%d, rd_en:%d, latency:%d\n",
|
|
__func__, chn_id, wr_en, rd_en, slow_down);
|
|
pr_info("%s: fake eng bitmap:0x%x\n", __func__, fakeng->bitmap);
|
|
}
|
|
|
|
static ssize_t emi_fake_eng_show(struct device_driver *driver, char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "0x%x\n", fakeng->bitmap);
|
|
}
|
|
|
|
static ssize_t emi_fake_eng_store
|
|
(struct device_driver *driver, const char *buf, size_t count)
|
|
{
|
|
unsigned int chn_id, en, wr_en, rd_en, latency;
|
|
int ret = 0, n;
|
|
|
|
n = sscanf(buf, "%u,%u,%u,%u,%u\n",
|
|
&chn_id, &en, &wr_en, &rd_en, &latency);
|
|
|
|
if (n != 5) {
|
|
pr_info("%s: Please enter correct input format. n=%d\n", __func__, n);
|
|
goto out;
|
|
}
|
|
|
|
pr_info("%s: %u %u %u %u %u\n",
|
|
__func__, chn_id, en, wr_en, rd_en, latency);
|
|
|
|
if (chn_id >= fakeng->emi_fake_eng_cnt) {
|
|
pr_info("%s: Please enter channel number between 0 ~ %d\n",
|
|
__func__, fakeng->emi_fake_eng_cnt - 1);
|
|
goto out;
|
|
}
|
|
|
|
if (en > 1 || wr_en > 1 || rd_en > 1) {
|
|
pr_info("%s: Please enter en, wr_en, rd_en value for 0 or 1\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
if (wr_en == 0 && rd_en == 0) {
|
|
pr_info("%s: At least one of wr_en or rd_en should be 1\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
if (latency > 100) {
|
|
pr_info("%s: Please enter latency value between 1 ~ 100\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
if (en)
|
|
emi_fake_eng_start(chn_id, wr_en, rd_en, latency);
|
|
else
|
|
emi_fake_eng_stop(chn_id);
|
|
|
|
out:
|
|
return ret ? : count;
|
|
}
|
|
|
|
static DRIVER_ATTR_RW(emi_fake_eng);
|
|
|
|
static int emi_fake_eng_remove(struct platform_device *pdev)
|
|
{
|
|
dev_info(&pdev->dev, "driver removed\n");
|
|
fakeng = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int emi_fake_eng_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *emi_feng_node = pdev->dev.of_node;
|
|
struct emi_fake_eng *feng;
|
|
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
dev_info(&pdev->dev, "driver probed\n");
|
|
|
|
feng = devm_kzalloc(&pdev->dev, sizeof(struct emi_fake_eng), GFP_KERNEL);
|
|
if (!feng)
|
|
return -ENOMEM;
|
|
|
|
ret = of_property_count_elems_of_size(emi_feng_node,
|
|
"reg", sizeof(unsigned int) * 4);
|
|
if (ret <= 0) {
|
|
dev_err(&pdev->dev, "No reg\n");
|
|
return -ENXIO;
|
|
}
|
|
feng->emi_fake_eng_cnt = (unsigned int)ret;
|
|
|
|
feng->fake_eng_base = devm_kmalloc_array(&pdev->dev,
|
|
feng->emi_fake_eng_cnt, sizeof(phys_addr_t), GFP_KERNEL);
|
|
if (!(feng->fake_eng_base))
|
|
return -ENOMEM;
|
|
|
|
|
|
for (i = 0; i < feng->emi_fake_eng_cnt; i++) {
|
|
feng->fake_eng_base[i] = of_iomap(emi_feng_node, i);
|
|
if (!feng->fake_eng_base[i])
|
|
return -ENOMEM;
|
|
}
|
|
|
|
feng->k_addr = devm_kmalloc_array(&pdev->dev,
|
|
feng->emi_fake_eng_cnt, sizeof(void *), GFP_KERNEL);
|
|
if (!(feng->k_addr))
|
|
return -ENOMEM;
|
|
|
|
/* Set to global pointer */
|
|
fakeng = feng;
|
|
|
|
/* Initial channel bitmap */
|
|
fakeng->bitmap = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id emi_fake_eng_of_ids[] = {
|
|
{.compatible = "mediatek,emi-fake-engine",},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver emi_fake_eng_drv = {
|
|
.probe = emi_fake_eng_probe,
|
|
.remove = emi_fake_eng_remove,
|
|
.driver = {
|
|
.name = "emi_fake_eng_drv",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = emi_fake_eng_of_ids,
|
|
},
|
|
};
|
|
|
|
static void __exit emi_fake_eng_exit(void)
|
|
{
|
|
pr_info("emi fake engine unloaded\n");
|
|
|
|
driver_remove_file(&emi_fake_eng_drv.driver,
|
|
&driver_attr_emi_fake_eng);
|
|
|
|
platform_driver_unregister(&emi_fake_eng_drv);
|
|
|
|
}
|
|
static int __init emi_fake_eng_init(void)
|
|
{
|
|
int ret;
|
|
|
|
pr_info("emi fake engine loaded\n");
|
|
|
|
ret = platform_driver_register(&emi_fake_eng_drv);
|
|
if (ret) {
|
|
pr_info("emi fake engine: failed to register dirver\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = driver_create_file(&emi_fake_eng_drv.driver,
|
|
&driver_attr_emi_fake_eng);
|
|
if (ret) {
|
|
pr_info("emi fake engine: failed to create control file\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
module_init(emi_fake_eng_init);
|
|
module_exit(emi_fake_eng_exit);
|
|
|
|
MODULE_DESCRIPTION("MediaTek EMI Fake Engine Driver");
|
|
MODULE_LICENSE("GPL v2");
|