// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/delay.h>
#include <media/dvb_ca_en50221.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/spi/spi.h>

#include "mdrv_adapter.h"
#include "mtk_pcmcia.h"
#include "mtk_pcmcia_core.h"
#include "mtk_pcmcia_drv.h"
#include "mtk_msb9121_core.h"
#define EIGHT 8
#define MASK_BYTE 0xFF
#define MODULE_NOT_READY   4
extern struct dvb_adapter *mdrv_get_dvb_adapter(void);

typedef enum {
	OK = 0,
	NOT_OK = -1,
} CI_STATUS_T;

#define MLP_CI_CORE_INFO(fmt, arg...)	//pr_info("[mdbginfo_CADD_CI]" fmt, ##arg)

#define MLP_CI_CORE_ENTRY(fmt, arg...)	//pr_info("[mdbgin_CADD_CI_CORE]" fmt, ##arg)

#define MLP_CI_ENABLE 1

#define MsOS_DelayTask(x)  msleep_interruptible((unsigned int)x)
#define PCMCIA_WAIT_READY_POLLING_TIME				10
#define PCMCIA_WAIT_READY_POLLING_TIMEOUT_COUNT		300
#define PCMCIA_DEFAULT_POWERON_DURATION			10
#define PCMCIA_WAIT_READ_CIS_TIME		2000
#define PCMCIA_SAT_RS_WAIT_DELAY		1000
#define NOT_READY 4
int _CardState;
static int companion_chip;
bool resume = FALSE;
bool str_flg = FALSE;
bool processing = FALSE;
bool prob_done = FALSE;
struct gpio_desc *pcm_pwr_ctrl = NULL;
static unsigned char _gu8HW_PowerOnDuration = PCMCIA_DEFAULT_POWERON_DURATION;

//static int mtk_pcmcia_remove(struct spi_device *pdev)
static int mtk_pcmcia_remove(struct platform_device *pdev)
{
	struct mtk_dvb_ci *dvb = platform_get_drvdata(pdev);

	MTK_CI_INFO("\n");
	dvb_ca_en50221_release(&dvb->ca);
	return 0;
}

//=======MLP layer

int mlp_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
{
	MLP_CI_CORE_ENTRY("[%s][%d] slot=%d\n", __func__, __LINE__, slot);//check
	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}

	mdrv_pcmcia_reset_hw(slot);
	if (mdrv_pcmcia_ready_status())
		MTK_CI_INFO("CardReady=TRUE\n");
	else
		MTK_CI_INFO("CardReady=FALSE\n");

	processing = false;
	return OK;
}

int mlp_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
{
	unsigned char bDetectStatus = 0;
	unsigned char bStatusChange = 0;

	MLP_CI_CORE_ENTRY("[%s][%d] slot=%d open=%d\n", __func__, __LINE__, slot, open);//check

	if (str_flg) {
		str_flg = FALSE;
		MTK_CI_INFO("STR return not ready\n");
		return MODULE_NOT_READY;
	}
	processing = true;

	if (resume) {
		bStatusChange = mdrv_pcmcia_polling(slot);
		bDetectStatus = mdrv_pcmcia_is_module_still_plugged(slot);

		if (bDetectStatus) {
			//MTK_CI_INFO("Card IN\n");
			if (bStatusChange) {
				MTK_CI_INFO("Card IN\n");
				/* set the gpio to high */
				if (companion_chip) {
					mtk_msb9121_ci_power_en(1);
					MTK_CI_INFO("ci power on\n");
					if (!mtk_msb9121_SwitchPCMCIAMode())
						MTK_CI_ERR("switch to PCMCIA mode fail !!!\n");
				} else {
					gpiod_set_value(pcm_pwr_ctrl, 1);
					MTK_CI_INFO("ci power on\n");
				}
				MTK_CI_INFO("ci waiting power stable (%dms)\n",
						_gu8HW_PowerOnDuration);
				MsOS_DelayTask(_gu8HW_PowerOnDuration);
				// NEED Waiting for Power On after VBUS Power Enable
				MTK_CI_INFO("Card IN done\n");
			}

			_CardState = DVB_CA_EN50221_POLL_CAM_PRESENT;
			if (mdrv_pcmcia_ready_status())
				_CardState |= DVB_CA_EN50221_POLL_CAM_READY;
		} else {
			//MTK_CI_INFO("Card OUT\n");
			if (bStatusChange) {
				MTK_CI_INFO("Card OUT\n");
				/* set the gpio to low */
				if (companion_chip) {
					mtk_msb9121_ci_power_en(0);
					MTK_CI_INFO("ci power off\n");
					if (!mtk_msb9121_SwitchGPIOMode())
						MTK_CI_ERR("switch to GPIO mode fail !!!\n");
				} else {
					gpiod_set_value(pcm_pwr_ctrl, 0);
					MTK_CI_INFO("ci power off\n");
				}
				MTK_CI_INFO("Card OUT done\n");
			}
			_CardState = 0;
			fgCardIsrtCondition = 1;
			//MTK_CI_INFO("fgCardIsrtCondition = 1\n");
		}

		//MTK_CI_INFO("_CardState=%d\n", _CardState);
		processing = false;
		return _CardState;
	} else {
		MTK_CI_ERR("Already suspended\n");
	}

	//MTK_CI_INFO("resume=%d\n", resume);
	processing = false;
	return MODULE_NOT_READY;

}
#define D_2 2
int mlp_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
{
	int attrAdress = address/D_2;
	unsigned char value = 0;//for coverity

	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d address=%d\n",
		__func__, __LINE__, slot, address);//check
	if (address == 0) {
		//fix CANAL READY cam BUG wait more time then read CIS
		MsOS_DelayTask(PCMCIA_WAIT_READ_CIS_TIME);
		MTK_CI_INFO("wait for read CIS\n");
	}

	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}
	mdrv_pcmcia_read_attrib_mem(slot, attrAdress, &value);
	processing = false;
	MLP_CI_CORE_INFO
		("[%s][%d] address=0x%02x value=0x%02x\n",
		__func__, __LINE__, address, value);
	return value;
}

int mlp_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
{
	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d address=%d value=%x\n",
		__func__, __LINE__, slot, address, value);//check


	//No need to do address/2 ?
	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}
	mdrv_pcmcia_write_attrib_mem(slot, address, value);
	//Can not get return value here

	processing = false;
	return 0;
}

int mlp_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
{
	int Data = 0;

	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d address=%d\n",
		__func__, __LINE__, slot, address);//Check

	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}
	Data = mdrv_pcmcia_read_io_mem(slot, address);
	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d address=0x%02x Data=0x%02x\n",
		__func__, __LINE__, slot, address, Data);

	processing = false;
	return Data;
}

int mlp_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
{
	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d address=0x%02x value=0x%02x\n",
		__func__, __LINE__, slot, address, value);//check

	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}
	mdrv_pcmcia_write_io_mem(slot, address, value);
	//Can not get return value here

	processing = false;
	return 0;
}
//ktime_t ci_stime1;
extern ktime_t io_write_done;
extern ktime_t da_up;
ktime_t polling_da;

int mlp_ci_read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
{
	int data_len = 0;
	int status;

	//ci_stime1 = ktime_get();
	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d\n",
		__func__, __LINE__, slot);//Check

	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		return 0;
	}
	//MTK_CI_ERR("\n");
	//polling_da = ktime_get();
	status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
	if (!(status & STATUSREG_DA)) {
		//MTK_CI_ERR("status 0x%02x\n",status);
		/* no data */
		status = 0;
		processing = false;
		return status;
	}
	//da_up = ktime_get();
	//trace_printk("%s write to DA cost:%lld us spi coust:%lld\n",__func__, ktime_to_us(ktime_sub(da_up, io_write_done)), ktime_to_us(ktime_sub(da_up, polling_da)));
	data_len = mdrv_pcmcia_read_data(slot, ebuf, ecount);
	//pr_err("%s rev%d %lld us\n", __func__, data_len, ktime_to_us(ktime_sub(ktime_get(), ci_stime1)));

	processing = false;
	return data_len;
}

int mlp_ci_write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
{
	int status;
	MLP_CI_CORE_ENTRY
		("[%s][%d] slot=%d ecount=%d\n",
		__func__, __LINE__, slot, ecount);//check

	processing = true;
	if (!resume) {
		MTK_CI_ERR("Already suspended\n");
		processing = false;
		status = -EFAULT;
		goto exitnowrite;
	}
	status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
	if (status < 0)
		goto exitnowrite;
	if (status & (STATUSREG_DA | STATUSREG_RE)) {
		//if (status & STATUSREG_DA)
		//	dvb_ca_en50221_thread_wakeup(ca);
		MTK_CI_ERR("status 0x%02x\n",status);
		status = -EAGAIN;
		goto exitnowrite;
	}

	/* OK, set HC bit */
	mdrv_pcmcia_write_io_mem(slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC);

	/* check if interface is still free */
	status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
	if (status < 0)
		goto exit;
	if (!(status & STATUSREG_FR)) {
		/* it wasn't free => try again later */
		MTK_CI_ERR("status 0x%02x\n",status);
		status = -EAGAIN;
		goto exit;
	}

	/*
	 * It may need some time for the CAM to settle down, or there might
	 * be a race condition between the CAM, writing HC and our last
	 * check for DA. This happens, if the CAM asserts DA, just after
	 * checking DA before we are setting HC. In this case it might be
	 * a bug in the CAM to keep the FR bit, the lower layer/HW
	 * communication requires a longer timeout or the CAM needs more
	 * time internally. But this happens in reality!
	 * We need to read the status from the HW again and do the same
	 * we did for the previous check for DA
	 */
	status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
	if (status < 0)
		goto exit;

	if (status & (STATUSREG_DA | STATUSREG_RE)) {
		//if (status & STATUSREG_DA)
		//	dvb_ca_en50221_thread_wakeup(ca);
		MTK_CI_ERR("status 0x%02x\n",status);
		status = -EAGAIN;
		goto exit;
	}

	/* send the amount of data */
	mdrv_pcmcia_write_io_mem(slot, CTRLIF_SIZE_HIGH, ecount >> EIGHT);
	mdrv_pcmcia_write_io_mem(slot, CTRLIF_SIZE_LOW, ecount & MASK_BYTE);

	/* send the buffer */
	mdrv_pcmcia_write_io_mem_long(slot, 0, ecount, ebuf);

	/* check for write error (WE should now be 0) */
	status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
	if (status < 0)
		goto exit;
	if (status & STATUSREG_WE) {
		//sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
		MTK_CI_ERR("status 0x%02x write error retry cover\n", status);
		//status = -EIO;
		//goto exit;
		mdrv_pcmcia_write_io_mem(slot, CTRLIF_COMMAND, IRQEN);// low HC

		mdrv_pcmcia_write_io_mem(slot, CTRLIF_COMMAND, CMDREG_RS);// set RS clear interface
		MTK_CI_ERR("set RS flag\n");

		msleep_interruptible(PCMCIA_SAT_RS_WAIT_DELAY);
		MTK_CI_ERR("%s %d set delay 1S\n", __func__, __LINE__);


		status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
		if (status < 0)
			goto exitnowrite;
		//if (status & (STATUSREG_DA | STATUSREG_RE)) {
		if (!(status & STATUSREG_FR)) {
			//if (status & STATUSREG_DA)
			//	dvb_ca_en50221_thread_wakeup(ca);
			MTK_CI_INFO("omg status 0x%02x\n", status);
			status = -EAGAIN;
			goto exitnowrite;
		}
		MTK_CI_INFO("omg status 0x%02x\n", status);
		/* OK, set HC bit */
		mdrv_pcmcia_write_io_mem(slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC);

		/* check if interface is still free */
		status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
		if (status < 0)
			goto exit;
		if (!(status & STATUSREG_FR)) {
			/* it wasn't free => try again later */
			MTK_CI_INFO("status 0x%02x\n", status);
			status = -EAGAIN;
			goto exit;
		}

		/* send the amount of data */
		mdrv_pcmcia_write_io_mem(slot, CTRLIF_SIZE_HIGH, ecount >> EIGHT);
		mdrv_pcmcia_write_io_mem(slot, CTRLIF_SIZE_LOW, ecount & MASK_BYTE);

		/* send the buffer */
		mdrv_pcmcia_write_io_mem_long(slot, 0, ecount, ebuf);

		/* check for write error (WE should now be 0) */
		status = mdrv_pcmcia_read_io_mem(slot, CTRLIF_STATUS);
		if (status < 0)
			goto exit;
		if (status & STATUSREG_WE) {
			MTK_CI_INFO("status 0x%02x write error again\n", status);
			status = -EIO;
			goto exit;
		}

	}
	status = ecount;

	//MTK_CI_INFO("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
		//ebuf[0], (ebuf[1] & 0x80) == 0, ecount);
exit:
	mdrv_pcmcia_write_io_mem(slot, CTRLIF_COMMAND, IRQEN);

exitnowrite:
	processing = false;
	return status;
}


int mlp_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
{
	MLP_CI_CORE_ENTRY("[%s][%d] not support\n", __func__, __LINE__);//check

	return 0;
}

int mlp_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
{
	MLP_CI_CORE_ENTRY("[%s][%d] slot=%d\n", __func__, __LINE__, slot);
	//MTK_CI_INFO("not support\n");

	return OK;

}


static int mtk_pcmcia_notifier_link_notify(struct dvb_device *dev,
	struct dvbdev_link *link)
{
	MTK_CI_INFO("\n");
	return 0;
}

static int mtk_pcmcia_notifier_streaming_event(struct dvb_device *dvbdev,
	enum cfm_notify_event_type_t ev_type,
	struct cfm_properties *props, void *notify_data)
{
	MTK_CI_INFO("\n");
	return 0;
}
irqreturn_t ci_irq_handler(int irq, void *dev_id)
{
	struct mtk_dvb_ci *dvb = (struct mtk_dvb_ci*) dev_id;

	if(!prob_done) {
		MTK_CI_INFO("CI interrupt but not prob done\n");
		return IRQ_HANDLED;
	}
	//MTK_CI_ERR("test IRQ ready\n");

	if (!companion_chip)
		mdrv_pcmcia_isr();
	//MTK_CI_ERR("test IRQ ready\n");
	dvb_ca_en50221_frda_irq(&dvb->ca, 0);
	//MTK_CI_ERR("test IRQ done\n");
	if (!companion_chip)
		mdrv_pcmcia_Enable_InterruptV2(0, TRUE);
	return IRQ_HANDLED;
}

//static int mtk_pcmcia_probe(struct spi_device *pdev)
static int mtk_pcmcia_probe(struct platform_device *pdev)
{
	struct mtk_dvb_ci *dvb = NULL;
	int ret = 0, i;
	int slot_count = 0;
	struct device_node *dn;
	struct dvbdev_notifier *notifier = NULL;
	struct dvb_device *dvbdev = NULL;

	MLP_CI_CORE_ENTRY("[%s][%d]\n", __func__, __LINE__);//check

	dvb = kzalloc(sizeof(struct mtk_dvb_ci), GFP_KERNEL);
	if (!dvb) {
		MTK_CI_ERR("kzalloc failed\n");
		return -ENOMEM;
	}

	dvb->pdev = pdev;

	dn = pdev->dev.of_node;
	ret = of_property_read_u32(dn, "companion_chip", &dvb->companion_chip);
	if (ret) {
		MTK_CI_ERR("Failed to get companion_chip\n");
		ret = -ENONET;
		goto mtk_pcmcia_probe_err_exit;
	}
	MTK_CI_INFO("companion_chip=%d\n",dvb->companion_chip);

	mdrv_pcmcia_set_companion_chip(dvb->companion_chip);
	companion_chip = dvb->companion_chip;
	if (dvb->companion_chip) {
		mtk_msb9121_reset_companion_chip();
		mdrv_pcmcia_init_sw(0, FALSE);
	} else {
		/* "pcm_pwr_ctrl" label is matching the device tree declaration.
		 * OUT_LOW is the value at init
		 */
		pcm_pwr_ctrl = devm_gpiod_get(&pdev->dev, "pcm_pwr_ctrl", GPIOD_OUT_LOW);
		if (IS_ERR(pcm_pwr_ctrl)) {
			MTK_CI_ERR("Unable to get pcm_pwr_ctrl from dtsi\n");
			ret = -ENONET;
			goto mtk_pcmcia_probe_err_exit;
		}
		MTK_CI_INFO("[%s][%d] pcm_pwr_ctrl get pass\n", __func__, __LINE__);


		//maping register
		ret = mdrv_ci_res_init(pdev, &dvb->resource);
		if (ret < 0) {
			MTK_CI_ERR("resoucre init fail\n");
			goto mtk_pcmcia_probe_err_exit;
		}
		mdrv_pcmcia_init_sw(dvb->resource.regmaps->riu_vaddr, FALSE);

		ret = mdrv_ci_clock_init(pdev, &dvb->clock);
		if (ret < 0) {
			MTK_CI_ERR("pcm clk fail\n");
			goto mtk_pcmcia_probe_err_exit;
		}

	}
	mdrv_pcmcia_init_hw();
	resume = TRUE;

	dvb->adapter = mdrv_get_dvb_adapter();
	if (!dvb->adapter) {
		MTK_CI_ERR("mdrv_get_dvb_adapter error.\n");
		ret = -ENODEV;
		goto mtk_pcmcia_probe_err_exit;
	}

	dvb->ca.data = dvb;
	dvb->ca.owner = THIS_MODULE;
	dvb->ca.slot_reset = mlp_ci_slot_reset;
	dvb->ca.poll_slot_status = mlp_ci_poll_slot_status;
	dvb->ca.read_attribute_mem = mlp_ci_read_attribute_mem;
	dvb->ca.write_attribute_mem = mlp_ci_write_attribute_mem;
	dvb->ca.read_cam_control = mlp_ci_read_cam_control;
	dvb->ca.write_cam_control = mlp_ci_write_cam_control;
	dvb->ca.slot_ts_enable = mlp_ci_slot_ts_enable;
	dvb->ca.slot_shutdown = mlp_ci_slot_shutdown;
	dvb->ca.read_data = mlp_ci_read_data;
	dvb->ca.write_data = mlp_ci_write_data;

	//MTK_CI_INFO("DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE mode\n");
	//ret = dvb_ca_en50221_init(dvb->adapter, &dvb->ca, DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE, 1);

	dn = pdev->dev.of_node;

	ret = of_property_read_u32(dn, "slot_count", &slot_count);
	if (ret) {
		MTK_CI_ERR("Failed to get slot_count\n");
		ret = -ENONET;
		goto mtk_pcmcia_probe_err_exit;
	}
	MTK_CI_INFO("slot_count=%d\n", slot_count);

	pr_info("[%s][%d] 0 mode(pollng)\n", __func__, __LINE__);
	ret = dvb_ca_en50221_init(dvb->adapter, &dvb->ca, 0, slot_count);

	if (ret != 0) {
		dvb_ca_en50221_release(&dvb->ca);
		MTK_CI_ERR("ci initialisation failed\n");
	}

	/* PAD_INT for IRQA interrupt gpio*/
	if (dvb->companion_chip) {
		dvb->ci_irq = mtk_msb9121_ci_irq();
		if (dvb->ci_irq <= 0) {
			MTK_CI_ERR("gpio_irq: no irq, fail");
			ret = -ENONET;
			goto mtk_pcmcia_probe_err_exit;

		}
		MTK_CI_INFO("dvb->ci_irq=%d", dvb->ci_irq);

		ret =
		    devm_request_irq(&pdev->dev, dvb->ci_irq, ci_irq_handler, IRQF_TRIGGER_FALLING,
				     "dvb-ci-irq", dvb);

		if (ret < 0) {
			MTK_CI_ERR("failed to request irq\n");
			ret = -ENONET;
			goto mtk_pcmcia_probe_err_exit;

		}

	} else {
		dvb->ci_irq = platform_get_irq(pdev, 0);
		if (dvb->ci_irq  < 0) {
			MTK_CI_ERR("failed to get irq\n");
			goto mtk_pcmcia_probe_err_exit;
		}

		MTK_CI_INFO("ci irq = %x\n", dvb->ci_irq);
		//ci_interrupt_irq = dvb->ci_irq;
		mdrv_pcmcia_Enable_InterruptV2(0, false);
		ret =
			devm_request_irq(&pdev->dev,
							 dvb->ci_irq,
							 ci_irq_handler,
							 0,
							 "mediatek,pcmcia", dvb);
		if (ret < 0) {
			MTK_CI_ERR("failed to request irq\n");
			goto mtk_pcmcia_probe_err_exit;
		}
	}
	notifier = devm_kzalloc(&pdev->dev, sizeof(struct dvbdev_notifier), GFP_KERNEL);
	if (notifier) {
		/*
		 * dvb_device* is stored in dvb_ca_private
		 * and we cannot get.
		 */
		for (i = 15; i >= 0; i--) {
			dvbdev = mdrv_adapter_find_device(DVB_DEVICE_CA, i);
			if (dvbdev)
				break;
		}
		MTK_CI_INFO("register notifier for CA%d\n", i);
		notifier->link_notify = &mtk_pcmcia_notifier_link_notify;
		notifier->streaming_event_notify = &mtk_pcmcia_notifier_streaming_event;
		ret = mdrv_adapter_register_device_notifier(
			dvbdev, notifier);
		if (ret < 0)
			MTK_CI_ERR("register notifier fail\n");
	} else
		MTK_CI_ERR("fail to create notifier\n");

	prob_done = TRUE;
	fgCardIsrtCondition = 0;
	MTK_CI_INFO("fgCardIsrtCondition = 0\n");
	MTK_CI_INFO("succeeded\n");
	if (!dvb->companion_chip) {
		mdrv_pcmcia_Enable_InterruptV2(0, TRUE);
		MTK_CI_INFO("enable enterrupt\n");
	}
	return ret;

mtk_pcmcia_probe_err_exit:
	if (dvb != NULL)
		kfree(dvb);
	return ret;
}


static int mtk_pcmcia_suspend(struct device *pdev)
{
#define WAIT_PROCESSING_INTERVAL 20
#define RETRY_MAX_TIME 1000

	int checktime = 0;

	MLP_CI_CORE_ENTRY("[%s][%d]\n", __func__, __LINE__);
	while (processing) {
		MsOS_DelayTask(WAIT_PROCESSING_INTERVAL);
		checktime += WAIT_PROCESSING_INTERVAL;
		if (checktime > RETRY_MAX_TIME) {
			MTK_CI_ERR("Wait for processing timeout!\n");
			break;
		}
	}
	resume = FALSE; //dvb core stop polling when suspend

	//struct mtk_dvb_ci *dvb = platform_get_drvdata(mtk_pcmcia);
	mdrv_pcmcia_set_power_state(E_POWER_SUSPEND);

	if (slot_info_status != NOT_READY) {
		slot_info_pre_status = slot_info_status;
		MTK_CI_INFO("save status\n");
	}
	MTK_CI_INFO("slot_info_status = %d\n", slot_info_status);
	MTK_CI_INFO("slot_info_pre_status = %d\n", slot_info_pre_status);
	return 0;
}

static int mtk_pcmcia_resume(struct device *pdev)
{
	MLP_CI_CORE_ENTRY("[%s][%d]\n", __func__, __LINE__);
	//struct mtk_dvb_ci *dvb = platform_get_drvdata(mtk_pcmcia);
	str_ci_module_not_ready = 1;
	MTK_CI_INFO("str_ci_module_not_ready = %u\n", str_ci_module_not_ready);
	MTK_CI_INFO("slot_info_pre_status = %d\n", slot_info_pre_status);

	fgCardIsrtCondition = 0;
	MTK_CI_INFO("fgCardIsrtCondition = %u\n", fgCardIsrtCondition);
	if (companion_chip)
		mtk_msb9121_reset_companion_chip();

	mdrv_pcmcia_set_power_state(E_POWER_RESUME);
	str_flg = TRUE;
	resume = TRUE;
	MTK_CI_INFO("resume = %u str_flg = %u\n", resume, str_flg);
	return 0;
}

static const struct dev_pm_ops mtk_pcmcia_pm_ops = {
	.suspend = mtk_pcmcia_suspend,
	.resume = mtk_pcmcia_resume,
};


static const struct of_device_id mtk_pcmcia_dt_match[] = {
	{.compatible = "mediatek,pcmcia"},
	{},
};

MODULE_DEVICE_TABLE(of, mtk_pcmcia_dt_match);

static struct platform_driver mtk_pcmcia_driver = {
//static struct spi_driver mtk_pcmcia_driver = {
	.probe = mtk_pcmcia_probe,
	.remove = mtk_pcmcia_remove,
	.driver = {
		   .name = "mediatek,pcmcia",
		   .owner = THIS_MODULE,
		   .of_match_table = mtk_pcmcia_dt_match,
		   .pm = &mtk_pcmcia_pm_ops
		   },
};

module_platform_driver(mtk_pcmcia_driver);
//module_spi_driver(mtk_pcmcia_driver);

MODULE_DESCRIPTION("Mediatek PCMCIA driver");
MODULE_AUTHOR("Jimmy-CY Chen <Jimmy-CY.Chen@mediatek.com>");
MODULE_LICENSE("GPL");
