diff options
Diffstat (limited to 'drivers/tdm/device/fsl_ucc_tdm.c')
-rw-r--r-- | drivers/tdm/device/fsl_ucc_tdm.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/tdm/device/fsl_ucc_tdm.c b/drivers/tdm/device/fsl_ucc_tdm.c index 44204a3..c9a3338 100644 --- a/drivers/tdm/device/fsl_ucc_tdm.c +++ b/drivers/tdm/device/fsl_ucc_tdm.c @@ -999,6 +999,177 @@ static int ucc_tdm_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static void store_clk_config(struct ucc_tdm_private *utdm_priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + /* store si clk */ + utdm_priv->cmxsi1cr_h = in_be32(&qe_mux_reg->cmxsi1cr_h); + utdm_priv->cmxsi1cr_l = in_be32(&qe_mux_reg->cmxsi1cr_l); + + /* store si sync */ + utdm_priv->cmxsi1syr = in_be32(&qe_mux_reg->cmxsi1syr); + + /* store ucc clk */ + memcpy_fromio(utdm_priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32)); +} + +static void resume_clk_config(struct ucc_tdm_private *utdm_priv) +{ + struct qe_mux *qe_mux_reg = &qe_immr->qmx; + + memcpy_toio(qe_mux_reg->cmxucr, utdm_priv->cmxucr, 4 * sizeof(u32)); + + out_be32(&qe_mux_reg->cmxsi1cr_h, utdm_priv->cmxsi1cr_h); + out_be32(&qe_mux_reg->cmxsi1cr_l, utdm_priv->cmxsi1cr_l); + + out_be32(&qe_mux_reg->cmxsi1syr, utdm_priv->cmxsi1syr); + +} + +static int ucc_tdm_suspend(struct device *dev) +{ + struct ucc_tdm_private *utdm_priv = dev_get_drvdata(dev); + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + + if (!utdm_priv) + return -EINVAL; + + ut_info = utdm_priv->ut_info; + uf_regs = utdm_priv->uf_regs; + + /* backup gumr guemr*/ + utdm_priv->gumr = in_be32(&uf_regs->gumr); + utdm_priv->guemr = in_8(&uf_regs->guemr); + + utdm_priv->ucc_pram_bak = kmalloc(sizeof(struct ucc_transparent_param), + GFP_KERNEL); + if (!utdm_priv->ucc_pram_bak) + return -ENOMEM; + + /* backup transparent parameter */ + memcpy_fromio(utdm_priv->ucc_pram_bak, utdm_priv->ucc_pram, + sizeof(struct ucc_transparent_param)); + + /* store the clk configuration */ + store_clk_config(utdm_priv); + + /* save power */ + ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + dev_dbg(dev, "ucc tdm suspend\n"); + + return 0; +} + +static int ucc_tdm_resume(struct device *dev) +{ + struct ucc_tdm_private *utdm_priv = dev_get_drvdata(dev); + struct ucc_tdm_info *ut_info; + struct ucc_fast __iomem *uf_regs; + struct ucc_fast_private *uccf; + struct ucc_fast_info *uf_info; + int ret, i; + u32 cecr_subblock, bd_status; + + if (!utdm_priv) + return -EINVAL; + + ut_info = utdm_priv->ut_info; + uf_info = &ut_info->uf_info; + uf_regs = utdm_priv->uf_regs; + uccf = utdm_priv->uccf; + + /* restore gumr guemr */ + out_8(&uf_regs->guemr, utdm_priv->guemr); + out_be32(&uf_regs->gumr, utdm_priv->gumr); + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, uf_info->urfs); + out_be16(&uf_regs->urfet, uf_info->urfet); + out_be16(&uf_regs->urfset, uf_info->urfset); + out_be16(&uf_regs->utfs, uf_info->utfs); + out_be16(&uf_regs->utfet, uf_info->utfet); + out_be16(&uf_regs->utftt, uf_info->utftt); + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* tdm Rx Tx and sync clock routing */ + resume_clk_config(utdm_priv); + + out_be32(&uf_regs->uccm, uf_info->uccm_mask); + out_be32(&uf_regs->ucce, 0xffffffff); + + ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* rebuild SIRAM */ + init_si(utdm_priv); + + /* Write to QE CECR, UCCx channel to Stop Transmission */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + /* Set UPSMR normal mode */ + out_be32(&uf_regs->upsmr, 0); + + /* init parameter base */ + cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); + ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, utdm_priv->ucc_pram_offset); + + utdm_priv->ucc_pram = (struct ucc_transparent_param __iomem *) + qe_muram_addr(utdm_priv->ucc_pram_offset); + + /* restore ucc parameter */ + memcpy_toio(utdm_priv->ucc_pram, utdm_priv->ucc_pram_bak, + sizeof(struct ucc_transparent_param)); + kfree(utdm_priv->ucc_pram_bak); + + /* rebuild BD entry */ + for (i = 0; i < NUM_OF_BUF; i++) { + if (i < (NUM_OF_BUF - 1)) + bd_status = R_E | R_I | R_CM; + else + bd_status = R_E | R_I | R_W | R_CM; + + out_be32((u32 *)(utdm_priv->rx_bd + i), bd_status); + out_be32(&utdm_priv->rx_bd[i].buf, utdm_priv->dma_rx_addr + + i * MAX_RX_BUF_LENGTH); + + if (i < (NUM_OF_BUF - 1)) + bd_status = T_I; + else + bd_status = T_I | T_W; + + out_be32((u32 *)(utdm_priv->tx_bd + i), bd_status); + out_be32(&utdm_priv->tx_bd[i].buf, utdm_priv->dma_tx_addr + + i * MAX_RX_BUF_LENGTH); + } + + /* if tdm is busy enable TX and RX */ + if (utdm_priv->tdm_busy == 1) { + cecr_subblock = ucc_fast_get_qe_cr_subblock( + utdm_priv->ut_info->uf_info.ucc_num); + + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + + ucc_fast_enable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX); + + /* Enable the TDM port */ + utdm_priv->si_regs->siglmr1_h |= (0x1 << utdm_priv->tdm_port); + } + + return 0; +} + +SIMPLE_DEV_PM_OPS(ucc_tdm_pm_ops, ucc_tdm_suspend, ucc_tdm_resume); +#endif + static const struct of_device_id fsl_ucc_tdm_of_match[] = { { .compatible = "fsl,ucc-tdm", @@ -1015,6 +1186,9 @@ static struct platform_driver ucc_tdm_driver = { .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = fsl_ucc_tdm_of_match, +#ifdef CONFIG_PM + .pm = &ucc_tdm_pm_ops, +#endif }, }; |