MLK-10913-1: mxc: sim: Add the SIM driver support for i.MX6UL-EVK platform.
Modify the driver to support the SIM on i.MX6UL-EVK platform. The main modification is: 1. Add port index to support different port on platform. 2. Add POS-CARD support. The POS card has external IC to assert when SVEN to low. Add support. 3. Using a function to calculate the strict timing delay. Signed-off-by: Luwei Zhou <b45643@freescale.com> (cherry picked from 17d1315b0704e2db63ee6bd7aaefa0c796f53104)5.4-rM2-2.2.x-imx-squashed
parent
3867b4bd9f
commit
50b2602a93
|
@ -114,6 +114,10 @@
|
|||
#define RCV_FIFO_WPTR (0x74)
|
||||
#define RCV_FIFO_RPTR (0x78)
|
||||
|
||||
/* SIM SETUP register bits */
|
||||
#define SIM_SETUP_SPS_PORT0 (0 << 1)
|
||||
#define SIM_SETUP_SPS_PORT1 (1 << 1)
|
||||
|
||||
/* SIM port[0|1]_cntl register bits */
|
||||
#define SIM_PORT_CNTL_SFPD (1 << 7)
|
||||
#define SIM_PORT_CNTL_3VOLT (1 << 6)
|
||||
|
@ -131,6 +135,8 @@
|
|||
/* SIM enable register bits */
|
||||
#define SIM_ENABLE_XMTEN (1 << 1)
|
||||
#define SIM_ENABLE_RCVEN (1 << 0)
|
||||
#define SIM_ESTOP_EN (1 << 5)
|
||||
#define SIM_ESTOP_EXE (1 << 6)
|
||||
|
||||
/* SIM int_mask register bits */
|
||||
#define SIM_INT_MASK_RFEM (1 << 13)
|
||||
|
@ -227,13 +233,14 @@
|
|||
#define ATR_THRESHOLD_MAX (100)
|
||||
#define ATR_MAX_CWT (10080)
|
||||
#define ATR_MAX_DURATION (20160)
|
||||
#define FCLK_FREQ (5000000)
|
||||
#define SIM_BLOCK_CLK_FREQ (60000000)
|
||||
#define FCLK_FREQ (4000000)
|
||||
|
||||
#define ATR_TIMEOUT (5)
|
||||
#define TX_TIMEOUT (10)
|
||||
#define RX_TIMEOUT (100)
|
||||
#define RESET_RETRY_TIMES (5)
|
||||
#define SIM_QUIRK_TKT259347 (1 << 0)
|
||||
#define EMV_RESET_LOW_CYCLES 42000
|
||||
|
||||
/* Main SIM driver structure */
|
||||
struct sim_t{
|
||||
|
@ -274,7 +281,12 @@ struct sim_t{
|
|||
bool last_is_tx;
|
||||
u16 rcv_head;
|
||||
spinlock_t lock;
|
||||
bool sven_low_active;
|
||||
u32 port_index;
|
||||
u32 port_detect_reg;
|
||||
u32 port_ctrl_reg;
|
||||
u32 clk_rate;
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
static struct miscdevice sim_dev;
|
||||
|
@ -328,9 +340,11 @@ static void sim_set_tx(struct sim_t *sim, u8 enable)
|
|||
u32 reg_data;
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
||||
if (enable)
|
||||
reg_data |= SIM_ENABLE_XMTEN;
|
||||
else
|
||||
if (enable) {
|
||||
reg_data |= SIM_ENABLE_XMTEN | SIM_ENABLE_RCVEN;
|
||||
if (sim->quirks & SIM_QUIRK_TKT259347)
|
||||
reg_data &= ~(SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
||||
} else
|
||||
reg_data &= ~SIM_ENABLE_XMTEN;
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
||||
|
@ -340,10 +354,16 @@ static void sim_set_rx(struct sim_t *sim, u8 enable)
|
|||
{
|
||||
u32 reg_data;
|
||||
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
||||
if (enable)
|
||||
if (enable) {
|
||||
reg_data |= SIM_ENABLE_RCVEN;
|
||||
else
|
||||
reg_data &= ~SIM_ENABLE_XMTEN;
|
||||
if (sim->quirks & SIM_QUIRK_TKT259347)
|
||||
reg_data |= (SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
||||
} else {
|
||||
reg_data &= ~SIM_ENABLE_RCVEN;
|
||||
if (sim->quirks & SIM_QUIRK_TKT259347)
|
||||
reg_data &= ~(SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
||||
}
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
||||
}
|
||||
|
@ -387,7 +407,7 @@ static void sim_receive_atr_set(struct sim_t *sim)
|
|||
u32 reg_data;
|
||||
|
||||
/*Enable RX*/
|
||||
__raw_writel(SIM_ENABLE_RCVEN, sim->ioaddr + ENABLE);
|
||||
sim_set_rx(sim, 1);
|
||||
|
||||
/*Receive fifo threshold = 1 to trigger GPC timer in irq handler*/
|
||||
reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(1);
|
||||
|
@ -746,45 +766,54 @@ static void sim_start(struct sim_t *sim)
|
|||
u32 reg_data, clk_rate, clk_div = 0;
|
||||
pr_debug("%s entering.\n", __func__);
|
||||
|
||||
__raw_writel(0, sim->ioaddr + SETUP);
|
||||
if (sim->port_index == 1)
|
||||
__raw_writel(SIM_SETUP_SPS_PORT1, sim->ioaddr + SETUP);
|
||||
else
|
||||
__raw_writel(SIM_SETUP_SPS_PORT0, sim->ioaddr + SETUP);
|
||||
|
||||
/* ~ 5 MHz */
|
||||
/*1 ~ 5 MHz */
|
||||
clk_rate = clk_get_rate(sim->clk);
|
||||
clk_div = (clk_rate + sim->clk_rate - 1) / sim->clk_rate;
|
||||
__raw_writel(clk_div, sim->ioaddr + CLK_PRESCALER);
|
||||
|
||||
/*Set the port pin to be open drained*/
|
||||
reg_data = __raw_readl(sim->ioaddr + OD_CONFIG);
|
||||
reg_data |= SIM_OD_CONFIG_OD_P0;
|
||||
if (sim->port_index == 1)
|
||||
reg_data |= SIM_OD_CONFIG_OD_P1;
|
||||
else
|
||||
reg_data |= SIM_OD_CONFIG_OD_P0;
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + OD_CONFIG);
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
/*One pin mode*/
|
||||
reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
/* presense detect */
|
||||
pr_debug("%s p0_det is 0x%x \n", __func__,
|
||||
__raw_readl(sim->ioaddr + PORT0_DETECT));
|
||||
if (__raw_readl(sim->ioaddr + PORT0_DETECT) & SIM_PORT_DETECT_SPDP) {
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
||||
__raw_readl(sim->ioaddr + sim->port_detect_reg));
|
||||
if (__raw_readl(sim->ioaddr + sim->port_detect_reg)
|
||||
& SIM_PORT_DETECT_SPDP) {
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
||||
reg_data &= ~SIM_PORT_DETECT_SPDS;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
||||
sim->present = SIM_PRESENT_REMOVED;
|
||||
sim->state = SIM_STATE_REMOVED;
|
||||
} else {
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
||||
reg_data |= SIM_PORT_DETECT_SPDS;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
||||
sim->present = SIM_PRESENT_DETECTED;
|
||||
sim->state = SIM_STATE_DETECTED;
|
||||
};
|
||||
|
||||
/*enable card interrupt. clear interrupt status*/
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
||||
reg_data |= SIM_PORT_DETECT_SDI;
|
||||
reg_data |= SIM_PORT_DETECT_SDIM;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
||||
};
|
||||
|
||||
static void sim_activate(struct sim_t *sim)
|
||||
|
@ -794,48 +823,72 @@ static void sim_activate(struct sim_t *sim)
|
|||
/* activate on sequence */
|
||||
if (sim->present != SIM_PRESENT_REMOVED) {
|
||||
/*Disable Reset pin*/
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data &= ~SIM_PORT_CNTL_SRST;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
/*If sven is low active, we need to set sevn to be high*/
|
||||
if (sim->sven_low_active)
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
/*Enable VCC pin*/
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
if (sim->sven_low_active)
|
||||
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
||||
else
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
msleep(10);
|
||||
/*Enable clock pin*/
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SCEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
msleep(10);
|
||||
} else {
|
||||
pr_err("No card%s\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void sim_reset_low_delay(struct sim_t *sim)
|
||||
{
|
||||
u32 fclk_in_khz;
|
||||
u32 delay_in_us;
|
||||
|
||||
fclk_in_khz = sim->clk_rate / MSEC_PER_SEC;
|
||||
delay_in_us = EMV_RESET_LOW_CYCLES * USEC_PER_MSEC / fclk_in_khz;
|
||||
mdelay(delay_in_us / USEC_PER_MSEC);
|
||||
udelay(delay_in_us % USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static void sim_cold_reset_sequency(struct sim_t *sim)
|
||||
{
|
||||
u32 reg_data;
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data &= ~SIM_PORT_CNTL_SRST;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
if (sim->sven_low_active)
|
||||
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
||||
else
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
mdelay(9);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SCEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
mdelay(8);
|
||||
udelay(600);
|
||||
sim_reset_low_delay(sim);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SRST;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
};
|
||||
|
||||
static void sim_deactivate(struct sim_t *sim)
|
||||
|
@ -845,16 +898,34 @@ static void sim_deactivate(struct sim_t *sim)
|
|||
pr_debug("%s entering.\n", __func__);
|
||||
/* Auto powdown to implement the deactivate sequence */
|
||||
if (sim->present != SIM_PRESENT_REMOVED) {
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data |= SIM_PORT_CNTL_SAPD;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
reg_data |= SIM_PORT_CNTL_SFPD;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
} else {
|
||||
pr_err("No card%s\n", __func__);
|
||||
}
|
||||
|
||||
sim->present = SIM_PRESENT_REMOVED;
|
||||
if (sim->sven_low_active) {
|
||||
/*Set the RESET to be low*/
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data &= ~SIM_PORT_CNTL_SRST;
|
||||
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
usleep_range(2, 5);
|
||||
|
||||
/*Set the clock to be low*/
|
||||
reg_data &= ~SIM_PORT_CNTL_SCEN;
|
||||
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
usleep_range(2, 5);
|
||||
|
||||
/*Set the sven to be high*/
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
} else {
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SAPD;
|
||||
__raw_writel(reg_data,
|
||||
sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SFPD;
|
||||
__raw_writel(reg_data,
|
||||
sim->ioaddr + sim->port_ctrl_reg);
|
||||
}
|
||||
} else
|
||||
pr_err(">>>No card%s\n", __func__);
|
||||
};
|
||||
|
||||
static void sim_cold_reset(struct sim_t *sim)
|
||||
|
@ -873,20 +944,25 @@ static void sim_warm_reset_sequency(struct sim_t *sim)
|
|||
{
|
||||
u32 reg_data;
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
||||
reg_data |= (SIM_PORT_CNTL_SRST | SIM_PORT_CNTL_SVEN | SIM_PORT_CNTL_SCEN);
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= (SIM_PORT_CNTL_SRST | SIM_PORT_CNTL_SCEN);
|
||||
if (sim->sven_low_active)
|
||||
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
||||
else
|
||||
reg_data |= SIM_PORT_CNTL_SVEN;
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
udelay(20);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data &= ~SIM_PORT_CNTL_SRST;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
|
||||
mdelay(8);
|
||||
udelay(600);
|
||||
sim_reset_low_delay(sim);
|
||||
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
||||
reg_data |= SIM_PORT_CNTL_SRST;
|
||||
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
||||
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
||||
}
|
||||
|
||||
static void sim_warm_reset(struct sim_t *sim)
|
||||
|
@ -1492,9 +1568,9 @@ static int sim_release(struct inode *inode, struct file *file)
|
|||
struct sim_t *sim = (struct sim_t *) file->private_data;
|
||||
|
||||
/* disable presense detection */
|
||||
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
||||
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
||||
__raw_writel(reg_data | SIM_PORT_DETECT_SDIM,
|
||||
sim->ioaddr + PORT0_DETECT);
|
||||
sim->ioaddr + sim->port_detect_reg);
|
||||
|
||||
if (sim->present != SIM_PRESENT_REMOVED)
|
||||
sim_deactivate(sim);
|
||||
|
@ -1527,14 +1603,24 @@ static struct platform_device_id imx_sim_devtype[] = {
|
|||
{
|
||||
.name = "imx7d-sim",
|
||||
.driver_data = 0,
|
||||
}, {
|
||||
.name = "imx6ul-sim",
|
||||
.driver_data = SIM_QUIRK_TKT259347,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
enum imx_sim_type {
|
||||
IMX7D_SIM = 0,
|
||||
IMX6UL_SIM,
|
||||
};
|
||||
|
||||
static const struct of_device_id sim_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx7d-sim",
|
||||
.data = &imx_sim_devtype[0], },
|
||||
.data = &imx_sim_devtype[IMX7D_SIM], },
|
||||
{ .compatible = "fsl,imx6ul-sim",
|
||||
.data = &imx_sim_devtype[IMX6UL_SIM], },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -1545,6 +1631,7 @@ static int sim_probe(struct platform_device *pdev)
|
|||
int ret = 0;
|
||||
const struct of_device_id *of_id;
|
||||
struct sim_t *sim = NULL;
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
|
||||
sim = devm_kzalloc(&pdev->dev, sizeof(struct sim_t),
|
||||
GFP_KERNEL);
|
||||
|
@ -1597,6 +1684,18 @@ static int sim_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
sim->sven_low_active = of_property_read_bool(of_node,
|
||||
"sven_low_active");
|
||||
|
||||
ret = of_property_read_u32(of_node, "port", &sim->port_index);
|
||||
if (ret)
|
||||
sim->port_index = 0;
|
||||
sim->port_ctrl_reg = (sim->port_index == 0) ?
|
||||
PORT0_CNTL : PORT1_CNTL;
|
||||
sim->port_detect_reg = (sim->port_index == 0) ?
|
||||
PORT0_DETECT : PORT1_DETECT;
|
||||
sim->quirks = pdev->id_entry->driver_data;
|
||||
|
||||
platform_set_drvdata(pdev, sim);
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue