diff options
author | Jiucheng Xu <Jiucheng.Xu@freescale.com> | 2014-01-16 07:15:29 (GMT) |
---|---|---|
committer | Jose Rivera <German.Rivera@freescale.com> | 2014-03-24 16:31:46 (GMT) |
commit | 0211617adfe9944b334c549dc161ed7e96f448c2 (patch) | |
tree | 06c19241d37cbfdb64ccaac26b9465dae2c42841 /drivers | |
parent | 2944285c4e9c6e798ca2f7f84747827ba608b12a (diff) | |
download | linux-fsl-qoriq-0211617adfe9944b334c549dc161ed7e96f448c2.tar.xz |
QE-TDM: Add PM support for QE-TDM
Add suspend and resume function to qe-tdm.
Signed-off-by: Jiucheng Xu <Jiucheng.Xu@freescale.com>
Signed-off-by: Zhao Qiang <B45475@freescale.com>
Change-Id: I81cac61575196957d28071ebf3f77be848494ff7
Reviewed-on: http://git.am.freescale.net:8181/9216
Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com>
Reviewed-by: Xiaobo Xie <X.Xie@freescale.com>
Reviewed-by: Jose Rivera <German.Rivera@freescale.com>
Reviewed-on: http://git.am.freescale.net:8181/10111
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tdm/device/fsl_ucc_tdm.c | 174 | ||||
-rw-r--r-- | drivers/tdm/device/fsl_ucc_tdm.h | 8 |
2 files changed, 182 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 }, }; diff --git a/drivers/tdm/device/fsl_ucc_tdm.h b/drivers/tdm/device/fsl_ucc_tdm.h index 627eb6c..1a1b161 100644 --- a/drivers/tdm/device/fsl_ucc_tdm.h +++ b/drivers/tdm/device/fsl_ucc_tdm.h @@ -153,6 +153,14 @@ struct ucc_tdm_private { wait_queue_head_t tdm_queue; bool tdm_queue_flag; struct tdm_adapter adap; +#ifdef CONFIG_PM + struct ucc_transparent_param *ucc_pram_bak; + u32 gumr; + u8 guemr; + u32 cmxsi1cr_l, cmxsi1cr_h; + u32 cmxsi1syr; + u32 cmxucr[4]; +#endif }; #define NUM_OF_BUF 4 |