blob: dcf16236459f74a7ae9611a4530d957703ab37c3 [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/device.h>
#include "aml_pcmcia.h"
#include "aml_ci.h"
static int aml_pcmcia_debug = 1;
module_param_named(pcmcia_debug, aml_pcmcia_debug, int, 0644);
MODULE_PARM_DESC(pcmcia_debug, "enable verbose debug messages");
static int reset_time_h_t = 2000;
module_param_named(reset_time_h, reset_time_h_t, int, 0644);
MODULE_PARM_DESC(reset_time_h, "reset time h");
static int reset_time_l_t = 2000;
module_param_named(reset_time_l, reset_time_l_t, int, 0644);
MODULE_PARM_DESC(reset_time_l, "reset time l");
#define pr_dbg(fmt, args...)\
do {\
if (aml_pcmcia_debug)\
pr_err("aml_pcmcia:" fmt, ## args);\
} while (0)
#define pr_error(fmt, args...) pr_err("aml_pcmcia: " fmt, ## args)
static int pcmcia_plugin(struct aml_pcmcia *pc, int reset)
{
if (pc->slot_state == MODULE_XTRACTED) {
pc->pwr(pc, AML_PWR_OPEN);/*hi is open power*/
pr_dbg("CAM Plugged IN: Adapter(%d) Slot(0)\n", 0);
//udelay(50);
usleep_range(50, 60);
if (pc->io_device_type != AML_DVB_IO_TYPE_CIBUS)
aml_pcmcia_reset(pc);
/*wait unplug*/
pc->init_irq(pc, IRQF_TRIGGER_RISING);
//udelay(500);
usleep_range(500, 510);
pc->slot_state = MODULE_INSERTED;
} else {
pr_error("repeat into pcmcia insert \r\n");
if (reset)
aml_pcmcia_reset(pc);
}
//msleep(1);
usleep_range(1000, 1010);
pc->pcmcia_plugin(pc, 1);
return 0;
}
static int pcmcia_unplug(struct aml_pcmcia *pc)
{
if (pc->slot_state == MODULE_INSERTED) {
pr_dbg(" CAM Unplugged: Adapter(%d) Slot(0)\n", 0);
/*udelay(50);*/
/*aml_pcmcia_reset(pc);*/
/*wait plugin*/
pc->init_irq(pc, IRQF_TRIGGER_FALLING);
//udelay(500);
usleep_range(500, 510);
pc->pwr(pc, AML_PWR_CLOSE);/*hi is open power*/
pc->slot_state = MODULE_XTRACTED;
}
usleep_range(1000, 1010);
//msleep(1);
pc->pcmcia_plugin(pc, 0);
return 0;
}
static irqreturn_t pcmcia_irq_handler(int irq, void *dev_id)
{
struct aml_pcmcia *pc = (struct aml_pcmcia *)dev_id;
pr_dbg("%s--into--\r\n", __func__);
disable_irq_nosync(pc->irq);
schedule_work(&pc->pcmcia_work);
enable_irq(pc->irq);
return IRQ_HANDLED;
}
static void aml_pcmcia_work(struct work_struct *work)
{
int cd1, cd2;
struct aml_pcmcia *pc = container_of(work, struct aml_pcmcia, pcmcia_work);
if (pc->start_work == 0)
return;
cd1 = pc->get_cd1(pc);
cd2 = pc->get_cd2(pc);
if (cd1 != cd2) {
pr_error("work CAM card not inerted.\n");
} else {
if (!cd1) {
pr_error("work Adapter(%d) Slot(0): CAM Plugin\n", 0);
pcmcia_plugin(pc, 0);
} else {
pr_error("work Adapter(%d) Slot(0): CAM Unplug\n", 0);
pcmcia_unplug(pc);
}
}
}
void aml_pcmcia_detect_cam(struct aml_pcmcia *pc)
{
int cd1, cd2;
if (!pc) {
pr_error("pc is null\n");
return;
}
if (pc->start_work == 0) {
pr_error("pc start work is 0\n");
return;
}
cd1 = pc->get_cd1(pc);
cd2 = pc->get_cd2(pc);
if (cd1 != cd2) {
pr_error("CAM card not inerted. check end\n");
} else {
if (!cd1) {
pr_error("Adapter(%d) Slot(0): CAM Plugin\n", 0);
pcmcia_plugin(pc, 1);
} else {
pr_error("Adapter(%d) Slot(0): CAM Unplug\n", 0);
pcmcia_unplug(pc);
}
}
}
EXPORT_SYMBOL(aml_pcmcia_detect_cam);
static struct aml_pcmcia *pc_cur;
int aml_pcmcia_init(struct aml_pcmcia *pc)
{
int err = 0;
unsigned long mode;
pr_dbg("%s start pc->irq=%d\r\n", __func__, pc->irq);
pc->rst(pc, AML_L);
/*power on*/
if (pc->io_device_type != AML_DVB_IO_TYPE_CIBUS)
pc->pwr(pc, AML_PWR_OPEN);/*hi is open power*/
/*assuming cam unpluged, config the INT to waiting-for-plugin mode*/
pc->init_irq(pc, IRQF_TRIGGER_LOW);
INIT_WORK(&pc->pcmcia_work, aml_pcmcia_work);
mode = IRQF_ONESHOT;
if (pc->io_device_type == AML_DVB_IO_TYPE_SPI_T312 ||
pc->io_device_type == AML_DVB_IO_TYPE_CIBUS) {
mode = mode | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
}
err = request_irq(pc->irq,
pcmcia_irq_handler,
mode, "aml-pcmcia", pc);
if (err != 0) {
pr_error("ERROR: IRQ registration failed ! <%d>", err);
return -ENODEV;
}
pc_cur = pc;
pr_dbg("%s ok\r\n", __func__);
if (pc->io_device_type == AML_DVB_IO_TYPE_SPI_T312 ||
pc->io_device_type == AML_DVB_IO_TYPE_CIBUS) {
//mcu start very fast,so she can detect cam before soc init end.
//so we need add detect cam fun for first time.
aml_pcmcia_detect_cam(pc);
}
return 0;
}
EXPORT_SYMBOL(aml_pcmcia_init);
int aml_pcmcia_exit(struct aml_pcmcia *pc)
{
pc->pwr(pc, AML_PWR_CLOSE);/*hi is open power*/
free_irq(pc->irq, pc);
return 0;
}
EXPORT_SYMBOL(aml_pcmcia_exit);
int aml_pcmcia_reset(struct aml_pcmcia *pc)
{
pr_dbg("CAM RESET-->start\n");
/* viaccess neotion cam need delay 2000 and 3000 */
/* smit cam need delay 1000 and 1500 */
/* need change delay according cam vendor */
pc->rst(pc, AML_H);/*HI is reset*/
msleep(reset_time_h_t);
pc->rst(pc, AML_L);/*default LOW*/
msleep(reset_time_l_t);
pr_dbg("CAM RESET--end\n");
return 0;
}
EXPORT_SYMBOL(aml_pcmcia_reset);